Add dynamic payload handlers to rdt.c. These follow the same API as the ones
[ffmpeg.git] / libavformat / rdt.c
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 rdt.c
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 "rtp_internal.h"
31 #include "rdt.h"
32 #include "libavutil/base64.h"
33 #include "libavutil/md5.h"
34 #include "rm.h"
35 #include "internal.h"
36
37 typedef struct rdt_data {
38     AVFormatContext *rmctx;
39     uint8_t *mlti_data;
40     unsigned int mlti_data_size;
41 } rdt_data;
42
43 void
44 ff_rdt_calc_response_and_checksum(char response[41], char chksum[9],
45                                   const char *challenge)
46 {
47     int ch_len = strlen (challenge), i;
48     unsigned char zres[16],
49         buf[64] = { 0xa1, 0xe9, 0x14, 0x9d, 0x0e, 0x6b, 0x3b, 0x59 };
50 #define XOR_TABLE_SIZE 37
51     const unsigned char xor_table[XOR_TABLE_SIZE] = {
52         0x05, 0x18, 0x74, 0xd0, 0x0d, 0x09, 0x02, 0x53,
53         0xc0, 0x01, 0x05, 0x05, 0x67, 0x03, 0x19, 0x70,
54         0x08, 0x27, 0x66, 0x10, 0x10, 0x72, 0x08, 0x09,
55         0x63, 0x11, 0x03, 0x71, 0x08, 0x08, 0x70, 0x02,
56         0x10, 0x57, 0x05, 0x18, 0x54 };
57
58     /* some (length) checks */
59     if (ch_len == 40) /* what a hack... */
60         ch_len = 32;
61     else if (ch_len > 56)
62         ch_len = 56;
63     memcpy(buf + 8, challenge, ch_len);
64
65     /* xor challenge bytewise with xor_table */
66     for (i = 0; i < XOR_TABLE_SIZE; i++)
67         buf[8 + i] ^= xor_table[i];
68
69     av_md5_sum(zres, buf, 64);
70     ff_data_to_hex(response, zres, 16);
71     for (i=0;i<32;i++) response[i] = tolower(response[i]);
72
73     /* add tail */
74     strcpy (response + 32, "01d0a8e3");
75
76     /* calculate checksum */
77     for (i = 0; i < 8; i++)
78         chksum[i] = response[i * 4];
79     chksum[8] = 0;
80 }
81
82 static int
83 rdt_load_mdpr (rdt_data *rdt, AVStream *st, int rule_nr)
84 {
85     ByteIOContext *pb;
86     int size;
87     uint32_t tag;
88
89     /**
90      * Layout of the MLTI chunk:
91      * 4:MLTI
92      * 2:<number of streams>
93      * Then for each stream ([number_of_streams] times):
94      *     2:<mdpr index>
95      * 2:<number of mdpr chunks>
96      * Then for each mdpr chunk ([number_of_mdpr_chunks] times):
97      *     4:<size>
98      *     [size]:<data>
99      * we skip MDPR chunks until we reach the one of the stream
100      * we're interested in, and forward that ([size]+[data]) to
101      * the RM demuxer to parse the stream-specific header data.
102      */
103     if (!rdt->mlti_data)
104         return -1;
105     url_open_buf(&pb, rdt->mlti_data, rdt->mlti_data_size, URL_RDONLY);
106     tag = get_le32(pb);
107     if (tag == MKTAG('M', 'L', 'T', 'I')) {
108         int num, chunk_nr;
109
110         /* read index of MDPR chunk numbers */
111         num = get_be16(pb);
112         if (rule_nr < 0 || rule_nr >= num)
113             return -1;
114         url_fskip(pb, rule_nr * 2);
115         chunk_nr = get_be16(pb);
116         url_fskip(pb, (num - 1 - rule_nr) * 2);
117
118         /* read MDPR chunks */
119         num = get_be16(pb);
120         if (chunk_nr >= num)
121             return -1;
122         while (chunk_nr--)
123             url_fskip(pb, get_be32(pb));
124         size = get_be32(pb);
125     } else {
126         size = rdt->mlti_data_size;
127         url_fseek(pb, 0, SEEK_SET);
128     }
129     rdt->rmctx->pb = pb;
130     if (ff_rm_read_mdpr_codecdata(rdt->rmctx, st, size) < 0)
131         return -1;
132
133     url_close_buf(pb);
134     return 0;
135 }
136
137 static unsigned char *
138 rdt_parse_b64buf (unsigned int *target_len, const char *p)
139 {
140     unsigned char *target;
141     int len = strlen(p);
142     if (*p == '\"') {
143         p++;
144         len -= 2; /* skip embracing " at start/end */
145     }
146     *target_len = len * 3 / 4;
147     target = av_mallocz(*target_len + FF_INPUT_BUFFER_PADDING_SIZE);
148     av_base64_decode(target, p, *target_len);
149     return target;
150 }
151
152 static int
153 rdt_parse_sdp_line (AVStream *stream, void *d, const char *line)
154 {
155     rdt_data *rdt = d;
156     const char *p = line;
157
158     if (av_strstart(p, "OpaqueData:buffer;", &p)) {
159         rdt->mlti_data = rdt_parse_b64buf(&rdt->mlti_data_size, p);
160         rdt_load_mdpr(rdt, stream, 0);
161     } else if (av_strstart(p, "StartTime:integer;", &p))
162         stream->first_dts = atoi(p);
163
164     return 0;
165 }
166
167 static void *
168 rdt_new_extradata (void)
169 {
170     rdt_data *rdt = av_mallocz(sizeof(rdt_data));
171
172     av_open_input_stream(&rdt->rmctx, NULL, "", &rdt_demuxer, NULL);
173
174     return rdt;
175 }
176
177 static void
178 rdt_free_extradata (void *d)
179 {
180     rdt_data *rdt = d;
181
182     if (rdt->rmctx)
183         av_close_input_stream(rdt->rmctx);
184     av_freep(&rdt->mlti_data);
185     av_free(rdt);
186 }
187
188 #define RDT_HANDLER(n, s, t) \
189 static RTPDynamicProtocolHandler ff_rdt_ ## n ## _handler = { \
190     s, \
191     t, \
192     CODEC_ID_NONE, \
193     rdt_parse_sdp_line, \
194     rdt_new_extradata, \
195     rdt_free_extradata \
196 };
197
198 RDT_HANDLER(live_video, "x-pn-multirate-realvideo-live", CODEC_TYPE_VIDEO);
199 RDT_HANDLER(live_audio, "x-pn-multirate-realaudio-live", CODEC_TYPE_AUDIO);
200 RDT_HANDLER(video,      "x-pn-realvideo",                CODEC_TYPE_VIDEO);
201 RDT_HANDLER(audio,      "x-pn-realaudio",                CODEC_TYPE_AUDIO);
202
203 void av_register_rdt_dynamic_payload_handlers(void)
204 {
205     ff_register_dynamic_payload_handler(&ff_rdt_video_handler);
206     ff_register_dynamic_payload_handler(&ff_rdt_audio_handler);
207     ff_register_dynamic_payload_handler(&ff_rdt_live_video_handler);
208     ff_register_dynamic_payload_handler(&ff_rdt_live_audio_handler);
209 }