From patchwork Wed Jun 29 17:14:26 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Vignesh Venkat X-Patchwork-Id: 36529 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:8b27:b0:88:1bbf:7fd2 with SMTP id l39csp440536pzh; Wed, 29 Jun 2022 10:14:42 -0700 (PDT) X-Google-Smtp-Source: AGRyM1shX8ki2Ekw8slj25PHwcqB9T3iQr4BpTcAPkhTdeHfx/VCKJhqjJ61mHqvfV5RmL3dyZ3F X-Received: by 2002:a17:907:8a28:b0:726:a02a:5bea with SMTP id sc40-20020a1709078a2800b00726a02a5beamr4559004ejc.175.1656522882069; Wed, 29 Jun 2022 10:14:42 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1656522882; cv=none; d=google.com; s=arc-20160816; b=djmTxTmaRxv5jnu+ENWeoJsIuBeG7vm2xAA9XF/r7gcSTvtRvYDSLZu1EJEVnbmxDW BKWGnZz5Voh2oXydsBEtcwhwVq4ixQWod5y4vtpYDqfhHbnpFnS4Rxy7m4LdAyMw/Atq /gPUBu/Gz94MsTUXsk74L+zH5xgS8BtDIJkb/g4H/GedM6Ikdbdgu6ZV7HsWvt6FvHNr ENoRPQA2+MjMTNGY5UuMzQ8A7cKHvkeStVh4gOZnKmGrXK5KgiKtvXc/UBxmkfUlLQf2 x65QhRio4xJ2LVpiaLOBVoxwMaHv+tJ2mBzvMAf/HGJevaIMe0fOD1I1J2pBCsBhf5Qh bbSQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:to:from:references:mime-version :message-id:in-reply-to:date:dkim-signature:delivered-to; bh=aLpjSGdBoTk+ia8WbFEZ02/Qw3PZw2nPuI2bxn4FmGc=; b=jJRH+AH/CarR4HiDp2zlIo8YeY7NRDS1JPOSK5QsU2OBrvrD0qfkDGUGp8CrTa+y7r f13ZUjSd/tAl2qJ/tIdyu5jJihFjY2I1ieKELerQWJkHl/DVJxY+1swnvgFohLIfhvc3 9xqyYcEeWIW+v4rZZKSyoBKecqUuXx7tcUK9ANxAw0WEjGmi+z5W+L0UEeVtB0kOdTV+ Bl5XdllG3kAa/U7iFYkY6LZNKblqIXw0qxgD+nr4oPzSPwMJ3OVPT0oL7OTH3mkTYlkx yg5Qt9DFRHIkUXl0NQ0wjUKyAKjGJaKurEdYPCPgVAiWtH1XDpzIEsnvCgiyhtRfPfYW oRgQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@google.com header.s=20210112 header.b=UFcSHwRz; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id hg3-20020a1709072cc300b0072697f3b60csi1475145ejc.505.2022.06.29.10.14.41; Wed, 29 Jun 2022 10:14:42 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@google.com header.s=20210112 header.b=UFcSHwRz; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id AE14B68B6FF; Wed, 29 Jun 2022 20:14:37 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-yb1-f201.google.com (mail-yb1-f201.google.com [209.85.219.201]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 024F468B4FD for ; Wed, 29 Jun 2022 20:14:30 +0300 (EEST) Received: by mail-yb1-f201.google.com with SMTP id l6-20020a25bf86000000b00668c915a3f2so14286664ybk.4 for ; Wed, 29 Jun 2022 10:14:30 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=Ye8cmFzwxRLCI+9H3aL24WYLxn9+0ZrL3vNKtKQ5Htk=; b=UFcSHwRzlRShvaB6MC2YRshLw1iGPahXgW4RwnncANb3/1gwiP0HD9Aef5tiXYrJim AeT9ECQ9cT6uOqNWPFUsY8bgFw66bu5qO1hHuGH14WYCyEkWEWU12F9PE+IrE48FVyVR yLRgDE5OdBUEZM2qdSaH5D+kD332PcXJarRm10/Sbl7ywdvonl9uRngILmAWnVUhx1xy iB6OoGU1Dn/lGmH3IDs4P+TbaTobL8CTWJ4FDGoLk0pOHhyg5ymvYgkjHiohXgeKF9TN 41fSIFBaI5j1DKRFa6j2FncouafmPGSo5BM/gfMkHIjIROUJ+oTgNlr83YwvAdrsG+Bd J9RQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=Ye8cmFzwxRLCI+9H3aL24WYLxn9+0ZrL3vNKtKQ5Htk=; b=6RMb5N7GVmWA4kCTVe6ffjj22AXPN7T3V6pqlzV7oQ98ib56kU+invAbZAgyOI7AyG 2G1Prm85uou0s9qs/4lCwU5o2l4hyggxtFO2xOYhhmQz6ZOH4vd/QGLWQI++Xo9TLi5m 36ITTTPQ513bJ1oJKHuccfQ5w0WrffJW94E8IV1CIK9NlATtL+iOyvw069Bb7xsp2Ik7 sA3zTQZnN6lH6PKAoNmu+Kj+I2TCpwacP1NvkTbBwyTbSEXvZTevSVwxj5WByIKKJpwg 6Ysd7Uk1geE6DBtzUmN3SZLWw17t7TnU39ktLqKAUIamJGszAqvVpCITtF+ScvpUeak0 oGbg== X-Gm-Message-State: AJIora9OyRX8Q97d5Kr5iEd9IfbFIz0/gQhbLW86F+e9Y4RwIlFOFWVf uKHyOla75ek9DZVMR8q5wgIm9udR6J705YZ3bxV4Ei4Wb9umkaTeepyn1PY2RBFZjuDVlZy5vyv TAt2j6ht9f/pon1jpOEXOHAPPxQ2UbtLedLBO3uqIdJf7IWNvPZfaavUzav00AbsBFVcX X-Received: from vigneshv3.mtv.corp.google.com ([2620:0:1000:2511:c6ab:644:6f18:8e98]) (user=vigneshv job=sendgmr) by 2002:a81:6ad7:0:b0:31b:a0f1:c093 with SMTP id f206-20020a816ad7000000b0031ba0f1c093mr4980220ywc.400.1656522869431; Wed, 29 Jun 2022 10:14:29 -0700 (PDT) Date: Wed, 29 Jun 2022 10:14:26 -0700 In-Reply-To: Message-Id: <20220629171426.128505-1-vigneshv@google.com> Mime-Version: 1.0 References: X-Mailer: git-send-email 2.37.0.rc0.161.g10f37bed90-goog From: Vignesh Venkatasubramanian To: ffmpeg-devel@ffmpeg.org Subject: [FFmpeg-devel] [PATCH] avfilter/xstack: Add support for fixed size grid X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: Vignesh Venkatasubramanian Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: KlX5fQZgS0kk Add a short hand parameter for making a fixed size grid. The existing xstack layout parameter syntax gets tedious if all one wants is a matrix like grid of the input streams. Add a grid option to the xstack filter that simplifies this use case by simply specifying the number of rows and columns instead of specific x/y co-ordinate for each stream. Also updating the filter documentation to explain the new option. Signed-off-by: Vignesh Venkatasubramanian --- doc/filters.texi | 22 ++++++++++-- libavfilter/vf_stack.c | 81 +++++++++++++++++++++++++++++++++++------- 2 files changed, 88 insertions(+), 15 deletions(-) diff --git a/doc/filters.texi b/doc/filters.texi index e525e87b3c..5a889895c6 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -24381,8 +24381,26 @@ the output video frame will be filled. Similarly, videos can overlap each other if their position doesn't leave enough space for the full frame of adjoining videos. -For 2 inputs, a default layout of @code{0_0|w0_0} is set. In all other cases, -a layout must be set by the user. +For 2 inputs, a default layout of @code{0_0|w0_0} (equivalent to +@code{grid=2x1}) is set. In all other cases, a layout or a grid must be set by +the user. Either @code{grid} or @code{layout} can be specified at a time. +Specifying both will result in an error. + +@item grid +Specify a fixed size grid of inputs. +This option is used to create a fixed size grid of the input streams. Set the +grid size in the form @code{COLUMNSxROWS}. There must be @code{ROWS * COLUMNS} +input streams and they will be arranged as a grid with @code{ROWS} rows and +@code{COLUMNS} columns. When using this option, each input stream within a row +must have the same height and all the rows must have the same width. + +If @code{grid} is set, then @code{inputs} option is ignored and is implicitly +set to @code{ROWS * COLUMNS}. + +For 2 inputs, a default grid of @code{2x1} (equivalent to +@code{layout=0_0|w0_0}) is set. In all other cases, a layout or a grid must be +set by the user. Either @code{grid} or @code{layout} can be specified at a time. +Specifying both will result in an error. @item shortest If set to 1, force the output to terminate when the shortest input diff --git a/libavfilter/vf_stack.c b/libavfilter/vf_stack.c index aa32a1bf5e..d583d21dbe 100644 --- a/libavfilter/vf_stack.c +++ b/libavfilter/vf_stack.c @@ -48,6 +48,8 @@ typedef struct StackContext { int is_vertical; int is_horizontal; int nb_planes; + int nb_grid_columns; + int nb_grid_rows; uint8_t fillcolor[4]; char *fillcolor_str; int fillcolor_enable; @@ -85,33 +87,43 @@ static av_cold int init(AVFilterContext *ctx) 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); - - s->items = av_calloc(s->nb_inputs, sizeof(*s->items)); - if (!s->items) - return AVERROR(ENOMEM); - if (!strcmp(ctx->filter->name, "xstack")) { + int is_grid; if (strcmp(s->fillcolor_str, "none") && av_parse_color(s->fillcolor, s->fillcolor_str, -1, ctx) >= 0) { s->fillcolor_enable = 1; } else { s->fillcolor_enable = 0; } - if (!s->layout) { + is_grid = s->nb_grid_rows && s->nb_grid_columns; + if (s->layout && is_grid) { + av_log(ctx, AV_LOG_ERROR, "Both layout and grid were specified. Only one is allowed.\n"); + return AVERROR(EINVAL); + } + if (!s->layout && !is_grid) { if (s->nb_inputs == 2) { - s->layout = av_strdup("0_0|w0_0"); - if (!s->layout) - return AVERROR(ENOMEM); + s->nb_grid_rows = 1; + s->nb_grid_columns = 2; + is_grid = 1; } else { - av_log(ctx, AV_LOG_ERROR, "No layout specified.\n"); + av_log(ctx, AV_LOG_ERROR, "No layout or grid specified.\n"); return AVERROR(EINVAL); } } + if (is_grid) { + s->nb_inputs = s->nb_grid_rows * s->nb_grid_columns; + } } + s->frames = av_calloc(s->nb_inputs, sizeof(*s->frames)); + if (!s->frames) + return AVERROR(ENOMEM); + + 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 }; @@ -244,6 +256,48 @@ static int config_output(AVFilterLink *outlink) width += ctx->inputs[i]->w; } } + } else if (s->nb_grid_rows && s->nb_grid_columns) { + int inw = 0, inh = 0; + int k = 0; + int row_height; + height = 0; + width = 0; + for (i = 0; i < s->nb_grid_rows; i++, inh += row_height) { + row_height = ctx->inputs[i * s->nb_grid_columns]->h; + inw = 0; + for (int j = 0; j < s->nb_grid_columns; j++, k++) { + AVFilterLink *inlink = ctx->inputs[k]; + StackItem *item = &s->items[k]; + + if (ctx->inputs[k]->h != row_height) { + av_log(ctx, AV_LOG_ERROR, "Input %d height %d does not match current row's height %d.\n", + k, ctx->inputs[k]->h, row_height); + return AVERROR(EINVAL); + } + + 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; + + 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; + inw += ctx->inputs[k]->w; + } + height += row_height; + if (!i) + width = inw; + if (i && width != inw) { + av_log(ctx, AV_LOG_ERROR, "Row %d width %d does not match previous row width %d.\n", i, inw, width); + return AVERROR(EINVAL); + } + } } else { char *arg, *p = s->layout, *saveptr = NULL; char *arg2, *p2, *saveptr2 = NULL; @@ -436,6 +490,7 @@ const AVFilter ff_vf_vstack = { 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=NULL}, 0, 0, .flags = FLAGS }, + { "grid", "set fixed size grid layout", OFFSET(nb_grid_columns), AV_OPT_TYPE_IMAGE_SIZE, {.str=NULL}, 0, 0, .flags = FLAGS }, { "shortest", "force termination when the shortest input terminates", OFFSET(shortest), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, .flags = FLAGS }, { "fill", "set the color for unused pixels", OFFSET(fillcolor_str), AV_OPT_TYPE_STRING, {.str = "none"}, .flags = FLAGS }, { NULL },