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.
50 #include "libmpdemux/asf.h"
53 #include "asf_mmst_streaming.h"
60 #define BUF_SIZE 102400
61 #define HDR_BUF_SIZE 8192
62 #define MAX_STREAMS 20
66 uint8_t buf
[BUF_SIZE
];
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;
81 cmd
->buf
[cmd
->num_bytes
+1] = value
% 256 ;
83 cmd
->buf
[cmd
->num_bytes
+2] = value
% 256 ;
85 cmd
->buf
[cmd
->num_bytes
+3] = value
% 256 ;
90 static uint32_t get_32 (unsigned char *cmd
, int offset
)
95 ret
|= cmd
[offset
+1]<<8 ;
96 ret
|= cmd
[offset
+2]<<16 ;
97 ret
|= cmd
[offset
+3]<<24 ;
102 static void send_command (int s
, int command
, uint32_t switches
,
103 uint32_t extra
, int length
,
109 len8
= (length
+ 7) / 8;
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
);
120 put_32 (&cmd
, 0x0); /* unknown */
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
);
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");
137 static iconv_t url_conv
;
140 static void string_utf16(char *dest
, char *src
, int len
) {
146 if (url_conv
!= (iconv_t
)(-1))
148 memset(dest
, 0, 1000);
149 len1
= len
; len2
= 1000;
152 iconv(url_conv
, &ip
, &len1
, &op
, &len2
);
157 if (len
> 499) len
= 499;
158 for (i
=0; i
<len
; i
++) {
162 /* trailing zeroes */
170 static void get_answer (int s
)
175 while (command
== 0x1b) {
178 len
= recv (s
, data
, BUF_SIZE
, 0) ;
180 mp_tmsg(MSGT_NETWORK
,MSGL_ERR
,"\nAlert! EOF\n");
184 command
= get_32 (data
, 36) & 0xFFFF;
187 send_command (s
, 0x1b, 0, 0, 0, data
);
191 static int get_data (int s
, char *buf
, size_t count
)
196 while (total
< count
) {
198 len
= recv (s
, &buf
[total
], count
-total
, 0);
201 perror ("read error:");
208 // mp_msg(MSGT_NETWORK,MSGL_INFO,"[%d/%d]", total, count);
218 static int get_header (int s
, uint8_t *header
, streaming_ctrl_t
*streaming_ctrl
)
220 unsigned char pre_header
[8];
226 if (!get_data (s
, pre_header
, 8)) {
227 mp_tmsg(MSGT_NETWORK
,MSGL_ERR
,"pre-header read failed\n");
230 if (pre_header
[4] == 0x02) {
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");
243 if (!get_data (s
, &header
[header_len
], packet_len
)) {
244 mp_tmsg(MSGT_NETWORK
,MSGL_ERR
,"Header data read failed.\n");
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 ) {
257 // mp_msg(MSGT_NETWORK,MSGL_INFO,"get header packet finished\n");
269 if (!get_data (s
, (char*)&packet_len
, 4)) {
270 mp_tmsg(MSGT_NETWORK
,MSGL_ERR
,"packet_len read failed.\n");
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");
284 if (!get_data (s
, data
, packet_len
)) {
285 mp_tmsg(MSGT_NETWORK
,MSGL_ERR
,"Command data read failed.\n");
289 command
= get_32 (data
, 24) & 0xFFFF;
291 // mp_msg(MSGT_NETWORK,MSGL_INFO,"command: %02x\n", command);
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
)
305 int packet_length
=-1;
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);
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);
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);
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
;
359 mp_tmsg(MSGT_NETWORK
,MSGL_ERR
,"Too many IDs, stream skipped.");
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
]);
371 printf("%x ", header
[b
]);
375 mp_tmsg(MSGT_NETWORK
,MSGL_WARN
,"unknown object\n");
379 // mp_msg(MSGT_NETWORK,MSGL_INFO,"length : %lld\n", length);
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];
394 if (!get_data (s
, pre_header
, 8)) {
395 mp_tmsg(MSGT_NETWORK
,MSGL_ERR
,"pre-header read failed\n");
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) {
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");
416 if (!get_data (s
, data
, packet_len
)) {
417 mp_tmsg(MSGT_NETWORK
,MSGL_ERR
,"Media data read failed.\n");
421 streaming_bufferize(stream_ctrl
, data
, padding
);
428 if (!get_data (s
, (char*)&packet_len
, 4)) {
429 mp_tmsg(MSGT_NETWORK
,MSGL_ERR
,"packet_len read failed.\n");
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");
440 if (!get_data (s
, data
, packet_len
)) {
441 mp_tmsg(MSGT_NETWORK
,MSGL_ERR
,"Command data read failed.\n");
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");
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);
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");
462 else if (command
== 0x21 ) {
463 // Looks like it's new in WMS9
464 // Unknown command, but ignoring it seems to work.
467 else if (command
!= 0x05) {
468 mp_tmsg(MSGT_NETWORK
,MSGL_ERR
,"unknown command %02x\n",command
);
473 // mp_msg(MSGT_NETWORK,MSGL_INFO,"get media packet succ\n");
479 static int packet_length1
;
481 static int asf_mmst_streaming_read( int fd
, char *buffer
, int size
, streaming_ctrl_t
*stream_ctrl
)
485 while( stream_ctrl
->buffer_size
==0 ) {
486 // buffer is empty - fill it!
487 int ret
= get_media_packet( fd
, packet_length1
, stream_ctrl
);
489 mp_tmsg(MSGT_NETWORK
,MSGL_ERR
,"get_media_packet error : %s\n",strerror(errno
));
491 } else if (ret
==0) //EOF?
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;
509 static int asf_mmst_streaming_seek( int fd
, off_t pos
, streaming_ctrl_t
*streaming_ctrl
)
512 // Shut up gcc warning
518 int asf_mmst_streaming_start(stream_t
*stream
)
522 uint8_t asf_header
[HDR_BUF_SIZE
];
524 int i
, packet_length
;
525 char *path
, *unescpath
;
526 URL_t
*url1
= stream
->streaming_ctrl
->url
;
530 closesocket( stream
->fd
);
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);
542 mp_tmsg(MSGT_NETWORK
,MSGL_FATAL
,"Memory allocation failed.\n");
545 url_unescape_string(unescpath
,path
);
549 if( url1
->port
==0 ) {
552 s
= connect2Server( url1
->hostname
, url1
->port
, 1);
557 mp_tmsg(MSGT_NETWORK
,MSGL_INFO
,"Connected\n");
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
567 /* prepare for the url encoding conversion */
569 url_conv
= iconv_open("UTF-16LE", "UTF-8");
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
587 string_utf16 (&data
[8], "\002\000\\\\192.168.0.1\\TCP\\1037", 24);
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.
596 string_utf16 (&data
[8], path
, strlen(path
));
598 send_command (s
, 5, 0, 0, strlen(path
)*2+10, data
);
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.
608 memset (data
, 0, 40);
611 send_command (s
, 0x15, 1, 0, 40, data
);
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
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].
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.
635 memset (data
, 0, 40);
637 int audio_id
= stream
->opts
->audio_id
;
642 send_command(s
, 0x33, num_stream_ids
, 0xFFFF | audio_id
<< 16, 8, data
);
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
);
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.
662 memset (data
, 0, 40);
669 send_command (s
, 0x07, 1, 0xFFFF | stream_ids
[0] << 16, 24, data
);
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
);
681 if (url_conv
!= (iconv_t
)(-1))
682 iconv_close(url_conv
);