1 /*****************************************************************************
2 * real.c: real rtsp input
3 *****************************************************************************
4 * Copyright (C) 2002-2004 the xine project
5 * Copyright (C) 2005 VideoLAN
8 * Authors: Gildas Bazin <gbazin@videolan.org>
9 * Adapted from xine which itself adapted it from joschkas real tools.
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24 *****************************************************************************/
30 #include <vlc_common.h>
34 #include "real_sdpplin.h"
36 #define XOR_TABLE_LEN 37
37 static const unsigned char xor_table[] = {
38 0x05, 0x18, 0x74, 0xd0, 0x0d, 0x09, 0x02, 0x53,
39 0xc0, 0x01, 0x05, 0x05, 0x67, 0x03, 0x19, 0x70,
40 0x08, 0x27, 0x66, 0x10, 0x10, 0x72, 0x08, 0x09,
41 0x63, 0x11, 0x03, 0x71, 0x08, 0x08, 0x70, 0x02,
42 0x10, 0x57, 0x05, 0x18, 0x54, 0x00, 0x00, 0x00 };
44 #define BE_32(x) GetDWBE(x)
45 #define LE_32(x) GetDWLE(x)
46 #define BE_16(x) GetWBE(x)
47 #define LE_16(x) GetWLE(x)
48 #define BE_32C(x,y) do {uint32_t in=y; *(uint32_t *)(x)=GetDWBE(&in);} while(0)
49 #define LE_32C(x,y) do {uint32_t in=y; *(uint32_t *)(x)=GetDWLE(&in);} while(0)
50 #define MAX(x,y) ((x>y) ? x : y)
53 static void hash(char *field, char *param)
63 lprintf("hash input: %x %x %x %x\n", a, b, c, d);
64 lprintf("hash parameter:\n");
66 a = ((b & c) | (~b & d)) + LE_32((param+0x00)) + a - 0x28955B88;
67 a = ((a << 0x07) | (a >> 0x19)) + b;
68 d = ((a & b) | (~a & c)) + LE_32((param+0x04)) + d - 0x173848AA;
69 d = ((d << 0x0c) | (d >> 0x14)) + a;
70 c = ((d & a) | (~d & b)) + LE_32((param+0x08)) + c + 0x242070DB;
71 c = ((c << 0x11) | (c >> 0x0f)) + d;
72 b = ((c & d) | (~c & a)) + LE_32((param+0x0c)) + b - 0x3E423112;
73 b = ((b << 0x16) | (b >> 0x0a)) + c;
74 a = ((b & c) | (~b & d)) + LE_32((param+0x10)) + a - 0x0A83F051;
75 a = ((a << 0x07) | (a >> 0x19)) + b;
76 d = ((a & b) | (~a & c)) + LE_32((param+0x14)) + d + 0x4787C62A;
77 d = ((d << 0x0c) | (d >> 0x14)) + a;
78 c = ((d & a) | (~d & b)) + LE_32((param+0x18)) + c - 0x57CFB9ED;
79 c = ((c << 0x11) | (c >> 0x0f)) + d;
80 b = ((c & d) | (~c & a)) + LE_32((param+0x1c)) + b - 0x02B96AFF;
81 b = ((b << 0x16) | (b >> 0x0a)) + c;
82 a = ((b & c) | (~b & d)) + LE_32((param+0x20)) + a + 0x698098D8;
83 a = ((a << 0x07) | (a >> 0x19)) + b;
84 d = ((a & b) | (~a & c)) + LE_32((param+0x24)) + d - 0x74BB0851;
85 d = ((d << 0x0c) | (d >> 0x14)) + a;
86 c = ((d & a) | (~d & b)) + LE_32((param+0x28)) + c - 0x0000A44F;
87 c = ((c << 0x11) | (c >> 0x0f)) + d;
88 b = ((c & d) | (~c & a)) + LE_32((param+0x2C)) + b - 0x76A32842;
89 b = ((b << 0x16) | (b >> 0x0a)) + c;
90 a = ((b & c) | (~b & d)) + LE_32((param+0x30)) + a + 0x6B901122;
91 a = ((a << 0x07) | (a >> 0x19)) + b;
92 d = ((a & b) | (~a & c)) + LE_32((param+0x34)) + d - 0x02678E6D;
93 d = ((d << 0x0c) | (d >> 0x14)) + a;
94 c = ((d & a) | (~d & b)) + LE_32((param+0x38)) + c - 0x5986BC72;
95 c = ((c << 0x11) | (c >> 0x0f)) + d;
96 b = ((c & d) | (~c & a)) + LE_32((param+0x3c)) + b + 0x49B40821;
97 b = ((b << 0x16) | (b >> 0x0a)) + c;
99 a = ((b & d) | (~d & c)) + LE_32((param+0x04)) + a - 0x09E1DA9E;
100 a = ((a << 0x05) | (a >> 0x1b)) + b;
101 d = ((a & c) | (~c & b)) + LE_32((param+0x18)) + d - 0x3FBF4CC0;
102 d = ((d << 0x09) | (d >> 0x17)) + a;
103 c = ((d & b) | (~b & a)) + LE_32((param+0x2c)) + c + 0x265E5A51;
104 c = ((c << 0x0e) | (c >> 0x12)) + d;
105 b = ((c & a) | (~a & d)) + LE_32((param+0x00)) + b - 0x16493856;
106 b = ((b << 0x14) | (b >> 0x0c)) + c;
107 a = ((b & d) | (~d & c)) + LE_32((param+0x14)) + a - 0x29D0EFA3;
108 a = ((a << 0x05) | (a >> 0x1b)) + b;
109 d = ((a & c) | (~c & b)) + LE_32((param+0x28)) + d + 0x02441453;
110 d = ((d << 0x09) | (d >> 0x17)) + a;
111 c = ((d & b) | (~b & a)) + LE_32((param+0x3c)) + c - 0x275E197F;
112 c = ((c << 0x0e) | (c >> 0x12)) + d;
113 b = ((c & a) | (~a & d)) + LE_32((param+0x10)) + b - 0x182C0438;
114 b = ((b << 0x14) | (b >> 0x0c)) + c;
115 a = ((b & d) | (~d & c)) + LE_32((param+0x24)) + a + 0x21E1CDE6;
116 a = ((a << 0x05) | (a >> 0x1b)) + b;
117 d = ((a & c) | (~c & b)) + LE_32((param+0x38)) + d - 0x3CC8F82A;
118 d = ((d << 0x09) | (d >> 0x17)) + a;
119 c = ((d & b) | (~b & a)) + LE_32((param+0x0c)) + c - 0x0B2AF279;
120 c = ((c << 0x0e) | (c >> 0x12)) + d;
121 b = ((c & a) | (~a & d)) + LE_32((param+0x20)) + b + 0x455A14ED;
122 b = ((b << 0x14) | (b >> 0x0c)) + c;
123 a = ((b & d) | (~d & c)) + LE_32((param+0x34)) + a - 0x561C16FB;
124 a = ((a << 0x05) | (a >> 0x1b)) + b;
125 d = ((a & c) | (~c & b)) + LE_32((param+0x08)) + d - 0x03105C08;
126 d = ((d << 0x09) | (d >> 0x17)) + a;
127 c = ((d & b) | (~b & a)) + LE_32((param+0x1c)) + c + 0x676F02D9;
128 c = ((c << 0x0e) | (c >> 0x12)) + d;
129 b = ((c & a) | (~a & d)) + LE_32((param+0x30)) + b - 0x72D5B376;
130 b = ((b << 0x14) | (b >> 0x0c)) + c;
132 a = (b ^ c ^ d) + LE_32((param+0x14)) + a - 0x0005C6BE;
133 a = ((a << 0x04) | (a >> 0x1c)) + b;
134 d = (a ^ b ^ c) + LE_32((param+0x20)) + d - 0x788E097F;
135 d = ((d << 0x0b) | (d >> 0x15)) + a;
136 c = (d ^ a ^ b) + LE_32((param+0x2c)) + c + 0x6D9D6122;
137 c = ((c << 0x10) | (c >> 0x10)) + d;
138 b = (c ^ d ^ a) + LE_32((param+0x38)) + b - 0x021AC7F4;
139 b = ((b << 0x17) | (b >> 0x09)) + c;
140 a = (b ^ c ^ d) + LE_32((param+0x04)) + a - 0x5B4115BC;
141 a = ((a << 0x04) | (a >> 0x1c)) + b;
142 d = (a ^ b ^ c) + LE_32((param+0x10)) + d + 0x4BDECFA9;
143 d = ((d << 0x0b) | (d >> 0x15)) + a;
144 c = (d ^ a ^ b) + LE_32((param+0x1c)) + c - 0x0944B4A0;
145 c = ((c << 0x10) | (c >> 0x10)) + d;
146 b = (c ^ d ^ a) + LE_32((param+0x28)) + b - 0x41404390;
147 b = ((b << 0x17) | (b >> 0x09)) + c;
148 a = (b ^ c ^ d) + LE_32((param+0x34)) + a + 0x289B7EC6;
149 a = ((a << 0x04) | (a >> 0x1c)) + b;
150 d = (a ^ b ^ c) + LE_32((param+0x00)) + d - 0x155ED806;
151 d = ((d << 0x0b) | (d >> 0x15)) + a;
152 c = (d ^ a ^ b) + LE_32((param+0x0c)) + c - 0x2B10CF7B;
153 c = ((c << 0x10) | (c >> 0x10)) + d;
154 b = (c ^ d ^ a) + LE_32((param+0x18)) + b + 0x04881D05;
155 b = ((b << 0x17) | (b >> 0x09)) + c;
156 a = (b ^ c ^ d) + LE_32((param+0x24)) + a - 0x262B2FC7;
157 a = ((a << 0x04) | (a >> 0x1c)) + b;
158 d = (a ^ b ^ c) + LE_32((param+0x30)) + d - 0x1924661B;
159 d = ((d << 0x0b) | (d >> 0x15)) + a;
160 c = (d ^ a ^ b) + LE_32((param+0x3c)) + c + 0x1fa27cf8;
161 c = ((c << 0x10) | (c >> 0x10)) + d;
162 b = (c ^ d ^ a) + LE_32((param+0x08)) + b - 0x3B53A99B;
163 b = ((b << 0x17) | (b >> 0x09)) + c;
165 a = ((~d | b) ^ c) + LE_32((param+0x00)) + a - 0x0BD6DDBC;
166 a = ((a << 0x06) | (a >> 0x1a)) + b;
167 d = ((~c | a) ^ b) + LE_32((param+0x1c)) + d + 0x432AFF97;
168 d = ((d << 0x0a) | (d >> 0x16)) + a;
169 c = ((~b | d) ^ a) + LE_32((param+0x38)) + c - 0x546BDC59;
170 c = ((c << 0x0f) | (c >> 0x11)) + d;
171 b = ((~a | c) ^ d) + LE_32((param+0x14)) + b - 0x036C5FC7;
172 b = ((b << 0x15) | (b >> 0x0b)) + c;
173 a = ((~d | b) ^ c) + LE_32((param+0x30)) + a + 0x655B59C3;
174 a = ((a << 0x06) | (a >> 0x1a)) + b;
175 d = ((~c | a) ^ b) + LE_32((param+0x0C)) + d - 0x70F3336E;
176 d = ((d << 0x0a) | (d >> 0x16)) + a;
177 c = ((~b | d) ^ a) + LE_32((param+0x28)) + c - 0x00100B83;
178 c = ((c << 0x0f) | (c >> 0x11)) + d;
179 b = ((~a | c) ^ d) + LE_32((param+0x04)) + b - 0x7A7BA22F;
180 b = ((b << 0x15) | (b >> 0x0b)) + c;
181 a = ((~d | b) ^ c) + LE_32((param+0x20)) + a + 0x6FA87E4F;
182 a = ((a << 0x06) | (a >> 0x1a)) + b;
183 d = ((~c | a) ^ b) + LE_32((param+0x3c)) + d - 0x01D31920;
184 d = ((d << 0x0a) | (d >> 0x16)) + a;
185 c = ((~b | d) ^ a) + LE_32((param+0x18)) + c - 0x5CFEBCEC;
186 c = ((c << 0x0f) | (c >> 0x11)) + d;
187 b = ((~a | c) ^ d) + LE_32((param+0x34)) + b + 0x4E0811A1;
188 b = ((b << 0x15) | (b >> 0x0b)) + c;
189 a = ((~d | b) ^ c) + LE_32((param+0x10)) + a - 0x08AC817E;
190 a = ((a << 0x06) | (a >> 0x1a)) + b;
191 d = ((~c | a) ^ b) + LE_32((param+0x2c)) + d - 0x42C50DCB;
192 d = ((d << 0x0a) | (d >> 0x16)) + a;
193 c = ((~b | d) ^ a) + LE_32((param+0x08)) + c + 0x2AD7D2BB;
194 c = ((c << 0x0f) | (c >> 0x11)) + d;
195 b = ((~a | c) ^ d) + LE_32((param+0x24)) + b - 0x14792C6F;
196 b = ((b << 0x15) | (b >> 0x0b)) + c;
198 lprintf("hash output: %x %x %x %x\n", a, b, c, d);
203 d += LE_32(field+12);
211 static void call_hash (char *key, char *challenge, unsigned int len) {
212 uint8_t *ptr1, *ptr2;
213 uint32_t a, b, c, d, tmp;
215 ptr1=(uint8_t*)(key+16);
216 ptr2=(uint8_t*)(key+20);
225 lprintf("not verified: (len << 3) > a true\n");
229 tmp = LE_32(ptr2) + (len >> 0x1d);
235 memcpy(key+b+24, challenge, a);
241 lprintf("not verified: while ( d < len )\n");
242 hash(key, challenge+d-0x3f);
249 memcpy(key+b+24, challenge+c, len-c);
252 static void calc_response (char *result, char *field) {
257 memset (buf1, 0, 64);
260 memcpy (buf2, field+16, 8);
261 i = ( LE_32((buf2)) >> 3 ) & 0x3f;
268 lprintf("not verified: ! (i < 56)\n");
272 call_hash (field, buf1, i);
273 call_hash (field, buf2, 8);
274 memcpy (result, field, 16);
277 static void calc_response_string (char *result, char *challenge) {
280 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF,
281 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
282 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
283 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
288 /* calculate response */
289 call_hash(field, challenge, 64);
290 calc_response(zres,field);
292 /* convert zres to ascii string */
293 for (i=0; i<16; i++ ) {
296 a = (zres[i] >> 4) & 15;
299 result[i*2] = ((a<10) ? (a+48) : (a+87)) & 255;
300 result[i*2+1] = ((b<10) ? (b+48) : (b+87)) & 255;
304 void real_calc_response_and_checksum (char *response, char *chksum, char *challenge) {
306 int ch_len, resp_len;
311 /* initialize return values */
312 memset(response, 0, 64);
313 memset(chksum, 0, 34);
315 /* initialize buffer */
318 BE_32C(ptr, 0xa1e9149d);
320 BE_32C(ptr, 0x0e6b3b59);
323 /* some (length) checks */
324 if (challenge != NULL)
326 ch_len = strlen (challenge);
328 if (ch_len == 40) /* what a hack... */
333 if ( ch_len > 56 ) ch_len=56;
335 /* copy challenge to buf */
336 memcpy(ptr, challenge, ch_len);
339 /* xor challenge bytewise with xor_table */
340 for (i=0; i<XOR_TABLE_LEN; i++)
341 ptr[i] = ptr[i] ^ xor_table[i];
343 calc_response_string (response, buf);
346 resp_len = strlen (response);
347 strcpy (&response[resp_len], "01d0a8e3");
349 /* calculate checksum */
350 for (i=0; i<resp_len/4; i++)
351 chksum[i] = response[i*4];
356 * takes a MLTI-Chunk and a rule number got from match_asm_rule,
357 * returns a pointer to selected data and number of bytes in that.
359 static int select_mlti_data(const char *mlti_chunk, int mlti_size, int selection, char **out) {
361 int numrules, codec, size;
364 /* MLTI chunk should begin with MLTI */
365 if ((mlti_chunk[0] != 'M')
366 ||(mlti_chunk[1] != 'L')
367 ||(mlti_chunk[2] != 'T')
368 ||(mlti_chunk[3] != 'I'))
370 lprintf("MLTI tag not detected, copying data\n");
371 memcpy(*out, mlti_chunk, mlti_size);
377 /* next 16 bits are the number of rules */
378 numrules=BE_16(mlti_chunk);
379 if (selection >= numrules) return 0;
381 /* now <numrules> indices of codecs follows */
382 /* we skip to selection */
383 mlti_chunk+=(selection+1)*2;
386 codec=BE_16(mlti_chunk);
388 /* skip to number of codecs */
389 mlti_chunk+=(numrules-selection)*2;
391 /* get number of codecs */
392 numrules=BE_16(mlti_chunk);
394 if (codec >= numrules) {
395 lprintf("codec index >= number of codecs. %i %i\n", codec, numrules);
401 /* now seek to selected codec */
402 for (i=0; i<codec; i++) {
403 size=BE_32(mlti_chunk);
406 size=BE_32(mlti_chunk);
408 memcpy(*out, mlti_chunk+4, size);
413 * looking at stream description.
416 rmff_header_t *real_parse_sdp(char *data, char **stream_rules, uint32_t bandwidth) {
418 sdpplin_t *desc = NULL;
419 rmff_header_t *header = NULL;
424 int max_packet_size=0;
425 int avg_packet_size=0;
428 if( !data ) return NULL;
430 desc=sdpplin_parse(data);
431 if( !desc ) return NULL;
433 buf= (char *)malloc(2048);
434 if( !buf ) goto error;
436 header = calloc( 1, sizeof(rmff_header_t) );
437 if( !header ) goto error;
439 header->fileheader=rmff_new_fileheader(4+desc->stream_count);
440 header->cont=rmff_new_cont(
446 header->data=rmff_new_dataheader(0,0);
447 if( !header->data ) goto error;
449 header->streams = calloc( desc->stream_count+1, sizeof(rmff_mdpr_t*) );
450 if( !header->streams ) goto error;
452 lprintf("number of streams: %u\n", desc->stream_count);
454 for (i=0; i<desc->stream_count; i++) {
461 lprintf("calling asmrp_match with:\n%s\n%u\n", desc->stream[i]->asm_rule_book, bandwidth);
463 n=asmrp_match(desc->stream[i]->asm_rule_book, bandwidth, rulematches, sizeof(rulematches)/sizeof(rulematches[0]));
464 for (j=0; j<n; j++) {
465 lprintf("asmrp rule match: %u for stream %u\n", rulematches[j], desc->stream[i]->stream_id);
466 sprintf(b,"stream=%u;rule=%u,", desc->stream[i]->stream_id, rulematches[j]);
467 strcat(*stream_rules, b);
470 if (!desc->stream[i]->mlti_data) {
475 len=select_mlti_data(desc->stream[i]->mlti_data,
476 desc->stream[i]->mlti_data_size, rulematches[0], &buf);
478 header->streams[i]=rmff_new_mdpr(
479 desc->stream[i]->stream_id,
480 desc->stream[i]->max_bit_rate,
481 desc->stream[i]->avg_bit_rate,
482 desc->stream[i]->max_packet_size,
483 desc->stream[i]->avg_packet_size,
484 desc->stream[i]->start_time,
485 desc->stream[i]->preroll,
486 desc->stream[i]->duration,
487 desc->stream[i]->stream_name,
488 desc->stream[i]->mime_type,
491 if( !header->streams[i] ) goto error;
493 duration=MAX(duration,desc->stream[i]->duration);
494 max_bit_rate+=desc->stream[i]->max_bit_rate;
495 avg_bit_rate+=desc->stream[i]->avg_bit_rate;
496 max_packet_size=MAX(max_packet_size, desc->stream[i]->max_packet_size);
498 avg_packet_size=(avg_packet_size + desc->stream[i]->avg_packet_size) / 2;
500 avg_packet_size=desc->stream[i]->avg_packet_size;
503 if (*stream_rules && strlen(*stream_rules) && (*stream_rules)[strlen(*stream_rules)-1] == ',')
504 (*stream_rules)[strlen(*stream_rules)-1]=0; /* delete last ',' in stream_rules */
506 header->prop=rmff_new_prop(
518 if( !header->prop ) goto error;
520 rmff_fix_header(header);
522 sdpplin_free( desc );
527 sdpplin_free( desc );
528 rmff_free_header( header );
533 int real_get_rdt_chunk_header(rtsp_client_t *rtsp_session, rmff_pheader_t *ph) {
542 n=rtsp_read_data(rtsp_session, header, 8);
544 if (header[0] != 0x24)
546 lprintf("rdt chunk not recognized: got 0x%02x\n", header[0]);
549 size=(header[1]<<16)+(header[2]<<8)+(header[3]);
551 if ((flags1!=0x40)&&(flags1!=0x42))
553 lprintf("got flags1: 0x%02x\n",flags1);
556 lprintf("got end of stream packet\n");
562 n=rtsp_read_data(rtsp_session, header+3, 5);
564 lprintf("ignoring bytes:\n");
565 n=rtsp_read_data(rtsp_session, header+4, 4);
570 unknown1=(header[5]<<16)+(header[6]<<8)+(header[7]);
571 n=rtsp_read_data(rtsp_session, header, 6);
576 lprintf("ts: %u size: %u, flags: 0x%02x, unknown values: %u 0x%02x 0x%02x\n",
577 ts, size, flags1, unknown1, header[4], header[5]);
581 ph->object_version=0;
583 ph->stream_number=(flags1>>1)&1;
586 ph->flags=0; /* TODO: determine keyframe flag and insert here? */
590 int real_get_rdt_chunk(rtsp_client_t *rtsp_session, rmff_pheader_t *ph,
591 unsigned char **buffer) {
594 rmff_dump_pheader(ph, (char*)*buffer);
595 n=rtsp_read_data(rtsp_session, (uint8_t*)(*buffer + 12), ph->length - 12);
596 return (n <= 0) ? 0 : n+12;
599 //! maximum size of the rtsp description, must be < INT_MAX
600 #define MAX_DESC_BUF (20 * 1024 * 1024)
601 rmff_header_t *real_setup_and_get_header(rtsp_client_t *rtsp_session, int bandwidth) {
603 char *description=NULL;
604 char *session_id=NULL;
605 rmff_header_t *h=NULL;
606 char *challenge1 = NULL;
609 char *subscribe=NULL;
610 char *buf = malloc(256);
613 char *mrl=rtsp_get_mrl(rtsp_session);
618 challenge1=strdup(rtsp_search_answers(rtsp_session,"RealChallenge1"));
619 lprintf("Challenge1: %s\n", challenge1);
621 /* request stream description */
622 rtsp_schedule_field(rtsp_session, "Accept: application/sdp");
623 sprintf(buf, "Bandwidth: %u", bandwidth);
624 rtsp_schedule_field(rtsp_session, buf);
625 rtsp_schedule_field(rtsp_session, "GUID: 00000000-0000-0000-0000-000000000000");
626 rtsp_schedule_field(rtsp_session, "RegionData: 0");
627 rtsp_schedule_field(rtsp_session, "ClientID: Linux_2.4_6.0.9.1235_play32_RN01_EN_586");
628 rtsp_schedule_field(rtsp_session, "SupportsMaximumASMBandwidth: 1");
629 rtsp_schedule_field(rtsp_session, "Language: en-US");
630 rtsp_schedule_field(rtsp_session, "Require: com.real.retain-entity-for-setup");
632 status=rtsp_request_describe(rtsp_session,NULL);
633 if ( status<200 || status>299 ) {
634 char *alert=rtsp_search_answers(rtsp_session,"Alert");
636 lprintf("real: got message from server:\n%s\n", alert);
638 rtsp_send_ok( rtsp_session );
645 /* receive description */
647 if (!rtsp_search_answers(rtsp_session,"Content-length"))
648 lprintf("real: got no Content-length!\n");
650 size=atoi(rtsp_search_answers(rtsp_session,"Content-length"));
652 if (size > MAX_DESC_BUF) {
653 printf("real: Content-length for description too big (> %uMB)!\n",
654 MAX_DESC_BUF/(1024*1024) );
658 if (!rtsp_search_answers(rtsp_session,"ETag"))
659 lprintf("real: got no ETag!\n");
661 session_id=strdup(rtsp_search_answers(rtsp_session,"ETag"));
663 lprintf("Stream description size: %i\n", size);
665 description = malloc(size+1);
668 if( rtsp_read_data(rtsp_session, (uint8_t*)description, size) <= 0)
671 //fprintf(stderr, "%s", description);
673 /* parse sdp (sdpplin) and create a header and a subscribe string */
674 subscribe = malloc(256);
678 strcpy(subscribe, "Subscribe: ");
679 h=real_parse_sdp(description, &subscribe, bandwidth);
686 fprintf("Title: %s\nCopyright: %s\nAuthor: %s\nStreams: %i\n",
687 h->cont->title, h->cont->copyright, h->cont->author, h->prop->num_streams);
690 /* setup our streams */
691 real_calc_response_and_checksum (challenge2, checksum, challenge1);
692 buf = realloc(buf, strlen(challenge2) + strlen(checksum) + 32);
693 sprintf(buf, "RealChallenge2: %s, sd=%s", challenge2, checksum);
694 rtsp_schedule_field(rtsp_session, buf);
695 buf = realloc(buf, strlen(session_id) + 32);
696 sprintf(buf, "If-Match: %s", session_id);
697 rtsp_schedule_field(rtsp_session, buf);
698 rtsp_schedule_field(rtsp_session, "Transport: x-pn-tng/tcp;mode=play,rtp/avp/tcp;unicast;mode=play");
699 buf = realloc(buf, strlen(mrl) + 32);
700 sprintf(buf, "%s/streamid=0", mrl);
701 rtsp_request_setup(rtsp_session,buf);
703 if (h->prop->num_streams > 1) {
704 rtsp_schedule_field(rtsp_session, "Transport: x-pn-tng/tcp;mode=play,rtp/avp/tcp;unicast;mode=play");
705 buf = realloc(buf, strlen(session_id) + 32);
706 sprintf(buf, "If-Match: %s", session_id);
707 rtsp_schedule_field(rtsp_session, buf);
708 buf = realloc(buf, strlen(mrl) + 32);
709 sprintf(buf, "%s/streamid=1", mrl);
710 rtsp_request_setup(rtsp_session,buf);
712 /* set stream parameter (bandwidth) with our subscribe string */
713 rtsp_schedule_field(rtsp_session, subscribe);
714 rtsp_request_setparameter(rtsp_session,NULL);
716 /* and finally send a play request */
717 rtsp_schedule_field(rtsp_session, "Range: npt=0-");
718 rtsp_request_play(rtsp_session,NULL);
728 rmff_free_header( h );