[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