Commit | Line | Data |
---|---|---|
2ba45a60 DM |
1 | /* |
2 | * Tee pseudo-muxer | |
3 | * Copyright (c) 2012 Nicolas George | |
4 | * | |
5 | * This file is part of FFmpeg. | |
6 | * | |
7 | * FFmpeg is free software; you can redistribute it and/or | |
8 | * modify it under the terms of the GNU Lesser General Public License | |
9 | * as published by the Free Software Foundation; either | |
10 | * version 2.1 of the License, or (at your option) any later version. | |
11 | * | |
12 | * FFmpeg is distributed in the hope that it will be useful, | |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | * GNU Lesser General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU Lesser General Public License | |
18 | * along with FFmpeg; if not, write to the Free Software * Foundation, Inc., | |
19 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
20 | */ | |
21 | ||
22 | ||
23 | #include "libavutil/avutil.h" | |
24 | #include "libavutil/avstring.h" | |
25 | #include "libavutil/opt.h" | |
26 | #include "avformat.h" | |
27 | ||
28 | #define MAX_SLAVES 16 | |
29 | ||
30 | typedef struct { | |
31 | AVFormatContext *avf; | |
32 | AVBitStreamFilterContext **bsfs; ///< bitstream filters per stream | |
33 | ||
34 | /** map from input to output streams indexes, | |
35 | * disabled output streams are set to -1 */ | |
36 | int *stream_map; | |
37 | } TeeSlave; | |
38 | ||
39 | typedef struct TeeContext { | |
40 | const AVClass *class; | |
41 | unsigned nb_slaves; | |
42 | TeeSlave slaves[MAX_SLAVES]; | |
43 | } TeeContext; | |
44 | ||
45 | static const char *const slave_delim = "|"; | |
46 | static const char *const slave_opt_open = "["; | |
47 | static const char *const slave_opt_close = "]"; | |
48 | static const char *const slave_opt_delim = ":]"; /* must have the close too */ | |
49 | static const char *const slave_bsfs_spec_sep = "/"; | |
50 | ||
51 | static const AVClass tee_muxer_class = { | |
52 | .class_name = "Tee muxer", | |
53 | .item_name = av_default_item_name, | |
54 | .version = LIBAVUTIL_VERSION_INT, | |
55 | }; | |
56 | ||
57 | static int parse_slave_options(void *log, char *slave, | |
58 | AVDictionary **options, char **filename) | |
59 | { | |
60 | const char *p; | |
61 | char *key, *val; | |
62 | int ret; | |
63 | ||
64 | if (!strspn(slave, slave_opt_open)) { | |
65 | *filename = slave; | |
66 | return 0; | |
67 | } | |
68 | p = slave + 1; | |
69 | if (strspn(p, slave_opt_close)) { | |
70 | *filename = (char *)p + 1; | |
71 | return 0; | |
72 | } | |
73 | while (1) { | |
74 | ret = av_opt_get_key_value(&p, "=", slave_opt_delim, 0, &key, &val); | |
75 | if (ret < 0) { | |
76 | av_log(log, AV_LOG_ERROR, "No option found near \"%s\"\n", p); | |
77 | goto fail; | |
78 | } | |
79 | ret = av_dict_set(options, key, val, | |
80 | AV_DICT_DONT_STRDUP_KEY | AV_DICT_DONT_STRDUP_VAL); | |
81 | if (ret < 0) | |
82 | goto fail; | |
83 | if (strspn(p, slave_opt_close)) | |
84 | break; | |
85 | p++; | |
86 | } | |
87 | *filename = (char *)p + 1; | |
88 | return 0; | |
89 | ||
90 | fail: | |
91 | av_dict_free(options); | |
92 | return ret; | |
93 | } | |
94 | ||
95 | /** | |
96 | * Parse list of bitstream filters and add them to the list of filters | |
97 | * pointed to by bsfs. | |
98 | * | |
99 | * The list must be specified in the form: | |
100 | * BSFS ::= BSF[,BSFS] | |
101 | */ | |
102 | static int parse_bsfs(void *log_ctx, const char *bsfs_spec, | |
103 | AVBitStreamFilterContext **bsfs) | |
104 | { | |
105 | char *bsf_name, *buf, *dup, *saveptr; | |
106 | int ret = 0; | |
107 | ||
108 | if (!(dup = buf = av_strdup(bsfs_spec))) | |
109 | return AVERROR(ENOMEM); | |
110 | ||
111 | while (bsf_name = av_strtok(buf, ",", &saveptr)) { | |
112 | AVBitStreamFilterContext *bsf = av_bitstream_filter_init(bsf_name); | |
113 | ||
114 | if (!bsf) { | |
115 | av_log(log_ctx, AV_LOG_ERROR, | |
116 | "Cannot initialize bitstream filter with name '%s', " | |
117 | "unknown filter or internal error happened\n", | |
118 | bsf_name); | |
119 | ret = AVERROR_UNKNOWN; | |
120 | goto end; | |
121 | } | |
122 | ||
123 | /* append bsf context to the list of bsf contexts */ | |
124 | *bsfs = bsf; | |
125 | bsfs = &bsf->next; | |
126 | ||
127 | buf = NULL; | |
128 | } | |
129 | ||
130 | end: | |
131 | av_free(dup); | |
132 | return ret; | |
133 | } | |
134 | ||
135 | static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) | |
136 | { | |
137 | int i, ret; | |
138 | AVDictionary *options = NULL; | |
139 | AVDictionaryEntry *entry; | |
140 | char *filename; | |
141 | char *format = NULL, *select = NULL; | |
142 | AVFormatContext *avf2 = NULL; | |
143 | AVStream *st, *st2; | |
144 | int stream_count; | |
145 | ||
146 | if ((ret = parse_slave_options(avf, slave, &options, &filename)) < 0) | |
147 | return ret; | |
148 | ||
149 | #define STEAL_OPTION(option, field) do { \ | |
150 | if ((entry = av_dict_get(options, option, NULL, 0))) { \ | |
151 | field = entry->value; \ | |
152 | entry->value = NULL; /* prevent it from being freed */ \ | |
153 | av_dict_set(&options, option, NULL, 0); \ | |
154 | } \ | |
155 | } while (0) | |
156 | ||
157 | STEAL_OPTION("f", format); | |
158 | STEAL_OPTION("select", select); | |
159 | ||
160 | ret = avformat_alloc_output_context2(&avf2, NULL, format, filename); | |
161 | if (ret < 0) | |
162 | goto end; | |
163 | av_dict_copy(&avf2->metadata, avf->metadata, 0); | |
164 | ||
165 | tee_slave->stream_map = av_calloc(avf->nb_streams, sizeof(*tee_slave->stream_map)); | |
166 | if (!tee_slave->stream_map) { | |
167 | ret = AVERROR(ENOMEM); | |
168 | goto end; | |
169 | } | |
170 | ||
171 | stream_count = 0; | |
172 | for (i = 0; i < avf->nb_streams; i++) { | |
173 | st = avf->streams[i]; | |
174 | if (select) { | |
175 | ret = avformat_match_stream_specifier(avf, avf->streams[i], select); | |
176 | if (ret < 0) { | |
177 | av_log(avf, AV_LOG_ERROR, | |
178 | "Invalid stream specifier '%s' for output '%s'\n", | |
179 | select, slave); | |
180 | goto end; | |
181 | } | |
182 | ||
183 | if (ret == 0) { /* no match */ | |
184 | tee_slave->stream_map[i] = -1; | |
185 | continue; | |
186 | } | |
187 | } | |
188 | tee_slave->stream_map[i] = stream_count++; | |
189 | ||
190 | if (!(st2 = avformat_new_stream(avf2, NULL))) { | |
191 | ret = AVERROR(ENOMEM); | |
192 | goto end; | |
193 | } | |
194 | st2->id = st->id; | |
195 | st2->r_frame_rate = st->r_frame_rate; | |
196 | st2->time_base = st->time_base; | |
197 | st2->start_time = st->start_time; | |
198 | st2->duration = st->duration; | |
199 | st2->nb_frames = st->nb_frames; | |
200 | st2->disposition = st->disposition; | |
201 | st2->sample_aspect_ratio = st->sample_aspect_ratio; | |
202 | st2->avg_frame_rate = st->avg_frame_rate; | |
203 | av_dict_copy(&st2->metadata, st->metadata, 0); | |
204 | if ((ret = avcodec_copy_context(st2->codec, st->codec)) < 0) | |
205 | goto end; | |
206 | } | |
207 | ||
208 | if (!(avf2->oformat->flags & AVFMT_NOFILE)) { | |
209 | if ((ret = avio_open(&avf2->pb, filename, AVIO_FLAG_WRITE)) < 0) { | |
210 | av_log(avf, AV_LOG_ERROR, "Slave '%s': error opening: %s\n", | |
211 | slave, av_err2str(ret)); | |
212 | goto end; | |
213 | } | |
214 | } | |
215 | ||
216 | if ((ret = avformat_write_header(avf2, &options)) < 0) { | |
217 | av_log(avf, AV_LOG_ERROR, "Slave '%s': error writing header: %s\n", | |
218 | slave, av_err2str(ret)); | |
219 | goto end; | |
220 | } | |
221 | ||
222 | tee_slave->avf = avf2; | |
223 | tee_slave->bsfs = av_calloc(avf2->nb_streams, sizeof(TeeSlave)); | |
224 | if (!tee_slave->bsfs) { | |
225 | ret = AVERROR(ENOMEM); | |
226 | goto end; | |
227 | } | |
228 | ||
229 | entry = NULL; | |
230 | while (entry = av_dict_get(options, "bsfs", NULL, AV_DICT_IGNORE_SUFFIX)) { | |
231 | const char *spec = entry->key + strlen("bsfs"); | |
232 | if (*spec) { | |
233 | if (strspn(spec, slave_bsfs_spec_sep) != 1) { | |
234 | av_log(avf, AV_LOG_ERROR, | |
235 | "Specifier separator in '%s' is '%c', but only characters '%s' " | |
236 | "are allowed\n", entry->key, *spec, slave_bsfs_spec_sep); | |
237 | return AVERROR(EINVAL); | |
238 | } | |
239 | spec++; /* consume separator */ | |
240 | } | |
241 | ||
242 | for (i = 0; i < avf2->nb_streams; i++) { | |
243 | ret = avformat_match_stream_specifier(avf2, avf2->streams[i], spec); | |
244 | if (ret < 0) { | |
245 | av_log(avf, AV_LOG_ERROR, | |
246 | "Invalid stream specifier '%s' in bsfs option '%s' for slave " | |
247 | "output '%s'\n", spec, entry->key, filename); | |
248 | goto end; | |
249 | } | |
250 | ||
251 | if (ret > 0) { | |
252 | av_log(avf, AV_LOG_DEBUG, "spec:%s bsfs:%s matches stream %d of slave " | |
253 | "output '%s'\n", spec, entry->value, i, filename); | |
254 | if (tee_slave->bsfs[i]) { | |
255 | av_log(avf, AV_LOG_WARNING, | |
256 | "Duplicate bsfs specification associated to stream %d of slave " | |
257 | "output '%s', filters will be ignored\n", i, filename); | |
258 | continue; | |
259 | } | |
260 | ret = parse_bsfs(avf, entry->value, &tee_slave->bsfs[i]); | |
261 | if (ret < 0) { | |
262 | av_log(avf, AV_LOG_ERROR, | |
263 | "Error parsing bitstream filter sequence '%s' associated to " | |
264 | "stream %d of slave output '%s'\n", entry->value, i, filename); | |
265 | goto end; | |
266 | } | |
267 | } | |
268 | } | |
269 | ||
270 | av_dict_set(&options, entry->key, NULL, 0); | |
271 | } | |
272 | ||
273 | if (options) { | |
274 | entry = NULL; | |
275 | while ((entry = av_dict_get(options, "", entry, AV_DICT_IGNORE_SUFFIX))) | |
276 | av_log(avf2, AV_LOG_ERROR, "Unknown option '%s'\n", entry->key); | |
277 | ret = AVERROR_OPTION_NOT_FOUND; | |
278 | goto end; | |
279 | } | |
280 | ||
281 | end: | |
282 | av_free(format); | |
283 | av_free(select); | |
284 | av_dict_free(&options); | |
285 | return ret; | |
286 | } | |
287 | ||
288 | static void close_slaves(AVFormatContext *avf) | |
289 | { | |
290 | TeeContext *tee = avf->priv_data; | |
291 | AVFormatContext *avf2; | |
292 | unsigned i, j; | |
293 | ||
294 | for (i = 0; i < tee->nb_slaves; i++) { | |
295 | avf2 = tee->slaves[i].avf; | |
296 | ||
297 | for (j = 0; j < avf2->nb_streams; j++) { | |
298 | AVBitStreamFilterContext *bsf_next, *bsf = tee->slaves[i].bsfs[j]; | |
299 | while (bsf) { | |
300 | bsf_next = bsf->next; | |
301 | av_bitstream_filter_close(bsf); | |
302 | bsf = bsf_next; | |
303 | } | |
304 | } | |
305 | av_freep(&tee->slaves[i].stream_map); | |
306 | av_freep(&tee->slaves[i].bsfs); | |
307 | ||
308 | avio_close(avf2->pb); | |
309 | avf2->pb = NULL; | |
310 | avformat_free_context(avf2); | |
311 | tee->slaves[i].avf = NULL; | |
312 | } | |
313 | } | |
314 | ||
315 | static void log_slave(TeeSlave *slave, void *log_ctx, int log_level) | |
316 | { | |
317 | int i; | |
318 | av_log(log_ctx, log_level, "filename:'%s' format:%s\n", | |
319 | slave->avf->filename, slave->avf->oformat->name); | |
320 | for (i = 0; i < slave->avf->nb_streams; i++) { | |
321 | AVStream *st = slave->avf->streams[i]; | |
322 | AVBitStreamFilterContext *bsf = slave->bsfs[i]; | |
323 | ||
324 | av_log(log_ctx, log_level, " stream:%d codec:%s type:%s", | |
325 | i, avcodec_get_name(st->codec->codec_id), | |
326 | av_get_media_type_string(st->codec->codec_type)); | |
327 | if (bsf) { | |
328 | av_log(log_ctx, log_level, " bsfs:"); | |
329 | while (bsf) { | |
330 | av_log(log_ctx, log_level, "%s%s", | |
331 | bsf->filter->name, bsf->next ? "," : ""); | |
332 | bsf = bsf->next; | |
333 | } | |
334 | } | |
335 | av_log(log_ctx, log_level, "\n"); | |
336 | } | |
337 | } | |
338 | ||
339 | static int tee_write_header(AVFormatContext *avf) | |
340 | { | |
341 | TeeContext *tee = avf->priv_data; | |
342 | unsigned nb_slaves = 0, i; | |
343 | const char *filename = avf->filename; | |
344 | char *slaves[MAX_SLAVES]; | |
345 | int ret; | |
346 | ||
347 | while (*filename) { | |
348 | if (nb_slaves == MAX_SLAVES) { | |
349 | av_log(avf, AV_LOG_ERROR, "Maximum %d slave muxers reached.\n", | |
350 | MAX_SLAVES); | |
351 | ret = AVERROR_PATCHWELCOME; | |
352 | goto fail; | |
353 | } | |
354 | if (!(slaves[nb_slaves++] = av_get_token(&filename, slave_delim))) { | |
355 | ret = AVERROR(ENOMEM); | |
356 | goto fail; | |
357 | } | |
358 | if (strspn(filename, slave_delim)) | |
359 | filename++; | |
360 | } | |
361 | ||
362 | for (i = 0; i < nb_slaves; i++) { | |
363 | if ((ret = open_slave(avf, slaves[i], &tee->slaves[i])) < 0) | |
364 | goto fail; | |
365 | log_slave(&tee->slaves[i], avf, AV_LOG_VERBOSE); | |
366 | av_freep(&slaves[i]); | |
367 | } | |
368 | ||
369 | tee->nb_slaves = nb_slaves; | |
370 | ||
371 | for (i = 0; i < avf->nb_streams; i++) { | |
372 | int j, mapped = 0; | |
373 | for (j = 0; j < tee->nb_slaves; j++) | |
374 | mapped += tee->slaves[j].stream_map[i] >= 0; | |
375 | if (!mapped) | |
376 | av_log(avf, AV_LOG_WARNING, "Input stream #%d is not mapped " | |
377 | "to any slave.\n", i); | |
378 | } | |
379 | return 0; | |
380 | ||
381 | fail: | |
382 | for (i = 0; i < nb_slaves; i++) | |
383 | av_freep(&slaves[i]); | |
384 | close_slaves(avf); | |
385 | return ret; | |
386 | } | |
387 | ||
388 | static int filter_packet(void *log_ctx, AVPacket *pkt, | |
389 | AVFormatContext *fmt_ctx, AVBitStreamFilterContext *bsf_ctx) | |
390 | { | |
391 | AVCodecContext *enc_ctx = fmt_ctx->streams[pkt->stream_index]->codec; | |
392 | int ret = 0; | |
393 | ||
394 | while (bsf_ctx) { | |
395 | AVPacket new_pkt = *pkt; | |
396 | ret = av_bitstream_filter_filter(bsf_ctx, enc_ctx, NULL, | |
397 | &new_pkt.data, &new_pkt.size, | |
398 | pkt->data, pkt->size, | |
399 | pkt->flags & AV_PKT_FLAG_KEY); | |
400 | if (ret == 0 && new_pkt.data != pkt->data && new_pkt.destruct) { | |
401 | if ((ret = av_copy_packet(&new_pkt, pkt)) < 0) | |
402 | break; | |
403 | ret = 1; | |
404 | } | |
405 | ||
406 | if (ret > 0) { | |
407 | av_free_packet(pkt); | |
408 | new_pkt.buf = av_buffer_create(new_pkt.data, new_pkt.size, | |
409 | av_buffer_default_free, NULL, 0); | |
410 | if (!new_pkt.buf) | |
411 | break; | |
412 | } | |
413 | if (ret < 0) { | |
414 | av_log(log_ctx, AV_LOG_ERROR, | |
415 | "Failed to filter bitstream with filter %s for stream %d in file '%s' with codec %s\n", | |
416 | bsf_ctx->filter->name, pkt->stream_index, fmt_ctx->filename, | |
417 | avcodec_get_name(enc_ctx->codec_id)); | |
418 | } | |
419 | *pkt = new_pkt; | |
420 | ||
421 | bsf_ctx = bsf_ctx->next; | |
422 | } | |
423 | ||
424 | return ret; | |
425 | } | |
426 | ||
427 | static int tee_write_trailer(AVFormatContext *avf) | |
428 | { | |
429 | TeeContext *tee = avf->priv_data; | |
430 | AVFormatContext *avf2; | |
431 | int ret_all = 0, ret; | |
432 | unsigned i; | |
433 | ||
434 | for (i = 0; i < tee->nb_slaves; i++) { | |
435 | avf2 = tee->slaves[i].avf; | |
436 | if ((ret = av_write_trailer(avf2)) < 0) | |
437 | if (!ret_all) | |
438 | ret_all = ret; | |
439 | if (!(avf2->oformat->flags & AVFMT_NOFILE)) { | |
440 | if ((ret = avio_close(avf2->pb)) < 0) | |
441 | if (!ret_all) | |
442 | ret_all = ret; | |
443 | avf2->pb = NULL; | |
444 | } | |
445 | } | |
446 | close_slaves(avf); | |
447 | return ret_all; | |
448 | } | |
449 | ||
450 | static int tee_write_packet(AVFormatContext *avf, AVPacket *pkt) | |
451 | { | |
452 | TeeContext *tee = avf->priv_data; | |
453 | AVFormatContext *avf2; | |
454 | AVPacket pkt2; | |
455 | int ret_all = 0, ret; | |
456 | unsigned i, s; | |
457 | int s2; | |
458 | AVRational tb, tb2; | |
459 | ||
460 | for (i = 0; i < tee->nb_slaves; i++) { | |
461 | avf2 = tee->slaves[i].avf; | |
462 | s = pkt->stream_index; | |
463 | s2 = tee->slaves[i].stream_map[s]; | |
464 | if (s2 < 0) | |
465 | continue; | |
466 | ||
467 | if ((ret = av_copy_packet(&pkt2, pkt)) < 0 || | |
468 | (ret = av_dup_packet(&pkt2))< 0) | |
469 | if (!ret_all) { | |
470 | ret_all = ret; | |
471 | continue; | |
472 | } | |
473 | tb = avf ->streams[s ]->time_base; | |
474 | tb2 = avf2->streams[s2]->time_base; | |
475 | pkt2.pts = av_rescale_q(pkt->pts, tb, tb2); | |
476 | pkt2.dts = av_rescale_q(pkt->dts, tb, tb2); | |
477 | pkt2.duration = av_rescale_q(pkt->duration, tb, tb2); | |
478 | pkt2.stream_index = s2; | |
479 | ||
480 | filter_packet(avf2, &pkt2, avf2, tee->slaves[i].bsfs[s2]); | |
481 | if ((ret = av_interleaved_write_frame(avf2, &pkt2)) < 0) | |
482 | if (!ret_all) | |
483 | ret_all = ret; | |
484 | } | |
485 | return ret_all; | |
486 | } | |
487 | ||
488 | AVOutputFormat ff_tee_muxer = { | |
489 | .name = "tee", | |
490 | .long_name = NULL_IF_CONFIG_SMALL("Multiple muxer tee"), | |
491 | .priv_data_size = sizeof(TeeContext), | |
492 | .write_header = tee_write_header, | |
493 | .write_trailer = tee_write_trailer, | |
494 | .write_packet = tee_write_packet, | |
495 | .priv_class = &tee_muxer_class, | |
496 | .flags = AVFMT_NOFILE, | |
497 | }; |