From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100]) by master.gitmailbox.com (Postfix) with ESMTPS id C908B4D210 for ; Tue, 18 Feb 2025 13:10:54 +0000 (UTC) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id B338768C260; Tue, 18 Feb 2025 15:08:55 +0200 (EET) Received: from mail-pl1-f174.google.com (mail-pl1-f174.google.com [209.85.214.174]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id C665F68C1EB for ; Tue, 18 Feb 2025 15:08:47 +0200 (EET) Received: by mail-pl1-f174.google.com with SMTP id d9443c01a7336-220ca204d04so73116745ad.0 for ; Tue, 18 Feb 2025 05:08:47 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1739884125; x=1740488925; darn=ffmpeg.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:from:to:cc:subject:date:message-id :reply-to; bh=rmht7YmxHnd7JcXUoIyOgDYnzkPfpZMRjH2vYgCQwfQ=; b=nawf2Fzq0n/SLLeqdFScy2PlZMFBmWl6+lSdfv37JhYK0rRophOaLH/OEvCo4eNcHl 04Ax2BS6wkCeC5MlqHNCOJlmRhwkudiy1KnpXKQp1Ao9mqrDVnjQsC3DomH+ong1kVyB q9fS1p+bOGTHJ0sqVbn8+s50pga3MOg8jglgie+vUUyrRr48KXQ7O95o3WlRedvAB0D8 bAFjTTaY4qHUuPGQse7seRPHHqIQIlXw0AUKK7+F8Su3sDZHl/k1Bj/cPU9OdjcURtNO G54yv9hnoQqC/2dw6d0pKjgVy78Owj1p25VJEGXTGkvle+skV4oMTJZyNmUlDcfhk2Sz SMfA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1739884125; x=1740488925; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=rmht7YmxHnd7JcXUoIyOgDYnzkPfpZMRjH2vYgCQwfQ=; b=xJj1JZvpsktthg9rnAMjYi2/0XSccgCKfjKDAA7pTX2sPCjMCi8biLU7/7rJilLuRU BWoxz1ZE01Oq7jQOG368d9uKsEncDzyBybVBSQoXZKWlAH91AYzyUrE/8wSiM6ZBnmu8 brGiCn6vxMjZTPtVm/3RlR4szE8IU+KEuC4CZtjVMTkGQLFXu13yU5X736Uq/LacuJCY RGqoRFRNu5Mq9W1ykHLB4hbyiC042lF6y9NhCZUPGdjx0RSM1ax7NhNXCrpAmEFaJUVv LjHUp/OQup0FL0bTKE+8gZBNHlJavwh2nGdU3BlAhKYMbiVKU0ZDqdcb5+tvSQ0+EtAV TkfA== X-Gm-Message-State: AOJu0Yyr2qa8MgmiR4Wmd81fAd3Q15emsxJdW5URmhspTVRcXybx+GgB KlQtMpLNZwB9oB8yQsWRf5NXF/PK+mj68woWoN0jRT1F1Hu5sI3521adklr6 X-Gm-Gg: ASbGncu8oVYfm6xg1bfauqAFdjqab0Yu42dJSCcvWl/GmEQuLyjwg+pmJMWTXJjvyjQ QUsDC5YlhAvJBzUy45FVxYM2Btpd0Ln4g99xPCfsQgnC4CryGbcXApp5jiLKXwQ3aXcgN6ylEEt Y0KJYyIzV2+Ruyx6TxoP84gQEzUMH5ZDHynCiQMGvuWL9cY/RLktJ1Mi9dSTZ57y7P+LhrKFtV5 ZyDR4piGxOLkaBK7swNt05H69TSUlpEZZ1xDcRqyV84rfX50PZlixO6I6DBB0En+LTzLqCYGivt 4j2/0nnFngMtJaMM+7w9jySxfSwIoQ== X-Google-Smtp-Source: AGHT+IEXVkEwWK6ypofZgXUHaeGAqjX3k1VADr1GSNM8e8DVs0pXIVehQ4s7heMais5LK+AVsH04yA== X-Received: by 2002:a17:902:e88d:b0:220:fb23:48e0 with SMTP id d9443c01a7336-2210408f29bmr244653495ad.38.1739884125112; Tue, 18 Feb 2025 05:08:45 -0800 (PST) Received: from localhost.localdomain ([2800:2121:b040:c:a0a7:974:71c7:ca89]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-220d556d66fsm89178635ad.180.2025.02.18.05.08.43 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 18 Feb 2025 05:08:44 -0800 (PST) From: James Almer To: ffmpeg-devel@ffmpeg.org Date: Tue, 18 Feb 2025 10:08:13 -0300 Message-ID: <20250218130813.74-11-jamrial@gmail.com> X-Mailer: git-send-email 2.48.1 In-Reply-To: <20250218130813.74-1-jamrial@gmail.com> References: <20250218130813.74-1-jamrial@gmail.com> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH 11/11] fftools/ffmpeg: support reinitializing the encoder X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" Archived-At: List-Archive: List-Post: With this, an encoding process can change certain values that until now were settable during init only. A graceful reconfiguration will be attempted first, which depends on the capabilities of the underlying encoder. If that fails, a full encoder restart is performed instead. The reconfiguration parameters are passed as a list of target timestamp and key=value pairs to be applied at that point during encoding. Future improvements may also include targetting encoded frame number, and extending the command feature to also support passing encoder runtime arguments and not just filter runtime change arguments. Example: ffmpeg -i INPUT -reinit_opts:v:0 "pts=100000|crf=18,pts=200000|video_size=1280x720:crf=20" -crf 30 -c:v:0 libx264 OUTPUT Signed-off-by: James Almer --- doc/ffmpeg.texi | 5 ++ fftools/ffmpeg.c | 4 ++ fftools/ffmpeg.h | 10 +++ fftools/ffmpeg_enc.c | 141 +++++++++++++++++++++++++++++++++++++- fftools/ffmpeg_filter.c | 129 +++++++++++++++++++++++++++++++++- fftools/ffmpeg_mux_init.c | 16 ++++- fftools/ffmpeg_opt.c | 5 ++ 7 files changed, 303 insertions(+), 7 deletions(-) diff --git a/doc/ffmpeg.texi b/doc/ffmpeg.texi index da6549f043..0e98838b44 100644 --- a/doc/ffmpeg.texi +++ b/doc/ffmpeg.texi @@ -1209,6 +1209,11 @@ ffmpeg -i input.iamf -c:a copy -stream_group map=0=0:st=0:st=1:st=2:st=3 -stream -streamid 0:0 -streamid 1:1 -streamid 2:2 -streamid 3:3 output.mp4 @end example +@item -reinit_opts[:@var{stream_specifier}] pts=@var{pts}|@var{key}=@var{value}[:@var{key}=@var{value},pts=@var{pts}...] (@emph{output,per-stream}) + +Creates a "," separated group of options to reconfigure an encoder at the +specified output @var{pts} in AV_TIME_BASE units. + @item -target @var{type} (@emph{output}) Specify target file type (@code{vcd}, @code{svcd}, @code{dvd}, @code{dv}, @code{dv50}). @var{type} may be prefixed with @code{pal-}, @code{ntsc-} or diff --git a/fftools/ffmpeg.c b/fftools/ffmpeg.c index dc321fb4a2..8a19aeebf6 100644 --- a/fftools/ffmpeg.c +++ b/fftools/ffmpeg.c @@ -394,6 +394,7 @@ static void frame_data_free(void *opaque, uint8_t *data) FrameData *fd = (FrameData *)data; avcodec_parameters_free(&fd->par_enc); + av_dict_free(&fd->reinit_opts); av_free(data); } @@ -422,6 +423,7 @@ static int frame_data_ensure(AVBufferRef **dst, int writable) memcpy(fd, fd_src, sizeof(*fd)); fd->par_enc = NULL; + fd->reinit_opts = NULL; if (fd_src->par_enc) { int ret = 0; @@ -430,6 +432,8 @@ static int frame_data_ensure(AVBufferRef **dst, int writable) ret = fd->par_enc ? avcodec_parameters_copy(fd->par_enc, fd_src->par_enc) : AVERROR(ENOMEM); + if (!ret && fd_src->reinit_opts) + ret = av_dict_copy(&fd->reinit_opts, fd_src->reinit_opts, 0); if (ret < 0) { av_buffer_unref(dst); av_buffer_unref(&src); diff --git a/fftools/ffmpeg.h b/fftools/ffmpeg.h index af76d81a10..061898dd5f 100644 --- a/fftools/ffmpeg.h +++ b/fftools/ffmpeg.h @@ -248,6 +248,7 @@ typedef struct OptionsContext { SpecifierOptList enc_time_bases; SpecifierOptList autoscale; SpecifierOptList bits_per_raw_sample; + SpecifierOptList enc_reinit_opts; SpecifierOptList enc_stats_pre; SpecifierOptList enc_stats_post; SpecifierOptList mux_stats; @@ -341,6 +342,8 @@ typedef struct OutputFilterOptions { const enum AVColorSpace *color_spaces; const enum AVColorRange *color_ranges; + const char *reinit_opts; + // for simple filtergraphs only, view specifier passed // along to the decoder const ViewSpecifier *vs; @@ -567,6 +570,11 @@ typedef struct Encoder { AVCodecContext *enc_ctx; + // initial encoder options + AVDictionary *encoder_opts; + // pts|key=value list of options to reinitialize encoder + char *reinit_opts; + uint32_t codec_tag; int flags; int global_quality; @@ -676,6 +684,8 @@ typedef struct FrameData { int64_t wallclock[LATENCY_PROBE_NB]; AVCodecParameters *par_enc; + + AVDictionary *reinit_opts; } FrameData; extern InputFile **input_files; diff --git a/fftools/ffmpeg_enc.c b/fftools/ffmpeg_enc.c index 32b41ea51c..c657238072 100644 --- a/fftools/ffmpeg_enc.c +++ b/fftools/ffmpeg_enc.c @@ -31,6 +31,7 @@ #include "libavutil/intreadwrite.h" #include "libavutil/log.h" #include "libavutil/mem.h" +#include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "libavutil/rational.h" #include "libavutil/time.h" @@ -49,6 +50,7 @@ typedef struct EncoderPriv { // number of packets received from the encoder uint64_t packets_encoded; + int got_first_packet; int opened; int attach_par; @@ -78,6 +80,7 @@ void enc_free(Encoder **penc) if (enc->enc_ctx) av_freep(&enc->enc_ctx->stats_in); avcodec_free_context(&enc->enc_ctx); + av_dict_free(&enc->encoder_opts); av_freep(penc); } @@ -96,6 +99,29 @@ static const AVClass enc_class = { .item_name = enc_item_name, }; +static int enc_realloc(Encoder *enc, const AVCodec *codec) +{ + EncoderPriv *ep = ep_from_enc(enc); + char *stats_in = NULL; + + if (enc->enc_ctx) + stats_in = enc->enc_ctx->stats_in; + avcodec_free_context(&enc->enc_ctx); + + ep->opened = 0; + ep->got_first_packet = 0; + + enc->enc_ctx = avcodec_alloc_context3(codec); + if (!enc->enc_ctx) { + av_freep(&stats_in); + return AVERROR(ENOMEM); + } + + enc->enc_ctx->stats_in = stats_in; + + return 0; +} + int enc_alloc(Encoder **penc, const AVCodec *codec, Scheduler *sch, unsigned sch_idx, void *log_parent) { @@ -181,7 +207,26 @@ static int hw_device_setup_for_encode(Encoder *e, AVCodecContext *enc_ctx, return 0; } -static int enc_reopen(void *opaque, const AVFrame *frame) +static int apply_enc_options(Encoder *e, AVDictionary **opts) +{ + AVCodecContext *enc_ctx = e->enc_ctx; + + int ret = av_opt_set_dict2(enc_ctx, opts, AV_OPT_SEARCH_CHILDREN); + if (ret < 0) { + av_log(e, AV_LOG_ERROR, "Error applying encoder options: %s\n", + av_err2str(ret)); + return ret; + } + + ret = check_avoptions(*opts); + if (ret < 0) + return ret; + + return 0; +} + +static int enc_reopen(void *opaque, const AVFrame *frame, + AVDictionary **extra_encoder_opts) { OutputStream *ost = opaque; InputStream *ist = ost->ist; @@ -190,9 +235,32 @@ static int enc_reopen(void *opaque, const AVFrame *frame) AVCodecContext *enc_ctx = e->enc_ctx; Decoder *dec = NULL; const AVCodec *enc = enc_ctx->codec; + AVDictionary *encoder_opts = NULL; FrameData *fd; + int threads_manual; int ret; + ret = av_dict_copy(&encoder_opts, ost->enc->encoder_opts, 0); + if (ret < 0) + return ret; + + threads_manual = !!av_dict_get(encoder_opts, "threads", NULL, 0); + ret = apply_enc_options(e, &encoder_opts); + av_dict_free(&encoder_opts); + if (ret < 0) + return ret; + + if (extra_encoder_opts) { + threads_manual |= !!av_dict_get(*extra_encoder_opts, "threads", NULL, 0); + ret = apply_enc_options(e, extra_encoder_opts); + if (ret < 0) + return ret; + } + + // default to automatic thread count + if (!threads_manual) + enc_ctx->thread_count = 0; + // frame is always non-NULL for audio and video av_assert0(frame || (enc->type != AVMEDIA_TYPE_VIDEO && enc->type != AVMEDIA_TYPE_AUDIO)); @@ -364,7 +432,7 @@ int enc_open(void *opaque, const AVFrame *frame) if (ep->opened) return 0; - ret = enc_reopen(opaque, frame); + ret = enc_reopen(opaque, frame, NULL); if (ret < 0) return ret; @@ -696,9 +764,19 @@ static int encode_frame(OutputFile *of, OutputStream *ost, AVFrame *frame, return AVERROR(ENOMEM); fd->wallclock[LATENCY_PROBE_ENC_POST] = av_gettime_relative(); + // attach extradata to first packet if the encoder was reinitialized + if (!ep->got_first_packet && ep->packets_encoded && enc->extradata_size) { + uint8_t *extradata = av_packet_new_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA, + enc->extradata_size); + if (!extradata) + return AVERROR(ENOMEM); + memcpy(extradata, enc->extradata, enc->extradata_size); + ep->got_first_packet = 1; + } // attach stream parameters to first packet if requested avcodec_parameters_free(&fd->par_enc); - if (ep->attach_par && !ep->packets_encoded) { + if (!ep->packets_encoded) { + if (ep->attach_par) { fd->par_enc = avcodec_parameters_alloc(); if (!fd->par_enc) return AVERROR(ENOMEM); @@ -706,6 +784,8 @@ static int encode_frame(OutputFile *of, OutputStream *ost, AVFrame *frame, ret = avcodec_parameters_from_context(fd->par_enc, enc); if (ret < 0) return ret; + } + ep->got_first_packet = 1; } pkt->flags |= AV_PKT_FLAG_TRUSTED; @@ -880,12 +960,57 @@ static int flush_encoder(OutputStream *ost, EncoderThread *et) return ret; } +static int reinit_encoder(OutputStream *ost, EncoderThread *et) +{ + Encoder *e = ost->enc; + AVDictionary *copy = NULL; + const FrameData *fd = frame_data_c(et->frame); + int ret = AVERROR_BUG; + + // Lets try a graceful reconfiguration first + if (e->enc_ctx->codec->capabilities & AV_CODEC_CAP_RECONF) { + ret = av_dict_copy(©, fd->reinit_opts, 0); + if (ret < 0) + return ret; + + ret = avcodec_encode_reconfigure(e->enc_ctx, ©); + av_dict_free(©); + if (!ret) + return 0; + + av_log(e, AV_LOG_INFO, "Could not reconfigure the encoder." + " Trying to restart it instead\n"); + } + ret = av_dict_copy(©, fd->reinit_opts, 0); + if (ret < 0) + return ret; + + // Go ahead and do a full restart of the encoder + ret = flush_encoder(ost, et); + if (ret < 0 && ret != AVERROR_EOF) + goto end; + + ret = enc_realloc(e, e->enc_ctx->codec); + if (ret < 0) + goto end; + av_log(e, AV_LOG_DEBUG, "Restarting encoder\n"); + ret = enc_reopen(ost, et->frame, ©); + if (ret < 0) + goto end; + + ret = 0; +end: + av_dict_free(©); + return ret; +} + int encoder_thread(void *arg) { OutputStream *ost = arg; Encoder *e = ost->enc; EncoderPriv *ep = ep_from_enc(e); EncoderThread et; + const FrameData *fd; int ret = 0, input_status = 0; int name_set = 0; @@ -929,6 +1054,16 @@ int encoder_thread(void *arg) name_set = 1; } + fd = frame_data_c(et.frame); + if (fd && fd->reinit_opts) { + ret = reinit_encoder(ost, &et); + if (ret < 0) { + av_log(e, AV_LOG_ERROR, "Error reconfiguring or restarting encoder: %s\n", + av_err2str(ret)); + goto finish; + } + } + ret = frame_encode(ost, et.frame, et.pkt); av_packet_unref(et.pkt); diff --git a/fftools/ffmpeg_filter.c b/fftools/ffmpeg_filter.c index 09086ca67c..23e6da8902 100644 --- a/fftools/ffmpeg_filter.c +++ b/fftools/ffmpeg_filter.c @@ -192,6 +192,11 @@ typedef struct FPSConvContext { int framerate_clip; } FPSConvContext; +typedef struct ReinitOpts { + int64_t pts; + AVDictionary *dict; +} ReinitOpts; + typedef struct OutputFilterPriv { OutputFilter ofilter; @@ -243,6 +248,9 @@ typedef struct OutputFilterPriv { int64_t next_pts; FPSConvContext fps; + AVFifo *reinit_opts_fifo; + ReinitOpts reinit_opts; + unsigned flags; } OutputFilterPriv; @@ -674,6 +682,7 @@ static OutputFilter *ofilter_alloc(FilterGraph *fg, enum AVMediaType type) ofilter->graph = fg; ofilter->type = type; av_opt_set_defaults(ofp); + ofp->reinit_opts = (ReinitOpts){ .pts = AV_NOPTS_VALUE }; ofp->index = fg->nb_outputs - 1; snprintf(ofp->log_name, sizeof(ofp->log_name), "%co%d", @@ -806,6 +815,75 @@ static int set_channel_layout(OutputFilterPriv *f, const AVChannelLayout *layout return 0; } +static int parse_reinit_opts(AVFifo **pout, const char *opts, void *logctx) +{ + AVFifo *out; + int ret = AVERROR_BUG; + char *ptr, *str, *substr = NULL; + const char *token; + + str = av_strdup(opts); + if (!str) + return AVERROR(ENOMEM); + + out = av_fifo_alloc2(1, sizeof(ReinitOpts), AV_FIFO_FLAG_AUTO_GROW); + if (!out) { + ret = AVERROR(ENOMEM); + goto end; + } + + token = av_strtok(str, ",", &ptr); + while (token) { + ReinitOpts o = { 0 }; + const char *subtoken; + char *subptr, *endptr; + + substr = av_strdup(token); + if (!substr) { + ret = AVERROR(ENOMEM); + goto end; + } + subtoken = av_strtok(substr, "|", &subptr); + if (subtoken && subptr) { + if (!av_strstart(subtoken, "pts=", &subtoken)) { + av_log(logctx, AV_LOG_ERROR, "Invalid reinit identifier\n"); + ret = AVERROR(ENOMEM); + goto end; + } + o.pts = strtoll(subtoken, &endptr, 0); + if (*endptr || o.pts < 0) { + ret = AVERROR(EINVAL); + goto end; + } + ret = av_dict_parse_string(&o.dict, subptr, "=", ":", 0); + if (ret < 0) { + av_log(logctx, AV_LOG_ERROR, "Error parsing encoder options\n"); + goto end; + } + ret = av_fifo_write(out, &o, 1); + if (ret < 0) { + av_dict_free(&o.dict); + goto end; + } + } else { + ret = AVERROR(EINVAL); + goto end; + } + av_freep(&substr); + if (ptr) + ptr += strspn(ptr, " \n\t\r"); + token = av_strtok(NULL, ",", &ptr); + } + + ret = 0; +end: + *pout = out; + av_free(substr); + av_free(str); + + return ret; +} + int ofilter_bind_enc(OutputFilter *ofilter, unsigned sched_idx_enc, const OutputFilterOptions *opts) { @@ -832,6 +910,12 @@ int ofilter_bind_enc(OutputFilter *ofilter, unsigned sched_idx_enc, if (!ofp->name) return AVERROR(EINVAL); + if (opts->reinit_opts) { + ret = parse_reinit_opts(&ofp->reinit_opts_fifo, opts->reinit_opts, ofilter); + if (ret < 0) + return ret; + } + ret = av_dict_copy(&ofp->sws_opts, opts->sws_opts, 0); if (ret < 0) return ret; @@ -1045,6 +1129,12 @@ void fg_free(FilterGraph **pfg) av_freep(&ofilter->name); av_freep(&ofilter->apad); av_freep(&ofp->name); + av_dict_free(&ofp->reinit_opts.dict); + if (ofp->reinit_opts_fifo) { + while (av_fifo_read(ofp->reinit_opts_fifo, &ofp->reinit_opts, 1) >= 0) + av_dict_free(&ofp->reinit_opts.dict); + av_fifo_freep2(&ofp->reinit_opts_fifo); + } av_channel_layout_uninit(&ofp->ch_layout); av_frame_side_data_free(&ofp->side_data, &ofp->nb_side_data); av_freep(&fg->outputs[j]); @@ -2864,7 +2954,7 @@ static const char *unknown_if_null(const char *str) } static int send_frame(FilterGraph *fg, FilterGraphThread *fgt, - InputFilter *ifilter, AVFrame *frame) + InputFilter *ifilter, AVFrame *frame, int force_reinit) { InputFilterPriv *ifp = ifp_from_ifilter(ifilter); FrameData *fd; @@ -2917,7 +3007,7 @@ static int send_frame(FilterGraph *fg, FilterGraphThread *fgt, } /* (re)init the graph if possible, otherwise buffer the frame and return */ - if (need_reinit || !fgt->graph) { + if (need_reinit || force_reinit || !fgt->graph) { AVFrame *tmp = av_frame_alloc(); if (!tmp) @@ -2961,6 +3051,8 @@ static int send_frame(FilterGraph *fg, FilterGraphThread *fgt, av_bprintf(&reason, "downmix medatata changed, "); if (need_reinit & HWACCEL_CHANGED) av_bprintf(&reason, "hwaccel changed, "); + if (force_reinit) + av_bprintf(&reason, "reinitialization arguments were provided, "); if (reason.len > 1) reason.str[reason.len - 2] = '\0'; // remove last comma av_log(fg, AV_LOG_INFO, "Reconfiguring filter graph%s%s\n", reason.len ? " because " : "", reason.str); @@ -3125,7 +3217,38 @@ static int filter_thread(void *arg) ret = sub2video_frame(ifilter, (fgt.frame->buf[0] || hb_frame) ? fgt.frame : NULL, !fgt.graph); } else if (fgt.frame->buf[0]) { - ret = send_frame(fg, &fgt, ifilter, fgt.frame); + int reinit = 0; + for (unsigned i = 0; i < fg->nb_outputs; i++) { + OutputFilterPriv *ofp = ofp_from_ofilter(fg->outputs[i]); + + if (ofp->reinit_opts.pts == AV_NOPTS_VALUE && ofp->reinit_opts_fifo && + av_fifo_can_read(ofp->reinit_opts_fifo)) { + av_fifo_read(ofp->reinit_opts_fifo, &ofp->reinit_opts, 1); + } + if (ofp->reinit_opts.pts != AV_NOPTS_VALUE && + ofp->reinit_opts.pts == av_rescale_q(fgt.frame->pts, fgt.frame->time_base, AV_TIME_BASE_Q)) { + FrameData *fd = frame_data(fgt.frame); + if (!fd) { + ret = AVERROR(ENOMEM); + goto finish; + } + + av_dict_free(&fd->reinit_opts); + ret = av_dict_copy(&fd->reinit_opts, ofp->reinit_opts.dict, 0); + if (ret < 0) + goto finish; + + av_opt_set_dict(ofp, &ofp->reinit_opts.dict); + av_dict_free(&ofp->reinit_opts.dict); + + if (av_fifo_can_read(ofp->reinit_opts_fifo)) + av_fifo_read(ofp->reinit_opts_fifo, &ofp->reinit_opts, 1); + else + ofp->reinit_opts = (ReinitOpts){ .pts = AV_NOPTS_VALUE }; + reinit = 1; + } + } + ret = send_frame(fg, &fgt, ifilter, fgt.frame, reinit); } else { av_assert1(o == FRAME_OPAQUE_EOF); ret = send_eof(&fgt, ifilter, fgt.frame->pts, fgt.frame->time_base); diff --git a/fftools/ffmpeg_mux_init.c b/fftools/ffmpeg_mux_init.c index 3eff514f7b..bd0fe90c06 100644 --- a/fftools/ffmpeg_mux_init.c +++ b/fftools/ffmpeg_mux_init.c @@ -931,6 +931,7 @@ ost_bind_filter(const Muxer *mux, MuxStream *ms, OutputFilter *ofilter, 0 : mux->of.start_time, .vs = vs, .nb_threads = -1, + .reinit_opts = ost->enc->reinit_opts, .flags = OFILTER_FLAG_DISABLE_CONVERT * !!keep_pix_fmt | OFILTER_FLAG_AUTOSCALE * !!autoscale | @@ -1301,7 +1302,7 @@ static int ost_add(Muxer *mux, const OptionsContext *o, enum AVMediaType type, AVIOContext *s = NULL; char *buf = NULL, *arg = NULL; const char *enc_stats_pre = NULL, *enc_stats_post = NULL, *mux_stats = NULL; - const char *enc_time_base = NULL, *preset = NULL; + const char *enc_time_base = NULL, *enc_reinit_opts = NULL, *preset = NULL; ret = filter_codec_opts(o->g->codec_opts, enc->id, oc, st, enc, &encoder_opts, @@ -1341,6 +1342,16 @@ static int ost_add(Muxer *mux, const OptionsContext *o, enum AVMediaType type, goto fail; } + opt_match_per_stream_str(ost, &o->enc_reinit_opts, oc, st, &enc_reinit_opts); + if (enc_reinit_opts && + (type == AVMEDIA_TYPE_VIDEO || type == AVMEDIA_TYPE_AUDIO)) { + ost->enc->reinit_opts = av_strdup(enc_reinit_opts); + if (!ost->enc->reinit_opts) { + ret = AVERROR(ENOMEM); + goto fail; + } + } + opt_match_per_stream_str(ost, &o->enc_stats_pre, oc, st, &enc_stats_pre); if (enc_stats_pre && (type == AVMEDIA_TYPE_VIDEO || type == AVMEDIA_TYPE_AUDIO)) { @@ -1411,6 +1422,9 @@ static int ost_add(Muxer *mux, const OptionsContext *o, enum AVMediaType type, threads_manual = !!av_dict_get(encoder_opts, "threads", NULL, 0); + ret = av_dict_copy(&ost->enc->encoder_opts, encoder_opts, 0); + if (ret < 0) + goto fail; ret = av_opt_set_dict2(ost->enc->enc_ctx, &encoder_opts, AV_OPT_SEARCH_CHILDREN); if (ret < 0) { av_log(ost, AV_LOG_ERROR, "Error applying encoder options: %s\n", diff --git a/fftools/ffmpeg_opt.c b/fftools/ffmpeg_opt.c index 3c0c682594..76aabb7581 100644 --- a/fftools/ffmpeg_opt.c +++ b/fftools/ffmpeg_opt.c @@ -1788,6 +1788,11 @@ const OptionDef options[] = { { .off = OFFSET(mux_stats_fmt) }, "format of the stats written with -stats_mux_pre" }, + { "reinit_opts", OPT_TYPE_STRING, OPT_PERSTREAM | OPT_EXPERT | OPT_OUTPUT, + { .off = OFFSET(enc_reinit_opts) }, + "List of encoder options to use to reinitialize the encoder at given timestamps", + "pts1|video_size=size:g=12,pts2|video_size=size..." }, + /* video options */ { "vframes", OPT_TYPE_FUNC, OPT_VIDEO | OPT_FUNC_ARG | OPT_PERFILE | OPT_OUTPUT | OPT_EXPERT | OPT_HAS_CANON, { .func_arg = opt_video_frames }, -- 2.48.1 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".