Line |
Branch |
Exec |
Source |
1 |
|
|
/* |
2 |
|
|
* Realmedia RTSP protocol (RDT) support. |
3 |
|
|
* Copyright (c) 2007 Ronald S. Bultje |
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 |
9 |
|
|
* License 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 GNU |
15 |
|
|
* Lesser General Public License for more details. |
16 |
|
|
* |
17 |
|
|
* You should have received a copy of the GNU Lesser General Public |
18 |
|
|
* License along with FFmpeg; if not, write to the Free Software |
19 |
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
20 |
|
|
*/ |
21 |
|
|
|
22 |
|
|
/** |
23 |
|
|
* @file |
24 |
|
|
* @brief Realmedia RTSP protocol (RDT) support |
25 |
|
|
* @author Ronald S. Bultje <rbultje@ronald.bitfreak.net> |
26 |
|
|
*/ |
27 |
|
|
|
28 |
|
|
#include "avformat.h" |
29 |
|
|
#include "libavutil/avstring.h" |
30 |
|
|
#include "libavutil/mem.h" |
31 |
|
|
#include "demux.h" |
32 |
|
|
#include "rtpdec.h" |
33 |
|
|
#include "rdt.h" |
34 |
|
|
#include "libavutil/base64.h" |
35 |
|
|
#include "libavutil/md5.h" |
36 |
|
|
#include "rm.h" |
37 |
|
|
#include "internal.h" |
38 |
|
|
#include "avio_internal.h" |
39 |
|
|
#include "libavcodec/get_bits.h" |
40 |
|
|
|
41 |
|
|
struct RDTDemuxContext { |
42 |
|
|
AVFormatContext *ic; /**< the containing (RTSP) demux context */ |
43 |
|
|
/** Each RDT stream-set (represented by one RTSPStream) can contain |
44 |
|
|
* multiple streams (of the same content, but with possibly different |
45 |
|
|
* codecs/bitrates). Each such stream is represented by one AVStream |
46 |
|
|
* in the AVFormatContext, and this variable points to the offset in |
47 |
|
|
* that array such that the first is the first stream of this set. */ |
48 |
|
|
AVStream **streams; |
49 |
|
|
int n_streams; /**< streams with identical content in this set */ |
50 |
|
|
void *dynamic_protocol_context; |
51 |
|
|
DynamicPayloadPacketHandlerProc parse_packet; |
52 |
|
|
uint32_t prev_timestamp; |
53 |
|
|
int prev_set_id, prev_stream_id; |
54 |
|
|
}; |
55 |
|
|
|
56 |
|
|
RDTDemuxContext * |
57 |
|
✗ |
ff_rdt_parse_open(AVFormatContext *ic, int first_stream_of_set_idx, |
58 |
|
|
void *priv_data, const RTPDynamicProtocolHandler *handler) |
59 |
|
|
{ |
60 |
|
✗ |
RDTDemuxContext *s = av_mallocz(sizeof(RDTDemuxContext)); |
61 |
|
✗ |
if (!s) |
62 |
|
✗ |
return NULL; |
63 |
|
|
|
64 |
|
✗ |
s->ic = ic; |
65 |
|
✗ |
s->streams = &ic->streams[first_stream_of_set_idx]; |
66 |
|
|
do { |
67 |
|
✗ |
s->n_streams++; |
68 |
|
✗ |
} while (first_stream_of_set_idx + s->n_streams < ic->nb_streams && |
69 |
|
✗ |
s->streams[s->n_streams]->id == s->streams[0]->id); |
70 |
|
✗ |
s->prev_set_id = -1; |
71 |
|
✗ |
s->prev_stream_id = -1; |
72 |
|
✗ |
s->prev_timestamp = -1; |
73 |
|
✗ |
s->parse_packet = handler ? handler->parse_packet : NULL; |
74 |
|
✗ |
s->dynamic_protocol_context = priv_data; |
75 |
|
|
|
76 |
|
✗ |
return s; |
77 |
|
|
} |
78 |
|
|
|
79 |
|
|
void |
80 |
|
✗ |
ff_rdt_parse_close(RDTDemuxContext *s) |
81 |
|
|
{ |
82 |
|
✗ |
av_free(s); |
83 |
|
✗ |
} |
84 |
|
|
|
85 |
|
|
struct PayloadContext { |
86 |
|
|
AVFormatContext *rmctx; |
87 |
|
|
int nb_rmst; |
88 |
|
|
RMStream **rmst; |
89 |
|
|
uint8_t *mlti_data; |
90 |
|
|
unsigned int mlti_data_size; |
91 |
|
|
char buffer[RTP_MAX_PACKET_LENGTH + AV_INPUT_BUFFER_PADDING_SIZE]; |
92 |
|
|
int audio_pkt_cnt; /**< remaining audio packets in rmdec */ |
93 |
|
|
}; |
94 |
|
|
|
95 |
|
|
void |
96 |
|
✗ |
ff_rdt_calc_response_and_checksum(char response[41], char chksum[9], |
97 |
|
|
const char *challenge) |
98 |
|
|
{ |
99 |
|
✗ |
int ch_len = strlen (challenge), i; |
100 |
|
|
unsigned char zres[16], |
101 |
|
✗ |
buf[64] = { 0xa1, 0xe9, 0x14, 0x9d, 0x0e, 0x6b, 0x3b, 0x59 }; |
102 |
|
|
#define XOR_TABLE_SIZE 37 |
103 |
|
|
static const unsigned char xor_table[XOR_TABLE_SIZE] = { |
104 |
|
|
0x05, 0x18, 0x74, 0xd0, 0x0d, 0x09, 0x02, 0x53, |
105 |
|
|
0xc0, 0x01, 0x05, 0x05, 0x67, 0x03, 0x19, 0x70, |
106 |
|
|
0x08, 0x27, 0x66, 0x10, 0x10, 0x72, 0x08, 0x09, |
107 |
|
|
0x63, 0x11, 0x03, 0x71, 0x08, 0x08, 0x70, 0x02, |
108 |
|
|
0x10, 0x57, 0x05, 0x18, 0x54 }; |
109 |
|
|
|
110 |
|
|
/* some (length) checks */ |
111 |
|
✗ |
if (ch_len == 40) /* what a hack... */ |
112 |
|
✗ |
ch_len = 32; |
113 |
|
✗ |
else if (ch_len > 56) |
114 |
|
✗ |
ch_len = 56; |
115 |
|
✗ |
memcpy(buf + 8, challenge, ch_len); |
116 |
|
|
|
117 |
|
|
/* xor challenge bytewise with xor_table */ |
118 |
|
✗ |
for (i = 0; i < XOR_TABLE_SIZE; i++) |
119 |
|
✗ |
buf[8 + i] ^= xor_table[i]; |
120 |
|
|
|
121 |
|
✗ |
av_md5_sum(zres, buf, 64); |
122 |
|
✗ |
ff_data_to_hex(response, zres, 16, 1); |
123 |
|
|
|
124 |
|
|
/* add tail */ |
125 |
|
✗ |
strcpy (response + 32, "01d0a8e3"); |
126 |
|
|
|
127 |
|
|
/* calculate checksum */ |
128 |
|
✗ |
for (i = 0; i < 8; i++) |
129 |
|
✗ |
chksum[i] = response[i * 4]; |
130 |
|
✗ |
chksum[8] = 0; |
131 |
|
✗ |
} |
132 |
|
|
|
133 |
|
|
static int |
134 |
|
✗ |
rdt_load_mdpr (PayloadContext *rdt, AVStream *st, int rule_nr) |
135 |
|
|
{ |
136 |
|
|
FFIOContext pb0; |
137 |
|
✗ |
AVIOContext *const pb = &pb0.pub; |
138 |
|
|
unsigned int size; |
139 |
|
|
uint32_t tag; |
140 |
|
|
|
141 |
|
|
/** |
142 |
|
|
* Layout of the MLTI chunk: |
143 |
|
|
* 4: MLTI |
144 |
|
|
* 2: number of streams |
145 |
|
|
* Then for each stream ([number_of_streams] times): |
146 |
|
|
* 2: mdpr index |
147 |
|
|
* 2: number of mdpr chunks |
148 |
|
|
* Then for each mdpr chunk ([number_of_mdpr_chunks] times): |
149 |
|
|
* 4: size |
150 |
|
|
* [size]: data |
151 |
|
|
* we skip MDPR chunks until we reach the one of the stream |
152 |
|
|
* we're interested in, and forward that ([size]+[data]) to |
153 |
|
|
* the RM demuxer to parse the stream-specific header data. |
154 |
|
|
*/ |
155 |
|
✗ |
if (!rdt->mlti_data) |
156 |
|
✗ |
return -1; |
157 |
|
✗ |
ffio_init_read_context(&pb0, rdt->mlti_data, rdt->mlti_data_size); |
158 |
|
✗ |
tag = avio_rl32(pb); |
159 |
|
✗ |
if (tag == MKTAG('M', 'L', 'T', 'I')) { |
160 |
|
|
int num, chunk_nr; |
161 |
|
|
|
162 |
|
|
/* read index of MDPR chunk numbers */ |
163 |
|
✗ |
num = avio_rb16(pb); |
164 |
|
✗ |
if (rule_nr < 0 || rule_nr >= num) |
165 |
|
✗ |
return -1; |
166 |
|
✗ |
avio_skip(pb, rule_nr * 2); |
167 |
|
✗ |
chunk_nr = avio_rb16(pb); |
168 |
|
✗ |
avio_skip(pb, (num - 1 - rule_nr) * 2); |
169 |
|
|
|
170 |
|
|
/* read MDPR chunks */ |
171 |
|
✗ |
num = avio_rb16(pb); |
172 |
|
✗ |
if (chunk_nr >= num) |
173 |
|
✗ |
return -1; |
174 |
|
✗ |
while (chunk_nr--) |
175 |
|
✗ |
avio_skip(pb, avio_rb32(pb)); |
176 |
|
✗ |
size = avio_rb32(pb); |
177 |
|
|
} else { |
178 |
|
✗ |
size = rdt->mlti_data_size; |
179 |
|
✗ |
avio_seek(pb, 0, SEEK_SET); |
180 |
|
|
} |
181 |
|
✗ |
if (ff_rm_read_mdpr_codecdata(rdt->rmctx, pb, st, rdt->rmst[st->index], size, NULL) < 0) |
182 |
|
✗ |
return -1; |
183 |
|
|
|
184 |
|
✗ |
return 0; |
185 |
|
|
} |
186 |
|
|
|
187 |
|
|
/** |
188 |
|
|
* Actual data handling. |
189 |
|
|
*/ |
190 |
|
|
|
191 |
|
|
int |
192 |
|
✗ |
ff_rdt_parse_header(const uint8_t *buf, int len, |
193 |
|
|
int *pset_id, int *pseq_no, int *pstream_id, |
194 |
|
|
int *pis_keyframe, uint32_t *ptimestamp) |
195 |
|
|
{ |
196 |
|
|
GetBitContext gb; |
197 |
|
✗ |
int consumed = 0, set_id, seq_no, stream_id, is_keyframe, |
198 |
|
|
len_included, need_reliable; |
199 |
|
|
uint32_t timestamp; |
200 |
|
|
|
201 |
|
|
/* skip status packets */ |
202 |
|
✗ |
while (len >= 5 && buf[1] == 0xFF /* status packet */) { |
203 |
|
|
int pkt_len; |
204 |
|
|
|
205 |
|
✗ |
if (!(buf[0] & 0x80)) |
206 |
|
✗ |
return -1; /* not followed by a data packet */ |
207 |
|
|
|
208 |
|
✗ |
pkt_len = AV_RB16(buf+3); |
209 |
|
✗ |
if (pkt_len > len) |
210 |
|
✗ |
return AVERROR_INVALIDDATA; |
211 |
|
✗ |
buf += pkt_len; |
212 |
|
✗ |
len -= pkt_len; |
213 |
|
✗ |
consumed += pkt_len; |
214 |
|
|
} |
215 |
|
✗ |
if (len < 16) |
216 |
|
✗ |
return -1; |
217 |
|
|
/** |
218 |
|
|
* Layout of the header (in bits): |
219 |
|
|
* 1: len_included |
220 |
|
|
* Flag indicating whether this header includes a length field; |
221 |
|
|
* this can be used to concatenate multiple RDT packets in a |
222 |
|
|
* single UDP/TCP data frame and is used to precede RDT data |
223 |
|
|
* by stream status packets |
224 |
|
|
* 1: need_reliable |
225 |
|
|
* Flag indicating whether this header includes a "reliable |
226 |
|
|
* sequence number"; these are apparently sequence numbers of |
227 |
|
|
* data packets alone. For data packets, this flag is always |
228 |
|
|
* set, according to the Real documentation [1] |
229 |
|
|
* 5: set_id |
230 |
|
|
* ID of a set of streams of identical content, possibly with |
231 |
|
|
* different codecs or bitrates |
232 |
|
|
* 1: is_reliable |
233 |
|
|
* Flag set for certain streams deemed less tolerable for packet |
234 |
|
|
* loss |
235 |
|
|
* 16: seq_no |
236 |
|
|
* Packet sequence number; if >=0xFF00, this is a non-data packet |
237 |
|
|
* containing stream status info, the second byte indicates the |
238 |
|
|
* type of status packet (see wireshark docs / source code [2]) |
239 |
|
|
* if (len_included) { |
240 |
|
|
* 16: packet_len |
241 |
|
|
* } else { |
242 |
|
|
* packet_len = remainder of UDP/TCP frame |
243 |
|
|
* } |
244 |
|
|
* 1: is_back_to_back |
245 |
|
|
* Back-to-Back flag; used for timing, set for one in every 10 |
246 |
|
|
* packets, according to the Real documentation [1] |
247 |
|
|
* 1: is_slow_data |
248 |
|
|
* Slow-data flag; currently unused, according to Real docs [1] |
249 |
|
|
* 5: stream_id |
250 |
|
|
* ID of the stream within this particular set of streams |
251 |
|
|
* 1: is_no_keyframe |
252 |
|
|
* Non-keyframe flag (unset if packet belongs to a keyframe) |
253 |
|
|
* 32: timestamp (PTS) |
254 |
|
|
* if (set_id == 0x1F) { |
255 |
|
|
* 16: set_id (extended set-of-streams ID; see set_id) |
256 |
|
|
* } |
257 |
|
|
* if (need_reliable) { |
258 |
|
|
* 16: reliable_seq_no |
259 |
|
|
* Reliable sequence number (see need_reliable) |
260 |
|
|
* } |
261 |
|
|
* if (stream_id == 0x3F) { |
262 |
|
|
* 16: stream_id (extended stream ID; see stream_id) |
263 |
|
|
* } |
264 |
|
|
* [1] https://protocol.helixcommunity.org/files/2005/devdocs/RDT_Feature_Level_20.txt |
265 |
|
|
* [2] http://www.wireshark.org/docs/dfref/r/rdt.html and |
266 |
|
|
* http://anonsvn.wireshark.org/viewvc/trunk/epan/dissectors/packet-rdt.c |
267 |
|
|
*/ |
268 |
|
✗ |
init_get_bits(&gb, buf, len << 3); |
269 |
|
✗ |
len_included = get_bits1(&gb); |
270 |
|
✗ |
need_reliable = get_bits1(&gb); |
271 |
|
✗ |
set_id = get_bits(&gb, 5); |
272 |
|
✗ |
skip_bits(&gb, 1); |
273 |
|
✗ |
seq_no = get_bits(&gb, 16); |
274 |
|
✗ |
if (len_included) |
275 |
|
✗ |
skip_bits(&gb, 16); |
276 |
|
✗ |
skip_bits(&gb, 2); |
277 |
|
✗ |
stream_id = get_bits(&gb, 5); |
278 |
|
✗ |
is_keyframe = !get_bits1(&gb); |
279 |
|
✗ |
timestamp = get_bits_long(&gb, 32); |
280 |
|
✗ |
if (set_id == 0x1f) |
281 |
|
✗ |
set_id = get_bits(&gb, 16); |
282 |
|
✗ |
if (need_reliable) |
283 |
|
✗ |
skip_bits(&gb, 16); |
284 |
|
✗ |
if (stream_id == 0x1f) |
285 |
|
✗ |
stream_id = get_bits(&gb, 16); |
286 |
|
|
|
287 |
|
✗ |
if (pset_id) *pset_id = set_id; |
288 |
|
✗ |
if (pseq_no) *pseq_no = seq_no; |
289 |
|
✗ |
if (pstream_id) *pstream_id = stream_id; |
290 |
|
✗ |
if (pis_keyframe) *pis_keyframe = is_keyframe; |
291 |
|
✗ |
if (ptimestamp) *ptimestamp = timestamp; |
292 |
|
|
|
293 |
|
✗ |
return consumed + (get_bits_count(&gb) >> 3); |
294 |
|
|
} |
295 |
|
|
|
296 |
|
|
/**< return 0 on packet, no more left, 1 on packet, 1 on partial packet... */ |
297 |
|
|
static int |
298 |
|
✗ |
rdt_parse_packet (AVFormatContext *ctx, PayloadContext *rdt, AVStream *st, |
299 |
|
|
AVPacket *pkt, uint32_t *timestamp, |
300 |
|
|
const uint8_t *buf, int len, uint16_t rtp_seq, int flags) |
301 |
|
|
{ |
302 |
|
✗ |
int seq = 1, res; |
303 |
|
|
|
304 |
|
✗ |
if (rdt->audio_pkt_cnt == 0) { |
305 |
|
|
FFIOContext pb; |
306 |
|
|
int pos, rmflags; |
307 |
|
|
|
308 |
|
✗ |
ffio_init_read_context(&pb, buf, len); |
309 |
|
✗ |
rmflags = (flags & RTP_FLAG_KEY) ? 2 : 0; |
310 |
|
✗ |
res = ff_rm_parse_packet(rdt->rmctx, &pb.pub, st, rdt->rmst[st->index], |
311 |
|
✗ |
len, pkt, &seq, rmflags, *timestamp); |
312 |
|
✗ |
pos = avio_tell(&pb.pub); |
313 |
|
✗ |
if (res < 0) |
314 |
|
✗ |
return res; |
315 |
|
✗ |
if (res > 0) { |
316 |
|
✗ |
if (st->codecpar->codec_id == AV_CODEC_ID_AAC) { |
317 |
|
✗ |
memcpy (rdt->buffer, buf + pos, len - pos); |
318 |
|
✗ |
rdt->rmctx->pb = avio_alloc_context (rdt->buffer, len - pos, 0, |
319 |
|
|
NULL, NULL, NULL, NULL); |
320 |
|
|
} |
321 |
|
✗ |
goto get_cache; |
322 |
|
|
} |
323 |
|
|
} else { |
324 |
|
✗ |
get_cache: |
325 |
|
✗ |
rdt->audio_pkt_cnt = |
326 |
|
✗ |
ff_rm_retrieve_cache (rdt->rmctx, rdt->rmctx->pb, |
327 |
|
✗ |
st, rdt->rmst[st->index], pkt); |
328 |
|
✗ |
if (rdt->audio_pkt_cnt == 0 && |
329 |
|
✗ |
st->codecpar->codec_id == AV_CODEC_ID_AAC) |
330 |
|
✗ |
avio_context_free(&rdt->rmctx->pb); |
331 |
|
|
} |
332 |
|
✗ |
pkt->stream_index = st->index; |
333 |
|
✗ |
pkt->pts = *timestamp; |
334 |
|
|
|
335 |
|
✗ |
return rdt->audio_pkt_cnt > 0; |
336 |
|
|
} |
337 |
|
|
|
338 |
|
|
int |
339 |
|
✗ |
ff_rdt_parse_packet(RDTDemuxContext *s, AVPacket *pkt, |
340 |
|
|
uint8_t **bufptr, int len) |
341 |
|
|
{ |
342 |
|
✗ |
uint8_t *buf = bufptr ? *bufptr : NULL; |
343 |
|
✗ |
int seq_no, flags = 0, stream_id, set_id, is_keyframe; |
344 |
|
|
uint32_t timestamp; |
345 |
|
✗ |
int rv= 0; |
346 |
|
|
|
347 |
|
✗ |
if (!s->parse_packet) |
348 |
|
✗ |
return -1; |
349 |
|
|
|
350 |
|
✗ |
if (!buf && s->prev_stream_id != -1) { |
351 |
|
|
/* return the next packets, if any */ |
352 |
|
✗ |
timestamp= 0; ///< Should not be used if buf is NULL, but should be set to the timestamp of the packet returned.... |
353 |
|
✗ |
rv= s->parse_packet(s->ic, s->dynamic_protocol_context, |
354 |
|
✗ |
s->streams[s->prev_stream_id], |
355 |
|
|
pkt, ×tamp, NULL, 0, 0, flags); |
356 |
|
✗ |
return rv; |
357 |
|
|
} |
358 |
|
|
|
359 |
|
✗ |
if (len < 12) |
360 |
|
✗ |
return -1; |
361 |
|
✗ |
rv = ff_rdt_parse_header(buf, len, &set_id, &seq_no, &stream_id, &is_keyframe, ×tamp); |
362 |
|
✗ |
if (rv < 0) |
363 |
|
✗ |
return rv; |
364 |
|
✗ |
if (is_keyframe && |
365 |
|
✗ |
(set_id != s->prev_set_id || timestamp != s->prev_timestamp || |
366 |
|
✗ |
stream_id != s->prev_stream_id)) { |
367 |
|
✗ |
flags |= RTP_FLAG_KEY; |
368 |
|
✗ |
s->prev_set_id = set_id; |
369 |
|
✗ |
s->prev_timestamp = timestamp; |
370 |
|
|
} |
371 |
|
✗ |
s->prev_stream_id = stream_id; |
372 |
|
✗ |
buf += rv; |
373 |
|
✗ |
len -= rv; |
374 |
|
|
|
375 |
|
✗ |
if (s->prev_stream_id >= s->n_streams) { |
376 |
|
✗ |
s->prev_stream_id = -1; |
377 |
|
✗ |
return -1; |
378 |
|
|
} |
379 |
|
|
|
380 |
|
✗ |
rv = s->parse_packet(s->ic, s->dynamic_protocol_context, |
381 |
|
✗ |
s->streams[s->prev_stream_id], |
382 |
|
|
pkt, ×tamp, buf, len, 0, flags); |
383 |
|
|
|
384 |
|
✗ |
return rv; |
385 |
|
|
} |
386 |
|
|
|
387 |
|
|
void |
388 |
|
✗ |
ff_rdt_subscribe_rule (char *cmd, int size, |
389 |
|
|
int stream_nr, int rule_nr) |
390 |
|
|
{ |
391 |
|
✗ |
av_strlcatf(cmd, size, "stream=%d;rule=%d,stream=%d;rule=%d", |
392 |
|
✗ |
stream_nr, rule_nr * 2, stream_nr, rule_nr * 2 + 1); |
393 |
|
✗ |
} |
394 |
|
|
|
395 |
|
|
static unsigned char * |
396 |
|
✗ |
rdt_parse_b64buf (unsigned int *target_len, const char *p) |
397 |
|
|
{ |
398 |
|
|
unsigned char *target; |
399 |
|
✗ |
int len = strlen(p); |
400 |
|
✗ |
if (*p == '\"') { |
401 |
|
✗ |
p++; |
402 |
|
✗ |
len -= 2; /* skip embracing " at start/end */ |
403 |
|
|
} |
404 |
|
✗ |
*target_len = len * 3 / 4; |
405 |
|
✗ |
target = av_mallocz(*target_len + AV_INPUT_BUFFER_PADDING_SIZE); |
406 |
|
✗ |
if (!target) |
407 |
|
✗ |
return NULL; |
408 |
|
✗ |
av_base64_decode(target, p, *target_len); |
409 |
|
✗ |
return target; |
410 |
|
|
} |
411 |
|
|
|
412 |
|
|
static int |
413 |
|
✗ |
rdt_parse_sdp_line (AVFormatContext *s, int st_index, |
414 |
|
|
PayloadContext *rdt, const char *line) |
415 |
|
|
{ |
416 |
|
✗ |
AVStream *stream = s->streams[st_index]; |
417 |
|
✗ |
const char *p = line; |
418 |
|
|
|
419 |
|
✗ |
if (av_strstart(p, "OpaqueData:buffer;", &p)) { |
420 |
|
✗ |
rdt->mlti_data = rdt_parse_b64buf(&rdt->mlti_data_size, p); |
421 |
|
✗ |
} else if (av_strstart(p, "StartTime:integer;", &p)) |
422 |
|
✗ |
ffstream(stream)->first_dts = atoi(p); |
423 |
|
✗ |
else if (av_strstart(p, "ASMRuleBook:string;", &p)) { |
424 |
|
✗ |
int n, first = -1; |
425 |
|
|
|
426 |
|
✗ |
for (n = 0; n < s->nb_streams; n++) |
427 |
|
✗ |
if (s->streams[n]->id == stream->id) { |
428 |
|
✗ |
int count = s->streams[n]->index + 1, err; |
429 |
|
✗ |
if (first == -1) first = n; |
430 |
|
✗ |
if (rdt->nb_rmst < count) { |
431 |
|
✗ |
if ((err = av_reallocp(&rdt->rmst, |
432 |
|
|
count * sizeof(*rdt->rmst))) < 0) { |
433 |
|
✗ |
rdt->nb_rmst = 0; |
434 |
|
✗ |
return err; |
435 |
|
|
} |
436 |
|
✗ |
memset(rdt->rmst + rdt->nb_rmst, 0, |
437 |
|
✗ |
(count - rdt->nb_rmst) * sizeof(*rdt->rmst)); |
438 |
|
✗ |
rdt->nb_rmst = count; |
439 |
|
|
} |
440 |
|
✗ |
rdt->rmst[s->streams[n]->index] = ff_rm_alloc_rmstream(); |
441 |
|
✗ |
if (!rdt->rmst[s->streams[n]->index]) |
442 |
|
✗ |
return AVERROR(ENOMEM); |
443 |
|
✗ |
rdt_load_mdpr(rdt, s->streams[n], (n - first) * 2); |
444 |
|
|
} |
445 |
|
|
} |
446 |
|
|
|
447 |
|
✗ |
return 0; |
448 |
|
|
} |
449 |
|
|
|
450 |
|
|
static void |
451 |
|
✗ |
real_parse_asm_rule(AVStream *st, const char *p, const char *end) |
452 |
|
|
{ |
453 |
|
|
do { |
454 |
|
|
/* can be either averagebandwidth= or AverageBandwidth= */ |
455 |
|
✗ |
if (sscanf(p, " %*1[Aa]verage%*1[Bb]andwidth=%"SCNd64, &st->codecpar->bit_rate) == 1) |
456 |
|
✗ |
break; |
457 |
|
✗ |
if (!(p = strchr(p, ',')) || p > end) |
458 |
|
✗ |
p = end; |
459 |
|
✗ |
p++; |
460 |
|
✗ |
} while (p < end); |
461 |
|
✗ |
} |
462 |
|
|
|
463 |
|
|
static AVStream * |
464 |
|
✗ |
add_dstream(AVFormatContext *s, AVStream *orig_st) |
465 |
|
|
{ |
466 |
|
|
AVStream *st; |
467 |
|
|
|
468 |
|
✗ |
if (!(st = avformat_new_stream(s, NULL))) |
469 |
|
✗ |
return NULL; |
470 |
|
✗ |
st->id = orig_st->id; |
471 |
|
✗ |
st->codecpar->codec_type = orig_st->codecpar->codec_type; |
472 |
|
✗ |
ffstream(st)->first_dts = ffstream(orig_st)->first_dts; |
473 |
|
|
|
474 |
|
✗ |
return st; |
475 |
|
|
} |
476 |
|
|
|
477 |
|
|
static void |
478 |
|
✗ |
real_parse_asm_rulebook(AVFormatContext *s, AVStream *orig_st, |
479 |
|
|
const char *p) |
480 |
|
|
{ |
481 |
|
|
const char *end; |
482 |
|
✗ |
int n_rules = 0, odd = 0; |
483 |
|
|
AVStream *st; |
484 |
|
|
|
485 |
|
|
/** |
486 |
|
|
* The ASMRuleBook contains a list of comma-separated strings per rule, |
487 |
|
|
* and each rule is separated by a ;. The last one also has a ; at the |
488 |
|
|
* end so we can use it as delimiter. |
489 |
|
|
* Every rule occurs twice, once for when the RTSP packet header marker |
490 |
|
|
* is set and once for if it isn't. We only read the first because we |
491 |
|
|
* don't care much (that's what the "odd" variable is for). |
492 |
|
|
* Each rule contains a set of one or more statements, optionally |
493 |
|
|
* preceded by a single condition. If there's a condition, the rule |
494 |
|
|
* starts with a '#'. Multiple conditions are merged between brackets, |
495 |
|
|
* so there are never multiple conditions spread out over separate |
496 |
|
|
* statements. Generally, these conditions are bitrate limits (min/max) |
497 |
|
|
* for multi-bitrate streams. |
498 |
|
|
*/ |
499 |
|
✗ |
if (*p == '\"') p++; |
500 |
|
|
while (1) { |
501 |
|
✗ |
if (!(end = strchr(p, ';'))) |
502 |
|
✗ |
break; |
503 |
|
✗ |
if (!odd && end != p) { |
504 |
|
✗ |
if (n_rules > 0) |
505 |
|
✗ |
st = add_dstream(s, orig_st); |
506 |
|
|
else |
507 |
|
✗ |
st = orig_st; |
508 |
|
✗ |
if (!st) |
509 |
|
✗ |
break; |
510 |
|
✗ |
real_parse_asm_rule(st, p, end); |
511 |
|
✗ |
n_rules++; |
512 |
|
|
} |
513 |
|
✗ |
p = end + 1; |
514 |
|
✗ |
odd ^= 1; |
515 |
|
|
} |
516 |
|
✗ |
} |
517 |
|
|
|
518 |
|
|
void |
519 |
|
✗ |
ff_real_parse_sdp_a_line (AVFormatContext *s, int stream_index, |
520 |
|
|
const char *line) |
521 |
|
|
{ |
522 |
|
✗ |
const char *p = line; |
523 |
|
|
|
524 |
|
✗ |
if (av_strstart(p, "ASMRuleBook:string;", &p)) |
525 |
|
✗ |
real_parse_asm_rulebook(s, s->streams[stream_index], p); |
526 |
|
✗ |
} |
527 |
|
|
|
528 |
|
|
|
529 |
|
|
|
530 |
|
✗ |
static av_cold int rdt_init(AVFormatContext *s, int st_index, PayloadContext *rdt) |
531 |
|
|
{ |
532 |
|
|
int ret; |
533 |
|
|
|
534 |
|
✗ |
rdt->rmctx = avformat_alloc_context(); |
535 |
|
✗ |
if (!rdt->rmctx) |
536 |
|
✗ |
return AVERROR(ENOMEM); |
537 |
|
|
|
538 |
|
✗ |
if ((ret = ff_copy_whiteblacklists(rdt->rmctx, s)) < 0) |
539 |
|
✗ |
return ret; |
540 |
|
|
|
541 |
|
✗ |
return avformat_open_input(&rdt->rmctx, "", &ff_rdt_demuxer.p, NULL); |
542 |
|
|
} |
543 |
|
|
|
544 |
|
|
static void |
545 |
|
✗ |
rdt_close_context (PayloadContext *rdt) |
546 |
|
|
{ |
547 |
|
|
int i; |
548 |
|
|
|
549 |
|
✗ |
for (i = 0; i < rdt->nb_rmst; i++) |
550 |
|
✗ |
if (rdt->rmst[i]) { |
551 |
|
✗ |
ff_rm_free_rmstream(rdt->rmst[i]); |
552 |
|
✗ |
av_freep(&rdt->rmst[i]); |
553 |
|
|
} |
554 |
|
✗ |
if (rdt->rmctx) |
555 |
|
✗ |
avformat_close_input(&rdt->rmctx); |
556 |
|
✗ |
av_freep(&rdt->mlti_data); |
557 |
|
✗ |
av_freep(&rdt->rmst); |
558 |
|
✗ |
} |
559 |
|
|
|
560 |
|
|
#define RDT_HANDLER(n, s, t) \ |
561 |
|
|
const RTPDynamicProtocolHandler ff_rdt_ ## n ## _handler = { \ |
562 |
|
|
.enc_name = s, \ |
563 |
|
|
.codec_type = t, \ |
564 |
|
|
.codec_id = AV_CODEC_ID_NONE, \ |
565 |
|
|
.priv_data_size = sizeof(PayloadContext), \ |
566 |
|
|
.init = rdt_init, \ |
567 |
|
|
.parse_sdp_a_line = rdt_parse_sdp_line, \ |
568 |
|
|
.close = rdt_close_context, \ |
569 |
|
|
.parse_packet = rdt_parse_packet \ |
570 |
|
|
} |
571 |
|
|
|
572 |
|
|
RDT_HANDLER(live_video, "x-pn-multirate-realvideo-live", AVMEDIA_TYPE_VIDEO); |
573 |
|
|
RDT_HANDLER(live_audio, "x-pn-multirate-realaudio-live", AVMEDIA_TYPE_AUDIO); |
574 |
|
|
RDT_HANDLER(video, "x-pn-realvideo", AVMEDIA_TYPE_VIDEO); |
575 |
|
|
RDT_HANDLER(audio, "x-pn-realaudio", AVMEDIA_TYPE_AUDIO); |
576 |
|
|
|
577 |
|
|
|