From 6160ed8911e4ed3f7ca3589a5357ae813e27175e Mon Sep 17 00:00:00 2001 From: "Steinar H. Gunderson" Date: Thu, 13 Feb 2020 00:32:13 +0100 Subject: [PATCH] Split out some work from the MJPEG encoder constructor. --- nageru/mjpeg_encoder.cpp | 112 +++++++++++++++++++++++---------------- 1 file changed, 65 insertions(+), 47 deletions(-) diff --git a/nageru/mjpeg_encoder.cpp b/nageru/mjpeg_encoder.cpp index 39a5959..867414d 100644 --- a/nageru/mjpeg_encoder.cpp +++ b/nageru/mjpeg_encoder.cpp @@ -117,6 +117,68 @@ int MJPEGEncoder::write_packet2(uint8_t *buf, int buf_size, AVIODataMarkerType t return buf_size; } +namespace { + +void add_video_stream(AVFormatContext *avctx) +{ + AVStream *stream = avformat_new_stream(avctx, nullptr); + if (stream == nullptr) { + fprintf(stderr, "avformat_new_stream() failed\n"); + abort(); + } + + // FFmpeg is very picky about having audio at 1/48000 timebase, + // no matter what we write. Even though we'd prefer our usual 1/120000, + // put the video on the same one, so that we can have locked audio. + stream->time_base = AVRational{ 1, OUTPUT_FREQUENCY }; + stream->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; + stream->codecpar->codec_id = AV_CODEC_ID_MJPEG; + + // Used for aspect ratio only. Can change without notice (the mux won't care). + stream->codecpar->width = global_flags.width; + stream->codecpar->height = global_flags.height; + + // TODO: We could perhaps use the interpretation for each card here + // (or at least the command-line flags) instead of the defaults, + // but what would we do when they change? + stream->codecpar->color_primaries = AVCOL_PRI_BT709; + stream->codecpar->color_trc = AVCOL_TRC_IEC61966_2_1; + stream->codecpar->color_space = AVCOL_SPC_BT709; + stream->codecpar->color_range = AVCOL_RANGE_MPEG; + stream->codecpar->chroma_location = AVCHROMA_LOC_LEFT; + stream->codecpar->field_order = AV_FIELD_PROGRESSIVE; +} + +void add_audio_stream(AVFormatContext *avctx) +{ + AVStream *stream = avformat_new_stream(avctx, nullptr); + if (stream == nullptr) { + fprintf(stderr, "avformat_new_stream() failed\n"); + abort(); + } + stream->time_base = AVRational{ 1, OUTPUT_FREQUENCY }; + stream->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; + stream->codecpar->codec_id = AV_CODEC_ID_PCM_S32LE; + stream->codecpar->channel_layout = AV_CH_LAYOUT_STEREO; + stream->codecpar->channels = 2; + stream->codecpar->sample_rate = OUTPUT_FREQUENCY; +} + +void finalize_mux(AVFormatContext *avctx) +{ + AVDictionary *options = NULL; + vector> opts = MUX_OPTS; + for (pair opt : opts) { + av_dict_set(&options, opt.first.c_str(), opt.second.c_str(), 0); + } + if (avformat_write_header(avctx, &options) < 0) { + fprintf(stderr, "avformat_write_header() failed\n"); + abort(); + } +} + +} // namespace + MJPEGEncoder::MJPEGEncoder(HTTPD *httpd, const string &va_display) : httpd(httpd) { @@ -132,56 +194,12 @@ MJPEGEncoder::MJPEGEncoder(HTTPD *httpd, const string &va_display) avctx->flags = AVFMT_FLAG_CUSTOM_IO; for (unsigned card_idx = 0; card_idx < global_flags.card_to_mjpeg_stream_export.size(); ++card_idx) { - AVStream *stream = avformat_new_stream(avctx.get(), nullptr); - if (stream == nullptr) { - fprintf(stderr, "avformat_new_stream() failed\n"); - abort(); - } - - // FFmpeg is very picky about having audio at 1/48000 timebase, - // no matter what we write. Even though we'd prefer our usual 1/120000, - // put the video on the same one, so that we can have locked audio. - stream->time_base = AVRational{ 1, OUTPUT_FREQUENCY }; - stream->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; - stream->codecpar->codec_id = AV_CODEC_ID_MJPEG; - - // Used for aspect ratio only. Can change without notice (the mux won't care). - stream->codecpar->width = global_flags.width; - stream->codecpar->height = global_flags.height; - - // TODO: We could perhaps use the interpretation for each card here - // (or at least the command-line flags) instead of the defaults, - // but what would we do when they change? - stream->codecpar->color_primaries = AVCOL_PRI_BT709; - stream->codecpar->color_trc = AVCOL_TRC_IEC61966_2_1; - stream->codecpar->color_space = AVCOL_SPC_BT709; - stream->codecpar->color_range = AVCOL_RANGE_MPEG; - stream->codecpar->chroma_location = AVCHROMA_LOC_LEFT; - stream->codecpar->field_order = AV_FIELD_PROGRESSIVE; + add_video_stream(avctx.get()); } for (unsigned card_idx = 0; card_idx < global_flags.card_to_mjpeg_stream_export.size(); ++card_idx) { - AVStream *stream = avformat_new_stream(avctx.get(), nullptr); - if (stream == nullptr) { - fprintf(stderr, "avformat_new_stream() failed\n"); - abort(); - } - stream->time_base = AVRational{ 1, OUTPUT_FREQUENCY }; - stream->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; - stream->codecpar->codec_id = AV_CODEC_ID_PCM_S32LE; - stream->codecpar->channel_layout = AV_CH_LAYOUT_STEREO; - stream->codecpar->channels = 2; - stream->codecpar->sample_rate = OUTPUT_FREQUENCY; - } - - AVDictionary *options = NULL; - vector> opts = MUX_OPTS; - for (pair opt : opts) { - av_dict_set(&options, opt.first.c_str(), opt.second.c_str(), 0); - } - if (avformat_write_header(avctx.get(), &options) < 0) { - fprintf(stderr, "avformat_write_header() failed\n"); - abort(); + add_audio_stream(avctx.get()); } + finalize_mux(avctx.get()); // Initialize VA-API. string error; -- 2.39.2