X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=shared%2Fmux.cpp;h=921605574d0a496841bc42215c4573b24faf3170;hb=7092d222bb356549ef453f9c6f0b21123fd8a2ce;hp=4970bcea81bcd684eb019d5484586d888c757a80;hpb=eeda8995329601f9f4e35047358400833eeae68e;p=nageru diff --git a/shared/mux.cpp b/shared/mux.cpp index 4970bce..9216055 100644 --- a/shared/mux.cpp +++ b/shared/mux.cpp @@ -47,21 +47,20 @@ struct PacketBefore { const AVFormatContext * const ctx; }; -Mux::Mux(AVFormatContext *avctx, int width, int height, Codec video_codec, const string &video_extradata, const AVCodecParameters *audio_codecpar, AVColorSpace color_space, WithAudio with_audio, int time_base, function write_callback, WriteStrategy write_strategy, const vector &metrics) +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) { - avstream_video = avformat_new_stream(avctx, nullptr); + AVStream *avstream_video = avformat_new_stream(avctx, nullptr); if (avstream_video == nullptr) { fprintf(stderr, "avformat_new_stream() failed\n"); - exit(1); + abort(); } avstream_video->time_base = AVRational{1, time_base}; avstream_video->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; if (video_codec == CODEC_H264) { avstream_video->codecpar->codec_id = AV_CODEC_ID_H264; - } 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 if (video_codec == CODEC_AV1) { + avstream_video->codecpar->codec_id = AV_CODEC_ID_AV1; } else { assert(video_codec == CODEC_MJPEG); avstream_video->codecpar->codec_id = AV_CODEC_ID_MJPEG; @@ -84,25 +83,38 @@ Mux::Mux(AVFormatContext *avctx, int width, int height, Codec video_codec, const avstream_video->codecpar->field_order = AV_FIELD_PROGRESSIVE; if (!video_extradata.empty()) { - avstream_video->codecpar->extradata = (uint8_t *)av_malloc(video_extradata.size()); + avstream_video->codecpar->extradata = (uint8_t *)av_malloc(video_extradata.size() + AV_INPUT_BUFFER_PADDING_SIZE); avstream_video->codecpar->extradata_size = video_extradata.size(); memcpy(avstream_video->codecpar->extradata, video_extradata.data(), video_extradata.size()); } + streams.push_back(avstream_video); - if (with_audio == WITH_AUDIO) { - avstream_audio = avformat_new_stream(avctx, nullptr); + if (audio_codecpar != nullptr) { + AVStream *avstream_audio = avformat_new_stream(avctx, nullptr); if (avstream_audio == nullptr) { fprintf(stderr, "avformat_new_stream() failed\n"); - exit(1); + abort(); } avstream_audio->time_base = AVRational{1, time_base}; if (avcodec_parameters_copy(avstream_audio->codecpar, audio_codecpar) < 0) { fprintf(stderr, "avcodec_parameters_copy() failed\n"); - exit(1); + abort(); } - } else { - assert(with_audio == WITHOUT_AUDIO); - avstream_audio = nullptr; + streams.push_back(avstream_audio); + } + + if (with_subtitles == WITH_SUBTITLES) { + AVStream *avstream_subtitles = avformat_new_stream(avctx, nullptr); + if (avstream_subtitles == nullptr) { + fprintf(stderr, "avformat_new_stream() failed\n"); + abort(); + } + avstream_subtitles->time_base = AVRational{1, time_base}; + avstream_subtitles->codecpar->codec_type = AVMEDIA_TYPE_SUBTITLE; + avstream_subtitles->codecpar->codec_id = AV_CODEC_ID_WEBVTT; + avstream_subtitles->disposition = AV_DISPOSITION_METADATA; + streams.push_back(avstream_subtitles); + subtitle_stream_idx = streams.size() - 1; } AVDictionary *options = NULL; @@ -110,9 +122,12 @@ Mux::Mux(AVFormatContext *avctx, int width, int height, Codec video_codec, const 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"); - exit(1); + 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; @@ -149,26 +164,22 @@ 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"); - exit(1); + abort(); } if (stream_index_override != -1) { pkt_copy.stream_index = stream_index_override; } - if (pkt_copy.stream_index == 0) { - pkt_copy.pts = av_rescale_q(pts, timebase, avstream_video->time_base); - pkt_copy.dts = av_rescale_q(dts, timebase, avstream_video->time_base); - pkt_copy.duration = av_rescale_q(pkt.duration, timebase, avstream_video->time_base); - } else if (pkt_copy.stream_index == 1) { - pkt_copy.pts = av_rescale_q(pts, timebase, avstream_audio->time_base); - pkt_copy.dts = av_rescale_q(dts, timebase, avstream_audio->time_base); - pkt_copy.duration = av_rescale_q(pkt.duration, timebase, avstream_audio->time_base); - } else { - assert(false); - } + assert(size_t(pkt_copy.stream_index) < streams.size()); + AVRational time_base = streams[pkt_copy.stream_index]->time_base; + pkt_copy.pts = av_rescale_q(pts, timebase, time_base); + pkt_copy.dts = av_rescale_q(dts, timebase, time_base); + pkt_copy.duration = av_rescale_q(pkt.duration, timebase, time_base); { lock_guard lock(mu); @@ -198,9 +209,12 @@ void Mux::write_packet_or_die(const AVPacket &pkt, int64_t unscaled_pts) } } int64_t old_pos = avctx->pb->pos; - if (av_interleaved_write_frame(avctx, const_cast(&pkt)) < 0) { - fprintf(stderr, "av_interleaved_write_frame() failed\n"); - abort(); + int err = av_interleaved_write_frame(avctx, const_cast(&pkt)); + if (err < 0) { + char errbuf[AV_ERROR_MAX_STRING_SIZE]; + av_strerror(err, errbuf, sizeof(errbuf)); + fprintf(stderr, "av_interleaved_write_frame() failed: %s\n", errbuf); + exit(EXIT_FAILURE); } avio_flush(avctx->pb); for (MuxMetrics *metric : metrics) {