Browse code

lavdev: add libavfilter virtual input device

This input device is to be considered still experimental, only video
output is supported.

Stefano Sabatini authored on 2011/06/11 22:40:08
Showing 7 changed files
... ...
@@ -7,6 +7,7 @@ version next:
7 7
 - boxblur filter added
8 8
 - BWF muxer
9 9
 - Flash Screen Video 2 decoder
10
+- lavfi input device added
10 11
 
11 12
 
12 13
 version 0.8:
... ...
@@ -1472,6 +1472,7 @@ dshow_indev_extralibs="-lpsapi -lole32 -lstrmiids -luuid"
1472 1472
 dv1394_indev_deps="dv1394 dv_demuxer"
1473 1473
 fbdev_indev_deps="linux_fb_h"
1474 1474
 jack_indev_deps="jack_jack_h sem_timedwait"
1475
+lavfi_indev_deps="avfilter"
1475 1476
 libdc1394_indev_deps="libdc1394"
1476 1477
 openal_indev_deps="openal"
1477 1478
 oss_indev_deps_any="soundcard_h sys_soundcard_h"
... ...
@@ -133,6 +133,60 @@ $ jack_connect metro:120_bpm ffmpeg:input_1
133 133
 For more information read:
134 134
 @url{http://jackaudio.org/}
135 135
 
136
+@section lavfi
137
+
138
+Libavfilter input virtual device.
139
+
140
+This input device reads data from the open output pads of a libavfilter
141
+filtergraph.
142
+
143
+For each filtergraph open output, the input device will create a
144
+corresponding stream which is mapped to the generated output. Currently
145
+only video data is supported. The filtergraph is specified through the
146
+option @option{graph}.
147
+
148
+To enable this input device, you need to configure your builf with
149
+@code{--enable-libavfilter}.
150
+
151
+@subsection Options
152
+
153
+@table @option
154
+
155
+@item graph
156
+Specify the filtergraph to use as input. Each video open output must be
157
+labelled by a unique string of the form "out@var{N}", where @var{N} is a
158
+number starting from 0 corresponding to the mapped input stream
159
+generated by the device.
160
+The first unlabelled output is automatically assigned to the "out0"
161
+label, but all the others need to be specified explicitely.
162
+
163
+If not specified defaults to the filename specified for the input
164
+device.
165
+@end table
166
+
167
+@subsection Examples
168
+
169
+@itemize
170
+@item
171
+Create a color video stream and play it back with @file{ffplay}:
172
+@example
173
+ffplay -f lavfi -graph "color=pink [out0]" dummy
174
+@end example
175
+
176
+@item
177
+As the previous example, but use filename for specifying the graph
178
+description, and omit the "out0" label:
179
+@example
180
+ffplay -f lavfi color=pink
181
+@end example
182
+
183
+@item
184
+Create three different video test filtered sources and play them:
185
+@example
186
+ffplay -f lavfi -graph "testsrc [out0]; testsrc,hflip [out1]; testsrc,negate [out2]" test3
187
+@end example
188
+@end itemize
189
+
136 190
 @section libdc1394
137 191
 
138 192
 IIDC1394 input device, based on libdc1394 and libraw1394.
... ...
@@ -2,6 +2,7 @@ include $(SUBDIR)../config.mak
2 2
 
3 3
 NAME    = avdevice
4 4
 FFLIBS  = avformat avcodec avutil
5
+FFLIBS-$(CONFIG_LAVFI_INDEV) += avfilter
5 6
 
6 7
 HEADERS = avdevice.h
7 8
 
... ...
@@ -19,6 +20,7 @@ OBJS-$(CONFIG_DSHOW_INDEV)               += dshow.o dshow_enummediatypes.o \
19 19
 OBJS-$(CONFIG_DV1394_INDEV)              += dv1394.o
20 20
 OBJS-$(CONFIG_FBDEV_INDEV)               += fbdev.o
21 21
 OBJS-$(CONFIG_JACK_INDEV)                += jack_audio.o
22
+OBJS-$(CONFIG_LAVFI_INDEV)               += lavfi.o
22 23
 OBJS-$(CONFIG_OPENAL_INDEV)              += openal-dec.o
23 24
 OBJS-$(CONFIG_OSS_INDEV)                 += oss_audio.o
24 25
 OBJS-$(CONFIG_OSS_OUTDEV)                += oss_audio.o
... ...
@@ -44,6 +44,7 @@ void avdevice_register_all(void)
44 44
     REGISTER_INDEV    (DV1394, dv1394);
45 45
     REGISTER_INDEV    (FBDEV, fbdev);
46 46
     REGISTER_INDEV    (JACK, jack);
47
+    REGISTER_INDEV    (LAVFI, lavfi);
47 48
     REGISTER_INDEV    (OPENAL, openal);
48 49
     REGISTER_INOUTDEV (OSS, oss);
49 50
     REGISTER_OUTDEV   (SDL, sdl);
... ...
@@ -23,7 +23,7 @@
23 23
 #include "libavformat/avformat.h"
24 24
 
25 25
 #define LIBAVDEVICE_VERSION_MAJOR 53
26
-#define LIBAVDEVICE_VERSION_MINOR  2
26
+#define LIBAVDEVICE_VERSION_MINOR  3
27 27
 #define LIBAVDEVICE_VERSION_MICRO  0
28 28
 
29 29
 #define LIBAVDEVICE_VERSION_INT AV_VERSION_INT(LIBAVDEVICE_VERSION_MAJOR, \
30 30
new file mode 100644
... ...
@@ -0,0 +1,282 @@
0
+/*
1
+ * Copyright (c) 2011 Stefano Sabatini
2
+ *
3
+ * This file is part of FFmpeg.
4
+ *
5
+ * FFmpeg is free software; you can redistribute it and/or
6
+ * modify it under the terms of the GNU Lesser General Public
7
+ * License as published by the Free Software Foundation; either
8
+ * version 2.1 of the License, or (at your option) any later version.
9
+ *
10
+ * FFmpeg is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
+ * Lesser General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU Lesser General Public
16
+ * License along with FFmpeg; if not, write to the Free Software
17
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
+ */
19
+
20
+/**
21
+ * @file
22
+ * libavfilter virtual input device
23
+ */
24
+
25
+/* #define DEBUG */
26
+
27
+#include "float.h"              /* DBL_MIN, DBL_MAX */
28
+
29
+#include "libavutil/log.h"
30
+#include "libavutil/mem.h"
31
+#include "libavutil/opt.h"
32
+#include "libavutil/parseutils.h"
33
+#include "libavutil/pixdesc.h"
34
+#include "libavfilter/avfilter.h"
35
+#include "libavfilter/avfiltergraph.h"
36
+#include "libavfilter/vsink_buffer.h"
37
+#include "avdevice.h"
38
+
39
+typedef struct {
40
+    AVClass *class;          ///< class for private options
41
+    char          *graph_str;
42
+    AVFilterGraph *graph;
43
+    AVFilterContext **sinks;
44
+    int *sink_stream_map;
45
+    int *stream_sink_map;
46
+} LavfiContext;
47
+
48
+static int *create_all_formats(int n)
49
+{
50
+    int i, j, *fmts, count = 0;
51
+
52
+    for (i = 0; i < n; i++)
53
+        if (!(av_pix_fmt_descriptors[i].flags & PIX_FMT_HWACCEL))
54
+            count++;
55
+
56
+    if (!(fmts = av_malloc((count+1) * sizeof(int))))
57
+        return NULL;
58
+    for (j = 0, i = 0; i < n; i++) {
59
+        if (!(av_pix_fmt_descriptors[i].flags & PIX_FMT_HWACCEL))
60
+            fmts[j++] = i;
61
+    }
62
+    fmts[j] = -1;
63
+    return fmts;
64
+}
65
+
66
+av_cold static int lavfi_read_close(AVFormatContext *avctx)
67
+{
68
+    LavfiContext *lavfi = avctx->priv_data;
69
+
70
+    av_freep(&lavfi->sink_stream_map);
71
+    av_freep(&lavfi->stream_sink_map);
72
+    avfilter_graph_free(&lavfi->graph);
73
+
74
+    return 0;
75
+}
76
+
77
+av_cold static int lavfi_read_header(AVFormatContext *avctx,
78
+                                     AVFormatParameters *ap)
79
+{
80
+    LavfiContext *lavfi = avctx->priv_data;
81
+    AVFilterInOut *input_links = NULL, *output_links = NULL, *inout;
82
+    AVFilter *buffersink;
83
+    int *pix_fmts = create_all_formats(PIX_FMT_NB);
84
+    int ret = 0, i, n;
85
+
86
+#define FAIL(ERR) { ret = ERR; goto end; }
87
+
88
+    avfilter_register_all();
89
+
90
+    if (!(buffersink = avfilter_get_by_name("buffersink"))) {
91
+        av_log(avctx, AV_LOG_ERROR,
92
+               "Missing required buffersink filter, aborting.\n");
93
+        FAIL(AVERROR_FILTER_NOT_FOUND);
94
+    }
95
+
96
+    if (!lavfi->graph_str)
97
+        lavfi->graph_str = av_strdup(avctx->filename);
98
+
99
+    /* parse the graph, create a stream for each open output */
100
+    if (!(lavfi->graph = avfilter_graph_alloc()))
101
+        FAIL(AVERROR(ENOMEM));
102
+
103
+    if ((ret = avfilter_graph_parse(lavfi->graph, lavfi->graph_str,
104
+                                    &input_links, &output_links, avctx)) < 0)
105
+        FAIL(ret);
106
+
107
+    if (input_links) {
108
+        av_log(avctx, AV_LOG_ERROR,
109
+               "Open inputs in the filtergraph are not acceptable\n");
110
+        FAIL(AVERROR(EINVAL));
111
+    }
112
+
113
+    /* count the outputs */
114
+    for (n = 0, inout = output_links; inout; n++, inout = inout->next);
115
+
116
+    if (!(lavfi->sink_stream_map = av_malloc(sizeof(int) * n)))
117
+        FAIL(AVERROR(ENOMEM));
118
+    if (!(lavfi->stream_sink_map = av_malloc(sizeof(int) * n)))
119
+        FAIL(AVERROR(ENOMEM));
120
+
121
+    for (i = 0; i < n; i++)
122
+        lavfi->stream_sink_map[i] = -1;
123
+
124
+    /* parse the output link names - they need to be of the form out0, out1, ...
125
+     * create a mapping between them and the streams */
126
+    for (i = 0, inout = output_links; inout; i++, inout = inout->next) {
127
+        int stream_idx;
128
+        if (!strcmp(inout->name, "out"))
129
+            stream_idx = 0;
130
+        else if (sscanf(inout->name, "out%d\n", &stream_idx) != 1) {
131
+            av_log(avctx,  AV_LOG_ERROR,
132
+                   "Invalid outpad name '%s'\n", inout->name);
133
+            FAIL(AVERROR(EINVAL));
134
+        }
135
+
136
+        if ((unsigned)stream_idx >= n) {
137
+            av_log(avctx, AV_LOG_ERROR,
138
+                   "Invalid index was specified in output '%s', "
139
+                   "must be a non-negative value < %d\n",
140
+                   inout->name, n);
141
+            FAIL(AVERROR(EINVAL));
142
+        }
143
+
144
+        /* is a video output? */
145
+        if (inout->filter_ctx->output_pads[inout->pad_idx].type != AVMEDIA_TYPE_VIDEO) {
146
+            av_log(avctx,  AV_LOG_ERROR,
147
+                   "Output '%s' is not a video output, not yet supported", inout->name);
148
+            FAIL(AVERROR(EINVAL));
149
+        }
150
+
151
+        if (lavfi->stream_sink_map[stream_idx] != -1) {
152
+            av_log(avctx,  AV_LOG_ERROR,
153
+                   "An with stream index %d was already specified\n",
154
+                   stream_idx);
155
+            FAIL(AVERROR(EINVAL));
156
+        }
157
+        lavfi->sink_stream_map[i] = stream_idx;
158
+        lavfi->stream_sink_map[stream_idx] = i;
159
+    }
160
+
161
+    /* for each open output create a corresponding stream */
162
+    for (i = 0, inout = output_links; inout; i++, inout = inout->next) {
163
+        AVStream *st;
164
+        if (!(st = av_new_stream(avctx, i)))
165
+            FAIL(AVERROR(ENOMEM));
166
+    }
167
+
168
+    /* create a sink for each output and connect them to the graph */
169
+    lavfi->sinks = av_malloc(sizeof(AVFilterContext *) * avctx->nb_streams);
170
+    if (!lavfi->sinks)
171
+        FAIL(AVERROR(ENOMEM));
172
+
173
+    for (i = 0, inout = output_links; inout; i++, inout = inout->next) {
174
+        AVFilterContext *sink;
175
+        if ((ret = avfilter_graph_create_filter(&sink, buffersink,
176
+                                                inout->name, NULL,
177
+                                                pix_fmts, lavfi->graph)) < 0)
178
+            FAIL(ret);
179
+        lavfi->sinks[i] = sink;
180
+        if ((ret = avfilter_link(inout->filter_ctx, 0, sink, 0)) < 0)
181
+            FAIL(ret);
182
+    }
183
+
184
+    /* configure the graph */
185
+    if ((ret = avfilter_graph_config(lavfi->graph, avctx)) < 0)
186
+        FAIL(ret);
187
+
188
+    /* fill each stream with the information in the corresponding sink */
189
+    for (i = 0; i < avctx->nb_streams; i++) {
190
+        AVFilterLink *link = lavfi->sinks[lavfi->stream_sink_map[i]]->inputs[0];
191
+        AVStream *st = avctx->streams[i];
192
+        st->codec->codec_type = link->type;
193
+        av_set_pts_info(st, 64, link->time_base.num, link->time_base.den);
194
+        if (link->type == AVMEDIA_TYPE_VIDEO) {
195
+            st->codec->codec_id   = CODEC_ID_RAWVIDEO;
196
+            st->codec->pix_fmt    = link->format;
197
+            st->codec->time_base  = link->time_base;
198
+            st->codec->width      = link->w;
199
+            st->codec->height     = link->h;
200
+        }
201
+    }
202
+
203
+end:
204
+    avfilter_inout_free(&input_links);
205
+    avfilter_inout_free(&output_links);
206
+    if (ret < 0)
207
+        lavfi_read_close(avctx);
208
+    return ret;
209
+}
210
+
211
+static int lavfi_read_packet(AVFormatContext *avctx, AVPacket *pkt)
212
+{
213
+    LavfiContext *lavfi = avctx->priv_data;
214
+    double min_pts = DBL_MAX;
215
+    int min_pts_sink_idx;
216
+    AVFilterBufferRef *picref;
217
+    AVPicture pict;
218
+    int ret, i, size;
219
+
220
+    /* iterate through all the graph sinks. Select the sink with the
221
+     * minimum PTS */
222
+    for (i = 0; i < avctx->nb_streams; i++) {
223
+        AVRational tb = lavfi->sinks[i]->inputs[0]->time_base;
224
+        double d;
225
+        int ret = av_vsink_buffer_get_video_buffer_ref(lavfi->sinks[i],
226
+                                                       &picref, AV_VSINK_BUF_FLAG_PEEK);
227
+        if (ret < 0)
228
+            return ret;
229
+        d = av_rescale_q(picref->pts, tb, AV_TIME_BASE_Q);
230
+        if (d < min_pts) {
231
+            min_pts = d;
232
+            min_pts_sink_idx = i;
233
+        }
234
+    }
235
+
236
+    av_vsink_buffer_get_video_buffer_ref(lavfi->sinks[min_pts_sink_idx],
237
+                                         &picref, 0);
238
+
239
+    size = avpicture_get_size(picref->format, picref->video->w, picref->video->h);
240
+    if ((ret = av_new_packet(pkt, size)) < 0)
241
+        return ret;
242
+
243
+    memcpy(pict.data,     picref->data,     4*sizeof(picref->data[0]));
244
+    memcpy(pict.linesize, picref->linesize, 4*sizeof(picref->linesize[0]));
245
+
246
+    avpicture_layout(&pict, picref->format, picref->video->w,
247
+                     picref->video->h, pkt->data, size);
248
+    pkt->stream_index = lavfi->sink_stream_map[min_pts_sink_idx];
249
+    pkt->pts = picref->pts;
250
+    pkt->size = size;
251
+    avfilter_unref_buffer(picref);
252
+
253
+    return size;
254
+}
255
+
256
+#define OFFSET(x) offsetof(LavfiContext, x)
257
+
258
+#define DEC AV_OPT_FLAG_DECODING_PARAM
259
+
260
+static const AVOption options[] = {
261
+    { "graph", "Libavfilter graph", OFFSET(graph_str),  FF_OPT_TYPE_STRING, {.str = NULL }, 0,  0, DEC },
262
+    { NULL },
263
+};
264
+
265
+static const AVClass lavfi_class = {
266
+    .class_name = "lavfi indev",
267
+    .item_name  = av_default_item_name,
268
+    .option     = options,
269
+    .version    = LIBAVUTIL_VERSION_INT,
270
+};
271
+
272
+AVInputFormat ff_lavfi_demuxer = {
273
+    .name           = "lavfi",
274
+    .long_name      = NULL_IF_CONFIG_SMALL("Libavfilter virtual input device"),
275
+    .priv_data_size = sizeof(LavfiContext),
276
+    .read_header    = lavfi_read_header,
277
+    .read_packet    = lavfi_read_packet,
278
+    .read_close     = lavfi_read_close,
279
+    .flags          = AVFMT_NOFILE,
280
+    .priv_class     = &lavfi_class,
281
+};