Browse code

lavf: add tee pseudo-muxer.

Nicolas George authored on 2013/02/10 04:50:11
Showing 6 changed files
... ...
@@ -14,6 +14,7 @@ version <next>:
14 14
   and treble audio filter
15 15
 - improved showspectrum filter, with multichannel support and sox-like colors
16 16
 - histogram filter
17
+- tee muxer
17 18
 
18 19
 
19 20
 version 1.1:
... ...
@@ -749,4 +749,39 @@ situations, giving a small seek granularity at the cost of additional container
749 749
 overhead.
750 750
 @end table
751 751
 
752
+@section tee
753
+
754
+The tee muxer can be used to write the same data to several files or any
755
+other kind of muxer. It can be used, for example, to both stream a video to
756
+the network and save it to disk at the same time.
757
+
758
+It is different from specifying several outputs to the @command{ffmpeg}
759
+command-line tool because the audio and video data will be encoded only once
760
+with the tee muxer; encoding can be a very expensive process. It is not
761
+useful when using the libavformat API directly because it is then possible
762
+to feed the same packets to several muxers directly.
763
+
764
+The slave outputs are specified in the file name given to the muxer,
765
+separated by '|'. If any of the slave name contains the '|' separator,
766
+leading or trailing spaces or any special character, it must be
767
+@ref{quoting_and_escaping, escaped}.
768
+
769
+Options can be specified for each slave by prepending them as a list of
770
+@var{key}=@var{value} pairs separated by ':', between square brackets. If
771
+the options values contain a special character or the ':' separator, they
772
+must be @ref{quoting_and_escaping, escaped}; note that this is a second
773
+level escaping.
774
+
775
+Example: encode something and both archive it in a WebM file and stream it
776
+as MPEG-TS over UDP:
777
+
778
+@example
779
+ffmpeg -i ... -c:v libx264 -c:a mp2 -f tee
780
+  "archive-20121107.mkv|[f=mpegts]udp://10.0.1.255:1234/"
781
+@end example
782
+
783
+Note: some codecs may need different options depending on the output format;
784
+the auto-detection of this can not work with the tee muxer. The main example
785
+is the @option{global_header} flag.
786
+
752 787
 @c man end MUXERS
... ...
@@ -358,6 +358,7 @@ OBJS-$(CONFIG_SWF_DEMUXER)               += swfdec.o swf.o
358 358
 OBJS-$(CONFIG_SWF_MUXER)                 += swfenc.o swf.o
359 359
 OBJS-$(CONFIG_TAK_DEMUXER)               += takdec.o apetag.o img2.o rawdec.o
360 360
 OBJS-$(CONFIG_TEDCAPTIONS_DEMUXER)       += tedcaptionsdec.o
361
+OBJS-$(CONFIG_TEE_MUXER)                 += tee.o
361 362
 OBJS-$(CONFIG_THP_DEMUXER)               += thp.o
362 363
 OBJS-$(CONFIG_TIERTEXSEQ_DEMUXER)        += tiertexseq.o
363 364
 OBJS-$(CONFIG_MKVTIMESTAMP_V2_MUXER)     += mkvtimestamp_v2.o
... ...
@@ -263,6 +263,7 @@ void av_register_all(void)
263 263
     REGISTER_DEMUXER (SUBVIEWER,        subviewer);
264 264
     REGISTER_MUXDEMUX(SWF,              swf);
265 265
     REGISTER_DEMUXER (TAK,              tak);
266
+    REGISTER_MUXER   (TEE, tee);
266 267
     REGISTER_DEMUXER (TEDCAPTIONS,      tedcaptions);
267 268
     REGISTER_MUXER   (TG2,              tg2);
268 269
     REGISTER_MUXER   (TGP,              tgp);
