1 /*****************************************************************************
2 * mms.c: MMS access plug-in
3 *****************************************************************************
4 * Copyright (C) 2001, 2002 VideoLAN
5 * $Id: mms.c,v 1.1 2002/11/12 00:54:40 fenrir Exp $
7 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
22 *****************************************************************************/
27 * - clean code, break huge code block
29 * - begin udp support...
32 /*****************************************************************************
34 *****************************************************************************/
36 #include <sys/types.h>
43 #include <vlc/input.h>
47 #elif defined( _MSC_VER ) && defined( _WIN32 )
52 # include <winsock2.h>
53 # include <ws2tcpip.h>
55 # define IN_MULTICAST(a) IN_CLASSD(a)
58 # include <sys/socket.h>
63 #include "var_buffer.h"
65 /****************************************************************************
67 * MMSProtocole documentation found at http://get.to/sdp
68 ****************************************************************************/
70 /*****************************************************************************
72 *****************************************************************************/
73 static int Open ( vlc_object_t * );
74 static void Close ( vlc_object_t * );
76 static int Read ( input_thread_t * p_input, byte_t * p_buffer,
78 static void Seek ( input_thread_t *, off_t );
79 static int SetProgram ( input_thread_t *, pgrm_descriptor_t * );
82 static int MMSOpen( input_thread_t *, url_t *, int, char * );
84 static int MMSStart ( input_thread_t *, uint32_t );
85 static int MMSStop ( input_thread_t *p_input ); // Not used
87 static int MMSClose ( input_thread_t * );
90 static int mms_CommandRead( input_thread_t *p_input, int i_command1, int i_command2 );
91 static int mms_CommandSend( input_thread_t *, int, uint32_t, uint32_t, uint8_t *, int );
93 static int mms_HeaderMediaRead( input_thread_t *, int );
95 static int mms_ReceivePacket( input_thread_t * );
97 static void mms_ParseURL( url_t *p_url, char *psz_url );
102 * XXX DON'T FREE MY MEMORY !!! XXX
105 #define INPUT_FDNETWORKCLOSE( p_input ) \
107 void *__p_access = p_input->p_access_data; \
108 input_socket_t *__p_socket = malloc( sizeof( input_socket_t ) ); \
109 memcpy( __p_socket, __p_access, sizeof( input_socket_t ) ); \
110 p_input->p_access_data = (void*)__p_socket; \
111 input_FDNetworkClose( p_input ); \
112 p_input->p_access_data = __p_access; \
117 /*****************************************************************************
119 *****************************************************************************/
121 set_description( _("MMS access module") );
122 set_capability( "access", 0 );
123 add_shortcut( "mms" );
124 add_shortcut( "mmsu" );
125 add_shortcut( "mmst" );
126 set_callbacks( Open, Close );
129 #define BUF_SIZE 200000
131 static int Open( vlc_object_t *p_this )
138 input_thread_t *p_input = (input_thread_t*)p_this;
140 /* *** allocate p_access_data *** */
141 p_input->p_access_data =
142 (void*)p_access = malloc( sizeof( access_t ) );
143 memset( p_access, 0, sizeof( access_t ) );
145 p_access->p_cmd = malloc( BUF_SIZE );
148 /* *** Parse URL and get server addr/port and path *** */
149 mms_ParseURL( &p_access->url, p_input->psz_name );
150 if( p_access->url.psz_server_addr == NULL ||
151 !( *p_access->url.psz_server_addr ) )
153 FREE( p_access->url.psz_private );
154 FREE( p_access->p_cmd );
155 msg_Err( p_input, "invalid server name" );
158 if( p_access->url.i_server_port == 0 )
160 p_access->url.i_server_port = 1755; // default port
164 /* *** connect to this server *** */
165 /* 1: look at requested protocol (udp/tcp) */
166 i_proto = MMS_PROTO_AUTO;
167 if( *p_input->psz_access )
169 if( !strncmp( p_input->psz_access, "mmsu", 4 ) )
171 i_proto = MMS_PROTO_UDP;
173 else if( !strncmp( p_input->psz_access, "mmst", 4 ) )
175 i_proto = MMS_PROTO_TCP;
178 /* 2: look at ip version ipv4/ipv6 */
180 if( config_GetInt( p_input, "ipv4" ) )
182 psz_network = "ipv4";
184 else if( config_GetInt( p_input, "ipv6" ) )
186 psz_network = "ipv6";
189 if( i_proto == MMS_PROTO_AUTO )
190 { // first try with TCP
192 MMSOpen( p_input, &p_access->url, MMS_PROTO_TCP, psz_network );
196 MMSOpen( p_input, &p_access->url, MMS_PROTO_UDP, psz_network );
203 MMSOpen( p_input, &p_access->url, i_proto, psz_network );
208 // all sockets are closed
209 msg_Err( p_input, "cannot connect to server" );
210 FREE( p_access->url.psz_private );
211 FREE( p_access->p_cmd );
214 msg_Dbg( p_input, "connected to %s", p_access->url.psz_server_addr );
216 // all sockets are open
219 /* *** set exported functions *** */
220 p_input->pf_read = Read;
221 p_input->pf_seek = Seek;
222 p_input->pf_set_program = SetProgram;
223 p_input->pf_set_area = NULL;
225 p_input->p_private = NULL; // XXX ??
227 /* *** finished to set some variable *** */
228 vlc_mutex_lock( &p_input->stream.stream_lock );
229 /* those data could be different for UDP/TCP */
230 p_input->stream.b_pace_control = 0;
231 p_input->stream.p_selected_area->i_tell = 0;
232 if( p_access->i_packet_count <= 0 )
234 p_input->stream.b_seekable = 0;
235 p_input->stream.p_selected_area->i_size = 0;
239 p_input->stream.b_seekable = 0;
240 p_input->stream.p_selected_area->i_size =
242 p_access->i_packet_count * p_access->i_packet_length;
245 p_input->stream.i_method = INPUT_METHOD_NETWORK;
246 vlc_mutex_unlock( &p_input->stream.stream_lock );
248 /* *** Start stream *** */
249 if( MMSStart( p_input, 0xffffffff ) < 0 )
251 msg_Err( p_input, "cannot start stream" );
253 FREE( p_access->url.psz_private );
254 FREE( p_access->p_cmd );
261 /*****************************************************************************
262 * Close: free unused data structures
263 *****************************************************************************/
264 static void Close( vlc_object_t *p_this )
266 input_thread_t * p_input = (input_thread_t *)p_this;
267 access_t *p_access = (access_t*)p_input->p_access_data;
269 /* close connection with server */
273 FREE( p_access->url.psz_private );
274 FREE( p_access->p_cmd );
277 /*****************************************************************************
278 * SetProgram: do nothing
279 *****************************************************************************/
280 static int SetProgram( input_thread_t * p_input,
281 pgrm_descriptor_t * p_program )
286 /*****************************************************************************
287 * Seek: try to go at the right place
288 *****************************************************************************/
289 static void Seek( input_thread_t * p_input, off_t i_pos )
294 * Probably some bad or missing command
300 access_t *p_access = (access_t*)p_input->p_access_data;
308 msg_Dbg( p_input, "seeking to %lld, header size:%d", i_pos, p_access->i_header );
309 if( i_pos < p_access->i_header)
312 if( p_access->i_pos < p_access->i_header )
314 /* no need to restart stream, it was already one
315 * or no stream was yet read */
316 p_access->i_pos = i_pos;
321 i_packet = 0xffffffff;
327 i_packet = ( i_pos - p_access->i_header ) / p_access->i_packet_length;
328 i_offset = ( i_pos - p_access->i_header ) % p_access->i_packet_length;
332 MMSStart( p_input, i_packet );
333 p_access->i_media_used += i_offset;
334 p_access->i_pos = i_pos;
338 static int Read ( input_thread_t * p_input, byte_t * p_buffer,
341 access_t *p_access = (access_t*)p_input->p_access_data;
347 /* *** send header if needed ** */
348 if( p_access->i_pos < p_access->i_header )
350 i_copy = __MIN( i_len, p_access->i_header - p_access->i_pos );
354 p_access->p_header + p_access->i_pos,
360 /* *** now send data if needed *** */
361 while( i_data < i_len )
363 if( p_access->i_media_used < p_access->i_media )
365 i_copy = __MIN( i_len - i_data ,
366 p_access->i_media - p_access->i_media_used );
367 memcpy( p_buffer + i_data,
368 p_access->p_media + p_access->i_media_used,
371 p_access->i_media_used += i_copy;
373 else if( p_access->p_media != NULL &&
374 p_access->i_media_used < p_access->i_packet_length )
376 i_copy = __MIN( i_len - i_data,
377 p_access->i_packet_length - p_access->i_media_used);
378 memset( p_buffer + i_data, 0, i_copy );
381 p_access->i_media_used += i_copy;
385 if( mms_HeaderMediaRead( p_input, MMS_PACKET_MEDIA ) < 0 );
387 p_access->i_pos += i_data;
393 p_access->i_pos += i_data;
398 static void asf_HeaderParse( mms_stream_t stream[128],
399 uint8_t *p_header, int i_header )
406 for( i = 0; i < 128; i++ )
408 stream[i].i_cat = MMS_STREAM_UNKNOWN;
411 var_buffer_initread( &buffer, p_header, i_header );
413 var_buffer_getguid( &buffer, &guid );
414 if( !CmpGuid( &guid, &asf_object_header_guid ) )
418 var_buffer_getmemory( &buffer, NULL, 30 - 16 );
422 if( var_buffer_readempty( &buffer ) )
427 var_buffer_getguid( &buffer, &guid );
428 i_size = var_buffer_get64( &buffer );
429 if( CmpGuid( &guid, &asf_object_stream_properties_guid ) )
434 // msg_Dbg( p_input, "found stream_properties" );
436 var_buffer_getguid( &buffer, &stream_type );
437 var_buffer_getmemory( &buffer, NULL, 32 );
438 i_stream_id = var_buffer_get8( &buffer ) & 0x7f;
439 var_buffer_getmemory( &buffer, NULL, i_size - 24 - 32 - 16 - 1 );
441 if( CmpGuid( &stream_type, &asf_object_stream_type_video ) )
443 // msg_Dbg( p_input, "video stream[%d] found", i_stream_id );
444 stream[i_stream_id].i_cat = MMS_STREAM_VIDEO;
446 else if( CmpGuid( &stream_type, &asf_object_stream_type_audio ) )
448 // msg_Dbg( p_input, "audio stream[%d] found", i_stream_id );
449 stream[i_stream_id].i_cat = MMS_STREAM_AUDIO;
453 // msg_Dbg( p_input, "unknown stream[%d] found", i_stream_id );
454 stream[i_stream_id].i_cat = MMS_STREAM_UNKNOWN;
457 else if ( CmpGuid( &guid, &asf_object_bitrate_properties_guid ) )
462 i_count = var_buffer_get16( &buffer );
466 i_stream_id = var_buffer_get16( &buffer )&0x7f;
467 stream[i_stream_id].i_bitrate = var_buffer_get32( &buffer );
471 var_buffer_getmemory( &buffer, NULL, i_size - 24 );
476 var_buffer_getmemory( &buffer, NULL, i_size - 24 );
481 /****************************************************************************
482 * MMSOpen : Open a connection with the server over mmst or mmsu(not yet)
483 ****************************************************************************/
484 static int MMSOpen( input_thread_t *p_input,
487 char *psz_network ) /* "", "ipv4", "ipv6" */
490 access_t *p_access = (access_t*)p_input->p_access_data;
492 network_socket_t socket_desc;
493 int b_udp = ( i_proto == MMS_PROTO_UDP ) ? 1 : 0;
498 int i_server_version;
500 int i_update_player_url;
501 int i_encryption_type;
509 /* *** Open a TCP connection with server *** */
510 msg_Dbg( p_input, "waiting for connection..." );
511 socket_desc.i_type = NETWORK_TCP;
512 socket_desc.psz_server_addr = p_url->psz_server_addr;
513 socket_desc.i_server_port = p_url->i_server_port;
514 socket_desc.psz_bind_addr = "";
515 socket_desc.i_bind_port = 0;
516 p_input->p_private = (void*)&socket_desc;
517 if( !( p_network = module_Need( p_input, "network", psz_network ) ) )
519 msg_Err( p_input, "failed to open a connection" );
522 module_Unneed( p_input, p_network );
523 p_access->socket_server.i_handle = socket_desc.i_handle;
524 p_input->i_mtu = socket_desc.i_mtu; // FIXME
526 "connection with \"%s:%d\" successful",
527 p_url->psz_server_addr,
528 p_url->i_server_port );
530 /* *** Bind port if UDP protocol is selected *** */
535 "MMS/UDP not yet implemented" );
537 p_access->_socket = p_access->socket_server;
538 INPUT_FDNETWORKCLOSE( p_input );
541 /* *** Default socket is the one for server communication *** */
542 p_access->_socket = p_access->socket_server;
544 /* *** Init context for mms prototcol *** */
545 GenerateGuid( &p_access->guid ); // used to identify client by server
547 "generated guid: "GUID_FMT,
548 GUID_PRINT( p_access->guid ) );
549 p_access->i_command_level = 1; // updated after 0x1A command
550 p_access->i_seq_num = 0;
551 p_access->i_media_packet_id_type = 0x04;
552 p_access->i_header_packet_id_type = 0x02;
553 p_access->i_proto = i_proto;
554 p_access->i_packet_seq_num = 0;
555 p_access->p_header = NULL;
556 p_access->i_header = 0;
557 p_access->p_media = NULL;
558 p_access->i_media = 0;
559 p_access->i_media_used = 0;
563 /* *** send command 1 : connection request *** */
564 var_buffer_initwrite( &buffer, 0 );
565 var_buffer_add16( &buffer, 0x001c );
566 var_buffer_add16( &buffer, 0x0003 );
568 "NSPlayer/7.0.0.1956; {"GUID_FMT"}; Host: %s",
569 GUID_PRINT( p_access->guid ),
570 p_url->psz_server_addr );
571 var_buffer_addUTF16( &buffer, tmp );
573 mms_CommandSend( p_input,
574 0x01, /* connexion request */
575 0x00000000, /* flags, FIXME */
576 0x0004000b, /* ???? */
580 mms_CommandRead( p_input, 0x01, 0 );
581 i_server_version = GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 32 );
582 i_tool_version = GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 36 );
583 i_update_player_url = GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 40 );
584 i_encryption_type = GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 44 );
585 p = (uint16_t*)( p_access->p_cmd + MMS_CMD_HEADERSIZE + 48 );
586 #define GETUTF16( psz, size ) \
589 psz = malloc( size + 1); \
590 for( i = 0; i < size; i++ ) \
597 GETUTF16( p_access->psz_server_version, i_server_version );
598 GETUTF16( p_access->psz_tool_version, i_tool_version );
599 GETUTF16( p_access->psz_update_player_url, i_update_player_url );
600 GETUTF16( p_access->psz_encryption_type, i_encryption_type );
603 "0x01 --> server_version:\"%s\" tool_version:\"%s\" update_player_url:\"%s\" encryption_type:\"%s\"",
604 p_access->psz_server_version,
605 p_access->psz_tool_version,
606 p_access->psz_update_player_url,
607 p_access->psz_encryption_type );
609 /* *** should make an 18 command to make data timing *** */
611 /* *** send command 2 : transport protocol selection *** */
612 var_buffer_reinitwrite( &buffer, 0 );
613 var_buffer_add32( &buffer, 0x00000000 );
614 var_buffer_add32( &buffer, 0x000a0000 );
615 var_buffer_add32( &buffer, 0x00000002 );
616 // FIXME wrong for UDP FIXME
617 sprintf( tmp, "\\\\127.0.0.1\\%s\\1242", b_udp ? "UDP" : "TCP" );
618 var_buffer_addUTF16( &buffer, tmp );
619 var_buffer_add16( &buffer, '0' );
621 mms_CommandSend( p_input,
622 0x02, /* connexion request */
623 0x00000000, /* flags, FIXME */
624 0xffffffff, /* ???? */
628 /* *** response from server, should be 0x02 or 0x03 *** */
629 mms_CommandRead( p_input, 0x02, 0 );
630 if( p_access->i_command == 0x03 )
633 "%s protocol selection failed", b_udp ? "UDP" : "TCP" );
634 var_buffer_free( &buffer );
638 else if( p_access->i_command != 0x02 )
640 msg_Warn( p_input, "received command isn't 0x02 in reponse to 0x02" );
643 /* *** send command 5 : media file name/path requested *** */
644 var_buffer_reinitwrite( &buffer, 0 );
645 var_buffer_add64( &buffer, 0 );
646 // var_buffer_addUTF16( &buffer, "/" );
647 var_buffer_addUTF16( &buffer, p_url->psz_path );
649 mms_CommandSend( p_input,
651 p_access->i_command_level,
656 /* *** wait for reponse *** */
657 mms_CommandRead( p_input, 0x1a, 0x06 );
659 /* test if server send 0x1A answer */
660 if( p_access->i_command == 0x1A )
662 msg_Err( p_input, "id/password requested (not yet supported)" );
664 var_buffer_free( &buffer );
668 if( p_access->i_command != 0x06 )
671 "unknown answer (0x%x instead of 0x06)",
672 p_access->i_command );
673 var_buffer_free( &buffer );
678 // 1 for file ok, 2 for authen ok
679 switch( GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE ) )
682 msg_Dbg( p_input, "Media file name/path accepted" );
685 msg_Dbg( p_input, "Authentication accepted" );
689 msg_Err( p_input, "error while asking for file %d",
690 GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE ) );
691 var_buffer_free( &buffer );
696 p_access->i_flags_broadcast =
697 GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 12 );
698 p_access->i_media_length =
699 GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 24 );
700 p_access->i_packet_length =
701 GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 44 );
702 p_access->i_packet_count =
703 GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 48 );
704 p_access->i_max_bit_rate =
705 GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 56 );
706 p_access->i_header_size =
707 GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 60 );
710 "answer 0x06 flags:0x%8.8x media_length:%ds packet_length:%d packet_count:%d max_bit_rate:%d header_size:%d",
711 p_access->i_flags_broadcast,
712 p_access->i_media_length,
713 p_access->i_packet_length,
714 p_access->i_packet_count,
715 p_access->i_max_bit_rate,
716 p_access->i_header_size );
718 /* *** send command 15 *** */
720 var_buffer_reinitwrite( &buffer, 0 );
721 var_buffer_add32( &buffer, 0 );
722 var_buffer_add32( &buffer, 0x8000 );
723 var_buffer_add32( &buffer, 0xffffffff );
724 var_buffer_add32( &buffer, 0x00 );
725 var_buffer_add32( &buffer, 0x00 );
726 var_buffer_add32( &buffer, 0x00 );
727 var_buffer_add64( &buffer, 0x40ac200000000000 );
728 var_buffer_add32( &buffer, p_access->i_header_packet_id_type );
729 mms_CommandSend( p_input, 0x15, p_access->i_command_level, 0x00,
730 buffer.p_data, buffer.i_data );
732 /* *** wait for reponse *** */
733 mms_CommandRead( p_input, 0x11, 0 );
735 if( p_access->i_command != 0x11 )
738 "unknown answer (0x%x instead of 0x11)",
739 p_access->i_command );
740 var_buffer_free( &buffer );
744 /* *** now read header packet *** */
745 if( mms_HeaderMediaRead( p_input, MMS_PACKET_HEADER ) < 0 )
747 msg_Err( p_input, "cannot receive header" );
748 var_buffer_free( &buffer );
752 /* *** parse header and get stream and their id *** */
753 // get all streams properties,
755 // TODO : stream bitrates properties(optional)
756 // and bitrate mutual exclusion(optional)
757 asf_HeaderParse( p_access->stream,
758 p_access->p_header, p_access->i_header );
760 /* *** now select stream we want to receive *** */
761 // TODO take care of stream bitrate TODO
764 var_buffer_reinitwrite( &buffer, 0 );
765 /* for now, select first audio and video stream */
768 for( i = 1; i < 128; i++ )
771 if( ( p_access->stream[i].i_cat == MMS_STREAM_AUDIO && !b_audio )||
772 ( p_access->stream[i].i_cat == MMS_STREAM_VIDEO && !b_video ) )
778 var_buffer_add16( &buffer, 0x0000 ); // on
782 var_buffer_add16( &buffer, 0xffff );
783 var_buffer_add16( &buffer, i );
784 var_buffer_add16( &buffer, 0x0000 );
787 "selecting stream[0x%x] %s (%d kb/s)",
789 ( p_access->stream[i].i_cat == MMS_STREAM_AUDIO ) ?
791 p_access->stream[i].i_bitrate / 1024);
792 if( p_access->stream[i].i_cat == MMS_STREAM_AUDIO )
796 if( p_access->stream[i].i_cat == MMS_STREAM_VIDEO )
802 else if( p_access->stream[i].i_cat != MMS_STREAM_UNKNOWN )
805 "ignoring stream[0x%x] %s (%d kb/s)",
807 ( p_access->stream[i].i_cat == MMS_STREAM_AUDIO ) ?
809 p_access->stream[i].i_bitrate / 1024);
815 msg_Err( p_input, "cannot find any stream" );
816 var_buffer_free( &buffer );
820 mms_CommandSend( p_input, 0x33,
822 0xffff | ( i_first << 16 ),
823 buffer.p_data, buffer.i_data );
825 mms_CommandRead( p_input, 0x21, 0 );
826 if( p_access->i_command != 0x21 )
829 "unknown answer (0x%x instead of 0x21)",
830 p_access->i_command );
831 var_buffer_free( &buffer );
837 var_buffer_free( &buffer );
839 msg_Info( p_input, "connection sucessful" );
844 /****************************************************************************
845 * MMSStart : Start streaming
846 ****************************************************************************/
847 static int MMSStart ( input_thread_t *p_input, uint32_t i_packet )
849 access_t *p_access = (access_t*)p_input->p_access_data;
852 /* *** start stream from packet 0 *** */
853 var_buffer_initwrite( &buffer, 0 );
854 var_buffer_add64( &buffer, 0 ); // seek point in second
855 var_buffer_add32( &buffer, 0xffffffff );
856 // var_buffer_add32( &buffer, 0xffffffff ); // begin from start
857 var_buffer_add32( &buffer, i_packet ); // begin from start
858 var_buffer_add8( &buffer, 0xff ); // stream time limit
859 var_buffer_add8( &buffer, 0xff ); // on 3bytes ...
860 var_buffer_add8( &buffer, 0xff ); //
861 var_buffer_add8( &buffer, 0x00 ); // don't use limit
862 var_buffer_add32( &buffer, p_access->i_media_packet_id_type );
864 mms_CommandSend( p_input, 0x07, p_access->i_command_level, 0x0001ffff,
865 buffer.p_data, buffer.i_data );
867 var_buffer_free( &buffer );
869 mms_CommandRead( p_input, 0x05, 0 );
871 if( p_access->i_command != 0x05 )
874 "unknown answer (0x%x instead of 0x05)",
875 p_access->i_command );
881 mms_HeaderMediaRead( p_input, MMS_PACKET_MEDIA );
882 msg_Dbg( p_input, "Streaming started" );
887 /****************************************************************************
888 * MMSStop : Stop streaming
889 ****************************************************************************/
890 static int MMSStop ( input_thread_t *p_input )
892 access_t *p_access = (access_t*)p_input->p_access_data;
894 /* *** stop stream but keep connection alive *** */
895 mms_CommandSend( p_input,
897 p_access->i_command_level,
903 /****************************************************************************
904 * MMSClose : Close streaming and connection
905 ****************************************************************************/
906 static int MMSClose ( input_thread_t *p_input )
908 access_t *p_access = (access_t*)p_input->p_access_data;
910 msg_Dbg( p_input, "Connection closed" );
912 /* *** tell server that we will disconnect *** */
913 mms_CommandSend( p_input,
915 p_access->i_command_level,
918 /* *** close sockets *** */
919 p_access->_socket = p_access->socket_server;
920 INPUT_FDNETWORKCLOSE( p_input );
922 if( p_access->i_proto == MMS_PROTO_UDP )
924 p_access->_socket = p_access->socket_data;
925 INPUT_FDNETWORKCLOSE( p_input );
928 FREE( p_access->p_media );
929 FREE( p_access->p_header );
931 FREE( p_access->psz_server_version );
932 FREE( p_access->psz_tool_version );
933 FREE( p_access->psz_update_player_url );
934 FREE( p_access->psz_encryption_type );
939 /*****************************************************************************
940 * mms_ParseURL : parse an url string and fill an url_t
941 *****************************************************************************/
942 static void mms_ParseURL( url_t *p_url, char *psz_url )
945 char *psz_server_port;
947 p_url->psz_private = strdup( psz_url );
949 psz_parser = p_url->psz_private;
951 while( *psz_parser == '/' )
955 p_url->psz_server_addr = psz_parser;
957 while( *psz_parser && *psz_parser != ':' && *psz_parser != '/' )
962 if( *psz_parser == ':' )
966 psz_server_port = psz_parser;
968 while( *psz_parser && *psz_parser != '/' )
975 psz_server_port = "";
977 if( *psz_parser == '/' )
981 p_url->psz_path = psz_parser;
984 if( *psz_server_port )
986 p_url->i_server_port = strtol( psz_server_port, &psz_parser, 10 );
990 p_url->i_server_port = 0;
994 static int mms_ReadData( input_thread_t *p_input,
1002 i_read = input_FDNetworkRead( p_input, p_data, i_data );
1005 msg_Err( p_input, "failed to read data" );
1015 /****************************************************************************
1017 * MMS specific functions
1019 ****************************************************************************/
1021 static int mms_CommandSend( input_thread_t *p_input,
1023 uint32_t i_prefix1, uint32_t i_prefix2,
1024 uint8_t *p_data, int i_data )
1026 var_buffer_t buffer;
1028 access_t *p_access = (access_t*)p_input->p_access_data;
1031 i_data_by8 = ( i_data + 7 ) / 8;
1033 /* first init uffer */
1034 var_buffer_initwrite( &buffer, 0 );
1036 var_buffer_add32( &buffer, 0x00000001 ); // start sequence
1037 var_buffer_add32( &buffer, 0xB00BFACE ); // ...
1038 // size after protocol type
1039 var_buffer_add32( &buffer, i_data + MMS_CMD_HEADERSIZE - 16 );
1040 var_buffer_add32( &buffer, 0x20534d4d ); // protocol "MMS "
1041 var_buffer_add32( &buffer, i_data_by8 + 4 );
1042 var_buffer_add32( &buffer, p_access->i_seq_num ); p_access->i_seq_num++;
1043 var_buffer_add64( &buffer, 0 );
1044 var_buffer_add32( &buffer, i_data_by8 + 2 );
1045 var_buffer_add32( &buffer, 0x00030000 | i_command ); /* dir | command */
1046 var_buffer_add32( &buffer, i_prefix1 ); /* command specific */
1047 var_buffer_add32( &buffer, i_prefix2 ); /* command specific */
1049 /* specific command data */
1050 if( p_data && i_data > 0 )
1052 var_buffer_addmemory( &buffer, p_data, i_data );
1056 if( send( p_access->_socket.i_handle,
1061 msg_Err( p_input, "failed to send command" );
1065 var_buffer_free( &buffer );
1069 static int mms_ReceiveCommand( input_thread_t *p_input )
1071 #define GET32( i_pos ) \
1072 ( p_access->p_cmd[i_pos] + ( p_access->p_cmd[i_pos +1] << 8 ) + \
1073 ( p_access->p_cmd[i_pos + 2] << 16 ) + \
1074 ( p_access->p_cmd[i_pos + 3] << 24 ) )
1076 access_t *p_access = (access_t*)p_input->p_access_data;
1083 /* *** Read complete command *** */
1085 input_FDNetworkRead( p_input, p_access->p_cmd, BUF_SIZE );
1086 if( p_access->i_cmd < 12 )
1088 msg_Warn( p_input, "failed to receive command" );
1089 p_access->i_command = 0;
1092 i_length = GetDWLE( p_access->p_cmd + 8 ) + 16;
1093 if( i_length > p_access->i_cmd )
1095 if( mms_ReadData( p_input,
1096 p_access->p_cmd + p_access->i_cmd,
1097 i_length - p_access->i_cmd ) < 0 )
1099 msg_Warn( p_input, "failed to receive command" );
1100 p_access->i_command = 0;
1105 msg_Dbg( p_input, "received %d bytes", p_access->i_cmd );
1107 p_access->i_command = GET32( 36 ) & 0xffff;
1109 "recv command start_sequence:0x%8.8x command_id:0x%8.8x length:%d len8:%d sequence 0x%8.8x len8_II:%d dir_comm:0x%8.8x",
1119 if( p_access->i_command == 0x1b )
1121 mms_CommandSend( p_input, 0x1b, 0, 0, NULL, 0 );
1124 } while( p_access->i_command == 0x1b );
1129 #define MMS_RETRY_MAX 10
1130 #define MMS_RETRY_SLEEP 50000
1132 static int mms_CommandRead( input_thread_t *p_input, int i_command1, int i_command2 )
1134 access_t *p_access = (access_t*)p_input->p_access_data;
1138 for( i_count = 0; i_count < MMS_RETRY_MAX; )
1141 i_status = mms_ReceiveCommand( p_input );
1142 if( i_status < 0 || p_access->i_command == 0 )
1145 msleep( MMS_RETRY_SLEEP );
1147 else if( i_command1 == 0 && i_command2 == 0)
1151 else if( p_access->i_command == i_command1 || p_access->i_command == i_command2 )
1157 switch( p_access->i_command )
1160 msg_Warn( p_input, "socket closed by server" );
1163 msg_Warn( p_input, "end of media stream" );
1170 msg_Warn( p_input, "failed to receive command (abording)" );
1177 static int mms_ReceivePacket( input_thread_t *p_input )
1179 access_t *p_access = (access_t*)p_input->p_access_data;
1180 uint8_t preheader[8];
1183 if( p_access->i_proto == MMS_PROTO_UDP )
1191 if( ( i_read = input_FDNetworkRead( p_input, preheader, 8 ) ) < 8 )
1193 msg_Warn( p_input, "cannot read preheader" );
1196 /* preheader format :
1197 * u32 i_sequence_number
1199 * u8 i_udp_sequence/i_tcp_flags
1202 if( preheader[4] == p_access->i_header_packet_id_type ||
1203 preheader[4] == p_access->i_media_packet_id_type ||
1204 preheader[4] == 0xff )// udp timing pair
1206 int i_packet_seq_num;
1207 int i_packet_length;
1212 i_packet_seq_num = GetDWLE( preheader );
1213 i_packet_length = GetWLE( preheader + 6 );
1214 i_packet_id = preheader[4];
1216 /* *** read complete packet *** */
1217 if( i_packet_length <= 8 )
1220 "empty or broken packet" );
1223 p_packet = malloc( i_packet_length - 8 );
1224 if( mms_ReadData( p_input,
1226 i_packet_length - 8 ) < 0 )
1229 "cannot read data" );
1233 if( i_packet_id == 0xff )
1236 "receive MMS UDP pair timing" );
1238 return( MMS_PACKET_UDP_TIMING );
1242 if( i_packet_seq_num != p_access->i_packet_seq_num )
1244 // FIXME for udp could be just wrong order ?
1246 "detected packet lost (%d != %d)",
1248 p_access->i_packet_seq_num );
1249 p_access->i_packet_seq_num = i_packet_seq_num;
1251 p_access->i_packet_seq_num++;
1253 if( i_packet_id == p_access->i_header_packet_id_type )
1255 FREE( p_access->p_header );
1256 p_access->p_header = p_packet;
1257 p_access->i_header = i_packet_length - 8;
1258 return( MMS_PACKET_HEADER );
1262 FREE( p_access->p_media );
1263 p_access->p_media = p_packet;
1264 p_access->i_media = i_packet_length - 8;
1265 p_access->i_media_used = 0;
1266 return( MMS_PACKET_MEDIA );
1272 int i_packet_length;
1274 if( GetDWLE( preheader + 4 ) != 0xb00bface )
1277 "incorrect command header (0x%x)",
1278 GetDWLE( preheader + 4 ) );
1280 memcpy( p_access->p_cmd, preheader, 8 );
1281 if( mms_ReadData( p_input,
1282 p_access->p_cmd + 8,
1286 "cannot read data" );
1288 p_access->i_cmd = 16;
1289 i_packet_length = GetDWLE( p_access->p_cmd + 8 );
1290 if( mms_ReadData( p_input,
1291 p_access->p_cmd + 16,
1292 i_packet_length ) < 0 )
1295 "cannot read data" );
1297 p_access->i_cmd += i_packet_length;
1298 p_access->i_command = GetDWLE( p_access->p_cmd + 36 ) & 0xffff;
1299 if( p_access->i_command == 0x1b )
1301 mms_CommandSend( p_input, 0x1b, 0, 0, NULL, 0 );
1305 return( MMS_PACKET_CMD );
1314 static int mms_HeaderMediaRead( input_thread_t *p_input, int i_type )
1316 access_t *p_access = (access_t*)p_input->p_access_data;
1319 for( i_count = 0; i_count < MMS_RETRY_MAX; )
1323 i_status = mms_ReceivePacket( p_input );
1328 "cannot receive header (%d/%d)", i_count, MMS_RETRY_MAX );
1329 msleep( MMS_RETRY_SLEEP );
1331 else if( i_status == i_type )
1335 else if( i_status == MMS_PACKET_CMD )
1337 switch( p_access->i_command )
1340 msg_Warn( p_input, "socket closed by server" );
1343 msg_Warn( p_input, "end of media stream" );
1346 /* XXX not too dificult to be done EXCEPT that we
1347 * need to restart demuxer... and I don't see how we
1348 * could do that :p */
1350 "reinitialization needed --> unsupported" );
1358 "cannot receive %s (abording)",
1359 ( i_type == MMS_PACKET_HEADER ) ? "header" : "media data" );