diff options
author | Clément Bœsch <ubitux@gmail.com> | 2013-05-25 16:25:46 +0200 |
---|---|---|
committer | Clément Bœsch <ubitux@gmail.com> | 2013-05-27 20:32:28 +0200 |
commit | 43286028906adab67ec87bc15ff3010d0f6c2ff0 (patch) | |
tree | 8192ba7da138231855eac0df78066327b9645e90 | |
parent | 3cec29cf5981c35cfbe2b43fcdea3f6b9bb108b9 (diff) | |
download | ffmpeg-43286028906adab67ec87bc15ff3010d0f6c2ff0.tar.gz |
lavfi: add dual input helpers.
-rw-r--r-- | libavfilter/Makefile | 2 | ||||
-rw-r--r-- | libavfilter/dualinput.c | 159 | ||||
-rw-r--r-- | libavfilter/dualinput.h | 46 | ||||
-rw-r--r-- | libavfilter/vf_overlay.c | 155 |
4 files changed, 227 insertions, 135 deletions
diff --git a/libavfilter/Makefile b/libavfilter/Makefile index f53afb2bab..9682cba216 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 0000000000..10e365267b --- /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 0000000000..98d05442c7 --- /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 478d6504fd..e6d949f2b3 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 } }; |