[FFmpeg-devel] [PATCH] avfilter/f_cue: add cue and acue filters

Bodecs Bela bodecsb at vivanet.hu
Sun Sep 2 00:48:49 EEST 2018


Hi Balint,


2018.08.25. 20:35 keltezéssel, Marton Balint írta:
> To delay filtering until a given wallclock timestamp.
>
> Signed-off-by: Marton Balint <cus at passwd.hu>
> ---
>   doc/filters.texi         |  36 ++++++++++
>   libavfilter/Makefile     |   2 +
>   libavfilter/allfilters.c |   2 +
>   libavfilter/f_cue.c      | 182 +++++++++++++++++++++++++++++++++++++++++++++++
>   libavfilter/version.h    |   2 +-
>   5 files changed, 223 insertions(+), 1 deletion(-)
>   create mode 100644 libavfilter/f_cue.c
>
> diff --git a/doc/filters.texi b/doc/filters.texi
> index 32c95b591c..79eec0c808 100644
> --- a/doc/filters.texi
> +++ b/doc/filters.texi
> @@ -551,6 +551,11 @@ Set LFO range.
>   Set LFO rate.
>   @end table
>   
> + at section acue
> +
> +Delay audio filtering until a given wallclock timestamp. See the @ref{cue}
> +filter.
> +
>   @section adeclick
>   Remove impulsive noise from input audio.
>   
> @@ -6987,6 +6992,37 @@ indicates 'never reset', and returns the largest area encountered during
>   playback.
>   @end table
>   
> + at anchor{cue}
> + at section cue
> +
> +Delay video filtering until a given wallclock timestamp. The filter first
> +passes on @option{preroll} amount of frames, then it buffers at most
> + at option{buffer} amount of frames and waits for the cue. After reaching the cue
> +it forwards the buffered frames and also any subsequent frames coming in its
> +input.
> +
> +The filter can be used synchronize the output of multiple ffmpeg processes for
> +realtime output devices like decklink. By putting the delay in the filtering
> +chain and pre-buffering frames the process can pass on data to output almost
> +immediately after the target wallclock timestamp is reached.
> +
> +Perfect frame accuracy cannot be guaranteed, but the result is good enough for
> +some use cases.
just for my curiousity, will you please give an example/use_case how to 
use these filters?

