2 * This file was ported to MPlayer from xine CVS real.c,v 1.8 2003/03/30 17:11:50
6 * Copyright (C) 2002 the xine project
8 * This file is part of xine, a free video player.
10 * xine is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * xine is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
25 * special functions for real streams.
26 * adopted from joschkas real tools.
34 #include "libavutil/common.h"
39 #include "libavutil/md5.h"
40 #include "libavutil/intreadwrite.h"
41 #include "stream/http.h"
48 #define XOR_TABLE_SIZE 37
50 static const unsigned char xor_table
[XOR_TABLE_SIZE
] = {
51 0x05, 0x18, 0x74, 0xd0, 0x0d, 0x09, 0x02, 0x53,
52 0xc0, 0x01, 0x05, 0x05, 0x67, 0x03, 0x19, 0x70,
53 0x08, 0x27, 0x66, 0x10, 0x10, 0x72, 0x08, 0x09,
54 0x63, 0x11, 0x03, 0x71, 0x08, 0x08, 0x70, 0x02,
55 0x10, 0x57, 0x05, 0x18, 0x54 };
61 static void hexdump (const char *buf
, int length
) {
65 printf (" hexdump> ");
66 for (i
= 0; i
< length
; i
++) {
67 unsigned char c
= buf
[i
];
83 static void real_calc_response_and_checksum (char *response
, char *chksum
, char *challenge
) {
87 unsigned char zres
[16], buf
[64];
89 /* initialize buffer */
90 AV_WB32(buf
, 0xa1e9149d);
91 AV_WB32(buf
+4, 0x0e6b3b59);
93 /* some (length) checks */
94 if (challenge
!= NULL
)
96 ch_len
= strlen (challenge
);
98 if (ch_len
== 40) /* what a hack... */
100 if ( ch_len
> 56 ) ch_len
=56;
102 /* copy challenge to buf */
103 memcpy(buf
+8, challenge
, ch_len
);
104 memset(buf
+8+ch_len
, 0, 56-ch_len
);
107 /* xor challenge bytewise with xor_table */
108 for (i
=0; i
<XOR_TABLE_SIZE
; i
++)
109 buf
[8+i
] ^= xor_table
[i
];
111 av_md5_sum(zres
, buf
, 64);
113 /* convert zres to ascii string */
114 for (i
=0; i
<16; i
++ )
115 sprintf(response
+i
*2, "%02x", zres
[i
]);
118 strcpy (&response
[32], "01d0a8e3");
120 /* calculate checksum */
122 chksum
[i
] = response
[i
*4];
128 * takes a MLTI-Chunk and a rule number got from match_asm_rule,
129 * returns a pointer to selected data and number of bytes in that.
132 static int select_mlti_data(const char *mlti_chunk
, int mlti_size
, int selection
, char **out
) {
134 int numrules
, codec
, size
;
137 /* MLTI chunk should begin with MLTI */
139 if ((mlti_chunk
[0] != 'M')
140 ||(mlti_chunk
[1] != 'L')
141 ||(mlti_chunk
[2] != 'T')
142 ||(mlti_chunk
[3] != 'I'))
145 printf("libreal: MLTI tag not detected, copying data\n");
147 *out
= xbuffer_copyin(*out
, 0, mlti_chunk
, mlti_size
);
153 /* next 16 bits are the number of rules */
154 numrules
=AV_RB16(mlti_chunk
);
155 if (selection
>= numrules
) return 0;
157 /* now <numrules> indices of codecs follows */
158 /* we skip to selection */
159 mlti_chunk
+=(selection
+1)*2;
162 codec
=AV_RB16(mlti_chunk
);
164 /* skip to number of codecs */
165 mlti_chunk
+=(numrules
-selection
)*2;
167 /* get number of codecs */
168 numrules
=AV_RB16(mlti_chunk
);
170 if (codec
>= numrules
) {
171 mp_msg(MSGT_STREAM
, MSGL_WARN
, "realrtsp: codec index >= number of codecs. %i %i\n",
178 /* now seek to selected codec */
179 for (i
=0; i
<codec
; i
++) {
180 size
=AV_RB32(mlti_chunk
);
184 size
=AV_RB32(mlti_chunk
);
187 hexdump(mlti_chunk
+4, size
);
189 *out
= xbuffer_copyin(*out
, 0, mlti_chunk
+4, size
);
194 * looking at stream description.
197 static rmff_header_t
*real_parse_sdp(char *data
, char **stream_rules
, uint32_t bandwidth
) {
200 rmff_header_t
*header
;
205 int max_packet_size
=0;
206 int avg_packet_size
=0;
210 if (!data
) return NULL
;
212 desc
=sdpplin_parse(data
);
214 if (!desc
) return NULL
;
216 buf
= xbuffer_init(2048);
217 header
=calloc(1,sizeof(rmff_header_t
));
219 header
->fileheader
=rmff_new_fileheader(4+desc
->stream_count
);
220 header
->cont
=rmff_new_cont(
225 header
->data
=rmff_new_dataheader(0,0);
226 header
->streams
=calloc(1,sizeof(rmff_mdpr_t
*)*(desc
->stream_count
+1));
228 printf("number of streams: %u\n", desc
->stream_count
);
231 for (i
=0; i
<desc
->stream_count
; i
++) {
236 int rulematches
[MAX_RULEMATCHES
];
239 printf("calling asmrp_match with:\n%s\n%u\n", desc
->stream
[i
]->asm_rule_book
, bandwidth
);
241 n
=asmrp_match(desc
->stream
[i
]->asm_rule_book
, bandwidth
, rulematches
);
242 for (j
=0; j
<n
; j
++) {
244 printf("asmrp rule match: %u for stream %u\n", rulematches
[j
], desc
->stream
[i
]->stream_id
);
246 sprintf(b
,"stream=%u;rule=%u,", desc
->stream
[i
]->stream_id
, rulematches
[j
]);
247 *stream_rules
= xbuffer_strcat(*stream_rules
, b
);
250 if (!desc
->stream
[i
]->mlti_data
) {
254 len
=select_mlti_data(desc
->stream
[i
]->mlti_data
, desc
->stream
[i
]->mlti_data_size
, rulematches
[0], &buf
);
256 header
->streams
[i
]=rmff_new_mdpr(
257 desc
->stream
[i
]->stream_id
,
258 desc
->stream
[i
]->max_bit_rate
,
259 desc
->stream
[i
]->avg_bit_rate
,
260 desc
->stream
[i
]->max_packet_size
,
261 desc
->stream
[i
]->avg_packet_size
,
262 desc
->stream
[i
]->start_time
,
263 desc
->stream
[i
]->preroll
,
264 desc
->stream
[i
]->duration
,
265 desc
->stream
[i
]->stream_name
,
266 desc
->stream
[i
]->mime_type
,
270 duration
=FFMAX(duration
,desc
->stream
[i
]->duration
);
271 max_bit_rate
+=desc
->stream
[i
]->max_bit_rate
;
272 avg_bit_rate
+=desc
->stream
[i
]->avg_bit_rate
;
273 max_packet_size
=FFMAX(max_packet_size
, desc
->stream
[i
]->max_packet_size
);
275 avg_packet_size
=(avg_packet_size
+ desc
->stream
[i
]->avg_packet_size
) / 2;
277 avg_packet_size
=desc
->stream
[i
]->avg_packet_size
;
280 if (*stream_rules
&& strlen(*stream_rules
) && (*stream_rules
)[strlen(*stream_rules
)-1] == ',')
281 (*stream_rules
)[strlen(*stream_rules
)-1]=0; /* delete last ',' in stream_rules */
283 header
->prop
=rmff_new_prop(
296 rmff_fix_header(header
);
297 buf
= xbuffer_free(buf
);
303 int real_get_rdt_chunk(rtsp_t
*rtsp_session
, char **buffer
, int rdt_rawdata
) {
312 static uint32_t prev_ts
= -1;
313 static int prev_stream_number
= -1;
315 n
=rtsp_read_data(rtsp_session
, header
, 8);
317 if (header
[0] != 0x24)
319 mp_msg(MSGT_STREAM
, MSGL_WARN
, "realrtsp: rdt chunk not recognized: got 0x%02x\n",
323 /* header[1] is channel, normally 0, ignored */
324 size
=(header
[2]<<8)+header
[3];
326 if ((flags1
& 0xc0) != 0x40)
329 printf("got flags1: 0x%02x\n",flags1
);
331 if(header
[6] == 0x06) { // eof packet
332 rtsp_read_data(rtsp_session
, header
, 7); // Skip the rest of the eof packet
333 /* Some files have short auxiliary streams, we must ignore eof packets
334 * for these streams to avoid premature eof.
335 * Now the code declares eof only if the stream with id == 0 gets eof
336 * (old code was: eof on the first eof packet received).
338 if(flags1
& 0x7c) // ignore eof for streams with id != 0
340 mp_msg(MSGT_STREAM
, MSGL_INFO
, "realrtsp: Stream EOF detected\n");
346 n
=rtsp_read_data(rtsp_session
, header
+3, 5);
349 printf("ignoring bytes:\n");
352 n
=rtsp_read_data(rtsp_session
, header
+4, 4);
358 // header[5..6] == frame number in stream
359 unknown1
=(header
[5]<<16)+(header
[6]<<8)+(header
[7]);
360 n
=rtsp_read_data(rtsp_session
, header
, 6);
365 printf("ts: %u, size: %u, flags: 0x%02x, unknown values: 0x%06x 0x%02x 0x%02x\n",
366 ts
, size
, flags1
, unknown1
, header
[4], header
[5]);
372 ph
.stream_number
=(flags1
>>1)&0x1f;
375 if ((flags2
&1) == 0 && (prev_ts
!= ts
|| prev_stream_number
!= ph
.stream_number
))
378 prev_stream_number
= ph
.stream_number
;
383 *buffer
= xbuffer_ensure_size(*buffer
, 12+size
);
385 n
=rtsp_read_data(rtsp_session
, *buffer
, size
-12);
386 return (n
<= 0) ? 0 : n
;
388 rmff_dump_pheader(&ph
, *buffer
);
390 n
=rtsp_read_data(rtsp_session
, (*buffer
)+12, size
);
392 return (n
<= 0) ? 0 : n
+12;
395 static int convert_timestamp(char *str
, int *sec
, int *msec
) {
396 int hh
, mm
, ss
, ms
= 0;
398 // Timestamp may be optionally quoted with ", skip it
399 // Since the url is escaped when we get here, we skip the string "%22"
400 if (!strncmp(str
, "%22", 3))
402 if (sscanf(str
, "%d:%d:%d.%d", &hh
, &mm
, &ss
, &ms
) < 3) {
404 if (sscanf(str
, "%d:%d.%d", &mm
, &ss
, &ms
) < 2) {
406 if (sscanf(str
, "%d.%d", &ss
, &ms
) < 1) {
413 *sec
= hh
* 3600 + mm
* 60 + ss
;
419 //! maximum size of the rtsp description, must be < INT_MAX
420 #define MAX_DESC_BUF (20 * 1024 * 1024)
421 rmff_header_t
*real_setup_and_get_header(rtsp_t
*rtsp_session
, uint32_t bandwidth
,
422 char *username
, char *password
) {
424 char *description
=NULL
;
425 char *session_id
=NULL
;
431 char *buf
= xbuffer_init(256);
432 char *mrl
=rtsp_get_mrl(rtsp_session
);
435 uint32_t maxbandwidth
= bandwidth
;
436 char* authfield
= NULL
;
440 challenge1
=strdup(rtsp_search_answers(rtsp_session
,"RealChallenge1"));
442 printf("real: Challenge1: %s\n", challenge1
);
445 /* set a reasonable default to get the best stream, unless bandwidth given */
447 bandwidth
= 10485800;
449 /* request stream description */
451 rtsp_schedule_field(rtsp_session
, "Accept: application/sdp");
452 sprintf(buf
, "Bandwidth: %u", bandwidth
);
453 rtsp_schedule_field(rtsp_session
, buf
);
454 rtsp_schedule_field(rtsp_session
, "GUID: 00000000-0000-0000-0000-000000000000");
455 rtsp_schedule_field(rtsp_session
, "RegionData: 0");
456 rtsp_schedule_field(rtsp_session
, "ClientID: Linux_2.4_6.0.9.1235_play32_RN01_EN_586");
457 rtsp_schedule_field(rtsp_session
, "SupportsMaximumASMBandwidth: 1");
458 rtsp_schedule_field(rtsp_session
, "Language: en-US");
459 rtsp_schedule_field(rtsp_session
, "Require: com.real.retain-entity-for-setup");
461 rtsp_schedule_field(rtsp_session
, authfield
);
462 status
=rtsp_request_describe(rtsp_session
,NULL
);
465 int authlen
, b64_authlen
;
467 char* authstr
= NULL
;
470 mp_msg(MSGT_STREAM
, MSGL_ERR
, "realrtsp: authorization failed, check your credentials\n");
473 if (!(authreq
= rtsp_search_answers(rtsp_session
,"WWW-Authenticate"))) {
474 mp_msg(MSGT_STREAM
, MSGL_ERR
, "realrtsp: 401 but no auth request, aborting\n");
478 mp_msg(MSGT_STREAM
, MSGL_ERR
, "realrtsp: auth required but no username supplied\n");
481 if (!strstr(authreq
, "Basic")) {
482 mp_msg(MSGT_STREAM
, MSGL_ERR
, "realrtsp: authenticator not supported (%s)\n", authreq
);
485 authlen
= strlen(username
) + (password
? strlen(password
) : 0) + 2;
486 authstr
= malloc(authlen
);
487 sprintf(authstr
, "%s:%s", username
, password
? password
: "");
488 authfield
= malloc(authlen
*2+22);
489 strcpy(authfield
, "Authorization: Basic ");
490 b64_authlen
= base64_encode(authstr
, authlen
, authfield
+21, authlen
*2);
492 if (b64_authlen
< 0) {
493 mp_msg(MSGT_STREAM
, MSGL_ERR
, "realrtsp: base64 output overflow, this should never happen\n");
496 authfield
[b64_authlen
+21] = 0;
497 goto rtsp_send_describe
;
504 if ( status
<200 || status
>299 )
506 char *alert
=rtsp_search_answers(rtsp_session
,"Alert");
508 mp_msg(MSGT_STREAM
, MSGL_WARN
, "realrtsp: got message from server:\n%s\n",
511 rtsp_send_ok(rtsp_session
);
512 buf
= xbuffer_free(buf
);
516 /* receive description */
518 if (!rtsp_search_answers(rtsp_session
,"Content-length"))
519 mp_msg(MSGT_STREAM
, MSGL_WARN
, "real: got no Content-length!\n");
521 size
=atoi(rtsp_search_answers(rtsp_session
,"Content-length"));
523 // as size is unsigned this also catches the case (size < 0)
524 if (size
> MAX_DESC_BUF
) {
525 mp_msg(MSGT_STREAM
, MSGL_ERR
, "realrtsp: Content-length for description too big (> %uMB)!\n",
526 MAX_DESC_BUF
/(1024*1024) );
531 if (!rtsp_search_answers(rtsp_session
,"ETag"))
532 mp_msg(MSGT_STREAM
, MSGL_WARN
, "realrtsp: got no ETag!\n");
534 session_id
=strdup(rtsp_search_answers(rtsp_session
,"ETag"));
537 printf("real: Stream description size: %u\n", size
);
540 description
=malloc(size
+1);
542 if( rtsp_read_data(rtsp_session
, description
, size
) <= 0) {
543 buf
= xbuffer_free(buf
);
548 /* parse sdp (sdpplin) and create a header and a subscribe string */
549 subscribe
= xbuffer_init(256);
550 strcpy(subscribe
, "Subscribe: ");
551 h
=real_parse_sdp(description
, &subscribe
, bandwidth
);
553 subscribe
= xbuffer_free(subscribe
);
554 buf
= xbuffer_free(buf
);
560 printf("Title: %s\nCopyright: %s\nAuthor: %s\nStreams: %i\n",
561 h
->cont
->title
, h
->cont
->copyright
, h
->cont
->author
, h
->prop
->num_streams
);
564 /* setup our streams */
565 real_calc_response_and_checksum (challenge2
, checksum
, challenge1
);
566 buf
= xbuffer_ensure_size(buf
, strlen(challenge2
) + strlen(checksum
) + 32);
567 sprintf(buf
, "RealChallenge2: %s, sd=%s", challenge2
, checksum
);
568 rtsp_schedule_field(rtsp_session
, buf
);
569 buf
= xbuffer_ensure_size(buf
, strlen(session_id
) + 32);
570 sprintf(buf
, "If-Match: %s", session_id
);
571 rtsp_schedule_field(rtsp_session
, buf
);
572 rtsp_schedule_field(rtsp_session
, "Transport: x-pn-tng/tcp;mode=play,rtp/avp/tcp;unicast;mode=play");
573 buf
= xbuffer_ensure_size(buf
, strlen(mrl
) + 32);
574 sprintf(buf
, "%s/streamid=0", mrl
);
575 rtsp_request_setup(rtsp_session
,buf
,NULL
);
577 /* Do setup for all the other streams we subscribed to */
578 for (i
= 1; i
< h
->prop
->num_streams
; i
++) {
579 rtsp_schedule_field(rtsp_session
, "Transport: x-pn-tng/tcp;mode=play,rtp/avp/tcp;unicast;mode=play");
580 buf
= xbuffer_ensure_size(buf
, strlen(session_id
) + 32);
581 sprintf(buf
, "If-Match: %s", session_id
);
582 rtsp_schedule_field(rtsp_session
, buf
);
584 buf
= xbuffer_ensure_size(buf
, strlen(mrl
) + 32);
585 sprintf(buf
, "%s/streamid=%d", mrl
, i
);
586 rtsp_request_setup(rtsp_session
,buf
,NULL
);
588 /* set stream parameter (bandwidth) with our subscribe string */
589 rtsp_schedule_field(rtsp_session
, subscribe
);
590 rtsp_request_setparameter(rtsp_session
,NULL
);
592 /* set delivery bandwidth */
594 sprintf(buf
, "SetDeliveryBandwidth: Bandwidth=%u;BackOff=0", maxbandwidth
);
595 rtsp_schedule_field(rtsp_session
, buf
);
596 rtsp_request_setparameter(rtsp_session
,NULL
);
600 int s_ss
= 0, s_ms
= 0, e_ss
= 0, e_ms
= 0;
602 if ((str
= rtsp_get_param(rtsp_session
, "start"))) {
603 convert_timestamp(str
, &s_ss
, &s_ms
);
606 if ((str
= rtsp_get_param(rtsp_session
, "end"))) {
607 convert_timestamp(str
, &e_ss
, &e_ms
);
610 str
= buf
+ sprintf(buf
, s_ms
? "%s%d.%d-" : "%s%d-", "Range: npt=", s_ss
, s_ms
);
612 sprintf(str
, e_ms
? "%d.%d" : "%d", e_ss
, e_ms
);
614 rtsp_schedule_field(rtsp_session
, buf
);
615 /* and finally send a play request */
616 rtsp_request_play(rtsp_session
,NULL
);
618 subscribe
= xbuffer_free(subscribe
);
619 buf
= xbuffer_free(buf
);
623 struct real_rtsp_session_t
*
624 init_real_rtsp_session (void)
626 struct real_rtsp_session_t
*real_rtsp_session
= NULL
;
628 real_rtsp_session
= malloc (sizeof (struct real_rtsp_session_t
));
629 real_rtsp_session
->recv
= xbuffer_init (BUF_SIZE
);
630 real_rtsp_session
->rdteof
= 0;
631 real_rtsp_session
->rdt_rawdata
= 0;
633 return real_rtsp_session
;
637 free_real_rtsp_session (struct real_rtsp_session_t
* real_session
)
642 xbuffer_free (real_session
->recv
);