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.
46 #ifndef CONFIG_SETLOCALE
58 #include "libmpdemux/asf.h"
68 #define BUF_SIZE 102400
69 #define HDR_BUF_SIZE 8192
70 #define MAX_STREAMS 20
74 uint8_t buf
[BUF_SIZE
];
80 static int num_stream_ids
;
81 static int stream_ids
[MAX_STREAMS
];
83 static int get_data (int s
, char *buf
, size_t count
);
85 static void put_32 (command_t
*cmd
, uint32_t value
)
87 cmd
->buf
[cmd
->num_bytes
] = value
% 256;
89 cmd
->buf
[cmd
->num_bytes
+1] = value
% 256 ;
91 cmd
->buf
[cmd
->num_bytes
+2] = value
% 256 ;
93 cmd
->buf
[cmd
->num_bytes
+3] = value
% 256 ;
98 static uint32_t get_32 (unsigned char *cmd
, int offset
)
103 ret
|= cmd
[offset
+1]<<8 ;
104 ret
|= cmd
[offset
+2]<<16 ;
105 ret
|= cmd
[offset
+3]<<24 ;
110 static void send_command (int s
, int command
, uint32_t switches
,
111 uint32_t extra
, int length
,
117 len8
= (length
+ 7) / 8;
121 put_32 (&cmd
, 0x00000001); /* start sequence */
122 put_32 (&cmd
, 0xB00BFACE); /* #-)) */
123 put_32 (&cmd
, len8
*8 + 32);
124 put_32 (&cmd
, 0x20534d4d); /* protocol type "MMS " */
125 put_32 (&cmd
, len8
+ 4);
126 put_32 (&cmd
, seq_num
);
128 put_32 (&cmd
, 0x0); /* unknown */
130 put_32 (&cmd
, len8
+2);
131 put_32 (&cmd
, 0x00030000 | command
); /* dir | command */
132 put_32 (&cmd
, switches
);
133 put_32 (&cmd
, extra
);
135 memcpy (&cmd
.buf
[48], data
, length
);
137 memset(&cmd
.buf
[48 + length
], 0, 8 - (length
& 7));
139 if (send (s
, cmd
.buf
, len8
*8+48, 0) != (len8
*8+48)) {
140 mp_msg(MSGT_NETWORK
,MSGL_ERR
,MSGTR_MPDEMUX_MMST_WriteError
);
145 static iconv_t url_conv
;
148 static void string_utf16(char *dest
, char *src
, int len
) {
154 if (url_conv
!= (iconv_t
)(-1))
156 memset(dest
, 0, 1000);
157 len1
= len
; len2
= 1000;
160 iconv(url_conv
, &ip
, &len1
, &op
, &len2
);
165 if (len
> 499) len
= 499;
166 for (i
=0; i
<len
; i
++) {
170 /* trailing zeroes */
178 static void get_answer (int s
)
183 while (command
== 0x1b) {
186 len
= recv (s
, data
, BUF_SIZE
, 0) ;
188 mp_msg(MSGT_NETWORK
,MSGL_ERR
,MSGTR_MPDEMUX_MMST_EOFAlert
);
192 command
= get_32 (data
, 36) & 0xFFFF;
195 send_command (s
, 0x1b, 0, 0, 0, data
);
199 static int get_data (int s
, char *buf
, size_t count
)
204 while (total
< count
) {
206 len
= recv (s
, &buf
[total
], count
-total
, 0);
209 perror ("read error:");
216 // mp_msg(MSGT_NETWORK,MSGL_INFO,"[%d/%d]", total, count);
226 static int get_header (int s
, uint8_t *header
, streaming_ctrl_t
*streaming_ctrl
)
228 unsigned char pre_header
[8];
234 if (!get_data (s
, pre_header
, 8)) {
235 mp_msg(MSGT_NETWORK
,MSGL_ERR
,MSGTR_MPDEMUX_MMST_PreHeaderReadFailed
);
238 if (pre_header
[4] == 0x02) {
242 packet_len
= (pre_header
[7] << 8 | pre_header
[6]) - 8;
244 // mp_msg(MSGT_NETWORK,MSGL_INFO,"asf header packet detected, len=%d\n", packet_len);
246 if (packet_len
< 0 || packet_len
> HDR_BUF_SIZE
- header_len
) {
247 mp_msg(MSGT_NETWORK
, MSGL_FATAL
, MSGTR_MPDEMUX_MMST_InvalidHeaderSize
);
251 if (!get_data (s
, &header
[header_len
], packet_len
)) {
252 mp_msg(MSGT_NETWORK
,MSGL_ERR
,MSGTR_MPDEMUX_MMST_HeaderDataReadFailed
);
256 header_len
+= packet_len
;
258 if ( (header
[header_len
-1] == 1) && (header
[header_len
-2]==1)) {
261 if( streaming_bufferize( streaming_ctrl
, header
, header_len
)<0 ) {
265 // mp_msg(MSGT_NETWORK,MSGL_INFO,"get header packet finished\n");
277 if (!get_data (s
, (char*)&packet_len
, 4)) {
278 mp_msg(MSGT_NETWORK
,MSGL_ERR
,MSGTR_MPDEMUX_MMST_packet_lenReadFailed
);
282 packet_len
= get_32 ((unsigned char*)&packet_len
, 0) + 4;
284 // mp_msg(MSGT_NETWORK,MSGL_INFO,"command packet detected, len=%d\n", packet_len);
286 if (packet_len
< 0 || packet_len
> BUF_SIZE
) {
287 mp_msg(MSGT_NETWORK
, MSGL_FATAL
,
288 MSGTR_MPDEMUX_MMST_InvalidRTSPPacketSize
);
292 if (!get_data (s
, data
, packet_len
)) {
293 mp_msg(MSGT_NETWORK
,MSGL_ERR
,MSGTR_MPDEMUX_MMST_CmdDataReadFailed
);
297 command
= get_32 (data
, 24) & 0xFFFF;
299 // mp_msg(MSGT_NETWORK,MSGL_INFO,"command: %02x\n", command);
302 send_command (s
, 0x1b, 0, 0, 0, data
);
306 // mp_msg(MSGT_NETWORK,MSGL_INFO,"get header packet succ\n");
310 static int interp_header (uint8_t *header
, int header_len
)
313 int packet_length
=-1;
320 while (i
<header_len
) {
322 uint64_t guid_1
, guid_2
, length
;
324 guid_2
= (uint64_t)header
[i
] | ((uint64_t)header
[i
+1]<<8)
325 | ((uint64_t)header
[i
+2]<<16) | ((uint64_t)header
[i
+3]<<24)
326 | ((uint64_t)header
[i
+4]<<32) | ((uint64_t)header
[i
+5]<<40)
327 | ((uint64_t)header
[i
+6]<<48) | ((uint64_t)header
[i
+7]<<56);
330 guid_1
= (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);
336 // mp_msg(MSGT_NETWORK,MSGL_INFO,"guid found: %016llx%016llx\n", guid_1, guid_2);
338 length
= (uint64_t)header
[i
] | ((uint64_t)header
[i
+1]<<8)
339 | ((uint64_t)header
[i
+2]<<16) | ((uint64_t)header
[i
+3]<<24)
340 | ((uint64_t)header
[i
+4]<<32) | ((uint64_t)header
[i
+5]<<40)
341 | ((uint64_t)header
[i
+6]<<48) | ((uint64_t)header
[i
+7]<<56);
345 if ( (guid_1
== 0x6cce6200aa00d9a6ULL
) && (guid_2
== 0x11cf668e75b22630ULL
) ) {
346 mp_msg(MSGT_NETWORK
,MSGL_INFO
,MSGTR_MPDEMUX_MMST_HeaderObject
);
347 } else if ((guid_1
== 0x6cce6200aa00d9a6ULL
) && (guid_2
== 0x11cf668e75b22636ULL
)) {
348 mp_msg(MSGT_NETWORK
,MSGL_INFO
,MSGTR_MPDEMUX_MMST_DataObject
);
349 } else if ((guid_1
== 0x6553200cc000e48eULL
) && (guid_2
== 0x11cfa9478cabdca1ULL
)) {
351 packet_length
= get_32(header
, i
+92-24);
353 mp_msg(MSGT_NETWORK
,MSGL_INFO
,MSGTR_MPDEMUX_MMST_FileObjectPacketLen
,
354 packet_length
, get_32(header
, i
+96-24));
357 } else if ((guid_1
== 0x6553200cc000e68eULL
) && (guid_2
== 0x11cfa9b7b7dc0791ULL
)) {
359 int stream_id
= header
[i
+48] | header
[i
+49] << 8;
361 mp_msg(MSGT_NETWORK
,MSGL_INFO
,MSGTR_MPDEMUX_MMST_StreamObjectStreamID
, stream_id
);
363 if (num_stream_ids
< MAX_STREAMS
) {
364 stream_ids
[num_stream_ids
] = stream_id
;
367 mp_msg(MSGT_NETWORK
,MSGL_ERR
,MSGTR_MPDEMUX_MMST_2ManyStreamID
);
373 printf ("unknown object (guid: %016llx, %016llx, len: %lld)\n", guid_1
, guid_2
, length
);
374 for (; b
< length
; b
++)
376 if (isascii(header
[b
]) || isalpha(header
[b
]))
377 printf("%c ", header
[b
]);
379 printf("%x ", header
[b
]);
383 mp_msg(MSGT_NETWORK
,MSGL_WARN
,MSGTR_MPDEMUX_MMST_UnknownObject
);
387 // mp_msg(MSGT_NETWORK,MSGL_INFO,"length : %lld\n", length);
393 return packet_length
;
398 static int get_media_packet (int s
, int padding
, streaming_ctrl_t
*stream_ctrl
) {
399 unsigned char pre_header
[8];
402 if (!get_data (s
, pre_header
, 8)) {
403 mp_msg(MSGT_NETWORK
,MSGL_ERR
,MSGTR_MPDEMUX_MMST_PreHeaderReadFailed
);
407 // for (i=0; i<8; i++)
408 // mp_msg(MSGT_NETWORK,MSGL_INFO,"pre_header[%d] = %02x (%d)\n",
409 // i, pre_header[i], pre_header[i]);
411 if (pre_header
[4] == 0x04) {
415 packet_len
= (pre_header
[7] << 8 | pre_header
[6]) - 8;
417 // mp_msg(MSGT_NETWORK,MSGL_INFO,"asf media packet detected, len=%d\n", packet_len);
419 if (packet_len
< 0 || packet_len
> BUF_SIZE
) {
420 mp_msg(MSGT_NETWORK
, MSGL_FATAL
, MSGTR_MPDEMUX_MMST_InvalidRTSPPacketSize
);
424 if (!get_data (s
, data
, packet_len
)) {
425 mp_msg(MSGT_NETWORK
,MSGL_ERR
,MSGTR_MPDEMUX_MMST_MediaDataReadFailed
);
429 streaming_bufferize(stream_ctrl
, data
, padding
);
436 if (!get_data (s
, (char*)&packet_len
, 4)) {
437 mp_msg(MSGT_NETWORK
,MSGL_ERR
,MSGTR_MPDEMUX_MMST_packet_lenReadFailed
);
441 packet_len
= get_32 ((unsigned char*)&packet_len
, 0) + 4;
443 if (packet_len
< 0 || packet_len
> BUF_SIZE
) {
444 mp_msg(MSGT_NETWORK
,MSGL_FATAL
,MSGTR_MPDEMUX_MMST_InvalidRTSPPacketSize
);
448 if (!get_data (s
, data
, packet_len
)) {
449 mp_msg(MSGT_NETWORK
,MSGL_ERR
,MSGTR_MPDEMUX_MMST_CmdDataReadFailed
);
453 if ( (pre_header
[7] != 0xb0) || (pre_header
[6] != 0x0b)
454 || (pre_header
[5] != 0xfa) || (pre_header
[4] != 0xce) ) {
456 mp_msg(MSGT_NETWORK
,MSGL_ERR
,MSGTR_MPDEMUX_MMST_MissingSignature
);
460 command
= get_32 (data
, 24) & 0xFFFF;
462 // mp_msg(MSGT_NETWORK,MSGL_INFO,"\ncommand packet detected, len=%d cmd=0x%X\n", packet_len, command);
465 send_command (s
, 0x1b, 0, 0, 0, data
);
466 else if (command
== 0x1e) {
467 mp_msg(MSGT_NETWORK
,MSGL_INFO
,MSGTR_MPDEMUX_MMST_PatentedTechnologyJoke
);
470 else if (command
== 0x21 ) {
471 // Looks like it's new in WMS9
472 // Unknown command, but ignoring it seems to work.
475 else if (command
!= 0x05) {
476 mp_msg(MSGT_NETWORK
,MSGL_ERR
,MSGTR_MPDEMUX_MMST_UnknownCmd
,command
);
481 // mp_msg(MSGT_NETWORK,MSGL_INFO,"get media packet succ\n");
487 static int packet_length1
;
489 static int asf_mmst_streaming_read( int fd
, char *buffer
, int size
, streaming_ctrl_t
*stream_ctrl
)
493 while( stream_ctrl
->buffer_size
==0 ) {
494 // buffer is empty - fill it!
495 int ret
= get_media_packet( fd
, packet_length1
, stream_ctrl
);
497 mp_msg(MSGT_NETWORK
,MSGL_ERR
,MSGTR_MPDEMUX_MMST_GetMediaPacketErr
,strerror(errno
));
499 } else if (ret
==0) //EOF?
503 len
= stream_ctrl
->buffer_size
-stream_ctrl
->buffer_pos
;
504 if(len
>size
) len
=size
;
505 memcpy( buffer
, (stream_ctrl
->buffer
)+(stream_ctrl
->buffer_pos
), len
);
506 stream_ctrl
->buffer_pos
+= len
;
507 if( stream_ctrl
->buffer_pos
>=stream_ctrl
->buffer_size
) {
508 free( stream_ctrl
->buffer
);
509 stream_ctrl
->buffer
= NULL
;
510 stream_ctrl
->buffer_size
= 0;
511 stream_ctrl
->buffer_pos
= 0;
517 static int asf_mmst_streaming_seek( int fd
, off_t pos
, streaming_ctrl_t
*streaming_ctrl
)
520 // Shut up gcc warning
526 int asf_mmst_streaming_start(stream_t
*stream
)
530 uint8_t asf_header
[HDR_BUF_SIZE
];
532 int len
, i
, packet_length
;
533 char *path
, *unescpath
;
534 URL_t
*url1
= stream
->streaming_ctrl
->url
;
538 closesocket( stream
->fd
);
543 path
= strchr(url1
->file
,'/') + 1;
545 /* mmst filename are not url_escaped by MS MediaPlayer and are expected as
546 * "plain text" by the server, so need to decode it here
548 unescpath
=malloc(strlen(path
)+1);
550 mp_msg(MSGT_NETWORK
,MSGL_FATAL
,MSGTR_MemAllocFailed
);
553 url_unescape_string(unescpath
,path
);
557 if( url1
->port
==0 ) {
560 s
= connect2Server( url1
->hostname
, url1
->port
, 1);
565 mp_msg(MSGT_NETWORK
,MSGL_INFO
,MSGTR_MPDEMUX_MMST_Connected
);
570 * Send the initial connect info including player version no. Client GUID (random) and the host address being connected to.
571 * This command is sent at the very start of protocol initiation. It sends local information to the serve
575 /* prepare for the url encoding conversion */
578 url_conv
= iconv_open("UTF-16LE",nl_langinfo(CODESET
));
580 url_conv
= iconv_open("UTF-16LE", NULL
);
584 snprintf (str
, 1023, "\034\003NSPlayer/7.0.0.1956; {33715801-BAB3-9D85-24E9-03B90328270A}; Host: %s", url1
->hostname
);
585 string_utf16 (data
, str
, strlen(str
));
586 // send_command(s, commandno ....)
587 send_command (s
, 1, 0, 0x0004000b, strlen(str
)*2+2, data
);
589 len
= recv (s
, data
, BUF_SIZE
, 0) ;
591 /*This sends details of the local machine IP address to a Funnel system at the server.
592 * Also, the TCP or UDP transport selection is sent.
594 * here 192.168.0.1 is local ip address TCP/UDP states the tronsport we r using
595 * and 1037 is the local TCP or UDP socket number
599 string_utf16 (&data
[8], "\002\000\\\\192.168.0.1\\TCP\\1037", 24);
601 send_command (s
, 2, 0, 0, 24*2+10, data
);
603 len
= recv (s
, data
, BUF_SIZE
, 0) ;
605 /* This command sends file path (at server) and file name request to the server.
608 string_utf16 (&data
[8], path
, strlen(path
));
610 send_command (s
, 5, 0, 0, strlen(path
)*2+10, data
);
615 /* The ASF header chunk request. Includes ?session' variable for pre header value.
616 * After this command is sent,
617 * the server replies with 0x11 command and then the header chunk with header data follows.
620 memset (data
, 0, 40);
623 send_command (s
, 0x15, 1, 0, 40, data
);
626 /* get_headers(s, asf_header); */
628 asf_header_len
= get_header (s
, asf_header
, stream
->streaming_ctrl
);
629 // mp_msg(MSGT_NETWORK,MSGL_INFO,"---------------------------------- asf_header %d\n",asf_header);
630 if (asf_header_len
==0) { //error reading header
634 packet_length
= interp_header (asf_header
, asf_header_len
);
638 * This command is the media stream MBR selector. Switches are always 6 bytes in length.
639 * After all switch elements, data ends with bytes [00 00] 00 20 ac 40 [02].
641 * [00 00] shows 0x61 0x00 (on the first 33 sent) or 0xff 0xff for ASF files, and with no ending data for WMV files.
642 * It is not yet understood what all this means.
643 * And the last [02] byte is probably the header ?session' value.
647 memset (data
, 0, 40);
653 send_command(s
, 0x33, num_stream_ids
, 0xFFFF | audio_id
<< 16, 8, data
);
655 for (i
=1; i
<num_stream_ids
; i
++) {
656 data
[ (i
-1) * 6 + 2 ] = 0xFF;
657 data
[ (i
-1) * 6 + 3 ] = 0xFF;
658 data
[ (i
-1) * 6 + 4 ] = stream_ids
[i
];
659 data
[ (i
-1) * 6 + 5 ] = 0x00;
662 send_command (s
, 0x33, num_stream_ids
, 0xFFFF | stream_ids
[0] << 16, (num_stream_ids
-1)*6+2 , data
);
667 /* Start sending file from packet xx.
668 * This command is also used for resume downloads or requesting a lost packet.
669 * Also used for seeking by sending a play point value which seeks to the media time point.
670 * Includes ?session' value in pre header and the maximum media stream time.
673 memset (data
, 0, 40);
680 send_command (s
, 0x07, 1, 0xFFFF | stream_ids
[0] << 16, 24, data
);
683 stream
->streaming_ctrl
->streaming_read
= asf_mmst_streaming_read
;
684 stream
->streaming_ctrl
->streaming_seek
= asf_mmst_streaming_seek
;
685 stream
->streaming_ctrl
->buffering
= 1;
686 stream
->streaming_ctrl
->status
= streaming_playing_e
;
688 packet_length1
= packet_length
;
689 mp_msg(MSGT_NETWORK
,MSGL_INFO
,"mmst packet_length = %d\n", packet_length
);
692 if (url_conv
!= (iconv_t
)(-1))
693 iconv_close(url_conv
);