../../../ffmpeg/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
170 / 261
65.1%
Branches:
84 / 178
47.2%
Functions:
7 / 7
100.0%

libavfilter/vf_stack.c
Line Branch Exec Source
1 /*
2 * Copyright (c) 2015 Paul B. Mahol
3 *
4 * This file is part of FFmpeg.
5 *
6 * FFmpeg is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * FFmpeg is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with FFmpeg; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 #include "config_components.h"
22
23 #include "libavutil/avstring.h"
24 #include "libavutil/imgutils.h"
25 #include "libavutil/mem.h"
26 #include "libavutil/opt.h"
27 #include "libavutil/parseutils.h"
28 #include "libavutil/pixdesc.h"
29
30 #include "avfilter.h"
31 #include "drawutils.h"
32 #include "filters.h"
33 #include "formats.h"
34 #include "framesync.h"
35 #include "video.h"
36
37 typedef struct StackItem {
38 int x[4], y[4];
39 int linesize[4];
40 int height[4];
41 } StackItem;
42
43 typedef struct StackContext {
44 const AVClass *class;
45 const AVPixFmtDescriptor *desc;
46 int nb_inputs;
47 char *layout;
48 int shortest;
49 int is_vertical;
50 int is_horizontal;
51 int nb_planes;
52 int nb_grid_columns;
53 int nb_grid_rows;
54 uint8_t fillcolor[4];
55 char *fillcolor_str;
56 int fillcolor_enable;
57
58 FFDrawContext draw;
59 FFDrawColor color;
60
61 StackItem *items;
62 AVFrame **frames;
63 FFFrameSync fs;
64 } StackContext;
65
66 6 static int query_formats(const AVFilterContext *ctx,
67 AVFilterFormatsConfig **cfg_in,
68 AVFilterFormatsConfig **cfg_out)
69 {
70 6 const StackContext *s = ctx->priv;
71 6 int reject_flags = AV_PIX_FMT_FLAG_BITSTREAM |
72 AV_PIX_FMT_FLAG_HWACCEL |
73 AV_PIX_FMT_FLAG_PAL;
74
75
2/2
✓ Branch 2 → 3 taken 3 times.
✓ Branch 2 → 6 taken 3 times.
6 if (s->fillcolor_enable) {
76 3 return ff_set_common_formats2(ctx, cfg_in, cfg_out,
77 ff_draw_supported_pixel_formats(0));
78 }
79
80 3 return ff_set_common_formats2(ctx, cfg_in, cfg_out,
81 ff_formats_pixdesc_filter(0, reject_flags));
82 }
83
84 18 static av_cold int init(AVFilterContext *ctx)
85 {
86 18 StackContext *s = ctx->priv;
87 int i, ret;
88
89
2/2
✓ Branch 2 → 3 taken 2 times.
✓ Branch 2 → 4 taken 16 times.
18 if (!strcmp(ctx->filter->name, "vstack"))
90 2 s->is_vertical = 1;
91
92
2/2
✓ Branch 4 → 5 taken 4 times.
✓ Branch 4 → 6 taken 14 times.
18 if (!strcmp(ctx->filter->name, "hstack"))
93 4 s->is_horizontal = 1;
94
95
2/2
✓ Branch 6 → 7 taken 12 times.
✓ Branch 6 → 30 taken 6 times.
18 if (!strcmp(ctx->filter->name, "xstack")) {
96 int is_grid;
97
2/4
✓ Branch 7 → 8 taken 12 times.
✗ Branch 7 → 11 not taken.
✓ Branch 9 → 10 taken 12 times.
✗ Branch 9 → 11 not taken.
24 if (strcmp(s->fillcolor_str, "none") &&
98 12 av_parse_color(s->fillcolor, s->fillcolor_str, -1, ctx) >= 0) {
99 12 s->fillcolor_enable = 1;
100 } else {
101 ✗ s->fillcolor_enable = 0;
102 }
103
1/4
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 15 taken 12 times.
✗ Branch 13 → 14 not taken.
✗ Branch 13 → 15 not taken.
12 is_grid = s->nb_grid_rows && s->nb_grid_columns;
104
2/4
✓ Branch 16 → 17 taken 12 times.
✗ Branch 16 → 20 not taken.
✗ Branch 17 → 18 not taken.
✓ Branch 17 → 20 taken 12 times.
12 if (s->layout && is_grid) {
105 ✗ av_log(ctx, AV_LOG_ERROR, "Both layout and grid were specified. Only one is allowed.\n");
106 ✗ return AVERROR(EINVAL);
107 }
108
1/4
✗ Branch 20 → 21 not taken.
✓ Branch 20 → 26 taken 12 times.
✗ Branch 21 → 22 not taken.
✗ Branch 21 → 26 not taken.
12 if (!s->layout && !is_grid) {
109
0/2
✗ Branch 22 → 23 not taken.
✗ Branch 22 → 24 not taken.
✗ if (s->nb_inputs == 2) {
110 ✗ s->nb_grid_rows = 1;
111 ✗ s->nb_grid_columns = 2;
112 ✗ is_grid = 1;
113 } else {
114 ✗ av_log(ctx, AV_LOG_ERROR, "No layout or grid specified.\n");
115 ✗ return AVERROR(EINVAL);
116 }
117 }
118
119
1/2
✗ Branch 26 → 27 not taken.
✓ Branch 26 → 30 taken 12 times.
12 if (is_grid) {
120
0/2
✗ Branch 27 → 28 not taken.
✗ Branch 27 → 29 not taken.
✗ if (s->nb_grid_rows > INT_MAX / s->nb_grid_columns)
121 ✗ return AVERROR(EINVAL);
122 ✗ s->nb_inputs = s->nb_grid_rows * s->nb_grid_columns;
123 }
124 }
125
126 18 s->frames = av_calloc(s->nb_inputs, sizeof(*s->frames));
127
1/2
✗ Branch 31 → 32 not taken.
✓ Branch 31 → 33 taken 18 times.
18 if (!s->frames)
128 ✗ return AVERROR(ENOMEM);
129
130 18 s->items = av_calloc(s->nb_inputs, sizeof(*s->items));
131
1/2
✗ Branch 34 → 35 not taken.
✓ Branch 34 → 36 taken 18 times.
18 if (!s->items)
132 ✗ return AVERROR(ENOMEM);
133
134
2/2
✓ Branch 45 → 37 taken 72 times.
✓ Branch 45 → 46 taken 18 times.
90 for (i = 0; i < s->nb_inputs; i++) {
135 72 AVFilterPad pad = { 0 };
136
137 72 pad.type = AVMEDIA_TYPE_VIDEO;
138 72 pad.name = av_asprintf("input%d", i);
139
1/2
✗ Branch 38 → 39 not taken.
✓ Branch 38 → 40 taken 72 times.
72 if (!pad.name)
140 ✗ return AVERROR(ENOMEM);
141
142
1/2
✗ Branch 41 → 42 not taken.
✓ Branch 41 → 43 taken 72 times.
72 if ((ret = ff_append_inpad_free_name(ctx, &pad)) < 0)
143 ✗ return ret;
144 }
145
146 18 return 0;
147 }
148
149 312 static int process_slice(AVFilterContext *ctx, void *arg, int job, int nb_jobs)
150 {
151 312 StackContext *s = ctx->priv;
152 312 AVFrame *out = arg;
153 312 AVFrame **in = s->frames;
154 312 const int start = ff_slice_pos(s->nb_inputs, job, nb_jobs);
155 312 const int end = ff_slice_pos(s->nb_inputs, job + 1, nb_jobs);
156
157
2/2
✓ Branch 10 → 5 taken 312 times.
✓ Branch 10 → 11 taken 312 times.
624 for (int i = start; i < end; i++) {
158 312 StackItem *item = &s->items[i];
159
160
2/2
✓ Branch 8 → 6 taken 936 times.
✓ Branch 8 → 9 taken 312 times.
1248 for (int p = 0; p < s->nb_planes; p++) {
161 936 av_image_copy_plane(out->data[p] + out->linesize[p] * item->y[p] + item->x[p],
162 out->linesize[p],
163 936 in[i]->data[p],
164 936 in[i]->linesize[p],
165 item->linesize[p], item->height[p]);
166 }
167 }
168
169 312 return 0;
170 }
171
172 153 static int process_frame(FFFrameSync *fs)
173 {
174 153 AVFilterContext *ctx = fs->parent;
175 153 AVFilterLink *outlink = ctx->outputs[0];
176 153 StackContext *s = fs->opaque;
177 153 AVFrame **in = s->frames;
178 AVFrame *out;
179 int i, ret;
180
181
2/2
✓ Branch 7 → 3 taken 312 times.
✓ Branch 7 → 8 taken 153 times.
465 for (i = 0; i < s->nb_inputs; i++) {
182
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 312 times.
312 if ((ret = ff_framesync_get_frame(&s->fs, i, &in[i], 0)) < 0)
183 ✗ return ret;
184 }
185
186 153 out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
187
1/2
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 11 taken 153 times.
153 if (!out)
188 ✗ return AVERROR(ENOMEM);
189 153 out->pts = av_rescale_q(s->fs.pts, s->fs.time_base, outlink->time_base);
190 153 out->sample_aspect_ratio = outlink->sample_aspect_ratio;
191
192
2/2
✓ Branch 11 → 12 taken 3 times.
✓ Branch 11 → 13 taken 150 times.
153 if (s->fillcolor_enable)
193 3 ff_fill_rectangle(&s->draw, &s->color, out->data, out->linesize,
194 0, 0, outlink->w, outlink->h);
195
196 153 ff_filter_execute(ctx, process_slice, out, NULL,
197
1/2
✗ Branch 13 → 14 not taken.
✓ Branch 13 → 15 taken 153 times.
153 FFMIN(s->nb_inputs, ff_filter_get_nb_threads(ctx)));
198
199 153 return ff_filter_frame(outlink, out);
200 }
201
202 6 static int config_output(AVFilterLink *outlink)
203 {
204 6 AVFilterContext *ctx = outlink->src;
205 6 StackContext *s = ctx->priv;
206 6 FilterLink *il = ff_filter_link(ctx->inputs[0]);
207 6 FilterLink *ol = ff_filter_link(outlink);
208 6 AVRational frame_rate = il->frame_rate;
209 6 AVRational sar = ctx->inputs[0]->sample_aspect_ratio;
210 6 int height = ctx->inputs[0]->h;
211 6 int width = ctx->inputs[0]->w;
212 FFFrameSyncIn *in;
213 int i, ret;
214
215 6 s->desc = av_pix_fmt_desc_get(outlink->format);
216
1/2
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 6 times.
6 if (!s->desc)
217 ✗ return AVERROR_BUG;
218
219
2/2
✓ Branch 7 → 8 taken 1 time.
✓ Branch 7 → 21 taken 5 times.
6 if (s->is_vertical) {
220
2/2
✓ Branch 20 → 9 taken 2 times.
✓ Branch 20 → 134 taken 1 time.
3 for (i = 0; i < s->nb_inputs; i++) {
221 2 AVFilterLink *inlink = ctx->inputs[i];
222 2 StackItem *item = &s->items[i];
223
224
1/2
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 12 taken 2 times.
2 if (ctx->inputs[i]->w != width) {
225 ✗ 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);
226 ✗ return AVERROR(EINVAL);
227 }
228
229
1/2
✗ Branch 13 → 14 not taken.
✓ Branch 13 → 15 taken 2 times.
2 if ((ret = av_image_fill_linesizes(item->linesize, inlink->format, inlink->w)) < 0) {
230 ✗ return ret;
231 }
232
233 2 item->height[1] = item->height[2] = AV_CEIL_RSHIFT(inlink->h, s->desc->log2_chroma_h);
234 2 item->height[0] = item->height[3] = inlink->h;
235
236
2/2
✓ Branch 15 → 16 taken 1 time.
✓ Branch 15 → 19 taken 1 time.
2 if (i) {
237 1 item->y[1] = item->y[2] = AV_CEIL_RSHIFT(height, s->desc->log2_chroma_h);
238 1 item->y[0] = item->y[3] = height;
239
240
1/2
✗ Branch 16 → 17 not taken.
✓ Branch 16 → 18 taken 1 time.
1 if (height > INT_MAX - ctx->inputs[i]->h)
241 ✗ return AVERROR(EINVAL);
242 1 height += ctx->inputs[i]->h;
243 }
244 }
245
2/2
✓ Branch 21 → 22 taken 2 times.
✓ Branch 21 → 38 taken 3 times.
5 } else if (s->is_horizontal) {
246
2/2
✓ Branch 37 → 23 taken 4 times.
✓ Branch 37 → 134 taken 2 times.
6 for (i = 0; i < s->nb_inputs; i++) {
247 4 AVFilterLink *inlink = ctx->inputs[i];
248 4 StackItem *item = &s->items[i];
249
250
1/2
✗ Branch 23 → 24 not taken.
✓ Branch 23 → 26 taken 4 times.
4 if (ctx->inputs[i]->h != height) {
251 ✗ 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);
252 ✗ return AVERROR(EINVAL);
253 }
254
255
1/2
✗ Branch 27 → 28 not taken.
✓ Branch 27 → 29 taken 4 times.
4 if ((ret = av_image_fill_linesizes(item->linesize, inlink->format, inlink->w)) < 0) {
256 ✗ return ret;
257 }
258
259 4 item->height[1] = item->height[2] = AV_CEIL_RSHIFT(inlink->h, s->desc->log2_chroma_h);
260 4 item->height[0] = item->height[3] = inlink->h;
261
262
2/2
✓ Branch 29 → 30 taken 2 times.
✓ Branch 29 → 36 taken 2 times.
4 if (i) {
263
1/2
✗ Branch 31 → 32 not taken.
✓ Branch 31 → 33 taken 2 times.
2 if ((ret = av_image_fill_linesizes(item->x, inlink->format, width)) < 0) {
264 ✗ return ret;
265 }
266
267
1/2
✗ Branch 33 → 34 not taken.
✓ Branch 33 → 35 taken 2 times.
2 if (width > INT_MAX - ctx->inputs[i]->w)
268 ✗ return AVERROR(EINVAL);
269 2 width += ctx->inputs[i]->w;
270 }
271 }
272
1/4
✗ Branch 38 → 39 not taken.
✓ Branch 38 → 66 taken 3 times.
✗ Branch 39 → 40 not taken.
✗ Branch 39 → 66 not taken.
3 } else if (s->nb_grid_rows && s->nb_grid_columns) {
273 ✗ int inw = 0, inh = 0;
274 ✗ int k = 0;
275 int row_height;
276 ✗ height = 0;
277 ✗ width = 0;
278
0/2
✗ Branch 64 → 41 not taken.
✗ Branch 64 → 65 not taken.
✗ for (i = 0; i < s->nb_grid_rows; i++, inh += row_height) {
279 ✗ row_height = ctx->inputs[i * s->nb_grid_columns]->h;
280 ✗ inw = 0;
281
0/2
✗ Branch 54 → 42 not taken.
✗ Branch 54 → 55 not taken.
✗ for (int j = 0; j < s->nb_grid_columns; j++, k++) {
282 ✗ AVFilterLink *inlink = ctx->inputs[k];
283 ✗ StackItem *item = &s->items[k];
284
285
0/2
✗ Branch 42 → 43 not taken.
✗ Branch 42 → 45 not taken.
✗ if (ctx->inputs[k]->h != row_height) {
286 ✗ av_log(ctx, AV_LOG_ERROR, "Input %d height %d does not match current row's height %d.\n",
287 ✗ k, ctx->inputs[k]->h, row_height);
288 ✗ return AVERROR(EINVAL);
289 }
290
291
0/2
✗ Branch 46 → 47 not taken.
✗ Branch 46 → 48 not taken.
✗ if ((ret = av_image_fill_linesizes(item->linesize, inlink->format, inlink->w)) < 0) {
292 ✗ return ret;
293 }
294
295 ✗ item->height[1] = item->height[2] = AV_CEIL_RSHIFT(inlink->h, s->desc->log2_chroma_h);
296 ✗ item->height[0] = item->height[3] = inlink->h;
297
298
0/2
✗ Branch 49 → 50 not taken.
✗ Branch 49 → 51 not taken.
✗ if ((ret = av_image_fill_linesizes(item->x, inlink->format, inw)) < 0) {
299 ✗ return ret;
300 }
301
302 ✗ item->y[1] = item->y[2] = AV_CEIL_RSHIFT(inh, s->desc->log2_chroma_h);
303 ✗ item->y[0] = item->y[3] = inh;
304
305
0/2
✗ Branch 51 → 52 not taken.
✗ Branch 51 → 53 not taken.
✗ if (inw > INT_MAX - ctx->inputs[k]->w)
306 ✗ return AVERROR(EINVAL);
307 ✗ inw += ctx->inputs[k]->w;
308 }
309
0/2
✗ Branch 55 → 56 not taken.
✗ Branch 55 → 57 not taken.
✗ if (height > INT_MAX - row_height)
310 ✗ return AVERROR(EINVAL);
311 ✗ height += row_height;
312
0/2
✗ Branch 57 → 58 not taken.
✗ Branch 57 → 59 not taken.
✗ if (!i)
313 ✗ width = inw;
314
0/4
✗ Branch 59 → 60 not taken.
✗ Branch 59 → 63 not taken.
✗ Branch 60 → 61 not taken.
✗ Branch 60 → 63 not taken.
✗ if (i && width != inw) {
315 ✗ av_log(ctx, AV_LOG_ERROR, "Row %d width %d does not match previous row width %d.\n", i, inw, width);
316 ✗ return AVERROR(EINVAL);
317 }
318 }
319 } else {
320 3 char *arg, *p = s->layout, *saveptr = NULL;
321 3 char *arg2, *p2, *saveptr2 = NULL;
322 3 char *arg3, *p3, *saveptr3 = NULL;
323 int inw, inh, size;
324
325
1/2
✓ Branch 66 → 67 taken 3 times.
✗ Branch 66 → 72 not taken.
3 if (s->fillcolor_enable) {
326 3 const AVFilterLink *inlink = ctx->inputs[0];
327 3 ret = ff_draw_init_from_link(&s->draw, inlink, 0);
328
1/2
✗ Branch 68 → 69 not taken.
✓ Branch 68 → 71 taken 3 times.
3 if (ret < 0) {
329 ✗ av_log(ctx, AV_LOG_ERROR, "Failed to initialize FFDrawContext\n");
330 ✗ return ret;
331 }
332 3 ff_draw_color(&s->draw, &s->color, s->fillcolor);
333 }
334
335
2/2
✓ Branch 131 → 73 taken 12 times.
✓ Branch 131 → 132 taken 3 times.
15 for (i = 0; i < s->nb_inputs; i++) {
336 12 AVFilterLink *inlink = ctx->inputs[i];
337 12 StackItem *item = &s->items[i];
338
339
1/2
✗ Branch 74 → 75 not taken.
✓ Branch 74 → 76 taken 12 times.
12 if (!(arg = av_strtok(p, "|", &saveptr)))
340 ✗ return AVERROR(EINVAL);
341
342 12 p = NULL;
343
344
1/2
✗ Branch 77 → 78 not taken.
✓ Branch 77 → 79 taken 12 times.
12 if ((ret = av_image_fill_linesizes(item->linesize, inlink->format, inlink->w)) < 0) {
345 ✗ return ret;
346 }
347
348 12 item->height[1] = item->height[2] = AV_CEIL_RSHIFT(inlink->h, s->desc->log2_chroma_h);
349 12 item->height[0] = item->height[3] = inlink->h;
350
351 12 p2 = arg;
352 12 inw = inh = 0;
353
354
2/2
✓ Branch 123 → 80 taken 24 times.
✓ Branch 123 → 124 taken 12 times.
36 for (int j = 0; j < 2; j++) {
355
1/2
✗ Branch 81 → 82 not taken.
✓ Branch 81 → 83 taken 24 times.
24 if (!(arg2 = av_strtok(p2, "_", &saveptr2)))
356 ✗ return AVERROR(EINVAL);
357
358 24 p2 = NULL;
359 24 p3 = arg2;
360
2/2
✓ Branch 121 → 84 taken 24 times.
✓ Branch 121 → 122 taken 24 times.
48 while ((arg3 = av_strtok(p3, "+", &saveptr3))) {
361 24 p3 = NULL;
362
1/2
✗ Branch 84 → 85 not taken.
✓ Branch 84 → 96 taken 24 times.
24 if (sscanf(arg3, "w%d", &size) == 1) {
363
0/6
✗ Branch 85 → 86 not taken.
✗ Branch 85 → 88 not taken.
✗ Branch 86 → 87 not taken.
✗ Branch 86 → 88 not taken.
✗ Branch 87 → 88 not taken.
✗ Branch 87 → 89 not taken.
✗ if (size == i || size < 0 || size >= s->nb_inputs)
364 ✗ return AVERROR(EINVAL);
365
366
0/2
✗ Branch 89 → 90 not taken.
✗ Branch 89 → 93 not taken.
✗ if (!j) {
367
0/2
✗ Branch 90 → 91 not taken.
✗ Branch 90 → 92 not taken.
✗ if (inw > INT_MAX - ctx->inputs[size]->w)
368 ✗ return AVERROR(EINVAL);
369 ✗ inw += ctx->inputs[size]->w;
370 } else {
371
0/2
✗ Branch 93 → 94 not taken.
✗ Branch 93 → 95 not taken.
✗ if (inh > INT_MAX - ctx->inputs[size]->w)
372 ✗ return AVERROR(EINVAL);
373 ✗ inh += ctx->inputs[size]->w;
374 }
375
1/2
✗ Branch 96 → 97 not taken.
✓ Branch 96 → 108 taken 24 times.
24 } else if (sscanf(arg3, "h%d", &size) == 1) {
376
0/6
✗ Branch 97 → 98 not taken.
✗ Branch 97 → 100 not taken.
✗ Branch 98 → 99 not taken.
✗ Branch 98 → 100 not taken.
✗ Branch 99 → 100 not taken.
✗ Branch 99 → 101 not taken.
✗ if (size == i || size < 0 || size >= s->nb_inputs)
377 ✗ return AVERROR(EINVAL);
378
379
0/2
✗ Branch 101 → 102 not taken.
✗ Branch 101 → 105 not taken.
✗ if (!j) {
380
0/2
✗ Branch 102 → 103 not taken.
✗ Branch 102 → 104 not taken.
✗ if (inw > INT_MAX - ctx->inputs[size]->h)
381 ✗ return AVERROR(EINVAL);
382 ✗ inw += ctx->inputs[size]->h;
383 } else {
384
0/2
✗ Branch 105 → 106 not taken.
✗ Branch 105 → 107 not taken.
✗ if (inh > INT_MAX - ctx->inputs[size]->h)
385 ✗ return AVERROR(EINVAL);
386 ✗ inh += ctx->inputs[size]->h;
387 }
388
1/2
✓ Branch 108 → 109 taken 24 times.
✗ Branch 108 → 118 not taken.
24 } else if (sscanf(arg3, "%d", &size) == 1) {
389
1/2
✗ Branch 109 → 110 not taken.
✓ Branch 109 → 111 taken 24 times.
24 if (size < 0)
390 ✗ return AVERROR(EINVAL);
391
392
2/2
✓ Branch 111 → 112 taken 12 times.
✓ Branch 111 → 115 taken 12 times.
24 if (!j) {
393
1/2
✗ Branch 112 → 113 not taken.
✓ Branch 112 → 114 taken 12 times.
12 if (inw > INT_MAX - size)
394 ✗ return AVERROR(EINVAL);
395 12 inw += size;
396 } else {
397
1/2
✗ Branch 115 → 116 not taken.
✓ Branch 115 → 117 taken 12 times.
12 if (inh > INT_MAX - size)
398 ✗ return AVERROR(EINVAL);
399 12 inh += size;
400 }
401 } else {
402 ✗ return AVERROR(EINVAL);
403 }
404 }
405 }
406
407
1/2
✗ Branch 125 → 126 not taken.
✓ Branch 125 → 127 taken 12 times.
12 if ((ret = av_image_fill_linesizes(item->x, inlink->format, inw)) < 0) {
408 ✗ return ret;
409 }
410
411 12 item->y[1] = item->y[2] = AV_CEIL_RSHIFT(inh, s->desc->log2_chroma_h);
412 12 item->y[0] = item->y[3] = inh;
413
414
2/4
✓ Branch 127 → 128 taken 12 times.
✗ Branch 127 → 129 not taken.
✗ Branch 128 → 129 not taken.
✓ Branch 128 → 130 taken 12 times.
12 if (inlink->w > INT_MAX - inw || inlink->h > INT_MAX - inh)
415 ✗ return AVERROR(EINVAL);
416 12 width = FFMAX(width, inlink->w + inw);
417 12 height = FFMAX(height, inlink->h + inh);
418 }
419 }
420
421 6 s->nb_planes = av_pix_fmt_count_planes(outlink->format);
422
423 6 outlink->w = width;
424 6 outlink->h = height;
425 6 ol->frame_rate = frame_rate;
426 6 outlink->sample_aspect_ratio = sar;
427
428
2/2
✓ Branch 143 → 136 taken 12 times.
✓ Branch 143 → 144 taken 5 times.
17 for (i = 1; i < s->nb_inputs; i++) {
429 12 il = ff_filter_link(ctx->inputs[i]);
430
2/2
✓ Branch 137 → 138 taken 11 times.
✓ Branch 137 → 139 taken 1 time.
12 if (ol->frame_rate.num != il->frame_rate.num ||
431
1/2
✗ Branch 138 → 139 not taken.
✓ Branch 138 → 142 taken 11 times.
11 ol->frame_rate.den != il->frame_rate.den) {
432 1 av_log(ctx, AV_LOG_VERBOSE,
433 "Video inputs have different frame rates, output will be VFR\n");
434 1 ol->frame_rate = av_make_q(1, 0);
435 1 break;
436 }
437 }
438
439
1/2
✗ Branch 145 → 146 not taken.
✓ Branch 145 → 147 taken 6 times.
6 if ((ret = ff_framesync_init(&s->fs, ctx, s->nb_inputs)) < 0)
440 ✗ return ret;
441
442 6 in = s->fs.in;
443 6 s->fs.opaque = s;
444 6 s->fs.on_event = process_frame;
445
446
2/2
✓ Branch 152 → 148 taken 18 times.
✓ Branch 152 → 153 taken 6 times.
24 for (i = 0; i < s->nb_inputs; i++) {
447 18 AVFilterLink *inlink = ctx->inputs[i];
448
449 18 in[i].time_base = inlink->time_base;
450 18 in[i].sync = 1;
451 18 in[i].before = EXT_STOP;
452
1/2
✗ Branch 148 → 149 not taken.
✓ Branch 148 → 150 taken 18 times.
18 in[i].after = s->shortest ? EXT_STOP : EXT_INFINITY;
453 }
454
455 6 ret = ff_framesync_configure(&s->fs);
456 6 outlink->time_base = s->fs.time_base;
457
458 6 return ret;
459 }
460
461 18 static av_cold void uninit(AVFilterContext *ctx)
462 {
463 18 StackContext *s = ctx->priv;
464
465 18 ff_framesync_uninit(&s->fs);
466 18 av_freep(&s->frames);
467 18 av_freep(&s->items);
468 18 }
469
470 601 static int activate(AVFilterContext *ctx)
471 {
472 601 StackContext *s = ctx->priv;
473 601 return ff_framesync_activate(&s->fs);
474 }
475
476 #define OFFSET(x) offsetof(StackContext, x)
477 #define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM
478 static const AVOption stack_options[] = {
479 { "inputs", "set number of inputs", OFFSET(nb_inputs), AV_OPT_TYPE_INT, {.i64=2}, 2, INT_MAX, .flags = FLAGS },
480 { "shortest", "force termination when the shortest input terminates", OFFSET(shortest), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, .flags = FLAGS },
481 { NULL },
482 };
483
484 AVFILTER_DEFINE_CLASS_EXT(stack, "(h|v)stack", stack_options);
485
486 static const AVFilterPad outputs[] = {
487 {
488 .name = "default",
489 .type = AVMEDIA_TYPE_VIDEO,
490 .config_props = config_output,
491 },
492 };
493
494 #if CONFIG_HSTACK_FILTER
495
496 const FFFilter ff_vf_hstack = {
497 .p.name = "hstack",
498 .p.description = NULL_IF_CONFIG_SMALL("Stack video inputs horizontally."),
499 .p.priv_class = &stack_class,
500 .p.flags = AVFILTER_FLAG_DYNAMIC_INPUTS | AVFILTER_FLAG_SLICE_THREADS,
501 .priv_size = sizeof(StackContext),
502 FILTER_OUTPUTS(outputs),
503 FILTER_QUERY_FUNC2(query_formats),
504 .init = init,
505 .uninit = uninit,
506 .activate = activate,
507 };
508
509 #endif /* CONFIG_HSTACK_FILTER */
510
511 #if CONFIG_VSTACK_FILTER
512
513 const FFFilter ff_vf_vstack = {
514 .p.name = "vstack",
515 .p.description = NULL_IF_CONFIG_SMALL("Stack video inputs vertically."),
516 .p.priv_class = &stack_class,
517 .p.flags = AVFILTER_FLAG_DYNAMIC_INPUTS | AVFILTER_FLAG_SLICE_THREADS,
518 .priv_size = sizeof(StackContext),
519 FILTER_OUTPUTS(outputs),
520 FILTER_QUERY_FUNC2(query_formats),
521 .init = init,
522 .uninit = uninit,
523 .activate = activate,
524 };
525
526 #endif /* CONFIG_VSTACK_FILTER */
527
528 #if CONFIG_XSTACK_FILTER
529
530 static const AVOption xstack_options[] = {
531 { "inputs", "set number of inputs", OFFSET(nb_inputs), AV_OPT_TYPE_INT, {.i64=2}, 2, INT_MAX, .flags = FLAGS },
532 { "layout", "set custom layout", OFFSET(layout), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, .flags = FLAGS },
533 { "grid", "set fixed size grid layout", OFFSET(nb_grid_columns), AV_OPT_TYPE_IMAGE_SIZE, {.str=NULL}, 0, 0, .flags = FLAGS },
534 { "shortest", "force termination when the shortest input terminates", OFFSET(shortest), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, .flags = FLAGS },
535 { "fill", "set the color for unused pixels", OFFSET(fillcolor_str), AV_OPT_TYPE_STRING, {.str = "none"}, .flags = FLAGS },
536 { NULL },
537 };
538
539 AVFILTER_DEFINE_CLASS(xstack);
540
541 const FFFilter ff_vf_xstack = {
542 .p.name = "xstack",
543 .p.description = NULL_IF_CONFIG_SMALL("Stack video inputs into custom layout."),
544 .p.priv_class = &xstack_class,
545 .p.flags = AVFILTER_FLAG_DYNAMIC_INPUTS | AVFILTER_FLAG_SLICE_THREADS,
546 .priv_size = sizeof(StackContext),
547 FILTER_OUTPUTS(outputs),
548 FILTER_QUERY_FUNC2(query_formats),
549 .init = init,
550 .uninit = uninit,
551 .activate = activate,
552 };
553
554 #endif /* CONFIG_XSTACK_FILTER */
555