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
55 #include "libmpdemux/asf.h"
65 #define BUF_SIZE 102400
66 #define HDR_BUF_SIZE 8192
67 #define MAX_STREAMS 20
71 uint8_t buf
[BUF_SIZE
];
77 static int num_stream_ids
;
78 static int stream_ids
[MAX_STREAMS
];
80 static int get_data (int s
, char *buf
, size_t count
);
82 static void put_32 (command_t
*cmd
, uint32_t value
)
84 cmd
->buf
[cmd
->num_bytes
] = value
% 256;
86 cmd
->buf
[cmd
->num_bytes
+1] = value
% 256 ;
88 cmd
->buf
[cmd
->num_bytes
+2] = value
% 256 ;
90 cmd
->buf
[cmd
->num_bytes
+3] = value
% 256 ;
95 static uint32_t get_32 (unsigned char *cmd
, int offset
)
100 ret
|= cmd
[offset
+1]<<8 ;
101 ret
|= cmd
[offset
+2]<<16 ;
102 ret
|= cmd
[offset
+3]<<24 ;
107 static void send_command (int s
, int command
, uint32_t switches
,
108 uint32_t extra
, int length
,
114 len8
= (length
+ 7) / 8;
118 put_32 (&cmd
, 0x00000001); /* start sequence */
119 put_32 (&cmd
, 0xB00BFACE); /* #-)) */
120 put_32 (&cmd
, len8
*8 + 32);
121 put_32 (&cmd
, 0x20534d4d); /* protocol type "MMS " */
122 put_32 (&cmd
, len8
+ 4);
123 put_32 (&cmd
, seq_num
);
125 put_32 (&cmd
, 0x0); /* unknown */
127 put_32 (&cmd
, len8
+2);
128 put_32 (&cmd
, 0x00030000 | command
); /* dir | command */
129 put_32 (&cmd
, switches
);
130 put_32 (&cmd
, extra
);
132 memcpy (&cmd
.buf
[48], data
, length
);
134 memset(&cmd
.buf
[48 + length
], 0, 8 - (length
& 7));
136 if (send (s
, cmd
.buf
, len8
*8+48, 0) != (len8
*8+48)) {
137 mp_msg(MSGT_NETWORK
,MSGL_ERR
,MSGTR_MPDEMUX_MMST_WriteError
);
142 static iconv_t url_conv
;
145 static void string_utf16(char *dest
, char *src
, int len
) {
151 if (url_conv
!= (iconv_t
)(-1))
153 memset(dest
, 0, 1000);
154 len1
= len
; len2
= 1000;
157 iconv(url_conv
, &ip
, &len1
, &op
, &len2
);
162 if (len
> 499) len
= 499;
163 for (i
=0; i
<len
; i
++) {
167 /* trailing zeroes */
175 static void get_answer (int s
)
180 while (command
== 0x1b) {
183 len
= recv (s
, data
, BUF_SIZE
, 0) ;
185 mp_msg(MSGT_NETWORK
,MSGL_ERR
,MSGTR_MPDEMUX_MMST_EOFAlert
);
189 command
= get_32 (data
, 36) & 0xFFFF;
192 send_command (s
, 0x1b, 0, 0, 0, data
);
196 static int get_data (int s
, char *buf
, size_t count
)
201 while (total
< count
) {
203 len
= recv (s
, &buf
[total
], count
-total
, 0);
206 perror ("read error:");
213 // mp_msg(MSGT_NETWORK,MSGL_INFO,"[%d/%d]", total, count);
223 static int get_header (int s
, uint8_t *header
, streaming_ctrl_t
*streaming_ctrl
)
225 unsigned char pre_header
[8];
231 if (!get_data (s
, pre_header
, 8)) {
232 mp_msg(MSGT_NETWORK
,MSGL_ERR
,MSGTR_MPDEMUX_MMST_PreHeaderReadFailed
);
235 if (pre_header
[4] == 0x02) {
239 packet_len
= (pre_header
[7] << 8 | pre_header
[6]) - 8;
241 // mp_msg(MSGT_NETWORK,MSGL_INFO,"asf header packet detected, len=%d\n", packet_len);
243 if (packet_len
< 0 || packet_len
> HDR_BUF_SIZE
- header_len
) {
244 mp_msg(MSGT_NETWORK
, MSGL_FATAL
, MSGTR_MPDEMUX_MMST_InvalidHeaderSize
);
248 if (!get_data (s
, &header
[header_len
], packet_len
)) {
249 mp_msg(MSGT_NETWORK
,MSGL_ERR
,MSGTR_MPDEMUX_MMST_HeaderDataReadFailed
);
253 header_len
+= packet_len
;
255 if ( (header
[header_len
-1] == 1) && (header
[header_len
-2]==1)) {
258 if( streaming_bufferize( streaming_ctrl
, header
, header_len
)<0 ) {
262 // mp_msg(MSGT_NETWORK,MSGL_INFO,"get header packet finished\n");
274 if (!get_data (s
, (char*)&packet_len
, 4)) {
275 mp_msg(MSGT_NETWORK
,MSGL_ERR
,MSGTR_MPDEMUX_MMST_packet_lenReadFailed
);
279 packet_len
= get_32 ((unsigned char*)&packet_len
, 0) + 4;
281 // mp_msg(MSGT_NETWORK,MSGL_INFO,"command packet detected, len=%d\n", packet_len);
283 if (packet_len
< 0 || packet_len
> BUF_SIZE
) {
284 mp_msg(MSGT_NETWORK
, MSGL_FATAL
,
285 MSGTR_MPDEMUX_MMST_InvalidRTSPPacketSize
);
289 if (!get_data (s
, data
, packet_len
)) {
290 mp_msg(MSGT_NETWORK
,MSGL_ERR
,MSGTR_MPDEMUX_MMST_CmdDataReadFailed
);
294 command
= get_32 (data
, 24) & 0xFFFF;
296 // mp_msg(MSGT_NETWORK,MSGL_INFO,"command: %02x\n", command);
299 send_command (s
, 0x1b, 0, 0, 0, data
);
303 // mp_msg(MSGT_NETWORK,MSGL_INFO,"get header packet succ\n");
307 static int interp_header (uint8_t *header
, int header_len
)
310 int packet_length
=-1;
317 while (i
<header_len
) {
319 uint64_t guid_1
, guid_2
, length
;
321 guid_2
= (uint64_t)header
[i
] | ((uint64_t)header
[i
+1]<<8)
322 | ((uint64_t)header
[i
+2]<<16) | ((uint64_t)header
[i
+3]<<24)
323 | ((uint64_t)header
[i
+4]<<32) | ((uint64_t)header
[i
+5]<<40)
324 | ((uint64_t)header
[i
+6]<<48) | ((uint64_t)header
[i
+7]<<56);
327 guid_1
= (uint64_t)header
[i
] | ((uint64_t)header
[i
+1]<<8)
328 | ((uint64_t)header
[i
+2]<<16) | ((uint64_t)header
[i
+3]<<24)
329 | ((uint64_t)header
[i
+4]<<32) | ((uint64_t)header
[i
+5]<<40)
330 | ((uint64_t)header
[i
+6]<<48) | ((uint64_t)header
[i
+7]<<56);
333 // mp_msg(MSGT_NETWORK,MSGL_INFO,"guid found: %016llx%016llx\n", guid_1, guid_2);
335 length
= (uint64_t)header
[i
] | ((uint64_t)header
[i
+1]<<8)
336 | ((uint64_t)header
[i
+2]<<16) | ((uint64_t)header
[i
+3]<<24)
337 | ((uint64_t)header
[i
+4]<<32) | ((uint64_t)header
[i
+5]<<40)
338 | ((uint64_t)header
[i
+6]<<48) | ((uint64_t)header
[i
+7]<<56);
342 if ( (guid_1
== 0x6cce6200aa00d9a6ULL
) && (guid_2
== 0x11cf668e75b22630ULL
) ) {
343 mp_msg(MSGT_NETWORK
,MSGL_INFO
,MSGTR_MPDEMUX_MMST_HeaderObject
);
344 } else if ((guid_1
== 0x6cce6200aa00d9a6ULL
) && (guid_2
== 0x11cf668e75b22636ULL
)) {
345 mp_msg(MSGT_NETWORK
,MSGL_INFO
,MSGTR_MPDEMUX_MMST_DataObject
);
346 } else if ((guid_1
== 0x6553200cc000e48eULL
) && (guid_2
== 0x11cfa9478cabdca1ULL
)) {
348 packet_length
= get_32(header
, i
+92-24);
350 mp_msg(MSGT_NETWORK
,MSGL_INFO
,MSGTR_MPDEMUX_MMST_FileObjectPacketLen
,
351 packet_length
, get_32(header
, i
+96-24));
354 } else if ((guid_1
== 0x6553200cc000e68eULL
) && (guid_2
== 0x11cfa9b7b7dc0791ULL
)) {
356 int stream_id
= header
[i
+48] | header
[i
+49] << 8;
358 mp_msg(MSGT_NETWORK
,MSGL_INFO
,MSGTR_MPDEMUX_MMST_StreamObjectStreamID
, stream_id
);
360 if (num_stream_ids
< MAX_STREAMS
) {
361 stream_ids
[num_stream_ids
] = stream_id
;
364 mp_msg(MSGT_NETWORK
,MSGL_ERR
,MSGTR_MPDEMUX_MMST_2ManyStreamID
);
370 printf ("unknown object (guid: %016llx, %016llx, len: %lld)\n", guid_1
, guid_2
, length
);
371 for (; b
< length
; b
++)
373 if (isascii(header
[b
]) || isalpha(header
[b
]))
374 printf("%c ", header
[b
]);
376 printf("%x ", header
[b
]);
380 mp_msg(MSGT_NETWORK
,MSGL_WARN
,MSGTR_MPDEMUX_MMST_UnknownObject
);
384 // mp_msg(MSGT_NETWORK,MSGL_INFO,"length : %lld\n", length);
390 return packet_length
;
395 static int get_media_packet (int s
, int padding
, streaming_ctrl_t
*stream_ctrl
) {
396 unsigned char pre_header
[8];
399 if (!get_data (s
, pre_header
, 8)) {
400 mp_msg(MSGT_NETWORK
,MSGL_ERR
,MSGTR_MPDEMUX_MMST_PreHeaderReadFailed
);
404 // for (i=0; i<8; i++)
405 // mp_msg(MSGT_NETWORK,MSGL_INFO,"pre_header[%d] = %02x (%d)\n",
406 // i, pre_header[i], pre_header[i]);
408 if (pre_header
[4] == 0x04) {
412 packet_len
= (pre_header
[7] << 8 | pre_header
[6]) - 8;
414 // mp_msg(MSGT_NETWORK,MSGL_INFO,"asf media packet detected, len=%d\n", packet_len);
416 if (packet_len
< 0 || packet_len
> BUF_SIZE
) {
417 mp_msg(MSGT_NETWORK
, MSGL_FATAL
, MSGTR_MPDEMUX_MMST_InvalidRTSPPacketSize
);
421 if (!get_data (s
, data
, packet_len
)) {
422 mp_msg(MSGT_NETWORK
,MSGL_ERR
,MSGTR_MPDEMUX_MMST_MediaDataReadFailed
);
426 streaming_bufferize(stream_ctrl
, data
, padding
);
433 if (!get_data (s
, (char*)&packet_len
, 4)) {
434 mp_msg(MSGT_NETWORK
,MSGL_ERR
,MSGTR_MPDEMUX_MMST_packet_lenReadFailed
);
438 packet_len
= get_32 ((unsigned char*)&packet_len
, 0) + 4;
440 if (packet_len
< 0 || packet_len
> BUF_SIZE
) {
441 mp_msg(MSGT_NETWORK
,MSGL_FATAL
,MSGTR_MPDEMUX_MMST_InvalidRTSPPacketSize
);
445 if (!get_data (s
, data
, packet_len
)) {
446 mp_msg(MSGT_NETWORK
,MSGL_ERR
,MSGTR_MPDEMUX_MMST_CmdDataReadFailed
);
450 if ( (pre_header
[7] != 0xb0) || (pre_header
[6] != 0x0b)
451 || (pre_header
[5] != 0xfa) || (pre_header
[4] != 0xce) ) {
453 mp_msg(MSGT_NETWORK
,MSGL_ERR
,MSGTR_MPDEMUX_MMST_MissingSignature
);
457 command
= get_32 (data
, 24) & 0xFFFF;
459 // mp_msg(MSGT_NETWORK,MSGL_INFO,"\ncommand packet detected, len=%d cmd=0x%X\n", packet_len, command);
462 send_command (s
, 0x1b, 0, 0, 0, data
);
463 else if (command
== 0x1e) {
464 mp_msg(MSGT_NETWORK
,MSGL_INFO
,MSGTR_MPDEMUX_MMST_PatentedTechnologyJoke
);
467 else if (command
== 0x21 ) {
468 // Looks like it's new in WMS9
469 // Unknown command, but ignoring it seems to work.
472 else if (command
!= 0x05) {
473 mp_msg(MSGT_NETWORK
,MSGL_ERR
,MSGTR_MPDEMUX_MMST_UnknownCmd
,command
);
478 // mp_msg(MSGT_NETWORK,MSGL_INFO,"get media packet succ\n");
484 static int packet_length1
;
486 static int asf_mmst_streaming_read( int fd
, char *buffer
, int size
, streaming_ctrl_t
*stream_ctrl
)
490 while( stream_ctrl
->buffer_size
==0 ) {
491 // buffer is empty - fill it!
492 int ret
= get_media_packet( fd
, packet_length1
, stream_ctrl
);
494 mp_msg(MSGT_NETWORK
,MSGL_ERR
,MSGTR_MPDEMUX_MMST_GetMediaPacketErr
,strerror(errno
));
496 } else if (ret
==0) //EOF?
500 len
= stream_ctrl
->buffer_size
-stream_ctrl
->buffer_pos
;
501 if(len
>size
) len
=size
;
502 memcpy( buffer
, (stream_ctrl
->buffer
)+(stream_ctrl
->buffer_pos
), len
);
503 stream_ctrl
->buffer_pos
+= len
;
504 if( stream_ctrl
->buffer_pos
>=stream_ctrl
->buffer_size
) {
505 free( stream_ctrl
->buffer
);
506 stream_ctrl
->buffer
= NULL
;
507 stream_ctrl
->buffer_size
= 0;
508 stream_ctrl
->buffer_pos
= 0;
514 static int asf_mmst_streaming_seek( int fd
, off_t pos
, streaming_ctrl_t
*streaming_ctrl
)
517 // Shut up gcc warning
523 int asf_mmst_streaming_start(stream_t
*stream
)
527 uint8_t asf_header
[HDR_BUF_SIZE
];
529 int len
, i
, packet_length
;
530 char *path
, *unescpath
;
531 URL_t
*url1
= stream
->streaming_ctrl
->url
;
535 closesocket( stream
->fd
);
540 path
= strchr(url1
->file
,'/') + 1;
542 /* mmst filename are not url_escaped by MS MediaPlayer and are expected as
543 * "plain text" by the server, so need to decode it here
545 unescpath
=malloc(strlen(path
)+1);
547 mp_msg(MSGT_NETWORK
,MSGL_FATAL
,MSGTR_MemAllocFailed
);
550 url_unescape_string(unescpath
,path
);
554 if( url1
->port
==0 ) {
557 s
= connect2Server( url1
->hostname
, url1
->port
, 1);
562 mp_msg(MSGT_NETWORK
,MSGL_INFO
,MSGTR_MPDEMUX_MMST_Connected
);
567 * Send the initial connect info including player version no. Client GUID (random) and the host address being connected to.
568 * This command is sent at the very start of protocol initiation. It sends local information to the serve
572 /* prepare for the url encoding conversion */
574 url_conv
= iconv_open("UTF-16LE", "UTF-8");
577 snprintf (str
, 1023, "\034\003NSPlayer/7.0.0.1956; {33715801-BAB3-9D85-24E9-03B90328270A}; Host: %s", url1
->hostname
);
578 string_utf16 (data
, str
, strlen(str
));
579 // send_command(s, commandno ....)
580 send_command (s
, 1, 0, 0x0004000b, strlen(str
)*2+2, data
);
582 len
= recv (s
, data
, BUF_SIZE
, 0) ;
584 /*This sends details of the local machine IP address to a Funnel system at the server.
585 * Also, the TCP or UDP transport selection is sent.
587 * here 192.168.0.1 is local ip address TCP/UDP states the tronsport we r using
588 * and 1037 is the local TCP or UDP socket number
592 string_utf16 (&data
[8], "\002\000\\\\192.168.0.1\\TCP\\1037", 24);
594 send_command (s
, 2, 0, 0, 24*2+10, data
);
596 len
= recv (s
, data
, BUF_SIZE
, 0) ;
598 /* This command sends file path (at server) and file name request to the server.
601 string_utf16 (&data
[8], path
, strlen(path
));
603 send_command (s
, 5, 0, 0, strlen(path
)*2+10, data
);
608 /* The ASF header chunk request. Includes ?session' variable for pre header value.
609 * After this command is sent,
610 * the server replies with 0x11 command and then the header chunk with header data follows.
613 memset (data
, 0, 40);
616 send_command (s
, 0x15, 1, 0, 40, data
);
619 /* get_headers(s, asf_header); */
621 asf_header_len
= get_header (s
, asf_header
, stream
->streaming_ctrl
);
622 // mp_msg(MSGT_NETWORK,MSGL_INFO,"---------------------------------- asf_header %d\n",asf_header);
623 if (asf_header_len
==0) { //error reading header
627 packet_length
= interp_header (asf_header
, asf_header_len
);
631 * This command is the media stream MBR selector. Switches are always 6 bytes in length.
632 * After all switch elements, data ends with bytes [00 00] 00 20 ac 40 [02].
634 * [00 00] shows 0x61 0x00 (on the first 33 sent) or 0xff 0xff for ASF files, and with no ending data for WMV files.
635 * It is not yet understood what all this means.
636 * And the last [02] byte is probably the header ?session' value.
640 memset (data
, 0, 40);
646 send_command(s
, 0x33, num_stream_ids
, 0xFFFF | audio_id
<< 16, 8, data
);
648 for (i
=1; i
<num_stream_ids
; i
++) {
649 data
[ (i
-1) * 6 + 2 ] = 0xFF;
650 data
[ (i
-1) * 6 + 3 ] = 0xFF;
651 data
[ (i
-1) * 6 + 4 ] = stream_ids
[i
];
652 data
[ (i
-1) * 6 + 5 ] = 0x00;
655 send_command (s
, 0x33, num_stream_ids
, 0xFFFF | stream_ids
[0] << 16, (num_stream_ids
-1)*6+2 , data
);
660 /* Start sending file from packet xx.
661 * This command is also used for resume downloads or requesting a lost packet.
662 * Also used for seeking by sending a play point value which seeks to the media time point.
663 * Includes ?session' value in pre header and the maximum media stream time.
666 memset (data
, 0, 40);
673 send_command (s
, 0x07, 1, 0xFFFF | stream_ids
[0] << 16, 24, data
);
676 stream
->streaming_ctrl
->streaming_read
= asf_mmst_streaming_read
;
677 stream
->streaming_ctrl
->streaming_seek
= asf_mmst_streaming_seek
;
678 stream
->streaming_ctrl
->buffering
= 1;
679 stream
->streaming_ctrl
->status
= streaming_playing_e
;
681 packet_length1
= packet_length
;
682 mp_msg(MSGT_NETWORK
,MSGL_INFO
,"mmst packet_length = %d\n", packet_length
);
685 if (url_conv
!= (iconv_t
)(-1))
686 iconv_close(url_conv
);