| @@ -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 | }; |