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