[FFmpeg-cvslog] lavfi: add dual input helpers.
Clément Bœsch
git at videolan.org
Mon May 27 20:43:45 CEST 2013
ffmpeg | branch: master | Clément Bœsch <ubitux at gmail.com> | Sat May 25 16:25:46 2013 +0200| [43286028906adab67ec87bc15ff3010d0f6c2ff0] | committer: Clément Bœsch
lavfi: add dual input helpers.
> http://git.videolan.org/gitweb.cgi/ffmpeg.git/?a=commit;h=43286028906adab67ec87bc15ff3010d0f6c2ff0
---
libavfilter/Makefile | 2 +-
libavfilter/dualinput.c | 159 ++++++++++++++++++++++++++++++++++++++++++++++
libavfilter/dualinput.h | 46 ++++++++++++++
libavfilter/vf_overlay.c | 155 ++++++--------------------------------------
4 files changed, 227 insertions(+), 135 deletions(-)
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index f53afb2..9682cba 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -157,7 +157,7 @@ OBJS-$(CONFIG_NOISE_FILTER) += vf_noise.o
OBJS-$(CONFIG_NULL_FILTER) += vf_null.o
OBJS-$(CONFIG_OCV_FILTER) += vf_libopencv.o
OBJS-$(CONFIG_OPENCL) += deshake_opencl.o unsharp_opencl.o
-OBJS-$(CONFIG_OVERLAY_FILTER) += vf_overlay.o
+OBJS-$(CONFIG_OVERLAY_FILTER) += vf_overlay.o dualinput.o
OBJS-$(CONFIG_OWDENOISE_FILTER) += vf_owdenoise.o
OBJS-$(CONFIG_PAD_FILTER) += vf_pad.o
OBJS-$(CONFIG_PERMS_FILTER) += f_perms.o
diff --git a/libavfilter/dualinput.c b/libavfilter/dualinput.c
new file mode 100644
index 0000000..10e3652
--- /dev/null
+++ b/libavfilter/dualinput.c
@@ -0,0 +1,159 @@
+/*
+ * 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
+ */
+
+#define MAIN 0
+#define SECOND 1
+
+#include "dualinput.h"
+#include "libavutil/timestamp.h"
+
+static int try_filter_frame(FFDualInputContext *s,
+ AVFilterContext *ctx, AVFrame *mainpic)
+{
+ int ret;
+
+ /* Discard obsolete second frames: if there is a next second frame with pts
+ * before the main frame, we can drop the current second. */
+ while (1) {
+ AVFrame *next_overpic = ff_bufqueue_peek(&s->queue[SECOND], 0);
+ if (!next_overpic && s->second_eof && !s->repeatlast) {
+ av_frame_free(&s->second_frame);
+ break;
+ }
+ if (!next_overpic || av_compare_ts(next_overpic->pts, ctx->inputs[SECOND]->time_base,
+ mainpic->pts, ctx->inputs[MAIN]->time_base) > 0)
+ break;
+ ff_bufqueue_get(&s->queue[SECOND]);
+ av_frame_free(&s->second_frame);
+ s->second_frame = next_overpic;
+ }
+
+ /* If there is no next frame and no EOF and the second frame is before
+ * the main frame, we can not know yet if it will be superseded. */
+ if (!s->queue[SECOND].available && !s->second_eof &&
+ (!s->second_frame || av_compare_ts(s->second_frame->pts, ctx->inputs[SECOND]->time_base,
+ mainpic->pts, ctx->inputs[MAIN]->time_base) < 0))
+ return AVERROR(EAGAIN);
+
+ /* At this point, we know that the current second frame extends to the
+ * time of the main frame. */
+ av_dlog(ctx, "main_pts:%s main_pts_time:%s",
+ av_ts2str(mainpic->pts), av_ts2timestr(mainpic->pts, &ctx->inputs[MAIN]->time_base));
+ if (s->second_frame)
+ av_dlog(ctx, " second_pts:%s second_pts_time:%s",
+ av_ts2str(s->second_frame->pts), av_ts2timestr(s->second_frame->pts, &ctx->inputs[SECOND]->time_base));
+ av_dlog(ctx, "\n");
+
+ if (s->second_frame && !ctx->is_disabled)
+ mainpic = s->process(ctx, mainpic, s->second_frame);
+ ret = ff_filter_frame(ctx->outputs[0], mainpic);
+ av_assert1(ret != AVERROR(EAGAIN));
+ s->frame_requested = 0;
+ return ret;
+}
+
+static int try_filter_next_frame(FFDualInputContext *s, AVFilterContext *ctx)
+{
+ AVFrame *next_mainpic = ff_bufqueue_peek(&s->queue[MAIN], 0);
+ int ret;
+
+ if (!next_mainpic)
+ return AVERROR(EAGAIN);
+ if ((ret = try_filter_frame(s, ctx, next_mainpic)) == AVERROR(EAGAIN))
+ return ret;
+ ff_bufqueue_get(&s->queue[MAIN]);
+ return ret;
+}
+
+static int flush_frames(FFDualInputContext *s, AVFilterContext *ctx)
+{
+ int ret;
+
+ while (!(ret = try_filter_next_frame(s, ctx)));
+ return ret == AVERROR(EAGAIN) ? 0 : ret;
+}
+
+int ff_dualinput_filter_frame_main(FFDualInputContext *s,
+ AVFilterLink *inlink, AVFrame *in)
+{
+ AVFilterContext *ctx = inlink->dst;
+ int ret;
+
+ if ((ret = flush_frames(s, ctx)) < 0)
+ return ret;
+ if ((ret = try_filter_frame(s, ctx, in)) < 0) {
+ if (ret != AVERROR(EAGAIN))
+ return ret;
+ ff_bufqueue_add(ctx, &s->queue[MAIN], in);
+ }
+
+ if (!s->second_frame)
+ return 0;
+ flush_frames(s, ctx);
+
+ return 0;
+}
+
+int ff_dualinput_filter_frame_second(FFDualInputContext *s,
+ AVFilterLink *inlink, AVFrame *in)
+{
+ AVFilterContext *ctx = inlink->dst;
+ int ret;
+
+ if ((ret = flush_frames(s, ctx)) < 0)
+ return ret;
+ ff_bufqueue_add(ctx, &s->queue[SECOND], in);
+ ret = try_filter_next_frame(s, ctx);
+ return ret == AVERROR(EAGAIN) ? 0 : ret;
+}
+
+int ff_dualinput_request_frame(FFDualInputContext *s, AVFilterLink *outlink)
+{
+ AVFilterContext *ctx = outlink->src;
+ int input, ret;
+
+ if (!try_filter_next_frame(s, ctx))
+ return 0;
+ s->frame_requested = 1;
+ while (s->frame_requested) {
+ /* TODO if we had a frame duration, we could guess more accurately */
+ input = !s->second_eof && (s->queue[MAIN].available ||
+ s->queue[SECOND].available < 2) ?
+ SECOND : MAIN;
+ ret = ff_request_frame(ctx->inputs[input]);
+ /* EOF on main is reported immediately */
+ if (ret == AVERROR_EOF && input == SECOND) {
+ s->second_eof = 1;
+ if (s->shortest)
+ return ret;
+ if ((ret = try_filter_next_frame(s, ctx)) != AVERROR(EAGAIN))
+ return ret;
+ ret = 0; /* continue requesting frames on main */
+ }
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
+void ff_dualinput_uninit(FFDualInputContext *s)
+{
+ av_frame_free(&s->second_frame);
+ ff_bufqueue_discard_all(&s->queue[MAIN]);
+ ff_bufqueue_discard_all(&s->queue[SECOND]);
+}
diff --git a/libavfilter/dualinput.h b/libavfilter/dualinput.h
new file mode 100644
index 0000000..98d0544
--- /dev/null
+++ b/libavfilter/dualinput.h
@@ -0,0 +1,46 @@
+/*
+ * 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
+ * Double input streams helper for filters
+ */
+
+#ifndef AVFILTER_DUALINPUT_H
+#define AVFILTER_DUALINPUT_H
+
+#include <stdint.h>
+#include "bufferqueue.h"
+#include "internal.h"
+
+typedef struct {
+ uint8_t frame_requested;
+ uint8_t second_eof;
+ AVFrame *second_frame;
+ struct FFBufQueue queue[2];
+ AVFrame *(*process)(AVFilterContext *ctx, AVFrame *main, const AVFrame *second);
+ int shortest; ///< terminate stream when the second input terminates
+ int repeatlast; ///< repeat last second frame
+} FFDualInputContext;
+
+int ff_dualinput_filter_frame_main(FFDualInputContext *s, AVFilterLink *inlink, AVFrame *in);
+int ff_dualinput_filter_frame_second(FFDualInputContext *s, AVFilterLink *inlink, AVFrame *in);
+int ff_dualinput_request_frame(FFDualInputContext *s, AVFilterLink *outlink);
+void ff_dualinput_uninit(FFDualInputContext *s);
+
+#endif /* AVFILTER_DUALINPUT_H */
diff --git a/libavfilter/vf_overlay.c b/libavfilter/vf_overlay.c
index 478d650..e6d949f 100644
--- a/libavfilter/vf_overlay.c
+++ b/libavfilter/vf_overlay.c
@@ -25,8 +25,6 @@
* overlay one video on top of another
*/
-/* #define DEBUG */
-
#include "avfilter.h"
#include "formats.h"
#include "libavutil/common.h"
@@ -37,9 +35,8 @@
#include "libavutil/imgutils.h"
#include "libavutil/mathematics.h"
#include "libavutil/opt.h"
-#include "libavutil/timestamp.h"
#include "internal.h"
-#include "bufferqueue.h"
+#include "dualinput.h"
#include "drawutils.h"
#include "video.h"
@@ -90,8 +87,6 @@ typedef struct {
int x, y; ///< position of overlayed picture
int allow_packed_rgb;
- uint8_t frame_requested;
- uint8_t overlay_eof;
uint8_t main_is_packed_rgb;
uint8_t main_rgba_map[4];
uint8_t main_has_alpha;
@@ -101,21 +96,20 @@ typedef struct {
enum OverlayFormat { OVERLAY_FORMAT_YUV420, OVERLAY_FORMAT_YUV444, OVERLAY_FORMAT_RGB, OVERLAY_FORMAT_NB} format;
enum EvalMode { EVAL_MODE_INIT, EVAL_MODE_FRAME, EVAL_MODE_NB } eval_mode;
- AVFrame *overpicref;
- struct FFBufQueue queue_main;
- struct FFBufQueue queue_over;
+ FFDualInputContext dinput;
int main_pix_step[4]; ///< steps per pixel for each plane of the main output
int overlay_pix_step[4]; ///< steps per pixel for each plane of the overlay
int hsub, vsub; ///< chroma subsampling values
- int shortest; ///< terminate stream when the shortest input terminates
- int repeatlast; ///< repeat last overlay frame
double var_values[VAR_VARS_NB];
char *x_expr, *y_expr;
AVExpr *x_pexpr, *y_pexpr;
} OverlayContext;
+// TODO: remove forward declaration
+static AVFrame *do_blend(AVFilterContext *ctx, AVFrame *mainpic, const AVFrame *second);
+
static av_cold int init(AVFilterContext *ctx)
{
OverlayContext *s = ctx->priv;
@@ -125,6 +119,7 @@ static av_cold int init(AVFilterContext *ctx)
"The rgb option is deprecated and is overriding the format option, use format instead\n");
s->format = OVERLAY_FORMAT_RGB;
}
+ s->dinput.process = do_blend;
return 0;
}
@@ -132,9 +127,7 @@ static av_cold void uninit(AVFilterContext *ctx)
{
OverlayContext *s = ctx->priv;
- av_frame_free(&s->overpicref);
- ff_bufqueue_discard_all(&s->queue_main);
- ff_bufqueue_discard_all(&s->queue_over);
+ ff_dualinput_uninit(&s->dinput);
av_expr_free(s->x_pexpr); s->x_pexpr = NULL;
av_expr_free(s->y_pexpr); s->y_pexpr = NULL;
}
@@ -355,7 +348,7 @@ static int config_output(AVFilterLink *outlink)
* Blend image in src to destination buffer dst at position (x, y).
*/
static void blend_image(AVFilterContext *ctx,
- AVFrame *dst, AVFrame *src,
+ AVFrame *dst, const AVFrame *src,
int x, int y)
{
OverlayContext *s = ctx->priv;
@@ -542,46 +535,13 @@ static void blend_image(AVFilterContext *ctx,
}
}
-static int try_filter_frame(AVFilterContext *ctx, AVFrame *mainpic)
+static AVFrame *do_blend(AVFilterContext *ctx, AVFrame *mainpic,
+ const AVFrame *second)
{
OverlayContext *s = ctx->priv;
AVFilterLink *inlink = ctx->inputs[0];
- AVFrame *next_overpic;
- int ret;
-
- /* Discard obsolete overlay frames: if there is a next overlay frame with pts
- * before the main frame, we can drop the current overlay. */
- while (1) {
- next_overpic = ff_bufqueue_peek(&s->queue_over, 0);
- if (!next_overpic && s->overlay_eof && !s->repeatlast) {
- av_frame_free(&s->overpicref);
- break;
- }
- if (!next_overpic || av_compare_ts(next_overpic->pts, ctx->inputs[OVERLAY]->time_base,
- mainpic->pts , ctx->inputs[MAIN]->time_base) > 0)
- break;
- ff_bufqueue_get(&s->queue_over);
- av_frame_free(&s->overpicref);
- s->overpicref = next_overpic;
- }
- /* If there is no next frame and no EOF and the overlay frame is before
- * the main frame, we can not know yet if it will be superseded. */
- if (!s->queue_over.available && !s->overlay_eof &&
- (!s->overpicref || av_compare_ts(s->overpicref->pts, ctx->inputs[OVERLAY]->time_base,
- mainpic->pts , ctx->inputs[MAIN]->time_base) < 0))
- return AVERROR(EAGAIN);
-
- /* At this point, we know that the current overlay frame extends to the
- * time of the main frame. */
- av_dlog(ctx, "main_pts:%s main_pts_time:%s",
- av_ts2str(mainpic->pts), av_ts2timestr(mainpic->pts, &ctx->inputs[MAIN]->time_base));
- if (s->overpicref)
- av_dlog(ctx, " over_pts:%s over_pts_time:%s",
- av_ts2str(s->overpicref->pts), av_ts2timestr(s->overpicref->pts, &ctx->inputs[OVERLAY]->time_base));
- av_dlog(ctx, "\n");
-
- if (s->overpicref) {
+ /* TODO: reindent */
if (s->eval_mode == EVAL_MODE_FRAME) {
int64_t pos = av_frame_get_pkt_pos(mainpic);
@@ -596,100 +556,27 @@ static int try_filter_frame(AVFilterContext *ctx, AVFrame *mainpic)
s->var_values[VAR_X], s->x,
s->var_values[VAR_Y], s->y);
}
- if (!ctx->is_disabled)
- blend_image(ctx, mainpic, s->overpicref, s->x, s->y);
-
- }
- ret = ff_filter_frame(ctx->outputs[0], mainpic);
- av_assert1(ret != AVERROR(EAGAIN));
- s->frame_requested = 0;
- return ret;
-}
-
-static int try_filter_next_frame(AVFilterContext *ctx)
-{
- OverlayContext *s = ctx->priv;
- AVFrame *next_mainpic = ff_bufqueue_peek(&s->queue_main, 0);
- int ret;
-
- if (!next_mainpic)
- return AVERROR(EAGAIN);
- if ((ret = try_filter_frame(ctx, next_mainpic)) == AVERROR(EAGAIN))
- return ret;
- ff_bufqueue_get(&s->queue_main);
- return ret;
-}
-
-static int flush_frames(AVFilterContext *ctx)
-{
- int ret;
- while (!(ret = try_filter_next_frame(ctx)));
- return ret == AVERROR(EAGAIN) ? 0 : ret;
+ blend_image(ctx, mainpic, second, s->x, s->y);
+ return mainpic;
}
static int filter_frame_main(AVFilterLink *inlink, AVFrame *inpicref)
{
- AVFilterContext *ctx = inlink->dst;
- OverlayContext *s = ctx->priv;
- int ret;
-
- if ((ret = flush_frames(ctx)) < 0)
- return ret;
- if ((ret = try_filter_frame(ctx, inpicref)) < 0) {
- if (ret != AVERROR(EAGAIN))
- return ret;
- ff_bufqueue_add(ctx, &s->queue_main, inpicref);
- }
-
- if (!s->overpicref)
- return 0;
- flush_frames(ctx);
-
- return 0;
+ OverlayContext *s = inlink->dst->priv;
+ return ff_dualinput_filter_frame_main(&s->dinput, inlink, inpicref);
}
static int filter_frame_over(AVFilterLink *inlink, AVFrame *inpicref)
{
- AVFilterContext *ctx = inlink->dst;
- OverlayContext *s = ctx->priv;
- int ret;
-
- if ((ret = flush_frames(ctx)) < 0)
- return ret;
- ff_bufqueue_add(ctx, &s->queue_over, inpicref);
- ret = try_filter_next_frame(ctx);
- return ret == AVERROR(EAGAIN) ? 0 : ret;
+ OverlayContext *s = inlink->dst->priv;
+ return ff_dualinput_filter_frame_second(&s->dinput, inlink, inpicref);
}
static int request_frame(AVFilterLink *outlink)
{
- AVFilterContext *ctx = outlink->src;
- OverlayContext *s = ctx->priv;
- int input, ret;
-
- if (!try_filter_next_frame(ctx))
- return 0;
- s->frame_requested = 1;
- while (s->frame_requested) {
- /* TODO if we had a frame duration, we could guess more accurately */
- input = !s->overlay_eof && (s->queue_main.available ||
- s->queue_over.available < 2) ?
- OVERLAY : MAIN;
- ret = ff_request_frame(ctx->inputs[input]);
- /* EOF on main is reported immediately */
- if (ret == AVERROR_EOF && input == OVERLAY) {
- s->overlay_eof = 1;
- if (s->shortest)
- return ret;
- if ((ret = try_filter_next_frame(ctx)) != AVERROR(EAGAIN))
- return ret;
- ret = 0; /* continue requesting frames on main */
- }
- if (ret < 0)
- return ret;
- }
- return 0;
+ OverlayContext *s = outlink->src->priv;
+ return ff_dualinput_request_frame(&s->dinput, outlink);
}
#define OFFSET(x) offsetof(OverlayContext, x)
@@ -702,12 +589,12 @@ static const AVOption overlay_options[] = {
{ "init", "eval expressions once during initialization", 0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_INIT}, .flags = FLAGS, .unit = "eval" },
{ "frame", "eval expressions per-frame", 0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_FRAME}, .flags = FLAGS, .unit = "eval" },
{ "rgb", "force packed RGB in input and output (deprecated)", OFFSET(allow_packed_rgb), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS },
- { "shortest", "force termination when the shortest input terminates", OFFSET(shortest), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, FLAGS },
+ { "shortest", "force termination when the shortest input terminates", OFFSET(dinput.shortest), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, FLAGS },
{ "format", "set output format", OFFSET(format), AV_OPT_TYPE_INT, {.i64=OVERLAY_FORMAT_YUV420}, 0, OVERLAY_FORMAT_NB-1, FLAGS, "format" },
{ "yuv420", "", 0, AV_OPT_TYPE_CONST, {.i64=OVERLAY_FORMAT_YUV420}, .flags = FLAGS, .unit = "format" },
{ "yuv444", "", 0, AV_OPT_TYPE_CONST, {.i64=OVERLAY_FORMAT_YUV444}, .flags = FLAGS, .unit = "format" },
{ "rgb", "", 0, AV_OPT_TYPE_CONST, {.i64=OVERLAY_FORMAT_RGB}, .flags = FLAGS, .unit = "format" },
- { "repeatlast", "repeat overlay of the last overlay frame", OFFSET(repeatlast), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, FLAGS },
+ { "repeatlast", "repeat overlay of the last overlay frame", OFFSET(dinput.repeatlast), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, FLAGS },
{ NULL }
};
More information about the ffmpeg-cvslog
mailing list