1 /*****************************************************************************
2 * mms.c: MMS access plug-in
3 *****************************************************************************
4 * Copyright (C) 2001, 2002 VideoLAN
5 * $Id: mms.c,v 1.3 2002/11/13 20:28:13 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 ssize_t NetRead ( input_thread_t * p_input, input_socket_t * p_socket,
79 byte_t * p_buffer, size_t i_len );
80 static void Seek ( input_thread_t *, off_t );
81 static int SetProgram ( input_thread_t *, pgrm_descriptor_t * );
84 static int MMSOpen( input_thread_t *, url_t *, int, char * );
86 static int MMSStart ( input_thread_t *, uint32_t );
87 static int MMSStop ( input_thread_t *p_input ); // Not used
89 static int MMSClose ( input_thread_t * );
92 static int mms_CommandRead( input_thread_t *p_input, int i_command1, int i_command2 );
93 static int mms_CommandSend( input_thread_t *, int, uint32_t, uint32_t, uint8_t *, int );
95 static int mms_HeaderMediaRead( input_thread_t *, int );
97 static int mms_ReceivePacket( input_thread_t * );
99 static void mms_ParseURL( url_t *p_url, char *psz_url );
104 * XXX DON'T FREE MY MEMORY !!! XXX
109 * Ok, ok, j'le ferai plus...
113 /*****************************************************************************
115 *****************************************************************************/
117 set_description( _("MMS access module") );
118 set_capability( "access", 0 );
119 add_category_hint( "stream", NULL );
120 add_bool( "mms-all", 0, NULL,
121 "force selection of all streams",
122 "force selection of all streams" );
123 add_string( "mms-stream", NULL, NULL,
125 "force this stream selection" );
126 add_integer( "mms-maxbitrate", 0, NULL,
128 "set max bitrate for auto streams selections" );
129 add_shortcut( "mms" );
130 add_shortcut( "mmsu" );
131 add_shortcut( "mmst" );
132 set_callbacks( Open, Close );
135 #define BUF_SIZE 200000
137 static int Open( vlc_object_t *p_this )
144 input_thread_t *p_input = (input_thread_t*)p_this;
146 /* *** allocate p_access_data *** */
147 p_input->p_access_data =
148 (void*)p_access = malloc( sizeof( access_t ) );
149 memset( p_access, 0, sizeof( access_t ) );
151 p_access->p_cmd = malloc( BUF_SIZE );
154 /* *** Parse URL and get server addr/port and path *** */
155 mms_ParseURL( &p_access->url, p_input->psz_name );
156 if( p_access->url.psz_server_addr == NULL ||
157 !( *p_access->url.psz_server_addr ) )
159 FREE( p_access->url.psz_private );
160 FREE( p_access->p_cmd );
161 msg_Err( p_input, "invalid server name" );
164 if( p_access->url.i_server_port == 0 )
166 p_access->url.i_server_port = 1755; // default port
170 /* *** connect to this server *** */
171 /* 1: look at requested protocol (udp/tcp) */
172 i_proto = MMS_PROTO_AUTO;
173 if( *p_input->psz_access )
175 if( !strncmp( p_input->psz_access, "mmsu", 4 ) )
177 i_proto = MMS_PROTO_UDP;
179 else if( !strncmp( p_input->psz_access, "mmst", 4 ) )
181 i_proto = MMS_PROTO_TCP;
184 /* 2: look at ip version ipv4/ipv6 */
186 if( config_GetInt( p_input, "ipv4" ) )
188 psz_network = "ipv4";
190 else if( config_GetInt( p_input, "ipv6" ) )
192 psz_network = "ipv6";
195 if( i_proto == MMS_PROTO_AUTO )
196 { // first try with TCP
198 MMSOpen( p_input, &p_access->url, MMS_PROTO_TCP, psz_network );
202 MMSOpen( p_input, &p_access->url, MMS_PROTO_UDP, psz_network );
209 MMSOpen( p_input, &p_access->url, i_proto, psz_network );
214 // all sockets are closed
215 msg_Err( p_input, "cannot connect to server" );
216 FREE( p_access->url.psz_private );
217 FREE( p_access->p_cmd );
220 msg_Dbg( p_input, "connected to %s", p_access->url.psz_server_addr );
222 // all sockets are open
225 /* *** set exported functions *** */
226 p_input->pf_read = Read;
227 p_input->pf_seek = Seek;
228 p_input->pf_set_program = SetProgram;
229 p_input->pf_set_area = NULL;
231 p_input->p_private = NULL; // XXX ??
233 /* *** finished to set some variable *** */
234 vlc_mutex_lock( &p_input->stream.stream_lock );
235 /* those data could be different for UDP/TCP */
236 p_input->stream.b_pace_control = 0;
237 p_input->stream.p_selected_area->i_tell = 0;
238 if( p_access->i_packet_count <= 0 )
240 p_input->stream.b_seekable = 0;
241 p_input->stream.p_selected_area->i_size = 0;
245 p_input->stream.b_seekable = 0;
246 p_input->stream.p_selected_area->i_size =
248 p_access->i_packet_count * p_access->i_packet_length;
251 p_input->stream.i_method = INPUT_METHOD_NETWORK;
252 vlc_mutex_unlock( &p_input->stream.stream_lock );
254 /* *** Start stream *** */
255 if( MMSStart( p_input, 0xffffffff ) < 0 )
257 msg_Err( p_input, "cannot start stream" );
259 FREE( p_access->url.psz_private );
260 FREE( p_access->p_cmd );
267 /*****************************************************************************
268 * Close: free unused data structures
269 *****************************************************************************/
270 static void Close( vlc_object_t *p_this )
272 input_thread_t * p_input = (input_thread_t *)p_this;
273 access_t *p_access = (access_t*)p_input->p_access_data;
275 /* close connection with server */
279 FREE( p_access->url.psz_private );
280 FREE( p_access->p_cmd );
283 /*****************************************************************************
284 * SetProgram: do nothing
285 *****************************************************************************/
286 static int SetProgram( input_thread_t * p_input,
287 pgrm_descriptor_t * p_program )
292 /*****************************************************************************
293 * Seek: try to go at the right place
294 *****************************************************************************/
295 static void Seek( input_thread_t * p_input, off_t i_pos )
300 * Probably some bad or missing command
306 access_t *p_access = (access_t*)p_input->p_access_data;
314 msg_Dbg( p_input, "seeking to %lld, header size:%d", i_pos, p_access->i_header );
315 if( i_pos < p_access->i_header)
318 if( p_access->i_pos < p_access->i_header )
320 /* no need to restart stream, it was already one
321 * or no stream was yet read */
322 p_access->i_pos = i_pos;
327 i_packet = 0xffffffff;
333 i_packet = ( i_pos - p_access->i_header ) / p_access->i_packet_length;
334 i_offset = ( i_pos - p_access->i_header ) % p_access->i_packet_length;
338 MMSStart( p_input, i_packet );
339 p_access->i_media_used += i_offset;
340 p_access->i_pos = i_pos;
344 static int Read ( input_thread_t * p_input, byte_t * p_buffer,
347 access_t *p_access = (access_t*)p_input->p_access_data;
353 /* *** send header if needed ** */
354 if( p_access->i_pos < p_access->i_header )
356 i_copy = __MIN( i_len, p_access->i_header - p_access->i_pos );
360 p_access->p_header + p_access->i_pos,
366 /* *** now send data if needed *** */
367 while( i_data < i_len )
369 if( p_access->i_media_used < p_access->i_media )
371 i_copy = __MIN( i_len - i_data ,
372 p_access->i_media - p_access->i_media_used );
373 memcpy( p_buffer + i_data,
374 p_access->p_media + p_access->i_media_used,
377 p_access->i_media_used += i_copy;
379 else if( p_access->p_media != NULL &&
380 p_access->i_media_used < p_access->i_packet_length )
382 i_copy = __MIN( i_len - i_data,
383 p_access->i_packet_length - p_access->i_media_used);
384 memset( p_buffer + i_data, 0, i_copy );
387 p_access->i_media_used += i_copy;
391 if( mms_HeaderMediaRead( p_input, MMS_PACKET_MEDIA ) < 0 );
393 p_access->i_pos += i_data;
399 p_access->i_pos += i_data;
403 /*****************************************************************************
404 * NetRead: read on a file descriptor, checking b_die periodically
405 *****************************************************************************/
406 static ssize_t NetRead( input_thread_t * p_input, input_socket_t * p_socket,
407 byte_t * p_buffer, size_t i_len )
413 struct timeval timeout;
417 /* Initialize file descriptor set */
419 FD_SET( p_socket->i_handle, &fds );
421 /* We'll wait 0.5 second if nothing happens */
423 timeout.tv_usec = 500000;
425 /* Find if some data is available */
426 i_ret = select( p_socket->i_handle + 1, &fds,
427 NULL, NULL, &timeout );
429 if( i_ret == -1 && errno != EINTR )
431 msg_Err( p_input, "network select error (%s)", strerror(errno) );
435 ssize_t i_recv = recv( p_socket->i_handle, p_buffer, i_len, 0 );
439 vlc_mutex_lock( &p_input->stream.stream_lock );
440 p_input->stream.p_selected_area->i_tell += i_recv;
441 vlc_mutex_unlock( &p_input->stream.stream_lock );
446 msg_Err( p_input, "recv failed (%s)", strerror(errno) );
457 static void asf_HeaderParse( mms_stream_t stream[128],
458 uint8_t *p_header, int i_header )
465 for( i = 0; i < 128; i++ )
467 stream[i].i_cat = MMS_STREAM_UNKNOWN;
470 var_buffer_initread( &buffer, p_header, i_header );
472 var_buffer_getguid( &buffer, &guid );
473 if( !CmpGuid( &guid, &asf_object_header_guid ) )
477 var_buffer_getmemory( &buffer, NULL, 30 - 16 );
481 if( var_buffer_readempty( &buffer ) )
486 var_buffer_getguid( &buffer, &guid );
487 i_size = var_buffer_get64( &buffer );
488 if( CmpGuid( &guid, &asf_object_stream_properties_guid ) )
493 // msg_Dbg( p_input, "found stream_properties" );
495 var_buffer_getguid( &buffer, &stream_type );
496 var_buffer_getmemory( &buffer, NULL, 32 );
497 i_stream_id = var_buffer_get8( &buffer ) & 0x7f;
498 var_buffer_getmemory( &buffer, NULL, i_size - 24 - 32 - 16 - 1 );
500 if( CmpGuid( &stream_type, &asf_object_stream_type_video ) )
502 // msg_Dbg( p_input, "video stream[%d] found", i_stream_id );
503 stream[i_stream_id].i_cat = MMS_STREAM_VIDEO;
505 else if( CmpGuid( &stream_type, &asf_object_stream_type_audio ) )
507 // msg_Dbg( p_input, "audio stream[%d] found", i_stream_id );
508 stream[i_stream_id].i_cat = MMS_STREAM_AUDIO;
512 // msg_Dbg( p_input, "unknown stream[%d] found", i_stream_id );
513 stream[i_stream_id].i_cat = MMS_STREAM_UNKNOWN;
516 else if ( CmpGuid( &guid, &asf_object_bitrate_properties_guid ) )
521 i_count = var_buffer_get16( &buffer );
525 i_stream_id = var_buffer_get16( &buffer )&0x7f;
526 stream[i_stream_id].i_bitrate = var_buffer_get32( &buffer );
530 var_buffer_getmemory( &buffer, NULL, i_size - 24 );
535 var_buffer_getmemory( &buffer, NULL, i_size - 24 );
540 static void mms_StreamSelect( input_thread_t * p_input,
541 mms_stream_t stream[128] )
543 /* XXX FIXME use mututal eclusion information */
545 int i_audio, i_video;
553 i_bitrate_max = config_GetInt( p_input, "mms-maxbitrate" );
555 if( config_GetInt( p_input, "mms-all" ) )
557 /* select all valid stream */
558 for( i = 1; i < 128; i++ )
560 if( stream[i].i_cat != MMS_STREAM_UNKNOWN )
562 stream[i].i_selected = 1;
569 for( i = 0; i < 128; i++ )
571 stream[i].i_selected = 0; /* by default, not selected */
574 psz_stream = config_GetPsz( p_input, "mms-stream" );
576 if( psz_stream && *psz_stream )
578 char *psz_tmp = psz_stream;
581 if( *psz_tmp == ',' )
588 i_stream = atoi( psz_tmp );
589 while( *psz_tmp != '\0' && *psz_tmp != ',' )
594 if( i_stream > 0 && i_stream < 128 &&
595 stream[i_stream].i_cat != MMS_STREAM_UNKNOWN )
597 stream[i_stream].i_selected = 1;
606 for( i = 1; i < 128; i++ )
608 if( stream[i].i_cat == MMS_STREAM_UNKNOWN )
612 else if( stream[i].i_cat == MMS_STREAM_AUDIO && i_audio <= 0 )
614 stream[i].i_selected = 1;
615 if( stream[i].i_bitrate> 0 )
617 i_bitrate_total += stream[i].i_bitrate;
621 else if( stream[i].i_cat == MMS_STREAM_VIDEO && i_video <= 0 )
623 stream[i].i_selected = 1;
624 if( stream[i].i_bitrate> 0 )
626 i_bitrate_total += stream[i].i_bitrate;
630 else if( i_bitrate_max > 0 )
633 // select this stream if it's bitrate is lower or upper
634 if( stream[i].i_cat == MMS_STREAM_AUDIO )
642 #define MMS_SELECT_XCHG( i1, i2 ) \
643 stream[i1].i_selected = 0; \
644 i_bitrate_total -= stream[i1].i_bitrate; \
645 stream[i2].i_selected = 1; \
646 i_bitrate_total += stream[i2].i_bitrate
648 if( stream[i].i_bitrate > 0 )
650 if( stream[i].i_bitrate < stream[i_index].i_bitrate &&
651 i_bitrate_total >= i_bitrate_max )
653 MMS_SELECT_XCHG( i_index, i );
655 else if( stream[i].i_bitrate > stream[i_index].i_bitrate &&
656 i_bitrate_total < i_bitrate_max )
658 MMS_SELECT_XCHG( i, i_index );
661 if( stream[i].i_cat == MMS_STREAM_AUDIO )
674 /****************************************************************************
675 * MMSOpen : Open a connection with the server over mmst or mmsu(not yet)
676 ****************************************************************************/
677 static int MMSOpen( input_thread_t *p_input,
680 char *psz_network ) /* "", "ipv4", "ipv6" */
683 access_t *p_access = (access_t*)p_input->p_access_data;
685 network_socket_t socket_desc;
686 int b_udp = ( i_proto == MMS_PROTO_UDP ) ? 1 : 0;
691 int i_server_version;
693 int i_update_player_url;
694 int i_encryption_type;
700 /* *** Open a TCP connection with server *** */
701 msg_Dbg( p_input, "waiting for connection..." );
702 socket_desc.i_type = NETWORK_TCP;
703 socket_desc.psz_server_addr = p_url->psz_server_addr;
704 socket_desc.i_server_port = p_url->i_server_port;
705 socket_desc.psz_bind_addr = "";
706 socket_desc.i_bind_port = 0;
707 p_input->p_private = (void*)&socket_desc;
708 if( !( p_network = module_Need( p_input, "network", psz_network ) ) )
710 msg_Err( p_input, "failed to open a connection" );
713 module_Unneed( p_input, p_network );
714 p_access->socket_server.i_handle = socket_desc.i_handle;
715 p_input->i_mtu = socket_desc.i_mtu; // FIXME
717 "connection with \"%s:%d\" successful",
718 p_url->psz_server_addr,
719 p_url->i_server_port );
721 /* *** Bind port if UDP protocol is selected *** */
726 "MMS/UDP not yet implemented" );
728 #if defined( UNDER_CE )
729 CloseHandle( (HANDLE)p_access->socket_server.i_handle );
730 #elif defined( WIN32 )
731 closesocket( p_access->socket_server.i_handle );
733 close( p_access->socket_server.i_handle );
738 /* *** Init context for mms prototcol *** */
739 GenerateGuid( &p_access->guid ); // used to identify client by server
741 "generated guid: "GUID_FMT,
742 GUID_PRINT( p_access->guid ) );
743 p_access->i_command_level = 1; // updated after 0x1A command
744 p_access->i_seq_num = 0;
745 p_access->i_media_packet_id_type = 0x04;
746 p_access->i_header_packet_id_type = 0x02;
747 p_access->i_proto = i_proto;
748 p_access->i_packet_seq_num = 0;
749 p_access->p_header = NULL;
750 p_access->i_header = 0;
751 p_access->p_media = NULL;
752 p_access->i_media = 0;
753 p_access->i_media_used = 0;
757 /* *** send command 1 : connection request *** */
758 var_buffer_initwrite( &buffer, 0 );
759 var_buffer_add16( &buffer, 0x001c );
760 var_buffer_add16( &buffer, 0x0003 );
762 "NSPlayer/7.0.0.1956; {"GUID_FMT"}; Host: %s",
763 GUID_PRINT( p_access->guid ),
764 p_url->psz_server_addr );
765 var_buffer_addUTF16( &buffer, tmp );
767 mms_CommandSend( p_input,
768 0x01, /* connexion request */
769 0x00000000, /* flags, FIXME */
770 0x0004000b, /* ???? */
774 mms_CommandRead( p_input, 0x01, 0 );
775 i_server_version = GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 32 );
776 i_tool_version = GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 36 );
777 i_update_player_url = GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 40 );
778 i_encryption_type = GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 44 );
779 p = (uint16_t*)( p_access->p_cmd + MMS_CMD_HEADERSIZE + 48 );
780 #define GETUTF16( psz, size ) \
783 psz = malloc( size + 1); \
784 for( i = 0; i < size; i++ ) \
791 GETUTF16( p_access->psz_server_version, i_server_version );
792 GETUTF16( p_access->psz_tool_version, i_tool_version );
793 GETUTF16( p_access->psz_update_player_url, i_update_player_url );
794 GETUTF16( p_access->psz_encryption_type, i_encryption_type );
797 "0x01 --> server_version:\"%s\" tool_version:\"%s\" update_player_url:\"%s\" encryption_type:\"%s\"",
798 p_access->psz_server_version,
799 p_access->psz_tool_version,
800 p_access->psz_update_player_url,
801 p_access->psz_encryption_type );
803 /* *** should make an 18 command to make data timing *** */
805 /* *** send command 2 : transport protocol selection *** */
806 var_buffer_reinitwrite( &buffer, 0 );
807 var_buffer_add32( &buffer, 0x00000000 );
808 var_buffer_add32( &buffer, 0x000a0000 );
809 var_buffer_add32( &buffer, 0x00000002 );
810 // FIXME wrong for UDP FIXME
811 sprintf( tmp, "\\\\127.0.0.1\\%s\\1242", b_udp ? "UDP" : "TCP" );
812 var_buffer_addUTF16( &buffer, tmp );
813 var_buffer_add16( &buffer, '0' );
815 mms_CommandSend( p_input,
816 0x02, /* connexion request */
817 0x00000000, /* flags, FIXME */
818 0xffffffff, /* ???? */
822 /* *** response from server, should be 0x02 or 0x03 *** */
823 mms_CommandRead( p_input, 0x02, 0 );
824 if( p_access->i_command == 0x03 )
827 "%s protocol selection failed", b_udp ? "UDP" : "TCP" );
828 var_buffer_free( &buffer );
832 else if( p_access->i_command != 0x02 )
834 msg_Warn( p_input, "received command isn't 0x02 in reponse to 0x02" );
837 /* *** send command 5 : media file name/path requested *** */
838 var_buffer_reinitwrite( &buffer, 0 );
839 var_buffer_add64( &buffer, 0 );
840 // var_buffer_addUTF16( &buffer, "/" );
841 var_buffer_addUTF16( &buffer, p_url->psz_path );
843 mms_CommandSend( p_input,
845 p_access->i_command_level,
850 /* *** wait for reponse *** */
851 mms_CommandRead( p_input, 0x1a, 0x06 );
853 /* test if server send 0x1A answer */
854 if( p_access->i_command == 0x1A )
856 msg_Err( p_input, "id/password requested (not yet supported)" );
858 var_buffer_free( &buffer );
862 if( p_access->i_command != 0x06 )
865 "unknown answer (0x%x instead of 0x06)",
866 p_access->i_command );
867 var_buffer_free( &buffer );
872 // 1 for file ok, 2 for authen ok
873 switch( GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE ) )
876 msg_Dbg( p_input, "Media file name/path accepted" );
879 msg_Dbg( p_input, "Authentication accepted" );
883 msg_Err( p_input, "error while asking for file %d",
884 GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE ) );
885 var_buffer_free( &buffer );
890 p_access->i_flags_broadcast =
891 GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 12 );
892 p_access->i_media_length =
893 GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 24 );
894 p_access->i_packet_length =
895 GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 44 );
896 p_access->i_packet_count =
897 GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 48 );
898 p_access->i_max_bit_rate =
899 GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 56 );
900 p_access->i_header_size =
901 GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 60 );
904 "answer 0x06 flags:0x%8.8x media_length:%ds packet_length:%d packet_count:%d max_bit_rate:%d header_size:%d",
905 p_access->i_flags_broadcast,
906 p_access->i_media_length,
907 p_access->i_packet_length,
908 p_access->i_packet_count,
909 p_access->i_max_bit_rate,
910 p_access->i_header_size );
912 /* *** send command 15 *** */
914 var_buffer_reinitwrite( &buffer, 0 );
915 var_buffer_add32( &buffer, 0 );
916 var_buffer_add32( &buffer, 0x8000 );
917 var_buffer_add32( &buffer, 0xffffffff );
918 var_buffer_add32( &buffer, 0x00 );
919 var_buffer_add32( &buffer, 0x00 );
920 var_buffer_add32( &buffer, 0x00 );
921 var_buffer_add64( &buffer, 0x40ac200000000000 );
922 var_buffer_add32( &buffer, p_access->i_header_packet_id_type );
923 mms_CommandSend( p_input, 0x15, p_access->i_command_level, 0x00,
924 buffer.p_data, buffer.i_data );
926 /* *** wait for reponse *** */
927 mms_CommandRead( p_input, 0x11, 0 );
929 if( p_access->i_command != 0x11 )
932 "unknown answer (0x%x instead of 0x11)",
933 p_access->i_command );
934 var_buffer_free( &buffer );
938 /* *** now read header packet *** */
939 if( mms_HeaderMediaRead( p_input, MMS_PACKET_HEADER ) < 0 )
941 msg_Err( p_input, "cannot receive header" );
942 var_buffer_free( &buffer );
946 /* *** parse header and get stream and their id *** */
947 // get all streams properties,
949 // TODO : stream bitrates properties(optional)
950 // and bitrate mutual exclusion(optional)
951 asf_HeaderParse( p_access->stream,
952 p_access->p_header, p_access->i_header );
953 mms_StreamSelect( p_input, p_access->stream );
954 /* *** now select stream we want to receive *** */
955 // TODO take care of stream bitrate TODO
958 var_buffer_reinitwrite( &buffer, 0 );
959 /* for now, select first audio and video stream */
960 for( i = 1; i < 128; i++ )
963 if( p_access->stream[i].i_cat != MMS_STREAM_UNKNOWN )
968 var_buffer_add16( &buffer, 0xffff );
969 var_buffer_add16( &buffer, i );
975 if( p_access->stream[i].i_selected )
977 var_buffer_add16( &buffer, 0x0000 );
979 "selecting stream[0x%x] %s (%d kb/s)",
981 ( p_access->stream[i].i_cat == MMS_STREAM_AUDIO ) ?
983 p_access->stream[i].i_bitrate / 1024);
987 var_buffer_add16( &buffer, 0x0002 );
989 "ignoring stream[0x%x] %s (%d kb/s)",
991 ( p_access->stream[i].i_cat == MMS_STREAM_AUDIO ) ?
993 p_access->stream[i].i_bitrate / 1024);
1001 msg_Err( p_input, "cannot find any stream" );
1002 var_buffer_free( &buffer );
1003 MMSClose( p_input );
1006 mms_CommandSend( p_input, 0x33,
1008 0xffff | ( i_first << 16 ),
1009 buffer.p_data, buffer.i_data );
1011 mms_CommandRead( p_input, 0x21, 0 );
1012 if( p_access->i_command != 0x21 )
1015 "unknown answer (0x%x instead of 0x21)",
1016 p_access->i_command );
1017 var_buffer_free( &buffer );
1018 MMSClose( p_input );
1023 var_buffer_free( &buffer );
1025 msg_Info( p_input, "connection sucessful" );
1030 /****************************************************************************
1031 * MMSStart : Start streaming
1032 ****************************************************************************/
1033 static int MMSStart ( input_thread_t *p_input, uint32_t i_packet )
1035 access_t *p_access = (access_t*)p_input->p_access_data;
1036 var_buffer_t buffer;
1038 /* *** start stream from packet 0 *** */
1039 var_buffer_initwrite( &buffer, 0 );
1040 var_buffer_add64( &buffer, 0 ); // seek point in second
1041 var_buffer_add32( &buffer, 0xffffffff );
1042 // var_buffer_add32( &buffer, 0xffffffff ); // begin from start
1043 var_buffer_add32( &buffer, i_packet ); // begin from start
1044 var_buffer_add8( &buffer, 0xff ); // stream time limit
1045 var_buffer_add8( &buffer, 0xff ); // on 3bytes ...
1046 var_buffer_add8( &buffer, 0xff ); //
1047 var_buffer_add8( &buffer, 0x00 ); // don't use limit
1048 var_buffer_add32( &buffer, p_access->i_media_packet_id_type );
1050 mms_CommandSend( p_input, 0x07, p_access->i_command_level, 0x0001ffff,
1051 buffer.p_data, buffer.i_data );
1053 var_buffer_free( &buffer );
1055 mms_CommandRead( p_input, 0x05, 0 );
1057 if( p_access->i_command != 0x05 )
1060 "unknown answer (0x%x instead of 0x05)",
1061 p_access->i_command );
1067 mms_HeaderMediaRead( p_input, MMS_PACKET_MEDIA );
1068 msg_Dbg( p_input, "Streaming started" );
1073 /****************************************************************************
1074 * MMSStop : Stop streaming
1075 ****************************************************************************/
1076 static int MMSStop ( input_thread_t *p_input )
1078 access_t *p_access = (access_t*)p_input->p_access_data;
1080 /* *** stop stream but keep connection alive *** */
1081 mms_CommandSend( p_input,
1083 p_access->i_command_level,
1089 /****************************************************************************
1090 * MMSClose : Close streaming and connection
1091 ****************************************************************************/
1092 static int MMSClose ( input_thread_t *p_input )
1094 access_t *p_access = (access_t*)p_input->p_access_data;
1096 msg_Dbg( p_input, "Connection closed" );
1098 /* *** tell server that we will disconnect *** */
1099 mms_CommandSend( p_input,
1101 p_access->i_command_level,
1104 /* *** close sockets *** */
1105 #if defined( UNDER_CE )
1106 CloseHandle( (HANDLE)p_access->socket_server.i_handle );
1107 #elif defined( WIN32 )
1108 closesocket( p_access->socket_server.i_handle );
1110 close( p_access->socket_server.i_handle );
1113 if( p_access->i_proto == MMS_PROTO_UDP )
1115 #if defined( UNDER_CE )
1116 CloseHandle( (HANDLE)p_access->socket_data.i_handle );
1117 #elif defined( WIN32 )
1118 closesocket( p_access->socket_data.i_handle );
1120 close( p_access->socket_data.i_handle );
1124 FREE( p_access->p_media );
1125 FREE( p_access->p_header );
1127 FREE( p_access->psz_server_version );
1128 FREE( p_access->psz_tool_version );
1129 FREE( p_access->psz_update_player_url );
1130 FREE( p_access->psz_encryption_type );
1135 /*****************************************************************************
1136 * mms_ParseURL : parse an url string and fill an url_t
1137 *****************************************************************************/
1138 static void mms_ParseURL( url_t *p_url, char *psz_url )
1141 char *psz_server_port;
1143 p_url->psz_private = strdup( psz_url );
1145 psz_parser = p_url->psz_private;
1147 while( *psz_parser == '/' )
1151 p_url->psz_server_addr = psz_parser;
1153 while( *psz_parser && *psz_parser != ':' && *psz_parser != '/' )
1158 if( *psz_parser == ':' )
1162 psz_server_port = psz_parser;
1164 while( *psz_parser && *psz_parser != '/' )
1171 psz_server_port = "";
1173 if( *psz_parser == '/' )
1177 p_url->psz_path = psz_parser;
1180 if( *psz_server_port )
1182 p_url->i_server_port = strtol( psz_server_port, &psz_parser, 10 );
1186 p_url->i_server_port = 0;
1190 static int mms_ReadData( input_thread_t *p_input,
1194 access_t *p_access = (access_t*)p_input->p_access_data;
1200 i_read = NetRead( p_input, &p_access->socket_server, p_data, i_data );
1203 msg_Err( p_input, "failed to read data" );
1213 /****************************************************************************
1215 * MMS specific functions
1217 ****************************************************************************/
1219 static int mms_CommandSend( input_thread_t *p_input,
1221 uint32_t i_prefix1, uint32_t i_prefix2,
1222 uint8_t *p_data, int i_data )
1224 var_buffer_t buffer;
1226 access_t *p_access = (access_t*)p_input->p_access_data;
1229 i_data_by8 = ( i_data + 7 ) / 8;
1231 /* first init uffer */
1232 var_buffer_initwrite( &buffer, 0 );
1234 var_buffer_add32( &buffer, 0x00000001 ); // start sequence
1235 var_buffer_add32( &buffer, 0xB00BFACE ); // ...
1236 // size after protocol type
1237 var_buffer_add32( &buffer, i_data + MMS_CMD_HEADERSIZE - 16 );
1238 var_buffer_add32( &buffer, 0x20534d4d ); // protocol "MMS "
1239 var_buffer_add32( &buffer, i_data_by8 + 4 );
1240 var_buffer_add32( &buffer, p_access->i_seq_num ); p_access->i_seq_num++;
1241 var_buffer_add64( &buffer, 0 );
1242 var_buffer_add32( &buffer, i_data_by8 + 2 );
1243 var_buffer_add32( &buffer, 0x00030000 | i_command ); /* dir | command */
1244 var_buffer_add32( &buffer, i_prefix1 ); /* command specific */
1245 var_buffer_add32( &buffer, i_prefix2 ); /* command specific */
1247 /* specific command data */
1248 if( p_data && i_data > 0 )
1250 var_buffer_addmemory( &buffer, p_data, i_data );
1254 if( send( p_access->socket_server.i_handle,
1259 msg_Err( p_input, "failed to send command" );
1263 var_buffer_free( &buffer );
1267 static int mms_ReceiveCommand( input_thread_t *p_input )
1269 #define GET32( i_pos ) \
1270 ( p_access->p_cmd[i_pos] + ( p_access->p_cmd[i_pos +1] << 8 ) + \
1271 ( p_access->p_cmd[i_pos + 2] << 16 ) + \
1272 ( p_access->p_cmd[i_pos + 3] << 24 ) )
1274 access_t *p_access = (access_t*)p_input->p_access_data;
1281 /* *** Read complete command *** */
1282 p_access->i_cmd = NetRead( p_input, &p_access->socket_server,
1283 p_access->p_cmd, BUF_SIZE );
1284 if( p_access->i_cmd < 12 )
1286 msg_Warn( p_input, "failed to receive command" );
1287 p_access->i_command = 0;
1290 i_length = GetDWLE( p_access->p_cmd + 8 ) + 16;
1291 if( i_length > p_access->i_cmd )
1293 if( mms_ReadData( p_input,
1294 p_access->p_cmd + p_access->i_cmd,
1295 i_length - p_access->i_cmd ) < 0 )
1297 msg_Warn( p_input, "failed to receive command" );
1298 p_access->i_command = 0;
1303 msg_Dbg( p_input, "received %d bytes", p_access->i_cmd );
1305 p_access->i_command = GET32( 36 ) & 0xffff;
1307 "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",
1317 if( p_access->i_command == 0x1b )
1319 mms_CommandSend( p_input, 0x1b, 0, 0, NULL, 0 );
1322 } while( p_access->i_command == 0x1b );
1327 #define MMS_RETRY_MAX 10
1328 #define MMS_RETRY_SLEEP 50000
1330 static int mms_CommandRead( input_thread_t *p_input, int i_command1, int i_command2 )
1332 access_t *p_access = (access_t*)p_input->p_access_data;
1336 for( i_count = 0; i_count < MMS_RETRY_MAX; )
1339 i_status = mms_ReceiveCommand( p_input );
1340 if( i_status < 0 || p_access->i_command == 0 )
1343 msleep( MMS_RETRY_SLEEP );
1345 else if( i_command1 == 0 && i_command2 == 0)
1349 else if( p_access->i_command == i_command1 || p_access->i_command == i_command2 )
1355 switch( p_access->i_command )
1358 msg_Warn( p_input, "socket closed by server" );
1361 msg_Warn( p_input, "end of media stream" );
1368 msg_Warn( p_input, "failed to receive command (abording)" );
1375 static int mms_ReceivePacket( input_thread_t *p_input )
1377 access_t *p_access = (access_t*)p_input->p_access_data;
1378 uint8_t preheader[8];
1381 if( p_access->i_proto == MMS_PROTO_UDP )
1389 i_read = NetRead( p_input, &p_access->socket_server, preheader, 8 );
1392 msg_Warn( p_input, "cannot read preheader" );
1395 /* preheader format :
1396 * u32 i_sequence_number
1398 * u8 i_udp_sequence/i_tcp_flags
1401 if( preheader[4] == p_access->i_header_packet_id_type ||
1402 preheader[4] == p_access->i_media_packet_id_type ||
1403 preheader[4] == 0xff )// udp timing pair
1405 int i_packet_seq_num;
1406 int i_packet_length;
1411 i_packet_seq_num = GetDWLE( preheader );
1412 i_packet_length = GetWLE( preheader + 6 );
1413 i_packet_id = preheader[4];
1415 /* *** read complete packet *** */
1416 if( i_packet_length <= 8 )
1419 "empty or broken packet" );
1422 p_packet = malloc( i_packet_length - 8 );
1423 if( mms_ReadData( p_input,
1425 i_packet_length - 8 ) < 0 )
1428 "cannot read data" );
1432 if( i_packet_id == 0xff )
1435 "receive MMS UDP pair timing" );
1437 return( MMS_PACKET_UDP_TIMING );
1441 if( i_packet_seq_num != p_access->i_packet_seq_num )
1443 // FIXME for udp could be just wrong order ?
1445 "detected packet lost (%d != %d)",
1447 p_access->i_packet_seq_num );
1448 p_access->i_packet_seq_num = i_packet_seq_num;
1450 p_access->i_packet_seq_num++;
1452 if( i_packet_id == p_access->i_header_packet_id_type )
1454 FREE( p_access->p_header );
1455 p_access->p_header = p_packet;
1456 p_access->i_header = i_packet_length - 8;
1457 return( MMS_PACKET_HEADER );
1461 FREE( p_access->p_media );
1462 p_access->p_media = p_packet;
1463 p_access->i_media = i_packet_length - 8;
1464 p_access->i_media_used = 0;
1465 return( MMS_PACKET_MEDIA );
1471 int i_packet_length;
1473 if( GetDWLE( preheader + 4 ) != 0xb00bface )
1476 "incorrect command header (0x%x)",
1477 GetDWLE( preheader + 4 ) );
1479 memcpy( p_access->p_cmd, preheader, 8 );
1480 if( mms_ReadData( p_input,
1481 p_access->p_cmd + 8,
1485 "cannot read data" );
1487 p_access->i_cmd = 16;
1488 i_packet_length = GetDWLE( p_access->p_cmd + 8 );
1489 if( mms_ReadData( p_input,
1490 p_access->p_cmd + 16,
1491 i_packet_length ) < 0 )
1494 "cannot read data" );
1496 p_access->i_cmd += i_packet_length;
1497 p_access->i_command = GetDWLE( p_access->p_cmd + 36 ) & 0xffff;
1498 if( p_access->i_command == 0x1b )
1500 mms_CommandSend( p_input, 0x1b, 0, 0, NULL, 0 );
1504 return( MMS_PACKET_CMD );
1513 static int mms_HeaderMediaRead( input_thread_t *p_input, int i_type )
1515 access_t *p_access = (access_t*)p_input->p_access_data;
1518 for( i_count = 0; i_count < MMS_RETRY_MAX; )
1522 i_status = mms_ReceivePacket( p_input );
1527 "cannot receive header (%d/%d)", i_count, MMS_RETRY_MAX );
1528 msleep( MMS_RETRY_SLEEP );
1530 else if( i_status == i_type )
1534 else if( i_status == MMS_PACKET_CMD )
1536 switch( p_access->i_command )
1539 msg_Warn( p_input, "socket closed by server" );
1542 msg_Warn( p_input, "end of media stream" );
1545 /* XXX not too dificult to be done EXCEPT that we
1546 * need to restart demuxer... and I don't see how we
1547 * could do that :p */
1549 "reinitialization needed --> unsupported" );
1557 "cannot receive %s (abording)",
1558 ( i_type == MMS_PACKET_HEADER ) ? "header" : "media data" );