Commit | Line | Data |
---|---|---|
2ba45a60 DM |
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/audio_fifo.h" | |
27 | #include "libavutil/avassert.h" | |
28 | #include "libavutil/channel_layout.h" | |
29 | #include "libavutil/common.h" | |
30 | #include "libavutil/internal.h" | |
31 | #include "libavutil/mathematics.h" | |
32 | #include "libavutil/opt.h" | |
33 | ||
34 | #include "audio.h" | |
35 | #include "avfilter.h" | |
36 | #include "buffersink.h" | |
37 | #include "internal.h" | |
38 | ||
39 | typedef struct BufferSinkContext { | |
40 | const AVClass *class; | |
41 | AVFifoBuffer *fifo; ///< FIFO buffer of video frame references | |
42 | unsigned warning_limit; | |
43 | ||
44 | /* only used for video */ | |
45 | enum AVPixelFormat *pixel_fmts; ///< list of accepted pixel formats, must be terminated with -1 | |
46 | int pixel_fmts_size; | |
47 | ||
48 | /* only used for audio */ | |
49 | enum AVSampleFormat *sample_fmts; ///< list of accepted sample formats, terminated by AV_SAMPLE_FMT_NONE | |
50 | int sample_fmts_size; | |
51 | int64_t *channel_layouts; ///< list of accepted channel layouts, terminated by -1 | |
52 | int channel_layouts_size; | |
53 | int *channel_counts; ///< list of accepted channel counts, terminated by -1 | |
54 | int channel_counts_size; | |
55 | int all_channel_counts; | |
56 | int *sample_rates; ///< list of accepted sample rates, terminated by -1 | |
57 | int sample_rates_size; | |
58 | ||
59 | /* only used for compat API */ | |
60 | AVAudioFifo *audio_fifo; ///< FIFO for audio samples | |
61 | int64_t next_pts; ///< interpolating audio pts | |
62 | } BufferSinkContext; | |
63 | ||
64 | #define NB_ITEMS(list) (list ## _size / sizeof(*list)) | |
65 | ||
66 | static av_cold void uninit(AVFilterContext *ctx) | |
67 | { | |
68 | BufferSinkContext *sink = ctx->priv; | |
69 | AVFrame *frame; | |
70 | ||
71 | if (sink->audio_fifo) | |
72 | av_audio_fifo_free(sink->audio_fifo); | |
73 | ||
74 | if (sink->fifo) { | |
75 | while (av_fifo_size(sink->fifo) >= sizeof(AVFilterBufferRef *)) { | |
76 | av_fifo_generic_read(sink->fifo, &frame, sizeof(frame), NULL); | |
77 | av_frame_free(&frame); | |
78 | } | |
79 | av_fifo_freep(&sink->fifo); | |
80 | } | |
81 | } | |
82 | ||
83 | static int add_buffer_ref(AVFilterContext *ctx, AVFrame *ref) | |
84 | { | |
85 | BufferSinkContext *buf = ctx->priv; | |
86 | ||
87 | if (av_fifo_space(buf->fifo) < sizeof(AVFilterBufferRef *)) { | |
88 | /* realloc fifo size */ | |
89 | if (av_fifo_realloc2(buf->fifo, av_fifo_size(buf->fifo) * 2) < 0) { | |
90 | av_log(ctx, AV_LOG_ERROR, | |
91 | "Cannot buffer more frames. Consume some available frames " | |
92 | "before adding new ones.\n"); | |
93 | return AVERROR(ENOMEM); | |
94 | } | |
95 | } | |
96 | ||
97 | /* cache frame */ | |
98 | av_fifo_generic_write(buf->fifo, &ref, sizeof(AVFilterBufferRef *), NULL); | |
99 | return 0; | |
100 | } | |
101 | ||
102 | static int filter_frame(AVFilterLink *link, AVFrame *frame) | |
103 | { | |
104 | AVFilterContext *ctx = link->dst; | |
105 | BufferSinkContext *buf = link->dst->priv; | |
106 | int ret; | |
107 | ||
108 | if ((ret = add_buffer_ref(ctx, frame)) < 0) | |
109 | return ret; | |
110 | if (buf->warning_limit && | |
111 | av_fifo_size(buf->fifo) / sizeof(AVFilterBufferRef *) >= buf->warning_limit) { | |
112 | av_log(ctx, AV_LOG_WARNING, | |
113 | "%d buffers queued in %s, something may be wrong.\n", | |
114 | buf->warning_limit, | |
115 | (char *)av_x_if_null(ctx->name, ctx->filter->name)); | |
116 | buf->warning_limit *= 10; | |
117 | } | |
118 | return 0; | |
119 | } | |
120 | ||
121 | int attribute_align_arg av_buffersink_get_frame(AVFilterContext *ctx, AVFrame *frame) | |
122 | { | |
123 | return av_buffersink_get_frame_flags(ctx, frame, 0); | |
124 | } | |
125 | ||
126 | int attribute_align_arg av_buffersink_get_frame_flags(AVFilterContext *ctx, AVFrame *frame, int flags) | |
127 | { | |
128 | BufferSinkContext *buf = ctx->priv; | |
129 | AVFilterLink *inlink = ctx->inputs[0]; | |
130 | int ret; | |
131 | AVFrame *cur_frame; | |
132 | ||
133 | /* no picref available, fetch it from the filterchain */ | |
134 | if (!av_fifo_size(buf->fifo)) { | |
135 | if (flags & AV_BUFFERSINK_FLAG_NO_REQUEST) | |
136 | return AVERROR(EAGAIN); | |
137 | if ((ret = ff_request_frame(inlink)) < 0) | |
138 | return ret; | |
139 | } | |
140 | ||
141 | if (!av_fifo_size(buf->fifo)) | |
142 | return AVERROR(EINVAL); | |
143 | ||
144 | if (flags & AV_BUFFERSINK_FLAG_PEEK) { | |
145 | cur_frame = *((AVFrame **)av_fifo_peek2(buf->fifo, 0)); | |
146 | if ((ret = av_frame_ref(frame, cur_frame)) < 0) | |
147 | return ret; | |
148 | } else { | |
149 | av_fifo_generic_read(buf->fifo, &cur_frame, sizeof(cur_frame), NULL); | |
150 | av_frame_move_ref(frame, cur_frame); | |
151 | av_frame_free(&cur_frame); | |
152 | } | |
153 | ||
154 | return 0; | |
155 | } | |
156 | ||
157 | static int read_from_fifo(AVFilterContext *ctx, AVFrame *frame, | |
158 | int nb_samples) | |
159 | { | |
160 | BufferSinkContext *s = ctx->priv; | |
161 | AVFilterLink *link = ctx->inputs[0]; | |
162 | AVFrame *tmp; | |
163 | ||
164 | if (!(tmp = ff_get_audio_buffer(link, nb_samples))) | |
165 | return AVERROR(ENOMEM); | |
166 | av_audio_fifo_read(s->audio_fifo, (void**)tmp->extended_data, nb_samples); | |
167 | ||
168 | tmp->pts = s->next_pts; | |
169 | if (s->next_pts != AV_NOPTS_VALUE) | |
170 | s->next_pts += av_rescale_q(nb_samples, (AVRational){1, link->sample_rate}, | |
171 | link->time_base); | |
172 | ||
173 | av_frame_move_ref(frame, tmp); | |
174 | av_frame_free(&tmp); | |
175 | ||
176 | return 0; | |
177 | } | |
178 | ||
179 | int attribute_align_arg av_buffersink_get_samples(AVFilterContext *ctx, | |
180 | AVFrame *frame, int nb_samples) | |
181 | { | |
182 | BufferSinkContext *s = ctx->priv; | |
183 | AVFilterLink *link = ctx->inputs[0]; | |
184 | AVFrame *cur_frame; | |
185 | int ret = 0; | |
186 | ||
187 | if (!s->audio_fifo) { | |
188 | int nb_channels = link->channels; | |
189 | if (!(s->audio_fifo = av_audio_fifo_alloc(link->format, nb_channels, nb_samples))) | |
190 | return AVERROR(ENOMEM); | |
191 | } | |
192 | ||
193 | while (ret >= 0) { | |
194 | if (av_audio_fifo_size(s->audio_fifo) >= nb_samples) | |
195 | return read_from_fifo(ctx, frame, nb_samples); | |
196 | ||
197 | if (!(cur_frame = av_frame_alloc())) | |
198 | return AVERROR(ENOMEM); | |
199 | ret = av_buffersink_get_frame_flags(ctx, cur_frame, 0); | |
200 | if (ret == AVERROR_EOF && av_audio_fifo_size(s->audio_fifo)) { | |
201 | av_frame_free(&cur_frame); | |
202 | return read_from_fifo(ctx, frame, av_audio_fifo_size(s->audio_fifo)); | |
203 | } else if (ret < 0) { | |
204 | av_frame_free(&cur_frame); | |
205 | return ret; | |
206 | } | |
207 | ||
208 | if (cur_frame->pts != AV_NOPTS_VALUE) { | |
209 | s->next_pts = cur_frame->pts - | |
210 | av_rescale_q(av_audio_fifo_size(s->audio_fifo), | |
211 | (AVRational){ 1, link->sample_rate }, | |
212 | link->time_base); | |
213 | } | |
214 | ||
215 | ret = av_audio_fifo_write(s->audio_fifo, (void**)cur_frame->extended_data, | |
216 | cur_frame->nb_samples); | |
217 | av_frame_free(&cur_frame); | |
218 | } | |
219 | ||
220 | return ret; | |
221 | } | |
222 | ||
223 | AVBufferSinkParams *av_buffersink_params_alloc(void) | |
224 | { | |
225 | static const int pixel_fmts[] = { AV_PIX_FMT_NONE }; | |
226 | AVBufferSinkParams *params = av_malloc(sizeof(AVBufferSinkParams)); | |
227 | if (!params) | |
228 | return NULL; | |
229 | ||
230 | params->pixel_fmts = pixel_fmts; | |
231 | return params; | |
232 | } | |
233 | ||
234 | AVABufferSinkParams *av_abuffersink_params_alloc(void) | |
235 | { | |
236 | AVABufferSinkParams *params = av_mallocz(sizeof(AVABufferSinkParams)); | |
237 | ||
238 | if (!params) | |
239 | return NULL; | |
240 | return params; | |
241 | } | |
242 | ||
243 | #define FIFO_INIT_SIZE 8 | |
244 | ||
245 | static av_cold int common_init(AVFilterContext *ctx) | |
246 | { | |
247 | BufferSinkContext *buf = ctx->priv; | |
248 | ||
249 | buf->fifo = av_fifo_alloc_array(FIFO_INIT_SIZE, sizeof(AVFilterBufferRef *)); | |
250 | if (!buf->fifo) { | |
251 | av_log(ctx, AV_LOG_ERROR, "Failed to allocate fifo\n"); | |
252 | return AVERROR(ENOMEM); | |
253 | } | |
254 | buf->warning_limit = 100; | |
255 | buf->next_pts = AV_NOPTS_VALUE; | |
256 | return 0; | |
257 | } | |
258 | ||
259 | void av_buffersink_set_frame_size(AVFilterContext *ctx, unsigned frame_size) | |
260 | { | |
261 | AVFilterLink *inlink = ctx->inputs[0]; | |
262 | ||
263 | inlink->min_samples = inlink->max_samples = | |
264 | inlink->partial_buf_size = frame_size; | |
265 | } | |
266 | ||
267 | #if FF_API_AVFILTERBUFFER | |
268 | FF_DISABLE_DEPRECATION_WARNINGS | |
269 | static void compat_free_buffer(AVFilterBuffer *buf) | |
270 | { | |
271 | AVFrame *frame = buf->priv; | |
272 | av_frame_free(&frame); | |
273 | av_free(buf); | |
274 | } | |
275 | ||
276 | static int compat_read(AVFilterContext *ctx, | |
277 | AVFilterBufferRef **pbuf, int nb_samples, int flags) | |
278 | { | |
279 | AVFilterBufferRef *buf; | |
280 | AVFrame *frame; | |
281 | int ret; | |
282 | ||
283 | if (!pbuf) | |
284 | return ff_poll_frame(ctx->inputs[0]); | |
285 | ||
286 | frame = av_frame_alloc(); | |
287 | if (!frame) | |
288 | return AVERROR(ENOMEM); | |
289 | ||
290 | if (!nb_samples) | |
291 | ret = av_buffersink_get_frame_flags(ctx, frame, flags); | |
292 | else | |
293 | ret = av_buffersink_get_samples(ctx, frame, nb_samples); | |
294 | ||
295 | if (ret < 0) | |
296 | goto fail; | |
297 | ||
298 | AV_NOWARN_DEPRECATED( | |
299 | if (ctx->inputs[0]->type == AVMEDIA_TYPE_VIDEO) { | |
300 | buf = avfilter_get_video_buffer_ref_from_arrays(frame->data, frame->linesize, | |
301 | AV_PERM_READ, | |
302 | frame->width, frame->height, | |
303 | frame->format); | |
304 | } else { | |
305 | buf = avfilter_get_audio_buffer_ref_from_arrays(frame->extended_data, | |
306 | frame->linesize[0], AV_PERM_READ, | |
307 | frame->nb_samples, | |
308 | frame->format, | |
309 | frame->channel_layout); | |
310 | } | |
311 | if (!buf) { | |
312 | ret = AVERROR(ENOMEM); | |
313 | goto fail; | |
314 | } | |
315 | ||
316 | avfilter_copy_frame_props(buf, frame); | |
317 | ) | |
318 | ||
319 | buf->buf->priv = frame; | |
320 | buf->buf->free = compat_free_buffer; | |
321 | ||
322 | *pbuf = buf; | |
323 | ||
324 | return 0; | |
325 | fail: | |
326 | av_frame_free(&frame); | |
327 | return ret; | |
328 | } | |
329 | ||
330 | int attribute_align_arg av_buffersink_read(AVFilterContext *ctx, AVFilterBufferRef **buf) | |
331 | { | |
332 | return compat_read(ctx, buf, 0, 0); | |
333 | } | |
334 | ||
335 | int attribute_align_arg av_buffersink_read_samples(AVFilterContext *ctx, AVFilterBufferRef **buf, | |
336 | int nb_samples) | |
337 | { | |
338 | return compat_read(ctx, buf, nb_samples, 0); | |
339 | } | |
340 | ||
341 | int attribute_align_arg av_buffersink_get_buffer_ref(AVFilterContext *ctx, | |
342 | AVFilterBufferRef **bufref, int flags) | |
343 | { | |
344 | *bufref = NULL; | |
345 | ||
346 | av_assert0( !strcmp(ctx->filter->name, "buffersink") | |
347 | || !strcmp(ctx->filter->name, "abuffersink") | |
348 | || !strcmp(ctx->filter->name, "ffbuffersink") | |
349 | || !strcmp(ctx->filter->name, "ffabuffersink")); | |
350 | ||
351 | return compat_read(ctx, bufref, 0, flags); | |
352 | } | |
353 | FF_ENABLE_DEPRECATION_WARNINGS | |
354 | #endif | |
355 | ||
356 | AVRational av_buffersink_get_frame_rate(AVFilterContext *ctx) | |
357 | { | |
358 | av_assert0( !strcmp(ctx->filter->name, "buffersink") | |
359 | || !strcmp(ctx->filter->name, "ffbuffersink")); | |
360 | ||
361 | return ctx->inputs[0]->frame_rate; | |
362 | } | |
363 | ||
364 | int attribute_align_arg av_buffersink_poll_frame(AVFilterContext *ctx) | |
365 | { | |
366 | BufferSinkContext *buf = ctx->priv; | |
367 | AVFilterLink *inlink = ctx->inputs[0]; | |
368 | ||
369 | av_assert0( !strcmp(ctx->filter->name, "buffersink") | |
370 | || !strcmp(ctx->filter->name, "abuffersink") | |
371 | || !strcmp(ctx->filter->name, "ffbuffersink") | |
372 | || !strcmp(ctx->filter->name, "ffabuffersink")); | |
373 | ||
374 | return av_fifo_size(buf->fifo)/sizeof(AVFilterBufferRef *) + ff_poll_frame(inlink); | |
375 | } | |
376 | ||
377 | static av_cold int vsink_init(AVFilterContext *ctx, void *opaque) | |
378 | { | |
379 | BufferSinkContext *buf = ctx->priv; | |
380 | AVBufferSinkParams *params = opaque; | |
381 | int ret; | |
382 | ||
383 | if (params) { | |
384 | if ((ret = av_opt_set_int_list(buf, "pix_fmts", params->pixel_fmts, AV_PIX_FMT_NONE, 0)) < 0) | |
385 | return ret; | |
386 | } | |
387 | ||
388 | return common_init(ctx); | |
389 | } | |
390 | ||
391 | #define CHECK_LIST_SIZE(field) \ | |
392 | if (buf->field ## _size % sizeof(*buf->field)) { \ | |
393 | av_log(ctx, AV_LOG_ERROR, "Invalid size for " #field ": %d, " \ | |
394 | "should be multiple of %d\n", \ | |
395 | buf->field ## _size, (int)sizeof(*buf->field)); \ | |
396 | return AVERROR(EINVAL); \ | |
397 | } | |
398 | static int vsink_query_formats(AVFilterContext *ctx) | |
399 | { | |
400 | BufferSinkContext *buf = ctx->priv; | |
401 | AVFilterFormats *formats = NULL; | |
402 | unsigned i; | |
403 | int ret; | |
404 | ||
405 | CHECK_LIST_SIZE(pixel_fmts) | |
406 | if (buf->pixel_fmts_size) { | |
407 | for (i = 0; i < NB_ITEMS(buf->pixel_fmts); i++) | |
408 | if ((ret = ff_add_format(&formats, buf->pixel_fmts[i])) < 0) { | |
409 | ff_formats_unref(&formats); | |
410 | return ret; | |
411 | } | |
412 | ff_set_common_formats(ctx, formats); | |
413 | } else { | |
414 | ff_default_query_formats(ctx); | |
415 | } | |
416 | ||
417 | return 0; | |
418 | } | |
419 | ||
420 | static av_cold int asink_init(AVFilterContext *ctx, void *opaque) | |
421 | { | |
422 | BufferSinkContext *buf = ctx->priv; | |
423 | AVABufferSinkParams *params = opaque; | |
424 | int ret; | |
425 | ||
426 | if (params) { | |
427 | if ((ret = av_opt_set_int_list(buf, "sample_fmts", params->sample_fmts, AV_SAMPLE_FMT_NONE, 0)) < 0 || | |
428 | (ret = av_opt_set_int_list(buf, "sample_rates", params->sample_rates, -1, 0)) < 0 || | |
429 | (ret = av_opt_set_int_list(buf, "channel_layouts", params->channel_layouts, -1, 0)) < 0 || | |
430 | (ret = av_opt_set_int_list(buf, "channel_counts", params->channel_counts, -1, 0)) < 0 || | |
431 | (ret = av_opt_set_int(buf, "all_channel_counts", params->all_channel_counts, 0)) < 0) | |
432 | return ret; | |
433 | } | |
434 | return common_init(ctx); | |
435 | } | |
436 | ||
437 | static int asink_query_formats(AVFilterContext *ctx) | |
438 | { | |
439 | BufferSinkContext *buf = ctx->priv; | |
440 | AVFilterFormats *formats = NULL; | |
441 | AVFilterChannelLayouts *layouts = NULL; | |
442 | unsigned i; | |
443 | int ret; | |
444 | ||
445 | CHECK_LIST_SIZE(sample_fmts) | |
446 | CHECK_LIST_SIZE(sample_rates) | |
447 | CHECK_LIST_SIZE(channel_layouts) | |
448 | CHECK_LIST_SIZE(channel_counts) | |
449 | ||
450 | if (buf->sample_fmts_size) { | |
451 | for (i = 0; i < NB_ITEMS(buf->sample_fmts); i++) | |
452 | if ((ret = ff_add_format(&formats, buf->sample_fmts[i])) < 0) { | |
453 | ff_formats_unref(&formats); | |
454 | return ret; | |
455 | } | |
456 | ff_set_common_formats(ctx, formats); | |
457 | } | |
458 | ||
459 | if (buf->channel_layouts_size || buf->channel_counts_size || | |
460 | buf->all_channel_counts) { | |
461 | for (i = 0; i < NB_ITEMS(buf->channel_layouts); i++) | |
462 | if ((ret = ff_add_channel_layout(&layouts, buf->channel_layouts[i])) < 0) { | |
463 | ff_channel_layouts_unref(&layouts); | |
464 | return ret; | |
465 | } | |
466 | for (i = 0; i < NB_ITEMS(buf->channel_counts); i++) | |
467 | if ((ret = ff_add_channel_layout(&layouts, FF_COUNT2LAYOUT(buf->channel_counts[i]))) < 0) { | |
468 | ff_channel_layouts_unref(&layouts); | |
469 | return ret; | |
470 | } | |
471 | if (buf->all_channel_counts) { | |
472 | if (layouts) | |
473 | av_log(ctx, AV_LOG_WARNING, | |
474 | "Conflicting all_channel_counts and list in options\n"); | |
475 | else if (!(layouts = ff_all_channel_counts())) | |
476 | return AVERROR(ENOMEM); | |
477 | } | |
478 | ff_set_common_channel_layouts(ctx, layouts); | |
479 | } | |
480 | ||
481 | if (buf->sample_rates_size) { | |
482 | formats = NULL; | |
483 | for (i = 0; i < NB_ITEMS(buf->sample_rates); i++) | |
484 | if ((ret = ff_add_format(&formats, buf->sample_rates[i])) < 0) { | |
485 | ff_formats_unref(&formats); | |
486 | return ret; | |
487 | } | |
488 | ff_set_common_samplerates(ctx, formats); | |
489 | } | |
490 | ||
491 | return 0; | |
492 | } | |
493 | ||
494 | #define OFFSET(x) offsetof(BufferSinkContext, x) | |
495 | #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM | |
496 | static const AVOption buffersink_options[] = { | |
497 | { "pix_fmts", "set the supported pixel formats", OFFSET(pixel_fmts), AV_OPT_TYPE_BINARY, .flags = FLAGS }, | |
498 | { NULL }, | |
499 | }; | |
500 | #undef FLAGS | |
501 | #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM | |
502 | static const AVOption abuffersink_options[] = { | |
503 | { "sample_fmts", "set the supported sample formats", OFFSET(sample_fmts), AV_OPT_TYPE_BINARY, .flags = FLAGS }, | |
504 | { "sample_rates", "set the supported sample rates", OFFSET(sample_rates), AV_OPT_TYPE_BINARY, .flags = FLAGS }, | |
505 | { "channel_layouts", "set the supported channel layouts", OFFSET(channel_layouts), AV_OPT_TYPE_BINARY, .flags = FLAGS }, | |
506 | { "channel_counts", "set the supported channel counts", OFFSET(channel_counts), AV_OPT_TYPE_BINARY, .flags = FLAGS }, | |
507 | { "all_channel_counts", "accept all channel counts", OFFSET(all_channel_counts), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, FLAGS }, | |
508 | { NULL }, | |
509 | }; | |
510 | #undef FLAGS | |
511 | ||
512 | AVFILTER_DEFINE_CLASS(buffersink); | |
513 | AVFILTER_DEFINE_CLASS(abuffersink); | |
514 | ||
515 | #if FF_API_AVFILTERBUFFER | |
516 | ||
517 | #define ffbuffersink_options buffersink_options | |
518 | #define ffabuffersink_options abuffersink_options | |
519 | AVFILTER_DEFINE_CLASS(ffbuffersink); | |
520 | AVFILTER_DEFINE_CLASS(ffabuffersink); | |
521 | ||
522 | static const AVFilterPad ffbuffersink_inputs[] = { | |
523 | { | |
524 | .name = "default", | |
525 | .type = AVMEDIA_TYPE_VIDEO, | |
526 | .filter_frame = filter_frame, | |
527 | }, | |
528 | { NULL }, | |
529 | }; | |
530 | ||
531 | AVFilter ff_vsink_ffbuffersink = { | |
532 | .name = "ffbuffersink", | |
533 | .description = NULL_IF_CONFIG_SMALL("Buffer video frames, and make them available to the end of the filter graph."), | |
534 | .priv_size = sizeof(BufferSinkContext), | |
535 | .priv_class = &ffbuffersink_class, | |
536 | .init_opaque = vsink_init, | |
537 | .uninit = uninit, | |
538 | ||
539 | .query_formats = vsink_query_formats, | |
540 | .inputs = ffbuffersink_inputs, | |
541 | .outputs = NULL, | |
542 | }; | |
543 | ||
544 | static const AVFilterPad ffabuffersink_inputs[] = { | |
545 | { | |
546 | .name = "default", | |
547 | .type = AVMEDIA_TYPE_AUDIO, | |
548 | .filter_frame = filter_frame, | |
549 | }, | |
550 | { NULL }, | |
551 | }; | |
552 | ||
553 | AVFilter ff_asink_ffabuffersink = { | |
554 | .name = "ffabuffersink", | |
555 | .description = NULL_IF_CONFIG_SMALL("Buffer audio frames, and make them available to the end of the filter graph."), | |
556 | .init_opaque = asink_init, | |
557 | .uninit = uninit, | |
558 | .priv_size = sizeof(BufferSinkContext), | |
559 | .priv_class = &ffabuffersink_class, | |
560 | .query_formats = asink_query_formats, | |
561 | .inputs = ffabuffersink_inputs, | |
562 | .outputs = NULL, | |
563 | }; | |
564 | #endif /* FF_API_AVFILTERBUFFER */ | |
565 | ||
566 | static const AVFilterPad avfilter_vsink_buffer_inputs[] = { | |
567 | { | |
568 | .name = "default", | |
569 | .type = AVMEDIA_TYPE_VIDEO, | |
570 | .filter_frame = filter_frame, | |
571 | }, | |
572 | { NULL } | |
573 | }; | |
574 | ||
575 | AVFilter ff_vsink_buffer = { | |
576 | .name = "buffersink", | |
577 | .description = NULL_IF_CONFIG_SMALL("Buffer video frames, and make them available to the end of the filter graph."), | |
578 | .priv_size = sizeof(BufferSinkContext), | |
579 | .priv_class = &buffersink_class, | |
580 | .init_opaque = vsink_init, | |
581 | .uninit = uninit, | |
582 | ||
583 | .query_formats = vsink_query_formats, | |
584 | .inputs = avfilter_vsink_buffer_inputs, | |
585 | .outputs = NULL, | |
586 | }; | |
587 | ||
588 | static const AVFilterPad avfilter_asink_abuffer_inputs[] = { | |
589 | { | |
590 | .name = "default", | |
591 | .type = AVMEDIA_TYPE_AUDIO, | |
592 | .filter_frame = filter_frame, | |
593 | }, | |
594 | { NULL } | |
595 | }; | |
596 | ||
597 | AVFilter ff_asink_abuffer = { | |
598 | .name = "abuffersink", | |
599 | .description = NULL_IF_CONFIG_SMALL("Buffer audio frames, and make them available to the end of the filter graph."), | |
600 | .priv_class = &abuffersink_class, | |
601 | .priv_size = sizeof(BufferSinkContext), | |
602 | .init_opaque = asink_init, | |
603 | .uninit = uninit, | |
604 | ||
605 | .query_formats = asink_query_formats, | |
606 | .inputs = avfilter_asink_abuffer_inputs, | |
607 | .outputs = NULL, | |
608 | }; |