Add support for changing expr on the fly.
Showing the changes to libavfilter (1 of 3 files). Show all
Unified · Split
| @@ -63,6 +63,7 @@ static const char *const var_names[] = { · 62 unmodified lines | |||
| 63 | "S", // Number of samples in the current frame | 63 | "S", // Number of samples in the current frame |
| 64 | "SR", // Audio sample rate | 64 | "SR", // Audio sample rate |
| 65 | "FR", ///< defined only for constant frame-rate video | 65 | "FR", ///< defined only for constant frame-rate video |
| 66 | "T_CHANGE", ///< time of first frame after latest command was applied | ||
| 66 | NULL | 67 | NULL |
| 67 | }; | 68 | }; |
| 68 | 69 | ||
| @@ -90,7 +91,8 @@ enum var_name { · 21 unmodified lines | |||
| 90 | VAR_S, | 91 | VAR_S, |
| 91 | VAR_SR, | 92 | VAR_SR, |
| 92 | VAR_FR, | 93 | VAR_FR, |
| 93 | VAR_VARS_NB | 94 | VAR_T_CHANGE, |
| 95 | VAR_VARS_NB, | ||
| 94 | }; | 96 | }; |
| 95 | 97 | ||
| 96 | typedef struct SetPTSContext { | 98 | typedef struct SetPTSContext { |
| @@ -120,6 +122,7 @@ static av_cold int init(AVFilterContext *ctx) · 23 unmodified lines | |||
| 120 | setpts->var_values[VAR_PREV_OUTT] = NAN; | 122 | setpts->var_values[VAR_PREV_OUTT] = NAN; |
| 121 | setpts->var_values[VAR_STARTPTS] = NAN; | 123 | setpts->var_values[VAR_STARTPTS] = NAN; |
| 122 | setpts->var_values[VAR_STARTT] = NAN; | 124 | setpts->var_values[VAR_STARTT] = NAN; |
| 125 | setpts->var_values[VAR_T_CHANGE] = NAN; | ||
| 123 | return 0; | 126 | return 0; |
| 124 | } | 127 | } |
| 125 | 128 | ||
| @@ -163,6 +166,9 @@ static double eval_pts(SetPTSContext *setpts, AVFilterLink *inlink, AVFrame *fra · 37 unmodified lines | |||
| 163 | setpts->var_values[VAR_STARTPTS] = TS2D(pts); | 166 | setpts->var_values[VAR_STARTPTS] = TS2D(pts); |
| 164 | setpts->var_values[VAR_STARTT ] = TS2T(pts, inlink->time_base); | 167 | setpts->var_values[VAR_STARTT ] = TS2T(pts, inlink->time_base); |
| 165 | } | 168 | } |
| 169 | if (isnan(setpts->var_values[VAR_T_CHANGE])) { | ||
| 170 | setpts->var_values[VAR_T_CHANGE] = TS2T(pts, inlink->time_base); | ||
| 171 | } | ||
| 166 | setpts->var_values[VAR_PTS ] = TS2D(pts); | 172 | setpts->var_values[VAR_PTS ] = TS2D(pts); |
| 167 | setpts->var_values[VAR_T ] = TS2T(pts, inlink->time_base); | 173 | setpts->var_values[VAR_T ] = TS2T(pts, inlink->time_base); |
| 168 | #if FF_API_FRAME_PKT | 174 | #if FF_API_FRAME_PKT |
| @@ -269,14 +275,45 @@ static av_cold void uninit(AVFilterContext *ctx) · 100 unmodified lines | |||
| 269 | setpts->expr = NULL; | 275 | setpts->expr = NULL; |
| 270 | } | 276 | } |
| 271 | 277 | ||
| 278 | static int process_command(AVFilterContext *ctx, const char *cmd, const char *arg, | ||
| 279 | char *res, int res_len, int flags) | ||
| 280 | { | ||
| 281 | SetPTSContext *setpts = ctx->priv; | ||
| 282 | AVExpr *new_expr; | ||
| 283 | int ret; | ||
| 284 | |||
| 285 | ret = ff_filter_process_command(ctx, cmd, arg, res, res_len, flags); | ||
| 286 | |||
| 287 | if (ret < 0) | ||
| 288 | return ret; | ||
| 289 | |||
| 290 | if (!strcmp(cmd, "expr")) { | ||
| 291 | ret = av_expr_parse(&new_expr, arg, var_names, NULL, NULL, NULL, NULL, 0, ctx); | ||
| 292 | // Only free and replace previous expression if new one succeeds, | ||
| 293 | // otherwise defensively keep everything intact even if reporting an error. | ||
| 294 | if (ret < 0) { | ||
| 295 | av_log(ctx, AV_LOG_ERROR, "Error while parsing expression '%s'\n", arg); | ||
| 296 | } else { | ||
| 297 | av_expr_free(setpts->expr); | ||
| 298 | setpts->expr = new_expr; | ||
| 299 | setpts->var_values[VAR_T_CHANGE] = NAN; | ||
| 300 | } | ||
| 301 | } else { | ||
| 302 | ret = AVERROR(EINVAL); | ||
| 303 | } | ||
| 304 | |||
| 305 | return ret; | ||
| 306 | } | ||
| 307 | |||
| 272 | #define OFFSET(x) offsetof(SetPTSContext, x) | 308 | #define OFFSET(x) offsetof(SetPTSContext, x) |
| 273 | #define V AV_OPT_FLAG_VIDEO_PARAM | 309 | #define V AV_OPT_FLAG_VIDEO_PARAM |
| 274 | #define A AV_OPT_FLAG_AUDIO_PARAM | 310 | #define A AV_OPT_FLAG_AUDIO_PARAM |
| 311 | #define R AV_OPT_FLAG_RUNTIME_PARAM | ||
| 275 | #define F AV_OPT_FLAG_FILTERING_PARAM | 312 | #define F AV_OPT_FLAG_FILTERING_PARAM |
| 276 | 313 | ||
| 277 | #if CONFIG_SETPTS_FILTER | 314 | #if CONFIG_SETPTS_FILTER |
| 278 | static const AVOption setpts_options[] = { | 315 | static const AVOption setpts_options[] = { |
| 279 | { "expr", "Expression determining the frame timestamp", OFFSET(expr_str), AV_OPT_TYPE_STRING, { .str = "PTS" }, .flags = V|F }, | 316 | { "expr", "Expression determining the frame timestamp", OFFSET(expr_str), AV_OPT_TYPE_STRING, { .str = "PTS" }, .flags = V|F|R }, |
| 280 | { NULL } | 317 | { NULL } |
| 281 | }; | 318 | }; |
| 282 | AVFILTER_DEFINE_CLASS(setpts); | 319 | AVFILTER_DEFINE_CLASS(setpts); |
| @@ -297,12 +334,13 @@ static const AVFilterPad avfilter_vf_setpts_outputs[] = { · 14 unmodified lines | |||
| 297 | }; | 334 | }; |
| 298 | 335 | ||
| 299 | const AVFilter ff_vf_setpts = { | 336 | const AVFilter ff_vf_setpts = { |
| 300 | .name = "setpts", | 337 | .name = "setpts", |
| 301 | .description = NULL_IF_CONFIG_SMALL("Set PTS for the output video frame."), | 338 | .description = NULL_IF_CONFIG_SMALL("Set PTS for the output video frame."), |
| 302 | .init = init, | 339 | .init = init, |
| 303 | .activate = activate, | 340 | .activate = activate, |
| 304 | .uninit = uninit, | 341 | .uninit = uninit, |
| 305 | .flags = AVFILTER_FLAG_METADATA_ONLY, | 342 | .process_command = process_command, |
| 343 | .flags = AVFILTER_FLAG_METADATA_ONLY, | ||
| 306 | 344 | ||
| 307 | .priv_size = sizeof(SetPTSContext), | 345 | .priv_size = sizeof(SetPTSContext), |
| 308 | .priv_class = &setpts_class, | 346 | .priv_class = &setpts_class, |
| @@ -315,7 +353,7 @@ const AVFilter ff_vf_setpts = { · 6 unmodified lines | |||
| 315 | #if CONFIG_ASETPTS_FILTER | 353 | #if CONFIG_ASETPTS_FILTER |
| 316 | 354 | ||
| 317 | static const AVOption asetpts_options[] = { | 355 | static const AVOption asetpts_options[] = { |
| 318 | { "expr", "Expression determining the frame timestamp", OFFSET(expr_str), AV_OPT_TYPE_STRING, { .str = "PTS" }, .flags = A|F }, | 356 | { "expr", "Expression determining the frame timestamp", OFFSET(expr_str), AV_OPT_TYPE_STRING, { .str = "PTS" }, .flags = A|F|R }, |
| 319 | { NULL } | 357 | { NULL } |
| 320 | }; | 358 | }; |
| 321 | AVFILTER_DEFINE_CLASS(asetpts); | 359 | AVFILTER_DEFINE_CLASS(asetpts); |
| @@ -336,14 +374,15 @@ static const AVFilterPad asetpts_outputs[] = { · 14 unmodified lines | |||
| 336 | }; | 374 | }; |
| 337 | 375 | ||
| 338 | const AVFilter ff_af_asetpts = { | 376 | const AVFilter ff_af_asetpts = { |
| 339 | .name = "asetpts", | 377 | .name = "asetpts", |
| 340 | .description = NULL_IF_CONFIG_SMALL("Set PTS for the output audio frame."), | 378 | .description = NULL_IF_CONFIG_SMALL("Set PTS for the output audio frame."), |
| 341 | .init = init, | 379 | .init = init, |
| 342 | .activate = activate, | 380 | .activate = activate, |
| 343 | .uninit = uninit, | 381 | .uninit = uninit, |
| 344 | .priv_size = sizeof(SetPTSContext), | 382 | .process_command = process_command, |
| 345 | .priv_class = &asetpts_class, | 383 | .priv_size = sizeof(SetPTSContext), |
| 346 | .flags = AVFILTER_FLAG_METADATA_ONLY, | 384 | .priv_class = &asetpts_class, |
| 385 | .flags = AVFILTER_FLAG_METADATA_ONLY, | ||
| 347 | FILTER_INPUTS(asetpts_inputs), | 386 | FILTER_INPUTS(asetpts_inputs), |
| 348 | FILTER_OUTPUTS(asetpts_outputs), | 387 | FILTER_OUTPUTS(asetpts_outputs), |
| 349 | }; | 388 | }; |