/src/ffmpeg/libavformat/rdt.c
Line | Count | 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 | 0 | { |
60 | 0 | RDTDemuxContext *s = av_mallocz(sizeof(RDTDemuxContext)); |
61 | 0 | if (!s) |
62 | 0 | return NULL; |
63 | | |
64 | 0 | s->ic = ic; |
65 | 0 | s->streams = &ic->streams[first_stream_of_set_idx]; |
66 | 0 | do { |
67 | 0 | s->n_streams++; |
68 | 0 | } while (first_stream_of_set_idx + s->n_streams < ic->nb_streams && |
69 | 0 | s->streams[s->n_streams]->id == s->streams[0]->id); |
70 | 0 | s->prev_set_id = -1; |
71 | 0 | s->prev_stream_id = -1; |
72 | 0 | s->prev_timestamp = -1; |
73 | 0 | s->parse_packet = handler ? handler->parse_packet : NULL; |
74 | 0 | s->dynamic_protocol_context = priv_data; |
75 | |
|
76 | 0 | return s; |
77 | 0 | } |
78 | | |
79 | | void |
80 | | ff_rdt_parse_close(RDTDemuxContext *s) |
81 | 0 | { |
82 | 0 | av_free(s); |
83 | 0 | } |
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 | 0 | { |
99 | 0 | int ch_len = strlen (challenge), i; |
100 | 0 | unsigned char zres[16], |
101 | 0 | buf[64] = { 0xa1, 0xe9, 0x14, 0x9d, 0x0e, 0x6b, 0x3b, 0x59 }; |
102 | 0 | #define XOR_TABLE_SIZE 37 |
103 | 0 | static const unsigned char xor_table[XOR_TABLE_SIZE] = { |
104 | 0 | 0x05, 0x18, 0x74, 0xd0, 0x0d, 0x09, 0x02, 0x53, |
105 | 0 | 0xc0, 0x01, 0x05, 0x05, 0x67, 0x03, 0x19, 0x70, |
106 | 0 | 0x08, 0x27, 0x66, 0x10, 0x10, 0x72, 0x08, 0x09, |
107 | 0 | 0x63, 0x11, 0x03, 0x71, 0x08, 0x08, 0x70, 0x02, |
108 | 0 | 0x10, 0x57, 0x05, 0x18, 0x54 }; |
109 | | |
110 | | /* some (length) checks */ |
111 | 0 | if (ch_len == 40) /* what a hack... */ |
112 | 0 | ch_len = 32; |
113 | 0 | else if (ch_len > 56) |
114 | 0 | ch_len = 56; |
115 | 0 | memcpy(buf + 8, challenge, ch_len); |
116 | | |
117 | | /* xor challenge bytewise with xor_table */ |
118 | 0 | for (i = 0; i < XOR_TABLE_SIZE; i++) |
119 | 0 | buf[8 + i] ^= xor_table[i]; |
120 | |
|
121 | 0 | av_md5_sum(zres, buf, 64); |
122 | 0 | ff_data_to_hex(response, zres, 16, 1); |
123 | | |
124 | | /* add tail */ |
125 | 0 | strcpy (response + 32, "01d0a8e3"); |
126 | | |
127 | | /* calculate checksum */ |
128 | 0 | for (i = 0; i < 8; i++) |
129 | 0 | chksum[i] = response[i * 4]; |
130 | 0 | chksum[8] = 0; |
131 | 0 | } |
132 | | |
133 | | static int |
134 | | rdt_load_mdpr (PayloadContext *rdt, AVStream *st, int rule_nr) |
135 | 0 | { |
136 | 0 | FFIOContext pb0; |
137 | 0 | AVIOContext *const pb = &pb0.pub; |
138 | 0 | unsigned int size; |
139 | 0 | 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 | 0 | if (!rdt->mlti_data) |
156 | 0 | return -1; |
157 | 0 | ffio_init_read_context(&pb0, rdt->mlti_data, rdt->mlti_data_size); |
158 | 0 | tag = avio_rl32(pb); |
159 | 0 | if (tag == MKTAG('M', 'L', 'T', 'I')) { |
160 | 0 | int num, chunk_nr; |
161 | | |
162 | | /* read index of MDPR chunk numbers */ |
163 | 0 | num = avio_rb16(pb); |
164 | 0 | if (rule_nr < 0 || rule_nr >= num) |
165 | 0 | return -1; |
166 | 0 | avio_skip(pb, rule_nr * 2); |
167 | 0 | chunk_nr = avio_rb16(pb); |
168 | 0 | avio_skip(pb, (num - 1 - rule_nr) * 2); |
169 | | |
170 | | /* read MDPR chunks */ |
171 | 0 | num = avio_rb16(pb); |
172 | 0 | if (chunk_nr >= num) |
173 | 0 | return -1; |
174 | 0 | while (chunk_nr--) |
175 | 0 | avio_skip(pb, avio_rb32(pb)); |
176 | 0 | size = avio_rb32(pb); |
177 | 0 | } else { |
178 | 0 | size = rdt->mlti_data_size; |
179 | 0 | avio_seek(pb, 0, SEEK_SET); |
180 | 0 | } |
181 | 0 | if (ff_rm_read_mdpr_codecdata(rdt->rmctx, pb, st, rdt->rmst[st->index], size, NULL) < 0) |
182 | 0 | return -1; |
183 | | |
184 | 0 | return 0; |
185 | 0 | } |
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 | 0 | { |
196 | 0 | GetBitContext gb; |
197 | 0 | int consumed = 0, set_id, seq_no, stream_id, is_keyframe, |
198 | 0 | len_included, need_reliable; |
199 | 0 | uint32_t timestamp; |
200 | | |
201 | | /* skip status packets */ |
202 | 0 | while (len >= 5 && buf[1] == 0xFF /* status packet */) { |
203 | 0 | int pkt_len; |
204 | |
|
205 | 0 | if (!(buf[0] & 0x80)) |
206 | 0 | return -1; /* not followed by a data packet */ |
207 | | |
208 | 0 | pkt_len = AV_RB16(buf+3); |
209 | 0 | if (pkt_len > len) |
210 | 0 | return AVERROR_INVALIDDATA; |
211 | 0 | buf += pkt_len; |
212 | 0 | len -= pkt_len; |
213 | 0 | consumed += pkt_len; |
214 | 0 | } |
215 | 0 | if (len < 16) |
216 | 0 | 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 | 0 | init_get_bits(&gb, buf, len << 3); |
269 | 0 | len_included = get_bits1(&gb); |
270 | 0 | need_reliable = get_bits1(&gb); |
271 | 0 | set_id = get_bits(&gb, 5); |
272 | 0 | skip_bits(&gb, 1); |
273 | 0 | seq_no = get_bits(&gb, 16); |
274 | 0 | if (len_included) |
275 | 0 | skip_bits(&gb, 16); |
276 | 0 | skip_bits(&gb, 2); |
277 | 0 | stream_id = get_bits(&gb, 5); |
278 | 0 | is_keyframe = !get_bits1(&gb); |
279 | 0 | timestamp = get_bits_long(&gb, 32); |
280 | 0 | if (set_id == 0x1f) |
281 | 0 | set_id = get_bits(&gb, 16); |
282 | 0 | if (need_reliable) |
283 | 0 | skip_bits(&gb, 16); |
284 | 0 | if (stream_id == 0x1f) |
285 | 0 | stream_id = get_bits(&gb, 16); |
286 | |
|
287 | 0 | if (pset_id) *pset_id = set_id; |
288 | 0 | if (pseq_no) *pseq_no = seq_no; |
289 | 0 | if (pstream_id) *pstream_id = stream_id; |
290 | 0 | if (pis_keyframe) *pis_keyframe = is_keyframe; |
291 | 0 | if (ptimestamp) *ptimestamp = timestamp; |
292 | |
|
293 | 0 | return consumed + (get_bits_count(&gb) >> 3); |
294 | 0 | } |
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 | 0 | { |
302 | 0 | int seq = 1, res; |
303 | |
|
304 | 0 | if (rdt->audio_pkt_cnt == 0) { |
305 | 0 | FFIOContext pb; |
306 | 0 | int pos, rmflags; |
307 | |
|
308 | 0 | ffio_init_read_context(&pb, buf, len); |
309 | 0 | rmflags = (flags & RTP_FLAG_KEY) ? 2 : 0; |
310 | 0 | res = ff_rm_parse_packet(rdt->rmctx, &pb.pub, st, rdt->rmst[st->index], |
311 | 0 | len, pkt, &seq, rmflags, *timestamp); |
312 | 0 | pos = avio_tell(&pb.pub); |
313 | 0 | if (res < 0) |
314 | 0 | return res; |
315 | 0 | if (res > 0) { |
316 | 0 | if (st->codecpar->codec_id == AV_CODEC_ID_AAC) { |
317 | 0 | memcpy (rdt->buffer, buf + pos, len - pos); |
318 | 0 | rdt->rmctx->pb = avio_alloc_context (rdt->buffer, len - pos, 0, |
319 | 0 | NULL, NULL, NULL, NULL); |
320 | 0 | } |
321 | 0 | goto get_cache; |
322 | 0 | } |
323 | 0 | } else { |
324 | 0 | get_cache: |
325 | 0 | rdt->audio_pkt_cnt = |
326 | 0 | ff_rm_retrieve_cache (rdt->rmctx, rdt->rmctx->pb, |
327 | 0 | st, rdt->rmst[st->index], pkt); |
328 | 0 | if (rdt->audio_pkt_cnt == 0 && |
329 | 0 | st->codecpar->codec_id == AV_CODEC_ID_AAC) |
330 | 0 | avio_context_free(&rdt->rmctx->pb); |
331 | 0 | } |
332 | 0 | pkt->stream_index = st->index; |
333 | 0 | pkt->pts = *timestamp; |
334 | |
|
335 | 0 | return rdt->audio_pkt_cnt > 0; |
336 | 0 | } |
337 | | |
338 | | int |
339 | | ff_rdt_parse_packet(RDTDemuxContext *s, AVPacket *pkt, |
340 | | uint8_t **bufptr, int len) |
341 | 0 | { |
342 | 0 | uint8_t *buf = bufptr ? *bufptr : NULL; |
343 | 0 | int seq_no, flags = 0, stream_id, set_id, is_keyframe; |
344 | 0 | uint32_t timestamp; |
345 | 0 | int rv= 0; |
346 | |
|
347 | 0 | if (!s->parse_packet) |
348 | 0 | return -1; |
349 | | |
350 | 0 | if (!buf && s->prev_stream_id != -1) { |
351 | | /* return the next packets, if any */ |
352 | 0 | timestamp= 0; ///< Should not be used if buf is NULL, but should be set to the timestamp of the packet returned.... |
353 | 0 | rv= s->parse_packet(s->ic, s->dynamic_protocol_context, |
354 | 0 | s->streams[s->prev_stream_id], |
355 | 0 | pkt, ×tamp, NULL, 0, 0, flags); |
356 | 0 | return rv; |
357 | 0 | } |
358 | | |
359 | 0 | if (len < 12) |
360 | 0 | return -1; |
361 | 0 | rv = ff_rdt_parse_header(buf, len, &set_id, &seq_no, &stream_id, &is_keyframe, ×tamp); |
362 | 0 | if (rv < 0) |
363 | 0 | return rv; |
364 | 0 | if (is_keyframe && |
365 | 0 | (set_id != s->prev_set_id || timestamp != s->prev_timestamp || |
366 | 0 | stream_id != s->prev_stream_id)) { |
367 | 0 | flags |= RTP_FLAG_KEY; |
368 | 0 | s->prev_set_id = set_id; |
369 | 0 | s->prev_timestamp = timestamp; |
370 | 0 | } |
371 | 0 | s->prev_stream_id = stream_id; |
372 | 0 | buf += rv; |
373 | 0 | len -= rv; |
374 | |
|
375 | 0 | if (s->prev_stream_id >= s->n_streams) { |
376 | 0 | s->prev_stream_id = -1; |
377 | 0 | return -1; |
378 | 0 | } |
379 | | |
380 | 0 | rv = s->parse_packet(s->ic, s->dynamic_protocol_context, |
381 | 0 | s->streams[s->prev_stream_id], |
382 | 0 | pkt, ×tamp, buf, len, 0, flags); |
383 | |
|
384 | 0 | return rv; |
385 | 0 | } |
386 | | |
387 | | void |
388 | | ff_rdt_subscribe_rule (char *cmd, int size, |
389 | | int stream_nr, int rule_nr) |
390 | 0 | { |
391 | 0 | av_strlcatf(cmd, size, "stream=%d;rule=%d,stream=%d;rule=%d", |
392 | 0 | stream_nr, rule_nr * 2, stream_nr, rule_nr * 2 + 1); |
393 | 0 | } |
394 | | |
395 | | static unsigned char * |
396 | | rdt_parse_b64buf (unsigned int *target_len, const char *p) |
397 | 0 | { |
398 | 0 | unsigned char *target; |
399 | 0 | int len = strlen(p); |
400 | 0 | if (*p == '\"') { |
401 | 0 | p++; |
402 | 0 | len -= 2; /* skip embracing " at start/end */ |
403 | 0 | } |
404 | 0 | *target_len = len * 3 / 4; |
405 | 0 | target = av_mallocz(*target_len + AV_INPUT_BUFFER_PADDING_SIZE); |
406 | 0 | if (!target) |
407 | 0 | return NULL; |
408 | 0 | av_base64_decode(target, p, *target_len); |
409 | 0 | return target; |
410 | 0 | } |
411 | | |
412 | | static int |
413 | | rdt_parse_sdp_line (AVFormatContext *s, int st_index, |
414 | | PayloadContext *rdt, const char *line) |
415 | 0 | { |
416 | 0 | AVStream *stream = s->streams[st_index]; |
417 | 0 | const char *p = line; |
418 | |
|
419 | 0 | if (av_strstart(p, "OpaqueData:buffer;", &p)) { |
420 | 0 | rdt->mlti_data = rdt_parse_b64buf(&rdt->mlti_data_size, p); |
421 | 0 | } else if (av_strstart(p, "StartTime:integer;", &p)) |
422 | 0 | ffstream(stream)->first_dts = atoi(p); |
423 | 0 | else if (av_strstart(p, "ASMRuleBook:string;", &p)) { |
424 | 0 | int n, first = -1; |
425 | |
|
426 | 0 | for (n = 0; n < s->nb_streams; n++) |
427 | 0 | if (s->streams[n]->id == stream->id) { |
428 | 0 | int count = s->streams[n]->index + 1, err; |
429 | 0 | if (first == -1) first = n; |
430 | 0 | if (rdt->nb_rmst < count) { |
431 | 0 | if ((err = av_reallocp(&rdt->rmst, |
432 | 0 | count * sizeof(*rdt->rmst))) < 0) { |
433 | 0 | rdt->nb_rmst = 0; |
434 | 0 | return err; |
435 | 0 | } |
436 | 0 | memset(rdt->rmst + rdt->nb_rmst, 0, |
437 | 0 | (count - rdt->nb_rmst) * sizeof(*rdt->rmst)); |
438 | 0 | rdt->nb_rmst = count; |
439 | 0 | } |
440 | 0 | rdt->rmst[s->streams[n]->index] = ff_rm_alloc_rmstream(); |
441 | 0 | if (!rdt->rmst[s->streams[n]->index]) |
442 | 0 | return AVERROR(ENOMEM); |
443 | 0 | rdt_load_mdpr(rdt, s->streams[n], (n - first) * 2); |
444 | 0 | } |
445 | 0 | } |
446 | | |
447 | 0 | return 0; |
448 | 0 | } |
449 | | |
450 | | static void |
451 | | real_parse_asm_rule(AVStream *st, const char *p, const char *end) |
452 | 0 | { |
453 | 0 | do { |
454 | | /* can be either averagebandwidth= or AverageBandwidth= */ |
455 | 0 | if (sscanf(p, " %*1[Aa]verage%*1[Bb]andwidth=%"SCNd64, &st->codecpar->bit_rate) == 1) |
456 | 0 | break; |
457 | 0 | if (!(p = strchr(p, ',')) || p > end) |
458 | 0 | p = end; |
459 | 0 | p++; |
460 | 0 | } while (p < end); |
461 | 0 | } |
462 | | |
463 | | static AVStream * |
464 | | add_dstream(AVFormatContext *s, AVStream *orig_st) |
465 | 0 | { |
466 | 0 | AVStream *st; |
467 | |
|
468 | 0 | if (!(st = avformat_new_stream(s, NULL))) |
469 | 0 | return NULL; |
470 | 0 | st->id = orig_st->id; |
471 | 0 | st->codecpar->codec_type = orig_st->codecpar->codec_type; |
472 | 0 | ffstream(st)->first_dts = ffstream(orig_st)->first_dts; |
473 | |
|
474 | 0 | return st; |
475 | 0 | } |
476 | | |
477 | | static void |
478 | | real_parse_asm_rulebook(AVFormatContext *s, AVStream *orig_st, |
479 | | const char *p) |
480 | 0 | { |
481 | 0 | const char *end; |
482 | 0 | int n_rules = 0, odd = 0; |
483 | 0 | 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 | 0 | if (*p == '\"') p++; |
500 | 0 | while (1) { |
501 | 0 | if (!(end = strchr(p, ';'))) |
502 | 0 | break; |
503 | 0 | if (!odd && end != p) { |
504 | 0 | if (n_rules > 0) |
505 | 0 | st = add_dstream(s, orig_st); |
506 | 0 | else |
507 | 0 | st = orig_st; |
508 | 0 | if (!st) |
509 | 0 | break; |
510 | 0 | real_parse_asm_rule(st, p, end); |
511 | 0 | n_rules++; |
512 | 0 | } |
513 | 0 | p = end + 1; |
514 | 0 | odd ^= 1; |
515 | 0 | } |
516 | 0 | } |
517 | | |
518 | | void |
519 | | ff_real_parse_sdp_a_line (AVFormatContext *s, int stream_index, |
520 | | const char *line) |
521 | 0 | { |
522 | 0 | const char *p = line; |
523 | |
|
524 | 0 | if (av_strstart(p, "ASMRuleBook:string;", &p)) |
525 | 0 | real_parse_asm_rulebook(s, s->streams[stream_index], p); |
526 | 0 | } |
527 | | |
528 | | |
529 | | |
530 | | static av_cold int rdt_init(AVFormatContext *s, int st_index, PayloadContext *rdt) |
531 | 0 | { |
532 | 0 | int ret; |
533 | |
|
534 | 0 | rdt->rmctx = avformat_alloc_context(); |
535 | 0 | if (!rdt->rmctx) |
536 | 0 | return AVERROR(ENOMEM); |
537 | | |
538 | 0 | if ((ret = ff_copy_whiteblacklists(rdt->rmctx, s)) < 0) |
539 | 0 | return ret; |
540 | | |
541 | 0 | return avformat_open_input(&rdt->rmctx, "", &ff_rdt_demuxer.p, NULL); |
542 | 0 | } |
543 | | |
544 | | static void |
545 | | rdt_close_context (PayloadContext *rdt) |
546 | 0 | { |
547 | 0 | int i; |
548 | |
|
549 | 0 | for (i = 0; i < rdt->nb_rmst; i++) |
550 | 0 | if (rdt->rmst[i]) { |
551 | 0 | ff_rm_free_rmstream(rdt->rmst[i]); |
552 | 0 | av_freep(&rdt->rmst[i]); |
553 | 0 | } |
554 | 0 | if (rdt->rmctx) |
555 | 0 | avformat_close_input(&rdt->rmctx); |
556 | 0 | av_freep(&rdt->mlti_data); |
557 | 0 | av_freep(&rdt->rmst); |
558 | 0 | } |
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); |