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.
51 #include "libmpdemux/asf.h"
54 #include "asf_mmst_streaming.h"
61 #define BUF_SIZE 102400
62 #define HDR_BUF_SIZE 8192
63 #define MAX_STREAMS 20
67 uint8_t buf
[BUF_SIZE
];
73 static int num_stream_ids
;
74 static int stream_ids
[MAX_STREAMS
];
76 static int get_data (int s
, char *buf
, size_t count
);
78 static void put_32 (command_t
*cmd
, uint32_t value
)
80 cmd
->buf
[cmd
->num_bytes
] = value
% 256;
82 cmd
->buf
[cmd
->num_bytes
+1] = value
% 256 ;
84 cmd
->buf
[cmd
->num_bytes
+2] = value
% 256 ;
86 cmd
->buf
[cmd
->num_bytes
+3] = value
% 256 ;
91 static uint32_t get_32 (unsigned char *cmd
, int offset
)
96 ret
|= cmd
[offset
+1]<<8 ;
97 ret
|= cmd
[offset
+2]<<16 ;
98 ret
|= cmd
[offset
+3]<<24 ;
103 static void send_command (int s
, int command
, uint32_t switches
,
104 uint32_t extra
, int length
,
110 len8
= (length
+ 7) / 8;
114 put_32 (&cmd
, 0x00000001); /* start sequence */
115 put_32 (&cmd
, 0xB00BFACE); /* #-)) */
116 put_32 (&cmd
, len8
*8 + 32);
117 put_32 (&cmd
, 0x20534d4d); /* protocol type "MMS " */
118 put_32 (&cmd
, len8
+ 4);
119 put_32 (&cmd
, seq_num
);
121 put_32 (&cmd
, 0x0); /* unknown */
123 put_32 (&cmd
, len8
+2);
124 put_32 (&cmd
, 0x00030000 | command
); /* dir | command */
125 put_32 (&cmd
, switches
);
126 put_32 (&cmd
, extra
);
128 memcpy (&cmd
.buf
[48], data
, length
);
130 memset(&cmd
.buf
[48 + length
], 0, 8 - (length
& 7));
132 if (send (s
, cmd
.buf
, len8
*8+48, 0) != (len8
*8+48)) {
133 mp_msg(MSGT_NETWORK
,MSGL_ERR
,MSGTR_MPDEMUX_MMST_WriteError
);
138 static iconv_t url_conv
;
141 static void string_utf16(char *dest
, char *src
, int len
) {
147 if (url_conv
!= (iconv_t
)(-1))
149 memset(dest
, 0, 1000);
150 len1
= len
; len2
= 1000;
153 iconv(url_conv
, &ip
, &len1
, &op
, &len2
);
158 if (len
> 499) len
= 499;
159 for (i
=0; i
<len
; i
++) {
163 /* trailing zeroes */
171 static void get_answer (int s
)
176 while (command
== 0x1b) {
179 len
= recv (s
, data
, BUF_SIZE
, 0) ;
181 mp_msg(MSGT_NETWORK
,MSGL_ERR
,MSGTR_MPDEMUX_MMST_EOFAlert
);
185 command
= get_32 (data
, 36) & 0xFFFF;
188 send_command (s
, 0x1b, 0, 0, 0, data
);
192 static int get_data (int s
, char *buf
, size_t count
)
197 while (total
< count
) {
199 len
= recv (s
, &buf
[total
], count
-total
, 0);
202 perror ("read error:");
209 // mp_msg(MSGT_NETWORK,MSGL_INFO,"[%d/%d]", total, count);
219 static int get_header (int s
, uint8_t *header
, streaming_ctrl_t
*streaming_ctrl
)
221 unsigned char pre_header
[8];
227 if (!get_data (s
, pre_header
, 8)) {
228 mp_msg(MSGT_NETWORK
,MSGL_ERR
,MSGTR_MPDEMUX_MMST_PreHeaderReadFailed
);
231 if (pre_header
[4] == 0x02) {
235 packet_len
= (pre_header
[7] << 8 | pre_header
[6]) - 8;
237 // mp_msg(MSGT_NETWORK,MSGL_INFO,"asf header packet detected, len=%d\n", packet_len);
239 if (packet_len
< 0 || packet_len
> HDR_BUF_SIZE
- header_len
) {
240 mp_msg(MSGT_NETWORK
, MSGL_FATAL
, MSGTR_MPDEMUX_MMST_InvalidHeaderSize
);
244 if (!get_data (s
, &header
[header_len
], packet_len
)) {
245 mp_msg(MSGT_NETWORK
,MSGL_ERR
,MSGTR_MPDEMUX_MMST_HeaderDataReadFailed
);
249 header_len
+= packet_len
;
251 if ( (header
[header_len
-1] == 1) && (header
[header_len
-2]==1)) {
254 if( streaming_bufferize( streaming_ctrl
, header
, header_len
)<0 ) {
258 // mp_msg(MSGT_NETWORK,MSGL_INFO,"get header packet finished\n");
270 if (!get_data (s
, (char*)&packet_len
, 4)) {
271 mp_msg(MSGT_NETWORK
,MSGL_ERR
,MSGTR_MPDEMUX_MMST_packet_lenReadFailed
);
275 packet_len
= get_32 ((unsigned char*)&packet_len
, 0) + 4;
277 // mp_msg(MSGT_NETWORK,MSGL_INFO,"command packet detected, len=%d\n", packet_len);
279 if (packet_len
< 0 || packet_len
> BUF_SIZE
) {
280 mp_msg(MSGT_NETWORK
, MSGL_FATAL
,
281 MSGTR_MPDEMUX_MMST_InvalidRTSPPacketSize
);
285 if (!get_data (s
, data
, packet_len
)) {
286 mp_msg(MSGT_NETWORK
,MSGL_ERR
,MSGTR_MPDEMUX_MMST_CmdDataReadFailed
);
290 command
= get_32 (data
, 24) & 0xFFFF;
292 // mp_msg(MSGT_NETWORK,MSGL_INFO,"command: %02x\n", command);
295 send_command (s
, 0x1b, 0, 0, 0, data
);
299 // mp_msg(MSGT_NETWORK,MSGL_INFO,"get header packet succ\n");
303 static int interp_header (uint8_t *header
, int header_len
)
306 int packet_length
=-1;
313 while (i
<header_len
) {
315 uint64_t guid_1
, guid_2
, length
;
317 guid_2
= (uint64_t)header
[i
] | ((uint64_t)header
[i
+1]<<8)
318 | ((uint64_t)header
[i
+2]<<16) | ((uint64_t)header
[i
+3]<<24)
319 | ((uint64_t)header
[i
+4]<<32) | ((uint64_t)header
[i
+5]<<40)
320 | ((uint64_t)header
[i
+6]<<48) | ((uint64_t)header
[i
+7]<<56);
323 guid_1
= (uint64_t)header
[i
] | ((uint64_t)header
[i
+1]<<8)
324 | ((uint64_t)header
[i
+2]<<16) | ((uint64_t)header
[i
+3]<<24)
325 | ((uint64_t)header
[i
+4]<<32) | ((uint64_t)header
[i
+5]<<40)
326 | ((uint64_t)header
[i
+6]<<48) | ((uint64_t)header
[i
+7]<<56);
329 // mp_msg(MSGT_NETWORK,MSGL_INFO,"guid found: %016llx%016llx\n", guid_1, guid_2);
331 length
= (uint64_t)header
[i
] | ((uint64_t)header
[i
+1]<<8)
332 | ((uint64_t)header
[i
+2]<<16) | ((uint64_t)header
[i
+3]<<24)
333 | ((uint64_t)header
[i
+4]<<32) | ((uint64_t)header
[i
+5]<<40)
334 | ((uint64_t)header
[i
+6]<<48) | ((uint64_t)header
[i
+7]<<56);
338 if ( (guid_1
== 0x6cce6200aa00d9a6ULL
) && (guid_2
== 0x11cf668e75b22630ULL
) ) {
339 mp_msg(MSGT_NETWORK
,MSGL_INFO
,MSGTR_MPDEMUX_MMST_HeaderObject
);
340 } else if ((guid_1
== 0x6cce6200aa00d9a6ULL
) && (guid_2
== 0x11cf668e75b22636ULL
)) {
341 mp_msg(MSGT_NETWORK
,MSGL_INFO
,MSGTR_MPDEMUX_MMST_DataObject
);
342 } else if ((guid_1
== 0x6553200cc000e48eULL
) && (guid_2
== 0x11cfa9478cabdca1ULL
)) {
344 packet_length
= get_32(header
, i
+92-24);
346 mp_msg(MSGT_NETWORK
,MSGL_INFO
,MSGTR_MPDEMUX_MMST_FileObjectPacketLen
,
347 packet_length
, get_32(header
, i
+96-24));
350 } else if ((guid_1
== 0x6553200cc000e68eULL
) && (guid_2
== 0x11cfa9b7b7dc0791ULL
)) {
352 int stream_id
= header
[i
+48] | header
[i
+49] << 8;
354 mp_msg(MSGT_NETWORK
,MSGL_INFO
,MSGTR_MPDEMUX_MMST_StreamObjectStreamID
, stream_id
);
356 if (num_stream_ids
< MAX_STREAMS
) {
357 stream_ids
[num_stream_ids
] = stream_id
;
360 mp_msg(MSGT_NETWORK
,MSGL_ERR
,MSGTR_MPDEMUX_MMST_2ManyStreamID
);
366 printf ("unknown object (guid: %016llx, %016llx, len: %lld)\n", guid_1
, guid_2
, length
);
367 for (; b
< length
; b
++)
369 if (isascii(header
[b
]) || isalpha(header
[b
]))
370 printf("%c ", header
[b
]);
372 printf("%x ", header
[b
]);
376 mp_msg(MSGT_NETWORK
,MSGL_WARN
,MSGTR_MPDEMUX_MMST_UnknownObject
);
380 // mp_msg(MSGT_NETWORK,MSGL_INFO,"length : %lld\n", length);
386 return packet_length
;
391 static int get_media_packet (int s
, int padding
, streaming_ctrl_t
*stream_ctrl
) {
392 unsigned char pre_header
[8];
395 if (!get_data (s
, pre_header
, 8)) {
396 mp_msg(MSGT_NETWORK
,MSGL_ERR
,MSGTR_MPDEMUX_MMST_PreHeaderReadFailed
);
400 // for (i=0; i<8; i++)
401 // mp_msg(MSGT_NETWORK,MSGL_INFO,"pre_header[%d] = %02x (%d)\n",
402 // i, pre_header[i], pre_header[i]);
404 if (pre_header
[4] == 0x04) {
408 packet_len
= (pre_header
[7] << 8 | pre_header
[6]) - 8;
410 // mp_msg(MSGT_NETWORK,MSGL_INFO,"asf media packet detected, len=%d\n", packet_len);
412 if (packet_len
< 0 || packet_len
> BUF_SIZE
) {
413 mp_msg(MSGT_NETWORK
, MSGL_FATAL
, MSGTR_MPDEMUX_MMST_InvalidRTSPPacketSize
);
417 if (!get_data (s
, data
, packet_len
)) {
418 mp_msg(MSGT_NETWORK
,MSGL_ERR
,MSGTR_MPDEMUX_MMST_MediaDataReadFailed
);
422 streaming_bufferize(stream_ctrl
, data
, padding
);
429 if (!get_data (s
, (char*)&packet_len
, 4)) {
430 mp_msg(MSGT_NETWORK
,MSGL_ERR
,MSGTR_MPDEMUX_MMST_packet_lenReadFailed
);
434 packet_len
= get_32 ((unsigned char*)&packet_len
, 0) + 4;
436 if (packet_len
< 0 || packet_len
> BUF_SIZE
) {
437 mp_msg(MSGT_NETWORK
,MSGL_FATAL
,MSGTR_MPDEMUX_MMST_InvalidRTSPPacketSize
);
441 if (!get_data (s
, data
, packet_len
)) {
442 mp_msg(MSGT_NETWORK
,MSGL_ERR
,MSGTR_MPDEMUX_MMST_CmdDataReadFailed
);
446 if ( (pre_header
[7] != 0xb0) || (pre_header
[6] != 0x0b)
447 || (pre_header
[5] != 0xfa) || (pre_header
[4] != 0xce) ) {
449 mp_msg(MSGT_NETWORK
,MSGL_ERR
,MSGTR_MPDEMUX_MMST_MissingSignature
);
453 command
= get_32 (data
, 24) & 0xFFFF;
455 // mp_msg(MSGT_NETWORK,MSGL_INFO,"\ncommand packet detected, len=%d cmd=0x%X\n", packet_len, command);
458 send_command (s
, 0x1b, 0, 0, 0, data
);
459 else if (command
== 0x1e) {
460 mp_msg(MSGT_NETWORK
,MSGL_INFO
,MSGTR_MPDEMUX_MMST_PatentedTechnologyJoke
);
463 else if (command
== 0x21 ) {
464 // Looks like it's new in WMS9
465 // Unknown command, but ignoring it seems to work.
468 else if (command
!= 0x05) {
469 mp_msg(MSGT_NETWORK
,MSGL_ERR
,MSGTR_MPDEMUX_MMST_UnknownCmd
,command
);
474 // mp_msg(MSGT_NETWORK,MSGL_INFO,"get media packet succ\n");
480 static int packet_length1
;
482 static int asf_mmst_streaming_read( int fd
, char *buffer
, int size
, streaming_ctrl_t
*stream_ctrl
)
486 while( stream_ctrl
->buffer_size
==0 ) {
487 // buffer is empty - fill it!
488 int ret
= get_media_packet( fd
, packet_length1
, stream_ctrl
);
490 mp_msg(MSGT_NETWORK
,MSGL_ERR
,MSGTR_MPDEMUX_MMST_GetMediaPacketErr
,strerror(errno
));
492 } else if (ret
==0) //EOF?
496 len
= stream_ctrl
->buffer_size
-stream_ctrl
->buffer_pos
;
497 if(len
>size
) len
=size
;
498 memcpy( buffer
, (stream_ctrl
->buffer
)+(stream_ctrl
->buffer_pos
), len
);
499 stream_ctrl
->buffer_pos
+= len
;
500 if( stream_ctrl
->buffer_pos
>=stream_ctrl
->buffer_size
) {
501 free( stream_ctrl
->buffer
);
502 stream_ctrl
->buffer
= NULL
;
503 stream_ctrl
->buffer_size
= 0;
504 stream_ctrl
->buffer_pos
= 0;
510 static int asf_mmst_streaming_seek( int fd
, off_t pos
, streaming_ctrl_t
*streaming_ctrl
)
513 // Shut up gcc warning
519 int asf_mmst_streaming_start(stream_t
*stream
)
523 uint8_t asf_header
[HDR_BUF_SIZE
];
525 int len
, i
, packet_length
;
526 char *path
, *unescpath
;
527 URL_t
*url1
= stream
->streaming_ctrl
->url
;
531 closesocket( stream
->fd
);
536 path
= strchr(url1
->file
,'/') + 1;
538 /* mmst filename are not url_escaped by MS MediaPlayer and are expected as
539 * "plain text" by the server, so need to decode it here
541 unescpath
=malloc(strlen(path
)+1);
543 mp_msg(MSGT_NETWORK
,MSGL_FATAL
,MSGTR_MemAllocFailed
);
546 url_unescape_string(unescpath
,path
);
550 if( url1
->port
==0 ) {
553 s
= connect2Server( url1
->hostname
, url1
->port
, 1);
558 mp_msg(MSGT_NETWORK
,MSGL_INFO
,MSGTR_MPDEMUX_MMST_Connected
);
563 * Send the initial connect info including player version no. Client GUID (random) and the host address being connected to.
564 * This command is sent at the very start of protocol initiation. It sends local information to the serve
568 /* prepare for the url encoding conversion */
570 url_conv
= iconv_open("UTF-16LE", "UTF-8");
573 snprintf (str
, 1023, "\034\003NSPlayer/7.0.0.1956; {33715801-BAB3-9D85-24E9-03B90328270A}; Host: %s", url1
->hostname
);
574 string_utf16 (data
, str
, strlen(str
));
575 // send_command(s, commandno ....)
576 send_command (s
, 1, 0, 0x0004000b, strlen(str
)*2+2, data
);
578 len
= recv (s
, data
, BUF_SIZE
, 0) ;
580 /*This sends details of the local machine IP address to a Funnel system at the server.
581 * Also, the TCP or UDP transport selection is sent.
583 * here 192.168.0.1 is local ip address TCP/UDP states the tronsport we r using
584 * and 1037 is the local TCP or UDP socket number
588 string_utf16 (&data
[8], "\002\000\\\\192.168.0.1\\TCP\\1037", 24);
590 send_command (s
, 2, 0, 0, 24*2+10, data
);
592 len
= recv (s
, data
, BUF_SIZE
, 0) ;
594 /* This command sends file path (at server) and file name request to the server.
597 string_utf16 (&data
[8], path
, strlen(path
));
599 send_command (s
, 5, 0, 0, strlen(path
)*2+10, data
);
604 /* The ASF header chunk request. Includes ?session' variable for pre header value.
605 * After this command is sent,
606 * the server replies with 0x11 command and then the header chunk with header data follows.
609 memset (data
, 0, 40);
612 send_command (s
, 0x15, 1, 0, 40, data
);
615 /* get_headers(s, asf_header); */
617 asf_header_len
= get_header (s
, asf_header
, stream
->streaming_ctrl
);
618 // mp_msg(MSGT_NETWORK,MSGL_INFO,"---------------------------------- asf_header %d\n",asf_header);
619 if (asf_header_len
==0) { //error reading header
623 packet_length
= interp_header (asf_header
, asf_header_len
);
627 * This command is the media stream MBR selector. Switches are always 6 bytes in length.
628 * After all switch elements, data ends with bytes [00 00] 00 20 ac 40 [02].
630 * [00 00] shows 0x61 0x00 (on the first 33 sent) or 0xff 0xff for ASF files, and with no ending data for WMV files.
631 * It is not yet understood what all this means.
632 * And the last [02] byte is probably the header ?session' value.
636 memset (data
, 0, 40);
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
);