> +
> + at table @option
> +
> + at item cue
> +The cue timestamp expressed in a UNIX timestamp in microseconds. Default is 0.
> +
> + at item preroll
> +The duration of content to pass on as preroll expressed in seconds. Default is 0.
> +
> + at item buffer
> +The maximum duration of content to buffer before waiting for the cue expressed
> +in seconds. Default is 0.
> +
> + at end table
> +
>   @anchor{curves}
>   @section curves
>   
> diff --git a/libavfilter/Makefile b/libavfilter/Makefile
> index e5d3a57af7..37a06e0ec0 100644
> --- a/libavfilter/Makefile
> +++ b/libavfilter/Makefile
> @@ -36,6 +36,7 @@ OBJS-$(CONFIG_ACONTRAST_FILTER)              += af_acontrast.o
>   OBJS-$(CONFIG_ACOPY_FILTER)                  += af_acopy.o
>   OBJS-$(CONFIG_ACROSSFADE_FILTER)             += af_afade.o
>   OBJS-$(CONFIG_ACRUSHER_FILTER)               += af_acrusher.o
> +OBJS-$(CONFIG_ACUE_FILTER)                   += f_cue.o
>   OBJS-$(CONFIG_ADECLICK_FILTER)               += af_adeclick.o
>   OBJS-$(CONFIG_ADECLIP_FILTER)                += af_adeclick.o
>   OBJS-$(CONFIG_ADELAY_FILTER)                 += af_adelay.o
> @@ -178,6 +179,7 @@ OBJS-$(CONFIG_COREIMAGE_FILTER)              += vf_coreimage.o
>   OBJS-$(CONFIG_COVER_RECT_FILTER)             += vf_cover_rect.o lavfutils.o
>   OBJS-$(CONFIG_CROP_FILTER)                   += vf_crop.o
>   OBJS-$(CONFIG_CROPDETECT_FILTER)             += vf_cropdetect.o
> +OBJS-$(CONFIG_CUE_FILTER)                    += f_cue.o
>   OBJS-$(CONFIG_CURVES_FILTER)                 += vf_curves.o
>   OBJS-$(CONFIG_DATASCOPE_FILTER)              += vf_datascope.o
>   OBJS-$(CONFIG_DCTDNOIZ_FILTER)               += vf_dctdnoiz.o
> diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
> index 9732ae5345..6c6d0f43f0 100644
> --- a/libavfilter/allfilters.c
> +++ b/libavfilter/allfilters.c
> @@ -27,6 +27,7 @@ extern AVFilter ff_af_abench;
>   extern AVFilter ff_af_acompressor;
>   extern AVFilter ff_af_acontrast;
>   extern AVFilter ff_af_acopy;
> +extern AVFilter ff_af_acue;
>   extern AVFilter ff_af_acrossfade;
>   extern AVFilter ff_af_acrusher;
>   extern AVFilter ff_af_adeclick;
> @@ -167,6 +168,7 @@ extern AVFilter ff_vf_coreimage;
>   extern AVFilter ff_vf_cover_rect;
>   extern AVFilter ff_vf_crop;
>   extern AVFilter ff_vf_cropdetect;
> +extern AVFilter ff_vf_cue;
>   extern AVFilter ff_vf_curves;
>   extern AVFilter ff_vf_datascope;
>   extern AVFilter ff_vf_dctdnoiz;
> diff --git a/libavfilter/f_cue.c b/libavfilter/f_cue.c
> new file mode 100644
> index 0000000000..732b5e218a
> --- /dev/null
> +++ b/libavfilter/f_cue.c
> @@ -0,0 +1,182 @@
> +/*
> + * Copyright (c) 2018 Marton Balint
> + *
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public License
> + * as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * FFmpeg is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public License
> + * along with FFmpeg; if not, write to the Free Software Foundation, Inc.,
> + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
> + */
> +
> +#include "libavutil/opt.h"
> +#include "libavutil/time.h"
> +#include "avfilter.h"
> +#include "filters.h"
> +#include "framequeue.h"
> +#include "internal.h"
> +
> +typedef struct CueContext {
> +    const AVClass *class;
> +    int64_t first_pts;
> +    int64_t cue;
> +    int64_t preroll;
> +    int64_t buffer;
> +    int status;
> +    FFFrameQueue queue;
> +} CueContext;
> +
> +static av_cold int init(AVFilterContext *ctx)
> +{
> +    CueContext *s = ctx->priv;
> +    ff_framequeue_init(&s->queue, &ctx->graph->internal->frame_queues);
> +    return 0;
> +}
> +
> +static av_cold void uninit(AVFilterContext *ctx)
> +{
> +    CueContext *s = ctx->priv;
> +    ff_framequeue_free(&s->queue);
> +}
> +
> +static int activate(AVFilterContext *ctx)
> +{
> +    AVFilterLink *inlink = ctx->inputs[0];
> +    AVFilterLink *outlink = ctx->outputs[0];
> +    CueContext *s = ctx->priv;
> +    int64_t pts;
> +    AVFrame *frame = NULL;
> +
> +    FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink);
> +
> +    if (s->status < 3 || s->status == 5) {
> +        int ret = ff_inlink_consume_frame(inlink, &frame);
> +        if (ret < 0)
> +            return ret;
> +        if (frame)
> +            pts = av_rescale_q(frame->pts, inlink->time_base, AV_TIME_BASE_Q);
> +    }
> +
> +    if (!s->status && frame) {
> +        s->first_pts = pts;
> +        s->status++;
> +    }
> +    if (s->status == 1 && frame) {
> +        if (pts - s->first_pts < s->preroll)
> +            return ff_filter_frame(outlink, frame);
> +        s->first_pts = pts;
> +        s->status++;
> +    }
> +    if (s->status == 2 && frame) {
> +        int ret = ff_framequeue_add(&s->queue, frame);
> +        if (ret < 0) {
> +            av_frame_free(&frame);
> +            return ret;
> +        }
> +        frame = NULL;
> +        if (!(pts - s->first_pts < s->buffer && (av_gettime() - s->cue) < 0))
> +            s->status++;
> +    }
> +    if (s->status == 3) {
> +        int64_t diff;
> +        while ((diff = (av_gettime() - s->cue)) < 0)
> +            av_usleep(av_clip(-diff / 2, 100, 1000000));
> +        s->status++;
> +    }
> +    if (s->status == 4) {
> +        if (ff_framequeue_queued_frames(&s->queue))
> +            return ff_filter_frame(outlink, ff_framequeue_take(&s->queue));
> +        s->status++;
> +    }
> +    if (s->status == 5 && frame)
> +        return ff_filter_frame(outlink, frame);
> +
> +    FF_FILTER_FORWARD_STATUS(inlink, outlink);
> +    FF_FILTER_FORWARD_WANTED(outlink, inlink);
> +
> +    return FFERROR_NOT_READY;
> +}
> +
> +#define OFFSET(x) offsetof(CueContext, x)
> +#define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_FILTERING_PARAM
> +static const AVOption options[] = {
> +    { "cue", "cue unix timestamp in microseconds", OFFSET(cue), AV_OPT_TYPE_INT64, { .i64 = 0 }, 0, INT64_MAX, FLAGS },
> +    { "preroll", "preroll duration in seconds", OFFSET(preroll), AV_OPT_TYPE_DURATION, { .i64 = 0 }, 0, INT64_MAX, FLAGS },
> +    { "buffer", "buffer duration in seconds", OFFSET(buffer), AV_OPT_TYPE_DURATION, { .i64 = 0 }, 0, INT64_MAX, FLAGS },
> +    { NULL }
> +};
> +
> +#if CONFIG_CUE_FILTER
> +#define cue_options options
> +AVFILTER_DEFINE_CLASS(cue);
> +
> +static const AVFilterPad cue_inputs[] = {
> +    {
> +        .name = "default",
> +        .type = AVMEDIA_TYPE_VIDEO,
> +    },
> +    { NULL }
> +};
> +
> +static const AVFilterPad cue_outputs[] = {
> +    {
> +        .name = "default",
> +        .type = AVMEDIA_TYPE_VIDEO,
> +    },
> +    { NULL }
> +};
> +
> +AVFilter ff_vf_cue = {
> +    .name        = "cue",
> +    .description = NULL_IF_CONFIG_SMALL("Delay filtering to match a cue."),
> +    .priv_size   = sizeof(CueContext),
> +    .priv_class  = &cue_class,
> +    .init        = init,
> +    .uninit      = uninit,
> +    .inputs      = cue_inputs,
> +    .outputs     = cue_outputs,
> +    .activate    = activate,
> +};
> +#endif /* CONFIG_CUE_FILTER */
> +
> +#if CONFIG_ACUE_FILTER
> +#define acue_options options
> +AVFILTER_DEFINE_CLASS(acue);
> +
> +static const AVFilterPad acue_inputs[] = {
> +    {
> +        .name = "default",
> +        .type = AVMEDIA_TYPE_AUDIO,
> +    },
> +    { NULL }
> +};
> +
> +static const AVFilterPad acue_outputs[] = {
> +    {
> +        .name = "default",
> +        .type = AVMEDIA_TYPE_AUDIO,
> +    },
> +    { NULL }
> +};
> +
> +AVFilter ff_af_acue = {
> +    .name        = "acue",
> +    .description = NULL_IF_CONFIG_SMALL("Delay filtering to match a cue."),
> +    .priv_size   = sizeof(CueContext),
> +    .priv_class  = &acue_class,
> +    .init        = init,
> +    .uninit      = uninit,
> +    .inputs      = acue_inputs,
> +    .outputs     = acue_outputs,
> +    .activate    = activate,
> +};
> +#endif /* CONFIG_ACUE_FILTER */
> diff --git a/libavfilter/version.h b/libavfilter/version.h
> index 0ac3a2f3a9..2ff2b6a318 100644
> --- a/libavfilter/version.h
> +++ b/libavfilter/version.h
> @@ -30,7 +30,7 @@
>   #include "libavutil/version.h"
>   
>   #define LIBAVFILTER_VERSION_MAJOR   7
> -#define LIBAVFILTER_VERSION_MINOR  26
> +#define LIBAVFILTER_VERSION_MINOR  27
>   #define LIBAVFILTER_VERSION_MICRO 100
>   
>   #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \
thank you,

bb



More information about the ffmpeg-devel mailing list