X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=shared%2Fmux.cpp;h=adec53de7923171842d90822a7ec8600b9e78fd0;hb=HEAD;hp=0a2eaef6a8310f53b5f7ea0d9a175ea1eb226570;hpb=ee6ece72bc12e5527e114e0e1973f0c0b2dc2138;p=nageru diff --git a/shared/mux.cpp b/shared/mux.cpp index 0a2eaef..a52f21f 100644 --- a/shared/mux.cpp +++ b/shared/mux.cpp @@ -50,6 +50,13 @@ struct PacketBefore { Mux::Mux(AVFormatContext *avctx, int width, int height, Codec video_codec, const string &video_extradata, const AVCodecParameters *audio_codecpar, AVColorSpace color_space, int time_base, function write_callback, WriteStrategy write_strategy, const vector &metrics, WithSubtitles with_subtitles) : write_strategy(write_strategy), avctx(avctx), write_callback(write_callback), metrics(metrics) { + // MPEG-TS ostensibly needs some conversions (e.g. for differing start codes), + // so let FFmpeg insert them as needed in case we are muxing to that. + // Curiously enough, things actually seem to go quite fine without + // (and it also seems FFmpeg's MPEG-TS muxer automatically does stuff like + // repeat PPS/SPS before keyframes for us), but it can't hurt. + avctx->flags |= AVFMT_FLAG_AUTO_BSF; + AVStream *avstream_video = avformat_new_stream(avctx, nullptr); if (avstream_video == nullptr) { fprintf(stderr, "avformat_new_stream() failed\n"); @@ -61,9 +68,6 @@ Mux::Mux(AVFormatContext *avctx, int width, int height, Codec video_codec, const avstream_video->codecpar->codec_id = AV_CODEC_ID_H264; } else if (video_codec == CODEC_AV1) { avstream_video->codecpar->codec_id = AV_CODEC_ID_AV1; - } else if (video_codec == CODEC_NV12) { - avstream_video->codecpar->codec_id = AV_CODEC_ID_RAWVIDEO; - avstream_video->codecpar->codec_tag = avcodec_pix_fmt_to_codec_tag(AV_PIX_FMT_NV12); } else { assert(video_codec == CODEC_MJPEG); avstream_video->codecpar->codec_id = AV_CODEC_ID_MJPEG; @@ -120,27 +124,10 @@ Mux::Mux(AVFormatContext *avctx, int width, int height, Codec video_codec, const subtitle_stream_idx = streams.size() - 1; } - AVDictionary *options = NULL; - vector> opts = MUX_OPTS; - for (pair opt : opts) { - av_dict_set(&options, opt.first.c_str(), opt.second.c_str(), 0); - } - int err = avformat_write_header(avctx, &options); - if (err < 0) { - char errbuf[AV_ERROR_MAX_STRING_SIZE]; - av_strerror(err, errbuf, sizeof(errbuf)); - fprintf(stderr, "avformat_write_header() failed: %s\n", errbuf); - exit(EXIT_FAILURE); - } - for (MuxMetrics *metric : metrics) { - metric->metric_written_bytes += avctx->pb->pos; - } - - // Make sure the header is written before the constructor exits. - avio_flush(avctx->pb); - if (write_strategy == WRITE_BACKGROUND) { writer_thread = thread(&Mux::thread_func, this); + } else { + write_header(); } } @@ -167,8 +154,9 @@ Mux::~Mux() void Mux::add_packet(const AVPacket &pkt, int64_t pts, int64_t dts, AVRational timebase, int stream_index_override) { + assert(pts >= dts); + AVPacket pkt_copy; - av_init_packet(&pkt_copy); if (av_packet_ref(&pkt_copy, &pkt) < 0) { fprintf(stderr, "av_copy_packet() failed\n"); abort(); @@ -184,7 +172,9 @@ void Mux::add_packet(const AVPacket &pkt, int64_t pts, int64_t dts, AVRational t { lock_guard lock(mu); - if (write_strategy == WriteStrategy::WRITE_BACKGROUND) { + if (drained) { + // Just drop the packet on the floor. + } else if (write_strategy == WriteStrategy::WRITE_BACKGROUND) { packet_queue.push_back(QueuedPacket{ av_packet_clone(&pkt_copy), pts }); if (plug_count == 0) packet_queue_ready.notify_all(); @@ -230,6 +220,7 @@ void Mux::write_packet_or_die(const AVPacket &pkt, int64_t unscaled_pts) void Mux::plug() { lock_guard lock(mu); + assert(!drained); ++plug_count; } @@ -254,10 +245,31 @@ void Mux::unplug() } } +void Mux::drain() +{ + lock_guard lock(mu); + assert(!drained); + assert(plug_count == 0); + for (QueuedPacket &qp : packet_queue) { + av_packet_free(&qp.pkt); + } + packet_queue.clear(); + drained = true; +} + +void Mux::undrain() +{ + lock_guard lock(mu); + assert(drained); + drained = false; +} + void Mux::thread_func() { pthread_setname_np(pthread_self(), "Mux"); + write_header(); + unique_lock lock(mu); for ( ;; ) { packet_queue_ready.wait(lock, [this]() { @@ -281,6 +293,31 @@ void Mux::thread_func() } } +void Mux::write_header() +{ + AVDictionary *options = NULL; + vector> opts = MUX_OPTS; + for (pair opt : opts) { + av_dict_set(&options, opt.first.c_str(), opt.second.c_str(), 0); + } + + int err = avformat_write_header(avctx, &options); + if (err < 0) { + char errbuf[AV_ERROR_MAX_STRING_SIZE]; + av_strerror(err, errbuf, sizeof(errbuf)); + fprintf(stderr, "avformat_write_header() failed: %s\n", errbuf); + exit(EXIT_FAILURE); + } + for (MuxMetrics *metric : metrics) { + metric->metric_written_bytes += avctx->pb->pos; + } + + // Make sure the header is written before the constructor exits + // (assuming we are in WRITE_FOREGROUND mode). + avio_flush(avctx->pb); + +} + void MuxMetrics::init(const vector> &labels) { vector> labels_video = labels;