FFmpeg
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
tee.c
Go to the documentation of this file.
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 TeeContext {
31  const AVClass *class;
32  unsigned nb_slaves;
34 } TeeContext;
35 
36 static const char *const slave_delim = "|";
37 static const char *const slave_opt_open = "[";
38 static const char *const slave_opt_close = "]";
39 static const char *const slave_opt_delim = ":]"; /* must have the close too */
40 
41 static const AVClass tee_muxer_class = {
42  .class_name = "Tee muxer",
43  .item_name = av_default_item_name,
44  .version = LIBAVUTIL_VERSION_INT,
45 };
46 
47 static int parse_slave_options(void *log, char *slave,
48  AVDictionary **options, char **filename)
49 {
50  const char *p;
51  char *key, *val;
52  int ret;
53 
54  if (!strspn(slave, slave_opt_open)) {
55  *filename = slave;
56  return 0;
57  }
58  p = slave + 1;
59  if (strspn(p, slave_opt_close)) {
60  *filename = (char *)p + 1;
61  return 0;
62  }
63  while (1) {
64  ret = av_opt_get_key_value(&p, "=", slave_opt_delim, 0, &key, &val);
65  if (ret < 0) {
66  av_log(log, AV_LOG_ERROR, "No option found near \"%s\"\n", p);
67  goto fail;
68  }
69  ret = av_dict_set(options, key, val,
71  if (ret < 0)
72  goto fail;
73  if (strspn(p, slave_opt_close))
74  break;
75  p++;
76  }
77  *filename = (char *)p + 1;
78  return 0;
79 
80 fail:
81  av_dict_free(options);
82  return ret;
83 }
84 
85 static int open_slave(AVFormatContext *avf, char *slave, AVFormatContext **ravf)
86 {
87  int i, ret;
88  AVDictionary *options = NULL;
89  AVDictionaryEntry *entry;
90  char *filename;
91  char *format = NULL;
92  AVFormatContext *avf2 = NULL;
93  AVStream *st, *st2;
94 
95  if ((ret = parse_slave_options(avf, slave, &options, &filename)) < 0)
96  return ret;
97  if ((entry = av_dict_get(options, "f", NULL, 0))) {
98  format = entry->value;
99  entry->value = NULL; /* prevent it from being freed */
100  av_dict_set(&options, "f", NULL, 0);
101  }
102 
103  ret = avformat_alloc_output_context2(&avf2, NULL, format, filename);
104  if (ret < 0)
105  goto fail;
106  av_free(format);
107 
108  for (i = 0; i < avf->nb_streams; i++) {
109  st = avf->streams[i];
110  if (!(st2 = avformat_new_stream(avf2, NULL))) {
111  ret = AVERROR(ENOMEM);
112  goto fail;
113  }
114  st2->id = st->id;
115  st2->r_frame_rate = st->r_frame_rate;
116  st2->time_base = st->time_base;
117  st2->start_time = st->start_time;
118  st2->duration = st->duration;
119  st2->nb_frames = st->nb_frames;
120  st2->disposition = st->disposition;
122  st2->avg_frame_rate = st->avg_frame_rate;
123  av_dict_copy(&st2->metadata, st->metadata, 0);
124  if ((ret = avcodec_copy_context(st2->codec, st->codec)) < 0)
125  goto fail;
126  }
127 
128  if (!(avf2->oformat->flags & AVFMT_NOFILE)) {
129  if ((ret = avio_open(&avf2->pb, filename, AVIO_FLAG_WRITE)) < 0) {
130  av_log(avf, AV_LOG_ERROR, "Slave '%s': error opening: %s\n",
131  slave, av_err2str(ret));
132  goto fail;
133  }
134  }
135 
136  if ((ret = avformat_write_header(avf2, &options)) < 0) {
137  av_log(avf, AV_LOG_ERROR, "Slave '%s': error writing header: %s\n",
138  slave, av_err2str(ret));
139  goto fail;
140  }
141  if (options) {
142  entry = NULL;
143  while ((entry = av_dict_get(options, "", entry, AV_DICT_IGNORE_SUFFIX)))
144  av_log(avf2, AV_LOG_ERROR, "Unknown option '%s'\n", entry->key);
146  goto fail;
147  }
148 
149  *ravf = avf2;
150  return 0;
151 
152 fail:
153  av_dict_free(&options);
154  return ret;
155 }
156 
157 static void close_slaves(AVFormatContext *avf)
158 {
159  TeeContext *tee = avf->priv_data;
160  AVFormatContext *avf2;
161  unsigned i;
162 
163  for (i = 0; i < tee->nb_slaves; i++) {
164  avf2 = tee->slaves[i];
165  avio_close(avf2->pb);
166  avf2->pb = NULL;
167  avformat_free_context(avf2);
168  tee->slaves[i] = NULL;
169  }
170 }
171 
173 {
174  TeeContext *tee = avf->priv_data;
175  unsigned nb_slaves = 0, i;
176  const char *filename = avf->filename;
177  char *slaves[MAX_SLAVES];
178  int ret;
179 
180  while (*filename) {
181  if (nb_slaves == MAX_SLAVES) {
182  av_log(avf, AV_LOG_ERROR, "Maximum %d slave muxers reached.\n",
183  MAX_SLAVES);
184  ret = AVERROR_PATCHWELCOME;
185  goto fail;
186  }
187  if (!(slaves[nb_slaves++] = av_get_token(&filename, slave_delim))) {
188  ret = AVERROR(ENOMEM);
189  goto fail;
190  }
191  if (strspn(filename, slave_delim))
192  filename++;
193  }
194 
195  for (i = 0; i < nb_slaves; i++) {
196  if ((ret = open_slave(avf, slaves[i], &tee->slaves[i])) < 0)
197  goto fail;
198  av_freep(&slaves[i]);
199  }
200 
201  tee->nb_slaves = nb_slaves;
202  return 0;
203 
204 fail:
205  for (i = 0; i < nb_slaves; i++)
206  av_freep(&slaves[i]);
207  close_slaves(avf);
208  return ret;
209 }
210 
212 {
213  TeeContext *tee = avf->priv_data;
214  AVFormatContext *avf2;
215  int ret_all = 0, ret;
216  unsigned i;
217 
218  for (i = 0; i < tee->nb_slaves; i++) {
219  avf2 = tee->slaves[i];
220  if ((ret = av_write_trailer(avf2)) < 0)
221  if (!ret_all)
222  ret_all = ret;
223  if (!(avf2->oformat->flags & AVFMT_NOFILE)) {
224  if ((ret = avio_close(avf2->pb)) < 0)
225  if (!ret_all)
226  ret_all = ret;
227  avf2->pb = NULL;
228  }
229  }
230  close_slaves(avf);
231  return ret_all;
232 }
233 
235 {
236  TeeContext *tee = avf->priv_data;
237  AVFormatContext *avf2;
238  AVPacket pkt2;
239  int ret_all = 0, ret;
240  unsigned i, s;
241  AVRational tb, tb2;
242 
243  for (i = 0; i < tee->nb_slaves; i++) {
244  avf2 = tee->slaves[i];
245  s = pkt->stream_index;
246  if (s >= avf2->nb_streams) {
247  if (!ret_all)
248  ret_all = AVERROR(EINVAL);
249  continue;
250  }
251  if ((ret = av_copy_packet(&pkt2, pkt)) < 0 ||
252  (ret = av_dup_packet(&pkt2))< 0)
253  if (!ret_all) {
254  ret = ret_all;
255  continue;
256  }
257  tb = avf ->streams[s]->time_base;
258  tb2 = avf2->streams[s]->time_base;
259  pkt2.pts = av_rescale_q(pkt->pts, tb, tb2);
260  pkt2.dts = av_rescale_q(pkt->dts, tb, tb2);
261  pkt2.duration = av_rescale_q(pkt->duration, tb, tb2);
262  if ((ret = av_interleaved_write_frame(avf2, &pkt2)) < 0)
263  if (!ret_all)
264  ret_all = ret;
265  }
266  return ret_all;
267 }
268 
270  .name = "tee",
271  .long_name = NULL_IF_CONFIG_SMALL("Multiple muxer tee"),
272  .priv_data_size = sizeof(TeeContext),
276  .priv_class = &tee_muxer_class,
277  .flags = AVFMT_NOFILE,
278 };