lavfi: make avfilter_get_video_buffer() private on next bump.
[ffmpeg.git] / libavfilter / buffersrc.c
1 /*
2  * Copyright (c) 2008 Vitor Sessak
3  *
4  * This file is part of Libav.
5  *
6  * Libav 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  * Libav 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 Libav; 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  * memory buffer source filter
24  */
25
26 #include "audio.h"
27 #include "avfilter.h"
28 #include "buffersrc.h"
29 #include "formats.h"
30 #include "video.h"
31 #include "vsrc_buffer.h"
32
33 #include "libavutil/audioconvert.h"
34 #include "libavutil/fifo.h"
35 #include "libavutil/imgutils.h"
36 #include "libavutil/opt.h"
37 #include "libavutil/samplefmt.h"
38
39 typedef struct {
40     const AVClass    *class;
41     AVFifoBuffer     *fifo;
42     AVRational        time_base;     ///< time_base to set in the output link
43
44     /* video only */
45     int               h, w;
46     enum PixelFormat  pix_fmt;
47     AVRational        pixel_aspect;
48
49     /* audio only */
50     int sample_rate;
51     enum AVSampleFormat sample_fmt;
52     char               *sample_fmt_str;
53     uint64_t channel_layout;
54     char    *channel_layout_str;
55
56     int eof;
57 } BufferSourceContext;
58
59 #define CHECK_VIDEO_PARAM_CHANGE(s, c, width, height, format)\
60     if (c->w != width || c->h != height || c->pix_fmt != format) {\
61         av_log(s, AV_LOG_ERROR, "Changing frame properties on the fly is not supported.\n");\
62         return AVERROR(EINVAL);\
63     }
64
65 #define CHECK_AUDIO_PARAM_CHANGE(s, c, srate, ch_layout, format)\
66     if (c->sample_fmt != format || c->sample_rate != srate ||\
67         c->channel_layout != ch_layout) {\
68         av_log(s, AV_LOG_ERROR, "Changing frame properties on the fly is not supported.\n");\
69         return AVERROR(EINVAL);\
70     }
71
72 #if FF_API_VSRC_BUFFER_ADD_FRAME
73 int av_vsrc_buffer_add_frame(AVFilterContext *buffer_filter, AVFrame *frame,
74                              int64_t pts, AVRational pixel_aspect)
75 {
76     int64_t orig_pts = frame->pts;
77     AVRational orig_sar = frame->sample_aspect_ratio;
78     int ret;
79
80     frame->pts = pts;
81     frame->sample_aspect_ratio = pixel_aspect;
82     if ((ret = av_buffersrc_write_frame(buffer_filter, frame)) < 0)
83         return ret;
84     frame->pts = orig_pts;
85     frame->sample_aspect_ratio = orig_sar;
86
87     return 0;
88 }
89 #endif
90
91 int av_buffersrc_write_frame(AVFilterContext *buffer_filter, AVFrame *frame)
92 {
93     BufferSourceContext *c = buffer_filter->priv;
94     AVFilterBufferRef *buf;
95     int ret;
96
97     if (!frame) {
98         c->eof = 1;
99         return 0;
100     } else if (c->eof)
101         return AVERROR(EINVAL);
102
103     if (!av_fifo_space(c->fifo) &&
104         (ret = av_fifo_realloc2(c->fifo, av_fifo_size(c->fifo) +
105                                          sizeof(buf))) < 0)
106         return ret;
107
108     switch (buffer_filter->outputs[0]->type) {
109     case AVMEDIA_TYPE_VIDEO:
110         CHECK_VIDEO_PARAM_CHANGE(buffer_filter, c, frame->width, frame->height,
111                                  frame->format);
112         buf = ff_get_video_buffer(buffer_filter->outputs[0], AV_PERM_WRITE,
113                                   c->w, c->h);
114         av_image_copy(buf->data, buf->linesize, frame->data, frame->linesize,
115                       c->pix_fmt, c->w, c->h);
116         break;
117     case AVMEDIA_TYPE_AUDIO:
118         CHECK_AUDIO_PARAM_CHANGE(buffer_filter, c, frame->sample_rate, frame->channel_layout,
119                                  frame->format);
120         buf = ff_get_audio_buffer(buffer_filter->outputs[0], AV_PERM_WRITE,
121                                   frame->nb_samples);
122         av_samples_copy(buf->extended_data, frame->extended_data,
123                         0, 0, frame->nb_samples,
124                         av_get_channel_layout_nb_channels(frame->channel_layout),
125                         frame->format);
126         break;
127     default:
128         return AVERROR(EINVAL);
129     }
130
131     avfilter_copy_frame_props(buf, frame);
132
133     if ((ret = av_fifo_generic_write(c->fifo, &buf, sizeof(buf), NULL)) < 0) {
134         avfilter_unref_buffer(buf);
135         return ret;
136     }
137
138     return 0;
139 }
140
141 int av_buffersrc_buffer(AVFilterContext *s, AVFilterBufferRef *buf)
142 {
143     BufferSourceContext *c = s->priv;
144     int ret;
145
146     if (!buf) {
147         c->eof = 1;
148         return 0;
149     } else if (c->eof)
150         return AVERROR(EINVAL);
151
152     if (!av_fifo_space(c->fifo) &&
153         (ret = av_fifo_realloc2(c->fifo, av_fifo_size(c->fifo) +
154                                          sizeof(buf))) < 0)
155         return ret;
156
157     switch (s->outputs[0]->type) {
158     case AVMEDIA_TYPE_VIDEO:
159         CHECK_VIDEO_PARAM_CHANGE(s, c, buf->video->w, buf->video->h, buf->format);
160         break;
161     case AVMEDIA_TYPE_AUDIO:
162         CHECK_AUDIO_PARAM_CHANGE(s, c, buf->audio->sample_rate, buf->audio->channel_layout,
163                                  buf->format);
164         break;
165     default:
166         return AVERROR(EINVAL);
167     }
168
169     if ((ret = av_fifo_generic_write(c->fifo, &buf, sizeof(buf), NULL)) < 0)
170         return ret;
171
172     return 0;
173 }
174
175 static av_cold int init_video(AVFilterContext *ctx, const char *args, void *opaque)
176 {
177     BufferSourceContext *c = ctx->priv;
178     char pix_fmt_str[128];
179     int n = 0;
180
181     if (!args ||
182         (n = sscanf(args, "%d:%d:%127[^:]:%d:%d:%d:%d", &c->w, &c->h, pix_fmt_str,
183                     &c->time_base.num, &c->time_base.den,
184                     &c->pixel_aspect.num, &c->pixel_aspect.den)) != 7) {
185         av_log(ctx, AV_LOG_ERROR, "Expected 7 arguments, but %d found in '%s'\n", n, args);
186         return AVERROR(EINVAL);
187     }
188     if ((c->pix_fmt = av_get_pix_fmt(pix_fmt_str)) == PIX_FMT_NONE) {
189         char *tail;
190         c->pix_fmt = strtol(pix_fmt_str, &tail, 10);
191         if (*tail || c->pix_fmt < 0 || c->pix_fmt >= PIX_FMT_NB) {
192             av_log(ctx, AV_LOG_ERROR, "Invalid pixel format string '%s'\n", pix_fmt_str);
193             return AVERROR(EINVAL);
194         }
195     }
196
197     if (!(c->fifo = av_fifo_alloc(sizeof(AVFilterBufferRef*))))
198         return AVERROR(ENOMEM);
199
200     av_log(ctx, AV_LOG_INFO, "w:%d h:%d pixfmt:%s\n", c->w, c->h, av_pix_fmt_descriptors[c->pix_fmt].name);
201     return 0;
202 }
203
204 #define OFFSET(x) offsetof(BufferSourceContext, x)
205 #define A AV_OPT_FLAG_AUDIO_PARAM
206 static const AVOption audio_options[] = {
207     { "time_base",      NULL, OFFSET(time_base),           AV_OPT_TYPE_RATIONAL, { 0 }, 0, INT_MAX, A },
208     { "sample_rate",    NULL, OFFSET(sample_rate),         AV_OPT_TYPE_INT,      { 0 }, 0, INT_MAX, A },
209     { "sample_fmt",     NULL, OFFSET(sample_fmt_str),      AV_OPT_TYPE_STRING,             .flags = A },
210     { "channel_layout", NULL, OFFSET(channel_layout_str),  AV_OPT_TYPE_STRING,             .flags = A },
211     { NULL },
212 };
213
214 static const AVClass abuffer_class = {
215     .class_name = "abuffer source",
216     .item_name  = av_default_item_name,
217     .option     = audio_options,
218     .version    = LIBAVUTIL_VERSION_INT,
219 };
220
221 static av_cold int init_audio(AVFilterContext *ctx, const char *args, void *opaque)
222 {
223     BufferSourceContext *s = ctx->priv;
224     int ret = 0;
225
226     s->class = &abuffer_class;
227     av_opt_set_defaults(s);
228
229     if ((ret = av_set_options_string(s, args, "=", ":")) < 0) {
230         av_log(ctx, AV_LOG_ERROR, "Error parsing options string: %s.\n", args);
231         goto fail;
232     }
233
234     s->sample_fmt = av_get_sample_fmt(s->sample_fmt_str);
235     if (s->sample_fmt == AV_SAMPLE_FMT_NONE) {
236         av_log(ctx, AV_LOG_ERROR, "Invalid sample format %s.\n",
237                s->sample_fmt_str);
238         ret = AVERROR(EINVAL);
239         goto fail;
240     }
241
242     s->channel_layout = av_get_channel_layout(s->channel_layout_str);
243     if (!s->channel_layout) {
244         av_log(ctx, AV_LOG_ERROR, "Invalid channel layout %s.\n",
245                s->channel_layout_str);
246         ret = AVERROR(EINVAL);
247         goto fail;
248     }
249
250     if (!(s->fifo = av_fifo_alloc(sizeof(AVFilterBufferRef*)))) {
251         ret = AVERROR(ENOMEM);
252         goto fail;
253     }
254
255     if (!s->time_base.num)
256         s->time_base = (AVRational){1, s->sample_rate};
257
258     av_log(ctx, AV_LOG_VERBOSE, "tb:%d/%d samplefmt:%s samplerate: %d "
259            "ch layout:%s\n", s->time_base.num, s->time_base.den, s->sample_fmt_str,
260            s->sample_rate, s->channel_layout_str);
261
262 fail:
263     av_opt_free(s);
264     return ret;
265 }
266
267 static av_cold void uninit(AVFilterContext *ctx)
268 {
269     BufferSourceContext *s = ctx->priv;
270     while (s->fifo && av_fifo_size(s->fifo)) {
271         AVFilterBufferRef *buf;
272         av_fifo_generic_read(s->fifo, &buf, sizeof(buf), NULL);
273         avfilter_unref_buffer(buf);
274     }
275     av_fifo_free(s->fifo);
276     s->fifo = NULL;
277 }
278
279 static int query_formats(AVFilterContext *ctx)
280 {
281     BufferSourceContext *c = ctx->priv;
282     AVFilterChannelLayouts *channel_layouts = NULL;
283     AVFilterFormats *formats = NULL;
284     AVFilterFormats *samplerates = NULL;
285
286     switch (ctx->outputs[0]->type) {
287     case AVMEDIA_TYPE_VIDEO:
288         ff_add_format(&formats, c->pix_fmt);
289         ff_set_common_formats(ctx, formats);
290         break;
291     case AVMEDIA_TYPE_AUDIO:
292         ff_add_format(&formats,           c->sample_fmt);
293         ff_set_common_formats(ctx, formats);
294
295         ff_add_format(&samplerates,       c->sample_rate);
296         ff_set_common_samplerates(ctx, samplerates);
297
298         ff_add_channel_layout(&channel_layouts, c->channel_layout);
299         ff_set_common_channel_layouts(ctx, channel_layouts);
300         break;
301     default:
302         return AVERROR(EINVAL);
303     }
304
305     return 0;
306 }
307
308 static int config_props(AVFilterLink *link)
309 {
310     BufferSourceContext *c = link->src->priv;
311
312     switch (link->type) {
313     case AVMEDIA_TYPE_VIDEO:
314         link->w = c->w;
315         link->h = c->h;
316         link->sample_aspect_ratio = c->pixel_aspect;
317         break;
318     case AVMEDIA_TYPE_AUDIO:
319         link->channel_layout = c->channel_layout;
320         link->sample_rate    = c->sample_rate;
321         break;
322     default:
323         return AVERROR(EINVAL);
324     }
325
326     link->time_base = c->time_base;
327     return 0;
328 }
329
330 static int request_frame(AVFilterLink *link)
331 {
332     BufferSourceContext *c = link->src->priv;
333     AVFilterBufferRef *buf;
334
335     if (!av_fifo_size(c->fifo)) {
336         if (c->eof)
337             return AVERROR_EOF;
338         return AVERROR(EAGAIN);
339     }
340     av_fifo_generic_read(c->fifo, &buf, sizeof(buf), NULL);
341
342     switch (link->type) {
343     case AVMEDIA_TYPE_VIDEO:
344         ff_start_frame(link, avfilter_ref_buffer(buf, ~0));
345         ff_draw_slice(link, 0, link->h, 1);
346         ff_end_frame(link);
347         break;
348     case AVMEDIA_TYPE_AUDIO:
349         ff_filter_samples(link, avfilter_ref_buffer(buf, ~0));
350         break;
351     default:
352         return AVERROR(EINVAL);
353     }
354
355     avfilter_unref_buffer(buf);
356
357     return 0;
358 }
359
360 static int poll_frame(AVFilterLink *link)
361 {
362     BufferSourceContext *c = link->src->priv;
363     int size = av_fifo_size(c->fifo);
364     if (!size && c->eof)
365         return AVERROR_EOF;
366     return size/sizeof(AVFilterBufferRef*);
367 }
368
369 AVFilter avfilter_vsrc_buffer = {
370     .name      = "buffer",
371     .description = NULL_IF_CONFIG_SMALL("Buffer video frames, and make them accessible to the filterchain."),
372     .priv_size = sizeof(BufferSourceContext),
373     .query_formats = query_formats,
374
375     .init      = init_video,
376     .uninit    = uninit,
377
378     .inputs    = (AVFilterPad[]) {{ .name = NULL }},
379     .outputs   = (AVFilterPad[]) {{ .name            = "default",
380                                     .type            = AVMEDIA_TYPE_VIDEO,
381                                     .request_frame   = request_frame,
382                                     .poll_frame      = poll_frame,
383                                     .config_props    = config_props, },
384                                   { .name = NULL}},
385 };
386
387 AVFilter avfilter_asrc_abuffer = {
388     .name          = "abuffer",
389     .description   = NULL_IF_CONFIG_SMALL("Buffer audio frames, and make them accessible to the filterchain."),
390     .priv_size     = sizeof(BufferSourceContext),
391     .query_formats = query_formats,
392
393     .init      = init_audio,
394     .uninit    = uninit,
395
396     .inputs    = (AVFilterPad[]) {{ .name = NULL }},
397     .outputs   = (AVFilterPad[]) {{ .name            = "default",
398                                     .type            = AVMEDIA_TYPE_AUDIO,
399                                     .request_frame   = request_frame,
400                                     .poll_frame      = poll_frame,
401                                     .config_props    = config_props, },
402                                   { .name = NULL}},
403 };