From 4fcfb9c4ebf3650fd7e82ba78d81c8aebdabd228 Mon Sep 17 00:00:00 2001 From: Paul B Mahol Date: Wed, 24 Oct 2018 11:00:58 +0200 Subject: avfilter: add xstack filter --- libavfilter/vf_stack.c | 151 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 140 insertions(+), 11 deletions(-) (limited to 'libavfilter/vf_stack.c') diff --git a/libavfilter/vf_stack.c b/libavfilter/vf_stack.c index b2b8c68041..8731674aa7 100644 --- a/libavfilter/vf_stack.c +++ b/libavfilter/vf_stack.c @@ -29,14 +29,23 @@ #include "framesync.h" #include "video.h" +typedef struct StackItem { + int x[4], y[4]; + int linesize[4]; + int height[4]; +} StackItem; + typedef struct StackContext { const AVClass *class; const AVPixFmtDescriptor *desc; int nb_inputs; + char *layout; int shortest; int is_vertical; + int is_horizontal; int nb_planes; + StackItem *items; AVFrame **frames; FFFrameSync fs; } StackContext; @@ -66,10 +75,19 @@ static av_cold int init(AVFilterContext *ctx) if (!strcmp(ctx->filter->name, "vstack")) s->is_vertical = 1; + if (!strcmp(ctx->filter->name, "hstack")) + s->is_horizontal = 1; + s->frames = av_calloc(s->nb_inputs, sizeof(*s->frames)); if (!s->frames) return AVERROR(ENOMEM); + if (!strcmp(ctx->filter->name, "xstack")) { + s->items = av_calloc(s->nb_inputs, sizeof(*s->items)); + if (!s->items) + return AVERROR(ENOMEM); + } + for (i = 0; i < s->nb_inputs; i++) { AVFilterPad pad = { 0 }; @@ -112,13 +130,15 @@ static int process_frame(FFFrameSync *fs) int linesize[4]; int height[4]; - if ((ret = av_image_fill_linesizes(linesize, inlink->format, inlink->w)) < 0) { - av_frame_free(&out); - return ret; - } + if (s->is_horizontal || s->is_vertical) { + if ((ret = av_image_fill_linesizes(linesize, inlink->format, inlink->w)) < 0) { + av_frame_free(&out); + return ret; + } - height[1] = height[2] = AV_CEIL_RSHIFT(inlink->h, s->desc->log2_chroma_h); - height[0] = height[3] = inlink->h; + height[1] = height[2] = AV_CEIL_RSHIFT(inlink->h, s->desc->log2_chroma_h); + height[0] = height[3] = inlink->h; + } for (p = 0; p < s->nb_planes; p++) { if (s->is_vertical) { @@ -128,13 +148,21 @@ static int process_frame(FFFrameSync *fs) in[i]->linesize[p], linesize[p], height[p]); offset[p] += height[p]; - } else { + } else if (s->is_horizontal) { av_image_copy_plane(out->data[p] + offset[p], out->linesize[p], in[i]->data[p], in[i]->linesize[p], linesize[p], height[p]); offset[p] += linesize[p]; + } else { + StackItem *item = &s->items[i]; + + av_image_copy_plane(out->data[p] + out->linesize[p] * item->y[p] + item->x[p], + out->linesize[p], + in[i]->data[p], + in[i]->linesize[p], + item->linesize[p], item->height[p]); } } } @@ -154,6 +182,10 @@ static int config_output(AVFilterLink *outlink) FFFrameSyncIn *in; int i, ret; + s->desc = av_pix_fmt_desc_get(outlink->format); + if (!s->desc) + return AVERROR_BUG; + if (s->is_vertical) { for (i = 1; i < s->nb_inputs; i++) { if (ctx->inputs[i]->w != width) { @@ -162,7 +194,7 @@ static int config_output(AVFilterLink *outlink) } height += ctx->inputs[i]->h; } - } else { + } else if (s->is_horizontal) { for (i = 1; i < s->nb_inputs; i++) { if (ctx->inputs[i]->h != height) { av_log(ctx, AV_LOG_ERROR, "Input %d height %d does not match input %d height %d.\n", i, ctx->inputs[i]->h, 0, height); @@ -170,11 +202,81 @@ static int config_output(AVFilterLink *outlink) } width += ctx->inputs[i]->w; } + } else { + char *arg, *p = s->layout, *saveptr = NULL; + char *arg2, *p2, *saveptr2 = NULL; + char *arg3, *p3, *saveptr3 = NULL; + int inw, inh, size; + + for (i = 0; i < s->nb_inputs; i++) { + AVFilterLink *inlink = ctx->inputs[i]; + StackItem *item = &s->items[i]; + + if (!(arg = av_strtok(p, "|", &saveptr))) + return AVERROR(EINVAL); + + p = NULL; + + if ((ret = av_image_fill_linesizes(item->linesize, inlink->format, inlink->w)) < 0) { + return ret; + } + + item->height[1] = item->height[2] = AV_CEIL_RSHIFT(inlink->h, s->desc->log2_chroma_h); + item->height[0] = item->height[3] = inlink->h; + + p2 = arg; + inw = inh = 0; + + for (int j = 0; j < 2; j++) { + if (!(arg2 = av_strtok(p2, "_", &saveptr2))) + return AVERROR(EINVAL); + + p2 = NULL; + p3 = arg2; + while ((arg3 = av_strtok(p3, "+", &saveptr3))) { + p3 = NULL; + if (sscanf(arg3, "w%d", &size) == 1) { + if (size == i || size < 0 || size >= s->nb_inputs) + return AVERROR(EINVAL); + + if (!j) + inw += ctx->inputs[size]->w; + else + inh += ctx->inputs[size]->w; + } else if (sscanf(arg3, "h%d", &size) == 1) { + if (size == i || size < 0 || size >= s->nb_inputs) + return AVERROR(EINVAL); + + if (!j) + inw += ctx->inputs[size]->h; + else + inh += ctx->inputs[size]->h; + } else if (sscanf(arg3, "%d", &size) == 1) { + if (size < 0) + return AVERROR(EINVAL); + + if (!j) + inw += size; + else + inh += size; + } else { + return AVERROR(EINVAL); + } + } + } + + if ((ret = av_image_fill_linesizes(item->x, inlink->format, inw)) < 0) { + return ret; + } + + item->y[1] = item->y[2] = AV_CEIL_RSHIFT(inh, s->desc->log2_chroma_h); + item->y[0] = item->y[3] = inh; + + width = FFMAX(width, inlink->w + inw); + height = FFMAX(height, inlink->h + inh); + } } - s->desc = av_pix_fmt_desc_get(outlink->format); - if (!s->desc) - return AVERROR_BUG; s->nb_planes = av_pix_fmt_count_planes(outlink->format); outlink->w = width; @@ -209,6 +311,7 @@ static av_cold void uninit(AVFilterContext *ctx) ff_framesync_uninit(&s->fs); av_freep(&s->frames); + av_freep(&s->items); for (i = 0; i < ctx->nb_inputs; i++) av_freep(&ctx->input_pads[i].name); @@ -276,3 +379,29 @@ AVFilter ff_vf_vstack = { }; #endif /* CONFIG_VSTACK_FILTER */ + +#if CONFIG_XSTACK_FILTER + +static const AVOption xstack_options[] = { + { "inputs", "set number of inputs", OFFSET(nb_inputs), AV_OPT_TYPE_INT, {.i64=2}, 2, INT_MAX, .flags = FLAGS }, + { "layout", "set custom layout", OFFSET(layout), AV_OPT_TYPE_STRING, {.str="0_0|w0_0"}, 0, 0, .flags = FLAGS }, + { "shortest", "force termination when the shortest input terminates", OFFSET(shortest), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, .flags = FLAGS }, + { NULL }, +}; + +AVFILTER_DEFINE_CLASS(xstack); + +AVFilter ff_vf_xstack = { + .name = "xstack", + .description = NULL_IF_CONFIG_SMALL("Stack video inputs into custom layout."), + .priv_size = sizeof(StackContext), + .priv_class = &xstack_class, + .query_formats = query_formats, + .outputs = outputs, + .init = init, + .uninit = uninit, + .activate = activate, + .flags = AVFILTER_FLAG_DYNAMIC_INPUTS, +}; + +#endif /* CONFIG_XSTACK_FILTER */ -- cgit v1.2.3