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  AVFilterContext *avctx = outlink->src;
56  StackBaseContext *sctx = avctx->priv;
57  AVFilterLink *inlink0 = avctx->inputs[0];
58  int width, height, ret;
59 
60  if (sctx->mode == STACK_H) {
61  height = sctx->tile_height;
62  width = 0;
63 
64  if (!height)
65  height = inlink0->h;
66 
67  for (int i = 0; i < sctx->nb_inputs; i++) {
68  AVFilterLink *inlink = avctx->inputs[i];
69  StackItemRegion *region = &sctx->regions[i];
70 
72  width += av_rescale(height, inlink->w, inlink->h);
73  }
74  } else if (sctx->mode == STACK_V) {
75  height = 0;
76  width = sctx->tile_width;
77 
78  if (!width)
79  width = inlink0->w;
80 
81  for (int i = 0; i < sctx->nb_inputs; i++) {
82  AVFilterLink *inlink = avctx->inputs[i];
83  StackItemRegion *region = &sctx->regions[i];
84 
86  height += av_rescale(width, inlink->h, inlink->w);
87  }
88  } else if (sctx->nb_grid_rows && sctx->nb_grid_columns) {
89  int xpos = 0, ypos = 0;
90  int ow, oh, k = 0;
91 
92  ow = sctx->tile_width;
93  oh = sctx->tile_height;
94 
95  if (!ow || !oh) {
96  ow = avctx->inputs[0]->w;
97  oh = avctx->inputs[0]->h;
98  }
99 
100  for (int i = 0; i < sctx->nb_grid_columns; i++) {
101  ypos = 0;
102 
103  for (int j = 0; j < sctx->nb_grid_rows; j++) {
104  StackItemRegion *region = &sctx->regions[k++];
105 
106  SET_OUTPUT_REGION(region, xpos, ypos, ow, oh);
107  ypos += oh;
108  }
109 
110  xpos += ow;
111  }
112 
113  width = ow * sctx->nb_grid_columns;
114  height = oh * sctx->nb_grid_rows;
115  } else {
116  char *arg, *p = sctx->layout, *saveptr = NULL;
117  char *arg2, *p2, *saveptr2 = NULL;
118  char *arg3, *p3, *saveptr3 = NULL;
119  int xpos, ypos, size;
120  int ow, oh;
121 
122  width = 0;
123  height = 0;
124 
125  for (int i = 0; i < sctx->nb_inputs; i++) {
126  AVFilterLink *inlink = avctx->inputs[i];
127  StackItemRegion *region = &sctx->regions[i];
128 
129  ow = inlink->w;
130  oh = inlink->h;
131 
132  if (!(arg = av_strtok(p, "|", &saveptr)))
133  return AVERROR(EINVAL);
134 
135  p = NULL;
136  p2 = arg;
137  xpos = ypos = 0;
138 
139  for (int j = 0; j < 3; j++) {
140  if (!(arg2 = av_strtok(p2, "_", &saveptr2))) {
141  if (j == 2)
142  break;
143  else
144  return AVERROR(EINVAL);
145  }
146 
147  p2 = NULL;
148  p3 = arg2;
149 
150  if (j == 2) {
151  if ((ret = av_parse_video_size(&ow, &oh, p3)) < 0) {
152  av_log(avctx, AV_LOG_ERROR, "Invalid size '%s'\n", p3);
153  return ret;
154  }
155 
156  break;
157  }
158 
159  while ((arg3 = av_strtok(p3, "+", &saveptr3))) {
160  p3 = NULL;
161  if (sscanf(arg3, "w%d", &size) == 1) {
162  if (size == i || size < 0 || size >= sctx->nb_inputs)
163  return AVERROR(EINVAL);
164 
165  if (!j)
166  xpos += sctx->regions[size].width;
167  else
168  ypos += sctx->regions[size].width;
169  } else if (sscanf(arg3, "h%d", &size) == 1) {
170  if (size == i || size < 0 || size >= sctx->nb_inputs)
171  return AVERROR(EINVAL);
172 
173  if (!j)
174  xpos += sctx->regions[size].height;
175  else
176  ypos += sctx->regions[size].height;
177  } else if (sscanf(arg3, "%d", &size) == 1) {
178  if (size < 0)
179  return AVERROR(EINVAL);
180 
181  if (!j)
182  xpos += size;
183  else
184  ypos += size;
185  } else {
186  return AVERROR(EINVAL);
187  }
188  }
189  }
190 
191  SET_OUTPUT_REGION(region, xpos, ypos, ow, oh);
192  width = FFMAX(width, xpos + ow);
193  height = FFMAX(height, ypos + oh);
194  }
195 
196  }
197 
198  outlink->w = width;
199  outlink->h = height;
200  outlink->frame_rate = inlink0->frame_rate;
201  outlink->sample_aspect_ratio = inlink0->sample_aspect_ratio;
202 
203  for (int i = 1; i < sctx->nb_inputs; i++) {
204  AVFilterLink *inlink = avctx->inputs[i];
205  if (outlink->frame_rate.num != inlink->frame_rate.num ||
206  outlink->frame_rate.den != inlink->frame_rate.den) {
207  av_log(avctx, AV_LOG_VERBOSE,
208  "Video inputs have different frame rates, output will be VFR\n");
209  outlink->frame_rate = av_make_q(1, 0);
210  break;
211  }
212  }
213 
214  ret = init_framesync(avctx);
215  if (ret < 0)
216  return ret;
217 
218  outlink->time_base = sctx->fs.time_base;
219 
220  return 0;
221 }
222 
223 static int stack_init(AVFilterContext *avctx)
224 {
225  StackBaseContext *sctx = avctx->priv;
226  int ret;
227 
228  if (!strcmp(avctx->filter->name, HSTACK_NAME))
229  sctx->mode = STACK_H;
230  else if (!strcmp(avctx->filter->name, VSTACK_NAME))
231  sctx->mode = STACK_V;
232  else {
233  int is_grid;
234 
235  av_assert0(strcmp(avctx->filter->name, XSTACK_NAME) == 0);
236  sctx->mode = STACK_X;
237  is_grid = sctx->nb_grid_rows && sctx->nb_grid_columns;
238 
239  if (sctx->layout && is_grid) {
240  av_log(avctx, AV_LOG_ERROR, "Both layout and grid were specified. Only one is allowed.\n");
241  return AVERROR(EINVAL);
242  }
243 
244  if (!sctx->layout && !is_grid) {
245  if (sctx->nb_inputs == 2) {
246  sctx->nb_grid_rows = 1;
247  sctx->nb_grid_columns = 2;
248  is_grid = 1;
249  } else {
250  av_log(avctx, AV_LOG_ERROR, "No layout or grid specified.\n");
251  return AVERROR(EINVAL);
252  }
253  }
254 
255  if (is_grid)
256  sctx->nb_inputs = sctx->nb_grid_rows * sctx->nb_grid_columns;
257 
258  if (strcmp(sctx->fillcolor_str, "none") &&
259  av_parse_color(sctx->fillcolor, sctx->fillcolor_str, -1, avctx) >= 0) {
260  sctx->fillcolor_enable = 1;
261  } else {
262  sctx->fillcolor_enable = 0;
263  }
264  }
265 
266  for (int i = 0; i < sctx->nb_inputs; i++) {
267  AVFilterPad pad = { 0 };
268 
269  pad.type = AVMEDIA_TYPE_VIDEO;
270  pad.name = av_asprintf("input%d", i);
271 
272  if (!pad.name)
273  return AVERROR(ENOMEM);
274 
275  if ((ret = ff_append_inpad_free_name(avctx, &pad)) < 0)
276  return ret;
277  }
278 
279  sctx->regions = av_calloc(sctx->nb_inputs, sizeof(*sctx->regions));
280  if (!sctx->regions)
281  return AVERROR(ENOMEM);
282 
283  return 0;
284 }
285 
287 {
288  StackBaseContext *sctx = avctx->priv;
289 
290  av_freep(&sctx->regions);
291  ff_framesync_uninit(&sctx->fs);
292 }
293 
294 static int stack_activate(AVFilterContext *avctx)
295 {
296  StackBaseContext *sctx = avctx->priv;
297  return ff_framesync_activate(&sctx->fs);
298 }
299 
300 static const AVFilterPad stack_outputs[] = {
301  {
302  .name = "default",
303  .type = AVMEDIA_TYPE_VIDEO,
304  .config_props = config_output,
305  },
306 };
307 
308 #define STACK_COMMON_OPTS \
309  { "inputs", "Set number of inputs", OFFSET(base.nb_inputs), AV_OPT_TYPE_INT, { .i64 = 2 }, 2, UINT16_MAX, .flags = FLAGS }, \
310  { "shortest", "Force termination when the shortest input terminates", OFFSET(base.shortest), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS },
311 
312 #define DEFINE_HSTACK_OPTIONS(api) \
313  static const AVOption hstack_##api##_options[] = { \
314  STACK_COMMON_OPTS \
315  { "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 }, \
316  { NULL } \
317  }
318 
319 #define DEFINE_VSTACK_OPTIONS(api) \
320  static const AVOption vstack_##api##_options[] = { \
321  STACK_COMMON_OPTS \
322  { "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 }, \
323  { NULL } \
324  }
325 
326 #define DEFINE_XSTACK_OPTIONS(api) \
327  static const AVOption xstack_##api##_options[] = { \
328  STACK_COMMON_OPTS \
329  { "layout", "Set custom layout", OFFSET(base.layout), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, .flags = FLAGS }, \
330  { "grid", "set fixed size grid layout", OFFSET(base.nb_grid_columns), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, .flags = FLAGS }, \
331  { "grid_tile_size", "set tile size in grid layout", OFFSET(base.tile_width), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, .flags = FLAGS }, \
332  { "fill", "Set the color for unused pixels", OFFSET(base.fillcolor_str), AV_OPT_TYPE_STRING, {.str = "none"}, .flags = FLAGS }, \
333  { NULL } \
334  }
335 
336 #define DEFINE_STACK_FILTER(category, api, capi, filter_flags) \
337  static const AVClass category##_##api##_class = { \
338  .class_name = #category "_" #api, \
339  .item_name = av_default_item_name, \
340  .option = category##_##api##_options, \
341  .version = LIBAVUTIL_VERSION_INT, \
342  }; \
343  const AVFilter ff_vf_##category##_##api = { \
344  .name = #category "_" #api, \
345  .description = NULL_IF_CONFIG_SMALL(#capi " " #category), \
346  .priv_size = sizeof(StackHWContext), \
347  .priv_class = &category##_##api##_class, \
348  .init = api##_stack_init, \
349  .uninit = api##_stack_uninit, \
350  .activate = stack_activate, \
351  FILTER_QUERY_FUNC(api##_stack_query_formats), \
352  FILTER_OUTPUTS(stack_outputs), \
353  .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, \
354  .flags = AVFILTER_FLAG_DYNAMIC_INPUTS | filter_flags, \
355  }
STACK_V
@ STACK_V
Definition: stack_internal.h:24
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:134
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:304
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:356
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:196
FFMAX
#define FFMAX(a, b)
Definition: macros.h:47
AVFilter::name
const char * name
Filter name.
Definition: avfilter.h:170
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:2996
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:422
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: internal.h:33
AV_LOG_ERROR
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:180
av_cold
#define av_cold
Definition: attributes.h:90
stack_uninit
static av_cold void stack_uninit(AVFilterContext *avctx)
Definition: stack_internal.c:286
HSTACK_NAME
#define HSTACK_NAME
Definition: vf_stack_qsv.c:45
width
#define width
STACK_X
@ STACK_X
Definition: stack_internal.h:25
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_H
@ STACK_H
Definition: stack_internal.h:23
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
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:131
AVFilterContext::inputs
AVFilterLink ** inputs
array of pointers to input links
Definition: avfilter.h:415
AVFilterContext::nb_inputs
unsigned nb_inputs
number of input pads
Definition: avfilter.h:416
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:300
height
#define height
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:255
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: internal.h:39
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:262
ret
ret
Definition: filter_design.txt:187
AVFilterPad::type
enum AVMediaType type
AVFilterPad type.
Definition: internal.h:44
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:294
XSTACK_NAME
#define XSTACK_NAME
Definition: vf_stack_qsv.c:47
stack_init
static int stack_init(AVFilterContext *avctx)
Definition: stack_internal.c:223
AVFilterContext
An instance of a filter.
Definition: avfilter.h:407
AVMEDIA_TYPE_VIDEO
@ AVMEDIA_TYPE_VIDEO
Definition: avutil.h:201
av_freep
#define av_freep(p)
Definition: tableprint_vlc.h:34
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:355
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:410
StackBaseContext::mode
int mode
Definition: stack_internal.h:39