1 /*****************************************************************************
2 * mms.c: MMS access plug-in
3 *****************************************************************************
4 * Copyright (C) 2001, 2002 VideoLAN
5 * $Id: mms.c,v 1.7 2002/11/25 00:22:04 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>
66 /****************************************************************************
68 * MMSProtocole documentation found at http://get.to/sdp
69 ****************************************************************************/
71 /*****************************************************************************
73 *****************************************************************************/
74 static int Open ( vlc_object_t * );
75 static void Close ( vlc_object_t * );
77 static int Read ( input_thread_t * p_input, byte_t * p_buffer,
79 static void Seek ( input_thread_t *, off_t );
80 static int SetProgram ( input_thread_t *, pgrm_descriptor_t * );
83 static int MMSOpen( input_thread_t *, url_t *, int, char * );
85 static int MMSStart ( input_thread_t *, uint32_t );
86 static int MMSStop ( input_thread_t *p_input );
88 static int MMSClose ( input_thread_t * );
91 static int mms_CommandRead( input_thread_t *p_input, int i_command1, int i_command2 );
92 static int mms_CommandSend( input_thread_t *, int, uint32_t, uint32_t, uint8_t *, int );
94 static int mms_HeaderMediaRead( input_thread_t *, int );
96 static int mms_ReceivePacket( input_thread_t * );
98 static void mms_ParseURL( url_t *p_url, char *psz_url );
103 * XXX DON'T FREE MY MEMORY !!! XXX
108 * Ok, ok, j'le ferai plus...
114 /*****************************************************************************
116 *****************************************************************************/
118 set_description( _("MMS access module") );
119 set_capability( "access", 0 );
120 add_category_hint( "stream", NULL );
121 add_bool( "mms-all", 0, NULL,
122 "force selection of all streams",
123 "force selection of all streams" );
125 add_string( "mms-stream", NULL, NULL,
127 "force this stream selection" );
128 add_integer( "mms-maxbitrate", 0, NULL,
130 "set max bitrate for auto streams selections" );
131 add_shortcut( "mms" );
132 add_shortcut( "mmsu" );
133 add_shortcut( "mmst" );
134 set_callbacks( Open, Close );
137 #define BUF_SIZE 200000
139 static int Open( vlc_object_t *p_this )
146 input_thread_t *p_input = (input_thread_t*)p_this;
148 /* *** allocate p_access_data *** */
149 p_input->p_access_data =
150 (void*)p_access = malloc( sizeof( access_t ) );
151 memset( p_access, 0, sizeof( access_t ) );
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 msg_Err( p_input, "invalid server name" );
163 if( p_access->url.i_server_port == 0 )
165 p_access->url.i_server_port = 1755; /* default port */
167 if( p_access->url.i_bind_port == 0 )
169 p_access->url.i_bind_port = 7000; /* default port */
173 /* *** connect to this server *** */
174 /* 1: look at requested protocol (udp/tcp) */
175 i_proto = MMS_PROTO_AUTO;
176 if( *p_input->psz_access )
178 if( !strncmp( p_input->psz_access, "mmsu", 4 ) )
180 i_proto = MMS_PROTO_UDP;
182 else if( !strncmp( p_input->psz_access, "mmst", 4 ) )
184 i_proto = MMS_PROTO_TCP;
187 /* 2: look at ip version ipv4/ipv6 */
189 if( config_GetInt( p_input, "ipv4" ) )
191 psz_network = "ipv4";
193 else if( config_GetInt( p_input, "ipv6" ) )
195 psz_network = "ipv6";
198 if( i_proto == MMS_PROTO_AUTO )
199 { /* first try with TCP */
201 MMSOpen( p_input, &p_access->url, MMS_PROTO_TCP, psz_network );
203 { /* then with UDP */
205 MMSOpen( p_input, &p_access->url, MMS_PROTO_UDP, psz_network );
212 MMSOpen( p_input, &p_access->url, i_proto, psz_network );
217 msg_Err( p_input, "cannot connect to server" );
218 FREE( p_access->url.psz_private );
221 msg_Dbg( p_input, "connected to %s", p_access->url.psz_server_addr );
224 /* *** set exported functions *** */
225 p_input->pf_read = Read;
226 p_input->pf_seek = Seek;
227 p_input->pf_set_program = SetProgram;
228 p_input->pf_set_area = NULL;
230 p_input->p_private = NULL;
232 /* *** finished to set some variable *** */
233 vlc_mutex_lock( &p_input->stream.stream_lock );
234 /* those data could be different for UDP/TCP */
235 p_input->stream.b_pace_control = 1;
236 p_input->stream.p_selected_area->i_tell = 0;
237 if( p_access->i_packet_count <= 0 )
239 p_input->stream.b_seekable = 0;
240 p_input->stream.p_selected_area->i_size = 0;
244 p_input->stream.b_seekable = 0;
245 p_input->stream.p_selected_area->i_size =
247 p_access->i_packet_count * p_access->i_packet_length;
250 p_input->stream.i_method = INPUT_METHOD_NETWORK;
251 vlc_mutex_unlock( &p_input->stream.stream_lock );
253 /* *** Start stream *** */
254 if( MMSStart( p_input, 0xffffffff ) < 0 )
256 msg_Err( p_input, "cannot start stream" );
258 FREE( p_access->url.psz_private );
265 /*****************************************************************************
266 * Close: free unused data structures
267 *****************************************************************************/
268 static void Close( vlc_object_t *p_this )
270 input_thread_t * p_input = (input_thread_t *)p_this;
271 access_t *p_access = (access_t*)p_input->p_access_data;
273 /* close connection with server */
277 FREE( p_access->url.psz_private );
280 /*****************************************************************************
281 * SetProgram: do nothing
282 *****************************************************************************/
283 static int SetProgram( input_thread_t * p_input,
284 pgrm_descriptor_t * p_program )
289 /*****************************************************************************
290 * Seek: try to go at the right place
291 *****************************************************************************/
292 static void Seek( input_thread_t * p_input, off_t i_pos )
297 * Probably some bad or missing command
303 access_t *p_access = (access_t*)p_input->p_access_data;
311 msg_Dbg( p_input, "seeking to %lld, header size:%d", i_pos, p_access->i_header );
312 if( i_pos < p_access->i_header)
315 if( p_access->i_pos < p_access->i_header )
317 /* no need to restart stream, it was already one
318 * or no stream was yet read */
319 p_access->i_pos = i_pos;
324 i_packet = 0xffffffff;
330 i_packet = ( i_pos - p_access->i_header ) / p_access->i_packet_length;
331 i_offset = ( i_pos - p_access->i_header ) % p_access->i_packet_length;
335 MMSStart( p_input, i_packet );
336 p_access->i_media_used += i_offset;
337 p_access->i_pos = i_pos;
341 static int Read ( input_thread_t * p_input, byte_t * p_buffer,
344 access_t *p_access = (access_t*)p_input->p_access_data;
350 /* *** send header if needed ** */
351 if( p_access->i_pos < p_access->i_header )
353 i_copy = __MIN( i_len, p_access->i_header - p_access->i_pos );
357 p_access->p_header + p_access->i_pos,
363 /* *** now send data if needed *** */
364 while( i_data < i_len )
366 if( p_access->i_media_used < p_access->i_media )
368 i_copy = __MIN( i_len - i_data ,
369 p_access->i_media - p_access->i_media_used );
370 memcpy( p_buffer + i_data,
371 p_access->p_media + p_access->i_media_used,
374 p_access->i_media_used += i_copy;
376 else if( p_access->p_media != NULL &&
377 p_access->i_media_used < p_access->i_packet_length )
379 i_copy = __MIN( i_len - i_data,
380 p_access->i_packet_length - p_access->i_media_used);
381 memset( p_buffer + i_data, 0, i_copy );
384 p_access->i_media_used += i_copy;
388 if( mms_HeaderMediaRead( p_input, MMS_PACKET_MEDIA ) < 0 );
390 p_access->i_pos += i_data;
396 p_access->i_pos += i_data;
400 static void asf_HeaderParse( mms_stream_t stream[128],
401 uint8_t *p_header, int i_header )
408 for( i = 0; i < 128; i++ )
410 stream[i].i_cat = MMS_STREAM_UNKNOWN;
413 // fprintf( stderr, " ---------------------header:%d\n", i_header );
414 var_buffer_initread( &buffer, p_header, i_header );
416 var_buffer_getguid( &buffer, &guid );
418 if( !CmpGuid( &guid, &asf_object_header_guid ) )
421 // fprintf( stderr, " ---------------------ERROR------\n" );
423 var_buffer_getmemory( &buffer, NULL, 30 - 16 );
427 // fprintf( stderr, " ---------------------data:%d\n", buffer.i_data );
428 if( var_buffer_readempty( &buffer ) )
433 var_buffer_getguid( &buffer, &guid );
434 i_size = var_buffer_get64( &buffer );
435 if( CmpGuid( &guid, &asf_object_stream_properties_guid ) )
439 // msg_Dbg( p_input, "found stream_properties" );
441 var_buffer_getguid( &buffer, &stream_type );
442 var_buffer_getmemory( &buffer, NULL, 32 );
443 i_stream_id = var_buffer_get8( &buffer ) & 0x7f;
445 // fprintf( stderr, " 1---------------------skip:%lld\n", i_size - 24 - 32 - 16 - 1 );
446 var_buffer_getmemory( &buffer, NULL, i_size - 24 - 32 - 16 - 1);
448 if( CmpGuid( &stream_type, &asf_object_stream_type_video ) )
450 // msg_Dbg( p_input, "video stream[%d] found", i_stream_id );
451 stream[i_stream_id].i_cat = MMS_STREAM_VIDEO;
453 else if( CmpGuid( &stream_type, &asf_object_stream_type_audio ) )
455 // msg_Dbg( p_input, "audio stream[%d] found", i_stream_id );
456 stream[i_stream_id].i_cat = MMS_STREAM_AUDIO;
460 // msg_Dbg( p_input, "unknown stream[%d] found", i_stream_id );
461 stream[i_stream_id].i_cat = MMS_STREAM_UNKNOWN;
464 else if ( CmpGuid( &guid, &asf_object_bitrate_properties_guid ) )
469 i_count = var_buffer_get16( &buffer );
473 i_stream_id = var_buffer_get16( &buffer )&0x7f;
474 stream[i_stream_id].i_bitrate = var_buffer_get32( &buffer );
478 // fprintf( stderr, " 2---------------------skip:%lld\n", i_size - 24);
479 var_buffer_getmemory( &buffer, NULL, i_size - 24 );
484 var_buffer_getmemory( &buffer, NULL, i_size - 24 );
485 // fprintf( stderr, " 3---------------------skip:%lld\n", i_size - 24);
490 static void mms_StreamSelect( input_thread_t * p_input,
491 mms_stream_t stream[128] )
493 /* XXX FIXME use mututal eclusion information */
495 int i_audio, i_video;
496 int b_audio, b_video;
504 i_bitrate_max = config_GetInt( p_input, "mms-maxbitrate" );
505 b_audio = config_GetInt( p_input, "audio" );
506 b_video = config_GetInt( p_input, "video" );
507 if( config_GetInt( p_input, "mms-all" ) )
509 /* select all valid stream */
510 for( i = 1; i < 128; i++ )
512 if( stream[i].i_cat != MMS_STREAM_UNKNOWN )
514 stream[i].i_selected = 1;
521 for( i = 0; i < 128; i++ )
523 stream[i].i_selected = 0; /* by default, not selected */
526 psz_stream = config_GetPsz( p_input, "mms-stream" );
528 if( psz_stream && *psz_stream )
530 char *psz_tmp = psz_stream;
533 if( *psz_tmp == ',' )
540 i_stream = atoi( psz_tmp );
541 while( *psz_tmp != '\0' && *psz_tmp != ',' )
546 if( i_stream > 0 && i_stream < 128 &&
547 stream[i_stream].i_cat != MMS_STREAM_UNKNOWN )
549 stream[i_stream].i_selected = 1;
560 * - no audio nor video stream
562 * - if i_bitrate_max not set keep the highest bitrate
563 * - if i_bitrate_max is set, keep stream that make we used best
564 * quality regarding i_bitrate_max
567 * - it doesn't use mutual exclusion info..
568 * - when selecting a better stream we could select
569 * something that make i_bitrate_total> i_bitrate_max
571 for( i = 1; i < 128; i++ )
573 if( stream[i].i_cat == MMS_STREAM_UNKNOWN )
577 else if( stream[i].i_cat == MMS_STREAM_AUDIO && b_audio &&
579 ( ( ( stream[i].i_bitrate > stream[i_audio].i_bitrate &&
580 ( i_bitrate_total + stream[i].i_bitrate - stream[i_audio].i_bitrate
581 < i_bitrate_max || !i_bitrate_max) ) ||
582 ( stream[i].i_bitrate < stream[i_audio].i_bitrate &&
583 i_bitrate_max != 0 && i_bitrate_total > i_bitrate_max )
586 /* unselect old stream */
589 stream[i_audio].i_selected = 0;
590 if( stream[i_audio].i_bitrate> 0 )
592 i_bitrate_total -= stream[i_audio].i_bitrate;
596 stream[i].i_selected = 1;
597 if( stream[i].i_bitrate> 0 )
599 i_bitrate_total += stream[i].i_bitrate;
603 else if( stream[i].i_cat == MMS_STREAM_VIDEO && b_video &&
606 ( ( stream[i].i_bitrate > stream[i_video].i_bitrate &&
607 ( i_bitrate_total + stream[i].i_bitrate - stream[i_video].i_bitrate
608 < i_bitrate_max || !i_bitrate_max) ) ||
609 ( stream[i].i_bitrate < stream[i_video].i_bitrate &&
610 i_bitrate_max != 0 && i_bitrate_total > i_bitrate_max )
613 /* unselect old stream */
615 stream[i_video].i_selected = 0;
616 if( stream[i_video].i_bitrate> 0 )
618 i_bitrate_total -= stream[i_video].i_bitrate;
621 stream[i].i_selected = 1;
622 if( stream[i].i_bitrate> 0 )
624 i_bitrate_total += stream[i].i_bitrate;
630 if( i_bitrate_max > 0 )
633 "requested bitrate:%d real bitrate:%d",
634 i_bitrate_max, i_bitrate_total );
644 /****************************************************************************
645 * MMSOpen : Open a connection with the server over mmst or mmsu(not yet)
646 ****************************************************************************/
647 static int MMSOpen( input_thread_t *p_input,
650 char *psz_network ) /* "", "ipv4", "ipv6" */
653 access_t *p_access = (access_t*)p_input->p_access_data;
655 network_socket_t socket_desc;
656 int b_udp = ( i_proto == MMS_PROTO_UDP ) ? 1 : 0;
661 int i_server_version;
663 int i_update_player_url;
664 int i_encryption_type;
670 /* *** Open a TCP connection with server *** */
671 msg_Dbg( p_input, "waiting for connection..." );
672 socket_desc.i_type = NETWORK_TCP;
673 socket_desc.psz_server_addr = p_url->psz_server_addr;
674 socket_desc.i_server_port = p_url->i_server_port;
675 socket_desc.psz_bind_addr = "";
676 socket_desc.i_bind_port = 0;
677 p_input->p_private = (void*)&socket_desc;
678 if( !( p_network = module_Need( p_input, "network", psz_network ) ) )
680 msg_Err( p_input, "failed to open a connection (tcp)" );
683 module_Unneed( p_input, p_network );
684 p_access->socket_tcp.i_handle = socket_desc.i_handle;
685 p_input->i_mtu = 0; /*socket_desc.i_mtu;*/
687 "connection(tcp) with \"%s:%d\" successful",
688 p_url->psz_server_addr,
689 p_url->i_server_port );
691 /* *** Bind port if UDP protocol is selected *** */
695 "MMS/UDP not yet functionnal, anyway trying..." );
696 if( !p_url->psz_bind_addr || !*p_url->psz_bind_addr )
698 msg_Err( p_input, "for udp you have to provide bind address (mms://<server_addr>@<bind_addr/<path> (FIXME)" );
699 #if defined( UNDER_CE )
700 CloseHandle( (HANDLE)p_access->socket_tcp.i_handle );
701 #elif defined( WIN32 )
702 closesocket( p_access->socket_tcp.i_handle );
704 close( p_access->socket_tcp.i_handle );
708 socket_desc.i_type = NETWORK_UDP;
709 socket_desc.psz_server_addr = "";
710 socket_desc.i_server_port = 0;
711 socket_desc.psz_bind_addr = p_url->psz_bind_addr;
712 socket_desc.i_bind_port = p_url->i_bind_port;
713 p_input->p_private = (void*)&socket_desc;
714 if( !( p_network = module_Need( p_input, "network", psz_network ) ) )
716 msg_Err( p_input, "failed to open a connection (udp)" );
717 #if defined( UNDER_CE )
718 CloseHandle( (HANDLE)p_access->socket_tcp.i_handle );
719 #elif defined( WIN32 )
720 closesocket( p_access->socket_tcp.i_handle );
722 close( p_access->socket_tcp.i_handle );
726 module_Unneed( p_input, p_network );
727 p_access->socket_udp.i_handle = socket_desc.i_handle;
728 p_input->i_mtu = 0;/*socket_desc.i_mtu; FIXME */
732 /* *** Init context for mms prototcol *** */
733 GenerateGuid( &p_access->guid ); /* used to identify client by server */
735 "generated guid: "GUID_FMT,
736 GUID_PRINT( p_access->guid ) );
737 p_access->i_command_level = 1; /* updated after 0x1A command */
738 p_access->i_seq_num = 0;
739 p_access->i_media_packet_id_type = 0x04;
740 p_access->i_header_packet_id_type = 0x02;
741 p_access->i_proto = i_proto;
742 p_access->i_packet_seq_num = 0;
743 p_access->p_header = NULL;
744 p_access->i_header = 0;
745 p_access->p_media = NULL;
746 p_access->i_media = 0;
747 p_access->i_media_used = 0;
750 p_access->i_buffer_tcp = 0;
751 p_access->i_buffer_udp = 0;
752 p_access->p_cmd = NULL;
755 /* *** send command 1 : connection request *** */
756 var_buffer_initwrite( &buffer, 0 );
757 var_buffer_add16( &buffer, 0x001c );
758 var_buffer_add16( &buffer, 0x0003 );
760 "NSPlayer/7.0.0.1956; {"GUID_FMT"}; Host: %s",
761 GUID_PRINT( p_access->guid ),
762 p_url->psz_server_addr );
763 var_buffer_addUTF16( &buffer, tmp );
765 mms_CommandSend( p_input,
766 0x01, /* connexion request */
767 0x00000000, /* flags, FIXME */
768 0x0004000b, /* ???? */
772 mms_CommandRead( p_input, 0x01, 0 );
773 i_server_version = GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 32 );
774 i_tool_version = GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 36 );
775 i_update_player_url = GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 40 );
776 i_encryption_type = GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 44 );
777 p = (uint16_t*)( p_access->p_cmd + MMS_CMD_HEADERSIZE + 48 );
778 #define GETUTF16( psz, size ) \
781 psz = malloc( size + 1); \
782 for( i = 0; i < size; i++ ) \
789 GETUTF16( p_access->psz_server_version, i_server_version );
790 GETUTF16( p_access->psz_tool_version, i_tool_version );
791 GETUTF16( p_access->psz_update_player_url, i_update_player_url );
792 GETUTF16( p_access->psz_encryption_type, i_encryption_type );
795 "0x01 --> server_version:\"%s\" tool_version:\"%s\" update_player_url:\"%s\" encryption_type:\"%s\"",
796 p_access->psz_server_version,
797 p_access->psz_tool_version,
798 p_access->psz_update_player_url,
799 p_access->psz_encryption_type );
801 /* *** should make an 18 command to make data timing *** */
803 /* *** send command 2 : transport protocol selection *** */
804 var_buffer_reinitwrite( &buffer, 0 );
805 var_buffer_add32( &buffer, 0x00000000 );
806 var_buffer_add32( &buffer, 0x000a0000 );
807 var_buffer_add32( &buffer, 0x00000002 );
812 p_url->psz_bind_addr,
813 p_url->i_bind_port );
817 sprintf( tmp, "\\\\127.0.0.1\\TCP\\1242" );
819 var_buffer_addUTF16( &buffer, tmp );
820 var_buffer_add16( &buffer, '0' );
822 mms_CommandSend( p_input,
823 0x02, /* connexion request */
824 0x00000000, /* flags, FIXME */
825 0xffffffff, /* ???? */
829 /* *** response from server, should be 0x02 or 0x03 *** */
830 mms_CommandRead( p_input, 0x02, 0x03 );
831 if( p_access->i_command == 0x03 )
834 "%s protocol selection failed", b_udp ? "UDP" : "TCP" );
835 var_buffer_free( &buffer );
839 else if( p_access->i_command != 0x02 )
841 msg_Warn( p_input, "received command isn't 0x02 in reponse to 0x02" );
844 /* *** send command 5 : media file name/path requested *** */
845 var_buffer_reinitwrite( &buffer, 0 );
846 var_buffer_add64( &buffer, 0 );
847 var_buffer_addUTF16( &buffer, p_url->psz_path );
849 mms_CommandSend( p_input,
851 p_access->i_command_level,
856 /* *** wait for reponse *** */
857 mms_CommandRead( p_input, 0x1a, 0x06 );
859 /* test if server send 0x1A answer */
860 if( p_access->i_command == 0x1A )
862 msg_Err( p_input, "id/password requested (not yet supported)" );
864 var_buffer_free( &buffer );
868 if( p_access->i_command != 0x06 )
871 "unknown answer (0x%x instead of 0x06)",
872 p_access->i_command );
873 var_buffer_free( &buffer );
878 /* 1 for file ok, 2 for authen ok */
879 switch( GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE ) )
882 msg_Dbg( p_input, "Media file name/path accepted" );
885 msg_Dbg( p_input, "Authentication accepted" );
889 msg_Err( p_input, "error while asking for file %d",
890 GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE ) );
891 var_buffer_free( &buffer );
896 p_access->i_flags_broadcast =
897 GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 12 );
898 p_access->i_media_length =
899 GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 24 );
900 p_access->i_packet_length =
901 GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 44 );
902 p_access->i_packet_count =
903 GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 48 );
904 p_access->i_max_bit_rate =
905 GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 56 );
906 p_access->i_header_size =
907 GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 60 );
910 "answer 0x06 flags:0x%8.8x media_length:%ds packet_length:%d packet_count:%d max_bit_rate:%d header_size:%d",
911 p_access->i_flags_broadcast,
912 p_access->i_media_length,
913 p_access->i_packet_length,
914 p_access->i_packet_count,
915 p_access->i_max_bit_rate,
916 p_access->i_header_size );
918 /* XXX XXX dirty hack XXX XXX */
919 p_input->i_mtu = 3 * p_access->i_packet_length;
921 /* *** send command 15 *** */
923 var_buffer_reinitwrite( &buffer, 0 );
924 var_buffer_add32( &buffer, 0 );
925 var_buffer_add32( &buffer, 0x8000 );
926 var_buffer_add32( &buffer, 0xffffffff );
927 var_buffer_add32( &buffer, 0x00 );
928 var_buffer_add32( &buffer, 0x00 );
929 var_buffer_add32( &buffer, 0x00 );
930 var_buffer_add64( &buffer, 0x40ac200000000000 );
931 var_buffer_add32( &buffer, p_access->i_header_packet_id_type );
932 mms_CommandSend( p_input, 0x15, p_access->i_command_level, 0x00,
933 buffer.p_data, buffer.i_data );
935 /* *** wait for reponse *** */
936 mms_CommandRead( p_input, 0x11, 0 );
938 if( p_access->i_command != 0x11 )
941 "unknown answer (0x%x instead of 0x11)",
942 p_access->i_command );
943 var_buffer_free( &buffer );
947 /* *** now read header packet *** */
948 if( mms_HeaderMediaRead( p_input, MMS_PACKET_HEADER ) < 0 )
950 msg_Err( p_input, "cannot receive header" );
951 var_buffer_free( &buffer );
955 /* *** parse header and get stream and their id *** */
956 /* get all streams properties,
958 * TODO : stream bitrates properties(optional)
959 * and bitrate mutual exclusion(optional) */
960 asf_HeaderParse( p_access->stream,
961 p_access->p_header, p_access->i_header );
962 mms_StreamSelect( p_input, p_access->stream );
963 /* *** now select stream we want to receive *** */
964 /* TODO take care of stream bitrate TODO */
967 var_buffer_reinitwrite( &buffer, 0 );
968 /* for now, select first audio and video stream */
969 for( i = 1; i < 128; i++ )
972 if( p_access->stream[i].i_cat != MMS_STREAM_UNKNOWN )
977 var_buffer_add16( &buffer, 0xffff );
978 var_buffer_add16( &buffer, i );
984 if( p_access->stream[i].i_selected )
986 var_buffer_add16( &buffer, 0x0000 );
988 "selecting stream[0x%x] %s (%d kb/s)",
990 ( p_access->stream[i].i_cat == MMS_STREAM_AUDIO ) ?
992 p_access->stream[i].i_bitrate / 1024);
996 var_buffer_add16( &buffer, 0x0002 );
998 "ignoring stream[0x%x] %s (%d kb/s)",
1000 ( p_access->stream[i].i_cat == MMS_STREAM_AUDIO ) ?
1002 p_access->stream[i].i_bitrate / 1024);
1008 if( i_streams == 0 )
1010 msg_Err( p_input, "cannot find any stream" );
1011 var_buffer_free( &buffer );
1012 MMSClose( p_input );
1015 mms_CommandSend( p_input, 0x33,
1017 0xffff | ( i_first << 16 ),
1018 buffer.p_data, buffer.i_data );
1020 mms_CommandRead( p_input, 0x21, 0 );
1021 if( p_access->i_command != 0x21 )
1024 "unknown answer (0x%x instead of 0x21)",
1025 p_access->i_command );
1026 var_buffer_free( &buffer );
1027 MMSClose( p_input );
1032 var_buffer_free( &buffer );
1034 msg_Info( p_input, "connection sucessful" );
1039 /****************************************************************************
1040 * MMSStart : Start streaming
1041 ****************************************************************************/
1042 static int MMSStart ( input_thread_t *p_input, uint32_t i_packet )
1044 access_t *p_access = (access_t*)p_input->p_access_data;
1045 var_buffer_t buffer;
1047 /* *** start stream from packet 0 *** */
1048 var_buffer_initwrite( &buffer, 0 );
1049 var_buffer_add64( &buffer, 0 ); /* seek point in second */
1050 var_buffer_add32( &buffer, 0xffffffff );
1051 var_buffer_add32( &buffer, i_packet ); // begin from start
1052 var_buffer_add8( &buffer, 0xff ); // stream time limit
1053 var_buffer_add8( &buffer, 0xff ); // on 3bytes ...
1054 var_buffer_add8( &buffer, 0xff ); //
1055 var_buffer_add8( &buffer, 0x00 ); // don't use limit
1056 var_buffer_add32( &buffer, p_access->i_media_packet_id_type );
1058 mms_CommandSend( p_input, 0x07, p_access->i_command_level, 0x0001ffff,
1059 buffer.p_data, buffer.i_data );
1061 var_buffer_free( &buffer );
1063 mms_CommandRead( p_input, 0x05, 0 );
1065 if( p_access->i_command != 0x05 )
1068 "unknown answer (0x%x instead of 0x05)",
1069 p_access->i_command );
1075 mms_HeaderMediaRead( p_input, MMS_PACKET_MEDIA );
1076 msg_Dbg( p_input, "Streaming started" );
1081 /****************************************************************************
1082 * MMSStop : Stop streaming
1083 ****************************************************************************/
1084 static int MMSStop ( input_thread_t *p_input )
1086 access_t *p_access = (access_t*)p_input->p_access_data;
1088 /* *** stop stream but keep connection alive *** */
1089 mms_CommandSend( p_input,
1091 p_access->i_command_level,
1097 /****************************************************************************
1098 * MMSClose : Close streaming and connection
1099 ****************************************************************************/
1100 static int MMSClose ( input_thread_t *p_input )
1102 access_t *p_access = (access_t*)p_input->p_access_data;
1104 msg_Dbg( p_input, "Connection closed" );
1106 /* *** tell server that we will disconnect *** */
1107 mms_CommandSend( p_input,
1109 p_access->i_command_level,
1112 /* *** close sockets *** */
1113 #if defined( UNDER_CE )
1114 CloseHandle( (HANDLE)p_access->socket_tcp.i_handle );
1115 #elif defined( WIN32 )
1116 closesocket( p_access->socket_tcp.i_handle );
1118 close( p_access->socket_tcp.i_handle );
1121 if( p_access->i_proto == MMS_PROTO_UDP )
1123 #if defined( UNDER_CE )
1124 CloseHandle( (HANDLE)p_access->socket_udp.i_handle );
1125 #elif defined( WIN32 )
1126 closesocket( p_access->socket_udp.i_handle );
1128 close( p_access->socket_udp.i_handle );
1132 FREE( p_access->p_cmd );
1133 FREE( p_access->p_media );
1134 FREE( p_access->p_header );
1136 FREE( p_access->psz_server_version );
1137 FREE( p_access->psz_tool_version );
1138 FREE( p_access->psz_update_player_url );
1139 FREE( p_access->psz_encryption_type );
1144 /*****************************************************************************
1145 * mms_ParseURL : parse an url string and fill an url_t
1146 *****************************************************************************/
1147 static void mms_ParseURL( url_t *p_url, char *psz_url )
1150 char *psz_server_port;
1152 p_url->psz_private = strdup( psz_url );
1154 psz_parser = p_url->psz_private;
1156 while( *psz_parser == '/' )
1160 p_url->psz_server_addr = psz_parser;
1162 while( *psz_parser &&
1163 *psz_parser != ':' && *psz_parser != '/' && *psz_parser != '@' )
1168 if( *psz_parser == ':' )
1172 psz_server_port = psz_parser;
1174 while( *psz_parser && *psz_parser != '/' )
1181 psz_server_port = "";
1184 if( *psz_parser == '@' )
1186 char *psz_bind_port;
1191 p_url->psz_bind_addr = psz_parser;
1193 while( *psz_parser && *psz_parser != ':' && *psz_parser != '/' )
1198 if( *psz_parser == ':' )
1202 psz_bind_port = psz_parser;
1204 while( *psz_parser && *psz_parser != '/' )
1213 if( *psz_bind_port )
1215 p_url->i_bind_port = strtol( psz_bind_port, &psz_parser, 10 );
1219 p_url->i_bind_port = 0;
1224 p_url->psz_bind_addr = "";
1225 p_url->i_bind_port = 0;
1228 if( *psz_parser == '/' )
1232 p_url->psz_path = psz_parser;
1235 if( *psz_server_port )
1237 p_url->i_server_port = strtol( psz_server_port, &psz_parser, 10 );
1241 p_url->i_server_port = 0;
1245 /****************************************************************************
1247 * MMS specific functions
1249 ****************************************************************************/
1251 static int mms_CommandSend( input_thread_t *p_input,
1253 uint32_t i_prefix1, uint32_t i_prefix2,
1254 uint8_t *p_data, int i_data )
1256 var_buffer_t buffer;
1258 access_t *p_access = (access_t*)p_input->p_access_data;
1261 i_data_by8 = ( i_data + 7 ) / 8;
1263 /* first init uffer */
1264 var_buffer_initwrite( &buffer, 0 );
1266 var_buffer_add32( &buffer, 0x00000001 ); /* start sequence */
1267 var_buffer_add32( &buffer, 0xB00BFACE );
1268 /* size after protocol type */
1269 var_buffer_add32( &buffer, i_data + MMS_CMD_HEADERSIZE - 16 );
1270 var_buffer_add32( &buffer, 0x20534d4d ); /* protocol "MMS " */
1271 var_buffer_add32( &buffer, i_data_by8 + 4 );
1272 var_buffer_add32( &buffer, p_access->i_seq_num ); p_access->i_seq_num++;
1273 var_buffer_add64( &buffer, 0 );
1274 var_buffer_add32( &buffer, i_data_by8 + 2 );
1275 var_buffer_add32( &buffer, 0x00030000 | i_command ); /* dir | command */
1276 var_buffer_add32( &buffer, i_prefix1 ); /* command specific */
1277 var_buffer_add32( &buffer, i_prefix2 ); /* command specific */
1279 /* specific command data */
1280 if( p_data && i_data > 0 )
1282 var_buffer_addmemory( &buffer, p_data, i_data );
1286 if( send( p_access->socket_tcp.i_handle,
1291 msg_Err( p_input, "failed to send command" );
1295 var_buffer_free( &buffer );
1299 static int NetFillBuffer( input_thread_t *p_input )
1304 access_t *p_access = (access_t*)p_input->p_access_data;
1305 struct timeval timeout;
1309 /* FIXME when using udp */
1310 ssize_t i_tcp, i_udp;
1311 ssize_t i_tcp_read, i_udp_read;
1314 /* Initialize file descriptor set */
1317 i_tcp = MMS_BUFFER_SIZE/2 - p_access->i_buffer_tcp;
1319 if( p_access->i_proto == MMS_PROTO_UDP )
1321 i_udp = MMS_BUFFER_SIZE/2 - p_access->i_buffer_udp;
1325 i_udp = 0; /* there isn't udp socket */
1331 FD_SET( p_access->socket_tcp.i_handle, &fds );
1332 i_handle_max = __MAX( i_handle_max, p_access->socket_tcp.i_handle );
1336 FD_SET( p_access->socket_udp.i_handle, &fds );
1337 i_handle_max = __MAX( i_handle_max, p_access->socket_udp.i_handle );
1340 if( i_handle_max == 0 )
1342 msg_Warn( p_input, "nothing to read %d:%d", i_tcp, i_udp );
1347 msg_Warn( p_input, "ask for tcp:%d udp:%d", i_tcp, i_udp );
1350 /* We'll wait 0.5 second if nothing happens */
1352 timeout.tv_usec = 500000;
1354 /* Find if some data is available */
1355 i_ret = select( i_handle_max + 1,
1357 NULL, NULL, &timeout );
1359 if( i_ret == -1 && errno != EINTR )
1361 msg_Err( p_input, "network select error (%s)", strerror(errno) );
1365 if( i_tcp > 0 && FD_ISSET( p_access->socket_tcp.i_handle, &fds ) )
1368 recv( p_access->socket_tcp.i_handle,
1369 p_access->buffer_tcp + p_access->i_buffer_tcp,
1370 i_tcp + MMS_BUFFER_SIZE/2, 0 );
1377 if( i_udp > 0 && FD_ISSET( p_access->socket_udp.i_handle, &fds ) )
1379 i_udp_read = recv( p_access->socket_udp.i_handle,
1380 p_access->buffer_udp + p_access->i_buffer_udp,
1381 i_udp + MMS_BUFFER_SIZE/2, 0 );
1389 p_access->i_buffer_tcp += i_tcp_read;
1390 p_access->i_buffer_udp += i_udp_read;
1393 "filling TCP buffer with %d bytes (buffer:%d)",
1395 p_access->i_buffer_tcp );
1396 if( p_access->i_proto == MMS_PROTO_UDP )
1399 "filling UDP buffer with %d bytes (buffer:%d)",
1401 p_access->i_buffer_udp );
1404 return( i_tcp_read + i_udp_read);
1408 static int mms_ParseCommand( input_thread_t *p_input,
1413 #define GET32( i_pos ) \
1414 ( p_access->p_cmd[i_pos] + ( p_access->p_cmd[i_pos +1] << 8 ) + \
1415 ( p_access->p_cmd[i_pos + 2] << 16 ) + \
1416 ( p_access->p_cmd[i_pos + 3] << 24 ) )
1418 access_t *p_access = (access_t*)p_input->p_access_data;
1422 if( p_access->p_cmd )
1424 free( p_access->p_cmd );
1426 p_access->i_cmd = i_data;
1427 p_access->p_cmd = malloc( i_data );
1428 memcpy( p_access->p_cmd, p_data, i_data );
1430 *pi_used = i_data; /* by default */
1432 if( i_data < MMS_CMD_HEADERSIZE )
1434 msg_Warn( p_input, "truncated command (header incomplete)" );
1435 p_access->i_command = 0;
1438 i_id = GetDWLE( p_data + 4 );
1439 i_length = GetDWLE( p_data + 8 ) + 16;
1441 if( i_id != 0xb00bface )
1444 "incorrect command header (0x%x)", i_id );
1445 p_access->i_command = 0;
1449 if( i_length > p_access->i_cmd )
1452 "truncated command (missing %d bytes)",
1453 i_length - i_data );
1454 p_access->i_command = 0;
1457 else if( i_length < p_access->i_cmd )
1459 p_access->i_cmd = i_length;
1460 *pi_used = i_length;
1464 "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",
1474 p_access->i_command = GET32( 36 ) & 0xffff;
1476 return( MMS_PACKET_CMD );
1479 static int mms_ParsePacket( input_thread_t *p_input,
1480 uint8_t *p_data, int i_data,
1483 access_t *p_access = (access_t*)p_input->p_access_data;
1484 int i_packet_seq_num;
1485 int i_packet_length;
1491 *pi_used = i_data; /* default */
1494 msg_Warn( p_input, "truncated packet (header incomplete)" );
1498 i_packet_id = p_data[4];
1499 i_packet_seq_num = GetDWLE( p_data );
1500 i_packet_length = GetWLE( p_data + 6 );
1503 if( i_packet_length > i_data || i_packet_length <= 8)
1506 "truncated packet (missing %d bytes)",
1507 i_packet_length - i_data );
1510 else if( i_packet_length < i_data )
1512 *pi_used = i_packet_length;
1515 if( i_packet_id == 0xff )
1518 "receive MMS UDP pair timing" );
1519 return( MMS_PACKET_UDP_TIMING );
1522 if( i_packet_id != p_access->i_header_packet_id_type &&
1523 i_packet_id != p_access->i_media_packet_id_type )
1525 msg_Warn( p_input, "incorrect Packet Id Type (0x%x)", i_packet_id );
1529 /* we now have a media or a header packet */
1530 p_packet = malloc( i_packet_length - 8 ); // don't bother with preheader
1531 memcpy( p_packet, p_data + 8, i_packet_length - 8 );
1533 if( i_packet_seq_num != p_access->i_packet_seq_num )
1535 /* FIXME for udp could be just wrong order ? */
1537 "detected packet lost (%d != %d)",
1539 p_access->i_packet_seq_num );
1540 p_access->i_packet_seq_num = i_packet_seq_num;
1542 p_access->i_packet_seq_num++;
1544 if( i_packet_id == p_access->i_header_packet_id_type )
1546 FREE( p_access->p_header );
1547 p_access->p_header = p_packet;
1548 p_access->i_header = i_packet_length - 8;
1550 "receive header packet (%d bytes)",
1551 i_packet_length - 8 );
1553 return( MMS_PACKET_HEADER );
1557 FREE( p_access->p_media );
1558 p_access->p_media = p_packet;
1559 p_access->i_media = i_packet_length - 8;
1560 p_access->i_media_used = 0;
1562 "receive media packet (%d bytes)",
1563 i_packet_length - 8 );
1565 return( MMS_PACKET_MEDIA );
1569 static int mms_ReceivePacket( input_thread_t *p_input )
1571 access_t *p_access = (access_t*)p_input->p_access_data;
1572 int i_packet_tcp_type;
1573 int i_packet_udp_type;
1577 if( NetFillBuffer( p_input ) < 0 )
1579 msg_Warn( p_input, "cannot fill buffer" );
1584 i_packet_tcp_type = -1;
1585 i_packet_udp_type = -1;
1587 if( p_access->i_buffer_tcp > 0 )
1591 if( GetDWLE( p_access->buffer_tcp + 4 ) == 0xb00bface )
1594 mms_ParseCommand( p_input,
1595 p_access->buffer_tcp,
1596 p_access->i_buffer_tcp,
1603 mms_ParsePacket( p_input,
1604 p_access->buffer_tcp,
1605 p_access->i_buffer_tcp,
1608 if( i_used < MMS_BUFFER_SIZE )
1610 memmove( p_access->buffer_tcp,
1611 p_access->buffer_tcp + i_used,
1612 MMS_BUFFER_SIZE - i_used );
1614 p_access->i_buffer_tcp -= i_used;
1616 else if( p_access->i_buffer_udp > 0 )
1620 if( GetDWLE( p_access->buffer_tcp + 4 ) == 0xb00bface )
1623 mms_ParseCommand( p_input,
1624 p_access->buffer_tcp,
1625 p_access->i_buffer_tcp,
1633 mms_ParsePacket( p_input,
1634 p_access->buffer_udp,
1635 p_access->i_buffer_udp,
1638 if( i_used < MMS_BUFFER_SIZE )
1640 memmove( p_access->buffer_udp,
1641 p_access->buffer_udp + i_used,
1642 MMS_BUFFER_SIZE - i_used );
1644 p_access->i_buffer_udp -= i_used;
1648 i_packet_udp_type = -1;
1651 if( i_packet_tcp_type == MMS_PACKET_CMD &&
1652 p_access->i_command == 0x1b )
1654 mms_CommandSend( p_input, 0x1b, 0, 0, NULL, 0 );
1655 i_packet_tcp_type = -1;
1658 if( i_packet_tcp_type != -1 )
1660 return( i_packet_tcp_type );
1662 else if( i_packet_udp_type != -1 )
1664 return( i_packet_udp_type );
1670 static int mms_ReceiveCommand( input_thread_t *p_input )
1672 access_t *p_access = (access_t*)p_input->p_access_data;
1678 NetFillBuffer( p_input );
1680 i_status = mms_ParseCommand( p_input,
1681 p_access->buffer_tcp,
1682 p_access->i_buffer_tcp,
1684 if( i_used < MMS_BUFFER_SIZE )
1686 memmove( p_access->buffer_tcp,
1687 p_access->buffer_tcp + i_used,
1688 MMS_BUFFER_SIZE - i_used );
1690 p_access->i_buffer_tcp -= i_used;
1697 if( p_access->i_command == 0x1b )
1699 mms_CommandSend( p_input, 0x1b, 0, 0, NULL, 0 );
1710 #define MMS_RETRY_MAX 10
1711 #define MMS_RETRY_SLEEP 50000
1713 static int mms_CommandRead( input_thread_t *p_input, int i_command1, int i_command2 )
1715 access_t *p_access = (access_t*)p_input->p_access_data;
1719 for( i_count = 0; i_count < MMS_RETRY_MAX; )
1722 i_status = mms_ReceiveCommand( p_input );
1723 if( i_status < 0 || p_access->i_command == 0 )
1726 msleep( MMS_RETRY_SLEEP );
1728 else if( i_command1 == 0 && i_command2 == 0)
1732 else if( p_access->i_command == i_command1 || p_access->i_command == i_command2 )
1738 switch( p_access->i_command )
1741 msg_Warn( p_input, "socket closed by server" );
1744 msg_Warn( p_input, "end of media stream" );
1751 msg_Warn( p_input, "failed to receive command (abording)" );
1757 static int mms_HeaderMediaRead( input_thread_t *p_input, int i_type )
1759 access_t *p_access = (access_t*)p_input->p_access_data;
1762 for( i_count = 0; i_count < MMS_RETRY_MAX; )
1766 i_status = mms_ReceivePacket( p_input );
1771 "cannot receive header (%d/%d)", i_count, MMS_RETRY_MAX );
1772 msleep( MMS_RETRY_SLEEP );
1774 else if( i_status == i_type || i_type == MMS_PACKET_ANY )
1778 else if( i_status == MMS_PACKET_CMD )
1780 switch( p_access->i_command )
1783 msg_Warn( p_input, "socket closed by server" );
1786 msg_Warn( p_input, "end of media stream" );
1789 /* XXX not too dificult to be done EXCEPT that we
1790 * need to restart demuxer... and I don't see how we
1791 * could do that :p */
1793 "reinitialization needed --> unsupported" );
1801 "cannot receive %s (abording)",
1802 ( i_type == MMS_PACKET_HEADER ) ? "header" : "media data" );