stream_cue: Avoid probing empty filename in cue_find_bin()
[mplayer.git] / stream / asf_mmst_streaming.c
blob3af97b148bcf7f67b40659ccc58a516dafee288f
1 /*
2 * MMST implementation taken from the xine-mms plugin made by
3 * Major MMS (http://geocities.com/majormms/).
4 * Ported to MPlayer by Abhijeet Phatak <abhijeetphatak@yahoo.com>.
6 * Information about the MMS protocol can be found at http://get.to/sdp
8 * copyright (C) 2002 Abhijeet Phatak <abhijeetphatak@yahoo.com>
9 * copyright (C) 2002 the xine project
10 * copyright (C) 2000-2001 major mms
12 * This file is part of MPlayer.
14 * MPlayer is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
19 * MPlayer is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
24 * You should have received a copy of the GNU General Public License along
25 * with MPlayer; if not, write to the Free Software Foundation, Inc.,
26 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <unistd.h>
34 #include <errno.h>
35 #include <inttypes.h>
37 #include "config.h"
38 #include "options.h"
39 #include "mp_msg.h"
41 #if HAVE_WINSOCK2_H
42 #include <winsock2.h>
43 #endif
45 #ifdef CONFIG_ICONV
46 #include <iconv.h>
47 #endif
49 #include "url.h"
50 #include "libmpdemux/asf.h"
52 #include "stream.h"
53 #include "asf_mmst_streaming.h"
54 #include "network.h"
55 #include "tcp.h"
57 extern int audio_id;
58 extern int video_id;
60 #define BUF_SIZE 102400
61 #define HDR_BUF_SIZE 8192
62 #define MAX_STREAMS 20
64 typedef struct
66 uint8_t buf[BUF_SIZE];
67 int num_bytes;
69 } command_t;
71 static int seq_num;
72 static int num_stream_ids;
73 static int stream_ids[MAX_STREAMS];
75 static int get_data (int s, char *buf, size_t count);
77 static void put_32 (command_t *cmd, uint32_t value)
79 cmd->buf[cmd->num_bytes ] = value % 256;
80 value = value >> 8;
81 cmd->buf[cmd->num_bytes+1] = value % 256 ;
82 value = value >> 8;
83 cmd->buf[cmd->num_bytes+2] = value % 256 ;
84 value = value >> 8;
85 cmd->buf[cmd->num_bytes+3] = value % 256 ;
87 cmd->num_bytes += 4;
90 static uint32_t get_32 (unsigned char *cmd, int offset)
92 uint32_t ret;
94 ret = cmd[offset] ;
95 ret |= cmd[offset+1]<<8 ;
96 ret |= cmd[offset+2]<<16 ;
97 ret |= cmd[offset+3]<<24 ;
99 return ret;
102 static void send_command (int s, int command, uint32_t switches,
103 uint32_t extra, int length,
104 char *data)
106 command_t cmd;
107 int len8;
109 len8 = (length + 7) / 8;
111 cmd.num_bytes = 0;
113 put_32 (&cmd, 0x00000001); /* start sequence */
114 put_32 (&cmd, 0xB00BFACE); /* #-)) */
115 put_32 (&cmd, len8*8 + 32);
116 put_32 (&cmd, 0x20534d4d); /* protocol type "MMS " */
117 put_32 (&cmd, len8 + 4);
118 put_32 (&cmd, seq_num);
119 seq_num++;
120 put_32 (&cmd, 0x0); /* unknown */
121 put_32 (&cmd, 0x0);
122 put_32 (&cmd, len8+2);
123 put_32 (&cmd, 0x00030000 | command); /* dir | command */
124 put_32 (&cmd, switches);
125 put_32 (&cmd, extra);
127 memcpy (&cmd.buf[48], data, length);
128 if (length & 7)
129 memset(&cmd.buf[48 + length], 0, 8 - (length & 7));
131 if (send (s, cmd.buf, len8*8+48, 0) != (len8*8+48)) {
132 mp_tmsg(MSGT_NETWORK,MSGL_ERR,"write error\n");
136 #ifdef CONFIG_ICONV
137 static iconv_t url_conv;
138 #endif
140 static void string_utf16(char *dest, char *src, int len) {
141 int i;
142 #ifdef CONFIG_ICONV
143 size_t len1, len2;
144 char *ip, *op;
146 if (url_conv != (iconv_t)(-1))
148 memset(dest, 0, 1000);
149 len1 = len; len2 = 1000;
150 ip = src; op = dest;
152 iconv(url_conv, &ip, &len1, &op, &len2);
154 else
156 #endif
157 if (len > 499) len = 499;
158 for (i=0; i<len; i++) {
159 dest[i*2] = src[i];
160 dest[i*2+1] = 0;
162 /* trailing zeroes */
163 dest[i*2] = 0;
164 dest[i*2+1] = 0;
165 #ifdef CONFIG_ICONV
167 #endif
170 static void get_answer (int s)
172 char data[BUF_SIZE];
173 int command = 0x1b;
175 while (command == 0x1b) {
176 int len;
178 len = recv (s, data, BUF_SIZE, 0) ;
179 if (!len) {
180 mp_tmsg(MSGT_NETWORK,MSGL_ERR,"\nAlert! EOF\n");
181 return;
184 command = get_32 (data, 36) & 0xFFFF;
186 if (command == 0x1b)
187 send_command (s, 0x1b, 0, 0, 0, data);
191 static int get_data (int s, char *buf, size_t count)
193 ssize_t len;
194 size_t total = 0;
196 while (total < count) {
198 len = recv (s, &buf[total], count-total, 0);
200 if (len<=0) {
201 perror ("read error:");
202 return 0;
205 total += len;
207 if (len != 0) {
208 // mp_msg(MSGT_NETWORK,MSGL_INFO,"[%d/%d]", total, count);
209 fflush (stdout);
214 return 1;
218 static int get_header (int s, uint8_t *header, streaming_ctrl_t *streaming_ctrl)
220 unsigned char pre_header[8];
221 int header_len;
223 header_len = 0;
225 while (1) {
226 if (!get_data (s, pre_header, 8)) {
227 mp_tmsg(MSGT_NETWORK,MSGL_ERR,"pre-header read failed\n");
228 return 0;
230 if (pre_header[4] == 0x02) {
232 int packet_len;
234 packet_len = (pre_header[7] << 8 | pre_header[6]) - 8;
236 // mp_msg(MSGT_NETWORK,MSGL_INFO,"asf header packet detected, len=%d\n", packet_len);
238 if (packet_len < 0 || packet_len > HDR_BUF_SIZE - header_len) {
239 mp_tmsg(MSGT_NETWORK, MSGL_FATAL, "Invalid header size, giving up.\n");
240 return 0;
243 if (!get_data (s, &header[header_len], packet_len)) {
244 mp_tmsg(MSGT_NETWORK,MSGL_ERR,"Header data read failed.\n");
245 return 0;
248 header_len += packet_len;
250 if ( (header[header_len-1] == 1) && (header[header_len-2]==1)) {
253 if( streaming_bufferize( streaming_ctrl, header, header_len )<0 ) {
254 return -1;
257 // mp_msg(MSGT_NETWORK,MSGL_INFO,"get header packet finished\n");
259 return header_len;
263 } else {
265 int32_t packet_len;
266 int command;
267 char data[BUF_SIZE];
269 if (!get_data (s, (char*)&packet_len, 4)) {
270 mp_tmsg(MSGT_NETWORK,MSGL_ERR,"packet_len read failed.\n");
271 return 0;
274 packet_len = get_32 ((unsigned char*)&packet_len, 0) + 4;
276 // mp_msg(MSGT_NETWORK,MSGL_INFO,"command packet detected, len=%d\n", packet_len);
278 if (packet_len < 0 || packet_len > BUF_SIZE) {
279 mp_tmsg(MSGT_NETWORK, MSGL_FATAL,
280 "Invalid RTSP packet size, giving up.\n");
281 return 0;
284 if (!get_data (s, data, packet_len)) {
285 mp_tmsg(MSGT_NETWORK,MSGL_ERR,"Command data read failed.\n");
286 return 0;
289 command = get_32 (data, 24) & 0xFFFF;
291 // mp_msg(MSGT_NETWORK,MSGL_INFO,"command: %02x\n", command);
293 if (command == 0x1b)
294 send_command (s, 0x1b, 0, 0, 0, data);
298 // mp_msg(MSGT_NETWORK,MSGL_INFO,"get header packet succ\n");
302 static int interp_header (uint8_t *header, int header_len)
304 int i;
305 int packet_length=-1;
308 * parse header
311 i = 30;
312 while (i<header_len) {
314 uint64_t guid_1, guid_2, length;
316 guid_2 = (uint64_t)header[i] | ((uint64_t)header[i+1]<<8)
317 | ((uint64_t)header[i+2]<<16) | ((uint64_t)header[i+3]<<24)
318 | ((uint64_t)header[i+4]<<32) | ((uint64_t)header[i+5]<<40)
319 | ((uint64_t)header[i+6]<<48) | ((uint64_t)header[i+7]<<56);
320 i += 8;
322 guid_1 = (uint64_t)header[i] | ((uint64_t)header[i+1]<<8)
323 | ((uint64_t)header[i+2]<<16) | ((uint64_t)header[i+3]<<24)
324 | ((uint64_t)header[i+4]<<32) | ((uint64_t)header[i+5]<<40)
325 | ((uint64_t)header[i+6]<<48) | ((uint64_t)header[i+7]<<56);
326 i += 8;
328 // mp_msg(MSGT_NETWORK,MSGL_INFO,"guid found: %016llx%016llx\n", guid_1, guid_2);
330 length = (uint64_t)header[i] | ((uint64_t)header[i+1]<<8)
331 | ((uint64_t)header[i+2]<<16) | ((uint64_t)header[i+3]<<24)
332 | ((uint64_t)header[i+4]<<32) | ((uint64_t)header[i+5]<<40)
333 | ((uint64_t)header[i+6]<<48) | ((uint64_t)header[i+7]<<56);
335 i += 8;
337 if ( (guid_1 == 0x6cce6200aa00d9a6ULL) && (guid_2 == 0x11cf668e75b22630ULL) ) {
338 mp_tmsg(MSGT_NETWORK,MSGL_INFO,"header object\n");
339 } else if ((guid_1 == 0x6cce6200aa00d9a6ULL) && (guid_2 == 0x11cf668e75b22636ULL)) {
340 mp_tmsg(MSGT_NETWORK,MSGL_INFO,"data object\n");
341 } else if ((guid_1 == 0x6553200cc000e48eULL) && (guid_2 == 0x11cfa9478cabdca1ULL)) {
343 packet_length = get_32(header, i+92-24);
345 mp_tmsg(MSGT_NETWORK,MSGL_INFO,"file object, packet length = %d (%d)\n",
346 packet_length, get_32(header, i+96-24));
349 } else if ((guid_1 == 0x6553200cc000e68eULL) && (guid_2 == 0x11cfa9b7b7dc0791ULL)) {
351 int stream_id = header[i+48] | header[i+49] << 8;
353 mp_tmsg(MSGT_NETWORK,MSGL_INFO,"stream object, stream ID: %d\n", stream_id);
355 if (num_stream_ids < MAX_STREAMS) {
356 stream_ids[num_stream_ids] = stream_id;
357 num_stream_ids++;
358 } else {
359 mp_tmsg(MSGT_NETWORK,MSGL_ERR,"Too many IDs, stream skipped.");
362 } else {
363 #if 0
364 int b = i;
365 printf ("unknown object (guid: %016llx, %016llx, len: %lld)\n", guid_1, guid_2, length);
366 for (; b < length; b++)
368 if (isascii(header[b]) || isalpha(header[b]))
369 printf("%c ", header[b]);
370 else
371 printf("%x ", header[b]);
373 printf("\n");
374 #else
375 mp_tmsg(MSGT_NETWORK,MSGL_WARN,"unknown object\n");
376 #endif
379 // mp_msg(MSGT_NETWORK,MSGL_INFO,"length : %lld\n", length);
381 i += length-24;
385 return packet_length;
390 static int get_media_packet (int s, int padding, streaming_ctrl_t *stream_ctrl) {
391 unsigned char pre_header[8];
392 char data[BUF_SIZE];
394 if (!get_data (s, pre_header, 8)) {
395 mp_tmsg(MSGT_NETWORK,MSGL_ERR,"pre-header read failed\n");
396 return 0;
399 // for (i=0; i<8; i++)
400 // mp_msg(MSGT_NETWORK,MSGL_INFO,"pre_header[%d] = %02x (%d)\n",
401 // i, pre_header[i], pre_header[i]);
403 if (pre_header[4] == 0x04) {
405 int packet_len;
407 packet_len = (pre_header[7] << 8 | pre_header[6]) - 8;
409 // mp_msg(MSGT_NETWORK,MSGL_INFO,"asf media packet detected, len=%d\n", packet_len);
411 if (packet_len < 0 || packet_len > BUF_SIZE) {
412 mp_tmsg(MSGT_NETWORK, MSGL_FATAL, "Invalid RTSP packet size, giving up.\n");
413 return 0;
416 if (!get_data (s, data, packet_len)) {
417 mp_tmsg(MSGT_NETWORK,MSGL_ERR,"Media data read failed.\n");
418 return 0;
421 streaming_bufferize(stream_ctrl, data, padding);
423 } else {
425 int32_t packet_len;
426 int command;
428 if (!get_data (s, (char*)&packet_len, 4)) {
429 mp_tmsg(MSGT_NETWORK,MSGL_ERR,"packet_len read failed.\n");
430 return 0;
433 packet_len = get_32 ((unsigned char*)&packet_len, 0) + 4;
435 if (packet_len < 0 || packet_len > BUF_SIZE) {
436 mp_tmsg(MSGT_NETWORK,MSGL_FATAL,"Invalid RTSP packet size, giving up.\n");
437 return 0;
440 if (!get_data (s, data, packet_len)) {
441 mp_tmsg(MSGT_NETWORK,MSGL_ERR,"Command data read failed.\n");
442 return 0;
445 if ( (pre_header[7] != 0xb0) || (pre_header[6] != 0x0b)
446 || (pre_header[5] != 0xfa) || (pre_header[4] != 0xce) ) {
448 mp_tmsg(MSGT_NETWORK,MSGL_ERR,"missing signature\n");
449 return -1;
452 command = get_32 (data, 24) & 0xFFFF;
454 // mp_msg(MSGT_NETWORK,MSGL_INFO,"\ncommand packet detected, len=%d cmd=0x%X\n", packet_len, command);
456 if (command == 0x1b)
457 send_command (s, 0x1b, 0, 0, 0, data);
458 else if (command == 0x1e) {
459 mp_tmsg(MSGT_NETWORK,MSGL_INFO,"Everything done. Thank you for downloading a media file containing proprietary and patented technology.\n");
460 return 0;
462 else if (command == 0x21 ) {
463 // Looks like it's new in WMS9
464 // Unknown command, but ignoring it seems to work.
465 return 0;
467 else if (command != 0x05) {
468 mp_tmsg(MSGT_NETWORK,MSGL_ERR,"unknown command %02x\n",command);
469 return -1;
473 // mp_msg(MSGT_NETWORK,MSGL_INFO,"get media packet succ\n");
475 return 1;
479 static int packet_length1;
481 static int asf_mmst_streaming_read( int fd, char *buffer, int size, streaming_ctrl_t *stream_ctrl )
483 int len;
485 while( stream_ctrl->buffer_size==0 ) {
486 // buffer is empty - fill it!
487 int ret = get_media_packet( fd, packet_length1, stream_ctrl);
488 if( ret<0 ) {
489 mp_tmsg(MSGT_NETWORK,MSGL_ERR,"get_media_packet error : %s\n",strerror(errno));
490 return -1;
491 } else if (ret==0) //EOF?
492 return ret;
495 len = stream_ctrl->buffer_size-stream_ctrl->buffer_pos;
496 if(len>size) len=size;
497 memcpy( buffer, (stream_ctrl->buffer)+(stream_ctrl->buffer_pos), len );
498 stream_ctrl->buffer_pos += len;
499 if( stream_ctrl->buffer_pos>=stream_ctrl->buffer_size ) {
500 free( stream_ctrl->buffer );
501 stream_ctrl->buffer = NULL;
502 stream_ctrl->buffer_size = 0;
503 stream_ctrl->buffer_pos = 0;
505 return len;
509 static int asf_mmst_streaming_seek( int fd, off_t pos, streaming_ctrl_t *streaming_ctrl )
511 return -1;
512 // Shut up gcc warning
513 fd++;
514 pos++;
515 streaming_ctrl=NULL;
518 int asf_mmst_streaming_start(stream_t *stream)
520 char str[1024];
521 char data[BUF_SIZE];
522 uint8_t asf_header[HDR_BUF_SIZE];
523 int asf_header_len;
524 int i, packet_length;
525 char *path, *unescpath;
526 URL_t *url1 = stream->streaming_ctrl->url;
527 int s = stream->fd;
529 if( s>0 ) {
530 closesocket( stream->fd );
531 stream->fd = -1;
534 /* parse url */
535 path = strchr(url1->file,'/') + 1;
537 /* mmst filename are not url_escaped by MS MediaPlayer and are expected as
538 * "plain text" by the server, so need to decode it here
540 unescpath=malloc(strlen(path)+1);
541 if (!unescpath) {
542 mp_tmsg(MSGT_NETWORK,MSGL_FATAL,"Memory allocation failed.\n");
543 return -1;
545 url_unescape_string(unescpath,path);
546 path=unescpath;
549 if( url1->port==0 ) {
550 url1->port=1755;
552 s = connect2Server( url1->hostname, url1->port, 1);
553 if( s<0 ) {
554 free(path);
555 return s;
557 mp_tmsg(MSGT_NETWORK,MSGL_INFO,"Connected\n");
559 seq_num=0;
562 * Send the initial connect info including player version no. Client GUID (random) and the host address being connected to.
563 * This command is sent at the very start of protocol initiation. It sends local information to the serve
564 * cmd 1 0x01
565 * */
567 /* prepare for the url encoding conversion */
568 #ifdef CONFIG_ICONV
569 url_conv = iconv_open("UTF-16LE", "UTF-8");
570 #endif
572 snprintf (str, 1023, "\034\003NSPlayer/7.0.0.1956; {33715801-BAB3-9D85-24E9-03B90328270A}; Host: %s", url1->hostname);
573 string_utf16 (data, str, strlen(str));
574 // send_command(s, commandno ....)
575 send_command (s, 1, 0, 0x0004000b, strlen(str)*2+2, data);
577 recv (s, data, BUF_SIZE, 0) ;
579 /*This sends details of the local machine IP address to a Funnel system at the server.
580 * Also, the TCP or UDP transport selection is sent.
582 * here 192.168.0.1 is local ip address TCP/UDP states the tronsport we r using
583 * and 1037 is the local TCP or UDP socket number
584 * cmd 2 0x02
585 * */
587 string_utf16 (&data[8], "\002\000\\\\192.168.0.1\\TCP\\1037", 24);
588 memset (data, 0, 8);
589 send_command (s, 2, 0, 0, 24*2+10, data);
591 recv (s, data, BUF_SIZE, 0) ;
593 /* This command sends file path (at server) and file name request to the server.
594 * 0x5 */
596 string_utf16 (&data[8], path, strlen(path));
597 memset (data, 0, 8);
598 send_command (s, 5, 0, 0, strlen(path)*2+10, data);
599 free(path);
601 get_answer (s);
603 /* The ASF header chunk request. Includes ?session' variable for pre header value.
604 * After this command is sent,
605 * the server replies with 0x11 command and then the header chunk with header data follows.
606 * 0x15 */
608 memset (data, 0, 40);
609 data[32] = 2;
611 send_command (s, 0x15, 1, 0, 40, data);
613 num_stream_ids = 0;
614 /* get_headers(s, asf_header); */
616 asf_header_len = get_header (s, asf_header, stream->streaming_ctrl);
617 // mp_msg(MSGT_NETWORK,MSGL_INFO,"---------------------------------- asf_header %d\n",asf_header);
618 if (asf_header_len==0) { //error reading header
619 closesocket(s);
620 return -1;
622 packet_length = interp_header (asf_header, asf_header_len);
626 * This command is the media stream MBR selector. Switches are always 6 bytes in length.
627 * After all switch elements, data ends with bytes [00 00] 00 20 ac 40 [02].
628 * Where:
629 * [00 00] shows 0x61 0x00 (on the first 33 sent) or 0xff 0xff for ASF files, and with no ending data for WMV files.
630 * It is not yet understood what all this means.
631 * And the last [02] byte is probably the header ?session' value.
633 * 0x33 */
635 memset (data, 0, 40);
637 int audio_id = stream->opts->audio_id;
638 if (audio_id > 0) {
639 data[2] = 0xFF;
640 data[3] = 0xFF;
641 data[4] = audio_id;
642 send_command(s, 0x33, num_stream_ids, 0xFFFF | audio_id << 16, 8, data);
643 } else {
644 for (i=1; i<num_stream_ids; i++) {
645 data [ (i-1) * 6 + 2 ] = 0xFF;
646 data [ (i-1) * 6 + 3 ] = 0xFF;
647 data [ (i-1) * 6 + 4 ] = stream_ids[i];
648 data [ (i-1) * 6 + 5 ] = 0x00;
651 send_command (s, 0x33, num_stream_ids, 0xFFFF | stream_ids[0] << 16, (num_stream_ids-1)*6+2 , data);
654 get_answer (s);
656 /* Start sending file from packet xx.
657 * This command is also used for resume downloads or requesting a lost packet.
658 * Also used for seeking by sending a play point value which seeks to the media time point.
659 * Includes ?session' value in pre header and the maximum media stream time.
660 * 0x07 */
662 memset (data, 0, 40);
664 for (i=8; i<16; i++)
665 data[i] = 0xFF;
667 data[20] = 0x04;
669 send_command (s, 0x07, 1, 0xFFFF | stream_ids[0] << 16, 24, data);
671 stream->fd = s;
672 stream->streaming_ctrl->streaming_read = asf_mmst_streaming_read;
673 stream->streaming_ctrl->streaming_seek = asf_mmst_streaming_seek;
674 stream->streaming_ctrl->buffering = 1;
675 stream->streaming_ctrl->status = streaming_playing_e;
677 packet_length1 = packet_length;
678 mp_msg(MSGT_NETWORK,MSGL_INFO,"mmst packet_length = %d\n", packet_length);
680 #ifdef CONFIG_ICONV
681 if (url_conv != (iconv_t)(-1))
682 iconv_close(url_conv);
683 #endif
685 return 0;