269 270
new file mode 100644
... ...
@@ -0,0 +1,278 @@
0
+/*
1
+ * Tee pesudo-muxer
2
+ * Copyright (c) 2012 Nicolas George
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 License
8
+ * 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
14
+ * GNU Lesser General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU Lesser General Public License
17
+ * along with FFmpeg; if not, write to the Free Software * Foundation, Inc.,
18
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
+ */
20
+
21
+
22
+#include "libavutil/avutil.h"
23
+#include "libavutil/avstring.h"
24
+#include "libavutil/opt.h"
25
+#include "avformat.h"
26
+
27
+#define MAX_SLAVES 16
28
+
29
+typedef struct TeeContext {
30
+    const AVClass *class;
31
+    unsigned nb_slaves;
32
+    AVFormatContext *slaves[MAX_SLAVES];
33
+} TeeContext;
34
+
35
+static const char *const slave_delim     = "|";
36
+static const char *const slave_opt_open  = "[";
37
+static const char *const slave_opt_close = "]";
38
+static const char *const slave_opt_delim = ":]"; /* must have the close too */
39
+
40
+static const AVClass tee_muxer_class = {
41
+    .class_name = "Tee muxer",
42
+    .item_name  = av_default_item_name,
43
+    .version    = LIBAVUTIL_VERSION_INT,
44
+};
45
+
46
+static int parse_slave_options(void *log, char *slave,
47
+                               AVDictionary **options, char **filename)
48
+{
49
+    const char *p;
50
+    char *key, *val;
51
+    int ret;
52
+
53
+    if (!strspn(slave, slave_opt_open)) {
54
+        *filename = slave;
55
+        return 0;
56
+    }
57
+    p = slave + 1;
58
+    if (strspn(p, slave_opt_close)) {
59
+        *filename = (char *)p + 1;
60
+        return 0;
61
+    }
62
+    while (1) {
63
+        ret = av_opt_get_key_value(&p, "=", slave_opt_delim, 0, &key, &val);
64
+        if (ret < 0) {
65
+            av_log(log, AV_LOG_ERROR, "No option found near \"%s\"\n", p);
66
+            goto fail;
67
+        }
68
+        ret = av_dict_set(options, key, val,
69
+                          AV_DICT_DONT_STRDUP_KEY | AV_DICT_DONT_STRDUP_VAL);
70
+        if (ret < 0)
71
+            goto fail;
72
+        if (strspn(p, slave_opt_close))
73
+            break;
74
+        p++;
75
+    }
76
+    *filename = (char *)p + 1;
77
+    return 0;
78
+
79
+fail:
80
+    av_dict_free(options);
81
+    return ret;
82
+}
83
+
84
+static int open_slave(AVFormatContext *avf, char *slave, AVFormatContext **ravf)
85
+{
86
+    int i, ret;
87
+    AVDictionary *options = NULL;
88
+    AVDictionaryEntry *entry;
89
+    char *filename;
90
+    char *format = NULL;
91
+    AVFormatContext *avf2 = NULL;
92
+    AVStream *st, *st2;
93
+
94
+    if ((ret = parse_slave_options(avf, slave, &options, &filename)) < 0)
95
+        return ret;
96
+    if ((entry = av_dict_get(options, "f", NULL, 0))) {
97
+        format = entry->value;
98
+        entry->value = NULL; /* prevent it from being freed */
99
+        av_dict_set(&options, "f", NULL, 0);
100
+    }
101
+
102
+    avformat_alloc_output_context2(&avf2, NULL, format, filename);
103
+    if (ret < 0)
104
+        goto fail;
105
+    av_free(format);
106
+
107
+    for (i = 0; i < avf->nb_streams; i++) {
108
+        st = avf->streams[i];
109
+        if (!(st2 = avformat_new_stream(avf2, NULL))) {
110
+            ret = AVERROR(ENOMEM);
111
+            goto fail;
112
+        }
113
+        st2->id = st->id;
114
+        st2->r_frame_rate        = st->r_frame_rate;
115
+        st2->time_base           = st->time_base;
116
+        st2->start_time          = st->start_time;
117
+        st2->duration            = st->duration;
118
+        st2->nb_frames           = st->nb_frames;
119
+        st2->disposition         = st->disposition;
120
+        st2->sample_aspect_ratio = st->sample_aspect_ratio;
121
+        st2->avg_frame_rate      = st->avg_frame_rate;
122
+        av_dict_copy(&st2->metadata, st->metadata, 0);
123
+        if ((ret = avcodec_copy_context(st2->codec, st->codec)) < 0)
124
+            goto fail;
125
+    }
126
+
127
+    if (!(avf2->oformat->flags & AVFMT_NOFILE)) {
128
+        if ((ret = avio_open(&avf2->pb, filename, AVIO_FLAG_WRITE)) < 0) {
129
+            av_log(avf, AV_LOG_ERROR, "Slave '%s': error opening: %s\n",
130
+                   slave, av_err2str(ret));
131
+            goto fail;
132
+        }
133
+    }
134
+
135
+    if ((ret = avformat_write_header(avf2, &options)) < 0) {
136
+        av_log(avf, AV_LOG_ERROR, "Slave '%s': error writing header: %s\n",
137
+               slave, av_err2str(ret));
138
+        goto fail;
139
+    }
140
+    if (options) {
141
+        entry = NULL;
142
+        while ((entry = av_dict_get(options, "", entry, AV_DICT_IGNORE_SUFFIX)))
143
+            av_log(avf2, AV_LOG_ERROR, "Unknown option '%s'\n", entry->key);
144
+        ret = AVERROR_OPTION_NOT_FOUND;
145
+        goto fail;
146
+    }
147
+
148
+    *ravf = avf2;
149
+    return 0;
150
+
151
+fail:
152
+    av_dict_free(&options);
153
+    return ret;
154
+}
155
+
156
+static void close_slaves(AVFormatContext *avf)
157
+{
158
+    TeeContext *tee = avf->priv_data;
159
+    AVFormatContext *avf2;
160
+    unsigned i;
161
+
162
+    for (i = 0; i < tee->nb_slaves; i++) {
163
+        avf2 = tee->slaves[i];
164
+        avio_close(avf2->pb);
165
+        avf2->pb = NULL;
166
+        avformat_free_context(avf2);
167
+        tee->slaves[i] = NULL;
168
+    }
169
+}
170
+
171
+static int tee_write_header(AVFormatContext *avf)
172
+{
173
+    TeeContext *tee = avf->priv_data;
174
+    unsigned nb_slaves = 0, i;
175
+    const char *filename = avf->filename;
176
+    char *slaves[MAX_SLAVES];
177
+    int ret;
178
+
179
+    while (*filename) {
180
+        if (nb_slaves == MAX_SLAVES) {
181
+            av_log(avf, AV_LOG_ERROR, "Maximum %d slave muxers reached.\n",
182
+                   MAX_SLAVES);
183
+            ret = AVERROR_PATCHWELCOME;
184
+            goto fail;
185
+        }
186
+        if (!(slaves[nb_slaves++] = av_get_token(&filename, slave_delim))) {
187
+            ret = AVERROR(ENOMEM);
188
+            goto fail;
189
+        }
190
+        if (strspn(filename, slave_delim))
191
+            filename++;
192
+    }
193
+
194
+    for (i = 0; i < nb_slaves; i++) {
195
+        if ((ret = open_slave(avf, slaves[i], &tee->slaves[i])) < 0)
196
+            goto fail;
197
+        av_freep(&slaves[i]);
198
+    }
199
+
200
+    tee->nb_slaves = nb_slaves;
201
+    return 0;
202
+
203
+fail:
204
+    for (i = 0; i < nb_slaves; i++)
205
+        av_freep(&slaves[i]);
206
+    close_slaves(avf);
207
+    return ret;
208
+}
209
+
210
+static int tee_write_trailer(AVFormatContext *avf)
211
+{
212
+    TeeContext *tee = avf->priv_data;
213
+    AVFormatContext *avf2;
214
+    int ret_all = 0, ret;
215
+    unsigned i;
216
+
217
+    for (i = 0; i < tee->nb_slaves; i++) {
218
+        avf2 = tee->slaves[i];
219
+        if ((ret = av_write_trailer(avf2)) < 0)
220
+            if (!ret_all)
221
+                ret_all = ret;
222
+        if (!(avf2->oformat->flags & AVFMT_NOFILE)) {
223
+            if ((ret = avio_close(avf2->pb)) < 0)
224
+                if (!ret_all)
225
+                    ret_all = ret;
226
+            avf2->pb = NULL;
227
+        }
228
+    }
229
+    close_slaves(avf);
230
+    return ret_all;
231
+}
232
+
233
+static int tee_write_packet(AVFormatContext *avf, AVPacket *pkt)
234
+{
235
+    TeeContext *tee = avf->priv_data;
236
+    AVFormatContext *avf2;
237
+    AVPacket pkt2;
238
+    int ret_all = 0, ret;
239
+    unsigned i, s;
240
+    AVRational tb, tb2;
241
+
242
+    for (i = 0; i < tee->nb_slaves; i++) {
243
+        avf2 = tee->slaves[i];
244
+        s = pkt->stream_index;
245
+        if (s >= avf2->nb_streams) {
246
+            if (!ret_all)
247
+                ret_all = AVERROR(EINVAL);
248
+            continue;
249
+        }
250
+        if ((ret = av_copy_packet(&pkt2, pkt)) < 0 ||
251
+            (ret = av_dup_packet(&pkt2))< 0)
252
+            if (!ret_all) {
253
+                ret = ret_all;
254
+                continue;
255
+            }
256
+        tb  = avf ->streams[s]->time_base;
257
+        tb2 = avf2->streams[s]->time_base;
258
+        pkt2.pts      = av_rescale_q(pkt->pts,      tb, tb2);
259
+        pkt2.dts      = av_rescale_q(pkt->dts,      tb, tb2);
260
+        pkt2.duration = av_rescale_q(pkt->duration, tb, tb2);
261
+        if ((ret = av_interleaved_write_frame(avf2, &pkt2)) < 0)
262
+            if (!ret_all)
263
+                ret_all = ret;
264
+    }
265
+    return ret_all;
266
+}
267
+
268
+AVOutputFormat ff_tee_muxer = {
269
+    .name              = "tee",
270
+    .long_name         = NULL_IF_CONFIG_SMALL("Multiple muxer tee"),
271
+    .priv_data_size    = sizeof(TeeContext),
272
+    .write_header      = tee_write_header,
273
+    .write_trailer     = tee_write_trailer,
274
+    .write_packet      = tee_write_packet,
275
+    .priv_class        = &tee_muxer_class,
276
+    .flags             = AVFMT_NOFILE,
277
+};
... ...
@@ -30,8 +30,8 @@
30 30
 #include "libavutil/avutil.h"
31 31
 
32 32
 #define LIBAVFORMAT_VERSION_MAJOR 54
33
-#define LIBAVFORMAT_VERSION_MINOR 61
34
-#define LIBAVFORMAT_VERSION_MICRO 104
33
+#define LIBAVFORMAT_VERSION_MINOR 62
34
+#define LIBAVFORMAT_VERSION_MICRO 100
35 35
 
36 36
 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
37 37
                                                LIBAVFORMAT_VERSION_MINOR, \