Flag added in a few simple filters. A bunch of other filters can likely
use the feature as well.
... | ... |
@@ -1683,6 +1683,8 @@ static void show_help_filter(const char *name) |
1683 | 1683 |
if (f->priv_class) |
1684 | 1684 |
show_help_children(f->priv_class, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM | |
1685 | 1685 |
AV_OPT_FLAG_AUDIO_PARAM); |
1686 |
+ if (f->flags & AVFILTER_FLAG_SUPPORT_TIMELINE) |
|
1687 |
+ printf("This filter has support for timeline through the 'enable' option.\n"); |
|
1686 | 1688 |
#else |
1687 | 1689 |
av_log(NULL, AV_LOG_ERROR, "Build without libavfilter; " |
1688 | 1690 |
"can not to satisfy request\n"); |
... | ... |
@@ -267,6 +267,36 @@ See the ``Quoting and escaping'' section in the ffmpeg-utils manual |
267 | 267 |
for more information about the escaping and quoting rules adopted by |
268 | 268 |
FFmpeg. |
269 | 269 |
|
270 |
+@chapter Timeline editing |
|
271 |
+ |
|
272 |
+Some filters support a generic @option{enable} option. For the filters |
|
273 |
+supporting timeline editing, this option can be set to an expression which is |
|
274 |
+evaluated before sending a frame to the filter. If the evaluation is non-zero, |
|
275 |
+the filter will be enabled, otherwise the frame will be sent unchanged to the |
|
276 |
+next filter in the filtergraph. |
|
277 |
+ |
|
278 |
+The expression accepts the following values: |
|
279 |
+@table @samp |
|
280 |
+@item t |
|
281 |
+timestamp expressed in seconds, NAN if the input timestamp is unknown |
|
282 |
+ |
|
283 |
+@item n |
|
284 |
+sequential number of the input frame, starting from 0 |
|
285 |
+ |
|
286 |
+@item pos |
|
287 |
+the position in the file of the input frame, NAN if unknown |
|
288 |
+@end table |
|
289 |
+ |
|
290 |
+Like any other filtering option, the @option{enable} option follows the same |
|
291 |
+rules. |
|
292 |
+ |
|
293 |
+For example, to enable a denoiser filter (@ref{hqdn3d}) from 10 seconds to 3 |
|
294 |
+minutes, and a @ref{curves} filter starting at 3 seconds: |
|
295 |
+@example |
|
296 |
+hqdn3d = enable='between(t,10,3*60)', |
|
297 |
+curves = enable='gte(t,3)' : preset=cross_process |
|
298 |
+@end example |
|
299 |
+ |
|
270 | 300 |
@c man end FILTERGRAPH DESCRIPTION |
271 | 301 |
|
272 | 302 |
@chapter Audio Filters |
... | ... |
@@ -2409,6 +2439,7 @@ indicates never reset and return the largest area encountered during |
2409 | 2409 |
playback. |
2410 | 2410 |
@end table |
2411 | 2411 |
|
2412 |
+@anchor{curves} |
|
2412 | 2413 |
@section curves |
2413 | 2414 |
|
2414 | 2415 |
Apply color adjustments using curves. |
... | ... |
@@ -4013,6 +4044,7 @@ ffplay -i input -vf histogram |
4013 | 4013 |
|
4014 | 4014 |
@end itemize |
4015 | 4015 |
|
4016 |
+@anchor{hqdn3d} |
|
4016 | 4017 |
@section hqdn3d |
4017 | 4018 |
|
4018 | 4019 |
High precision/quality 3d denoise filter. This filter aims to reduce |
... | ... |
@@ -23,6 +23,7 @@ |
23 | 23 |
#include "libavutil/avstring.h" |
24 | 24 |
#include "libavutil/channel_layout.h" |
25 | 25 |
#include "libavutil/common.h" |
26 |
+#include "libavutil/eval.h" |
|
26 | 27 |
#include "libavutil/imgutils.h" |
27 | 28 |
#include "libavutil/opt.h" |
28 | 29 |
#include "libavutil/pixdesc.h" |
... | ... |
@@ -483,12 +484,20 @@ static const AVClass *filter_child_class_next(const AVClass *prev) |
483 | 483 |
return NULL; |
484 | 484 |
} |
485 | 485 |
|
486 |
+#define OFFSET(x) offsetof(AVFilterContext, x) |
|
487 |
+#define FLAGS AV_OPT_FLAG_FILTERING_PARAM |
|
488 |
+static const AVOption filters_common_options[] = { |
|
489 |
+ { "enable", "set enable expression", OFFSET(enable_str), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS }, |
|
490 |
+ { NULL } |
|
491 |
+}; |
|
492 |
+ |
|
486 | 493 |
static const AVClass avfilter_class = { |
487 | 494 |
.class_name = "AVFilter", |
488 | 495 |
.item_name = default_filter_name, |
489 | 496 |
.version = LIBAVUTIL_VERSION_INT, |
490 | 497 |
.category = AV_CLASS_CATEGORY_FILTER, |
491 | 498 |
.child_next = filter_child_next, |
499 |
+ .option = filters_common_options, |
|
492 | 500 |
.child_class_next = filter_child_class_next, |
493 | 501 |
}; |
494 | 502 |
|
... | ... |
@@ -618,9 +627,16 @@ void avfilter_free(AVFilterContext *filter) |
618 | 618 |
while(filter->command_queue){ |
619 | 619 |
ff_command_queue_pop(filter); |
620 | 620 |
} |
621 |
+ av_opt_free(filter); |
|
622 |
+ av_expr_free(filter->enable); |
|
623 |
+ filter->enable = NULL; |
|
624 |
+ av_freep(&filter->var_values); |
|
621 | 625 |
av_free(filter); |
622 | 626 |
} |
623 | 627 |
|
628 |
+static const char *const var_names[] = { "t", "n", "pos", NULL }; |
|
629 |
+enum { VAR_T, VAR_N, VAR_POS, VAR_VARS_NB }; |
|
630 |
+ |
|
624 | 631 |
static int process_options(AVFilterContext *ctx, AVDictionary **options, |
625 | 632 |
const char *args) |
626 | 633 |
{ |
... | ... |
@@ -630,6 +646,8 @@ static int process_options(AVFilterContext *ctx, AVDictionary **options, |
630 | 630 |
const char *key; |
631 | 631 |
int offset= -1; |
632 | 632 |
|
633 |
+ av_opt_set_defaults(ctx); |
|
634 |
+ |
|
633 | 635 |
if (!args) |
634 | 636 |
return 0; |
635 | 637 |
|
... | ... |
@@ -665,6 +683,12 @@ static int process_options(AVFilterContext *ctx, AVDictionary **options, |
665 | 665 |
} |
666 | 666 |
|
667 | 667 |
av_log(ctx, AV_LOG_DEBUG, "Setting '%s' to value '%s'\n", key, value); |
668 |
+ |
|
669 |
+ if (av_opt_find(ctx, key, NULL, 0, 0)) { |
|
670 |
+ ret = av_opt_set(ctx, key, value, 0); |
|
671 |
+ if (ret < 0) |
|
672 |
+ return ret; |
|
673 |
+ } else { |
|
668 | 674 |
av_dict_set(options, key, value, 0); |
669 | 675 |
if ((ret = av_opt_set(ctx->priv, key, value, 0)) < 0) { |
670 | 676 |
if (!av_opt_find(ctx->priv, key, NULL, 0, AV_OPT_SEARCH_CHILDREN | AV_OPT_SEARCH_FAKE_OBJ)) { |
... | ... |
@@ -675,11 +699,27 @@ static int process_options(AVFilterContext *ctx, AVDictionary **options, |
675 | 675 |
return ret; |
676 | 676 |
} |
677 | 677 |
} |
678 |
+ } |
|
678 | 679 |
|
679 | 680 |
av_free(value); |
680 | 681 |
av_free(parsed_key); |
681 | 682 |
count++; |
682 | 683 |
} |
684 |
+ |
|
685 |
+ if (ctx->enable_str) { |
|
686 |
+ if (!(ctx->filter->flags & AVFILTER_FLAG_SUPPORT_TIMELINE)) { |
|
687 |
+ av_log(ctx, AV_LOG_ERROR, "Timeline ('enable' option) not supported " |
|
688 |
+ "with filter '%s'\n", ctx->filter->name); |
|
689 |
+ return AVERROR_PATCHWELCOME; |
|
690 |
+ } |
|
691 |
+ ctx->var_values = av_calloc(VAR_VARS_NB, sizeof(*ctx->var_values)); |
|
692 |
+ if (!ctx->var_values) |
|
693 |
+ return AVERROR(ENOMEM); |
|
694 |
+ ret = av_expr_parse((AVExpr**)&ctx->enable, ctx->enable_str, var_names, |
|
695 |
+ NULL, NULL, NULL, NULL, 0, ctx->priv); |
|
696 |
+ if (ret < 0) |
|
697 |
+ return ret; |
|
698 |
+ } |
|
683 | 699 |
return count; |
684 | 700 |
} |
685 | 701 |
|
... | ... |
@@ -852,6 +892,7 @@ static int default_filter_frame(AVFilterLink *link, AVFrame *frame) |
852 | 852 |
static int ff_filter_frame_framed(AVFilterLink *link, AVFrame *frame) |
853 | 853 |
{ |
854 | 854 |
int (*filter_frame)(AVFilterLink *, AVFrame *); |
855 |
+ AVFilterContext *dstctx = link->dst; |
|
855 | 856 |
AVFilterPad *dst = link->dstpad; |
856 | 857 |
AVFrame *out; |
857 | 858 |
int ret; |
... | ... |
@@ -914,6 +955,15 @@ static int ff_filter_frame_framed(AVFilterLink *link, AVFrame *frame) |
914 | 914 |
} |
915 | 915 |
|
916 | 916 |
pts = out->pts; |
917 |
+ if (dstctx->enable_str) { |
|
918 |
+ int64_t pos = av_frame_get_pkt_pos(out); |
|
919 |
+ dstctx->var_values[VAR_N] = link->frame_count; |
|
920 |
+ dstctx->var_values[VAR_T] = pts == AV_NOPTS_VALUE ? NAN : pts * av_q2d(link->time_base); |
|
921 |
+ dstctx->var_values[VAR_POS] = pos == -1 ? NAN : pos; |
|
922 |
+ if (!av_expr_eval(dstctx->enable, dstctx->var_values, NULL)) |
|
923 |
+ filter_frame = dst->passthrough_filter_frame ? dst->passthrough_filter_frame |
|
924 |
+ : default_filter_frame; |
|
925 |
+ } |
|
917 | 926 |
ret = filter_frame(link, out); |
918 | 927 |
link->frame_count++; |
919 | 928 |
link->frame_requested = 0; |
... | ... |
@@ -385,6 +385,19 @@ struct AVFilterPad { |
385 | 385 |
int needs_fifo; |
386 | 386 |
|
387 | 387 |
int needs_writable; |
388 |
+ |
|
389 |
+ /** |
|
390 |
+ * Passthrough filtering callback. |
|
391 |
+ * |
|
392 |
+ * If a filter supports timeline editing (in case |
|
393 |
+ * AVFILTER_FLAG_SUPPORT_TIMELINE is enabled) then it can implement a |
|
394 |
+ * custom passthrough callback to update its local context (for example to |
|
395 |
+ * keep a frame reference, or simply send the filter to a custom outlink). |
|
396 |
+ * The filter must not do any change to the frame in this callback. |
|
397 |
+ * |
|
398 |
+ * Input pads only. |
|
399 |
+ */ |
|
400 |
+ int (*passthrough_filter_frame)(AVFilterLink *link, AVFrame *frame); |
|
388 | 401 |
}; |
389 | 402 |
#endif |
390 | 403 |
|
... | ... |
@@ -428,6 +441,12 @@ enum AVMediaType avfilter_pad_get_type(const AVFilterPad *pads, int pad_idx); |
428 | 428 |
* the options supplied to it. |
429 | 429 |
*/ |
430 | 430 |
#define AVFILTER_FLAG_DYNAMIC_OUTPUTS (1 << 1) |
431 |
+/** |
|
432 |
+ * Some filters support a generic "enable" expression option that can be used |
|
433 |
+ * to enable or disable a filter in the timeline. Filters supporting this |
|
434 |
+ * option have this flag set. |
|
435 |
+ */ |
|
436 |
+#define AVFILTER_FLAG_SUPPORT_TIMELINE (1 << 16) |
|
431 | 437 |
|
432 | 438 |
/** |
433 | 439 |
* Filter definition. This defines the pads a filter contains, and all the |
... | ... |
@@ -522,7 +541,7 @@ typedef struct AVFilter { |
522 | 522 |
|
523 | 523 |
/** An instance of a filter */ |
524 | 524 |
struct AVFilterContext { |
525 |
- const AVClass *av_class; ///< needed for av_log() |
|
525 |
+ const AVClass *av_class; ///< needed for av_log() and filters common options |
|
526 | 526 |
|
527 | 527 |
const AVFilter *filter; ///< the AVFilter of which this is an instance |
528 | 528 |
|
... | ... |
@@ -547,6 +566,10 @@ struct AVFilterContext { |
547 | 547 |
struct AVFilterGraph *graph; ///< filtergraph this filter belongs to |
548 | 548 |
|
549 | 549 |
struct AVFilterCommand *command_queue; |
550 |
+ |
|
551 |
+ char *enable_str; ///< enable expression string |
|
552 |
+ void *enable; ///< parsed expression (AVExpr*) |
|
553 |
+ double *var_values; ///< variable values for the enable expression |
|
550 | 554 |
}; |
551 | 555 |
|
552 | 556 |
/** |