[FFmpeg-devel] [PATCH v2] avfilter/setpts: add command support
Addressed issue with old expr being released regardless of new command succeeding which caused crash on invalid commands. -- -- with best regards Oleg Afanasyev From a50bf9e58a0f90d63aba3c84de40f31dc22ebfce Mon Sep 17 00:00:00 2001 From: Oleg Date: Sat, 29 Apr 2023 19:56:46 +0100 Subject: [PATCH] avfilter/setpts: add command support Add support for changing expr on the fly. Signed-off-by: Oleg --- doc/filters.texi | 7 + libavfilter/setpts.c | 73 +--- 2 files changed, 63 insertions(+), 17 deletions(-) diff --git a/doc/filters.texi b/doc/filters.texi index 50e1682144..fbdb1f8ecf 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -29384,6 +29384,9 @@ The wallclock (RTC) time at the start of the movie in microseconds. @item TB The timebase of the input timestamps. +@item T_CHANGE +Time of the first frame after command was applied or time of the first frame if no commands. + @end table @subsection Examples @@ -29439,6 +29442,10 @@ asetpts=N/SR/TB @end itemize +@subsection Commands + +Both filters support all above options as @ref{commands}. + @section setrange Force color range for the output video frame. diff --git a/libavfilter/setpts.c b/libavfilter/setpts.c index 5bcc0c2dcf..c805a60ee4 100644 --- a/libavfilter/setpts.c +++ b/libavfilter/setpts.c @@ -63,6 +63,7 @@ static const char *const var_names[] = { "S", // Number of samples in the current frame "SR", // Audio sample rate "FR", ///< defined only for constant frame-rate video +"T_CHANGE",///< time of first frame after latest command was applied NULL }; @@ -90,7 +91,8 @@ enum var_name { VAR_S, VAR_SR, VAR_FR, -VAR_VARS_NB +VAR_T_CHANGE, +VAR_VARS_NB, }; typedef struct SetPTSContext { @@ -120,6 +122,7 @@ static av_cold int init(AVFilterContext *ctx) setpts->var_values[VAR_PREV_OUTT] = NAN; setpts->var_values[VAR_STARTPTS]= NAN; setpts->var_values[VAR_STARTT] = NAN; +setpts->var_values[VAR_T_CHANGE]= NAN; return 0; } @@ -163,6 +166,9 @@ static double eval_pts(SetPTSContext *setpts, AVFilterLink *inlink, AVFrame *fra setpts->var_values[VAR_STARTPTS] = TS2D(pts); setpts->var_values[VAR_STARTT ] = TS2T(pts, inlink->time_base); } +if (isnan(setpts->var_values[VAR_T_CHANGE])) { +setpts->var_values[VAR_T_CHANGE] = TS2T(pts, inlink->time_base); +} setpts->var_values[VAR_PTS ] = TS2D(pts); setpts->var_values[VAR_T ] = TS2T(pts, inlink->time_base); #if FF_API_FRAME_PKT @@ -269,14 +275,45 @@ static av_cold void uninit(AVFilterContext *ctx) setpts->expr = NULL; } +static int process_command(AVFilterContext *ctx, const char *cmd, const char *arg, + char *res, int res_len, int flags) +{ +SetPTSContext *setpts = ctx->priv; +AVExpr *new_expr; +int ret; + +ret = ff_filter_process_command(ctx, cmd, arg, res, res_len, flags); + +if (ret < 0) +return ret; + +if (!strcmp(cmd, "expr")) { +ret = av_expr_parse(&new_expr, arg, var_names, NULL, NULL, NULL, NULL, 0, ctx); +// Only free and replace previous expression if new one succeeds, +// otherwise defensively keep everything intact even if reporting an error. +if (ret < 0) { +av_log(ctx, AV_LOG_ERROR, "Error while parsing expression '%s'\n", arg); +} else { +av_expr_free(setpts->expr); +setpts->expr = new_expr; +setpts->var_values[VAR_T_CHANGE] = NAN; +} +} else { +ret = AVERROR(EINVAL); +} + +return ret; +} + #define OFFSET(x) offsetof(SetPTSContext, x) #define V AV_OPT_FLAG_VIDEO_PARAM #define A AV_OPT_FLAG_AUDIO_PARAM +#define R AV_OPT_FLAG_RUNTIME_PARAM #define F AV_OPT_FLAG_FILTERING_PARAM #if CONFIG_SETPTS_FILTER static const AVOption setpts_options[] = { -{ "expr", "Expression determining the frame timestamp", OFFSET(expr_str), AV_OPT_TYPE_STRING, { .str = "PTS" }, .flags = V|F }, +{ "expr", "Expression determining the frame timestamp", OFFSET(expr_str), AV_OPT_TYPE_STRING, { .str = "PTS" }, .flags = V|F|R }, { NULL } }; AVFILTER_DEFINE_CLASS(setpts); @@ -297,12 +334,13 @@ static const AVFilterPad avfilter_vf_setpts_outputs[] = { }; const AVFilter ff_vf_setpts = { -.name = "setpts", -.description = NULL_IF_CONFIG_SMALL("Set PTS for the output video frame."), -.init = init, -.activate = activate, -.uninit= uninit, -.flags = AVFILTER_FLAG_METADATA_ONLY, +.name= "setpts", +.description = NULL_IF_CONFIG_SMALL("Set PTS for
Re: [FFmpeg-devel] [PATCH] avfilter/setpts: add command support
On Sun, 7 May 2023 at 23:47, Stefano Sabatini wrote: > > On date Monday 2023-05-01 22:01:05 +0100, Oleg Afanasyev wrote: > > I'm using setpts to generate timelapses with slowdowns in the middle. > > Using setpts filter requires complicated expr to handle intervals. This > > patch allows commands to change expr and also adds a constant that > > provides time of last command applications to allow specifying gradual > > changes using difference between time and cmd time. > > > > -- > > with best regards > > Oleg Afanasyev > > > From a714a0957a57c1d392feca0ba675ba5ac7c875ee Mon Sep 17 00:00:00 2001 > > From: Oleg > > Date: Sat, 29 Apr 2023 19:56:46 +0100 > > Subject: [PATCH] avfilter/setpts: add command support > > > > Add support for changing expr on the fly. > > > > Signed-off-by: Oleg > > --- > > doc/filters.texi | 7 + > > libavfilter/setpts.c | 68 +--- > > 2 files changed, 58 insertions(+), 17 deletions(-) > > > > diff --git a/doc/filters.texi b/doc/filters.texi > > index 50e1682144..fbdb1f8ecf 100644 > > diff --git a/libavfilter/setpts.c b/libavfilter/setpts.c > > index 5bcc0c2dcf..7b09ce7707 100644 > > --- a/libavfilter/setpts.c > > +++ b/libavfilter/setpts.c > [...] > > +static int process_command(AVFilterContext *ctx, const char *cmd, const > > char *arg, > > + char *res, int res_len, int flags) > > +{ > > +SetPTSContext *setpts = ctx->priv; > > +int ret; > > + > > +ret = ff_filter_process_command(ctx, cmd, arg, res, res_len, flags); > > + > > +if (ret < 0) > > +return ret; > > + > > > +if (!strcmp(cmd, "expr")) { > > +av_expr_free(setpts->expr); > > +ret = av_expr_parse(&setpts->expr, arg, var_names, NULL, NULL, > > NULL, NULL, 0, ctx); > > +if (ret < 0) { > > +av_log(ctx, AV_LOG_ERROR, "Error while parsing expression > > '%s'\n", arg); > > +} > > what happens in case setpts->expr is freed and this fails? > > probably it should keep a reference to expr and remove it only in case > the new expression was successfully parsed Fixed! Didn't realize that encoding continues even if command fails, so it was crashing with the previous expression still in place. > > [...] > > Looks good to me otherwise. > > ___ > ffmpeg-devel mailing list > ffmpeg-devel@ffmpeg.org > https://ffmpeg.org/mailman/listinfo/ffmpeg-devel > > To unsubscribe, visit link above, or email > ffmpeg-devel-requ...@ffmpeg.org with subject "unsubscribe". From a50bf9e58a0f90d63aba3c84de40f31dc22ebfce Mon Sep 17 00:00:00 2001 From: Oleg Date: Sat, 29 Apr 2023 19:56:46 +0100 Subject: [PATCH] avfilter/setpts: add command support Add support for changing expr on the fly. Signed-off-by: Oleg --- doc/filters.texi | 7 + libavfilter/setpts.c | 73 +--- 2 files changed, 63 insertions(+), 17 deletions(-) diff --git a/doc/filters.texi b/doc/filters.texi index 50e1682144..fbdb1f8ecf 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -29384,6 +29384,9 @@ The wallclock (RTC) time at the start of the movie in microseconds. @item TB The timebase of the input timestamps. +@item T_CHANGE +Time of the first frame after command was applied or time of the first frame if no commands. + @end table @subsection Examples @@ -29439,6 +29442,10 @@ asetpts=N/SR/TB @end itemize +@subsection Commands + +Both filters support all above options as @ref{commands}. + @section setrange Force color range for the output video frame. diff --git a/libavfilter/setpts.c b/libavfilter/setpts.c index 5bcc0c2dcf..c805a60ee4 100644 --- a/libavfilter/setpts.c +++ b/libavfilter/setpts.c @@ -63,6 +63,7 @@ static const char *const var_names[] = { "S", // Number of samples in the current frame "SR", // Audio sample rate "FR", ///< defined only for constant frame-rate video +"T_CHANGE",///< time of first frame after latest command was applied NULL }; @@ -90,7 +91,8 @@ enum var_name { VAR_S, VAR_SR, VAR_FR, -VAR_VARS_NB +VAR_T_CHANGE, +VAR_VARS_NB, }; typedef struct SetPTSContext { @@ -120,6 +122,7 @@ static av_cold int init(AVFilterContext *ctx) setpts->var_values[VAR_PREV_OUTT] = NAN; setpts->var_values[VAR_STARTPTS]= NAN; setpts->var_values[VAR_STARTT] = NAN; +setpts->var_values[VAR_T_CHANGE
[FFmpeg-devel] [PATCH] avfilter/setpts: add command support
I'm using setpts to generate timelapses with slowdowns in the middle. Using setpts filter requires complicated expr to handle intervals. This patch allows commands to change expr and also adds a constant that provides time of last command applications to allow specifying gradual changes using difference between time and cmd time. -- with best regards Oleg Afanasyev From a714a0957a57c1d392feca0ba675ba5ac7c875ee Mon Sep 17 00:00:00 2001 From: Oleg Date: Sat, 29 Apr 2023 19:56:46 +0100 Subject: [PATCH] avfilter/setpts: add command support Add support for changing expr on the fly. Signed-off-by: Oleg --- doc/filters.texi | 7 + libavfilter/setpts.c | 68 +--- 2 files changed, 58 insertions(+), 17 deletions(-) diff --git a/doc/filters.texi b/doc/filters.texi index 50e1682144..fbdb1f8ecf 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -29384,6 +29384,9 @@ The wallclock (RTC) time at the start of the movie in microseconds. @item TB The timebase of the input timestamps. +@item T_CHANGE +Time of the first frame after command was applied or time of the first frame if no commands. + @end table @subsection Examples @@ -29439,6 +29442,10 @@ asetpts=N/SR/TB @end itemize +@subsection Commands + +Both filters support all above options as @ref{commands}. + @section setrange Force color range for the output video frame. diff --git a/libavfilter/setpts.c b/libavfilter/setpts.c index 5bcc0c2dcf..7b09ce7707 100644 --- a/libavfilter/setpts.c +++ b/libavfilter/setpts.c @@ -63,6 +63,7 @@ static const char *const var_names[] = { "S", // Number of samples in the current frame "SR", // Audio sample rate "FR", ///< defined only for constant frame-rate video +"T_CHANGE",///< time of first frame after latest command was applied NULL }; @@ -90,7 +91,8 @@ enum var_name { VAR_S, VAR_SR, VAR_FR, -VAR_VARS_NB +VAR_T_CHANGE, +VAR_VARS_NB, }; typedef struct SetPTSContext { @@ -120,6 +122,7 @@ static av_cold int init(AVFilterContext *ctx) setpts->var_values[VAR_PREV_OUTT] = NAN; setpts->var_values[VAR_STARTPTS]= NAN; setpts->var_values[VAR_STARTT] = NAN; +setpts->var_values[VAR_T_CHANGE]= NAN; return 0; } @@ -163,6 +166,9 @@ static double eval_pts(SetPTSContext *setpts, AVFilterLink *inlink, AVFrame *fra setpts->var_values[VAR_STARTPTS] = TS2D(pts); setpts->var_values[VAR_STARTT ] = TS2T(pts, inlink->time_base); } +if (isnan(setpts->var_values[VAR_T_CHANGE])) { +setpts->var_values[VAR_T_CHANGE] = TS2T(pts, inlink->time_base); +} setpts->var_values[VAR_PTS ] = TS2D(pts); setpts->var_values[VAR_T ] = TS2T(pts, inlink->time_base); #if FF_API_FRAME_PKT @@ -269,14 +275,40 @@ static av_cold void uninit(AVFilterContext *ctx) setpts->expr = NULL; } +static int process_command(AVFilterContext *ctx, const char *cmd, const char *arg, + char *res, int res_len, int flags) +{ +SetPTSContext *setpts = ctx->priv; +int ret; + +ret = ff_filter_process_command(ctx, cmd, arg, res, res_len, flags); + +if (ret < 0) +return ret; + +if (!strcmp(cmd, "expr")) { +av_expr_free(setpts->expr); +ret = av_expr_parse(&setpts->expr, arg, var_names, NULL, NULL, NULL, NULL, 0, ctx); +if (ret < 0) { +av_log(ctx, AV_LOG_ERROR, "Error while parsing expression '%s'\n", arg); +} +setpts->var_values[VAR_T_CHANGE] = NAN; +} else { +ret = AVERROR(EINVAL); +} + +return ret; +} + #define OFFSET(x) offsetof(SetPTSContext, x) #define V AV_OPT_FLAG_VIDEO_PARAM #define A AV_OPT_FLAG_AUDIO_PARAM +#define R AV_OPT_FLAG_RUNTIME_PARAM #define F AV_OPT_FLAG_FILTERING_PARAM #if CONFIG_SETPTS_FILTER static const AVOption setpts_options[] = { -{ "expr", "Expression determining the frame timestamp", OFFSET(expr_str), AV_OPT_TYPE_STRING, { .str = "PTS" }, .flags = V|F }, +{ "expr", "Expression determining the frame timestamp", OFFSET(expr_str), AV_OPT_TYPE_STRING, { .str = "PTS" }, .flags = V|F|R }, { NULL } }; AVFILTER_DEFINE_CLASS(setpts); @@ -297,12 +329,13 @@ static const AVFilterPad avfilter_vf_setpts_outputs[] = { }; const AVFilter ff_vf_setpts = { -.name = "setpts", -.description = NULL_IF_CONFIG_SMALL("Set PTS for the output video frame."), -.init = init, -.activate = activate, -.uninit= uninit, -.flags = AVFILTER_FLAG_METADATA_ONLY, +.name= "setpts", +.description = NULL_IF_CONFIG_SMALL("Set PTS for the output video frame."), +