ffmpeg / libavformat / rtp.c @ 5509bffa
History | View | Annotate | Download (30.2 KB)
1 |
/*
|
---|---|
2 |
* RTP input/output format
|
3 |
* Copyright (c) 2002 Fabrice Bellard.
|
4 |
*
|
5 |
* This library 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 of the License, or (at your option) any later version.
|
9 |
*
|
10 |
* This library 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 this library; if not, write to the Free Software
|
17 |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
18 |
*/
|
19 |
#include "avformat.h" |
20 |
#include "mpegts.h" |
21 |
#include "bitstream.h" |
22 |
|
23 |
#include <unistd.h> |
24 |
#include <sys/types.h> |
25 |
#include <sys/socket.h> |
26 |
#include <netinet/in.h> |
27 |
#ifndef __BEOS__
|
28 |
# include <arpa/inet.h> |
29 |
#else
|
30 |
# include "barpainet.h" |
31 |
#endif
|
32 |
#include <netdb.h> |
33 |
|
34 |
//#define DEBUG
|
35 |
|
36 |
|
37 |
/* TODO: - add RTCP statistics reporting (should be optional).
|
38 |
|
39 |
- add support for h263/mpeg4 packetized output : IDEA: send a
|
40 |
buffer to 'rtp_write_packet' contains all the packets for ONE
|
41 |
frame. Each packet should have a four byte header containing
|
42 |
the length in big endian format (same trick as
|
43 |
'url_open_dyn_packet_buf')
|
44 |
*/
|
45 |
|
46 |
/* from http://www.iana.org/assignments/rtp-parameters last updated 05 January 2005 */
|
47 |
AVRtpPayloadType_t AVRtpPayloadTypes[]= |
48 |
{ |
49 |
{0, "PCMU", CODEC_TYPE_AUDIO, CODEC_ID_PCM_MULAW, 8000, 1}, |
50 |
{1, "Reserved", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
51 |
{2, "Reserved", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
52 |
{3, "GSM", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1}, |
53 |
{4, "G723", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1}, |
54 |
{5, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1}, |
55 |
{6, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 16000, 1}, |
56 |
{7, "LPC", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1}, |
57 |
{8, "PCMA", CODEC_TYPE_AUDIO, CODEC_ID_PCM_ALAW, 8000, 1}, |
58 |
{9, "G722", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1}, |
59 |
{10, "L16", CODEC_TYPE_AUDIO, CODEC_ID_PCM_S16BE, 44100, 2}, |
60 |
{11, "L16", CODEC_TYPE_AUDIO, CODEC_ID_PCM_S16BE, 44100, 1}, |
61 |
{12, "QCELP", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1}, |
62 |
{13, "CN", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1}, |
63 |
{14, "MPA", CODEC_TYPE_AUDIO, CODEC_ID_MP2, 90000, -1}, |
64 |
{15, "G728", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1}, |
65 |
{16, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 11025, 1}, |
66 |
{17, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 22050, 1}, |
67 |
{18, "G729", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1}, |
68 |
{19, "reserved", CODEC_TYPE_AUDIO, CODEC_ID_NONE, -1, -1}, |
69 |
{20, "unassigned", CODEC_TYPE_AUDIO, CODEC_ID_NONE, -1, -1}, |
70 |
{21, "unassigned", CODEC_TYPE_AUDIO, CODEC_ID_NONE, -1, -1}, |
71 |
{22, "unassigned", CODEC_TYPE_AUDIO, CODEC_ID_NONE, -1, -1}, |
72 |
{23, "unassigned", CODEC_TYPE_AUDIO, CODEC_ID_NONE, -1, -1}, |
73 |
{24, "unassigned", CODEC_TYPE_VIDEO, CODEC_ID_NONE, -1, -1}, |
74 |
{25, "CelB", CODEC_TYPE_VIDEO, CODEC_ID_NONE, 90000, -1}, |
75 |
{26, "JPEG", CODEC_TYPE_VIDEO, CODEC_ID_MJPEG, 90000, -1}, |
76 |
{27, "unassigned", CODEC_TYPE_VIDEO, CODEC_ID_NONE, -1, -1}, |
77 |
{28, "nv", CODEC_TYPE_VIDEO, CODEC_ID_NONE, 90000, -1}, |
78 |
{29, "unassigned", CODEC_TYPE_VIDEO, CODEC_ID_NONE, -1, -1}, |
79 |
{30, "unassigned", CODEC_TYPE_VIDEO, CODEC_ID_NONE, -1, -1}, |
80 |
{31, "H261", CODEC_TYPE_VIDEO, CODEC_ID_H261, 90000, -1}, |
81 |
{32, "MPV", CODEC_TYPE_VIDEO, CODEC_ID_MPEG1VIDEO, 90000, -1}, |
82 |
{33, "MP2T", CODEC_TYPE_DATA, CODEC_ID_MPEG2TS, 90000, -1}, |
83 |
{34, "H263", CODEC_TYPE_VIDEO, CODEC_ID_H263, 90000, -1}, |
84 |
{35, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
85 |
{36, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
86 |
{37, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
87 |
{38, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
88 |
{39, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
89 |
{40, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
90 |
{41, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
91 |
{42, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
92 |
{43, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
93 |
{44, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
94 |
{45, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
95 |
{46, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
96 |
{47, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
97 |
{48, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
98 |
{49, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
99 |
{50, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
100 |
{51, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
101 |
{52, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
102 |
{53, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
103 |
{54, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
104 |
{55, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
105 |
{56, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
106 |
{57, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
107 |
{58, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
108 |
{59, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
109 |
{60, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
110 |
{61, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
111 |
{62, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
112 |
{63, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
113 |
{64, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
114 |
{65, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
115 |
{66, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
116 |
{67, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
117 |
{68, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
118 |
{69, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
119 |
{70, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
120 |
{71, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
121 |
{72, "reserved for RTCP conflict avoidance", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
122 |
{73, "reserved for RTCP conflict avoidance", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
123 |
{74, "reserved for RTCP conflict avoidance", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
124 |
{75, "reserved for RTCP conflict avoidance", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
125 |
{76, "reserved for RTCP conflict avoidance", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
126 |
{77, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
127 |
{78, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
128 |
{79, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
129 |
{80, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
130 |
{81, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
131 |
{82, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
132 |
{83, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
133 |
{84, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
134 |
{85, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
135 |
{86, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
136 |
{87, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
137 |
{88, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
138 |
{89, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
139 |
{90, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
140 |
{91, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
141 |
{92, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
142 |
{93, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
143 |
{94, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
144 |
{95, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
145 |
{96, "dynamic", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
146 |
{97, "dynamic", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
147 |
{98, "dynamic", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
148 |
{99, "dynamic", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
149 |
{100, "dynamic", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
150 |
{101, "dynamic", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
151 |
{102, "dynamic", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
152 |
{103, "dynamic", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
153 |
{104, "dynamic", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
154 |
{105, "dynamic", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
155 |
{106, "dynamic", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
156 |
{107, "dynamic", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
157 |
{108, "dynamic", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
158 |
{109, "dynamic", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
159 |
{110, "dynamic", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
160 |
{111, "dynamic", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
161 |
{112, "dynamic", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
162 |
{113, "dynamic", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
163 |
{114, "dynamic", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
164 |
{115, "dynamic", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
165 |
{116, "dynamic", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
166 |
{117, "dynamic", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
167 |
{118, "dynamic", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
168 |
{119, "dynamic", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
169 |
{120, "dynamic", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
170 |
{121, "dynamic", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
171 |
{122, "dynamic", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
172 |
{123, "dynamic", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
173 |
{124, "dynamic", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
174 |
{125, "dynamic", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
175 |
{126, "dynamic", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
176 |
{127, "dynamic", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}, |
177 |
{-1, "", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1} |
178 |
}; |
179 |
|
180 |
AVRtpDynamicPayloadType_t AVRtpDynamicPayloadTypes[]= |
181 |
{ |
182 |
{"MP4V-ES", CODEC_TYPE_VIDEO, CODEC_ID_MPEG4},
|
183 |
{"mpeg4-generic", CODEC_TYPE_AUDIO, CODEC_ID_MPEG4AAC},
|
184 |
{"", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE}
|
185 |
}; |
186 |
|
187 |
struct RTPDemuxContext {
|
188 |
AVFormatContext *ic; |
189 |
AVStream *st; |
190 |
int payload_type;
|
191 |
uint32_t ssrc; |
192 |
uint16_t seq; |
193 |
uint32_t timestamp; |
194 |
uint32_t base_timestamp; |
195 |
uint32_t cur_timestamp; |
196 |
int max_payload_size;
|
197 |
MpegTSContext *ts; /* only used for MP2T payloads */
|
198 |
int read_buf_index;
|
199 |
int read_buf_size;
|
200 |
|
201 |
/* rtcp sender statistics receive */
|
202 |
int64_t last_rtcp_ntp_time; |
203 |
int64_t first_rtcp_ntp_time; |
204 |
uint32_t last_rtcp_timestamp; |
205 |
/* rtcp sender statistics */
|
206 |
unsigned int packet_count; |
207 |
unsigned int octet_count; |
208 |
unsigned int last_octet_count; |
209 |
int first_packet;
|
210 |
/* buffer for output */
|
211 |
uint8_t buf[RTP_MAX_PACKET_LENGTH]; |
212 |
uint8_t *buf_ptr; |
213 |
/* special infos for au headers parsing */
|
214 |
rtp_payload_data_t *rtp_payload_data; |
215 |
}; |
216 |
|
217 |
int rtp_get_codec_info(AVCodecContext *codec, int payload_type) |
218 |
{ |
219 |
if (AVRtpPayloadTypes[payload_type].codec_id != CODEC_ID_NONE) {
|
220 |
codec->codec_type = AVRtpPayloadTypes[payload_type].codec_type; |
221 |
codec->codec_id = AVRtpPayloadTypes[payload_type].codec_type; |
222 |
if (AVRtpPayloadTypes[payload_type].audio_channels > 0) |
223 |
codec->channels = AVRtpPayloadTypes[payload_type].audio_channels; |
224 |
if (AVRtpPayloadTypes[payload_type].clock_rate > 0) |
225 |
codec->sample_rate = AVRtpPayloadTypes[payload_type].clock_rate; |
226 |
return 0; |
227 |
} |
228 |
return -1; |
229 |
} |
230 |
|
231 |
/* return < 0 if unknown payload type */
|
232 |
int rtp_get_payload_type(AVCodecContext *codec)
|
233 |
{ |
234 |
int i, payload_type;
|
235 |
|
236 |
/* compute the payload type */
|
237 |
for (payload_type = -1, i = 0; AVRtpPayloadTypes[i].pt >= 0; ++i) |
238 |
if (AVRtpPayloadTypes[i].codec_id == codec->codec_id) {
|
239 |
if (codec->codec_id == CODEC_ID_PCM_S16BE)
|
240 |
if (codec->channels != AVRtpPayloadTypes[i].audio_channels)
|
241 |
continue;
|
242 |
payload_type = AVRtpPayloadTypes[i].pt; |
243 |
} |
244 |
return payload_type;
|
245 |
} |
246 |
|
247 |
static inline uint32_t decode_be32(const uint8_t *p) |
248 |
{ |
249 |
return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; |
250 |
} |
251 |
|
252 |
static inline uint64_t decode_be64(const uint8_t *p) |
253 |
{ |
254 |
return ((uint64_t)decode_be32(p) << 32) | decode_be32(p + 4); |
255 |
} |
256 |
|
257 |
static int rtcp_parse_packet(RTPDemuxContext *s, const unsigned char *buf, int len) |
258 |
{ |
259 |
if (buf[1] != 200) |
260 |
return -1; |
261 |
s->last_rtcp_ntp_time = decode_be64(buf + 8);
|
262 |
if (s->first_rtcp_ntp_time == AV_NOPTS_VALUE)
|
263 |
s->first_rtcp_ntp_time = s->last_rtcp_ntp_time; |
264 |
s->last_rtcp_timestamp = decode_be32(buf + 16);
|
265 |
return 0; |
266 |
} |
267 |
|
268 |
/**
|
269 |
* open a new RTP parse context for stream 'st'. 'st' can be NULL for
|
270 |
* MPEG2TS streams to indicate that they should be demuxed inside the
|
271 |
* rtp demux (otherwise CODEC_ID_MPEG2TS packets are returned)
|
272 |
*/
|
273 |
RTPDemuxContext *rtp_parse_open(AVFormatContext *s1, AVStream *st, int payload_type, rtp_payload_data_t *rtp_payload_data)
|
274 |
{ |
275 |
RTPDemuxContext *s; |
276 |
|
277 |
s = av_mallocz(sizeof(RTPDemuxContext));
|
278 |
if (!s)
|
279 |
return NULL; |
280 |
s->payload_type = payload_type; |
281 |
s->last_rtcp_ntp_time = AV_NOPTS_VALUE; |
282 |
s->first_rtcp_ntp_time = AV_NOPTS_VALUE; |
283 |
s->ic = s1; |
284 |
s->st = st; |
285 |
s->rtp_payload_data = rtp_payload_data; |
286 |
if (!strcmp(AVRtpPayloadTypes[payload_type].enc_name, "MP2T")) { |
287 |
s->ts = mpegts_parse_open(s->ic); |
288 |
if (s->ts == NULL) { |
289 |
av_free(s); |
290 |
return NULL; |
291 |
} |
292 |
} else {
|
293 |
switch(st->codec->codec_id) {
|
294 |
case CODEC_ID_MPEG1VIDEO:
|
295 |
case CODEC_ID_MPEG2VIDEO:
|
296 |
case CODEC_ID_MP2:
|
297 |
case CODEC_ID_MP3:
|
298 |
case CODEC_ID_MPEG4:
|
299 |
st->need_parsing = 1;
|
300 |
break;
|
301 |
default:
|
302 |
break;
|
303 |
} |
304 |
} |
305 |
return s;
|
306 |
} |
307 |
|
308 |
static int rtp_parse_mp4_au(RTPDemuxContext *s, const uint8_t *buf) |
309 |
{ |
310 |
int au_headers_length, au_header_size, i;
|
311 |
GetBitContext getbitcontext; |
312 |
rtp_payload_data_t *infos; |
313 |
|
314 |
infos = s->rtp_payload_data; |
315 |
|
316 |
if (infos == NULL) |
317 |
return -1; |
318 |
|
319 |
/* decode the first 2 bytes where are stored the AUHeader sections
|
320 |
length in bits */
|
321 |
au_headers_length = BE_16(buf); |
322 |
|
323 |
if (au_headers_length > RTP_MAX_PACKET_LENGTH)
|
324 |
return -1; |
325 |
|
326 |
infos->au_headers_length_bytes = (au_headers_length + 7) / 8; |
327 |
|
328 |
/* skip AU headers length section (2 bytes) */
|
329 |
buf += 2;
|
330 |
|
331 |
init_get_bits(&getbitcontext, buf, infos->au_headers_length_bytes * 8);
|
332 |
|
333 |
/* XXX: Wrong if optionnal additional sections are present (cts, dts etc...) */
|
334 |
au_header_size = infos->sizelength + infos->indexlength; |
335 |
if (au_header_size <= 0 || (au_headers_length % au_header_size != 0)) |
336 |
return -1; |
337 |
|
338 |
infos->nb_au_headers = au_headers_length / au_header_size; |
339 |
infos->au_headers = av_malloc(sizeof(struct AUHeaders) * infos->nb_au_headers); |
340 |
|
341 |
/* XXX: We handle multiple AU Section as only one (need to fix this for interleaving)
|
342 |
In my test, the faad decoder doesnt behave correctly when sending each AU one by one
|
343 |
but does when sending the whole as one big packet... */
|
344 |
infos->au_headers[0].size = 0; |
345 |
infos->au_headers[0].index = 0; |
346 |
for (i = 0; i < infos->nb_au_headers; ++i) { |
347 |
infos->au_headers[0].size += get_bits_long(&getbitcontext, infos->sizelength);
|
348 |
infos->au_headers[0].index = get_bits_long(&getbitcontext, infos->indexlength);
|
349 |
} |
350 |
|
351 |
infos->nb_au_headers = 1;
|
352 |
|
353 |
return 0; |
354 |
} |
355 |
|
356 |
/**
|
357 |
* Parse an RTP or RTCP packet directly sent as a buffer.
|
358 |
* @param s RTP parse context.
|
359 |
* @param pkt returned packet
|
360 |
* @param buf input buffer or NULL to read the next packets
|
361 |
* @param len buffer len
|
362 |
* @return 0 if a packet is returned, 1 if a packet is returned and more can follow
|
363 |
* (use buf as NULL to read the next). -1 if no packet (error or no more packet).
|
364 |
*/
|
365 |
int rtp_parse_packet(RTPDemuxContext *s, AVPacket *pkt,
|
366 |
const uint8_t *buf, int len) |
367 |
{ |
368 |
unsigned int ssrc, h; |
369 |
int payload_type, seq, delta_timestamp, ret;
|
370 |
AVStream *st; |
371 |
uint32_t timestamp; |
372 |
|
373 |
if (!buf) {
|
374 |
/* return the next packets, if any */
|
375 |
if (s->read_buf_index >= s->read_buf_size)
|
376 |
return -1; |
377 |
ret = mpegts_parse_packet(s->ts, pkt, s->buf + s->read_buf_index, |
378 |
s->read_buf_size - s->read_buf_index); |
379 |
if (ret < 0) |
380 |
return -1; |
381 |
s->read_buf_index += ret; |
382 |
if (s->read_buf_index < s->read_buf_size)
|
383 |
return 1; |
384 |
else
|
385 |
return 0; |
386 |
} |
387 |
|
388 |
if (len < 12) |
389 |
return -1; |
390 |
|
391 |
if ((buf[0] & 0xc0) != (RTP_VERSION << 6)) |
392 |
return -1; |
393 |
if (buf[1] >= 200 && buf[1] <= 204) { |
394 |
rtcp_parse_packet(s, buf, len); |
395 |
return -1; |
396 |
} |
397 |
payload_type = buf[1] & 0x7f; |
398 |
seq = (buf[2] << 8) | buf[3]; |
399 |
timestamp = decode_be32(buf + 4);
|
400 |
ssrc = decode_be32(buf + 8);
|
401 |
|
402 |
/* NOTE: we can handle only one payload type */
|
403 |
if (s->payload_type != payload_type)
|
404 |
return -1; |
405 |
#if defined(DEBUG) || 1 |
406 |
if (seq != ((s->seq + 1) & 0xffff)) { |
407 |
av_log(s->st->codec, AV_LOG_ERROR, "RTP: PT=%02x: bad cseq %04x expected=%04x\n",
|
408 |
payload_type, seq, ((s->seq + 1) & 0xffff)); |
409 |
} |
410 |
#endif
|
411 |
s->seq = seq; |
412 |
len -= 12;
|
413 |
buf += 12;
|
414 |
|
415 |
st = s->st; |
416 |
if (!st) {
|
417 |
/* specific MPEG2TS demux support */
|
418 |
ret = mpegts_parse_packet(s->ts, pkt, buf, len); |
419 |
if (ret < 0) |
420 |
return -1; |
421 |
if (ret < len) {
|
422 |
s->read_buf_size = len - ret; |
423 |
memcpy(s->buf, buf + ret, s->read_buf_size); |
424 |
s->read_buf_index = 0;
|
425 |
return 1; |
426 |
} |
427 |
} else {
|
428 |
switch(st->codec->codec_id) {
|
429 |
case CODEC_ID_MP2:
|
430 |
/* better than nothing: skip mpeg audio RTP header */
|
431 |
if (len <= 4) |
432 |
return -1; |
433 |
h = decode_be32(buf); |
434 |
len -= 4;
|
435 |
buf += 4;
|
436 |
av_new_packet(pkt, len); |
437 |
memcpy(pkt->data, buf, len); |
438 |
break;
|
439 |
case CODEC_ID_MPEG1VIDEO:
|
440 |
/* better than nothing: skip mpeg video RTP header */
|
441 |
if (len <= 4) |
442 |
return -1; |
443 |
h = decode_be32(buf); |
444 |
buf += 4;
|
445 |
len -= 4;
|
446 |
if (h & (1 << 26)) { |
447 |
/* mpeg2 */
|
448 |
if (len <= 4) |
449 |
return -1; |
450 |
buf += 4;
|
451 |
len -= 4;
|
452 |
} |
453 |
av_new_packet(pkt, len); |
454 |
memcpy(pkt->data, buf, len); |
455 |
break;
|
456 |
default:
|
457 |
av_new_packet(pkt, len); |
458 |
memcpy(pkt->data, buf, len); |
459 |
break;
|
460 |
} |
461 |
|
462 |
switch(st->codec->codec_id) {
|
463 |
case CODEC_ID_MP2:
|
464 |
case CODEC_ID_MPEG1VIDEO:
|
465 |
if (s->last_rtcp_ntp_time != AV_NOPTS_VALUE) {
|
466 |
int64_t addend; |
467 |
/* XXX: is it really necessary to unify the timestamp base ? */
|
468 |
/* compute pts from timestamp with received ntp_time */
|
469 |
delta_timestamp = timestamp - s->last_rtcp_timestamp; |
470 |
/* convert to 90 kHz without overflow */
|
471 |
addend = (s->last_rtcp_ntp_time - s->first_rtcp_ntp_time) >> 14;
|
472 |
addend = (addend * 5625) >> 14; |
473 |
pkt->pts = addend + delta_timestamp; |
474 |
} |
475 |
break;
|
476 |
case CODEC_ID_MPEG4:
|
477 |
pkt->pts = timestamp; |
478 |
break;
|
479 |
case CODEC_ID_MPEG4AAC:
|
480 |
if (rtp_parse_mp4_au(s, buf))
|
481 |
return -1; |
482 |
{ |
483 |
rtp_payload_data_t *infos = s->rtp_payload_data; |
484 |
if (infos == NULL) |
485 |
return -1; |
486 |
buf += infos->au_headers_length_bytes + 2;
|
487 |
len -= infos->au_headers_length_bytes + 2;
|
488 |
|
489 |
/* XXX: Fixme we only handle the case where rtp_parse_mp4_au define
|
490 |
one au_header */
|
491 |
av_new_packet(pkt, infos->au_headers[0].size);
|
492 |
memcpy(pkt->data, buf, infos->au_headers[0].size);
|
493 |
buf += infos->au_headers[0].size;
|
494 |
len -= infos->au_headers[0].size;
|
495 |
} |
496 |
s->read_buf_size = len; |
497 |
s->buf_ptr = (char *)buf;
|
498 |
pkt->stream_index = s->st->index; |
499 |
return 0; |
500 |
default:
|
501 |
/* no timestamp info yet */
|
502 |
break;
|
503 |
} |
504 |
pkt->stream_index = s->st->index; |
505 |
} |
506 |
return 0; |
507 |
} |
508 |
|
509 |
void rtp_parse_close(RTPDemuxContext *s)
|
510 |
{ |
511 |
if (!strcmp(AVRtpPayloadTypes[s->payload_type].enc_name, "MP2T")) { |
512 |
mpegts_parse_close(s->ts); |
513 |
} |
514 |
av_free(s); |
515 |
} |
516 |
|
517 |
/* rtp output */
|
518 |
|
519 |
static int rtp_write_header(AVFormatContext *s1) |
520 |
{ |
521 |
RTPDemuxContext *s = s1->priv_data; |
522 |
int payload_type, max_packet_size, n;
|
523 |
AVStream *st; |
524 |
|
525 |
if (s1->nb_streams != 1) |
526 |
return -1; |
527 |
st = s1->streams[0];
|
528 |
|
529 |
payload_type = rtp_get_payload_type(st->codec); |
530 |
if (payload_type < 0) |
531 |
payload_type = RTP_PT_PRIVATE; /* private payload type */
|
532 |
s->payload_type = payload_type; |
533 |
|
534 |
s->base_timestamp = random(); |
535 |
s->timestamp = s->base_timestamp; |
536 |
s->ssrc = random(); |
537 |
s->first_packet = 1;
|
538 |
|
539 |
max_packet_size = url_fget_max_packet_size(&s1->pb); |
540 |
if (max_packet_size <= 12) |
541 |
return AVERROR_IO;
|
542 |
s->max_payload_size = max_packet_size - 12;
|
543 |
|
544 |
switch(st->codec->codec_id) {
|
545 |
case CODEC_ID_MP2:
|
546 |
case CODEC_ID_MP3:
|
547 |
s->buf_ptr = s->buf + 4;
|
548 |
s->cur_timestamp = 0;
|
549 |
break;
|
550 |
case CODEC_ID_MPEG1VIDEO:
|
551 |
s->cur_timestamp = 0;
|
552 |
break;
|
553 |
case CODEC_ID_MPEG2TS:
|
554 |
n = s->max_payload_size / TS_PACKET_SIZE; |
555 |
if (n < 1) |
556 |
n = 1;
|
557 |
s->max_payload_size = n * TS_PACKET_SIZE; |
558 |
s->buf_ptr = s->buf; |
559 |
break;
|
560 |
default:
|
561 |
s->buf_ptr = s->buf; |
562 |
break;
|
563 |
} |
564 |
|
565 |
return 0; |
566 |
} |
567 |
|
568 |
/* send an rtcp sender report packet */
|
569 |
static void rtcp_send_sr(AVFormatContext *s1, int64_t ntp_time) |
570 |
{ |
571 |
RTPDemuxContext *s = s1->priv_data; |
572 |
#if defined(DEBUG)
|
573 |
printf("RTCP: %02x %Lx %x\n", s->payload_type, ntp_time, s->timestamp);
|
574 |
#endif
|
575 |
put_byte(&s1->pb, (RTP_VERSION << 6));
|
576 |
put_byte(&s1->pb, 200);
|
577 |
put_be16(&s1->pb, 6); /* length in words - 1 */ |
578 |
put_be32(&s1->pb, s->ssrc); |
579 |
put_be64(&s1->pb, ntp_time); |
580 |
put_be32(&s1->pb, s->timestamp); |
581 |
put_be32(&s1->pb, s->packet_count); |
582 |
put_be32(&s1->pb, s->octet_count); |
583 |
put_flush_packet(&s1->pb); |
584 |
} |
585 |
|
586 |
/* send an rtp packet. sequence number is incremented, but the caller
|
587 |
must update the timestamp itself */
|
588 |
static void rtp_send_data(AVFormatContext *s1, const uint8_t *buf1, int len, int m) |
589 |
{ |
590 |
RTPDemuxContext *s = s1->priv_data; |
591 |
|
592 |
#ifdef DEBUG
|
593 |
printf("rtp_send_data size=%d\n", len);
|
594 |
#endif
|
595 |
|
596 |
/* build the RTP header */
|
597 |
put_byte(&s1->pb, (RTP_VERSION << 6));
|
598 |
put_byte(&s1->pb, (s->payload_type & 0x7f) | ((m & 0x01) << 7)); |
599 |
put_be16(&s1->pb, s->seq); |
600 |
put_be32(&s1->pb, s->timestamp); |
601 |
put_be32(&s1->pb, s->ssrc); |
602 |
|
603 |
put_buffer(&s1->pb, buf1, len); |
604 |
put_flush_packet(&s1->pb); |
605 |
|
606 |
s->seq++; |
607 |
s->octet_count += len; |
608 |
s->packet_count++; |
609 |
} |
610 |
|
611 |
/* send an integer number of samples and compute time stamp and fill
|
612 |
the rtp send buffer before sending. */
|
613 |
static void rtp_send_samples(AVFormatContext *s1, |
614 |
const uint8_t *buf1, int size, int sample_size) |
615 |
{ |
616 |
RTPDemuxContext *s = s1->priv_data; |
617 |
int len, max_packet_size, n;
|
618 |
|
619 |
max_packet_size = (s->max_payload_size / sample_size) * sample_size; |
620 |
/* not needed, but who nows */
|
621 |
if ((size % sample_size) != 0) |
622 |
av_abort(); |
623 |
while (size > 0) { |
624 |
len = (max_packet_size - (s->buf_ptr - s->buf)); |
625 |
if (len > size)
|
626 |
len = size; |
627 |
|
628 |
/* copy data */
|
629 |
memcpy(s->buf_ptr, buf1, len); |
630 |
s->buf_ptr += len; |
631 |
buf1 += len; |
632 |
size -= len; |
633 |
n = (s->buf_ptr - s->buf); |
634 |
/* if buffer full, then send it */
|
635 |
if (n >= max_packet_size) {
|
636 |
rtp_send_data(s1, s->buf, n, 0);
|
637 |
s->buf_ptr = s->buf; |
638 |
/* update timestamp */
|
639 |
s->timestamp += n / sample_size; |
640 |
} |
641 |
} |
642 |
} |
643 |
|
644 |
/* NOTE: we suppose that exactly one frame is given as argument here */
|
645 |
/* XXX: test it */
|
646 |
static void rtp_send_mpegaudio(AVFormatContext *s1, |
647 |
const uint8_t *buf1, int size) |
648 |
{ |
649 |
RTPDemuxContext *s = s1->priv_data; |
650 |
AVStream *st = s1->streams[0];
|
651 |
int len, count, max_packet_size;
|
652 |
|
653 |
max_packet_size = s->max_payload_size; |
654 |
|
655 |
/* test if we must flush because not enough space */
|
656 |
len = (s->buf_ptr - s->buf); |
657 |
if ((len + size) > max_packet_size) {
|
658 |
if (len > 4) { |
659 |
rtp_send_data(s1, s->buf, s->buf_ptr - s->buf, 0);
|
660 |
s->buf_ptr = s->buf + 4;
|
661 |
/* 90 KHz time stamp */
|
662 |
s->timestamp = s->base_timestamp + |
663 |
(s->cur_timestamp * 90000LL) / st->codec->sample_rate;
|
664 |
} |
665 |
} |
666 |
|
667 |
/* add the packet */
|
668 |
if (size > max_packet_size) {
|
669 |
/* big packet: fragment */
|
670 |
count = 0;
|
671 |
while (size > 0) { |
672 |
len = max_packet_size - 4;
|
673 |
if (len > size)
|
674 |
len = size; |
675 |
/* build fragmented packet */
|
676 |
s->buf[0] = 0; |
677 |
s->buf[1] = 0; |
678 |
s->buf[2] = count >> 8; |
679 |
s->buf[3] = count;
|
680 |
memcpy(s->buf + 4, buf1, len);
|
681 |
rtp_send_data(s1, s->buf, len + 4, 0); |
682 |
size -= len; |
683 |
buf1 += len; |
684 |
count += len; |
685 |
} |
686 |
} else {
|
687 |
if (s->buf_ptr == s->buf + 4) { |
688 |
/* no fragmentation possible */
|
689 |
s->buf[0] = 0; |
690 |
s->buf[1] = 0; |
691 |
s->buf[2] = 0; |
692 |
s->buf[3] = 0; |
693 |
} |
694 |
memcpy(s->buf_ptr, buf1, size); |
695 |
s->buf_ptr += size; |
696 |
} |
697 |
s->cur_timestamp += st->codec->frame_size; |
698 |
} |
699 |
|
700 |
/* NOTE: a single frame must be passed with sequence header if
|
701 |
needed. XXX: use slices. */
|
702 |
static void rtp_send_mpegvideo(AVFormatContext *s1, |
703 |
const uint8_t *buf1, int size) |
704 |
{ |
705 |
RTPDemuxContext *s = s1->priv_data; |
706 |
AVStream *st = s1->streams[0];
|
707 |
int len, h, max_packet_size;
|
708 |
uint8_t *q; |
709 |
|
710 |
max_packet_size = s->max_payload_size; |
711 |
|
712 |
while (size > 0) { |
713 |
/* XXX: more correct headers */
|
714 |
h = 0;
|
715 |
if (st->codec->sub_id == 2) |
716 |
h |= 1 << 26; /* mpeg 2 indicator */ |
717 |
q = s->buf; |
718 |
*q++ = h >> 24;
|
719 |
*q++ = h >> 16;
|
720 |
*q++ = h >> 8;
|
721 |
*q++ = h; |
722 |
|
723 |
if (st->codec->sub_id == 2) { |
724 |
h = 0;
|
725 |
*q++ = h >> 24;
|
726 |
*q++ = h >> 16;
|
727 |
*q++ = h >> 8;
|
728 |
*q++ = h; |
729 |
} |
730 |
|
731 |
len = max_packet_size - (q - s->buf); |
732 |
if (len > size)
|
733 |
len = size; |
734 |
|
735 |
memcpy(q, buf1, len); |
736 |
q += len; |
737 |
|
738 |
/* 90 KHz time stamp */
|
739 |
s->timestamp = s->base_timestamp + |
740 |
av_rescale((int64_t)s->cur_timestamp * st->codec->time_base.num, 90000, st->codec->time_base.den); //FIXME pass timestamps |
741 |
rtp_send_data(s1, s->buf, q - s->buf, (len == size)); |
742 |
|
743 |
buf1 += len; |
744 |
size -= len; |
745 |
} |
746 |
s->cur_timestamp++; |
747 |
} |
748 |
|
749 |
static void rtp_send_raw(AVFormatContext *s1, |
750 |
const uint8_t *buf1, int size) |
751 |
{ |
752 |
RTPDemuxContext *s = s1->priv_data; |
753 |
AVStream *st = s1->streams[0];
|
754 |
int len, max_packet_size;
|
755 |
|
756 |
max_packet_size = s->max_payload_size; |
757 |
|
758 |
while (size > 0) { |
759 |
len = max_packet_size; |
760 |
if (len > size)
|
761 |
len = size; |
762 |
|
763 |
/* 90 KHz time stamp */
|
764 |
s->timestamp = s->base_timestamp + |
765 |
av_rescale((int64_t)s->cur_timestamp * st->codec->time_base.num, 90000, st->codec->time_base.den); //FIXME pass timestamps |
766 |
rtp_send_data(s1, buf1, len, (len == size)); |
767 |
|
768 |
buf1 += len; |
769 |
size -= len; |
770 |
} |
771 |
s->cur_timestamp++; |
772 |
} |
773 |
|
774 |
/* NOTE: size is assumed to be an integer multiple of TS_PACKET_SIZE */
|
775 |
static void rtp_send_mpegts_raw(AVFormatContext *s1, |
776 |
const uint8_t *buf1, int size) |
777 |
{ |
778 |
RTPDemuxContext *s = s1->priv_data; |
779 |
int len, out_len;
|
780 |
|
781 |
while (size >= TS_PACKET_SIZE) {
|
782 |
len = s->max_payload_size - (s->buf_ptr - s->buf); |
783 |
if (len > size)
|
784 |
len = size; |
785 |
memcpy(s->buf_ptr, buf1, len); |
786 |
buf1 += len; |
787 |
size -= len; |
788 |
s->buf_ptr += len; |
789 |
|
790 |
out_len = s->buf_ptr - s->buf; |
791 |
if (out_len >= s->max_payload_size) {
|
792 |
rtp_send_data(s1, s->buf, out_len, 0);
|
793 |
s->buf_ptr = s->buf; |
794 |
} |
795 |
} |
796 |
} |
797 |
|
798 |
/* write an RTP packet. 'buf1' must contain a single specific frame. */
|
799 |
static int rtp_write_packet(AVFormatContext *s1, AVPacket *pkt) |
800 |
{ |
801 |
RTPDemuxContext *s = s1->priv_data; |
802 |
AVStream *st = s1->streams[0];
|
803 |
int rtcp_bytes;
|
804 |
int64_t ntp_time; |
805 |
int size= pkt->size;
|
806 |
uint8_t *buf1= pkt->data; |
807 |
|
808 |
#ifdef DEBUG
|
809 |
printf("%d: write len=%d\n", pkt->stream_index, size);
|
810 |
#endif
|
811 |
|
812 |
/* XXX: mpeg pts hardcoded. RTCP send every 0.5 seconds */
|
813 |
rtcp_bytes = ((s->octet_count - s->last_octet_count) * RTCP_TX_RATIO_NUM) / |
814 |
RTCP_TX_RATIO_DEN; |
815 |
if (s->first_packet || rtcp_bytes >= 28) { |
816 |
/* compute NTP time */
|
817 |
/* XXX: 90 kHz timestamp hardcoded */
|
818 |
ntp_time = (pkt->pts << 28) / 5625; |
819 |
rtcp_send_sr(s1, ntp_time); |
820 |
s->last_octet_count = s->octet_count; |
821 |
s->first_packet = 0;
|
822 |
} |
823 |
|
824 |
switch(st->codec->codec_id) {
|
825 |
case CODEC_ID_PCM_MULAW:
|
826 |
case CODEC_ID_PCM_ALAW:
|
827 |
case CODEC_ID_PCM_U8:
|
828 |
case CODEC_ID_PCM_S8:
|
829 |
rtp_send_samples(s1, buf1, size, 1 * st->codec->channels);
|
830 |
break;
|
831 |
case CODEC_ID_PCM_U16BE:
|
832 |
case CODEC_ID_PCM_U16LE:
|
833 |
case CODEC_ID_PCM_S16BE:
|
834 |
case CODEC_ID_PCM_S16LE:
|
835 |
rtp_send_samples(s1, buf1, size, 2 * st->codec->channels);
|
836 |
break;
|
837 |
case CODEC_ID_MP2:
|
838 |
case CODEC_ID_MP3:
|
839 |
rtp_send_mpegaudio(s1, buf1, size); |
840 |
break;
|
841 |
case CODEC_ID_MPEG1VIDEO:
|
842 |
rtp_send_mpegvideo(s1, buf1, size); |
843 |
break;
|
844 |
case CODEC_ID_MPEG2TS:
|
845 |
rtp_send_mpegts_raw(s1, buf1, size); |
846 |
break;
|
847 |
default:
|
848 |
/* better than nothing : send the codec raw data */
|
849 |
rtp_send_raw(s1, buf1, size); |
850 |
break;
|
851 |
} |
852 |
return 0; |
853 |
} |
854 |
|
855 |
static int rtp_write_trailer(AVFormatContext *s1) |
856 |
{ |
857 |
// RTPDemuxContext *s = s1->priv_data;
|
858 |
return 0; |
859 |
} |
860 |
|
861 |
AVOutputFormat rtp_mux = { |
862 |
"rtp",
|
863 |
"RTP output format",
|
864 |
NULL,
|
865 |
NULL,
|
866 |
sizeof(RTPDemuxContext),
|
867 |
CODEC_ID_PCM_MULAW, |
868 |
CODEC_ID_NONE, |
869 |
rtp_write_header, |
870 |
rtp_write_packet, |
871 |
rtp_write_trailer, |
872 |
}; |
873 |
|
874 |
int rtp_init(void) |
875 |
{ |
876 |
av_register_output_format(&rtp_mux); |
877 |
return 0; |
878 |
} |