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 "ffmpeg_files/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
];
238 if (!desc
->stream
[i
])
241 printf("calling asmrp_match with:\n%s\n%u\n", desc
->stream
[i
]->asm_rule_book
, bandwidth
);
243 n
=asmrp_match(desc
->stream
[i
]->asm_rule_book
, bandwidth
, rulematches
);
244 for (j
=0; j
<n
; j
++) {
246 printf("asmrp rule match: %u for stream %u\n", rulematches
[j
], desc
->stream
[i
]->stream_id
);
248 sprintf(b
,"stream=%u;rule=%u,", desc
->stream
[i
]->stream_id
, rulematches
[j
]);
249 *stream_rules
= xbuffer_strcat(*stream_rules
, b
);
252 if (!desc
->stream
[i
]->mlti_data
) {
254 buf
= xbuffer_free(buf
);
256 len
=select_mlti_data(desc
->stream
[i
]->mlti_data
, desc
->stream
[i
]->mlti_data_size
, rulematches
[0], &buf
);
258 header
->streams
[i
]=rmff_new_mdpr(
259 desc
->stream
[i
]->stream_id
,
260 desc
->stream
[i
]->max_bit_rate
,
261 desc
->stream
[i
]->avg_bit_rate
,
262 desc
->stream
[i
]->max_packet_size
,
263 desc
->stream
[i
]->avg_packet_size
,
264 desc
->stream
[i
]->start_time
,
265 desc
->stream
[i
]->preroll
,
266 desc
->stream
[i
]->duration
,
267 desc
->stream
[i
]->stream_name
,
268 desc
->stream
[i
]->mime_type
,
272 duration
=FFMAX(duration
,desc
->stream
[i
]->duration
);
273 max_bit_rate
+=desc
->stream
[i
]->max_bit_rate
;
274 avg_bit_rate
+=desc
->stream
[i
]->avg_bit_rate
;
275 max_packet_size
=FFMAX(max_packet_size
, desc
->stream
[i
]->max_packet_size
);
277 avg_packet_size
=(avg_packet_size
+ desc
->stream
[i
]->avg_packet_size
) / 2;
279 avg_packet_size
=desc
->stream
[i
]->avg_packet_size
;
282 if (*stream_rules
&& strlen(*stream_rules
) && (*stream_rules
)[strlen(*stream_rules
)-1] == ',')
283 (*stream_rules
)[strlen(*stream_rules
)-1]=0; /* delete last ',' in stream_rules */
285 header
->prop
=rmff_new_prop(
298 rmff_fix_header(header
);
299 buf
= xbuffer_free(buf
);
305 int real_get_rdt_chunk(rtsp_t
*rtsp_session
, char **buffer
, int rdt_rawdata
) {
314 static uint32_t prev_ts
= -1;
315 static int prev_stream_number
= -1;
317 n
=rtsp_read_data(rtsp_session
, header
, 8);
319 if (header
[0] != 0x24)
321 mp_msg(MSGT_STREAM
, MSGL_WARN
, "realrtsp: rdt chunk not recognized: got 0x%02x\n",
325 /* header[1] is channel, normally 0, ignored */
326 size
=(header
[2]<<8)+header
[3];
328 if ((flags1
& 0xc0) != 0x40)
331 printf("got flags1: 0x%02x\n",flags1
);
333 if(header
[6] == 0x06) { // eof packet
334 rtsp_read_data(rtsp_session
, header
, 7); // Skip the rest of the eof packet
335 /* Some files have short auxiliary streams, we must ignore eof packets
336 * for these streams to avoid premature eof.
337 * Now the code declares eof only if the stream with id == 0 gets eof
338 * (old code was: eof on the first eof packet received).
340 if(flags1
& 0x7c) // ignore eof for streams with id != 0
342 mp_msg(MSGT_STREAM
, MSGL_INFO
, "realrtsp: Stream EOF detected\n");
348 n
=rtsp_read_data(rtsp_session
, header
+3, 5);
351 printf("ignoring bytes:\n");
354 n
=rtsp_read_data(rtsp_session
, header
+4, 4);
360 // header[5..6] == frame number in stream
361 unknown1
=(header
[5]<<16)+(header
[6]<<8)+(header
[7]);
362 n
=rtsp_read_data(rtsp_session
, header
, 6);
367 printf("ts: %u, size: %u, flags: 0x%02x, unknown values: 0x%06x 0x%02x 0x%02x\n",
368 ts
, size
, flags1
, unknown1
, header
[4], header
[5]);
374 ph
.stream_number
=(flags1
>>1)&0x1f;
377 if ((flags2
&1) == 0 && (prev_ts
!= ts
|| prev_stream_number
!= ph
.stream_number
))
380 prev_stream_number
= ph
.stream_number
;
385 *buffer
= xbuffer_ensure_size(*buffer
, 12+size
);
389 n
=rtsp_read_data(rtsp_session
, *buffer
, size
-12);
390 return (n
<= 0) ? 0 : n
;
392 rmff_dump_pheader(&ph
, *buffer
);
396 n
=rtsp_read_data(rtsp_session
, (*buffer
)+12, size
);
398 return (n
<= 0) ? 0 : n
+12;
401 static int convert_timestamp(char *str
, int *sec
, int *msec
) {
402 int hh
, mm
, ss
, ms
= 0;
404 // Timestamp may be optionally quoted with ", skip it
405 // Since the url is escaped when we get here, we skip the string "%22"
406 if (!strncmp(str
, "%22", 3))
408 if (sscanf(str
, "%d:%d:%d.%d", &hh
, &mm
, &ss
, &ms
) < 3) {
410 if (sscanf(str
, "%d:%d.%d", &mm
, &ss
, &ms
) < 2) {
412 if (sscanf(str
, "%d.%d", &ss
, &ms
) < 1) {
419 *sec
= hh
* 3600 + mm
* 60 + ss
;
425 //! maximum size of the rtsp description, must be < INT_MAX
426 #define MAX_DESC_BUF (20 * 1024 * 1024)
427 rmff_header_t
*real_setup_and_get_header(rtsp_t
*rtsp_session
, uint32_t bandwidth
,
428 char *username
, char *password
) {
430 char *description
=NULL
;
431 char *session_id
=NULL
;
432 rmff_header_t
*h
= NULL
;
433 char *challenge1
= NULL
;
436 char *subscribe
= NULL
;
437 char *buf
= xbuffer_init(256);
438 char *mrl
=rtsp_get_mrl(rtsp_session
);
441 uint32_t maxbandwidth
= bandwidth
;
442 char* authfield
= NULL
;
446 challenge1
=rtsp_search_answers(rtsp_session
,"RealChallenge1");
449 challenge1
=strdup(challenge1
);
451 printf("real: Challenge1: %s\n", challenge1
);
454 /* set a reasonable default to get the best stream, unless bandwidth given */
456 bandwidth
= 10485800;
458 /* request stream description */
460 rtsp_schedule_field(rtsp_session
, "Accept: application/sdp");
461 sprintf(buf
, "Bandwidth: %u", bandwidth
);
462 rtsp_schedule_field(rtsp_session
, buf
);
463 rtsp_schedule_field(rtsp_session
, "GUID: 00000000-0000-0000-0000-000000000000");
464 rtsp_schedule_field(rtsp_session
, "RegionData: 0");
465 rtsp_schedule_field(rtsp_session
, "ClientID: Linux_2.4_6.0.9.1235_play32_RN01_EN_586");
466 rtsp_schedule_field(rtsp_session
, "SupportsMaximumASMBandwidth: 1");
467 rtsp_schedule_field(rtsp_session
, "Language: en-US");
468 rtsp_schedule_field(rtsp_session
, "Require: com.real.retain-entity-for-setup");
470 rtsp_schedule_field(rtsp_session
, authfield
);
471 status
=rtsp_request_describe(rtsp_session
,NULL
);
474 int authlen
, b64_authlen
;
476 char* authstr
= NULL
;
479 mp_msg(MSGT_STREAM
, MSGL_ERR
, "realrtsp: authorization failed, check your credentials\n");
482 if (!(authreq
= rtsp_search_answers(rtsp_session
,"WWW-Authenticate"))) {
483 mp_msg(MSGT_STREAM
, MSGL_ERR
, "realrtsp: 401 but no auth request, aborting\n");
487 mp_msg(MSGT_STREAM
, MSGL_ERR
, "realrtsp: auth required but no username supplied\n");
490 if (!strstr(authreq
, "Basic")) {
491 mp_msg(MSGT_STREAM
, MSGL_ERR
, "realrtsp: authenticator not supported (%s)\n", authreq
);
494 authlen
= strlen(username
) + (password
? strlen(password
) : 0) + 2;
495 authstr
= malloc(authlen
);
496 sprintf(authstr
, "%s:%s", username
, password
? password
: "");
497 authfield
= malloc(authlen
*2+22);
498 strcpy(authfield
, "Authorization: Basic ");
499 b64_authlen
= base64_encode(authstr
, authlen
, authfield
+21, authlen
*2);
501 if (b64_authlen
< 0) {
502 mp_msg(MSGT_STREAM
, MSGL_ERR
, "realrtsp: base64 output overflow, this should never happen\n");
505 authfield
[b64_authlen
+21] = 0;
506 goto rtsp_send_describe
;
513 if ( status
<200 || status
>299 )
515 char *alert
=rtsp_search_answers(rtsp_session
,"Alert");
517 mp_msg(MSGT_STREAM
, MSGL_WARN
, "realrtsp: got message from server:\n%s\n",
520 rtsp_send_ok(rtsp_session
);
524 /* receive description */
526 if (!rtsp_search_answers(rtsp_session
,"Content-length"))
527 mp_msg(MSGT_STREAM
, MSGL_WARN
, "real: got no Content-length!\n");
529 size
=atoi(rtsp_search_answers(rtsp_session
,"Content-length"));
531 // as size is unsigned this also catches the case (size < 0)
532 if (size
> MAX_DESC_BUF
) {
533 mp_msg(MSGT_STREAM
, MSGL_ERR
, "realrtsp: Content-length for description too big (> %uMB)!\n",
534 MAX_DESC_BUF
/(1024*1024) );
538 if (!rtsp_search_answers(rtsp_session
,"ETag"))
539 mp_msg(MSGT_STREAM
, MSGL_WARN
, "realrtsp: got no ETag!\n");
541 session_id
=strdup(rtsp_search_answers(rtsp_session
,"ETag"));
544 printf("real: Stream description size: %u\n", size
);
547 description
=malloc(size
+1);
549 if( rtsp_read_data(rtsp_session
, description
, size
) <= 0) {
554 /* parse sdp (sdpplin) and create a header and a subscribe string */
555 subscribe
= xbuffer_init(256);
556 strcpy(subscribe
, "Subscribe: ");
557 h
=real_parse_sdp(description
, &subscribe
, bandwidth
);
564 printf("Title: %s\nCopyright: %s\nAuthor: %s\nStreams: %i\n",
565 h
->cont
->title
, h
->cont
->copyright
, h
->cont
->author
, h
->prop
->num_streams
);
568 /* setup our streams */
569 real_calc_response_and_checksum (challenge2
, checksum
, challenge1
);
570 buf
= xbuffer_ensure_size(buf
, strlen(challenge2
) + strlen(checksum
) + 32);
571 sprintf(buf
, "RealChallenge2: %s, sd=%s", challenge2
, checksum
);
572 rtsp_schedule_field(rtsp_session
, buf
);
573 buf
= xbuffer_ensure_size(buf
, strlen(session_id
) + 32);
574 sprintf(buf
, "If-Match: %s", session_id
);
575 rtsp_schedule_field(rtsp_session
, buf
);
576 rtsp_schedule_field(rtsp_session
, "Transport: x-pn-tng/tcp;mode=play,rtp/avp/tcp;unicast;mode=play");
577 buf
= xbuffer_ensure_size(buf
, strlen(mrl
) + 32);
578 sprintf(buf
, "%s/streamid=0", mrl
);
579 rtsp_request_setup(rtsp_session
,buf
,NULL
);
581 /* Do setup for all the other streams we subscribed to */
582 for (i
= 1; i
< h
->prop
->num_streams
; i
++) {
583 rtsp_schedule_field(rtsp_session
, "Transport: x-pn-tng/tcp;mode=play,rtp/avp/tcp;unicast;mode=play");
584 buf
= xbuffer_ensure_size(buf
, strlen(session_id
) + 32);
585 sprintf(buf
, "If-Match: %s", session_id
);
586 rtsp_schedule_field(rtsp_session
, buf
);
588 buf
= xbuffer_ensure_size(buf
, strlen(mrl
) + 32);
589 sprintf(buf
, "%s/streamid=%d", mrl
, i
);
590 rtsp_request_setup(rtsp_session
,buf
,NULL
);
592 /* set stream parameter (bandwidth) with our subscribe string */
593 rtsp_schedule_field(rtsp_session
, subscribe
);
594 rtsp_request_setparameter(rtsp_session
,NULL
);
596 /* set delivery bandwidth */
598 sprintf(buf
, "SetDeliveryBandwidth: Bandwidth=%u;BackOff=0", maxbandwidth
);
599 rtsp_schedule_field(rtsp_session
, buf
);
600 rtsp_request_setparameter(rtsp_session
,NULL
);
604 int s_ss
= 0, s_ms
= 0, e_ss
= 0, e_ms
= 0;
606 if ((str
= rtsp_get_param(rtsp_session
, "start"))) {
607 convert_timestamp(str
, &s_ss
, &s_ms
);
610 if ((str
= rtsp_get_param(rtsp_session
, "end"))) {
611 convert_timestamp(str
, &e_ss
, &e_ms
);
614 str
= buf
+ sprintf(buf
, s_ms
? "%s%d.%d-" : "%s%d-", "Range: npt=", s_ss
, s_ms
);
616 sprintf(str
, e_ms
? "%d.%d" : "%d", e_ss
, e_ms
);
618 rtsp_schedule_field(rtsp_session
, buf
);
619 /* and finally send a play request */
620 rtsp_request_play(rtsp_session
,NULL
);
623 subscribe
= xbuffer_free(subscribe
);
624 buf
= xbuffer_free(buf
);
631 struct real_rtsp_session_t
*
632 init_real_rtsp_session (void)
634 struct real_rtsp_session_t
*real_rtsp_session
= NULL
;
636 real_rtsp_session
= malloc (sizeof (struct real_rtsp_session_t
));
637 real_rtsp_session
->recv
= xbuffer_init (BUF_SIZE
);
638 real_rtsp_session
->rdteof
= 0;
639 real_rtsp_session
->rdt_rawdata
= 0;
641 return real_rtsp_session
;
645 free_real_rtsp_session (struct real_rtsp_session_t
* real_session
)
650 xbuffer_free (real_session
->recv
);