1 /*****************************************************************************
2 * mms.c: MMS access plug-in
3 *****************************************************************************
4 * Copyright (C) 2001, 2002 VideoLAN
5 * $Id: mms.c,v 1.25 2003/03/15 03:02: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 *****************************************************************************/
37 #include <sys/types.h>
44 #include <vlc/input.h>
51 # include <winsock2.h>
52 # include <ws2tcpip.h>
54 # define IN_MULTICAST(a) IN_CLASSD(a)
57 # include <sys/socket.h>
58 # include <netinet/in.h>
60 # include <arpa/inet.h>
61 # elif defined( SYS_BEOS )
62 # include <net/netdb.h>
73 /****************************************************************************
75 * MMSProtocole documentation found at http://get.to/sdp
76 ****************************************************************************/
78 /*****************************************************************************
80 *****************************************************************************/
81 static int Open ( vlc_object_t * );
82 static void Close ( vlc_object_t * );
84 static int Read ( input_thread_t * p_input, byte_t * p_buffer,
86 static void Seek ( input_thread_t *, off_t );
87 static int SetProgram ( input_thread_t *, pgrm_descriptor_t * );
90 static int MMSOpen( input_thread_t *, url_t *, int, char * );
92 static int MMSStart ( input_thread_t *, uint32_t );
93 static int MMSStop ( input_thread_t *p_input );
95 static int MMSClose ( input_thread_t * );
98 static int mms_CommandRead( input_thread_t *p_input, int i_command1, int i_command2 );
99 static int mms_CommandSend( input_thread_t *, int, uint32_t, uint32_t, uint8_t *, int );
101 static int mms_HeaderMediaRead( input_thread_t *, int );
103 static int mms_ReceivePacket( input_thread_t * );
105 static void mms_ParseURL( url_t *p_url, char *psz_url );
110 * XXX DON'T FREE MY MEMORY !!! XXX
115 * Ok, ok, j'le ferai plus...
121 * Vous pourriez signer vos commentaires (même si on voit bien qui peut
122 * écrire ce genre de trucs :p), et écrire en anglais, bordel de
126 /*****************************************************************************
128 *****************************************************************************/
129 #define CACHING_TEXT N_("caching value in ms")
130 #define CACHING_LONGTEXT N_( \
131 "Allows you to modify the default caching value for mms streams. This " \
132 "value should be set in miliseconds units." )
135 set_description( _("MMS access module") );
136 set_capability( "access", 0 );
137 add_category_hint( "stream", NULL, VLC_TRUE );
138 add_integer( "mms-caching", 4 * DEFAULT_PTS_DELAY / 1000, NULL,
139 CACHING_TEXT, CACHING_LONGTEXT, VLC_TRUE );
141 add_bool( "mms-all", 0, NULL,
142 "force selection of all streams",
143 "force selection of all streams", VLC_TRUE );
145 add_string( "mms-stream", NULL, NULL,
147 "force this stream selection", VLC_TRUE );
148 add_integer( "mms-maxbitrate", 0, NULL,
150 "set max bitrate for auto streams selections", VLC_FALSE );
151 add_shortcut( "mms" );
152 add_shortcut( "mmsu" );
153 add_shortcut( "mmst" );
154 set_callbacks( Open, Close );
157 #define BUF_SIZE 200000
159 static int Open( vlc_object_t *p_this )
166 input_thread_t *p_input = (input_thread_t*)p_this;
168 /* *** allocate p_access_data *** */
169 p_input->p_access_data =
170 (void*)p_access = malloc( sizeof( access_t ) );
171 memset( p_access, 0, sizeof( access_t ) );
174 /* *** Parse URL and get server addr/port and path *** */
175 mms_ParseURL( &p_access->url, p_input->psz_name );
176 if( p_access->url.psz_server_addr == NULL ||
177 !( *p_access->url.psz_server_addr ) )
179 FREE( p_access->url.psz_private );
180 msg_Err( p_input, "invalid server name" );
183 if( p_access->url.i_server_port == 0 )
185 p_access->url.i_server_port = 1755; /* default port */
187 if( p_access->url.i_bind_port == 0 )
189 p_access->url.i_bind_port = 7000; /* default port */
193 /* *** connect to this server *** */
194 /* 1: look at requested protocol (udp/tcp) */
195 i_proto = MMS_PROTO_AUTO;
196 if( *p_input->psz_access )
198 if( !strncmp( p_input->psz_access, "mmsu", 4 ) )
200 i_proto = MMS_PROTO_UDP;
202 else if( !strncmp( p_input->psz_access, "mmst", 4 ) )
204 i_proto = MMS_PROTO_TCP;
207 /* 2: look at ip version ipv4/ipv6 */
209 if( config_GetInt( p_input, "ipv4" ) )
211 psz_network = "ipv4";
213 else if( config_GetInt( p_input, "ipv6" ) )
215 psz_network = "ipv6";
218 if( i_proto == MMS_PROTO_AUTO )
219 { /* first try with TCP */
221 MMSOpen( p_input, &p_access->url, MMS_PROTO_TCP, psz_network );
223 { /* then with UDP */
225 MMSOpen( p_input, &p_access->url, MMS_PROTO_UDP, psz_network );
232 MMSOpen( p_input, &p_access->url, i_proto, psz_network );
237 msg_Err( p_input, "cannot connect to server" );
238 FREE( p_access->url.psz_private );
241 msg_Dbg( p_input, "connected to %s", p_access->url.psz_server_addr );
244 /* *** set exported functions *** */
245 p_input->pf_read = Read;
246 p_input->pf_seek = Seek;
247 p_input->pf_set_program = SetProgram;
248 p_input->pf_set_area = NULL;
250 p_input->p_private = NULL;
252 /* *** finished to set some variable *** */
253 vlc_mutex_lock( &p_input->stream.stream_lock );
254 p_input->stream.b_pace_control = 0;
255 if( p_access->i_proto == MMS_PROTO_UDP )
257 p_input->stream.b_connected = 0;
261 p_input->stream.b_connected = 1;
263 p_input->stream.p_selected_area->i_tell = 0;
267 * broadcast yy=0x02, xx= 0x00
268 * pre-recorded yy=0x01, xx= 0x80 if video, 0x00 no video
270 if( p_access->i_packet_count <= 0 || ( p_access->i_flags_broadcast >> 24 ) == 0x02 )
272 p_input->stream.b_seekable = 0;
273 p_input->stream.p_selected_area->i_size = 0;
277 p_input->stream.b_seekable = 1;
278 p_input->stream.p_selected_area->i_size =
280 p_access->i_packet_count * p_access->i_packet_length;
283 p_input->stream.i_method = INPUT_METHOD_NETWORK;
284 vlc_mutex_unlock( &p_input->stream.stream_lock );
286 /* *** Start stream *** */
287 if( MMSStart( p_input, 0xffffffff ) < 0 )
289 msg_Err( p_input, "cannot start stream" );
291 FREE( p_access->url.psz_private );
295 /* Update default_pts to a suitable value for mms access */
296 p_input->i_pts_delay = config_GetInt( p_input, "mms-caching" ) * 1000;
301 /*****************************************************************************
302 * Close: free unused data structures
303 *****************************************************************************/
304 static void Close( vlc_object_t *p_this )
306 input_thread_t * p_input = (input_thread_t *)p_this;
307 access_t *p_access = (access_t*)p_input->p_access_data;
309 /* close connection with server */
313 FREE( p_access->url.psz_private );
315 FREE( p_input->p_access_data );
318 /*****************************************************************************
319 * SetProgram: do nothing
320 *****************************************************************************/
321 static int SetProgram( input_thread_t * p_input,
322 pgrm_descriptor_t * p_program )
327 /*****************************************************************************
328 * Seek: try to go at the right place
329 *****************************************************************************/
330 static void Seek( input_thread_t * p_input, off_t i_pos )
335 * Probably some bad or missing command
341 access_t *p_access = (access_t*)p_input->p_access_data;
351 vlc_mutex_lock( &p_input->stream.stream_lock );
353 if( i_pos < p_access->i_header)
356 if( p_access->i_pos < p_access->i_header )
358 /* no need to restart stream, it was already one
359 * or no stream was yet read */
360 p_access->i_pos = i_pos;
365 i_packet = 0xffffffff;
371 i_packet = ( i_pos - p_access->i_header ) / p_access->i_packet_length;
372 i_offset = ( i_pos - p_access->i_header ) % p_access->i_packet_length;
374 msg_Dbg( p_input, "seeking to "I64Fd " (packet:%d)", i_pos, i_packet );
377 msg_Dbg( p_input, "stream stopped (seek)" );
379 /* *** restart stream *** */
380 var_buffer_initwrite( &buffer, 0 );
381 var_buffer_add64( &buffer, 0 ); /* seek point in second */
382 var_buffer_add32( &buffer, 0xffffffff );
383 var_buffer_add32( &buffer, i_packet ); // begin from start
384 var_buffer_add8( &buffer, 0xff ); // stream time limit
385 var_buffer_add8( &buffer, 0xff ); // on 3bytes ...
386 var_buffer_add8( &buffer, 0xff ); //
387 var_buffer_add8( &buffer, 0x00 ); // don't use limit
388 var_buffer_add32( &buffer, p_access->i_media_packet_id_type );
390 mms_CommandSend( p_input, 0x07, p_access->i_command_level, 0x0001ffff,
391 buffer.p_data, buffer.i_data );
393 var_buffer_free( &buffer );
398 mms_HeaderMediaRead( p_input, MMS_PACKET_CMD );
399 if( p_access->i_command == 0x1e )
401 msg_Dbg( p_input, "received 0x1e (seek)" );
408 mms_HeaderMediaRead( p_input, MMS_PACKET_CMD );
409 if( p_access->i_command == 0x05 )
411 msg_Dbg( p_input, "received 0x05 (seek)" );
417 mms_HeaderMediaRead( p_input, MMS_PACKET_MEDIA );
418 msg_Dbg( p_input, "Streaming restarted" );
420 p_access->i_media_used += i_offset;
421 p_access->i_pos = i_pos;
422 p_input->stream.p_selected_area->i_tell = i_pos;
423 vlc_mutex_unlock( &p_input->stream.stream_lock );
428 static int Read ( input_thread_t * p_input, byte_t * p_buffer,
431 access_t *p_access = (access_t*)p_input->p_access_data;
437 /* *** send header if needed ** */
438 if( p_access->i_pos < p_access->i_header )
440 i_copy = __MIN( i_len, p_access->i_header - p_access->i_pos );
444 p_access->p_header + p_access->i_pos,
450 /* *** now send data if needed *** */
451 while( i_data < i_len )
453 if( p_access->i_media_used < p_access->i_media )
455 i_copy = __MIN( i_len - i_data ,
456 p_access->i_media - p_access->i_media_used );
457 memcpy( p_buffer + i_data,
458 p_access->p_media + p_access->i_media_used,
461 p_access->i_media_used += i_copy;
463 else if( p_access->p_media != NULL &&
464 p_access->i_media_used < p_access->i_packet_length )
466 i_copy = __MIN( i_len - i_data,
467 p_access->i_packet_length - p_access->i_media_used);
468 memset( p_buffer + i_data, 0, i_copy );
471 p_access->i_media_used += i_copy;
476 || mms_HeaderMediaRead( p_input, MMS_PACKET_MEDIA ) < 0 )
478 p_access->i_pos += i_data;
484 p_access->i_pos += i_data;
488 static void asf_HeaderParse( mms_stream_t stream[128],
489 uint8_t *p_header, int i_header )
496 for( i = 0; i < 128; i++ )
498 stream[i].i_cat = MMS_STREAM_UNKNOWN;
501 //fprintf( stderr, " ---------------------header:%d\n", i_header );
502 var_buffer_initread( &buffer, p_header, i_header );
504 var_buffer_getguid( &buffer, &guid );
506 if( !CmpGuid( &guid, &asf_object_header_guid ) )
509 // fprintf( stderr, " ---------------------ERROR------\n" );
511 var_buffer_getmemory( &buffer, NULL, 30 - 16 );
515 //fprintf( stderr, " ---------------------data:%d\n", buffer.i_data );
517 var_buffer_getguid( &buffer, &guid );
518 i_size = var_buffer_get64( &buffer );
520 //fprintf( stderr, " guid=0x%8.8x-0x%4.4x-0x%4.4x-%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x size=%lld\n",
521 // guid.v1,guid.v2, guid.v3,
522 // guid.v4[0],guid.v4[1],guid.v4[2],guid.v4[3],
523 // guid.v4[4],guid.v4[5],guid.v4[6],guid.v4[7],
526 if( CmpGuid( &guid, &asf_object_stream_properties_guid ) )
530 //msg_Dbg( p_input, "found stream_properties" );
532 var_buffer_getguid( &buffer, &stream_type );
533 var_buffer_getmemory( &buffer, NULL, 32 );
534 i_stream_id = var_buffer_get8( &buffer ) & 0x7f;
536 //fprintf( stderr, " 1---------------------skip:%lld\n", i_size - 24 - 32 - 16 - 1 );
537 var_buffer_getmemory( &buffer, NULL, i_size - 24 - 32 - 16 - 1);
539 if( CmpGuid( &stream_type, &asf_object_stream_type_video ) )
541 //fprintf( stderr, "\nvideo stream[%d] found\n", i_stream_id );
542 //msg_Dbg( p_input, "video stream[%d] found", i_stream_id );
543 stream[i_stream_id].i_cat = MMS_STREAM_VIDEO;
545 else if( CmpGuid( &stream_type, &asf_object_stream_type_audio ) )
547 //fprintf( stderr, "\naudio stream[%d] found\n", i_stream_id );
548 //msg_Dbg( p_input, "audio stream[%d] found", i_stream_id );
549 stream[i_stream_id].i_cat = MMS_STREAM_AUDIO;
553 // msg_Dbg( p_input, "unknown stream[%d] found", i_stream_id );
554 stream[i_stream_id].i_cat = MMS_STREAM_UNKNOWN;
557 else if ( CmpGuid( &guid, &asf_object_bitrate_properties_guid ) )
562 i_count = var_buffer_get16( &buffer );
566 i_stream_id = var_buffer_get16( &buffer )&0x7f;
567 stream[i_stream_id].i_bitrate = var_buffer_get32( &buffer );
571 //fprintf( stderr, " 2---------------------skip:%lld\n", i_size - 24);
572 var_buffer_getmemory( &buffer, NULL, i_size - 24 );
576 //fprintf( stderr, " 3---------------------skip:%lld\n", i_size - 24);
578 var_buffer_getmemory( &buffer, NULL, i_size - 24 );
581 if( var_buffer_readempty( &buffer ) )
588 static void mms_StreamSelect( input_thread_t * p_input,
589 mms_stream_t stream[128] )
591 /* XXX FIXME use mututal eclusion information */
593 int i_audio, i_video;
594 int b_audio, b_video;
602 i_bitrate_max = config_GetInt( p_input, "mms-maxbitrate" );
603 b_audio = config_GetInt( p_input, "audio" );
604 b_video = config_GetInt( p_input, "video" );
605 if( config_GetInt( p_input, "mms-all" ) )
607 /* select all valid stream */
608 for( i = 1; i < 128; i++ )
610 if( stream[i].i_cat != MMS_STREAM_UNKNOWN )
612 stream[i].i_selected = 1;
619 for( i = 0; i < 128; i++ )
621 stream[i].i_selected = 0; /* by default, not selected */
624 psz_stream = config_GetPsz( p_input, "mms-stream" );
626 if( psz_stream && *psz_stream )
628 char *psz_tmp = psz_stream;
631 if( *psz_tmp == ',' )
638 i_stream = atoi( psz_tmp );
639 while( *psz_tmp != '\0' && *psz_tmp != ',' )
644 if( i_stream > 0 && i_stream < 128 &&
645 stream[i_stream].i_cat != MMS_STREAM_UNKNOWN )
647 stream[i_stream].i_selected = 1;
658 * - no audio nor video stream
660 * - if i_bitrate_max not set keep the highest bitrate
661 * - if i_bitrate_max is set, keep stream that make we used best
662 * quality regarding i_bitrate_max
665 * - it doesn't use mutual exclusion info..
666 * - when selecting a better stream we could select
667 * something that make i_bitrate_total> i_bitrate_max
669 for( i = 1; i < 128; i++ )
671 if( stream[i].i_cat == MMS_STREAM_UNKNOWN )
675 else if( stream[i].i_cat == MMS_STREAM_AUDIO && b_audio &&
677 ( ( ( stream[i].i_bitrate > stream[i_audio].i_bitrate &&
678 ( i_bitrate_total + stream[i].i_bitrate - stream[i_audio].i_bitrate
679 < i_bitrate_max || !i_bitrate_max) ) ||
680 ( stream[i].i_bitrate < stream[i_audio].i_bitrate &&
681 i_bitrate_max != 0 && i_bitrate_total > i_bitrate_max )
684 /* unselect old stream */
687 stream[i_audio].i_selected = 0;
688 if( stream[i_audio].i_bitrate> 0 )
690 i_bitrate_total -= stream[i_audio].i_bitrate;
694 stream[i].i_selected = 1;
695 if( stream[i].i_bitrate> 0 )
697 i_bitrate_total += stream[i].i_bitrate;
701 else if( stream[i].i_cat == MMS_STREAM_VIDEO && b_video &&
704 ( ( stream[i].i_bitrate > stream[i_video].i_bitrate &&
705 ( i_bitrate_total + stream[i].i_bitrate - stream[i_video].i_bitrate
706 < i_bitrate_max || !i_bitrate_max) ) ||
707 ( stream[i].i_bitrate < stream[i_video].i_bitrate &&
708 i_bitrate_max != 0 && i_bitrate_total > i_bitrate_max )
711 /* unselect old stream */
713 stream[i_video].i_selected = 0;
714 if( stream[i_video].i_bitrate> 0 )
716 i_bitrate_total -= stream[i_video].i_bitrate;
719 stream[i].i_selected = 1;
720 if( stream[i].i_bitrate> 0 )
722 i_bitrate_total += stream[i].i_bitrate;
728 if( i_bitrate_max > 0 )
731 "requested bitrate:%d real bitrate:%d",
732 i_bitrate_max, i_bitrate_total );
742 /****************************************************************************
743 * MMSOpen : Open a connection with the server over mmst or mmsu(not yet)
744 ****************************************************************************/
745 static int MMSOpen( input_thread_t *p_input,
748 char *psz_network ) /* "", "ipv4", "ipv6" */
751 access_t *p_access = (access_t*)p_input->p_access_data;
753 network_socket_t socket_desc;
754 int b_udp = ( i_proto == MMS_PROTO_UDP ) ? 1 : 0;
759 int i_server_version;
761 int i_update_player_url;
762 int i_encryption_type;
768 /* *** Open a TCP connection with server *** */
769 msg_Dbg( p_input, "waiting for connection..." );
770 socket_desc.i_type = NETWORK_TCP;
771 socket_desc.psz_server_addr = p_url->psz_server_addr;
772 socket_desc.i_server_port = p_url->i_server_port;
773 socket_desc.psz_bind_addr = "";
774 socket_desc.i_bind_port = 0;
775 p_input->p_private = (void*)&socket_desc;
776 if( !( p_network = module_Need( p_input, "network", psz_network ) ) )
778 msg_Err( p_input, "failed to open a connection (tcp)" );
781 module_Unneed( p_input, p_network );
782 p_access->socket_tcp.i_handle = socket_desc.i_handle;
783 p_input->i_mtu = 0; /*socket_desc.i_mtu;*/
785 "connection(tcp) with \"%s:%d\" successful",
786 p_url->psz_server_addr,
787 p_url->i_server_port );
789 /* *** Bind port if UDP protocol is selected *** */
792 if( !p_url->psz_bind_addr || !*p_url->psz_bind_addr )
794 struct sockaddr_in name;
795 socklen_t i_namelen = sizeof( struct sockaddr_in );
797 if( getsockname( p_access->socket_tcp.i_handle,
798 (struct sockaddr*)&name, &i_namelen ) < 0 )
801 msg_Err( p_input, "for udp you have to provide bind address (mms://<server_addr>@<bind_addr/<path> (FIXME)" );
802 #if defined( UNDER_CE )
803 CloseHandle( (HANDLE)p_access->socket_tcp.i_handle );
804 #elif defined( WIN32 )
805 closesocket( p_access->socket_tcp.i_handle );
807 close( p_access->socket_tcp.i_handle );
811 p_access->psz_bind_addr = inet_ntoa( name.sin_addr );
815 p_access->psz_bind_addr = strdup( p_url->psz_bind_addr );
817 socket_desc.i_type = NETWORK_UDP;
818 socket_desc.psz_server_addr = "";
819 socket_desc.i_server_port = 0;
820 socket_desc.psz_bind_addr = p_access->psz_bind_addr;
821 socket_desc.i_bind_port = p_url->i_bind_port;
822 p_input->p_private = (void*)&socket_desc;
823 if( !( p_network = module_Need( p_input, "network", psz_network ) ) )
825 msg_Err( p_input, "failed to open a connection (udp)" );
826 #if defined( UNDER_CE )
827 CloseHandle( (HANDLE)p_access->socket_tcp.i_handle );
828 #elif defined( WIN32 )
829 closesocket( p_access->socket_tcp.i_handle );
831 close( p_access->socket_tcp.i_handle );
835 module_Unneed( p_input, p_network );
836 p_access->socket_udp.i_handle = socket_desc.i_handle;
837 p_input->i_mtu = 0;/*socket_desc.i_mtu; FIXME */
842 p_access->psz_bind_addr = NULL;
845 /* *** Init context for mms prototcol *** */
846 GenerateGuid( &p_access->guid ); /* used to identify client by server */
848 "generated guid: "GUID_FMT,
849 GUID_PRINT( p_access->guid ) );
850 p_access->i_command_level = 1; /* updated after 0x1A command */
851 p_access->i_seq_num = 0;
852 p_access->i_media_packet_id_type = 0x04;
853 p_access->i_header_packet_id_type = 0x02;
854 p_access->i_proto = i_proto;
855 p_access->i_packet_seq_num = 0;
856 p_access->p_header = NULL;
857 p_access->i_header = 0;
858 p_access->p_media = NULL;
859 p_access->i_media = 0;
860 p_access->i_media_used = 0;
863 p_access->i_buffer_tcp = 0;
864 p_access->i_buffer_udp = 0;
865 p_access->p_cmd = NULL;
869 /* *** send command 1 : connection request *** */
870 var_buffer_initwrite( &buffer, 0 );
871 var_buffer_add16( &buffer, 0x001c );
872 var_buffer_add16( &buffer, 0x0003 );
874 "NSPlayer/7.0.0.1956; {"GUID_FMT"}; Host: %s",
875 GUID_PRINT( p_access->guid ),
876 p_url->psz_server_addr );
877 var_buffer_addUTF16( &buffer, tmp );
879 mms_CommandSend( p_input,
880 0x01, /* connexion request */
881 0x00000000, /* flags, FIXME */
882 0x0004000b, /* ???? */
886 if( mms_CommandRead( p_input, 0x01, 0 ) < 0 )
888 var_buffer_free( &buffer );
893 i_server_version = GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 32 );
894 i_tool_version = GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 36 );
895 i_update_player_url = GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 40 );
896 i_encryption_type = GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 44 );
897 p = (uint16_t*)( p_access->p_cmd + MMS_CMD_HEADERSIZE + 48 );
898 #define GETUTF16( psz, size ) \
901 psz = malloc( size + 1); \
902 for( i = 0; i < size; i++ ) \
909 GETUTF16( p_access->psz_server_version, i_server_version );
910 GETUTF16( p_access->psz_tool_version, i_tool_version );
911 GETUTF16( p_access->psz_update_player_url, i_update_player_url );
912 GETUTF16( p_access->psz_encryption_type, i_encryption_type );
915 "0x01 --> server_version:\"%s\" tool_version:\"%s\" update_player_url:\"%s\" encryption_type:\"%s\"",
916 p_access->psz_server_version,
917 p_access->psz_tool_version,
918 p_access->psz_update_player_url,
919 p_access->psz_encryption_type );
921 /* *** should make an 18 command to make data timing *** */
923 /* *** send command 2 : transport protocol selection *** */
924 var_buffer_reinitwrite( &buffer, 0 );
925 var_buffer_add32( &buffer, 0x00000000 );
926 var_buffer_add32( &buffer, 0x000a0000 );
927 var_buffer_add32( &buffer, 0x00000002 );
932 p_access->psz_bind_addr,
933 p_url->i_bind_port );
937 sprintf( tmp, "\\\\127.0.0.1\\TCP\\1242" );
939 var_buffer_addUTF16( &buffer, tmp );
940 var_buffer_add16( &buffer, '0' );
942 mms_CommandSend( p_input,
943 0x02, /* connexion request */
944 0x00000000, /* flags, FIXME */
945 0xffffffff, /* ???? */
949 /* *** response from server, should be 0x02 or 0x03 *** */
950 mms_CommandRead( p_input, 0x02, 0x03 );
951 if( p_access->i_command == 0x03 )
954 "%s protocol selection failed", b_udp ? "UDP" : "TCP" );
955 var_buffer_free( &buffer );
959 else if( p_access->i_command != 0x02 )
961 msg_Warn( p_input, "received command isn't 0x02 in reponse to 0x02" );
964 /* *** send command 5 : media file name/path requested *** */
965 var_buffer_reinitwrite( &buffer, 0 );
966 var_buffer_add64( &buffer, 0 );
967 var_buffer_addUTF16( &buffer, p_url->psz_path );
969 mms_CommandSend( p_input,
971 p_access->i_command_level,
976 /* *** wait for reponse *** */
977 mms_CommandRead( p_input, 0x1a, 0x06 );
979 /* test if server send 0x1A answer */
980 if( p_access->i_command == 0x1A )
982 msg_Err( p_input, "id/password requested (not yet supported)" );
984 var_buffer_free( &buffer );
988 if( p_access->i_command != 0x06 )
991 "unknown answer (0x%x instead of 0x06)",
992 p_access->i_command );
993 var_buffer_free( &buffer );
998 /* 1 for file ok, 2 for authen ok */
999 switch( GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE ) )
1002 msg_Dbg( p_input, "Media file name/path accepted" );
1005 msg_Dbg( p_input, "Authentication accepted" );
1009 msg_Err( p_input, "error while asking for file %d",
1010 GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE ) );
1011 var_buffer_free( &buffer );
1012 MMSClose( p_input );
1016 p_access->i_flags_broadcast =
1017 GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 12 );
1018 p_access->i_media_length =
1019 GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 24 );
1020 p_access->i_packet_length =
1021 GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 44 );
1022 p_access->i_packet_count =
1023 GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 48 );
1024 p_access->i_max_bit_rate =
1025 GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 56 );
1026 p_access->i_header_size =
1027 GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 60 );
1030 "answer 0x06 flags:0x%8.8x media_length:%ds packet_length:%d packet_count:%d max_bit_rate:%d header_size:%d",
1031 p_access->i_flags_broadcast,
1032 p_access->i_media_length,
1033 p_access->i_packet_length,
1034 p_access->i_packet_count,
1035 p_access->i_max_bit_rate,
1036 p_access->i_header_size );
1038 /* XXX XXX dirty hack XXX XXX */
1039 p_input->i_mtu = 3 * p_access->i_packet_length;
1041 /* *** send command 15 *** */
1043 var_buffer_reinitwrite( &buffer, 0 );
1044 var_buffer_add32( &buffer, 0 );
1045 var_buffer_add32( &buffer, 0x8000 );
1046 var_buffer_add32( &buffer, 0xffffffff );
1047 var_buffer_add32( &buffer, 0x00 );
1048 var_buffer_add32( &buffer, 0x00 );
1049 var_buffer_add32( &buffer, 0x00 );
1050 var_buffer_add64( &buffer, 0x40ac200000000000 );
1051 var_buffer_add32( &buffer, p_access->i_header_packet_id_type );
1052 mms_CommandSend( p_input, 0x15, p_access->i_command_level, 0x00,
1053 buffer.p_data, buffer.i_data );
1055 /* *** wait for reponse *** */
1056 mms_CommandRead( p_input, 0x11, 0 );
1058 if( p_access->i_command != 0x11 )
1061 "unknown answer (0x%x instead of 0x11)",
1062 p_access->i_command );
1063 var_buffer_free( &buffer );
1064 MMSClose( p_input );
1067 /* *** now read header packet *** */
1068 /* XXX could be split over multiples packets */
1071 if( mms_HeaderMediaRead( p_input, MMS_PACKET_HEADER ) < 0 )
1073 msg_Err( p_input, "cannot receive header" );
1074 var_buffer_free( &buffer );
1075 MMSClose( p_input );
1078 if( p_access->i_header >= p_access->i_header_size )
1081 "header complete(%d)",
1082 p_access->i_header );
1086 "header incomplete (%d/%d), reading more",
1088 p_access->i_header_size );
1091 /* *** parse header and get stream and their id *** */
1092 /* get all streams properties,
1094 * TODO : stream bitrates properties(optional)
1095 * and bitrate mutual exclusion(optional) */
1096 asf_HeaderParse( p_access->stream,
1097 p_access->p_header, p_access->i_header );
1098 mms_StreamSelect( p_input, p_access->stream );
1099 /* *** now select stream we want to receive *** */
1100 /* TODO take care of stream bitrate TODO */
1103 var_buffer_reinitwrite( &buffer, 0 );
1104 /* for now, select first audio and video stream */
1105 for( i = 1; i < 128; i++ )
1108 if( p_access->stream[i].i_cat != MMS_STREAM_UNKNOWN )
1113 var_buffer_add16( &buffer, 0xffff );
1114 var_buffer_add16( &buffer, i );
1120 if( p_access->stream[i].i_selected )
1122 var_buffer_add16( &buffer, 0x0000 );
1124 "selecting stream[0x%x] %s (%d kb/s)",
1126 ( p_access->stream[i].i_cat == MMS_STREAM_AUDIO ) ?
1128 p_access->stream[i].i_bitrate / 1024);
1132 var_buffer_add16( &buffer, 0x0002 );
1134 "ignoring stream[0x%x] %s (%d kb/s)",
1136 ( p_access->stream[i].i_cat == MMS_STREAM_AUDIO ) ?
1138 p_access->stream[i].i_bitrate / 1024);
1144 if( i_streams == 0 )
1146 msg_Err( p_input, "cannot find any stream" );
1147 var_buffer_free( &buffer );
1148 MMSClose( p_input );
1151 mms_CommandSend( p_input, 0x33,
1153 0xffff | ( i_first << 16 ),
1154 buffer.p_data, buffer.i_data );
1156 mms_CommandRead( p_input, 0x21, 0 );
1157 if( p_access->i_command != 0x21 )
1160 "unknown answer (0x%x instead of 0x21)",
1161 p_access->i_command );
1162 var_buffer_free( &buffer );
1163 MMSClose( p_input );
1168 var_buffer_free( &buffer );
1170 msg_Info( p_input, "connection sucessful" );
1175 /****************************************************************************
1176 * MMSStart : Start streaming
1177 ****************************************************************************/
1178 static int MMSStart ( input_thread_t *p_input, uint32_t i_packet )
1180 access_t *p_access = (access_t*)p_input->p_access_data;
1181 var_buffer_t buffer;
1183 /* *** start stream from packet 0 *** */
1184 var_buffer_initwrite( &buffer, 0 );
1185 var_buffer_add64( &buffer, 0 ); /* seek point in second */
1186 var_buffer_add32( &buffer, 0xffffffff );
1187 var_buffer_add32( &buffer, i_packet ); // begin from start
1188 var_buffer_add8( &buffer, 0xff ); // stream time limit
1189 var_buffer_add8( &buffer, 0xff ); // on 3bytes ...
1190 var_buffer_add8( &buffer, 0xff ); //
1191 var_buffer_add8( &buffer, 0x00 ); // don't use limit
1192 var_buffer_add32( &buffer, p_access->i_media_packet_id_type );
1194 mms_CommandSend( p_input, 0x07, p_access->i_command_level, 0x0001ffff,
1195 buffer.p_data, buffer.i_data );
1197 var_buffer_free( &buffer );
1199 mms_CommandRead( p_input, 0x05, 0 );
1201 if( p_access->i_command != 0x05 )
1204 "unknown answer (0x%x instead of 0x05)",
1205 p_access->i_command );
1211 mms_HeaderMediaRead( p_input, MMS_PACKET_MEDIA );
1212 msg_Dbg( p_input, "Streaming started" );
1217 /****************************************************************************
1218 * MMSStop : Stop streaming
1219 ****************************************************************************/
1220 static int MMSStop ( input_thread_t *p_input )
1222 access_t *p_access = (access_t*)p_input->p_access_data;
1224 /* *** stop stream but keep connection alive *** */
1225 mms_CommandSend( p_input,
1227 p_access->i_command_level,
1233 /****************************************************************************
1234 * MMSClose : Close streaming and connection
1235 ****************************************************************************/
1236 static int MMSClose ( input_thread_t *p_input )
1238 access_t *p_access = (access_t*)p_input->p_access_data;
1240 msg_Dbg( p_input, "Connection closed" );
1242 /* *** tell server that we will disconnect *** */
1243 mms_CommandSend( p_input,
1245 p_access->i_command_level,
1248 /* *** close sockets *** */
1249 #if defined( UNDER_CE )
1250 CloseHandle( (HANDLE)p_access->socket_tcp.i_handle );
1251 #elif defined( WIN32 )
1252 closesocket( p_access->socket_tcp.i_handle );
1254 close( p_access->socket_tcp.i_handle );
1257 if( p_access->i_proto == MMS_PROTO_UDP )
1259 #if defined( UNDER_CE )
1260 CloseHandle( (HANDLE)p_access->socket_udp.i_handle );
1261 #elif defined( WIN32 )
1262 closesocket( p_access->socket_udp.i_handle );
1264 close( p_access->socket_udp.i_handle );
1268 FREE( p_access->p_cmd );
1269 FREE( p_access->p_media );
1270 FREE( p_access->p_header );
1272 FREE( p_access->psz_server_version );
1273 FREE( p_access->psz_tool_version );
1274 FREE( p_access->psz_update_player_url );
1275 FREE( p_access->psz_encryption_type );
1280 /*****************************************************************************
1281 * mms_ParseURL : parse an url string and fill an url_t
1282 *****************************************************************************/
1283 static void mms_ParseURL( url_t *p_url, char *psz_url )
1286 char *psz_server_port;
1288 p_url->psz_private = strdup( psz_url );
1290 psz_parser = p_url->psz_private;
1292 while( *psz_parser == '/' )
1296 p_url->psz_server_addr = psz_parser;
1298 while( *psz_parser &&
1299 *psz_parser != ':' && *psz_parser != '/' && *psz_parser != '@' )
1304 if( *psz_parser == ':' )
1308 psz_server_port = psz_parser;
1310 while( *psz_parser && *psz_parser != '/' )
1317 psz_server_port = "";
1320 if( *psz_parser == '@' )
1322 char *psz_bind_port;
1327 p_url->psz_bind_addr = psz_parser;
1329 while( *psz_parser && *psz_parser != ':' && *psz_parser != '/' )
1334 if( *psz_parser == ':' )
1338 psz_bind_port = psz_parser;
1340 while( *psz_parser && *psz_parser != '/' )
1349 if( *psz_bind_port )
1351 p_url->i_bind_port = strtol( psz_bind_port, &psz_parser, 10 );
1355 p_url->i_bind_port = 0;
1360 p_url->psz_bind_addr = "";
1361 p_url->i_bind_port = 0;
1364 if( *psz_parser == '/' )
1368 p_url->psz_path = psz_parser;
1371 if( *psz_server_port )
1373 p_url->i_server_port = strtol( psz_server_port, &psz_parser, 10 );
1377 p_url->i_server_port = 0;
1381 /****************************************************************************
1383 * MMS specific functions
1385 ****************************************************************************/
1387 static int mms_CommandSend( input_thread_t *p_input,
1389 uint32_t i_prefix1, uint32_t i_prefix2,
1390 uint8_t *p_data, int i_data )
1392 var_buffer_t buffer;
1394 access_t *p_access = (access_t*)p_input->p_access_data;
1397 i_data_by8 = ( i_data + 7 ) / 8;
1399 /* first init uffer */
1400 var_buffer_initwrite( &buffer, 0 );
1402 var_buffer_add32( &buffer, 0x00000001 ); /* start sequence */
1403 var_buffer_add32( &buffer, 0xB00BFACE );
1404 /* size after protocol type */
1405 var_buffer_add32( &buffer, i_data + MMS_CMD_HEADERSIZE - 16 );
1406 var_buffer_add32( &buffer, 0x20534d4d ); /* protocol "MMS " */
1407 var_buffer_add32( &buffer, i_data_by8 + 4 );
1408 var_buffer_add32( &buffer, p_access->i_seq_num ); p_access->i_seq_num++;
1409 var_buffer_add64( &buffer, 0 );
1410 var_buffer_add32( &buffer, i_data_by8 + 2 );
1411 var_buffer_add32( &buffer, 0x00030000 | i_command ); /* dir | command */
1412 var_buffer_add32( &buffer, i_prefix1 ); /* command specific */
1413 var_buffer_add32( &buffer, i_prefix2 ); /* command specific */
1415 /* specific command data */
1416 if( p_data && i_data > 0 )
1418 var_buffer_addmemory( &buffer, p_data, i_data );
1422 if( send( p_access->socket_tcp.i_handle,
1427 msg_Err( p_input, "failed to send command" );
1431 var_buffer_free( &buffer );
1435 static int NetFillBuffer( input_thread_t *p_input )
1440 access_t *p_access = (access_t*)p_input->p_access_data;
1441 struct timeval timeout;
1445 /* FIXME when using udp */
1446 ssize_t i_tcp, i_udp;
1447 ssize_t i_tcp_read, i_udp_read;
1450 /* Initialize file descriptor set */
1453 i_tcp = MMS_BUFFER_SIZE/2 - p_access->i_buffer_tcp;
1455 if( p_access->i_proto == MMS_PROTO_UDP )
1457 i_udp = MMS_BUFFER_SIZE/2 - p_access->i_buffer_udp;
1461 i_udp = 0; /* there isn't udp socket */
1467 FD_SET( p_access->socket_tcp.i_handle, &fds );
1468 i_handle_max = __MAX( i_handle_max, p_access->socket_tcp.i_handle );
1472 FD_SET( p_access->socket_udp.i_handle, &fds );
1473 i_handle_max = __MAX( i_handle_max, p_access->socket_udp.i_handle );
1476 if( i_handle_max == 0 )
1478 msg_Warn( p_input, "nothing to read %d:%d", i_tcp, i_udp );
1483 // msg_Warn( p_input, "ask for tcp:%d udp:%d", i_tcp, i_udp );
1486 /* We'll wait 0.5 second if nothing happens */
1488 timeout.tv_usec = 500000;
1490 /* Find if some data is available */
1491 i_ret = select( i_handle_max + 1,
1493 NULL, NULL, &timeout );
1495 if( i_ret == -1 && errno != EINTR )
1497 msg_Err( p_input, "network select error (%s)", strerror(errno) );
1501 if( i_tcp > 0 && FD_ISSET( p_access->socket_tcp.i_handle, &fds ) )
1504 recv( p_access->socket_tcp.i_handle,
1505 p_access->buffer_tcp + p_access->i_buffer_tcp,
1506 i_tcp + MMS_BUFFER_SIZE/2, 0 );
1513 if( i_udp > 0 && FD_ISSET( p_access->socket_udp.i_handle, &fds ) )
1515 i_udp_read = recv( p_access->socket_udp.i_handle,
1516 p_access->buffer_udp + p_access->i_buffer_udp,
1517 i_udp + MMS_BUFFER_SIZE/2, 0 );
1525 if( p_access->i_proto == MMS_PROTO_UDP )
1528 "filling buffer TCP:%d+%d UDP:%d+%d",
1529 p_access->i_buffer_tcp,
1531 p_access->i_buffer_udp,
1537 "filling buffer TCP:%d+%d",
1538 p_access->i_buffer_tcp,
1542 p_access->i_buffer_tcp += i_tcp_read;
1543 p_access->i_buffer_udp += i_udp_read;
1545 return( i_tcp_read + i_udp_read);
1549 static int mms_ParseCommand( input_thread_t *p_input,
1554 #define GET32( i_pos ) \
1555 ( p_access->p_cmd[i_pos] + ( p_access->p_cmd[i_pos +1] << 8 ) + \
1556 ( p_access->p_cmd[i_pos + 2] << 16 ) + \
1557 ( p_access->p_cmd[i_pos + 3] << 24 ) )
1559 access_t *p_access = (access_t*)p_input->p_access_data;
1563 if( p_access->p_cmd )
1565 free( p_access->p_cmd );
1567 p_access->i_cmd = i_data;
1568 p_access->p_cmd = malloc( i_data );
1569 memcpy( p_access->p_cmd, p_data, i_data );
1571 *pi_used = i_data; /* by default */
1573 if( i_data < MMS_CMD_HEADERSIZE )
1575 msg_Warn( p_input, "truncated command (header incomplete)" );
1576 p_access->i_command = 0;
1579 i_id = GetDWLE( p_data + 4 );
1580 i_length = GetDWLE( p_data + 8 ) + 16;
1582 if( i_id != 0xb00bface )
1585 "incorrect command header (0x%x)", i_id );
1586 p_access->i_command = 0;
1590 if( i_length > p_access->i_cmd )
1593 "truncated command (missing %d bytes)",
1594 i_length - i_data );
1595 p_access->i_command = 0;
1598 else if( i_length < p_access->i_cmd )
1600 p_access->i_cmd = i_length;
1601 *pi_used = i_length;
1605 "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",
1609 /* 12: protocol type "MMS " */
1612 /* 24: unknown (0) */
1613 /* 28: unknown (0) */
1619 p_access->i_command = GET32( 36 ) & 0xffff;
1621 return( MMS_PACKET_CMD );
1624 static int mms_ParsePacket( input_thread_t *p_input,
1625 uint8_t *p_data, size_t i_data,
1628 access_t *p_access = (access_t*)p_input->p_access_data;
1629 int i_packet_seq_num;
1630 size_t i_packet_length;
1631 uint32_t i_packet_id;
1636 // *pi_used = i_data; /* default */
1637 *pi_used = i_data; /* default */
1640 msg_Warn( p_input, "truncated packet (header incomplete)" );
1644 i_packet_id = p_data[4];
1645 i_packet_seq_num = GetDWLE( p_data );
1646 i_packet_length = GetWLE( p_data + 6 );
1648 //msg_Warn( p_input, "------->i_packet_length=%d, i_data=%d", i_packet_length, i_data );
1650 if( i_packet_length > i_data || i_packet_length <= 8)
1653 "truncated packet (missing %d bytes)",
1654 i_packet_length - i_data );
1658 else if( i_packet_length < i_data )
1660 *pi_used = i_packet_length;
1663 if( i_packet_id == 0xff )
1666 "receive MMS UDP pair timing" );
1667 return( MMS_PACKET_UDP_TIMING );
1670 if( i_packet_id != p_access->i_header_packet_id_type &&
1671 i_packet_id != p_access->i_media_packet_id_type )
1673 msg_Warn( p_input, "incorrect Packet Id Type (0x%x)", i_packet_id );
1677 /* we now have a media or a header packet */
1678 p_packet = malloc( i_packet_length - 8 ); // don't bother with preheader
1679 memcpy( p_packet, p_data + 8, i_packet_length - 8 );
1681 if( i_packet_seq_num != p_access->i_packet_seq_num )
1683 /* FIXME for udp could be just wrong order ? */
1685 "detected packet lost (%d != %d)",
1687 p_access->i_packet_seq_num );
1688 p_access->i_packet_seq_num = i_packet_seq_num;
1690 p_access->i_packet_seq_num++;
1692 if( i_packet_id == p_access->i_header_packet_id_type )
1694 if( p_access->p_header )
1696 p_access->p_header = realloc( p_access->p_header,
1697 p_access->i_header + i_packet_length - 8 );
1698 memcpy( &p_access->p_header[p_access->i_header],
1700 i_packet_length - 8 );
1701 p_access->i_header += i_packet_length - 8;
1707 p_access->p_header = p_packet;
1708 p_access->i_header = i_packet_length - 8;
1710 /* msg_Dbg( p_input,
1711 "receive header packet (%d bytes)",
1712 i_packet_length - 8 ); */
1714 return( MMS_PACKET_HEADER );
1718 FREE( p_access->p_media );
1719 p_access->p_media = p_packet;
1720 p_access->i_media = i_packet_length - 8;
1721 p_access->i_media_used = 0;
1722 /* msg_Dbg( p_input,
1723 "receive media packet (%d bytes)",
1724 i_packet_length - 8 ); */
1726 return( MMS_PACKET_MEDIA );
1730 static int mms_ReceivePacket( input_thread_t *p_input )
1732 access_t *p_access = (access_t*)p_input->p_access_data;
1733 int i_packet_tcp_type;
1734 int i_packet_udp_type;
1738 if( NetFillBuffer( p_input ) < 0 )
1740 msg_Warn( p_input, "cannot fill buffer" );
1745 i_packet_tcp_type = -1;
1746 i_packet_udp_type = -1;
1748 if( p_access->i_buffer_tcp > 0 )
1752 if( GetDWLE( p_access->buffer_tcp + 4 ) == 0xb00bface )
1755 mms_ParseCommand( p_input,
1756 p_access->buffer_tcp,
1757 p_access->i_buffer_tcp,
1764 mms_ParsePacket( p_input,
1765 p_access->buffer_tcp,
1766 p_access->i_buffer_tcp,
1769 if( i_used < MMS_BUFFER_SIZE )
1771 memmove( p_access->buffer_tcp,
1772 p_access->buffer_tcp + i_used,
1773 MMS_BUFFER_SIZE - i_used );
1775 p_access->i_buffer_tcp -= i_used;
1777 else if( p_access->i_buffer_udp > 0 )
1781 if( GetDWLE( p_access->buffer_tcp + 4 ) == 0xb00bface )
1784 mms_ParseCommand( p_input,
1785 p_access->buffer_tcp,
1786 p_access->i_buffer_tcp,
1794 mms_ParsePacket( p_input,
1795 p_access->buffer_udp,
1796 p_access->i_buffer_udp,
1799 if( i_used < MMS_BUFFER_SIZE )
1801 memmove( p_access->buffer_udp,
1802 p_access->buffer_udp + i_used,
1803 MMS_BUFFER_SIZE - i_used );
1805 p_access->i_buffer_udp -= i_used;
1809 i_packet_udp_type = -1;
1812 if( i_packet_tcp_type == MMS_PACKET_CMD &&
1813 p_access->i_command == 0x1b )
1815 mms_CommandSend( p_input, 0x1b, 0, 0, NULL, 0 );
1816 i_packet_tcp_type = -1;
1819 if( i_packet_tcp_type != -1 )
1821 return( i_packet_tcp_type );
1823 else if( i_packet_udp_type != -1 )
1825 return( i_packet_udp_type );
1831 static int mms_ReceiveCommand( input_thread_t *p_input )
1833 access_t *p_access = (access_t*)p_input->p_access_data;
1840 if( NetFillBuffer( p_input ) < 0 )
1842 msg_Warn( p_input, "cannot fill buffer" );
1845 if( p_access->i_buffer_tcp > 0 )
1847 i_status = mms_ParseCommand( p_input,
1848 p_access->buffer_tcp,
1849 p_access->i_buffer_tcp,
1851 if( i_used < MMS_BUFFER_SIZE )
1853 memmove( p_access->buffer_tcp,
1854 p_access->buffer_tcp + i_used,
1855 MMS_BUFFER_SIZE - i_used );
1857 p_access->i_buffer_tcp -= i_used;
1864 if( p_access->i_command == 0x1b )
1866 mms_CommandSend( p_input, 0x1b, 0, 0, NULL, 0 );
1882 #define MMS_RETRY_MAX 10
1883 #define MMS_RETRY_SLEEP 50000
1885 static int mms_CommandRead( input_thread_t *p_input, int i_command1, int i_command2 )
1887 access_t *p_access = (access_t*)p_input->p_access_data;
1891 for( i_count = 0; i_count < MMS_RETRY_MAX; )
1894 i_status = mms_ReceiveCommand( p_input );
1895 if( i_status < 0 || p_access->i_command == 0 )
1898 msleep( MMS_RETRY_SLEEP );
1900 else if( i_command1 == 0 && i_command2 == 0)
1904 else if( p_access->i_command == i_command1 || p_access->i_command == i_command2 )
1910 switch( p_access->i_command )
1913 msg_Warn( p_input, "socket closed by server" );
1914 p_access->i_eos = 1;
1917 msg_Warn( p_input, "end of media stream" );
1918 p_access->i_eos = 1;
1925 msg_Warn( p_input, "failed to receive command (abording)" );
1931 static int mms_HeaderMediaRead( input_thread_t *p_input, int i_type )
1933 access_t *p_access = (access_t*)p_input->p_access_data;
1936 for( i_count = 0; i_count < MMS_RETRY_MAX; )
1940 i_status = mms_ReceivePacket( p_input );
1945 "cannot receive header (%d/%d)", i_count, MMS_RETRY_MAX );
1946 msleep( MMS_RETRY_SLEEP );
1948 else if( i_status == i_type || i_type == MMS_PACKET_ANY )
1952 else if( i_status == MMS_PACKET_CMD )
1954 switch( p_access->i_command )
1957 msg_Warn( p_input, "socket closed by server" );
1958 p_access->i_eos = 1;
1961 msg_Warn( p_input, "end of media stream" );
1962 p_access->i_eos = 1;
1965 /* XXX not too dificult to be done EXCEPT that we
1966 * need to restart demuxer... and I don't see how we
1967 * could do that :p */
1969 "reinitialization needed --> unsupported" );
1970 p_access->i_eos = 1;
1978 "cannot receive %s (abording)",
1979 ( i_type == MMS_PACKET_HEADER ) ? "header" : "media data" );