Add explanatory comments to the #endif part of multiple inclusion guards.
[mplayer/greg.git] / stream / asf_mmst_streaming.c
blob31536a9daeb960d15bf8d7b3c5a41c1ab4e71431
1 /*
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
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <unistd.h>
34 #include <errno.h>
35 #include <inttypes.h>
37 #include "config.h"
39 #include "mp_msg.h"
40 #include "help_mp.h"
42 #ifndef HAVE_WINSOCK2
43 #define closesocket close
44 #else
45 #include <winsock2.h>
46 #endif
48 #ifndef USE_SETLOCALE
49 #undef USE_ICONV
50 #endif
52 #ifdef USE_ICONV
53 #include <iconv.h>
54 #ifdef USE_LANGINFO
55 #include <langinfo.h>
56 #endif
57 #endif
59 #include "url.h"
60 #include "libmpdemux/asf.h"
62 #include "stream.h"
64 #include "network.h"
65 #include "tcp.h"
67 extern int audio_id;
68 extern int video_id;
70 #define BUF_SIZE 102400
71 #define HDR_BUF_SIZE 8192
72 #define MAX_STREAMS 20
74 typedef struct
76 uint8_t buf[BUF_SIZE];
77 int num_bytes;
79 } command_t;
81 static int seq_num;
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;
90 value = value >> 8;
91 cmd->buf[cmd->num_bytes+1] = value % 256 ;
92 value = value >> 8;
93 cmd->buf[cmd->num_bytes+2] = value % 256 ;
94 value = value >> 8;
95 cmd->buf[cmd->num_bytes+3] = value % 256 ;
97 cmd->num_bytes += 4;
100 static uint32_t get_32 (unsigned char *cmd, int offset)
102 uint32_t ret;
104 ret = cmd[offset] ;
105 ret |= cmd[offset+1]<<8 ;
106 ret |= cmd[offset+2]<<16 ;
107 ret |= cmd[offset+3]<<24 ;
109 return ret;
112 static void send_command (int s, int command, uint32_t switches,
113 uint32_t extra, int length,
114 char *data)
116 command_t cmd;
117 int len8;
119 len8 = (length + 7) / 8;
121 cmd.num_bytes = 0;
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);
129 seq_num++;
130 put_32 (&cmd, 0x0); /* unknown */
131 put_32 (&cmd, 0x0);
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);
138 if (length & 7)
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);
146 #ifdef USE_ICONV
147 static iconv_t url_conv;
148 #endif
150 static void string_utf16(char *dest, char *src, int len) {
151 int i;
152 #ifdef USE_ICONV
153 size_t len1, len2;
154 char *ip, *op;
156 if (url_conv != (iconv_t)(-1))
158 memset(dest, 0, 1000);
159 len1 = len; len2 = 1000;
160 ip = src; op = dest;
162 iconv(url_conv, &ip, &len1, &op, &len2);
164 else
166 #endif
167 if (len > 499) len = 499;
168 for (i=0; i<len; i++) {
169 dest[i*2] = src[i];
170 dest[i*2+1] = 0;
172 /* trailing zeroes */
173 dest[i*2] = 0;
174 dest[i*2+1] = 0;
175 #ifdef USE_ICONV
177 #endif
180 static void get_answer (int s)
182 char data[BUF_SIZE];
183 int command = 0x1b;
185 while (command == 0x1b) {
186 int len;
188 len = recv (s, data, BUF_SIZE, 0) ;
189 if (!len) {
190 mp_msg(MSGT_NETWORK,MSGL_ERR,MSGTR_MPDEMUX_MMST_EOFAlert);
191 return;
194 command = get_32 (data, 36) & 0xFFFF;
196 if (command == 0x1b)
197 send_command (s, 0x1b, 0, 0, 0, data);
201 static int get_data (int s, char *buf, size_t count)
203 ssize_t len;
204 size_t total = 0;
206 while (total < count) {
208 len = recv (s, &buf[total], count-total, 0);
210 if (len<=0) {
211 perror ("read error:");
212 return 0;
215 total += len;
217 if (len != 0) {
218 // mp_msg(MSGT_NETWORK,MSGL_INFO,"[%d/%d]", total, count);
219 fflush (stdout);
224 return 1;
228 static int get_header (int s, uint8_t *header, streaming_ctrl_t *streaming_ctrl)
230 unsigned char pre_header[8];
231 int header_len;
233 header_len = 0;
235 while (1) {
236 if (!get_data (s, pre_header, 8)) {
237 mp_msg(MSGT_NETWORK,MSGL_ERR,MSGTR_MPDEMUX_MMST_PreHeaderReadFailed);
238 return 0;
240 if (pre_header[4] == 0x02) {
242 int packet_len;
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);
250 return 0;
253 if (!get_data (s, &header[header_len], packet_len)) {
254 mp_msg(MSGT_NETWORK,MSGL_ERR,MSGTR_MPDEMUX_MMST_HeaderDataReadFailed);
255 return 0;
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 ) {
264 return -1;
267 // mp_msg(MSGT_NETWORK,MSGL_INFO,"get header packet finished\n");
269 return (header_len);
273 } else {
275 int32_t packet_len;
276 int command;
277 char data[BUF_SIZE];
279 if (!get_data (s, (char*)&packet_len, 4)) {
280 mp_msg(MSGT_NETWORK,MSGL_ERR,MSGTR_MPDEMUX_MMST_packet_lenReadFailed);
281 return 0;
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);
291 return 0;
294 if (!get_data (s, data, packet_len)) {
295 mp_msg(MSGT_NETWORK,MSGL_ERR,MSGTR_MPDEMUX_MMST_CmdDataReadFailed);
296 return 0;
299 command = get_32 (data, 24) & 0xFFFF;
301 // mp_msg(MSGT_NETWORK,MSGL_INFO,"command: %02x\n", command);
303 if (command == 0x1b)
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)
314 int i;
315 int packet_length=-1;
318 * parse header
321 i = 30;
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);
330 i += 8;
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);
336 i += 8;
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);
345 i += 8;
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;
367 num_stream_ids++;
368 } else {
369 mp_msg(MSGT_NETWORK,MSGL_ERR,MSGTR_MPDEMUX_MMST_2ManyStreamID);
372 } else {
373 #if 0
374 int b = i;
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]);
380 else
381 printf("%x ", header[b]);
383 printf("\n");
384 #else
385 mp_msg(MSGT_NETWORK,MSGL_WARN,MSGTR_MPDEMUX_MMST_UnknownObject);
386 #endif
389 // mp_msg(MSGT_NETWORK,MSGL_INFO,"length : %lld\n", length);
391 i += length-24;
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];
402 char data[BUF_SIZE];
404 if (!get_data (s, pre_header, 8)) {
405 mp_msg(MSGT_NETWORK,MSGL_ERR,MSGTR_MPDEMUX_MMST_PreHeaderReadFailed);
406 return 0;
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) {
415 int packet_len;
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);
423 return 0;
426 if (!get_data (s, data, packet_len)) {
427 mp_msg(MSGT_NETWORK,MSGL_ERR,MSGTR_MPDEMUX_MMST_MediaDataReadFailed);
428 return 0;
431 streaming_bufferize(stream_ctrl, data, padding);
433 } else {
435 int32_t packet_len;
436 int command;
438 if (!get_data (s, (char*)&packet_len, 4)) {
439 mp_msg(MSGT_NETWORK,MSGL_ERR,MSGTR_MPDEMUX_MMST_packet_lenReadFailed);
440 return 0;
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);
447 return 0;
450 if (!get_data (s, data, packet_len)) {
451 mp_msg(MSGT_NETWORK,MSGL_ERR,MSGTR_MPDEMUX_MMST_CmdDataReadFailed);
452 return 0;
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);
459 return -1;
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);
466 if (command == 0x1b)
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);
470 return 0;
472 else if (command == 0x21 ) {
473 // Looks like it's new in WMS9
474 // Unknown command, but ignoring it seems to work.
475 return 0;
477 else if (command != 0x05) {
478 mp_msg(MSGT_NETWORK,MSGL_ERR,MSGTR_MPDEMUX_MMST_UnknownCmd,command);
479 return -1;
483 // mp_msg(MSGT_NETWORK,MSGL_INFO,"get media packet succ\n");
485 return 1;
489 static int packet_length1;
491 static int asf_mmst_streaming_read( int fd, char *buffer, int size, streaming_ctrl_t *stream_ctrl )
493 int len;
495 while( stream_ctrl->buffer_size==0 ) {
496 // buffer is empty - fill it!
497 int ret = get_media_packet( fd, packet_length1, stream_ctrl);
498 if( ret<0 ) {
499 mp_msg(MSGT_NETWORK,MSGL_ERR,MSGTR_MPDEMUX_MMST_GetMediaPacketErr,strerror(errno));
500 return -1;
501 } else if (ret==0) //EOF?
502 return ret;
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;
515 return len;
519 static int asf_mmst_streaming_seek( int fd, off_t pos, streaming_ctrl_t *streaming_ctrl )
521 return -1;
522 // Shut up gcc warning
523 fd++;
524 pos++;
525 streaming_ctrl=NULL;
528 int asf_mmst_streaming_start(stream_t *stream)
530 char str[1024];
531 char data[BUF_SIZE];
532 uint8_t asf_header[HDR_BUF_SIZE];
533 int asf_header_len;
534 int len, i, packet_length;
535 char *path, *unescpath;
536 URL_t *url1 = stream->streaming_ctrl->url;
537 int s = stream->fd;
539 if( s>0 ) {
540 closesocket( stream->fd );
541 stream->fd = -1;
544 /* parse url */
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);
551 if (!unescpath) {
552 mp_msg(MSGT_NETWORK,MSGL_FATAL,MSGTR_MemAllocFailed);
553 return -1;
555 url_unescape_string(unescpath,path);
556 path=unescpath;
559 if( url1->port==0 ) {
560 url1->port=1755;
562 s = connect2Server( url1->hostname, url1->port, 1);
563 if( s<0 ) {
564 free(path);
565 return s;
567 mp_msg(MSGT_NETWORK,MSGL_INFO,MSGTR_MPDEMUX_MMST_Connected);
569 seq_num=0;
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
574 * cmd 1 0x01
575 * */
577 /* prepare for the url encoding conversion */
578 #ifdef USE_ICONV
579 #ifdef USE_LANGINFO
580 url_conv = iconv_open("UTF-16LE",nl_langinfo(CODESET));
581 #else
582 url_conv = iconv_open("UTF-16LE", NULL);
583 #endif
584 #endif
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
598 * cmd 2 0x02
599 * */
601 string_utf16 (&data[8], "\002\000\\\\192.168.0.1\\TCP\\1037", 24);
602 memset (data, 0, 8);
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.
608 * 0x5 */
610 string_utf16 (&data[8], path, strlen(path));
611 memset (data, 0, 8);
612 send_command (s, 5, 0, 0, strlen(path)*2+10, data);
613 free(path);
615 get_answer (s);
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.
620 * 0x15 */
622 memset (data, 0, 40);
623 data[32] = 2;
625 send_command (s, 0x15, 1, 0, 40, data);
627 num_stream_ids = 0;
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
633 closesocket(s);
634 return -1;
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].
642 * Where:
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.
647 * 0x33 */
649 memset (data, 0, 40);
651 if (audio_id > 0) {
652 data[2] = 0xFF;
653 data[3] = 0xFF;
654 data[4] = audio_id;
655 send_command(s, 0x33, num_stream_ids, 0xFFFF | audio_id << 16, 8, data);
656 } else {
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);
667 get_answer (s);
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.
673 * 0x07 */
675 memset (data, 0, 40);
677 for (i=8; i<16; i++)
678 data[i] = 0xFF;
680 data[20] = 0x04;
682 send_command (s, 0x07, 1, 0xFFFF | stream_ids[0] << 16, 24, data);
684 stream->fd = s;
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);
693 #ifdef USE_ICONV
694 if (url_conv != (iconv_t)(-1))
695 iconv_close(url_conv);
696 #endif
698 return 0;