2 * Realmedia RTSP protocol (RDT) support.
3 * Copyright (c) 2007 Ronald S. Bultje
5 * This file is part of FFmpeg.
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.
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.
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
24 * @brief Realmedia RTSP protocol (RDT) support
25 * @author Ronald S. Bultje <rbultje@ronald.bitfreak.net>
29 #include "libavutil/avstring.h"
30 #include "rtp_internal.h"
32 #include "libavutil/base64.h"
33 #include "libavutil/md5.h"
37 typedef struct rdt_data
{
38 AVFormatContext
*rmctx
;
40 unsigned int mlti_data_size
;
41 uint32_t prev_sn
, prev_ts
;
42 char buffer
[RTP_MAX_PACKET_LENGTH
+ FF_INPUT_BUFFER_PADDING_SIZE
];
46 ff_rdt_calc_response_and_checksum(char response
[41], char chksum
[9],
47 const char *challenge
)
49 int ch_len
= strlen (challenge
), i
;
50 unsigned char zres
[16],
51 buf
[64] = { 0xa1, 0xe9, 0x14, 0x9d, 0x0e, 0x6b, 0x3b, 0x59 };
52 #define XOR_TABLE_SIZE 37
53 const unsigned char xor_table
[XOR_TABLE_SIZE
] = {
54 0x05, 0x18, 0x74, 0xd0, 0x0d, 0x09, 0x02, 0x53,
55 0xc0, 0x01, 0x05, 0x05, 0x67, 0x03, 0x19, 0x70,
56 0x08, 0x27, 0x66, 0x10, 0x10, 0x72, 0x08, 0x09,
57 0x63, 0x11, 0x03, 0x71, 0x08, 0x08, 0x70, 0x02,
58 0x10, 0x57, 0x05, 0x18, 0x54 };
60 /* some (length) checks */
61 if (ch_len
== 40) /* what a hack... */
65 memcpy(buf
+ 8, challenge
, ch_len
);
67 /* xor challenge bytewise with xor_table */
68 for (i
= 0; i
< XOR_TABLE_SIZE
; i
++)
69 buf
[8 + i
] ^= xor_table
[i
];
71 av_md5_sum(zres
, buf
, 64);
72 ff_data_to_hex(response
, zres
, 16);
73 for (i
=0;i
<32;i
++) response
[i
] = tolower(response
[i
]);
76 strcpy (response
+ 32, "01d0a8e3");
78 /* calculate checksum */
79 for (i
= 0; i
< 8; i
++)
80 chksum
[i
] = response
[i
* 4];
85 rdt_load_mdpr (rdt_data
*rdt
, AVStream
*st
, int rule_nr
)
92 * Layout of the MLTI chunk:
94 * 2:<number of streams>
95 * Then for each stream ([number_of_streams] times):
97 * 2:<number of mdpr chunks>
98 * Then for each mdpr chunk ([number_of_mdpr_chunks] times):
101 * we skip MDPR chunks until we reach the one of the stream
102 * we're interested in, and forward that ([size]+[data]) to
103 * the RM demuxer to parse the stream-specific header data.
107 url_open_buf(&pb
, rdt
->mlti_data
, rdt
->mlti_data_size
, URL_RDONLY
);
109 if (tag
== MKTAG('M', 'L', 'T', 'I')) {
112 /* read index of MDPR chunk numbers */
114 if (rule_nr
< 0 || rule_nr
>= num
)
116 url_fskip(pb
, rule_nr
* 2);
117 chunk_nr
= get_be16(pb
);
118 url_fskip(pb
, (num
- 1 - rule_nr
) * 2);
120 /* read MDPR chunks */
125 url_fskip(pb
, get_be32(pb
));
128 size
= rdt
->mlti_data_size
;
129 url_fseek(pb
, 0, SEEK_SET
);
132 if (ff_rm_read_mdpr_codecdata(rdt
->rmctx
, st
, size
) < 0)
140 * Actual data handling.
143 static int rdt_parse_header(struct RTPDemuxContext
*s
, const uint8_t *buf
,
144 int len
, int *seq
, uint32_t *timestamp
, int *flags
)
146 rdt_data
*rdt
= s
->dynamic_protocol_context
;
147 int consumed
= 0, sn
;
149 if (buf
[0] < 0x40 || buf
[0] > 0x42) {
154 sn
= (buf
[0]>>1) & 0x1f;
155 *seq
= AV_RB16(buf
+1);
156 *timestamp
= AV_RB32(buf
+4);
157 if (!(buf
[3] & 1) && (sn
!= rdt
->prev_sn
|| *timestamp
!= rdt
->prev_ts
)) {
158 *flags
|= PKT_FLAG_KEY
;
160 rdt
->prev_ts
= *timestamp
;
163 return consumed
+ 10;
166 /**< return 0 on packet, no more left, 1 on packet, 1 on partial packet... */
168 rdt_parse_packet (RTPDemuxContext
*s
, AVPacket
*pkt
, uint32_t *timestamp
,
169 const uint8_t *buf
, int len
, int flags
)
171 rdt_data
*rdt
= s
->dynamic_protocol_context
;
173 ByteIOContext
*pb
= rdt
->rmctx
->pb
;
174 RMContext
*rm
= rdt
->rmctx
->priv_data
;
175 AVStream
*st
= s
->st
;
177 if (rm
->audio_pkt_cnt
== 0) {
180 url_open_buf (&pb
, buf
, len
, URL_RDONLY
);
181 flags
= (flags
& PKT_FLAG_KEY
) ? 2 : 0;
183 res
= ff_rm_parse_packet (rdt
->rmctx
, st
, len
, pkt
,
184 &seq
, &flags
, timestamp
);
189 if (rm
->audio_pkt_cnt
> 0 &&
190 st
->codec
->codec_id
== CODEC_ID_AAC
) {
191 memcpy (rdt
->buffer
, buf
+ pos
, len
- pos
);
192 url_open_buf (&pb
, rdt
->buffer
, len
- pos
, URL_RDONLY
);
196 ff_rm_retrieve_cache (rdt
->rmctx
, st
, pkt
);
197 if (rm
->audio_pkt_cnt
== 0 &&
198 st
->codec
->codec_id
== CODEC_ID_AAC
)
201 pkt
->stream_index
= st
->index
;
202 pkt
->pts
= *timestamp
;
204 return rm
->audio_pkt_cnt
> 0;
208 ff_rdt_parse_packet(RTPDemuxContext
*s
, AVPacket
*pkt
,
209 const uint8_t *buf
, int len
)
216 /* return the next packets, if any */
217 timestamp
= 0; ///< Should not be used if buf is NULL, but should be set to the timestamp of the packet returned....
218 rv
= rdt_parse_packet(s
, pkt
, ×tamp
, NULL
, 0, flags
);
224 rv
= rdt_parse_header(s
, buf
, len
, &seq
, ×tamp
, &flags
);
231 rv
= rdt_parse_packet(s
, pkt
, ×tamp
, buf
, len
, flags
);
237 ff_rdt_subscribe_rule (RTPDemuxContext
*s
, char *cmd
, int size
,
238 int stream_nr
, int rule_nr
)
240 rdt_data
*rdt
= s
->dynamic_protocol_context
;
242 av_strlcatf(cmd
, size
, "stream=%d;rule=%d,stream=%d;rule=%d",
243 stream_nr
, rule_nr
, stream_nr
, rule_nr
+ 1);
245 rdt_load_mdpr(rdt
, s
->st
, 0);
248 static unsigned char *
249 rdt_parse_b64buf (unsigned int *target_len
, const char *p
)
251 unsigned char *target
;
255 len
-= 2; /* skip embracing " at start/end */
257 *target_len
= len
* 3 / 4;
258 target
= av_mallocz(*target_len
+ FF_INPUT_BUFFER_PADDING_SIZE
);
259 av_base64_decode(target
, p
, *target_len
);
264 rdt_parse_sdp_line (AVStream
*stream
, void *d
, const char *line
)
267 const char *p
= line
;
269 if (av_strstart(p
, "OpaqueData:buffer;", &p
)) {
270 rdt
->mlti_data
= rdt_parse_b64buf(&rdt
->mlti_data_size
, p
);
271 } else if (av_strstart(p
, "StartTime:integer;", &p
))
272 stream
->first_dts
= atoi(p
);
278 rdt_new_extradata (void)
280 rdt_data
*rdt
= av_mallocz(sizeof(rdt_data
));
282 av_open_input_stream(&rdt
->rmctx
, NULL
, "", &rdt_demuxer
, NULL
);
290 rdt_free_extradata (void *d
)
295 av_close_input_stream(rdt
->rmctx
);
296 av_freep(&rdt
->mlti_data
);
300 #define RDT_HANDLER(n, s, t) \
301 static RTPDynamicProtocolHandler ff_rdt_ ## n ## _handler = { \
305 rdt_parse_sdp_line, \
310 RDT_HANDLER(live_video
, "x-pn-multirate-realvideo-live", CODEC_TYPE_VIDEO
);
311 RDT_HANDLER(live_audio
, "x-pn-multirate-realaudio-live", CODEC_TYPE_AUDIO
);
312 RDT_HANDLER(video
, "x-pn-realvideo", CODEC_TYPE_VIDEO
);
313 RDT_HANDLER(audio
, "x-pn-realaudio", CODEC_TYPE_AUDIO
);
315 void av_register_rdt_dynamic_payload_handlers(void)
317 ff_register_dynamic_payload_handler(&ff_rdt_video_handler
);
318 ff_register_dynamic_payload_handler(&ff_rdt_audio_handler
);
319 ff_register_dynamic_payload_handler(&ff_rdt_live_video_handler
);
320 ff_register_dynamic_payload_handler(&ff_rdt_live_audio_handler
);