FFmpeg
Main Page
Related Pages
Modules
Namespaces
Data Structures
Files
Examples
File List
Globals
All
Data Structures
Namespaces
Files
Functions
Variables
Typedefs
Enumerations
Enumerator
Macros
Groups
Pages
libavformat
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
;
33
AVFormatContext
*
slaves
[
MAX_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,
70
AV_DICT_DONT_STRDUP_KEY
|
AV_DICT_DONT_STRDUP_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
;
121
st2->
sample_aspect_ratio
= st->
sample_aspect_ratio
;
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
);
145
ret =
AVERROR_OPTION_NOT_FOUND
;
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
172
static
int
tee_write_header
(
AVFormatContext
*avf)
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
211
static
int
tee_write_trailer
(
AVFormatContext
*avf)
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
234
static
int
tee_write_packet
(
AVFormatContext
*avf,
AVPacket
*
pkt
)
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
269
AVOutputFormat
ff_tee_muxer
= {
270
.
name
=
"tee"
,
271
.long_name =
NULL_IF_CONFIG_SMALL
(
"Multiple muxer tee"
),
272
.priv_data_size =
sizeof
(
TeeContext
),
273
.
write_header
=
tee_write_header
,
274
.
write_trailer
=
tee_write_trailer
,
275
.
write_packet
=
tee_write_packet
,
276
.priv_class = &tee_muxer_class,
277
.
flags
=
AVFMT_NOFILE
,
278
};
Generated on Tue Jun 18 2013 19:21:05 for FFmpeg by
1.8.2