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
25 * along with this program; if not, write to the Free Software
26 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
43 #define closesocket close
60 #include "libmpdemux/asf.h"
70 #define BUF_SIZE 102400
71 #define HDR_BUF_SIZE 8192
72 #define MAX_STREAMS 20
76 uint8_t buf
[BUF_SIZE
];
82 static int num_stream_ids
;
83 static int stream_ids
[MAX_STREAMS
];
85 static int get_data (int s
, char *buf
, size_t count
);
87 static void put_32 (command_t
*cmd
, uint32_t value
)
89 cmd
->buf
[cmd
->num_bytes
] = value
% 256;
91 cmd
->buf
[cmd
->num_bytes
+1] = value
% 256 ;
93 cmd
->buf
[cmd
->num_bytes
+2] = value
% 256 ;
95 cmd
->buf
[cmd
->num_bytes
+3] = value
% 256 ;
100 static uint32_t get_32 (unsigned char *cmd
, int offset
)
105 ret
|= cmd
[offset
+1]<<8 ;
106 ret
|= cmd
[offset
+2]<<16 ;
107 ret
|= cmd
[offset
+3]<<24 ;
112 static void send_command (int s
, int command
, uint32_t switches
,
113 uint32_t extra
, int length
,
119 len8
= (length
+ 7) / 8;
123 put_32 (&cmd
, 0x00000001); /* start sequence */
124 put_32 (&cmd
, 0xB00BFACE); /* #-)) */
125 put_32 (&cmd
, len8
*8 + 32);
126 put_32 (&cmd
, 0x20534d4d); /* protocol type "MMS " */
127 put_32 (&cmd
, len8
+ 4);
128 put_32 (&cmd
, seq_num
);
130 put_32 (&cmd
, 0x0); /* unknown */
132 put_32 (&cmd
, len8
+2);
133 put_32 (&cmd
, 0x00030000 | command
); /* dir | command */
134 put_32 (&cmd
, switches
);
135 put_32 (&cmd
, extra
);
137 memcpy (&cmd
.buf
[48], data
, length
);
139 memset(&cmd
.buf
[48 + length
], 0, 8 - (length
& 7));
141 if (send (s
, cmd
.buf
, len8
*8+48, 0) != (len8
*8+48)) {
142 mp_msg(MSGT_NETWORK
,MSGL_ERR
,MSGTR_MPDEMUX_MMST_WriteError
);
147 static iconv_t url_conv
;
150 static void string_utf16(char *dest
, char *src
, int len
) {
156 if (url_conv
!= (iconv_t
)(-1))
158 memset(dest
, 0, 1000);
159 len1
= len
; len2
= 1000;
162 iconv(url_conv
, &ip
, &len1
, &op
, &len2
);
167 if (len
> 499) len
= 499;
168 for (i
=0; i
<len
; i
++) {
172 /* trailing zeroes */
180 static void get_answer (int s
)
185 while (command
== 0x1b) {
188 len
= recv (s
, data
, BUF_SIZE
, 0) ;
190 mp_msg(MSGT_NETWORK
,MSGL_ERR
,MSGTR_MPDEMUX_MMST_EOFAlert
);
194 command
= get_32 (data
, 36) & 0xFFFF;
197 send_command (s
, 0x1b, 0, 0, 0, data
);
201 static int get_data (int s
, char *buf
, size_t count
)
206 while (total
< count
) {
208 len
= recv (s
, &buf
[total
], count
-total
, 0);
211 perror ("read error:");
218 // mp_msg(MSGT_NETWORK,MSGL_INFO,"[%d/%d]", total, count);
228 static int get_header (int s
, uint8_t *header
, streaming_ctrl_t
*streaming_ctrl
)
230 unsigned char pre_header
[8];
236 if (!get_data (s
, pre_header
, 8)) {
237 mp_msg(MSGT_NETWORK
,MSGL_ERR
,MSGTR_MPDEMUX_MMST_PreHeaderReadFailed
);
240 if (pre_header
[4] == 0x02) {
244 packet_len
= (pre_header
[7] << 8 | pre_header
[6]) - 8;
246 // mp_msg(MSGT_NETWORK,MSGL_INFO,"asf header packet detected, len=%d\n", packet_len);
248 if (packet_len
< 0 || packet_len
> HDR_BUF_SIZE
- header_len
) {
249 mp_msg(MSGT_NETWORK
, MSGL_FATAL
, MSGTR_MPDEMUX_MMST_InvalidHeaderSize
);
253 if (!get_data (s
, &header
[header_len
], packet_len
)) {
254 mp_msg(MSGT_NETWORK
,MSGL_ERR
,MSGTR_MPDEMUX_MMST_HeaderDataReadFailed
);
258 header_len
+= packet_len
;
260 if ( (header
[header_len
-1] == 1) && (header
[header_len
-2]==1)) {
263 if( streaming_bufferize( streaming_ctrl
, header
, header_len
)<0 ) {
267 // mp_msg(MSGT_NETWORK,MSGL_INFO,"get header packet finished\n");
279 if (!get_data (s
, (char*)&packet_len
, 4)) {
280 mp_msg(MSGT_NETWORK
,MSGL_ERR
,MSGTR_MPDEMUX_MMST_packet_lenReadFailed
);
284 packet_len
= get_32 ((unsigned char*)&packet_len
, 0) + 4;
286 // mp_msg(MSGT_NETWORK,MSGL_INFO,"command packet detected, len=%d\n", packet_len);
288 if (packet_len
< 0 || packet_len
> BUF_SIZE
) {
289 mp_msg(MSGT_NETWORK
, MSGL_FATAL
,
290 MSGTR_MPDEMUX_MMST_InvalidRTSPPacketSize
);
294 if (!get_data (s
, data
, packet_len
)) {
295 mp_msg(MSGT_NETWORK
,MSGL_ERR
,MSGTR_MPDEMUX_MMST_CmdDataReadFailed
);
299 command
= get_32 (data
, 24) & 0xFFFF;
301 // mp_msg(MSGT_NETWORK,MSGL_INFO,"command: %02x\n", command);
304 send_command (s
, 0x1b, 0, 0, 0, data
);
308 // mp_msg(MSGT_NETWORK,MSGL_INFO,"get header packet succ\n");
312 static int interp_header (uint8_t *header
, int header_len
)
315 int packet_length
=-1;
322 while (i
<header_len
) {
324 uint64_t guid_1
, guid_2
, length
;
326 guid_2
= (uint64_t)header
[i
] | ((uint64_t)header
[i
+1]<<8)
327 | ((uint64_t)header
[i
+2]<<16) | ((uint64_t)header
[i
+3]<<24)
328 | ((uint64_t)header
[i
+4]<<32) | ((uint64_t)header
[i
+5]<<40)
329 | ((uint64_t)header
[i
+6]<<48) | ((uint64_t)header
[i
+7]<<56);
332 guid_1
= (uint64_t)header
[i
] | ((uint64_t)header
[i
+1]<<8)
333 | ((uint64_t)header
[i
+2]<<16) | ((uint64_t)header
[i
+3]<<24)
334 | ((uint64_t)header
[i
+4]<<32) | ((uint64_t)header
[i
+5]<<40)
335 | ((uint64_t)header
[i
+6]<<48) | ((uint64_t)header
[i
+7]<<56);
338 // mp_msg(MSGT_NETWORK,MSGL_INFO,"guid found: %016llx%016llx\n", guid_1, guid_2);
340 length
= (uint64_t)header
[i
] | ((uint64_t)header
[i
+1]<<8)
341 | ((uint64_t)header
[i
+2]<<16) | ((uint64_t)header
[i
+3]<<24)
342 | ((uint64_t)header
[i
+4]<<32) | ((uint64_t)header
[i
+5]<<40)
343 | ((uint64_t)header
[i
+6]<<48) | ((uint64_t)header
[i
+7]<<56);
347 if ( (guid_1
== 0x6cce6200aa00d9a6ULL
) && (guid_2
== 0x11cf668e75b22630ULL
) ) {
348 mp_msg(MSGT_NETWORK
,MSGL_INFO
,MSGTR_MPDEMUX_MMST_HeaderObject
);
349 } else if ((guid_1
== 0x6cce6200aa00d9a6ULL
) && (guid_2
== 0x11cf668e75b22636ULL
)) {
350 mp_msg(MSGT_NETWORK
,MSGL_INFO
,MSGTR_MPDEMUX_MMST_DataObject
);
351 } else if ((guid_1
== 0x6553200cc000e48eULL
) && (guid_2
== 0x11cfa9478cabdca1ULL
)) {
353 packet_length
= get_32(header
, i
+92-24);
355 mp_msg(MSGT_NETWORK
,MSGL_INFO
,MSGTR_MPDEMUX_MMST_FileObjectPacketLen
,
356 packet_length
, get_32(header
, i
+96-24));
359 } else if ((guid_1
== 0x6553200cc000e68eULL
) && (guid_2
== 0x11cfa9b7b7dc0791ULL
)) {
361 int stream_id
= header
[i
+48] | header
[i
+49] << 8;
363 mp_msg(MSGT_NETWORK
,MSGL_INFO
,MSGTR_MPDEMUX_MMST_StreamObjectStreamID
, stream_id
);
365 if (num_stream_ids
< MAX_STREAMS
) {
366 stream_ids
[num_stream_ids
] = stream_id
;
369 mp_msg(MSGT_NETWORK
,MSGL_ERR
,MSGTR_MPDEMUX_MMST_2ManyStreamID
);
375 printf ("unknown object (guid: %016llx, %016llx, len: %lld)\n", guid_1
, guid_2
, length
);
376 for (; b
< length
; b
++)
378 if (isascii(header
[b
]) || isalpha(header
[b
]))
379 printf("%c ", header
[b
]);
381 printf("%x ", header
[b
]);
385 mp_msg(MSGT_NETWORK
,MSGL_WARN
,MSGTR_MPDEMUX_MMST_UnknownObject
);
389 // mp_msg(MSGT_NETWORK,MSGL_INFO,"length : %lld\n", length);
395 return packet_length
;
400 static int get_media_packet (int s
, int padding
, streaming_ctrl_t
*stream_ctrl
) {
401 unsigned char pre_header
[8];
404 if (!get_data (s
, pre_header
, 8)) {
405 mp_msg(MSGT_NETWORK
,MSGL_ERR
,MSGTR_MPDEMUX_MMST_PreHeaderReadFailed
);
409 // for (i=0; i<8; i++)
410 // mp_msg(MSGT_NETWORK,MSGL_INFO,"pre_header[%d] = %02x (%d)\n",
411 // i, pre_header[i], pre_header[i]);
413 if (pre_header
[4] == 0x04) {
417 packet_len
= (pre_header
[7] << 8 | pre_header
[6]) - 8;
419 // mp_msg(MSGT_NETWORK,MSGL_INFO,"asf media packet detected, len=%d\n", packet_len);
421 if (packet_len
< 0 || packet_len
> BUF_SIZE
) {
422 mp_msg(MSGT_NETWORK
, MSGL_FATAL
, MSGTR_MPDEMUX_MMST_InvalidRTSPPacketSize
);
426 if (!get_data (s
, data
, packet_len
)) {
427 mp_msg(MSGT_NETWORK
,MSGL_ERR
,MSGTR_MPDEMUX_MMST_MediaDataReadFailed
);
431 streaming_bufferize(stream_ctrl
, data
, padding
);
438 if (!get_data (s
, (char*)&packet_len
, 4)) {
439 mp_msg(MSGT_NETWORK
,MSGL_ERR
,MSGTR_MPDEMUX_MMST_packet_lenReadFailed
);
443 packet_len
= get_32 ((unsigned char*)&packet_len
, 0) + 4;
445 if (packet_len
< 0 || packet_len
> BUF_SIZE
) {
446 mp_msg(MSGT_NETWORK
,MSGL_FATAL
,MSGTR_MPDEMUX_MMST_InvalidRTSPPacketSize
);
450 if (!get_data (s
, data
, packet_len
)) {
451 mp_msg(MSGT_NETWORK
,MSGL_ERR
,MSGTR_MPDEMUX_MMST_CmdDataReadFailed
);
455 if ( (pre_header
[7] != 0xb0) || (pre_header
[6] != 0x0b)
456 || (pre_header
[5] != 0xfa) || (pre_header
[4] != 0xce) ) {
458 mp_msg(MSGT_NETWORK
,MSGL_ERR
,MSGTR_MPDEMUX_MMST_MissingSignature
);
462 command
= get_32 (data
, 24) & 0xFFFF;
464 // mp_msg(MSGT_NETWORK,MSGL_INFO,"\ncommand packet detected, len=%d cmd=0x%X\n", packet_len, command);
467 send_command (s
, 0x1b, 0, 0, 0, data
);
468 else if (command
== 0x1e) {
469 mp_msg(MSGT_NETWORK
,MSGL_INFO
,MSGTR_MPDEMUX_MMST_PatentedTechnologyJoke
);
472 else if (command
== 0x21 ) {
473 // Looks like it's new in WMS9
474 // Unknown command, but ignoring it seems to work.
477 else if (command
!= 0x05) {
478 mp_msg(MSGT_NETWORK
,MSGL_ERR
,MSGTR_MPDEMUX_MMST_UnknownCmd
,command
);
483 // mp_msg(MSGT_NETWORK,MSGL_INFO,"get media packet succ\n");
489 static int packet_length1
;
491 static int asf_mmst_streaming_read( int fd
, char *buffer
, int size
, streaming_ctrl_t
*stream_ctrl
)
495 while( stream_ctrl
->buffer_size
==0 ) {
496 // buffer is empty - fill it!
497 int ret
= get_media_packet( fd
, packet_length1
, stream_ctrl
);
499 mp_msg(MSGT_NETWORK
,MSGL_ERR
,MSGTR_MPDEMUX_MMST_GetMediaPacketErr
,strerror(errno
));
501 } else if (ret
==0) //EOF?
505 len
= stream_ctrl
->buffer_size
-stream_ctrl
->buffer_pos
;
506 if(len
>size
) len
=size
;
507 memcpy( buffer
, (stream_ctrl
->buffer
)+(stream_ctrl
->buffer_pos
), len
);
508 stream_ctrl
->buffer_pos
+= len
;
509 if( stream_ctrl
->buffer_pos
>=stream_ctrl
->buffer_size
) {
510 free( stream_ctrl
->buffer
);
511 stream_ctrl
->buffer
= NULL
;
512 stream_ctrl
->buffer_size
= 0;
513 stream_ctrl
->buffer_pos
= 0;
519 static int asf_mmst_streaming_seek( int fd
, off_t pos
, streaming_ctrl_t
*streaming_ctrl
)
522 // Shut up gcc warning
528 int asf_mmst_streaming_start(stream_t
*stream
)
532 uint8_t asf_header
[HDR_BUF_SIZE
];
534 int len
, i
, packet_length
;
535 char *path
, *unescpath
;
536 URL_t
*url1
= stream
->streaming_ctrl
->url
;
540 closesocket( stream
->fd
);
545 path
= strchr(url1
->file
,'/') + 1;
547 /* mmst filename are not url_escaped by MS MediaPlayer and are expected as
548 * "plain text" by the server, so need to decode it here
550 unescpath
=malloc(strlen(path
)+1);
552 mp_msg(MSGT_NETWORK
,MSGL_FATAL
,MSGTR_MemAllocFailed
);
555 url_unescape_string(unescpath
,path
);
559 if( url1
->port
==0 ) {
562 s
= connect2Server( url1
->hostname
, url1
->port
, 1);
567 mp_msg(MSGT_NETWORK
,MSGL_INFO
,MSGTR_MPDEMUX_MMST_Connected
);
572 * Send the initial connect info including player version no. Client GUID (random) and the host address being connected to.
573 * This command is sent at the very start of protocol initiation. It sends local information to the serve
577 /* prepare for the url encoding conversion */
580 url_conv
= iconv_open("UTF-16LE",nl_langinfo(CODESET
));
582 url_conv
= iconv_open("UTF-16LE", NULL
);
586 snprintf (str
, 1023, "\034\003NSPlayer/7.0.0.1956; {33715801-BAB3-9D85-24E9-03B90328270A}; Host: %s", url1
->hostname
);
587 string_utf16 (data
, str
, strlen(str
));
588 // send_command(s, commandno ....)
589 send_command (s
, 1, 0, 0x0004000b, strlen(str
)*2+2, data
);
591 len
= recv (s
, data
, BUF_SIZE
, 0) ;
593 /*This sends details of the local machine IP address to a Funnel system at the server.
594 * Also, the TCP or UDP transport selection is sent.
596 * here 192.168.0.1 is local ip address TCP/UDP states the tronsport we r using
597 * and 1037 is the local TCP or UDP socket number
601 string_utf16 (&data
[8], "\002\000\\\\192.168.0.1\\TCP\\1037", 24);
603 send_command (s
, 2, 0, 0, 24*2+10, data
);
605 len
= recv (s
, data
, BUF_SIZE
, 0) ;
607 /* This command sends file path (at server) and file name request to the server.
610 string_utf16 (&data
[8], path
, strlen(path
));
612 send_command (s
, 5, 0, 0, strlen(path
)*2+10, data
);
617 /* The ASF header chunk request. Includes ?session' variable for pre header value.
618 * After this command is sent,
619 * the server replies with 0x11 command and then the header chunk with header data follows.
622 memset (data
, 0, 40);
625 send_command (s
, 0x15, 1, 0, 40, data
);
628 /* get_headers(s, asf_header); */
630 asf_header_len
= get_header (s
, asf_header
, stream
->streaming_ctrl
);
631 // mp_msg(MSGT_NETWORK,MSGL_INFO,"---------------------------------- asf_header %d\n",asf_header);
632 if (asf_header_len
==0) { //error reading header
636 packet_length
= interp_header (asf_header
, asf_header_len
);
640 * This command is the media stream MBR selector. Switches are always 6 bytes in length.
641 * After all switch elements, data ends with bytes [00 00] 00 20 ac 40 [02].
643 * [00 00] shows 0x61 0x00 (on the first 33 sent) or 0xff 0xff for ASF files, and with no ending data for WMV files.
644 * It is not yet understood what all this means.
645 * And the last [02] byte is probably the header ?session' value.
649 memset (data
, 0, 40);
655 send_command(s
, 0x33, num_stream_ids
, 0xFFFF | audio_id
<< 16, 8, data
);
657 for (i
=1; i
<num_stream_ids
; i
++) {
658 data
[ (i
-1) * 6 + 2 ] = 0xFF;
659 data
[ (i
-1) * 6 + 3 ] = 0xFF;
660 data
[ (i
-1) * 6 + 4 ] = stream_ids
[i
];
661 data
[ (i
-1) * 6 + 5 ] = 0x00;
664 send_command (s
, 0x33, num_stream_ids
, 0xFFFF | stream_ids
[0] << 16, (num_stream_ids
-1)*6+2 , data
);
669 /* Start sending file from packet xx.
670 * This command is also used for resume downloads or requesting a lost packet.
671 * Also used for seeking by sending a play point value which seeks to the media time point.
672 * Includes ?session' value in pre header and the maximum media stream time.
675 memset (data
, 0, 40);
682 send_command (s
, 0x07, 1, 0xFFFF | stream_ids
[0] << 16, 24, data
);
685 stream
->streaming_ctrl
->streaming_read
= asf_mmst_streaming_read
;
686 stream
->streaming_ctrl
->streaming_seek
= asf_mmst_streaming_seek
;
687 stream
->streaming_ctrl
->buffering
= 1;
688 stream
->streaming_ctrl
->status
= streaming_playing_e
;
690 packet_length1
= packet_length
;
691 mp_msg(MSGT_NETWORK
,MSGL_INFO
,"mmst packet_length = %d\n", packet_length
);
694 if (url_conv
!= (iconv_t
)(-1))
695 iconv_close(url_conv
);