diff options
author | Marton Balint <cus@passwd.hu> | 2018-06-29 00:03:04 +0200 |
---|---|---|
committer | Marton Balint <cus@passwd.hu> | 2018-09-09 21:21:42 +0200 |
commit | 43d3b61abeb7bf35dfbe33f790c1cca0b628c97f (patch) | |
tree | 965cea71f0212106377303e866dcf1c76ab77d9b | |
parent | 4737387d288d1e87e7c7df6203a42d6b1e88231e (diff) | |
download | ffmpeg-43d3b61abeb7bf35dfbe33f790c1cca0b628c97f.tar.gz |
avfilter/f_cue: add cue and acue filters
To delay filtering until a given wallclock timestamp.
Signed-off-by: Marton Balint <cus@passwd.hu>
-rw-r--r-- | Changelog | 1 | ||||
-rw-r--r-- | doc/filters.texi | 36 | ||||
-rw-r--r-- | libavfilter/Makefile | 2 | ||||
-rw-r--r-- | libavfilter/allfilters.c | 2 | ||||
-rw-r--r-- | libavfilter/f_cue.c | 182 | ||||
-rw-r--r-- | libavfilter/version.h | 2 |
6 files changed, 224 insertions, 1 deletions
@@ -23,6 +23,7 @@ version <next>: - WinCam Motion Video decoder - 1D LUT filter (lut1d) - RemotelyAnywhere Screen Capture decoder +- cue and acue filters version 4.0: diff --git a/doc/filters.texi b/doc/filters.texi index 37e79d34e1..5ccd6e0863 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -551,6 +551,11 @@ Set LFO range. Set LFO rate. @end table +@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 +@anchor{cue} +@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 +@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. + +@table @option + +@item cue +The cue timestamp expressed in a UNIX timestamp in microseconds. Default is 0. + +@item preroll +The duration of content to pass on as preroll expressed in seconds. Default is 0. + +@item buffer +The maximum duration of content to buffer before waiting for the cue expressed +in seconds. Default is 0. + +@end table + @anchor{curves} @section curves diff --git a/libavfilter/Makefile b/libavfilter/Makefile index e412000c8f..433d03fd5c 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 2fa9460335..712530c8d6 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 2ff2b6a318..ef982339d7 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 27 +#define LIBAVFILTER_VERSION_MINOR 28 #define LIBAVFILTER_VERSION_MICRO 100 #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \ |