[FFmpeg-devel] [PATCH] filters: add the vediting and aediting filters
Federico Simoncelli
federico.simoncelli at gmail.com
Mon Jan 6 15:59:51 CET 2014
The vediting and aediting filters allow the user to select segments
of video and audio to include.
Signed-off-by: Federico Simoncelli <fsimonce at redhat.com>
---
Changelog | 1 +
doc/filters.texi | 23 +++++
libavfilter/Makefile | 2 +
libavfilter/allfilters.c | 2 +
libavfilter/f_editing.c | 259 +++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 287 insertions(+)
create mode 100644 libavfilter/f_editing.c
diff --git a/Changelog b/Changelog
index 2cab110..c45b2fe 100644
--- a/Changelog
+++ b/Changelog
@@ -18,6 +18,7 @@ version <next>
- ATRAC3+ decoder
- VP8 in Ogg demuxing
- side & metadata support in NUT
+- editing filter
version 2.1:
diff --git a/doc/filters.texi b/doc/filters.texi
index a579964..4d0b4f6 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -1787,6 +1787,29 @@ slope
Determine how steep is the filter's shelf transition.
@end table
+ at section vediting, aediting
+
+Select the segments of video and audio to include.
+
+The filter accepts the following options:
+
+ at table @option
+ at item segments
+The segments of the video or audio to include. The format is:
+ at example
+vediting=START_TS1-END_TS1#START_TS2-END_TS2#...
+ at end example
+ at end table
+
+The segments must be monotonically ordered and cannot overlap.
+In general the same segments are declared both for video and
+audio tracks (vediting/aediting) but there's no limitation in
+this respect.
+
+The algorithm used to calculate the output pts is highly
+resistant to damaged streams (e.g. DVB-T) as it reuses the
+input pts preventing the audio/video de-synchronization.
+
@section volume
Adjust the input audio volume.
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 3d587fe..87f8c5a 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -53,6 +53,7 @@ OBJS-$(CONFIG_AVCODEC) += avcodec.o
OBJS-$(CONFIG_ACONVERT_FILTER) += af_aconvert.o
OBJS-$(CONFIG_ADELAY_FILTER) += af_adelay.o
OBJS-$(CONFIG_AECHO_FILTER) += af_aecho.o
+OBJS-$(CONFIG_AEDITING_FILTER) += f_editing.o
OBJS-$(CONFIG_AEVAL_FILTER) += aeval.o
OBJS-$(CONFIG_AFADE_FILTER) += af_afade.o
OBJS-$(CONFIG_AFORMAT_FILTER) += af_aformat.o
@@ -205,6 +206,7 @@ OBJS-$(CONFIG_TINTERLACE_FILTER) += vf_tinterlace.o
OBJS-$(CONFIG_TRANSPOSE_FILTER) += vf_transpose.o
OBJS-$(CONFIG_TRIM_FILTER) += trim.o
OBJS-$(CONFIG_UNSHARP_FILTER) += vf_unsharp.o
+OBJS-$(CONFIG_VEDITING_FILTER) += f_editing.o
OBJS-$(CONFIG_VFLIP_FILTER) += vf_vflip.o
OBJS-$(CONFIG_VIDSTABDETECT_FILTER) += vidstabutils.o vf_vidstabdetect.o
OBJS-$(CONFIG_VIDSTABTRANSFORM_FILTER) += vidstabutils.o vf_vidstabtransform.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index d58e8cc..6640ab6 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -50,6 +50,7 @@ void avfilter_register_all(void)
#endif
REGISTER_FILTER(ADELAY, adelay, af);
REGISTER_FILTER(AECHO, aecho, af);
+ REGISTER_FILTER(AEDITING, aediting, af);
REGISTER_FILTER(AEVAL, aeval, af);
REGISTER_FILTER(AFADE, afade, af);
REGISTER_FILTER(AFORMAT, aformat, af);
@@ -201,6 +202,7 @@ void avfilter_register_all(void)
REGISTER_FILTER(TRANSPOSE, transpose, vf);
REGISTER_FILTER(TRIM, trim, vf);
REGISTER_FILTER(UNSHARP, unsharp, vf);
+ REGISTER_FILTER(VEDITING, vediting, vf);
REGISTER_FILTER(VFLIP, vflip, vf);
REGISTER_FILTER(VIDSTABDETECT, vidstabdetect, vf);
REGISTER_FILTER(VIDSTABTRANSFORM, vidstabtransform, vf);
diff --git a/libavfilter/f_editing.c b/libavfilter/f_editing.c
new file mode 100644
index 0000000..e31d069
--- /dev/null
+++ b/libavfilter/f_editing.c
@@ -0,0 +1,259 @@
+/*
+ * Copyright (c) 2013 Federico Simoncelli <federico.simoncelli at gmail.com>
+ *
+ * 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
+ */
+
+/**
+ * @file
+ * filter for selecting video and audio segments
+ */
+
+#include <string.h>
+
+#include "audio.h"
+#include "video.h"
+#include "libavutil/opt.h"
+
+
+typedef struct _MediaSegment {
+ double start;
+ double end;
+ struct _MediaSegment *next;
+} MediaSegment;
+
+typedef struct {
+ const AVClass *class;
+ char *opt_segments;
+ double ts_base;
+ double ts_prev;
+ int frame_out;
+ MediaSegment *current;
+ MediaSegment *segments;
+} EditingContext;
+
+
+#define FLAGS ( \
+ AV_OPT_FLAG_AUDIO_PARAM | \
+ AV_OPT_FLAG_VIDEO_PARAM | \
+ AV_OPT_FLAG_FILTERING_PARAM \
+)
+#define OFFSET(x) offsetof(EditingContext, x)
+static const AVOption options[] = {
+ { "segments", "set the segment list", OFFSET(opt_segments),
+ AV_OPT_TYPE_STRING, { .str = NULL }, .flags=FLAGS },
+ { NULL }
+};
+
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+ int ret;
+ double frame_in_ts, frame_out_ts;
+ EditingContext *editing = inlink->dst->priv;
+
+ if (!editing->current) /* fast-forward to the end */
+ goto discard;
+
+ frame_in_ts = (double) frame->pts * av_q2d(inlink->time_base);
+
+ if (editing->ts_prev > frame_in_ts) {
+ av_log(inlink->dst, AV_LOG_ERROR, "Frame discontinuity "
+ "error %f\n", editing->ts_prev - frame_in_ts);
+ av_frame_free(&frame);
+ return AVERROR(EINVAL);
+ }
+
+ editing->ts_prev = frame_in_ts;
+ frame_out_ts = editing->ts_base +
+ (frame_in_ts - editing->current->start);
+
+ if (frame_in_ts >= editing->current->end) {
+ editing->current = editing->current->next;
+ editing->ts_base = frame_out_ts;
+ goto discard;
+ }
+
+ if (frame_in_ts <= editing->current->start) {
+ goto discard;
+ }
+
+ frame->pts = frame_out_ts / av_q2d(inlink->time_base);
+
+ ret = ff_filter_frame(inlink->dst->outputs[0], frame);
+ editing->frame_out = (ret == 0) ? 1 : 0;
+
+ return ret;
+
+ discard:
+ av_frame_free(&frame);
+ editing->frame_out = 0;
+ return 0;
+}
+
+static int request_frame(AVFilterLink *outlink)
+{
+ int ret;
+ EditingContext *editing = outlink->src->priv;
+
+ if (!editing->current) /* exit after last segment */
+ return AVERROR_EOF;
+
+ do {
+ ret = ff_request_frame(outlink->src->inputs[0]);
+ if (ret < 0)
+ return ret;
+ } while (!editing->frame_out);
+
+ return 0;
+}
+
+static int parse_segments(AVFilterContext *ctx)
+{
+ char *p, *sp;
+ EditingContext *editing = ctx->priv;
+ MediaSegment *segment, *j = NULL, **i = &editing->segments;
+
+ if (!editing->opt_segments) {
+ av_log(ctx, AV_LOG_ERROR, "Missing segments list\n");
+ return AVERROR(EINVAL);
+ }
+
+ for (p = editing->opt_segments;; p = NULL) {
+ if (!(p = strtok_r(p, "-", &sp)))
+ break;
+
+ segment = av_malloc(sizeof(MediaSegment));
+ segment->next = NULL;
+
+ segment->start = atof(p);
+
+ if (j && segment->start < j->end) {
+ av_log(ctx, AV_LOG_ERROR, "Non-monotonic segments\n");
+ return AVERROR(EINVAL);
+ }
+
+ if (!(p = strtok_r(NULL, "#", &sp))) {
+ av_log(ctx, AV_LOG_ERROR, "Invalid segment list\n");
+ return AVERROR(EINVAL);
+ }
+
+ segment->end = atof(p);
+
+ if (segment->start >= segment->end) {
+ av_log(ctx, AV_LOG_ERROR, "Invalid or empty segment\n");
+ return AVERROR(EINVAL);
+ }
+
+ *i = j = segment, i = &segment->next;
+ }
+
+ return 0;
+}
+
+static av_cold int init(AVFilterContext *ctx)
+{
+ int ret;
+ EditingContext *editing = ctx->priv;
+
+ ret = parse_segments(ctx);
+ if (ret < 0)
+ return ret;
+
+ editing->current = editing->segments;
+ editing->ts_base = 0;
+ editing->ts_prev = 0;
+ editing->frame_out = 0;
+
+ return 0;
+}
+
+static av_cold void uninit(AVFilterContext *ctx) {
+ MediaSegment *n, *i;
+ EditingContext *editing = ctx->priv;
+
+ for (i = editing->segments; i != NULL; i = n) {
+ n = i->next;
+ av_freep(&i);
+ }
+
+ editing->current = editing->segments = NULL;
+}
+
+#define vediting_options options
+AVFILTER_DEFINE_CLASS(vediting);
+
+#define aediting_options options
+AVFILTER_DEFINE_CLASS(aediting);
+
+static const AVFilterPad avfilter_af_editing_inputs[] = {
+ {
+ .name = "default",
+ .type = AVMEDIA_TYPE_AUDIO,
+ .filter_frame = filter_frame,
+ },
+ {NULL}
+};
+
+static const AVFilterPad avfilter_af_editing_outputs[] = {
+ {
+ .name = "default",
+ .type = AVMEDIA_TYPE_AUDIO,
+ .request_frame = request_frame,
+ },
+ {NULL}
+};
+
+AVFilter ff_af_aediting = {
+ .name = "aediting",
+ .description = NULL_IF_CONFIG_SMALL("Select audio segments"),
+ .init = init,
+ .uninit = uninit,
+ .priv_size = sizeof(EditingContext),
+ .priv_class = &aediting_class,
+ .inputs = avfilter_af_editing_inputs,
+ .outputs = avfilter_af_editing_outputs,
+};
+
+static const AVFilterPad avfilter_vf_editing_inputs[] = {
+ {
+ .name = "default",
+ .type = AVMEDIA_TYPE_VIDEO,
+ .filter_frame = filter_frame,
+ },
+ {NULL}
+};
+
+static const AVFilterPad avfilter_vf_editing_outputs[] = {
+ {
+ .name = "default",
+ .type = AVMEDIA_TYPE_VIDEO,
+ .request_frame = request_frame,
+ },
+ {NULL}
+};
+
+AVFilter ff_vf_vediting = {
+ .name = "vediting",
+ .description = NULL_IF_CONFIG_SMALL("Select video segments"),
+ .init = init,
+ .uninit = uninit,
+ .priv_size = sizeof(EditingContext),
+ .priv_class = &vediting_class,
+ .inputs = avfilter_vf_editing_inputs,
+ .outputs = avfilter_vf_editing_outputs,
+};
--
1.8.4.2
More information about the ffmpeg-devel
mailing list