FFmpeg
stack_internal.c
Go to the documentation of this file.
1 /*
2  * This file is part of FFmpeg.
3  *
4  * FFmpeg is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * FFmpeg is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with FFmpeg; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17  */
18 
19 #define OFFSET(x) offsetof(StackHWContext, x)
20 #define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM)
21 
22 #define SET_OUTPUT_REGION(region, rx, ry, rw, rh) do { \
23  region->x = rx; \
24  region->y = ry; \
25  region->width = rw; \
26  region->height = rh; \
27  } while (0)
28 
29 static int init_framesync(AVFilterContext *avctx)
30 {
31  StackBaseContext *sctx = avctx->priv;
32  int ret;
33 
34  ret = ff_framesync_init(&sctx->fs, avctx, avctx->nb_inputs);
35  if (ret < 0)
36  return ret;
37 
38  sctx->fs.on_event = process_frame;
39  sctx->fs.opaque = sctx;
40 
41  for (int i = 0; i < sctx->nb_inputs; i++) {
42  FFFrameSyncIn *in = &sctx->fs.in[i];
43 
44  in->before = EXT_STOP;
45  in->after = sctx->shortest ? EXT_STOP : EXT_INFINITY;
46  in->sync = 1;
47  in->time_base = avctx->inputs[i]->time_base;
48  }
49 
50  return ff_framesync_configure(&sctx->fs);
51 }
52 
53 static int config_comm_output(AVFilterLink *outlink)
54 {
55  FilterLink *outl = ff_filter_link(outlink);
56  AVFilterContext *avctx = outlink->src;
57  StackBaseContext *sctx = avctx->priv;
58  AVFilterLink *inlink0 = avctx->inputs[0];
59  FilterLink *inl0 = ff_filter_link(inlink0);
60  int width, height, ret;
61 
62  if (sctx->mode == STACK_H) {
63  height = sctx->tile_height;
64  width = 0;
65 
66  if (!height)
67  height = inlink0->h;
68 
69  for (int i = 0; i < sctx->nb_inputs; i++) {
70  AVFilterLink *inlink = avctx->inputs[i];
71  StackItemRegion *region = &sctx->regions[i];
72 
74  width += av_rescale(height, inlink->w, inlink->h);
75  }
76  } else if (sctx->mode == STACK_V) {
77  height = 0;
78  width = sctx->tile_width;
79 
80  if (!width)
81  width = inlink0->w;
82 
83  for (int i = 0; i < sctx->nb_inputs; i++) {
84  AVFilterLink *inlink = avctx->inputs[i];
85  StackItemRegion *region = &sctx->regions[i];
86 
88  height += av_rescale(width, inlink->h, inlink->w);
89  }
90  } else if (sctx->nb_grid_rows && sctx->nb_grid_columns) {
91  int xpos = 0, ypos = 0;
92  int ow, oh, k = 0;
93 
94  ow = sctx->tile_width;
95  oh = sctx->tile_height;
96 
97  if (!ow || !oh) {
98  ow = avctx->inputs[0]->w;
99  oh = avctx->inputs[0]->h;
100  }
101 
102  for (int i = 0; i < sctx->nb_grid_columns; i++) {
103  ypos = 0;
104 
105  for (int j = 0; j < sctx->nb_grid_rows; j++) {
106  StackItemRegion *region = &sctx->regions[k++];
107 
108  SET_OUTPUT_REGION(region, xpos, ypos, ow, oh);
109  ypos += oh;
110  }
111 
112  xpos += ow;
113  }
114 
115  width = ow * sctx->nb_grid_columns;
116  height = oh * sctx->nb_grid_rows;
117  } else {
118  char *arg, *p = sctx->layout, *saveptr = NULL;
119  char *arg2, *p2, *saveptr2 = NULL;
120  char *arg3, *p3, *saveptr3 = NULL;
121  int xpos, ypos, size;
122  int ow, oh;
123 
124  width = 0;
125  height = 0;
126 
127  for (int i = 0; i < sctx->nb_inputs; i++) {
128  AVFilterLink *inlink = avctx->inputs[i];
129  StackItemRegion *region = &sctx->regions[i];
130 
131  ow = inlink->w;
132  oh = inlink->h;
133 
134  if (!(arg = av_strtok(p, "|", &saveptr)))
135  return AVERROR(EINVAL);
136 
137  p = NULL;
138  p2 = arg;
139  xpos = ypos = 0;
140 
141  for (int j = 0; j < 3; j++) {
142  if (!(arg2 = av_strtok(p2, "_", &saveptr2))) {
143  if (j == 2)
144  break;
145  else
146  return AVERROR(EINVAL);
147  }
148 
149  p2 = NULL;
150  p3 = arg2;
151 
152  if (j == 2) {
153  if ((ret = av_parse_video_size(&ow, &oh, p3)) < 0) {
154  av_log(avctx, AV_LOG_ERROR, "Invalid size '%s'\n", p3);
155  return ret;
156  }
157 
158  break;
159  }
160 
161  while ((arg3 = av_strtok(p3, "+", &saveptr3))) {
162  p3 = NULL;
163  if (sscanf(arg3, "w%d", &size) == 1) {
164  if (size == i || size < 0 || size >= sctx->nb_inputs)
165  return AVERROR(EINVAL);
166 
167  if (!j)
168  xpos += sctx->regions[size].width;
169  else
170  ypos += sctx->regions[size].width;
171  } else if (sscanf(arg3, "h%d", &size) == 1) {
172  if (size == i || size < 0 || size >= sctx->nb_inputs)
173  return AVERROR(EINVAL);
174 
175  if (!j)
176  xpos += sctx->regions[size].height;
177  else
178  ypos += sctx->regions[size].height;
179  } else if (sscanf(arg3, "%d", &size) == 1) {
180  if (size < 0)
181  return AVERROR(EINVAL);
182 
183  if (!j)
184  xpos += size;
185  else
186  ypos += size;
187  } else {
188  return AVERROR(EINVAL);
189  }
190  }
191  }
192 
193  SET_OUTPUT_REGION(region, xpos, ypos, ow, oh);
194  width = FFMAX(width, xpos + ow);
195  height = FFMAX(height, ypos + oh);
196  }
197 
198  }
199 
200  outlink->w = width;
201  outlink->h = height;
202  outl->frame_rate = inl0->frame_rate;
203  outlink->sample_aspect_ratio = inlink0->sample_aspect_ratio;
204 
205  for (int i = 1; i < sctx->nb_inputs; i++) {
207  if (outl->frame_rate.num != inlink->frame_rate.num ||
208  outl->frame_rate.den != inlink->frame_rate.den) {
209  av_log(avctx, AV_LOG_VERBOSE,
210  "Video inputs have different frame rates, output will be VFR\n");
211  outl->frame_rate = av_make_q(1, 0);
212  break;
213  }
214  }
215 
216  ret = init_framesync(avctx);
217  if (ret < 0)
218  return ret;
219 
220  outlink->time_base = sctx->fs.time_base;
221 
222  return 0;
223 }
224 
225 static int stack_init(AVFilterContext *avctx)
226 {
227  StackBaseContext *sctx = avctx->priv;
228  int ret;
229 
230  if (!strcmp(avctx->filter->name, HSTACK_NAME))
231  sctx->mode = STACK_H;
232  else if (!strcmp(avctx->filter->name, VSTACK_NAME))
233  sctx->mode = STACK_V;
234  else {
235  int is_grid;
236 
237  av_assert0(strcmp(avctx->filter->name, XSTACK_NAME) == 0);
238  sctx->mode = STACK_X;
239  is_grid = sctx->nb_grid_rows && sctx->nb_grid_columns;
240 
241  if (sctx->layout && is_grid) {
242  av_log(avctx, AV_LOG_ERROR, "Both layout and grid were specified. Only one is allowed.\n");
243  return AVERROR(EINVAL);
244  }
245 
246  if (!sctx->layout && !is_grid) {
247  if (sctx->nb_inputs == 2) {
248  sctx->nb_grid_rows = 1;
249  sctx->nb_grid_columns = 2;
250  is_grid = 1;
251  } else {
252  av_log(avctx, AV_LOG_ERROR, "No layout or grid specified.\n");
253  return AVERROR(EINVAL);
254  }
255  }
256 
257  if (is_grid)
258  sctx->nb_inputs = sctx->nb_grid_rows * sctx->nb_grid_columns;
259 
260  if (strcmp(sctx->fillcolor_str, "none") &&
261  av_parse_color(sctx->fillcolor, sctx->fillcolor_str, -1, avctx) >= 0) {
262  sctx->fillcolor_enable = 1;
263  } else {
264  sctx->fillcolor_enable = 0;
265  }
266  }
267 
268  for (int i = 0; i < sctx->nb_inputs; i++) {
269  AVFilterPad pad = { 0 };
270 
271  pad.type = AVMEDIA_TYPE_VIDEO;
272  pad.name = av_asprintf("input%d", i);
273 
274  if (!pad.name)
275  return AVERROR(ENOMEM);
276 
277  if ((ret = ff_append_inpad_free_name(avctx, &pad)) < 0)
278  return ret;
279  }
280 
281  sctx->regions = av_calloc(sctx->nb_inputs, sizeof(*sctx->regions));
282  if (!sctx->regions)
283  return AVERROR(ENOMEM);
284 
285  return 0;
286 }
287 
289 {
290  StackBaseContext *sctx = avctx->priv;
291 
292  av_freep(&sctx->regions);
293  ff_framesync_uninit(&sctx->fs);
294 }
295 
296 static int stack_activate(AVFilterContext *avctx)
297 {
298  StackBaseContext *sctx = avctx->priv;
299  return ff_framesync_activate(&sctx->fs);
300 }
301 
302 static const AVFilterPad stack_outputs[] = {
303  {
304  .name = "default",
305  .type = AVMEDIA_TYPE_VIDEO,
306  .config_props = config_output,
307  },
308 };
309 
310 #define STACK_COMMON_OPTS \
311  { "inputs", "Set number of inputs", OFFSET(base.nb_inputs), AV_OPT_TYPE_INT, { .i64 = 2 }, 2, UINT16_MAX, .flags = FLAGS }, \
312  { "shortest", "Force termination when the shortest input terminates", OFFSET(base.shortest), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS },
313 
314 #define DEFINE_HSTACK_OPTIONS(api) \
315  static const AVOption hstack_##api##_options[] = { \
316  STACK_COMMON_OPTS \
317  { "height", "Set output height (0 to use the height of input 0)", OFFSET(base.tile_height), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, UINT16_MAX, FLAGS }, \
318  { NULL } \
319  }
320 
321 #define DEFINE_VSTACK_OPTIONS(api) \
322  static const AVOption vstack_##api##_options[] = { \
323  STACK_COMMON_OPTS \
324  { "width", "Set output width (0 to use the width of input 0)", OFFSET(base.tile_width), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, UINT16_MAX, FLAGS }, \
325  { NULL } \
326  }
327 
328 #define DEFINE_XSTACK_OPTIONS(api) \
329  static const AVOption xstack_##api##_options[] = { \
330  STACK_COMMON_OPTS \
331  { "layout", "Set custom layout", OFFSET(base.layout), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, .flags = FLAGS }, \
332  { "grid", "set fixed size grid layout", OFFSET(base.nb_grid_columns), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, .flags = FLAGS }, \
333  { "grid_tile_size", "set tile size in grid layout", OFFSET(base.tile_width), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, .flags = FLAGS }, \
334  { "fill", "Set the color for unused pixels", OFFSET(base.fillcolor_str), AV_OPT_TYPE_STRING, {.str = "none"}, .flags = FLAGS }, \
335  { NULL } \
336  }
337 
338 #define DEFINE_STACK_FILTER(category, api, capi, filter_flags) \
339  static const AVClass category##_##api##_class = { \
340  .class_name = #category "_" #api, \
341  .item_name = av_default_item_name, \
342  .option = category##_##api##_options, \
343  .version = LIBAVUTIL_VERSION_INT, \
344  }; \
345  const AVFilter ff_vf_##category##_##api = { \
346  .name = #category "_" #api, \
347  .description = NULL_IF_CONFIG_SMALL(#capi " " #category), \
348  .priv_size = sizeof(StackHWContext), \
349  .priv_class = &category##_##api##_class, \
350  .init = api##_stack_init, \
351  .uninit = api##_stack_uninit, \
352  .activate = stack_activate, \
353  FILTER_PIXFMTS_ARRAY(api ## _stack_pix_fmts), \
354  FILTER_OUTPUTS(stack_outputs), \
355  .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, \
356  .flags = AVFILTER_FLAG_DYNAMIC_INPUTS | filter_flags, \
357  }
FFFrameSyncIn::time_base
AVRational time_base
Time base for the incoming frames.
Definition: framesync.h:117
ff_framesync_configure
int ff_framesync_configure(FFFrameSync *fs)
Configure a frame sync structure.
Definition: framesync.c:137
AVERROR
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFrame structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample they are references to shared objects When the negotiation mechanism computes the intersection of the formats supported at each end of a all references to both lists are replaced with a reference to the intersection And when a single format is eventually chosen for a link amongst the remaining all references to the list are updated That means that if a filter requires that its input and output have the same format amongst a supported all it has to do is use a reference to the same list of formats query_formats can leave some formats unset and return AVERROR(EAGAIN) to cause the negotiation mechanism toagain later. That can be used by filters with complex requirements to use the format negotiated on one link to set the formats supported on another. Frame references ownership and permissions
ff_framesync_uninit
void ff_framesync_uninit(FFFrameSync *fs)
Free all memory currently allocated.
Definition: framesync.c:301
av_parse_color
int av_parse_color(uint8_t *rgba_color, const char *color_string, int slen, void *log_ctx)
Put the RGBA values that correspond to color_string in rgba_color.
Definition: parseutils.c:359
inlink
The exact code depends on how similar the blocks are and how related they are to the and needs to apply these operations to the correct inlink or outlink if there are several Macros are available to factor that when no extra processing is inlink
Definition: filter_design.txt:212
av_asprintf
char * av_asprintf(const char *fmt,...)
Definition: avstring.c:115
FFFrameSync::time_base
AVRational time_base
Time base for the output events.
Definition: framesync.h:184
StackBaseContext::fillcolor_str
char * fillcolor_str
Definition: stack_internal.h:52
AV_LOG_VERBOSE
#define AV_LOG_VERBOSE
Detailed information.
Definition: log.h:225
FFMAX
#define FFMAX(a, b)
Definition: macros.h:47
AVFilter::name
const char * name
Filter name.
Definition: avfilter.h:205
EXT_INFINITY
@ EXT_INFINITY
Extend the frame to infinity.
Definition: framesync.h:75
StackBaseContext::tile_width
int tile_width
Definition: stack_internal.h:47
StackItemRegion::height
int height
Definition: stack_internal.h:32
process_frame
static av_always_inline int process_frame(WriterContext *w, InputFile *ifile, AVFrame *frame, const AVPacket *pkt, int *packet_new)
Definition: ffprobe.c:3018
StackBaseContext::fs
FFFrameSync fs
Definition: stack_internal.h:38
StackBaseContext::nb_grid_rows
int nb_grid_rows
Definition: stack_internal.h:50
EXT_STOP
@ EXT_STOP
Completely stop all streams with this one.
Definition: framesync.h:65
FFFrameSync::on_event
int(* on_event)(struct FFFrameSync *fs)
Callback called when a frame event is ready.
Definition: framesync.h:194
StackBaseContext::layout
char * layout
Definition: stack_internal.h:51
AVFilterContext::priv
void * priv
private data for use by the filter
Definition: avfilter.h:472
FFFrameSyncIn
Input stream structure.
Definition: framesync.h:102
StackItemRegion::width
int width
Definition: stack_internal.h:31
FFFrameSyncIn::sync
unsigned sync
Synchronization level: frames on input at the highest sync level will generate output frame events.
Definition: framesync.h:160
AVRational::num
int num
Numerator.
Definition: rational.h:59
AVFilterPad
A filter pad used for either input or output.
Definition: filters.h:38
AV_LOG_ERROR
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:209
av_cold
#define av_cold
Definition: attributes.h:90
stack_uninit
static av_cold void stack_uninit(AVFilterContext *avctx)
Definition: stack_internal.c:288
HSTACK_NAME
#define HSTACK_NAME
Definition: vf_stack_qsv.c:45
av_strtok
char * av_strtok(char *s, const char *delim, char **saveptr)
Split the string into several tokens which can be accessed by successive calls to av_strtok().
Definition: avstring.c:178
av_assert0
#define av_assert0(cond)
assert() equivalent, that is always enabled.
Definition: avassert.h:40
STACK_X
@ STACK_X
Definition: stack_internal.h:25
StackBaseContext::fillcolor
uint8_t fillcolor[4]
Definition: stack_internal.h:40
StackBaseContext::tile_height
int tile_height
Definition: stack_internal.h:48
arg
const char * arg
Definition: jacosubdec.c:67
StackItemRegion
Definition: stack_internal.h:28
StackBaseContext::nb_grid_columns
int nb_grid_columns
Definition: stack_internal.h:49
FFFrameSync::in
FFFrameSyncIn * in
Pointer to array of inputs.
Definition: framesync.h:225
STACK_V
@ STACK_V
Definition: stack_internal.h:24
NULL
#define NULL
Definition: coverity.c:32
ff_append_inpad_free_name
int ff_append_inpad_free_name(AVFilterContext *f, AVFilterPad *p)
Definition: avfilter.c:132
AVFilterContext::inputs
AVFilterLink ** inputs
array of pointers to input links
Definition: avfilter.h:465
ff_filter_link
static FilterLink * ff_filter_link(AVFilterLink *link)
Definition: filters.h:197
AVFilterContext::nb_inputs
unsigned nb_inputs
number of input pads
Definition: avfilter.h:466
height
#define height
Definition: dsp.h:85
config_output
static int config_output(AVFilterLink *outlink)
Definition: af_aap.c:190
FFFrameSync::opaque
void * opaque
Opaque pointer, not used by the API.
Definition: framesync.h:199
size
int size
Definition: twinvq_data.h:10344
av_make_q
static AVRational av_make_q(int num, int den)
Create an AVRational.
Definition: rational.h:71
init_framesync
static int init_framesync(AVFilterContext *avctx)
Definition: stack_internal.c:29
stack_outputs
static const AVFilterPad stack_outputs[]
Definition: stack_internal.c:302
av_parse_video_size
int av_parse_video_size(int *width_ptr, int *height_ptr, const char *str)
Parse str and put in width_ptr and height_ptr the detected values.
Definition: parseutils.c:150
SET_OUTPUT_REGION
#define SET_OUTPUT_REGION(region, rx, ry, rw, rh)
Definition: stack_internal.c:22
StackBaseContext::fillcolor_enable
int fillcolor_enable
Definition: stack_internal.h:41
StackBaseContext
Definition: stack_internal.h:35
StackBaseContext::nb_inputs
int nb_inputs
Definition: stack_internal.h:45
StackBaseContext::regions
StackItemRegion * regions
Definition: stack_internal.h:42
i
#define i(width, name, range_min, range_max)
Definition: cbs_h2645.c:256
VSTACK_NAME
#define VSTACK_NAME
Definition: vf_stack_qsv.c:46
config_comm_output
static int config_comm_output(AVFilterLink *outlink)
Definition: stack_internal.c:53
AVFilterPad::name
const char * name
Pad name.
Definition: filters.h:44
av_rescale
int64_t av_rescale(int64_t a, int64_t b, int64_t c)
Rescale a 64-bit integer with rounding to nearest.
Definition: mathematics.c:129
av_calloc
void * av_calloc(size_t nmemb, size_t size)
Definition: mem.c:264
ret
ret
Definition: filter_design.txt:187
AVFilterPad::type
enum AVMediaType type
AVFilterPad type.
Definition: filters.h:49
ff_framesync_init
int ff_framesync_init(FFFrameSync *fs, AVFilterContext *parent, unsigned nb_in)
Initialize a frame sync structure.
Definition: framesync.c:86
FFFrameSyncIn::before
enum FFFrameSyncExtMode before
Extrapolation mode for timestamps before the first frame.
Definition: framesync.h:107
AVRational::den
int den
Denominator.
Definition: rational.h:60
stack_activate
static int stack_activate(AVFilterContext *avctx)
Definition: stack_internal.c:296
XSTACK_NAME
#define XSTACK_NAME
Definition: vf_stack_qsv.c:47
stack_init
static int stack_init(AVFilterContext *avctx)
Definition: stack_internal.c:225
AVFilterContext
An instance of a filter.
Definition: avfilter.h:457
AVMEDIA_TYPE_VIDEO
@ AVMEDIA_TYPE_VIDEO
Definition: avutil.h:201
av_freep
#define av_freep(p)
Definition: tableprint_vlc.h:34
STACK_H
@ STACK_H
Definition: stack_internal.h:23
av_log
#define av_log(a,...)
Definition: tableprint_vlc.h:27
FFFrameSyncIn::after
enum FFFrameSyncExtMode after
Extrapolation mode for timestamps after the last frame.
Definition: framesync.h:112
ff_framesync_activate
int ff_framesync_activate(FFFrameSync *fs)
Examine the frames in the filter's input and try to produce output.
Definition: framesync.c:352
StackBaseContext::shortest
int shortest
Definition: stack_internal.h:46
AVFilterContext::filter
const AVFilter * filter
the AVFilter of which this is an instance
Definition: avfilter.h:460
width
#define width
Definition: dsp.h:85
StackBaseContext::mode
int mode
Definition: stack_internal.h:39