FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavfilter/buffersink.c
Date: 2026-07-19 01:11:25
Exec Total Coverage
Lines: 109 152 71.7%
Functions: 24 29 82.8%
Branches: 57 122 46.7%

Line Branch Exec Source
1 /*
2 * Copyright (c) 2011 Stefano Sabatini
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 /**
22 * @file
23 * buffer sink
24 */
25
26 #include "libavutil/avassert.h"
27 #include "libavutil/avstring.h"
28 #include "libavutil/channel_layout.h"
29 #include "libavutil/common.h"
30 #include "libavutil/internal.h"
31 #include "libavutil/mem.h"
32 #include "libavutil/opt.h"
33
34 #include "audio.h"
35 #include "avfilter.h"
36 #include "avfilter_internal.h"
37 #include "buffersink.h"
38 #include "filters.h"
39 #include "formats.h"
40 #include "framequeue.h"
41 #include "video.h"
42
43 typedef struct BufferSinkContext {
44 const AVClass *class;
45 unsigned warning_limit;
46 unsigned frame_size;
47
48 /* only used for video */
49 enum AVPixelFormat *pixel_formats;
50 unsigned nb_pixel_formats;
51
52 int *colorspaces;
53 unsigned nb_colorspaces;
54
55 int *colorranges;
56 unsigned nb_colorranges;
57
58 int *alphamodes;
59 unsigned nb_alphamodes;
60
61 /* only used for audio */
62 enum AVSampleFormat *sample_formats;
63 unsigned nb_sample_formats;
64
65 int *samplerates;
66 unsigned nb_samplerates;
67
68 AVChannelLayout *channel_layouts;
69 unsigned nb_channel_layouts;
70
71 AVFrame *peeked_frame;
72 } BufferSinkContext;
73
74 int attribute_align_arg av_buffersink_get_frame(AVFilterContext *ctx, AVFrame *frame)
75 {
76 return av_buffersink_get_frame_flags(ctx, frame, 0);
77 }
78
79 507521 static int return_or_keep_frame(BufferSinkContext *buf, AVFrame *out, AVFrame *in, int flags)
80 {
81
2/2
✓ Branch 0 taken 45500 times.
✓ Branch 1 taken 462021 times.
507521 if ((flags & AV_BUFFERSINK_FLAG_PEEK)) {
82 45500 buf->peeked_frame = in;
83
2/2
✓ Branch 0 taken 13960 times.
✓ Branch 1 taken 31540 times.
45500 return out ? av_frame_ref(out, in) : 0;
84 } else {
85 av_assert1(out);
86 462021 buf->peeked_frame = NULL;
87 462021 av_frame_move_ref(out, in);
88 462021 av_frame_free(&in);
89 462021 return 0;
90 }
91 }
92
93 1399006 static int get_frame_internal(AVFilterContext *ctx, AVFrame *frame, int flags, int samples)
94 {
95 1399006 BufferSinkContext *buf = ctx->priv;
96 1399006 AVFilterLink *inlink = ctx->inputs[0];
97 1399006 FilterLinkInternal *li = ff_link_internal(inlink);
98 int status, ret;
99 AVFrame *cur_frame;
100 int64_t pts;
101 1399006 int buffersrc_empty = 0;
102
103
2/2
✓ Branch 0 taken 1353512 times.
✓ Branch 1 taken 45494 times.
1399006 if (buf->peeked_frame)
104 45494 return return_or_keep_frame(buf, frame, buf->peeked_frame, flags);
105
106 while (1) {
107
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3864963 times.
3864963 ret = samples ? ff_inlink_consume_samples(inlink, samples, samples, &cur_frame) :
108 3864963 ff_inlink_consume_frame(inlink, &cur_frame);
109
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3864963 times.
3864963 if (ret < 0) {
110 return ret;
111
2/2
✓ Branch 0 taken 462027 times.
✓ Branch 1 taken 3402936 times.
3864963 } else if (ret) {
112 /* TODO return the frame instead of copying it */
113 462027 return return_or_keep_frame(buf, frame, cur_frame, flags);
114
2/2
✓ Branch 1 taken 9596 times.
✓ Branch 2 taken 3393340 times.
3402936 } else if (ff_inlink_acknowledge_status(inlink, &status, &pts)) {
115 9596 return status;
116
2/2
✓ Branch 0 taken 462848 times.
✓ Branch 1 taken 2930492 times.
3393340 } else if ((flags & AV_BUFFERSINK_FLAG_NO_REQUEST)) {
117 462848 return AVERROR(EAGAIN);
118
2/2
✓ Branch 0 taken 2469225 times.
✓ Branch 1 taken 461267 times.
2930492 } else if (li->frame_wanted_out) {
119 2469225 ret = ff_filter_graph_run_once(ctx->graph);
120
2/2
✓ Branch 0 taken 419485 times.
✓ Branch 1 taken 2049740 times.
2469225 if (ret == FFERROR_BUFFERSRC_EMPTY) {
121 419485 buffersrc_empty = 1;
122
2/2
✓ Branch 0 taken 423894 times.
✓ Branch 1 taken 1625846 times.
2049740 } else if (ret == AVERROR(EAGAIN)) {
123
2/2
✓ Branch 0 taken 419041 times.
✓ Branch 1 taken 4853 times.
423894 if (buffersrc_empty)
124 419041 return ret;
125 4853 ff_inlink_request_frame(inlink);
126
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1625846 times.
1625846 } else if (ret < 0) {
127 return ret;
128 }
129 } else {
130 461267 ff_inlink_request_frame(inlink);
131 }
132 }
133 }
134
135 1399006 int attribute_align_arg av_buffersink_get_frame_flags(AVFilterContext *ctx, AVFrame *frame, int flags)
136 {
137 1399006 return get_frame_internal(ctx, frame, flags,
138 1399006 ff_filter_link(ctx->inputs[0])->min_samples);
139 }
140
141 int attribute_align_arg av_buffersink_get_samples(AVFilterContext *ctx,
142 AVFrame *frame, int nb_samples)
143 {
144 return get_frame_internal(ctx, frame, 0, nb_samples);
145 }
146
147 8441 static av_cold int common_init(AVFilterContext *ctx)
148 {
149 8441 BufferSinkContext *buf = ctx->priv;
150
151 8441 buf->warning_limit = 100;
152 8441 return 0;
153 }
154
155 #define TERMINATE_ARRAY(arr, val) \
156 if (s->arr) { \
157 void *tmp = av_realloc_array(s->arr, s->nb_ ## arr + 1, sizeof(*s->arr)); \
158 if (!tmp) \
159 return AVERROR(ENOMEM); \
160 s->arr = tmp; \
161 s->arr[s->nb_ ## arr] = val; \
162 }
163
164 6996 static int init_video(AVFilterContext *ctx)
165 {
166 6996 BufferSinkContext *s = ctx->priv;
167
168
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 6996 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
6996 TERMINATE_ARRAY(pixel_formats, AV_PIX_FMT_NONE);
169
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 6996 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
6996 TERMINATE_ARRAY(colorranges, -1);
170
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 6996 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
6996 TERMINATE_ARRAY(colorspaces, -1);
171
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 6996 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
6996 TERMINATE_ARRAY(alphamodes, -1);
172
173 6996 return common_init(ctx);
174 }
175
176 1445 static int init_audio(AVFilterContext *ctx)
177 {
178 1445 BufferSinkContext *s = ctx->priv;
179
180
3/4
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 1417 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 28 times.
1445 TERMINATE_ARRAY(sample_formats, AV_SAMPLE_FMT_NONE);
181
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1445 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
1445 TERMINATE_ARRAY(samplerates, -1);
182
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1445 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
1445 TERMINATE_ARRAY(channel_layouts, (AVChannelLayout){ .nb_channels = 0 });
183
184 1445 return common_init(ctx);
185 }
186
187 #undef TERMINATE_ARRAY
188
189 8441 static void uninit(AVFilterContext *ctx)
190 {
191 8441 BufferSinkContext *buf = ctx->priv;
192
193 8441 av_frame_free(&buf->peeked_frame);
194 8441 }
195
196 468342 static int activate(AVFilterContext *ctx)
197 {
198 468342 BufferSinkContext *buf = ctx->priv;
199 468342 FilterLinkInternal * const li = ff_link_internal(ctx->inputs[0]);
200
201
1/2
✓ Branch 0 taken 468342 times.
✗ Branch 1 not taken.
468342 if (buf->warning_limit &&
202
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 468342 times.
468342 ff_framequeue_queued_frames(&li->fifo) >= buf->warning_limit) {
203 av_log(ctx, AV_LOG_WARNING,
204 "%d buffers queued in %s, something may be wrong.\n",
205 buf->warning_limit,
206 (char *)av_x_if_null(ctx->name, ctx->filter->name));
207 buf->warning_limit *= 10;
208 }
209
210 /* The frame is queued, the rest is up to get_frame_internal */
211 468342 return 0;
212 }
213
214 1445 static int config_input_audio(AVFilterLink *inlink)
215 {
216 1445 BufferSinkContext *buf = inlink->dst->priv;
217 1445 FilterLink *l = ff_filter_link(inlink);
218
219 1445 l->min_samples = l->max_samples = buf->frame_size;
220
221 1445 return 0;
222 }
223
224 void av_buffersink_set_frame_size(AVFilterContext *ctx, unsigned frame_size)
225 {
226 BufferSinkContext *buf = ctx->priv;
227 buf->frame_size = frame_size;
228
229 if (ctx->inputs && ctx->inputs[0]) {
230 FilterLink *l = ff_filter_link(ctx->inputs[0]);
231 l->min_samples = l->max_samples = buf->frame_size;
232 }
233 }
234
235 #define MAKE_AVFILTERLINK_ACCESSOR(type, field) \
236 type av_buffersink_get_##field(const AVFilterContext *ctx) { \
237 av_assert0(fffilter(ctx->filter)->activate == activate); \
238 return ctx->inputs[0]->field; \
239 }
240
241
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 70 times.
70 MAKE_AVFILTERLINK_ACCESSOR(enum AVMediaType , type )
242
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 470530 times.
470530 MAKE_AVFILTERLINK_ACCESSOR(AVRational , time_base )
243
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8440 times.
8440 MAKE_AVFILTERLINK_ACCESSOR(int , format )
244
245
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8412 times.
8412 MAKE_AVFILTERLINK_ACCESSOR(int , w )
246
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8412 times.
8412 MAKE_AVFILTERLINK_ACCESSOR(int , h )
247
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8412 times.
8412 MAKE_AVFILTERLINK_ACCESSOR(AVRational , sample_aspect_ratio)
248
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8370 times.
8370 MAKE_AVFILTERLINK_ACCESSOR(enum AVColorSpace, colorspace)
249
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8370 times.
8370 MAKE_AVFILTERLINK_ACCESSOR(enum AVColorRange, color_range)
250
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8370 times.
8370 MAKE_AVFILTERLINK_ACCESSOR(enum AVAlphaMode , alpha_mode)
251
252
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8398 times.
8398 MAKE_AVFILTERLINK_ACCESSOR(int , sample_rate )
253
254 10830 AVRational av_buffersink_get_frame_rate(const AVFilterContext *ctx)
255 {
256 10830 FilterLink *l = ff_filter_link(ctx->inputs[0]);
257
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 10830 times.
10830 av_assert0(fffilter(ctx->filter)->activate == activate);
258 10830 return l->frame_rate;
259 }
260
261 AVBufferRef* av_buffersink_get_hw_frames_ctx(const AVFilterContext *ctx)
262 {
263 FilterLink *l = ff_filter_link(ctx->inputs[0]);
264 av_assert0(fffilter(ctx->filter)->activate == activate);
265 return l->hw_frames_ctx;
266 }
267
268 int av_buffersink_get_channels(const AVFilterContext *ctx)
269 {
270 av_assert0(fffilter(ctx->filter)->activate == activate);
271 return ctx->inputs[0]->ch_layout.nb_channels;
272 }
273
274 8398 int av_buffersink_get_ch_layout(const AVFilterContext *ctx, AVChannelLayout *out)
275 {
276 8398 AVChannelLayout ch_layout = { 0 };
277 int ret;
278
279
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8398 times.
8398 av_assert0(fffilter(ctx->filter)->activate == activate);
280 8398 ret = av_channel_layout_copy(&ch_layout, &ctx->inputs[0]->ch_layout);
281
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8398 times.
8398 if (ret < 0)
282 return ret;
283 8398 *out = ch_layout;
284 8398 return 0;
285 }
286
287 8370 const AVFrameSideData *const *av_buffersink_get_side_data(const AVFilterContext *ctx,
288 int *nb_side_data)
289 {
290
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8370 times.
8370 av_assert0(fffilter(ctx->filter)->activate == activate);
291 8370 *nb_side_data = ctx->inputs[0]->nb_side_data;
292 8370 return (const AVFrameSideData *const *)ctx->inputs[0]->side_data;
293 }
294
295 6996 static int vsink_query_formats(const AVFilterContext *ctx,
296 AVFilterFormatsConfig **cfg_in,
297 AVFilterFormatsConfig **cfg_out)
298 {
299 6996 const BufferSinkContext *buf = ctx->priv;
300 int ret;
301
302
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6996 times.
6996 if (buf->nb_pixel_formats) {
303 ret = ff_set_pixel_formats_from_list2(ctx, cfg_in, cfg_out, buf->pixel_formats);
304 if (ret < 0)
305 return ret;
306 }
307
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6996 times.
6996 if (buf->nb_colorspaces) {
308 ret = ff_set_common_color_spaces_from_list2(ctx, cfg_in, cfg_out, buf->colorspaces);
309 if (ret < 0)
310 return ret;
311 }
312
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6996 times.
6996 if (buf->nb_colorranges) {
313 ret = ff_set_common_color_ranges_from_list2(ctx, cfg_in, cfg_out, buf->colorranges);
314 if (ret < 0)
315 return ret;
316 }
317
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6996 times.
6996 if (buf->nb_alphamodes) {
318 ret = ff_set_common_alpha_modes_from_list2(ctx, cfg_in, cfg_out, buf->alphamodes);
319 if (ret < 0)
320 return ret;
321 }
322
323 6996 return 0;
324 }
325
326 1445 static int asink_query_formats(const AVFilterContext *ctx,
327 AVFilterFormatsConfig **cfg_in,
328 AVFilterFormatsConfig **cfg_out)
329 {
330 1445 const BufferSinkContext *buf = ctx->priv;
331 int ret;
332
333
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 1417 times.
1445 if (buf->nb_sample_formats) {
334 28 ret = ff_set_sample_formats_from_list2(ctx, cfg_in, cfg_out, buf->sample_formats);
335
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 28 times.
28 if (ret < 0)
336 return ret;
337 }
338
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1445 times.
1445 if (buf->nb_samplerates) {
339 ret = ff_set_common_samplerates_from_list2(ctx, cfg_in, cfg_out, buf->samplerates);
340 if (ret < 0)
341 return ret;
342 }
343
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1445 times.
1445 if (buf->nb_channel_layouts) {
344 ret = ff_set_common_channel_layouts_from_list2(ctx, cfg_in, cfg_out, buf->channel_layouts);
345 if (ret < 0)
346 return ret;
347 }
348
349 1445 return 0;
350 }
351
352 #define OFFSET(x) offsetof(BufferSinkContext, x)
353 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
354 static const AVOption buffersink_options[] = {
355 { "pixel_formats", "array of supported pixel formats", OFFSET(pixel_formats),
356 AV_OPT_TYPE_PIXEL_FMT | AV_OPT_TYPE_FLAG_ARRAY, .max = INT_MAX, .flags = FLAGS },
357 { "colorspaces", "array of supported color spaces", OFFSET(colorspaces),
358 AV_OPT_TYPE_INT | AV_OPT_TYPE_FLAG_ARRAY, .max = INT_MAX, .flags = FLAGS },
359 { "colorranges", "array of supported color ranges", OFFSET(colorranges),
360 AV_OPT_TYPE_INT | AV_OPT_TYPE_FLAG_ARRAY, .max = INT_MAX, .flags = FLAGS },
361 { "alphamodes", "array of supported color ranges", OFFSET(alphamodes),
362 AV_OPT_TYPE_INT | AV_OPT_TYPE_FLAG_ARRAY, .max = INT_MAX, .flags = FLAGS },
363
364 { NULL },
365 };
366 #undef FLAGS
367 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_AUDIO_PARAM
368 static const AVOption abuffersink_options[] = {
369 { "sample_formats", "array of supported sample formats", OFFSET(sample_formats),
370 AV_OPT_TYPE_SAMPLE_FMT | AV_OPT_TYPE_FLAG_ARRAY, .max = INT_MAX, .flags = FLAGS },
371 { "samplerates", "array of supported sample formats", OFFSET(samplerates),
372 AV_OPT_TYPE_INT | AV_OPT_TYPE_FLAG_ARRAY, .max = INT_MAX, .flags = FLAGS },
373 { "channel_layouts", "array of supported channel layouts", OFFSET(channel_layouts),
374 AV_OPT_TYPE_CHLAYOUT | AV_OPT_TYPE_FLAG_ARRAY, .flags = FLAGS },
375 { NULL },
376 };
377 #undef FLAGS
378
379 AVFILTER_DEFINE_CLASS(buffersink);
380 AVFILTER_DEFINE_CLASS(abuffersink);
381
382 const FFFilter ff_vsink_buffer = {
383 .p.name = "buffersink",
384 .p.description = NULL_IF_CONFIG_SMALL("Buffer video frames, and make them available to the end of the filter graph."),
385 .p.priv_class = &buffersink_class,
386 .p.outputs = NULL,
387 .priv_size = sizeof(BufferSinkContext),
388 .init = init_video,
389 .uninit = uninit,
390 .activate = activate,
391 FILTER_INPUTS(ff_video_default_filterpad),
392 FILTER_QUERY_FUNC2(vsink_query_formats),
393 };
394
395 static const AVFilterPad inputs_audio[] = {
396 {
397 .name = "default",
398 .type = AVMEDIA_TYPE_AUDIO,
399 .config_props = config_input_audio,
400 },
401 };
402
403 const FFFilter ff_asink_abuffer = {
404 .p.name = "abuffersink",
405 .p.description = NULL_IF_CONFIG_SMALL("Buffer audio frames, and make them available to the end of the filter graph."),
406 .p.priv_class = &abuffersink_class,
407 .p.outputs = NULL,
408 .priv_size = sizeof(BufferSinkContext),
409 .init = init_audio,
410 .uninit = uninit,
411 .activate = activate,
412 FILTER_INPUTS(inputs_audio),
413 FILTER_QUERY_FUNC2(asink_query_formats),
414 };
415