1 /*****************************************************************************
2 * mms.c: MMS access plug-in
3 *****************************************************************************
4 * Copyright (C) 2001, 2002 VideoLAN
5 * $Id: mms.c,v 1.2 2002/11/12 13:57:12 sam 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_shortcut( "mms" );
120 add_shortcut( "mmsu" );
121 add_shortcut( "mmst" );
122 set_callbacks( Open, Close );
125 #define BUF_SIZE 200000
127 static int Open( vlc_object_t *p_this )
134 input_thread_t *p_input = (input_thread_t*)p_this;
136 /* *** allocate p_access_data *** */
137 p_input->p_access_data =
138 (void*)p_access = malloc( sizeof( access_t ) );
139 memset( p_access, 0, sizeof( access_t ) );
141 p_access->p_cmd = malloc( BUF_SIZE );
144 /* *** Parse URL and get server addr/port and path *** */
145 mms_ParseURL( &p_access->url, p_input->psz_name );
146 if( p_access->url.psz_server_addr == NULL ||
147 !( *p_access->url.psz_server_addr ) )
149 FREE( p_access->url.psz_private );
150 FREE( p_access->p_cmd );
151 msg_Err( p_input, "invalid server name" );
154 if( p_access->url.i_server_port == 0 )
156 p_access->url.i_server_port = 1755; // default port
160 /* *** connect to this server *** */
161 /* 1: look at requested protocol (udp/tcp) */
162 i_proto = MMS_PROTO_AUTO;
163 if( *p_input->psz_access )
165 if( !strncmp( p_input->psz_access, "mmsu", 4 ) )
167 i_proto = MMS_PROTO_UDP;
169 else if( !strncmp( p_input->psz_access, "mmst", 4 ) )
171 i_proto = MMS_PROTO_TCP;
174 /* 2: look at ip version ipv4/ipv6 */
176 if( config_GetInt( p_input, "ipv4" ) )
178 psz_network = "ipv4";
180 else if( config_GetInt( p_input, "ipv6" ) )
182 psz_network = "ipv6";
185 if( i_proto == MMS_PROTO_AUTO )
186 { // first try with TCP
188 MMSOpen( p_input, &p_access->url, MMS_PROTO_TCP, psz_network );
192 MMSOpen( p_input, &p_access->url, MMS_PROTO_UDP, psz_network );
199 MMSOpen( p_input, &p_access->url, i_proto, psz_network );
204 // all sockets are closed
205 msg_Err( p_input, "cannot connect to server" );
206 FREE( p_access->url.psz_private );
207 FREE( p_access->p_cmd );
210 msg_Dbg( p_input, "connected to %s", p_access->url.psz_server_addr );
212 // all sockets are open
215 /* *** set exported functions *** */
216 p_input->pf_read = Read;
217 p_input->pf_seek = Seek;
218 p_input->pf_set_program = SetProgram;
219 p_input->pf_set_area = NULL;
221 p_input->p_private = NULL; // XXX ??
223 /* *** finished to set some variable *** */
224 vlc_mutex_lock( &p_input->stream.stream_lock );
225 /* those data could be different for UDP/TCP */
226 p_input->stream.b_pace_control = 0;
227 p_input->stream.p_selected_area->i_tell = 0;
228 if( p_access->i_packet_count <= 0 )
230 p_input->stream.b_seekable = 0;
231 p_input->stream.p_selected_area->i_size = 0;
235 p_input->stream.b_seekable = 0;
236 p_input->stream.p_selected_area->i_size =
238 p_access->i_packet_count * p_access->i_packet_length;
241 p_input->stream.i_method = INPUT_METHOD_NETWORK;
242 vlc_mutex_unlock( &p_input->stream.stream_lock );
244 /* *** Start stream *** */
245 if( MMSStart( p_input, 0xffffffff ) < 0 )
247 msg_Err( p_input, "cannot start stream" );
249 FREE( p_access->url.psz_private );
250 FREE( p_access->p_cmd );
257 /*****************************************************************************
258 * Close: free unused data structures
259 *****************************************************************************/
260 static void Close( vlc_object_t *p_this )
262 input_thread_t * p_input = (input_thread_t *)p_this;
263 access_t *p_access = (access_t*)p_input->p_access_data;
265 /* close connection with server */
269 FREE( p_access->url.psz_private );
270 FREE( p_access->p_cmd );
273 /*****************************************************************************
274 * SetProgram: do nothing
275 *****************************************************************************/
276 static int SetProgram( input_thread_t * p_input,
277 pgrm_descriptor_t * p_program )
282 /*****************************************************************************
283 * Seek: try to go at the right place
284 *****************************************************************************/
285 static void Seek( input_thread_t * p_input, off_t i_pos )
290 * Probably some bad or missing command
296 access_t *p_access = (access_t*)p_input->p_access_data;
304 msg_Dbg( p_input, "seeking to %lld, header size:%d", i_pos, p_access->i_header );
305 if( i_pos < p_access->i_header)
308 if( p_access->i_pos < p_access->i_header )
310 /* no need to restart stream, it was already one
311 * or no stream was yet read */
312 p_access->i_pos = i_pos;
317 i_packet = 0xffffffff;
323 i_packet = ( i_pos - p_access->i_header ) / p_access->i_packet_length;
324 i_offset = ( i_pos - p_access->i_header ) % p_access->i_packet_length;
328 MMSStart( p_input, i_packet );
329 p_access->i_media_used += i_offset;
330 p_access->i_pos = i_pos;
334 static int Read ( input_thread_t * p_input, byte_t * p_buffer,
337 access_t *p_access = (access_t*)p_input->p_access_data;
343 /* *** send header if needed ** */
344 if( p_access->i_pos < p_access->i_header )
346 i_copy = __MIN( i_len, p_access->i_header - p_access->i_pos );
350 p_access->p_header + p_access->i_pos,
356 /* *** now send data if needed *** */
357 while( i_data < i_len )
359 if( p_access->i_media_used < p_access->i_media )
361 i_copy = __MIN( i_len - i_data ,
362 p_access->i_media - p_access->i_media_used );
363 memcpy( p_buffer + i_data,
364 p_access->p_media + p_access->i_media_used,
367 p_access->i_media_used += i_copy;
369 else if( p_access->p_media != NULL &&
370 p_access->i_media_used < p_access->i_packet_length )
372 i_copy = __MIN( i_len - i_data,
373 p_access->i_packet_length - p_access->i_media_used);
374 memset( p_buffer + i_data, 0, i_copy );
377 p_access->i_media_used += i_copy;
381 if( mms_HeaderMediaRead( p_input, MMS_PACKET_MEDIA ) < 0 );
383 p_access->i_pos += i_data;
389 p_access->i_pos += i_data;
393 /*****************************************************************************
394 * NetRead: read on a file descriptor, checking b_die periodically
395 *****************************************************************************/
396 static ssize_t NetRead( input_thread_t * p_input, input_socket_t * p_socket,
397 byte_t * p_buffer, size_t i_len )
403 struct timeval timeout;
407 /* Initialize file descriptor set */
409 FD_SET( p_socket->i_handle, &fds );
411 /* We'll wait 0.5 second if nothing happens */
413 timeout.tv_usec = 500000;
415 /* Find if some data is available */
416 i_ret = select( p_socket->i_handle + 1, &fds,
417 NULL, NULL, &timeout );
419 if( i_ret == -1 && errno != EINTR )
421 msg_Err( p_input, "network select error (%s)", strerror(errno) );
425 ssize_t i_recv = recv( p_socket->i_handle, p_buffer, i_len, 0 );
429 vlc_mutex_lock( &p_input->stream.stream_lock );
430 p_input->stream.p_selected_area->i_tell += i_recv;
431 vlc_mutex_unlock( &p_input->stream.stream_lock );
436 msg_Err( p_input, "recv failed (%s)", strerror(errno) );
447 static void asf_HeaderParse( mms_stream_t stream[128],
448 uint8_t *p_header, int i_header )
455 for( i = 0; i < 128; i++ )
457 stream[i].i_cat = MMS_STREAM_UNKNOWN;
460 var_buffer_initread( &buffer, p_header, i_header );
462 var_buffer_getguid( &buffer, &guid );
463 if( !CmpGuid( &guid, &asf_object_header_guid ) )
467 var_buffer_getmemory( &buffer, NULL, 30 - 16 );
471 if( var_buffer_readempty( &buffer ) )
476 var_buffer_getguid( &buffer, &guid );
477 i_size = var_buffer_get64( &buffer );
478 if( CmpGuid( &guid, &asf_object_stream_properties_guid ) )
483 // msg_Dbg( p_input, "found stream_properties" );
485 var_buffer_getguid( &buffer, &stream_type );
486 var_buffer_getmemory( &buffer, NULL, 32 );
487 i_stream_id = var_buffer_get8( &buffer ) & 0x7f;
488 var_buffer_getmemory( &buffer, NULL, i_size - 24 - 32 - 16 - 1 );
490 if( CmpGuid( &stream_type, &asf_object_stream_type_video ) )
492 // msg_Dbg( p_input, "video stream[%d] found", i_stream_id );
493 stream[i_stream_id].i_cat = MMS_STREAM_VIDEO;
495 else if( CmpGuid( &stream_type, &asf_object_stream_type_audio ) )
497 // msg_Dbg( p_input, "audio stream[%d] found", i_stream_id );
498 stream[i_stream_id].i_cat = MMS_STREAM_AUDIO;
502 // msg_Dbg( p_input, "unknown stream[%d] found", i_stream_id );
503 stream[i_stream_id].i_cat = MMS_STREAM_UNKNOWN;
506 else if ( CmpGuid( &guid, &asf_object_bitrate_properties_guid ) )
511 i_count = var_buffer_get16( &buffer );
515 i_stream_id = var_buffer_get16( &buffer )&0x7f;
516 stream[i_stream_id].i_bitrate = var_buffer_get32( &buffer );
520 var_buffer_getmemory( &buffer, NULL, i_size - 24 );
525 var_buffer_getmemory( &buffer, NULL, i_size - 24 );
530 /****************************************************************************
531 * MMSOpen : Open a connection with the server over mmst or mmsu(not yet)
532 ****************************************************************************/
533 static int MMSOpen( input_thread_t *p_input,
536 char *psz_network ) /* "", "ipv4", "ipv6" */
539 access_t *p_access = (access_t*)p_input->p_access_data;
541 network_socket_t socket_desc;
542 int b_udp = ( i_proto == MMS_PROTO_UDP ) ? 1 : 0;
547 int i_server_version;
549 int i_update_player_url;
550 int i_encryption_type;
558 /* *** Open a TCP connection with server *** */
559 msg_Dbg( p_input, "waiting for connection..." );
560 socket_desc.i_type = NETWORK_TCP;
561 socket_desc.psz_server_addr = p_url->psz_server_addr;
562 socket_desc.i_server_port = p_url->i_server_port;
563 socket_desc.psz_bind_addr = "";
564 socket_desc.i_bind_port = 0;
565 p_input->p_private = (void*)&socket_desc;
566 if( !( p_network = module_Need( p_input, "network", psz_network ) ) )
568 msg_Err( p_input, "failed to open a connection" );
571 module_Unneed( p_input, p_network );
572 p_access->socket_server.i_handle = socket_desc.i_handle;
573 p_input->i_mtu = socket_desc.i_mtu; // FIXME
575 "connection with \"%s:%d\" successful",
576 p_url->psz_server_addr,
577 p_url->i_server_port );
579 /* *** Bind port if UDP protocol is selected *** */
584 "MMS/UDP not yet implemented" );
586 #if defined( UNDER_CE )
587 CloseHandle( (HANDLE)p_access->socket_server.i_handle );
588 #elif defined( WIN32 )
589 closesocket( p_access->socket_server.i_handle );
591 close( p_access->socket_server.i_handle );
596 /* *** Init context for mms prototcol *** */
597 GenerateGuid( &p_access->guid ); // used to identify client by server
599 "generated guid: "GUID_FMT,
600 GUID_PRINT( p_access->guid ) );
601 p_access->i_command_level = 1; // updated after 0x1A command
602 p_access->i_seq_num = 0;
603 p_access->i_media_packet_id_type = 0x04;
604 p_access->i_header_packet_id_type = 0x02;
605 p_access->i_proto = i_proto;
606 p_access->i_packet_seq_num = 0;
607 p_access->p_header = NULL;
608 p_access->i_header = 0;
609 p_access->p_media = NULL;
610 p_access->i_media = 0;
611 p_access->i_media_used = 0;
615 /* *** send command 1 : connection request *** */
616 var_buffer_initwrite( &buffer, 0 );
617 var_buffer_add16( &buffer, 0x001c );
618 var_buffer_add16( &buffer, 0x0003 );
620 "NSPlayer/7.0.0.1956; {"GUID_FMT"}; Host: %s",
621 GUID_PRINT( p_access->guid ),
622 p_url->psz_server_addr );
623 var_buffer_addUTF16( &buffer, tmp );
625 mms_CommandSend( p_input,
626 0x01, /* connexion request */
627 0x00000000, /* flags, FIXME */
628 0x0004000b, /* ???? */
632 mms_CommandRead( p_input, 0x01, 0 );
633 i_server_version = GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 32 );
634 i_tool_version = GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 36 );
635 i_update_player_url = GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 40 );
636 i_encryption_type = GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 44 );
637 p = (uint16_t*)( p_access->p_cmd + MMS_CMD_HEADERSIZE + 48 );
638 #define GETUTF16( psz, size ) \
641 psz = malloc( size + 1); \
642 for( i = 0; i < size; i++ ) \
649 GETUTF16( p_access->psz_server_version, i_server_version );
650 GETUTF16( p_access->psz_tool_version, i_tool_version );
651 GETUTF16( p_access->psz_update_player_url, i_update_player_url );
652 GETUTF16( p_access->psz_encryption_type, i_encryption_type );
655 "0x01 --> server_version:\"%s\" tool_version:\"%s\" update_player_url:\"%s\" encryption_type:\"%s\"",
656 p_access->psz_server_version,
657 p_access->psz_tool_version,
658 p_access->psz_update_player_url,
659 p_access->psz_encryption_type );
661 /* *** should make an 18 command to make data timing *** */
663 /* *** send command 2 : transport protocol selection *** */
664 var_buffer_reinitwrite( &buffer, 0 );
665 var_buffer_add32( &buffer, 0x00000000 );
666 var_buffer_add32( &buffer, 0x000a0000 );
667 var_buffer_add32( &buffer, 0x00000002 );
668 // FIXME wrong for UDP FIXME
669 sprintf( tmp, "\\\\127.0.0.1\\%s\\1242", b_udp ? "UDP" : "TCP" );
670 var_buffer_addUTF16( &buffer, tmp );
671 var_buffer_add16( &buffer, '0' );
673 mms_CommandSend( p_input,
674 0x02, /* connexion request */
675 0x00000000, /* flags, FIXME */
676 0xffffffff, /* ???? */
680 /* *** response from server, should be 0x02 or 0x03 *** */
681 mms_CommandRead( p_input, 0x02, 0 );
682 if( p_access->i_command == 0x03 )
685 "%s protocol selection failed", b_udp ? "UDP" : "TCP" );
686 var_buffer_free( &buffer );
690 else if( p_access->i_command != 0x02 )
692 msg_Warn( p_input, "received command isn't 0x02 in reponse to 0x02" );
695 /* *** send command 5 : media file name/path requested *** */
696 var_buffer_reinitwrite( &buffer, 0 );
697 var_buffer_add64( &buffer, 0 );
698 // var_buffer_addUTF16( &buffer, "/" );
699 var_buffer_addUTF16( &buffer, p_url->psz_path );
701 mms_CommandSend( p_input,
703 p_access->i_command_level,
708 /* *** wait for reponse *** */
709 mms_CommandRead( p_input, 0x1a, 0x06 );
711 /* test if server send 0x1A answer */
712 if( p_access->i_command == 0x1A )
714 msg_Err( p_input, "id/password requested (not yet supported)" );
716 var_buffer_free( &buffer );
720 if( p_access->i_command != 0x06 )
723 "unknown answer (0x%x instead of 0x06)",
724 p_access->i_command );
725 var_buffer_free( &buffer );
730 // 1 for file ok, 2 for authen ok
731 switch( GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE ) )
734 msg_Dbg( p_input, "Media file name/path accepted" );
737 msg_Dbg( p_input, "Authentication accepted" );
741 msg_Err( p_input, "error while asking for file %d",
742 GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE ) );
743 var_buffer_free( &buffer );
748 p_access->i_flags_broadcast =
749 GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 12 );
750 p_access->i_media_length =
751 GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 24 );
752 p_access->i_packet_length =
753 GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 44 );
754 p_access->i_packet_count =
755 GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 48 );
756 p_access->i_max_bit_rate =
757 GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 56 );
758 p_access->i_header_size =
759 GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 60 );
762 "answer 0x06 flags:0x%8.8x media_length:%ds packet_length:%d packet_count:%d max_bit_rate:%d header_size:%d",
763 p_access->i_flags_broadcast,
764 p_access->i_media_length,
765 p_access->i_packet_length,
766 p_access->i_packet_count,
767 p_access->i_max_bit_rate,
768 p_access->i_header_size );
770 /* *** send command 15 *** */
772 var_buffer_reinitwrite( &buffer, 0 );
773 var_buffer_add32( &buffer, 0 );
774 var_buffer_add32( &buffer, 0x8000 );
775 var_buffer_add32( &buffer, 0xffffffff );
776 var_buffer_add32( &buffer, 0x00 );
777 var_buffer_add32( &buffer, 0x00 );
778 var_buffer_add32( &buffer, 0x00 );
779 var_buffer_add64( &buffer, 0x40ac200000000000 );
780 var_buffer_add32( &buffer, p_access->i_header_packet_id_type );
781 mms_CommandSend( p_input, 0x15, p_access->i_command_level, 0x00,
782 buffer.p_data, buffer.i_data );
784 /* *** wait for reponse *** */
785 mms_CommandRead( p_input, 0x11, 0 );
787 if( p_access->i_command != 0x11 )
790 "unknown answer (0x%x instead of 0x11)",
791 p_access->i_command );
792 var_buffer_free( &buffer );
796 /* *** now read header packet *** */
797 if( mms_HeaderMediaRead( p_input, MMS_PACKET_HEADER ) < 0 )
799 msg_Err( p_input, "cannot receive header" );
800 var_buffer_free( &buffer );
804 /* *** parse header and get stream and their id *** */
805 // get all streams properties,
807 // TODO : stream bitrates properties(optional)
808 // and bitrate mutual exclusion(optional)
809 asf_HeaderParse( p_access->stream,
810 p_access->p_header, p_access->i_header );
812 /* *** now select stream we want to receive *** */
813 // TODO take care of stream bitrate TODO
816 var_buffer_reinitwrite( &buffer, 0 );
817 /* for now, select first audio and video stream */
820 for( i = 1; i < 128; i++ )
823 if( ( p_access->stream[i].i_cat == MMS_STREAM_AUDIO && !b_audio )||
824 ( p_access->stream[i].i_cat == MMS_STREAM_VIDEO && !b_video ) )
830 var_buffer_add16( &buffer, 0x0000 ); // on
834 var_buffer_add16( &buffer, 0xffff );
835 var_buffer_add16( &buffer, i );
836 var_buffer_add16( &buffer, 0x0000 );
839 "selecting stream[0x%x] %s (%d kb/s)",
841 ( p_access->stream[i].i_cat == MMS_STREAM_AUDIO ) ?
843 p_access->stream[i].i_bitrate / 1024);
844 if( p_access->stream[i].i_cat == MMS_STREAM_AUDIO )
848 if( p_access->stream[i].i_cat == MMS_STREAM_VIDEO )
854 else if( p_access->stream[i].i_cat != MMS_STREAM_UNKNOWN )
857 "ignoring stream[0x%x] %s (%d kb/s)",
859 ( p_access->stream[i].i_cat == MMS_STREAM_AUDIO ) ?
861 p_access->stream[i].i_bitrate / 1024);
867 msg_Err( p_input, "cannot find any stream" );
868 var_buffer_free( &buffer );
872 mms_CommandSend( p_input, 0x33,
874 0xffff | ( i_first << 16 ),
875 buffer.p_data, buffer.i_data );
877 mms_CommandRead( p_input, 0x21, 0 );
878 if( p_access->i_command != 0x21 )
881 "unknown answer (0x%x instead of 0x21)",
882 p_access->i_command );
883 var_buffer_free( &buffer );
889 var_buffer_free( &buffer );
891 msg_Info( p_input, "connection sucessful" );
896 /****************************************************************************
897 * MMSStart : Start streaming
898 ****************************************************************************/
899 static int MMSStart ( input_thread_t *p_input, uint32_t i_packet )
901 access_t *p_access = (access_t*)p_input->p_access_data;
904 /* *** start stream from packet 0 *** */
905 var_buffer_initwrite( &buffer, 0 );
906 var_buffer_add64( &buffer, 0 ); // seek point in second
907 var_buffer_add32( &buffer, 0xffffffff );
908 // var_buffer_add32( &buffer, 0xffffffff ); // begin from start
909 var_buffer_add32( &buffer, i_packet ); // begin from start
910 var_buffer_add8( &buffer, 0xff ); // stream time limit
911 var_buffer_add8( &buffer, 0xff ); // on 3bytes ...
912 var_buffer_add8( &buffer, 0xff ); //
913 var_buffer_add8( &buffer, 0x00 ); // don't use limit
914 var_buffer_add32( &buffer, p_access->i_media_packet_id_type );
916 mms_CommandSend( p_input, 0x07, p_access->i_command_level, 0x0001ffff,
917 buffer.p_data, buffer.i_data );
919 var_buffer_free( &buffer );
921 mms_CommandRead( p_input, 0x05, 0 );
923 if( p_access->i_command != 0x05 )
926 "unknown answer (0x%x instead of 0x05)",
927 p_access->i_command );
933 mms_HeaderMediaRead( p_input, MMS_PACKET_MEDIA );
934 msg_Dbg( p_input, "Streaming started" );
939 /****************************************************************************
940 * MMSStop : Stop streaming
941 ****************************************************************************/
942 static int MMSStop ( input_thread_t *p_input )
944 access_t *p_access = (access_t*)p_input->p_access_data;
946 /* *** stop stream but keep connection alive *** */
947 mms_CommandSend( p_input,
949 p_access->i_command_level,
955 /****************************************************************************
956 * MMSClose : Close streaming and connection
957 ****************************************************************************/
958 static int MMSClose ( input_thread_t *p_input )
960 access_t *p_access = (access_t*)p_input->p_access_data;
962 msg_Dbg( p_input, "Connection closed" );
964 /* *** tell server that we will disconnect *** */
965 mms_CommandSend( p_input,
967 p_access->i_command_level,
970 /* *** close sockets *** */
971 #if defined( UNDER_CE )
972 CloseHandle( (HANDLE)p_access->socket_server.i_handle );
973 #elif defined( WIN32 )
974 closesocket( p_access->socket_server.i_handle );
976 close( p_access->socket_server.i_handle );
979 if( p_access->i_proto == MMS_PROTO_UDP )
981 #if defined( UNDER_CE )
982 CloseHandle( (HANDLE)p_access->socket_data.i_handle );
983 #elif defined( WIN32 )
984 closesocket( p_access->socket_data.i_handle );
986 close( p_access->socket_data.i_handle );
990 FREE( p_access->p_media );
991 FREE( p_access->p_header );
993 FREE( p_access->psz_server_version );
994 FREE( p_access->psz_tool_version );
995 FREE( p_access->psz_update_player_url );
996 FREE( p_access->psz_encryption_type );
1001 /*****************************************************************************
1002 * mms_ParseURL : parse an url string and fill an url_t
1003 *****************************************************************************/
1004 static void mms_ParseURL( url_t *p_url, char *psz_url )
1007 char *psz_server_port;
1009 p_url->psz_private = strdup( psz_url );
1011 psz_parser = p_url->psz_private;
1013 while( *psz_parser == '/' )
1017 p_url->psz_server_addr = psz_parser;
1019 while( *psz_parser && *psz_parser != ':' && *psz_parser != '/' )
1024 if( *psz_parser == ':' )
1028 psz_server_port = psz_parser;
1030 while( *psz_parser && *psz_parser != '/' )
1037 psz_server_port = "";
1039 if( *psz_parser == '/' )
1043 p_url->psz_path = psz_parser;
1046 if( *psz_server_port )
1048 p_url->i_server_port = strtol( psz_server_port, &psz_parser, 10 );
1052 p_url->i_server_port = 0;
1056 static int mms_ReadData( input_thread_t *p_input,
1060 access_t *p_access = (access_t*)p_input->p_access_data;
1066 i_read = NetRead( p_input, &p_access->socket_server, p_data, i_data );
1069 msg_Err( p_input, "failed to read data" );
1079 /****************************************************************************
1081 * MMS specific functions
1083 ****************************************************************************/
1085 static int mms_CommandSend( input_thread_t *p_input,
1087 uint32_t i_prefix1, uint32_t i_prefix2,
1088 uint8_t *p_data, int i_data )
1090 var_buffer_t buffer;
1092 access_t *p_access = (access_t*)p_input->p_access_data;
1095 i_data_by8 = ( i_data + 7 ) / 8;
1097 /* first init uffer */
1098 var_buffer_initwrite( &buffer, 0 );
1100 var_buffer_add32( &buffer, 0x00000001 ); // start sequence
1101 var_buffer_add32( &buffer, 0xB00BFACE ); // ...
1102 // size after protocol type
1103 var_buffer_add32( &buffer, i_data + MMS_CMD_HEADERSIZE - 16 );
1104 var_buffer_add32( &buffer, 0x20534d4d ); // protocol "MMS "
1105 var_buffer_add32( &buffer, i_data_by8 + 4 );
1106 var_buffer_add32( &buffer, p_access->i_seq_num ); p_access->i_seq_num++;
1107 var_buffer_add64( &buffer, 0 );
1108 var_buffer_add32( &buffer, i_data_by8 + 2 );
1109 var_buffer_add32( &buffer, 0x00030000 | i_command ); /* dir | command */
1110 var_buffer_add32( &buffer, i_prefix1 ); /* command specific */
1111 var_buffer_add32( &buffer, i_prefix2 ); /* command specific */
1113 /* specific command data */
1114 if( p_data && i_data > 0 )
1116 var_buffer_addmemory( &buffer, p_data, i_data );
1120 if( send( p_access->socket_server.i_handle,
1125 msg_Err( p_input, "failed to send command" );
1129 var_buffer_free( &buffer );
1133 static int mms_ReceiveCommand( input_thread_t *p_input )
1135 #define GET32( i_pos ) \
1136 ( p_access->p_cmd[i_pos] + ( p_access->p_cmd[i_pos +1] << 8 ) + \
1137 ( p_access->p_cmd[i_pos + 2] << 16 ) + \
1138 ( p_access->p_cmd[i_pos + 3] << 24 ) )
1140 access_t *p_access = (access_t*)p_input->p_access_data;
1147 /* *** Read complete command *** */
1148 p_access->i_cmd = NetRead( p_input, &p_access->socket_server,
1149 p_access->p_cmd, BUF_SIZE );
1150 if( p_access->i_cmd < 12 )
1152 msg_Warn( p_input, "failed to receive command" );
1153 p_access->i_command = 0;
1156 i_length = GetDWLE( p_access->p_cmd + 8 ) + 16;
1157 if( i_length > p_access->i_cmd )
1159 if( mms_ReadData( p_input,
1160 p_access->p_cmd + p_access->i_cmd,
1161 i_length - p_access->i_cmd ) < 0 )
1163 msg_Warn( p_input, "failed to receive command" );
1164 p_access->i_command = 0;
1169 msg_Dbg( p_input, "received %d bytes", p_access->i_cmd );
1171 p_access->i_command = GET32( 36 ) & 0xffff;
1173 "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",
1183 if( p_access->i_command == 0x1b )
1185 mms_CommandSend( p_input, 0x1b, 0, 0, NULL, 0 );
1188 } while( p_access->i_command == 0x1b );
1193 #define MMS_RETRY_MAX 10
1194 #define MMS_RETRY_SLEEP 50000
1196 static int mms_CommandRead( input_thread_t *p_input, int i_command1, int i_command2 )
1198 access_t *p_access = (access_t*)p_input->p_access_data;
1202 for( i_count = 0; i_count < MMS_RETRY_MAX; )
1205 i_status = mms_ReceiveCommand( p_input );
1206 if( i_status < 0 || p_access->i_command == 0 )
1209 msleep( MMS_RETRY_SLEEP );
1211 else if( i_command1 == 0 && i_command2 == 0)
1215 else if( p_access->i_command == i_command1 || p_access->i_command == i_command2 )
1221 switch( p_access->i_command )
1224 msg_Warn( p_input, "socket closed by server" );
1227 msg_Warn( p_input, "end of media stream" );
1234 msg_Warn( p_input, "failed to receive command (abording)" );
1241 static int mms_ReceivePacket( input_thread_t *p_input )
1243 access_t *p_access = (access_t*)p_input->p_access_data;
1244 uint8_t preheader[8];
1247 if( p_access->i_proto == MMS_PROTO_UDP )
1255 i_read = NetRead( p_input, &p_access->socket_server, preheader, 8 );
1258 msg_Warn( p_input, "cannot read preheader" );
1261 /* preheader format :
1262 * u32 i_sequence_number
1264 * u8 i_udp_sequence/i_tcp_flags
1267 if( preheader[4] == p_access->i_header_packet_id_type ||
1268 preheader[4] == p_access->i_media_packet_id_type ||
1269 preheader[4] == 0xff )// udp timing pair
1271 int i_packet_seq_num;
1272 int i_packet_length;
1277 i_packet_seq_num = GetDWLE( preheader );
1278 i_packet_length = GetWLE( preheader + 6 );
1279 i_packet_id = preheader[4];
1281 /* *** read complete packet *** */
1282 if( i_packet_length <= 8 )
1285 "empty or broken packet" );
1288 p_packet = malloc( i_packet_length - 8 );
1289 if( mms_ReadData( p_input,
1291 i_packet_length - 8 ) < 0 )
1294 "cannot read data" );
1298 if( i_packet_id == 0xff )
1301 "receive MMS UDP pair timing" );
1303 return( MMS_PACKET_UDP_TIMING );
1307 if( i_packet_seq_num != p_access->i_packet_seq_num )
1309 // FIXME for udp could be just wrong order ?
1311 "detected packet lost (%d != %d)",
1313 p_access->i_packet_seq_num );
1314 p_access->i_packet_seq_num = i_packet_seq_num;
1316 p_access->i_packet_seq_num++;
1318 if( i_packet_id == p_access->i_header_packet_id_type )
1320 FREE( p_access->p_header );
1321 p_access->p_header = p_packet;
1322 p_access->i_header = i_packet_length - 8;
1323 return( MMS_PACKET_HEADER );
1327 FREE( p_access->p_media );
1328 p_access->p_media = p_packet;
1329 p_access->i_media = i_packet_length - 8;
1330 p_access->i_media_used = 0;
1331 return( MMS_PACKET_MEDIA );
1337 int i_packet_length;
1339 if( GetDWLE( preheader + 4 ) != 0xb00bface )
1342 "incorrect command header (0x%x)",
1343 GetDWLE( preheader + 4 ) );
1345 memcpy( p_access->p_cmd, preheader, 8 );
1346 if( mms_ReadData( p_input,
1347 p_access->p_cmd + 8,
1351 "cannot read data" );
1353 p_access->i_cmd = 16;
1354 i_packet_length = GetDWLE( p_access->p_cmd + 8 );
1355 if( mms_ReadData( p_input,
1356 p_access->p_cmd + 16,
1357 i_packet_length ) < 0 )
1360 "cannot read data" );
1362 p_access->i_cmd += i_packet_length;
1363 p_access->i_command = GetDWLE( p_access->p_cmd + 36 ) & 0xffff;
1364 if( p_access->i_command == 0x1b )
1366 mms_CommandSend( p_input, 0x1b, 0, 0, NULL, 0 );
1370 return( MMS_PACKET_CMD );
1379 static int mms_HeaderMediaRead( input_thread_t *p_input, int i_type )
1381 access_t *p_access = (access_t*)p_input->p_access_data;
1384 for( i_count = 0; i_count < MMS_RETRY_MAX; )
1388 i_status = mms_ReceivePacket( p_input );
1393 "cannot receive header (%d/%d)", i_count, MMS_RETRY_MAX );
1394 msleep( MMS_RETRY_SLEEP );
1396 else if( i_status == i_type )
1400 else if( i_status == MMS_PACKET_CMD )
1402 switch( p_access->i_command )
1405 msg_Warn( p_input, "socket closed by server" );
1408 msg_Warn( p_input, "end of media stream" );
1411 /* XXX not too dificult to be done EXCEPT that we
1412 * need to restart demuxer... and I don't see how we
1413 * could do that :p */
1415 "reinitialization needed --> unsupported" );
1423 "cannot receive %s (abording)",
1424 ( i_type == MMS_PACKET_HEADER ) ? "header" : "media data" );