[FFmpeg-devel] [PATCH] lavf/vf_stack: add keep_dar option for vstack and hstack

lance.lmwang at gmail.com lance.lmwang at gmail.com
Mon Jul 8 13:48:46 EEST 2019


From: Limin Wang <lance.lmwang at gmail.com>

It's useful to compare two 4K videos quality side by side on 4K TV.

Signed-off-by: Limin Wang <lance.lmwang at gmail.com>
---
 doc/filters.texi       |  6 ++++++
 libavfilter/vf_stack.c | 31 ++++++++++++++++++++++++++-----
 2 files changed, 32 insertions(+), 5 deletions(-)

diff --git a/doc/filters.texi b/doc/filters.texi
index ee6a93ffbf..675b02fc34 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -11280,6 +11280,9 @@ The filter accept the following option:
 @item inputs
 Set number of input streams. Default is 2.
 
+ at item keep_dar
+stack with 1/nb_inputs of each inputs screen to keep the same DAR, Default is 0.
+
 @item shortest
 If set to 1, force the output to terminate when the shortest input
 terminates. Default value is 0.
@@ -18333,6 +18336,9 @@ The filter accept the following option:
 @item inputs
 Set number of input streams. Default is 2.
 
+ at item keep_dar
+stack with 1/nb_inputs of each inputs screen to keep the same DAR, Default is 0.
+
 @item shortest
 If set to 1, force the output to terminate when the shortest input
 terminates. Default value is 0.
diff --git a/libavfilter/vf_stack.c b/libavfilter/vf_stack.c
index 4d254e0013..9196b5b0b7 100644
--- a/libavfilter/vf_stack.c
+++ b/libavfilter/vf_stack.c
@@ -44,6 +44,7 @@ typedef struct StackContext {
     int is_vertical;
     int is_horizontal;
     int nb_planes;
+    int keep_dar;
 
     StackItem *items;
     AVFrame **frames;
@@ -123,7 +124,7 @@ static int process_frame(FFFrameSync *fs)
     StackContext *s = fs->opaque;
     AVFrame **in = s->frames;
     AVFrame *out;
-    int i, p, ret, offset[4] = { 0 };
+    int i, p, ret, offset[4] = { 0 }, in_offset[4] = { 0 };
 
     for (i = 0; i < s->nb_inputs; i++) {
         if ((ret = ff_framesync_get_frame(&s->fs, i, &in[i], 0)) < 0)
@@ -153,16 +154,25 @@ static int process_frame(FFFrameSync *fs)
 
         for (p = 0; p < s->nb_planes; p++) {
             if (s->is_vertical) {
+                if (s->keep_dar) {
+                    height[p] /= s->nb_inputs;
+                    in_offset[p] = i * height[p] * in[i]->linesize[p];
+                }
+
                 av_image_copy_plane(out->data[p] + offset[p] * out->linesize[p],
                                     out->linesize[p],
-                                    in[i]->data[p],
+                                    in[i]->data[p] + in_offset[p],
                                     in[i]->linesize[p],
                                     linesize[p], height[p]);
                 offset[p] += height[p];
             } else if (s->is_horizontal) {
+                if (s->keep_dar) {
+                    linesize[p] /= s->nb_inputs;
+                    in_offset[p] = i * in[i]->linesize[p] / s->nb_inputs;
+                }
                 av_image_copy_plane(out->data[p] + offset[p],
                                     out->linesize[p],
-                                    in[i]->data[p],
+                                    in[i]->data[p] + in_offset[p],
                                     in[i]->linesize[p],
                                     linesize[p], height[p]);
                 offset[p] += linesize[p];
@@ -197,20 +207,30 @@ static int config_output(AVFilterLink *outlink)
         return AVERROR_BUG;
 
     if (s->is_vertical) {
+        if (s->keep_dar)
+            height /= s->nb_inputs;
         for (i = 1; i < s->nb_inputs; i++) {
             if (ctx->inputs[i]->w != width) {
                 av_log(ctx, AV_LOG_ERROR, "Input %d width %d does not match input %d width %d.\n", i, ctx->inputs[i]->w, 0, width);
                 return AVERROR(EINVAL);
             }
-            height += ctx->inputs[i]->h;
+            if (!s->keep_dar)
+                height += ctx->inputs[i]->h;
+            else
+                height += ctx->inputs[i]->h / s->nb_inputs;
         }
     } else if (s->is_horizontal) {
+        if (s->keep_dar)
+            width /= s->nb_inputs;
         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);
                 return AVERROR(EINVAL);
             }
-            width += ctx->inputs[i]->w;
+            if (!s->keep_dar)
+                width += ctx->inputs[i]->w;
+            else
+                width += ctx->inputs[i]->w / s->nb_inputs;
         }
     } else {
         char *arg, *p = s->layout, *saveptr = NULL;
@@ -339,6 +359,7 @@ static int activate(AVFilterContext *ctx)
 #define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM
 static const AVOption stack_options[] = {
     { "inputs", "set number of inputs", OFFSET(nb_inputs), AV_OPT_TYPE_INT, {.i64=2}, 2, INT_MAX, .flags = FLAGS },
+    { "keep_dar", "stack with 1/nb_inputs of each inputs screen to keep the same DAR", OFFSET(keep_dar), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, .flags = FLAGS },
     { "shortest", "force termination when the shortest input terminates", OFFSET(shortest), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, .flags = FLAGS },
     { NULL },
 };
-- 
2.21.0



More information about the ffmpeg-devel mailing list