FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavfilter/buffersink.c
Date: 2026-10-19 08:11:58
Exec Total Coverage
Lines: 112 158 70.9%
Functions: 25 30 83.3%
Branches: 60 132 45.5%

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 int *chromalocations;
62 unsigned nb_chromalocations;
63
64 /* only used for audio */
65 enum AVSampleFormat *sample_formats;
66 unsigned nb_sample_formats;
67
68 int *samplerates;
69 unsigned nb_samplerates;
70
71 AVChannelLayout *channel_layouts;
72 unsigned nb_channel_layouts;
73
74 AVFrame *peeked_frame;
75 } BufferSinkContext;
76
77 ✗ int attribute_align_arg av_buffersink_get_frame(AVFilterContext *ctx, AVFrame *frame)
78 {
79 ✗ return av_buffersink_get_frame_flags(ctx, frame, 0);
80 }
81
82 518148 static int return_or_keep_frame(BufferSinkContext *buf, AVFrame *out, AVFrame *in, int flags)
83 {
84
2/2
✓ Branch 0 taken 46444 times.
✓ Branch 1 taken 471704 times.
518148 if ((flags & AV_BUFFERSINK_FLAG_PEEK)) {
85 46444 buf->peeked_frame = in;
86
2/2
✓ Branch 0 taken 14349 times.
✓ Branch 1 taken 32095 times.
46444 return out ? av_frame_ref(out, in) : 0;
87 } else {
88 av_assert1(out);
89 471704 buf->peeked_frame = NULL;
90 471704 av_frame_move_ref(out, in);
91 471704 av_frame_free(&in);
92 471704 return 0;
93 }
94 }
95
96 1437177 static int get_frame_internal(AVFilterContext *ctx, AVFrame *frame, int flags, int samples)
97 {
98 1437177 BufferSinkContext *buf = ctx->priv;
99 1437177 AVFilterLink *inlink = ctx->inputs[0];
100 1437177 FilterLinkInternal *li = ff_link_internal(inlink);
101 int status, ret;
102 AVFrame *cur_frame;
103 int64_t pts;
104 1437177 int buffersrc_empty = 0;
105
106
2/2
✓ Branch 0 taken 1390748 times.
✓ Branch 1 taken 46429 times.
1437177 if (buf->peeked_frame)
107 46429 return return_or_keep_frame(buf, frame, buf->peeked_frame, flags);
108
109 while (1) {
110
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3958862 times.
3958862 ret = samples ? ff_inlink_consume_samples(inlink, samples, samples, &cur_frame) :
111 3958862 ff_inlink_consume_frame(inlink, &cur_frame);
112
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3958862 times.
3958862 if (ret < 0) {
113 ✗ return ret;
114
2/2
✓ Branch 0 taken 471719 times.
✓ Branch 1 taken 3487143 times.
3958862 } else if (ret) {
115 /* TODO return the frame instead of copying it */
116 471719 return return_or_keep_frame(buf, frame, cur_frame, flags);
117
2/2
✓ Branch 1 taken 9881 times.
✓ Branch 2 taken 3477262 times.
3487143 } else if (ff_inlink_acknowledge_status(inlink, &status, &pts)) {
118 9881 return status;
119
2/2
✓ Branch 0 taken 484053 times.
✓ Branch 1 taken 2993209 times.
3477262 } else if ((flags & AV_BUFFERSINK_FLAG_NO_REQUEST)) {
120 484053 return AVERROR(EAGAIN);
121
2/2
✓ Branch 0 taken 2525242 times.
✓ Branch 1 taken 467967 times.
2993209 } else if (li->frame_wanted_out) {
122 2525242 ret = ff_filter_graph_run_once(ctx->graph);
123
2/2
✓ Branch 0 taken 425544 times.
✓ Branch 1 taken 2099698 times.
2525242 if (ret == FFERROR_BUFFERSRC_EMPTY) {
124 425544 buffersrc_empty = 1;
125
2/2
✓ Branch 0 taken 430301 times.
✓ Branch 1 taken 1669397 times.
2099698 } else if (ret == AVERROR(EAGAIN)) {
126
2/2
✓ Branch 0 taken 425095 times.
✓ Branch 1 taken 5206 times.
430301 if (buffersrc_empty)
127 425095 return ret;
128 5206 ff_inlink_request_frame(inlink);
129
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1669397 times.
1669397 } else if (ret < 0) {
130 ✗ return ret;
131 }
132 } else {
133 467967 ff_inlink_request_frame(inlink);
134 }
135 }
136 }
137
138 1437177 int attribute_align_arg av_buffersink_get_frame_flags(AVFilterContext *ctx, AVFrame *frame, int flags)
139 {
140 1437177 return get_frame_internal(ctx, frame, flags,
141 1437177 ff_filter_link(ctx->inputs[0])->min_samples);
142 }
143
144 ✗ int attribute_align_arg av_buffersink_get_samples(AVFilterContext *ctx,
145 AVFrame *frame, int nb_samples)
146 {
147 ✗ return get_frame_internal(ctx, frame, 0, nb_samples);
148 }
149
150 8701 static av_cold int common_init(AVFilterContext *ctx)
151 {
152 8701 BufferSinkContext *buf = ctx->priv;
153
154 8701 buf->warning_limit = 100;
155 8701 return 0;
156 }
157
158 #define TERMINATE_ARRAY(arr, val) \
159 if (s->arr) { \
160 void *tmp = av_realloc_array(s->arr, s->nb_ ## arr + 1, sizeof(*s->arr)); \
161 if (!tmp) \
162 return AVERROR(ENOMEM); \
163 s->arr = tmp; \
164 s->arr[s->nb_ ## arr] = val; \
165 }
166
167 7084 static int init_video(AVFilterContext *ctx)
168 {
169 7084 BufferSinkContext *s = ctx->priv;
170
171
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 7084 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
7084 TERMINATE_ARRAY(pixel_formats, AV_PIX_FMT_NONE);
172
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 7084 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
7084 TERMINATE_ARRAY(colorranges, -1);
173
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 7084 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
7084 TERMINATE_ARRAY(colorspaces, -1);
174
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 7084 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
7084 TERMINATE_ARRAY(alphamodes, -1);
175
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 7084 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
7084 TERMINATE_ARRAY(chromalocations, -1);
176
177 7084 return common_init(ctx);
178 }
179
180 1617 static int init_audio(AVFilterContext *ctx)
181 {
182 1617 BufferSinkContext *s = ctx->priv;
183
184
3/4
✓ Branch 0 taken 36 times.
✓ Branch 1 taken 1581 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 36 times.
1617 TERMINATE_ARRAY(sample_formats, AV_SAMPLE_FMT_NONE);
185
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1617 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
1617 TERMINATE_ARRAY(samplerates, -1);
186
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1617 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
1617 TERMINATE_ARRAY(channel_layouts, (AVChannelLayout){ .nb_channels = 0 });
187
188 1617 return common_init(ctx);
189 }
190
191 #undef TERMINATE_ARRAY
192
193 8701 static void uninit(AVFilterContext *ctx)
194 {
195 8701 BufferSinkContext *buf = ctx->priv;
196
197 8701 av_frame_free(&buf->peeked_frame);
198 8701 }
199
200 478213 static int activate(AVFilterContext *ctx)
201 {
202 478213 BufferSinkContext *buf = ctx->priv;
203 478213 FilterLinkInternal * const li = ff_link_internal(ctx->inputs[0]);
204
205
1/2
✓ Branch 0 taken 478213 times.
✗ Branch 1 not taken.
478213 if (buf->warning_limit &&
206
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 478213 times.
478213 ff_framequeue_queued_frames(&li->fifo) >= buf->warning_limit) {
207 ✗ av_log(ctx, AV_LOG_WARNING,
208 "%d buffers queued in %s, something may be wrong.\n",
209 buf->warning_limit,
210 ✗ (char *)av_x_if_null(ctx->name, ctx->filter->name));
211 ✗ buf->warning_limit *= 10;
212 }
213
214 /* The frame is queued, the rest is up to get_frame_internal */
215 478213 return 0;
216 }
217
218 1617 static int config_input_audio(AVFilterLink *inlink)
219 {
220 1617 BufferSinkContext *buf = inlink->dst->priv;
221 1617 FilterLink *l = ff_filter_link(inlink);
222
223 1617 l->min_samples = l->max_samples = buf->frame_size;
224
225 1617 return 0;
226 }
227
228 ✗ void av_buffersink_set_frame_size(AVFilterContext *ctx, unsigned frame_size)
229 {
230 ✗ BufferSinkContext *buf = ctx->priv;
231 ✗ buf->frame_size = frame_size;
232
233 ✗ if (ctx->inputs && ctx->inputs[0]) {
234 ✗ FilterLink *l = ff_filter_link(ctx->inputs[0]);
235 ✗ l->min_samples = l->max_samples = buf->frame_size;
236 }
237 ✗ }
238
239 #define MAKE_AVFILTERLINK_ACCESSOR(type, field) \
240 type av_buffersink_get_##field(const AVFilterContext *ctx) { \
241 av_assert0(fffilter(ctx->filter)->activate == activate); \
242 return ctx->inputs[0]->field; \
243 }
244
245
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 83 times.
83 MAKE_AVFILTERLINK_ACCESSOR(enum AVMediaType , type )
246
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 480481 times.
480481 MAKE_AVFILTERLINK_ACCESSOR(AVRational , time_base )
247
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8700 times.
8700 MAKE_AVFILTERLINK_ACCESSOR(int , format )
248
249
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8664 times.
8664 MAKE_AVFILTERLINK_ACCESSOR(int , w )
250
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8664 times.
8664 MAKE_AVFILTERLINK_ACCESSOR(int , h )
251
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8664 times.
8664 MAKE_AVFILTERLINK_ACCESSOR(AVRational , sample_aspect_ratio)
252
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8617 times.
8617 MAKE_AVFILTERLINK_ACCESSOR(enum AVColorSpace, colorspace)
253
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8617 times.
8617 MAKE_AVFILTERLINK_ACCESSOR(enum AVColorRange, color_range)
254
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8617 times.
8617 MAKE_AVFILTERLINK_ACCESSOR(enum AVAlphaMode , alpha_mode)
255
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8617 times.
8617 MAKE_AVFILTERLINK_ACCESSOR(enum AVChromaLocation, chroma_location)
256
257
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8653 times.
8653 MAKE_AVFILTERLINK_ACCESSOR(int , sample_rate )
258
259 11090 AVRational av_buffersink_get_frame_rate(const AVFilterContext *ctx)
260 {
261 11090 FilterLink *l = ff_filter_link(ctx->inputs[0]);
262
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 11090 times.
11090 av_assert0(fffilter(ctx->filter)->activate == activate);
263 11090 return l->frame_rate;
264 }
265
266 ✗ AVBufferRef* av_buffersink_get_hw_frames_ctx(const AVFilterContext *ctx)
267 {
268 ✗ FilterLink *l = ff_filter_link(ctx->inputs[0]);
269 ✗ av_assert0(fffilter(ctx->filter)->activate == activate);
270 ✗ return l->hw_frames_ctx;
271 }
272
273 ✗ int av_buffersink_get_channels(const AVFilterContext *ctx)
274 {
275 ✗ av_assert0(fffilter(ctx->filter)->activate == activate);
276 ✗ return ctx->inputs[0]->ch_layout.nb_channels;
277 }
278
279 8653 int av_buffersink_get_ch_layout(const AVFilterContext *ctx, AVChannelLayout *out)
280 {
281 8653 AVChannelLayout ch_layout = { 0 };
282 int ret;
283
284
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8653 times.
8653 av_assert0(fffilter(ctx->filter)->activate == activate);
285 8653 ret = av_channel_layout_copy(&ch_layout, &ctx->inputs[0]->ch_layout);
286
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8653 times.
8653 if (ret < 0)
287 ✗ return ret;
288 8653 *out = ch_layout;
289 8653 return 0;
290 }
291
292 8617 const AVFrameSideData *const *av_buffersink_get_side_data(const AVFilterContext *ctx,
293 int *nb_side_data)
294 {
295
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8617 times.
8617 av_assert0(fffilter(ctx->filter)->activate == activate);
296 8617 *nb_side_data = ctx->inputs[0]->nb_side_data;
297 8617 return (const AVFrameSideData *const *)ctx->inputs[0]->side_data;
298 }
299
300 7084 static int vsink_query_formats(const AVFilterContext *ctx,
301 AVFilterFormatsConfig **cfg_in,
302 AVFilterFormatsConfig **cfg_out)
303 {
304 7084 const BufferSinkContext *buf = ctx->priv;
305 int ret;
306
307
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7084 times.
7084 if (buf->nb_pixel_formats) {
308 ✗ ret = ff_set_pixel_formats_from_list2(ctx, cfg_in, cfg_out, buf->pixel_formats);
309 ✗ if (ret < 0)
310 ✗ return ret;
311 }
312
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7084 times.
7084 if (buf->nb_colorspaces) {
313 ✗ ret = ff_set_common_color_spaces_from_list2(ctx, cfg_in, cfg_out, buf->colorspaces);
314 ✗ if (ret < 0)
315 ✗ return ret;
316 }
317
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7084 times.
7084 if (buf->nb_colorranges) {
318 ✗ ret = ff_set_common_color_ranges_from_list2(ctx, cfg_in, cfg_out, buf->colorranges);
319 ✗ if (ret < 0)
320 ✗ return ret;
321 }
322
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7084 times.
7084 if (buf->nb_chromalocations) {
323 ✗ ret = ff_set_common_chroma_locations_from_list2(ctx, cfg_in, cfg_out, buf->chromalocations);
324 ✗ if (ret < 0)
325 ✗ return ret;
326 }
327
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7084 times.
7084 if (buf->nb_alphamodes) {
328 ✗ ret = ff_set_common_alpha_modes_from_list2(ctx, cfg_in, cfg_out, buf->alphamodes);
329 ✗ if (ret < 0)
330 ✗ return ret;
331 }
332
333 7084 return 0;
334 }
335
336 1617 static int asink_query_formats(const AVFilterContext *ctx,
337 AVFilterFormatsConfig **cfg_in,
338 AVFilterFormatsConfig **cfg_out)
339 {
340 1617 const BufferSinkContext *buf = ctx->priv;
341 int ret;
342
343
2/2
✓ Branch 0 taken 36 times.
✓ Branch 1 taken 1581 times.
1617 if (buf->nb_sample_formats) {
344 36 ret = ff_set_sample_formats_from_list2(ctx, cfg_in, cfg_out, buf->sample_formats);
345
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 36 times.
36 if (ret < 0)
346 ✗ return ret;
347 }
348
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1617 times.
1617 if (buf->nb_samplerates) {
349 ✗ ret = ff_set_common_samplerates_from_list2(ctx, cfg_in, cfg_out, buf->samplerates);
350 ✗ if (ret < 0)
351 ✗ return ret;
352 }
353
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1617 times.
1617 if (buf->nb_channel_layouts) {
354 ✗ ret = ff_set_common_channel_layouts_from_list2(ctx, cfg_in, cfg_out, buf->channel_layouts);
355 ✗ if (ret < 0)
356 ✗ return ret;
357 }
358
359 1617 return 0;
360 }
361
362 #define OFFSET(x) offsetof(BufferSinkContext, x)
363 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
364 static const AVOption buffersink_options[] = {
365 { "pixel_formats", "array of supported pixel formats", OFFSET(pixel_formats),
366 AV_OPT_TYPE_PIXEL_FMT | AV_OPT_TYPE_FLAG_ARRAY, .max = INT_MAX, .flags = FLAGS },
367 { "colorspaces", "array of supported color spaces", OFFSET(colorspaces),
368 AV_OPT_TYPE_INT | AV_OPT_TYPE_FLAG_ARRAY, .max = INT_MAX, .flags = FLAGS },
369 { "colorranges", "array of supported color ranges", OFFSET(colorranges),
370 AV_OPT_TYPE_INT | AV_OPT_TYPE_FLAG_ARRAY, .max = INT_MAX, .flags = FLAGS },
371 { "chromalocations", "array of supported chroma locations", OFFSET(chromalocations),
372 AV_OPT_TYPE_INT | AV_OPT_TYPE_FLAG_ARRAY, .max = INT_MAX, .flags = FLAGS },
373 { "alphamodes", "array of supported color ranges", OFFSET(alphamodes),
374 AV_OPT_TYPE_INT | AV_OPT_TYPE_FLAG_ARRAY, .max = INT_MAX, .flags = FLAGS },
375
376 { NULL },
377 };
378 #undef FLAGS
379 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_AUDIO_PARAM
380 static const AVOption abuffersink_options[] = {
381 { "sample_formats", "array of supported sample formats", OFFSET(sample_formats),
382 AV_OPT_TYPE_SAMPLE_FMT | AV_OPT_TYPE_FLAG_ARRAY, .max = INT_MAX, .flags = FLAGS },
383 { "samplerates", "array of supported sample formats", OFFSET(samplerates),
384 AV_OPT_TYPE_INT | AV_OPT_TYPE_FLAG_ARRAY, .max = INT_MAX, .flags = FLAGS },
385 { "channel_layouts", "array of supported channel layouts", OFFSET(channel_layouts),
386 AV_OPT_TYPE_CHLAYOUT | AV_OPT_TYPE_FLAG_ARRAY, .flags = FLAGS },
387 { NULL },
388 };
389 #undef FLAGS
390
391 AVFILTER_DEFINE_CLASS(buffersink);
392 AVFILTER_DEFINE_CLASS(abuffersink);
393
394 const FFFilter ff_vsink_buffer = {
395 .p.name = "buffersink",
396 .p.description = NULL_IF_CONFIG_SMALL("Buffer video frames, and make them available to the end of the filter graph."),
397 .p.priv_class = &buffersink_class,
398 .p.outputs = NULL,
399 .priv_size = sizeof(BufferSinkContext),
400 .init = init_video,
401 .uninit = uninit,
402 .activate = activate,
403 FILTER_INPUTS(ff_video_default_filterpad),
404 FILTER_QUERY_FUNC2(vsink_query_formats),
405 .flags_internal = FF_FILTER_FLAG_DYNAMIC_FRAME_PARAMS,
406 };
407
408 static const AVFilterPad inputs_audio[] = {
409 {
410 .name = "default",
411 .type = AVMEDIA_TYPE_AUDIO,
412 .config_props = config_input_audio,
413 },
414 };
415
416 const FFFilter ff_asink_abuffer = {
417 .p.name = "abuffersink",
418 .p.description = NULL_IF_CONFIG_SMALL("Buffer audio frames, and make them available to the end of the filter graph."),
419 .p.priv_class = &abuffersink_class,
420 .p.outputs = NULL,
421 .priv_size = sizeof(BufferSinkContext),
422 .init = init_audio,
423 .uninit = uninit,
424 .activate = activate,
425 FILTER_INPUTS(inputs_audio),
426 FILTER_QUERY_FUNC2(asink_query_formats),
427 };
428