X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Faccess%2Fmms%2Fmmsh.c;h=fceea8b965fe12c61d4f11b433bcfa775c5dc916;hb=2531d5144140a1b5ca3b13419c979c53e0c951ab;hp=208108e10278092e1291d1bcaceddf66d4dd4d31;hpb=bfefedf7598d023fb5920d00110ff4aebe5b3048;p=vlc diff --git a/modules/access/mms/mmsh.c b/modules/access/mms/mmsh.c index 208108e102..fceea8b965 100644 --- a/modules/access/mms/mmsh.c +++ b/modules/access/mms/mmsh.c @@ -1,8 +1,8 @@ /***************************************************************************** * mmsh.c: ***************************************************************************** - * Copyright (C) 2001, 2002 VideoLAN - * $Id: mmsh.c,v 1.2 2003/04/25 17:33:57 fenrir Exp $ + * Copyright (C) 2001, 2002 the VideoLAN team + * $Id$ * * Authors: Laurent Aimar * @@ -18,1000 +18,957 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ -/* - * TODO: - * * http_proxy - * - */ - /***************************************************************************** * Preamble *****************************************************************************/ -#include -#include - -#include -#include -#ifdef HAVE_ERRNO_H -# include -#endif -#ifdef HAVE_FCNTL_H -# include -#endif -#ifdef HAVE_SYS_TIME_H -# include +#ifdef HAVE_CONFIG_H +# include "config.h" #endif -#ifdef HAVE_UNISTD_H -# include -#endif +#include +#include +#include +#include -#if defined( UNDER_CE ) -# include -#elif defined( WIN32 ) -# include -# include -# ifndef IN_MULTICAST -# define IN_MULTICAST(a) IN_CLASSD(a) -# endif -#else -# include -#endif - -#include "network.h" +#include +#include #include "asf.h" #include "buffer.h" #include "mms.h" #include "mmsh.h" +/* TODO: + * - authentication + */ + /***************************************************************************** * Local prototypes *****************************************************************************/ -int E_( MMSHOpen ) ( input_thread_t * ); -void E_( MMSHClose ) ( input_thread_t * ); +int MMSHOpen ( access_t * ); +void MMSHClose ( access_t * ); -static int Read ( input_thread_t * p_input, byte_t * p_buffer, - size_t i_len ); -static void Seek ( input_thread_t *, off_t ); +static block_t *Block( access_t *p_access ); +static ssize_t ReadRedirect( access_t *, uint8_t *, size_t ); +static int Seek( access_t *, uint64_t ); +static int Control( access_t *, int, va_list ); -/**************************************************************************** - **************************************************************************** - ******************* ******************* - ******************* Main functions ******************* - ******************* ******************* - **************************************************************************** - ****************************************************************************/ +static int Describe( access_t *, char **ppsz_location ); +static int Start( access_t *, uint64_t ); +static void Stop( access_t * ); + +static int GetPacket( access_t *, chunk_t * ); +static void GetHeader( access_t *p_access ); + +static int Restart( access_t * ); +static int Reset( access_t * ); + +//#define MMSH_USER_AGENT "NSPlayer/4.1.0.3856" +#define MMSH_USER_AGENT "NSPlayer/7.10.0.3059" /**************************************************************************** * Open: connect to ftp server and ask for file ****************************************************************************/ -int E_( MMSHOpen ) ( input_thread_t *p_input ) +int MMSHOpen( access_t *p_access ) { access_sys_t *p_sys; + char *psz_location = NULL; + char *psz_proxy; - uint8_t *p; - http_answer_t *p_ans; - http_field_t *p_field; - chunk_t ck; + STANDARD_BLOCK_ACCESS_INIT - /* init p_sys */ - p_sys = malloc( sizeof( access_sys_t ) ); - p_sys->i_proto = MMS_PROTO_HTTP; + p_sys->i_proto= MMS_PROTO_HTTP; + p_sys->fd = -1; - p_sys->p_socket = NULL; - p_sys->i_request_context = 1; - p_sys->i_buffer = 0; - p_sys->i_buffer_pos = 0; - p_sys->b_broadcast = VLC_TRUE; - p_sys->p_packet = NULL; - p_sys->i_packet_sequence = 0; - p_sys->i_packet_used = 0; - p_sys->i_packet_length = 0; - p_sys->i_pos = 0; - E_( GenerateGuid )( &p_sys->guid ); - - /* open a tcp connection */ - p_sys->p_url = E_( url_new )( p_input->psz_name ); + /* Handle proxy */ + p_sys->b_proxy = false; + memset( &p_sys->proxy, 0, sizeof(p_sys->proxy) ); - if( *p_sys->p_url->psz_host == '\0' ) + /* Check proxy */ + /* TODO reuse instead http-proxy from http access ? */ + psz_proxy = var_CreateGetNonEmptyString( p_access, "mmsh-proxy" ); + if( !psz_proxy ) { - msg_Err( p_input, "invalid server addresse" ); - goto exit_error; - } - if( p_sys->p_url->i_port <= 0 ) - { - p_sys->p_url->i_port = 80; + char *psz_http_proxy = var_InheritString( p_access, "http-proxy" ); + if( psz_http_proxy ) + { + psz_proxy = psz_http_proxy; + var_SetString( p_access, "mmsh-proxy", psz_proxy ); + } } - p_sys->p_socket = NetOpenTCP( p_input, p_sys->p_url ); - if( !p_sys->p_socket ) + if( psz_proxy ) { - msg_Err( p_input, "cannot connect" ); - goto exit_error; + p_sys->b_proxy = true; + vlc_UrlParse( &p_sys->proxy, psz_proxy, 0 ); + free( psz_proxy ); } - p_sys->i_request_context = 1; - - /* *** send first request *** */ - p = &p_sys->buffer[0]; - p += sprintf( p, "GET %s HTTP/1.0\r\n", p_sys->p_url->psz_path ); - p += sprintf( p,"Accept: */*\r\n" ); - p += sprintf( p, "User-Agent: NSPlayer/4.1.0.3856\r\n" ); - p += sprintf( p, "Host: %s:%d\r\n", p_sys->p_url->psz_host, p_sys->p_url->i_port ); - p += sprintf( p, "Pragma: no-cache,rate=1.000000,stream-time=0,stream-offset=0:0,request-context=%d,max-duration=0\r\n", p_sys->i_request_context++ ); - //p += sprintf( p, "Pragma: xClientGUID={c77e7400-738a-11d2-9add-0020af0a3278}\r\n" ); - p += sprintf( p, "Pragma: xClientGUID={"GUID_FMT"}\r\n", GUID_PRINT( p_sys->guid ) ); - p += sprintf( p, "Connection: Close\r\n\r\n" ); - NetWrite( p_input, p_sys->p_socket, p_sys->buffer, p - p_sys->buffer ); - - - if( NetFill ( p_input, p_sys, BUFFER_SIZE ) <= 0 ) +#ifdef HAVE_GETENV + else { - msg_Err( p_input, "cannot read answer" ); - goto exit_error; + const char *http_proxy = getenv( "http_proxy" ); + if( http_proxy ) + { + p_sys->b_proxy = true; + vlc_UrlParse( &p_sys->proxy, http_proxy, 0 ); + } } - NetClose( p_input, p_sys->p_socket ); +#endif - p_ans = http_answer_parse( p_sys->buffer, p_sys->i_buffer ); - if( !p_ans ) + if( p_sys->b_proxy ) { - msg_Err( p_input, "cannot parse answer" ); - goto exit_error; - } + if( ( p_sys->proxy.psz_host == NULL ) || + ( *p_sys->proxy.psz_host == '\0' ) ) + { + msg_Warn( p_access, "invalid proxy host" ); + vlc_UrlClean( &p_sys->proxy ); + free( p_sys ); + return VLC_EGENERIC; + } - if( p_ans->i_error >= 400 ) - { - msg_Err( p_input, "error %d (server return=`%s')", p_ans->i_error, p_ans->psz_answer ); - http_answer_free( p_ans ); - goto exit_error; - } - else if( p_ans->i_error >= 300 ) - { - msg_Err( p_input, "FIXME redirec unsuported %d (server return=`%s')", p_ans->i_error, p_ans->psz_answer ); - http_answer_free( p_ans ); - goto exit_error; - } - else if( p_ans->i_body <= 0 ) - { - msg_Err( p_input, "empty answer" ); - http_answer_free( p_ans ); - goto exit_error; + if( p_sys->proxy.i_port <= 0 ) + p_sys->proxy.i_port = 80; + msg_Dbg( p_access, "Using http proxy %s:%d", + p_sys->proxy.psz_host, p_sys->proxy.i_port ); } - /* now get features */ - /* FIXME FIXME test Content-Type to see if it's a plain stream or an asx FIXME */ - for( p_field = p_ans->p_fields; p_field != NULL; p_field = http_field_find( p_field->p_next, "Pragma" ) ) + /* open a tcp connection */ + vlc_UrlParse( &p_sys->url, p_access->psz_path, 0 ); + if( ( p_sys->url.psz_host == NULL ) || + ( *p_sys->url.psz_host == '\0' ) ) { - if( !strncasecmp( p_field->psz_value, "features", 8 ) ) - { - if( strstr( p_field->psz_value, "broadcast" ) ) - { - msg_Dbg( p_input, "stream type = broadcast" ); - p_sys->b_broadcast = VLC_TRUE; - } - else if( strstr( p_field->psz_value, "seekable" ) ) - { - msg_Dbg( p_input, "stream type = seekable" ); - p_sys->b_broadcast = VLC_FALSE; - } - else - { - msg_Warn( p_input, "unknow stream types (%s)", p_field->psz_value ); - p_sys->b_broadcast = VLC_FALSE; - } - } + msg_Err( p_access, "invalid host" ); + goto error; } + if( p_sys->url.i_port <= 0 ) + p_sys->url.i_port = 80; - /* gather header */ - p_sys->i_header = 0; - p_sys->p_header = malloc( p_ans->i_body ); - do + if( Describe( p_access, &psz_location ) ) + goto error; + + /* Handle redirection */ + if( psz_location && *psz_location ) { - if( chunk_parse( &ck, p_ans->p_body, p_ans->i_body ) ) - { - msg_Err( p_input, "invalid chunk answer" ); - goto exit_error; - } - if( ck.i_type != 0x4824 ) - { - msg_Err( p_input, "invalid chunk (0x%x)", ck.i_type ); - break; - } - if( ck.i_data > 0 ) - { - memcpy( &p_sys->p_header[p_sys->i_header], - ck.p_data, - ck.i_data ); + msg_Dbg( p_access, "redirection to %s", psz_location ); - p_sys->i_header += ck.i_data; - } + input_thread_t * p_input = access_GetParentInput( p_access ); + input_item_t * p_new_loc; - /* BEURK */ - p_ans->p_body += 12 + ck.i_data; - p_ans->i_body -= 12 + ck.i_data; + if( !p_input ) + { + free( psz_location ); + goto error; + } + /** \bug we do not autodelete here */ + p_new_loc = input_item_New( p_access, psz_location, psz_location ); + input_item_t *p_item = input_GetItem( p_input ); + input_item_AddSubItem( p_item, p_new_loc ); + input_item_AddSubItem2( p_item, p_new_loc ); - } while( p_ans->i_body > 12 ); + vlc_gc_decref( p_new_loc ); + vlc_object_release( p_input ); - http_answer_free( p_ans ); + free( psz_location ); - msg_Dbg( p_input, "complete header size=%d", p_sys->i_header ); - if( p_sys->i_header <= 0 ) - { - msg_Err( p_input, "header size == 0" ); - goto exit_error; + p_access->pf_block = NULL; + p_access->pf_read = ReadRedirect; + return VLC_SUCCESS; } - /* *** parse header and get stream and their id *** */ - /* get all streams properties, - * - * TODO : stream bitrates properties(optional) - * and bitrate mutual exclusion(optional) */ - E_( asf_HeaderParse )( &p_sys->asfh, - p_sys->p_header, p_sys->i_header ); - msg_Dbg( p_input, "packet count=%lld packet size=%d",p_sys->asfh.i_data_packets_count, p_sys->asfh.i_min_data_packet_size ); + free( psz_location ); - E_( asf_StreamSelect)( &p_sys->asfh, - config_GetInt( p_input, "mms-maxbitrate" ), - config_GetInt( p_input, "mms-all" ), - config_GetInt( p_input, "audio" ), - config_GetInt( p_input, "video" ) ); - - if( mmsh_start( p_input, p_sys, 0 ) ) + /* Start playing */ + if( Start( p_access, 0 ) ) { - msg_Err( p_input, "cannot start stream" ); - goto exit_error; + msg_Err( p_access, "cannot start stream" ); + free( p_sys->p_header ); + goto error; } - /* *** set exported functions *** */ - p_input->pf_read = Read; - p_input->pf_seek = Seek; - p_input->pf_set_program = input_SetProgram; - p_input->pf_set_area = NULL; - - p_input->p_private = NULL; - p_input->i_mtu = 3 * p_sys->asfh.i_min_data_packet_size; - - /* *** finished to set some variable *** */ - vlc_mutex_lock( &p_input->stream.stream_lock ); - p_input->stream.b_pace_control = 0; - if( p_sys->b_broadcast ) - { - p_input->stream.p_selected_area->i_size = 0; - p_input->stream.b_seekable = 0; - } - else + if( !p_sys->b_broadcast ) { - p_input->stream.p_selected_area->i_size = p_sys->asfh.i_file_size; - p_input->stream.b_seekable = 1; + p_access->info.i_size = p_sys->asfh.i_file_size; } - p_input->stream.p_selected_area->i_tell = 0; - p_input->stream.i_method = INPUT_METHOD_NETWORK; - vlc_mutex_unlock( &p_input->stream.stream_lock ); - - /* Update default_pts to a suitable value for ftp access */ - p_input->i_pts_delay = config_GetInt( p_input, "mms-caching" ) * 1000; - - p_input->p_access_data = p_sys; - - return( VLC_SUCCESS ); -exit_error: - E_( url_free )( p_sys->p_url ); + return VLC_SUCCESS; - if( p_sys->p_socket ) - { - NetClose( p_input, p_sys->p_socket ); - } +error: + vlc_UrlClean( &p_sys->proxy ); + vlc_UrlClean( &p_sys->url ); free( p_sys ); - return( VLC_EGENERIC ); + return VLC_EGENERIC; } /***************************************************************************** * Close: free unused data structures *****************************************************************************/ -void E_( MMSHClose ) ( input_thread_t *p_input ) +void MMSHClose ( access_t *p_access ) { - access_sys_t *p_sys = p_input->p_access_data; + access_sys_t *p_sys = p_access->p_sys; - msg_Dbg( p_input, "stopping stream" ); + Stop( p_access ); - mmsh_stop( p_input, p_sys ); + free( p_sys->p_header ); + vlc_UrlClean( &p_sys->proxy ); + vlc_UrlClean( &p_sys->url ); free( p_sys ); } -static int mmsh_get_packet( input_thread_t * p_input, access_sys_t *p_sys, chunk_t *p_ck ) +/***************************************************************************** + * Control: + *****************************************************************************/ +static int Control( access_t *p_access, int i_query, va_list args ) { - int i_mov = p_sys->i_buffer - p_sys->i_buffer_pos; + access_sys_t *p_sys = p_access->p_sys; + bool *pb_bool; + bool b_bool; + int64_t *pi_64; + int i_int; + + switch( i_query ) + { + /* */ + case ACCESS_CAN_SEEK: + pb_bool = (bool*)va_arg( args, bool* ); + *pb_bool = !p_sys->b_broadcast; + break; - if( p_sys->i_buffer_pos > BUFFER_SIZE / 2 ) - { - if( i_mov > 0 ) - { - memmove( &p_sys->buffer[0], - &p_sys->buffer[p_sys->i_buffer_pos], - i_mov ); - } + case ACCESS_CAN_FASTSEEK: + pb_bool = (bool*)va_arg( args, bool* ); + *pb_bool = false; + break; - p_sys->i_buffer = i_mov; - p_sys->i_buffer_pos = 0; - } + case ACCESS_CAN_PAUSE: + case ACCESS_CAN_CONTROL_PACE: + pb_bool = (bool*)va_arg( args, bool* ); + *pb_bool = true; + break; - if( NetFill( p_input, p_sys, 12 ) < 12 ) - { - msg_Warn( p_input, "cannot fill buffer" ); - return VLC_EGENERIC; - } + /* */ + case ACCESS_GET_PTS_DELAY: + pi_64 = (int64_t*)va_arg( args, int64_t * ); + *pi_64 = (int64_t)var_GetInteger( p_access, "mms-caching" ) * INT64_C(1000); + break; - chunk_parse( p_ck, &p_sys->buffer[p_sys->i_buffer_pos], p_sys->i_buffer - p_sys->i_buffer_pos ); + case ACCESS_GET_PRIVATE_ID_STATE: + i_int = (int)va_arg( args, int ); + pb_bool = (bool *)va_arg( args, bool * ); - if( p_ck->i_type == 0x4524 ) // Transfer complete - { - msg_Warn( p_input, "EOF" ); - return VLC_EGENERIC; - } - else if( p_ck->i_type != 0x4824 && p_ck->i_type != 0x4424 ) - { - msg_Err( p_input, "invalid chunk FATAL" ); - return VLC_EGENERIC; - } - - if( p_ck->i_data < p_ck->i_size2 - 8 ) - { - if( NetFill( p_input, p_sys, p_ck->i_size2 - 8 - p_ck->i_data ) <= 0 ) - { - msg_Warn( p_input, "cannot fill buffer" ); - return VLC_EGENERIC; - } - chunk_parse( p_ck, &p_sys->buffer[p_sys->i_buffer_pos], p_sys->i_buffer - p_sys->i_buffer_pos ); - } + if( (i_int < 0) || (i_int > 127) ) + return VLC_EGENERIC; + *pb_bool = p_sys->asfh.stream[i_int].i_selected ? true : false; + break; - if( p_sys->i_packet_sequence != 0 && p_ck->i_sequence != p_sys->i_packet_sequence ) - { - msg_Warn( p_input, "packet lost ?" ); - } + /* */ + case ACCESS_SET_PAUSE_STATE: + b_bool = (bool)va_arg( args, int ); + if( b_bool ) + Stop( p_access ); + else + Seek( p_access, p_access->info.i_pos ); + break; - p_sys->i_packet_sequence = p_ck->i_sequence + 1; - p_sys->i_packet_used = 0; - p_sys->i_packet_length = p_ck->i_data; - p_sys->p_packet = p_ck->p_data; + case ACCESS_GET_TITLE_INFO: + case ACCESS_SET_TITLE: + case ACCESS_SET_SEEKPOINT: + case ACCESS_SET_PRIVATE_ID_STATE: + case ACCESS_GET_CONTENT_TYPE: + return VLC_EGENERIC; - p_sys->i_buffer_pos += 12 + p_ck->i_data; + default: + msg_Warn( p_access, "unimplemented query in control" ); + return VLC_EGENERIC; + } return VLC_SUCCESS; } - /***************************************************************************** * Seek: try to go at the right place *****************************************************************************/ -static void Seek( input_thread_t * p_input, off_t i_pos ) +static int Seek( access_t *p_access, uint64_t i_pos ) { - access_sys_t *p_sys = p_input->p_access_data; + access_sys_t *p_sys = p_access->p_sys; chunk_t ck; - off_t i_offset; - off_t i_packet; + uint64_t i_offset; + uint64_t i_packet; + + msg_Dbg( p_access, "seeking to %"PRId64, i_pos ); i_packet = ( i_pos - p_sys->i_header ) / p_sys->asfh.i_min_data_packet_size; i_offset = ( i_pos - p_sys->i_header ) % p_sys->asfh.i_min_data_packet_size; - msg_Err( p_input, "seeking to "I64Fd, i_pos ); + Stop( p_access ); + Start( p_access, i_packet * p_sys->asfh.i_min_data_packet_size ); - vlc_mutex_lock( &p_input->stream.stream_lock ); - - mmsh_stop( p_input, p_sys ); - mmsh_start( p_input, p_sys, i_packet * p_sys->asfh.i_min_data_packet_size ); - - for( ;; ) + while( vlc_object_alive (p_access) ) { - if( mmsh_get_packet( p_input, p_sys, &ck ) ) - { + if( GetPacket( p_access, &ck ) ) break; - } /* skip headers */ if( ck.i_type != 0x4824 ) - { break; - } - msg_Warn( p_input, "skipping header" ); + + msg_Warn( p_access, "skipping header" ); } - p_sys->i_pos = i_pos; + p_access->info.i_pos = i_pos; + p_access->info.b_eof = false; p_sys->i_packet_used += i_offset; + return VLC_SUCCESS; +} - p_input->stream.p_selected_area->i_tell = i_pos; - vlc_mutex_unlock( &p_input->stream.stream_lock ); - +/***************************************************************************** + * ReadRedirect: + *****************************************************************************/ +static ssize_t ReadRedirect( access_t *p_access, uint8_t *p, size_t i_len ) +{ + VLC_UNUSED(p_access); VLC_UNUSED(p); VLC_UNUSED(i_len); + return 0; } /***************************************************************************** - * Read: + * Block: *****************************************************************************/ -static int Read ( input_thread_t * p_input, byte_t * p_buffer, - size_t i_len ) +static block_t *Block( access_t *p_access ) { - access_sys_t *p_sys = p_input->p_access_data; - size_t i_copy; - size_t i_data = 0; + access_sys_t *p_sys = p_access->p_sys; + const unsigned i_packet_min = p_sys->asfh.i_min_data_packet_size; + + if( p_access->info.i_pos < p_sys->i_start + p_sys->i_header ) + { + const size_t i_offset = p_access->info.i_pos - p_sys->i_start; + const size_t i_copy = p_sys->i_header - i_offset; - while( i_data < i_len ) + block_t *p_block = block_New( p_access, i_copy ); + if( !p_block ) + return NULL; + + memcpy( p_block->p_buffer, &p_sys->p_header[i_offset], i_copy ); + p_access->info.i_pos += i_copy; + return p_block; + } + else if( p_sys->i_packet_length > 0 && + p_sys->i_packet_used < __MAX( p_sys->i_packet_length, i_packet_min ) ) { + size_t i_copy = 0; + size_t i_padding = 0; + if( p_sys->i_packet_used < p_sys->i_packet_length ) - { - i_copy = __MIN( p_sys->i_packet_length - p_sys->i_packet_used, i_len - i_data ); + i_copy = p_sys->i_packet_length - p_sys->i_packet_used; + if( __MAX( p_sys->i_packet_used, p_sys->i_packet_length ) < i_packet_min ) + i_padding = i_packet_min - __MAX( p_sys->i_packet_used, p_sys->i_packet_length ); - memcpy( &p_buffer[i_data], - &p_sys->p_packet[p_sys->i_packet_used], - i_copy ); + block_t *p_block = block_New( p_access, i_copy + i_padding ); + if( !p_block ) + return NULL; - i_data += i_copy; - p_sys->i_packet_used += i_copy; - } - else if( p_sys->i_pos + i_data > p_sys->i_header && - (int)p_sys->i_packet_used < p_sys->asfh.i_min_data_packet_size ) - { - i_copy = __MIN( p_sys->asfh.i_min_data_packet_size - p_sys->i_packet_used, i_len - i_data ); + if( i_copy > 0 ) + memcpy( &p_block->p_buffer[0], &p_sys->p_packet[p_sys->i_packet_used], i_copy ); + if( i_padding > 0 ) + memset( &p_block->p_buffer[i_copy], 0, i_padding ); - memset( &p_buffer[i_data], 0, i_copy ); + p_sys->i_packet_used += i_copy + i_padding; + p_access->info.i_pos += i_copy + i_padding; + return p_block; - i_data += i_copy; - p_sys->i_packet_used += i_copy; + } + + chunk_t ck; + if( GetPacket( p_access, &ck ) ) + { + int i_ret = -1; + if( p_sys->b_broadcast ) + { + if( (ck.i_type == 0x4524) && (ck.i_sequence != 0) ) + i_ret = Restart( p_access ); + else if( ck.i_type == 0x4324 ) + i_ret = Reset( p_access ); } - else + if( i_ret ) { - chunk_t ck; - /* get a new packet */ - /* fill enought data (>12) */ - msg_Dbg( p_input, "waiting data (buffer = %d bytes", p_sys->i_buffer ); - - if( mmsh_get_packet( p_input, p_sys, &ck ) ) - { - return 0; - } - - //fprintf( stderr, "type=0x%x size=%d sequence=%d unknown=%d size2=%d data=%d\n", - // ck.i_type, ck.i_size, ck.i_sequence, ck.i_unknown, ck.i_size2, ck.i_data ); + p_access->info.b_eof = true; + return 0; } } + if( ck.i_type != 0x4424 ) + { + p_sys->i_packet_used = 0; + p_sys->i_packet_length = 0; + } - p_sys->i_pos += i_data; - - - return( i_data ); + return NULL; } - -/****************************************************************************/ -/****************************************************************************/ -/****************************************************************************/ -/****************************************************************************/ -/****************************************************************************/ - -static int mmsh_start( input_thread_t *p_input, access_sys_t *p_sys, off_t i_pos ) +/* */ +static int Restart( access_t *p_access ) { - uint8_t *p; - int i_streams = 0; - int i; - http_answer_t *p_ans; + access_sys_t *p_sys = p_access->p_sys; + char *psz_location = NULL; - msg_Dbg( p_input, "starting stream" ); + msg_Dbg( p_access, "Restart the stream" ); + p_sys->i_start = p_access->info.i_pos; - p_sys->p_socket = NetOpenTCP( p_input, p_sys->p_url ); + /* */ + msg_Dbg( p_access, "stoping the stream" ); + Stop( p_access ); - for( i = 1; i < 128; i++ ) + /* */ + msg_Dbg( p_access, "describe the stream" ); + if( Describe( p_access, &psz_location ) ) { - if( p_sys->asfh.stream[i].i_selected ) - { - i_streams++; - } - } - - if( i_streams <= 0 ) - { - msg_Err( p_input, "no stream selected" ); + msg_Err( p_access, "describe failed" ); return VLC_EGENERIC; } + free( psz_location ); - p = &p_sys->buffer[0]; - p += sprintf( p, "GET %s HTTP/1.0\r\n", p_sys->p_url->psz_path ); - p += sprintf( p,"Accept: */*\r\n" ); - p += sprintf( p, "User-Agent: NSPlayer/4.1.0.3856\r\n" ); - p += sprintf( p, "Host: %s:%d\r\n", p_sys->p_url->psz_host, p_sys->p_url->i_port ); - if( p_sys->b_broadcast ) - { - p += sprintf( p, "Pragma: no-cache,rate=1.000000,request-context=%d\r\n", p_sys->i_request_context++ ); - } - else + /* */ + if( Start( p_access, 0 ) ) { - p += sprintf( p, "Pragma: no-cache,rate=1.000000,stream-time=0,stream-offset=%u:%u,request-context=%d,max-duration=0\r\n", - (uint32_t)((i_pos >> 32)&0xffffffff), (uint32_t)(i_pos&0xffffffff), p_sys->i_request_context++ ); - } - p += sprintf( p, "Pragma: xPlayStrm=1\r\n" ); - //p += sprintf( p, "Pragma: xClientGUID={c77e7400-738a-11d2-9add-0020af0a3278}\r\n" ); - p += sprintf( p, "Pragma: xClientGUID={"GUID_FMT"}\r\n", GUID_PRINT( p_sys->guid ) ); - p += sprintf( p, "Pragma: stream-switch-count=%d\r\n", i_streams ); - p += sprintf( p, "Pragma: stream-switch-entry=" ); - for( i = 0; i < i_streams; i++ ) - { - if( p_sys->asfh.stream[i].i_selected ) - { - p += sprintf( p, "ffff:%d:0 ", p_sys->asfh.stream[i].i_id ); - } - else - { - p += sprintf( p, "ffff:%d:2 ", p_sys->asfh.stream[i].i_id ); - } + msg_Err( p_access, "Start failed" ); + return VLC_EGENERIC; } - p += sprintf( p, "\r\n" ); - p += sprintf( p, "Connection: Close\r\n\r\n" ); + return VLC_SUCCESS; +} +static int Reset( access_t *p_access ) +{ + access_sys_t *p_sys = p_access->p_sys; + asf_header_t old_asfh = p_sys->asfh; + int i; + msg_Dbg( p_access, "Reset the stream" ); + p_sys->i_start = p_access->info.i_pos; - NetWrite( p_input, p_sys->p_socket, p_sys->buffer, p - p_sys->buffer ); + /* */ + p_sys->i_packet_sequence = 0; + p_sys->i_packet_used = 0; + p_sys->i_packet_length = 0; + p_sys->p_packet = NULL; - msg_Dbg( p_input, "filling buffer" ); - /* we read until we found a \r\n\r\n or \n\n */ - p_sys->i_buffer = 0; - p_sys->i_buffer_pos = 0; - for( ;; ) - { - int i_try = 0; - int i_read; - uint8_t *p; + /* Get the next header FIXME memory loss ? */ + GetHeader( p_access ); + if( p_sys->i_header <= 0 ) + return VLC_EGENERIC; - p = &p_sys->buffer[p_sys->i_buffer]; - i_read = - NetRead( p_input, p_sys->p_socket, - &p_sys->buffer[p_sys->i_buffer], - 1024 ); + asf_HeaderParse ( &p_sys->asfh, + p_sys->p_header, p_sys->i_header ); + msg_Dbg( p_access, "packet count=%"PRId64" packet size=%d", + p_sys->asfh.i_data_packets_count, + p_sys->asfh.i_min_data_packet_size ); - if( i_read == 0 ) - { - if( i_try++ > 12 ) - { - break; - } - msg_Dbg( p_input, "another try (%d/12)", i_try ); - continue; - } + asf_StreamSelect( &p_sys->asfh, + var_CreateGetInteger( p_access, "mms-maxbitrate" ), + var_CreateGetBool( p_access, "mms-all" ), + var_CreateGetInteger( p_access, "audio" ), + var_CreateGetInteger( p_access, "video" ) ); - if( i_read <= 0 || p_input->b_die || p_input->b_error ) - { - break; - } - p_sys->i_buffer += i_read; - p_sys->buffer[p_sys->i_buffer] = '\0'; + /* Check we have comptible asfh */ + for( i = 1; i < 128; i++ ) + { + asf_stream_t *p_old = &old_asfh.stream[i]; + asf_stream_t *p_new = &p_sys->asfh.stream[i]; - if( strstr( p, "\r\n\r\n" ) || strstr( p, "\n\n" ) ) - { - msg_Dbg( p_input, "body found" ); + if( p_old->i_cat != p_new->i_cat || p_old->i_selected != p_new->i_selected ) break; - } - if( p_sys->i_buffer >= BUFFER_SIZE - 1024 ) - { - msg_Dbg( p_input, "buffer size exeded" ); - break; - } } - - p_ans = http_answer_parse( p_sys->buffer, p_sys->i_buffer ); - if( !p_ans ) + if( i < 128 ) { - msg_Err( p_input, "cannot parse answer" ); - return VLC_EGENERIC; + msg_Warn( p_access, "incompatible asf header, restart" ); + return Restart( p_access ); } - if( p_ans->i_error < 200 || p_ans->i_error >= 300 ) + /* */ + p_sys->i_packet_used = 0; + p_sys->i_packet_length = 0; + return VLC_SUCCESS; +} + +static int OpenConnection( access_t *p_access ) +{ + access_sys_t *p_sys = p_access->p_sys; + vlc_url_t srv = p_sys->b_proxy ? p_sys->proxy : p_sys->url; + + if( ( p_sys->fd = net_ConnectTCP( p_access, + srv.psz_host, srv.i_port ) ) < 0 ) { - msg_Err( p_input, "error %d (server return=`%s')", p_ans->i_error, p_ans->psz_answer ); - http_answer_free( p_ans ); + msg_Err( p_access, "cannot connect to %s:%d", + srv.psz_host, srv.i_port ); return VLC_EGENERIC; } - if( !p_ans->p_body ) + if( p_sys->b_proxy ) { - p_sys->i_buffer_pos = 0; - p_sys->i_buffer = 0; + net_Printf( VLC_OBJECT(p_access), p_sys->fd, NULL, + "GET http://%s:%d%s HTTP/1.0\r\n", + p_sys->url.psz_host, p_sys->url.i_port, + ( (p_sys->url.psz_path == NULL) || + (*p_sys->url.psz_path == '\0') ) ? + "/" : p_sys->url.psz_path ); + + /* Proxy Authentication */ + if( p_sys->proxy.psz_username && *p_sys->proxy.psz_username ) + { + char *buf; + char *b64; + + if( asprintf( &buf, "%s:%s", p_sys->proxy.psz_username, + p_sys->proxy.psz_password ? p_sys->proxy.psz_password : "" ) == -1 ) + return VLC_ENOMEM; + + b64 = vlc_b64_encode( buf ); + free( buf ); + + net_Printf( VLC_OBJECT(p_access), p_sys->fd, NULL, + "Proxy-Authorization: Basic %s\r\n", b64 ); + free( b64 ); + } } else { - p_sys->i_buffer_pos = p_ans->p_body - p_sys->buffer; + net_Printf( VLC_OBJECT(p_access), p_sys->fd, NULL, + "GET %s HTTP/1.0\r\n" + "Host: %s:%d\r\n", + ( (p_sys->url.psz_path == NULL) || + (*p_sys->url.psz_path == '\0') ) ? + "/" : p_sys->url.psz_path, + p_sys->url.psz_host, p_sys->url.i_port ); } - http_answer_free( p_ans ); - return VLC_SUCCESS; } -static void mmsh_stop( input_thread_t *p_input, access_sys_t *p_sys ) +/***************************************************************************** + * Describe: + *****************************************************************************/ +static int Describe( access_t *p_access, char **ppsz_location ) { - msg_Dbg( p_input, "closing stream" ); - NetClose( p_input, p_sys->p_socket ); -} + access_sys_t *p_sys = p_access->p_sys; + char *psz_location = NULL; + char *psz; + int i_code; -static ssize_t NetFill( input_thread_t *p_input, - access_sys_t *p_sys, int i_size ) -{ - int i_try = 0; - int i_total = 0; + /* Reinit context */ + p_sys->b_broadcast = true; + p_sys->i_request_context = 1; + p_sys->i_packet_sequence = 0; + p_sys->i_packet_used = 0; + p_sys->i_packet_length = 0; + p_sys->p_packet = NULL; + + GenerateGuid ( &p_sys->guid ); + + if( OpenConnection( p_access ) ) + return VLC_EGENERIC; - i_size = __MIN( i_size, BUFFER_SIZE - p_sys->i_buffer ); - if( i_size <= 0 ) + net_Printf( VLC_OBJECT(p_access), p_sys->fd, NULL, + "Accept: */*\r\n" + "User-Agent: "MMSH_USER_AGENT"\r\n" + "Pragma: no-cache,rate=1.000000,stream-time=0,stream-offset=0:0,request-context=%d,max-duration=0\r\n" + "Pragma: xClientGUID={"GUID_FMT"}\r\n" + "Connection: Close\r\n", + p_sys->i_request_context++, + GUID_PRINT( p_sys->guid ) ); + + if( net_Printf( VLC_OBJECT(p_access), p_sys->fd, NULL, "\r\n" ) < 0 ) { - return 0; + msg_Err( p_access, "failed to send request" ); + goto error; } - - for( ;; ) + /* Receive the http header */ + if( ( psz = net_Gets( p_access, p_sys->fd, NULL ) ) == NULL ) { - int i_read; + msg_Err( p_access, "failed to read answer" ); + goto error; + } - i_read = - NetRead( p_input, p_sys->p_socket, &p_sys->buffer[p_sys->i_buffer], i_size ); + if( strncmp( psz, "HTTP/1.", 7 ) ) + { + msg_Err( p_access, "invalid HTTP reply '%s'", psz ); + free( psz ); + goto error; + } + i_code = atoi( &psz[9] ); + if( i_code >= 400 ) + { + msg_Err( p_access, "error: %s", psz ); + free( psz ); + goto error; + } - if( i_read == 0 ) + msg_Dbg( p_access, "HTTP reply '%s'", psz ); + free( psz ); + for( ;; ) + { + char *psz = net_Gets( p_access, p_sys->fd, NULL ); + char *p; + + if( psz == NULL ) { - if( i_try++ > 2 ) - { - break; - } - msg_Dbg( p_input, "another try %d/2", i_try ); - continue; + msg_Err( p_access, "failed to read answer" ); + goto error; } - if( i_read < 0 || p_input->b_die || p_input->b_error ) + if( *psz == '\0' ) { + free( psz ); break; } - i_total += i_read; - p_sys->i_buffer += i_read; - if( i_total >= i_size ) + if( ( p = strchr( psz, ':' ) ) == NULL ) { - break; + msg_Err( p_access, "malformed header line: %s", psz ); + free( psz ); + goto error; } - } - - p_sys->buffer[p_sys->i_buffer] = '\0'; - - return i_total; -} - -/**************************************************************************** - * NetOpenTCP: - ****************************************************************************/ -static input_socket_t * NetOpenTCP( input_thread_t *p_input, url_t *p_url ) -{ - input_socket_t *p_socket; - char *psz_network; - module_t *p_network; - network_socket_t socket_desc; + *p++ = '\0'; + while( *p == ' ' ) p++; + /* FIXME FIXME test Content-Type to see if it's a plain stream or an + * asx FIXME */ + if( !strcasecmp( psz, "Pragma" ) ) + { + if( strstr( p, "features" ) ) + { + /* FIXME, it is a bit badly done here ..... */ + if( strstr( p, "broadcast" ) ) + { + msg_Dbg( p_access, "stream type = broadcast" ); + p_sys->b_broadcast = true; + } + else if( strstr( p, "seekable" ) ) + { + msg_Dbg( p_access, "stream type = seekable" ); + p_sys->b_broadcast = false; + } + else + { + msg_Warn( p_access, "unknow stream types (%s)", p ); + p_sys->b_broadcast = false; + } + } + } + else if( !strcasecmp( psz, "Location" ) ) + { + psz_location = strdup( p ); + } - p_socket = malloc( sizeof( input_socket_t ) ); - memset( p_socket, 0, sizeof( input_socket_t ) ); + free( psz ); + } - psz_network = ""; - if( config_GetInt( p_input, "ipv4" ) ) + /* Handle the redirection */ + if( ( (i_code == 301) || (i_code == 302) || + (i_code == 303) || (i_code == 307) ) && + psz_location && *psz_location ) { - psz_network = "ipv4"; + msg_Dbg( p_access, "redirection to %s", psz_location ); + net_Close( p_sys->fd ); p_sys->fd = -1; + + *ppsz_location = psz_location; + return VLC_SUCCESS; } - else if( config_GetInt( p_input, "ipv6" ) ) + free( psz_location ); + + /* Read the asf header */ + GetHeader( p_access ); + if( p_sys->i_header <= 0 ) { - psz_network = "ipv6"; + msg_Err( p_access, "header size == 0" ); + goto error; } + /* close this connection */ + net_Close( p_sys->fd ); + p_sys->fd = -1; - msg_Dbg( p_input, "waiting for connection..." ); + /* *** parse header and get stream and their id *** */ + /* get all streams properties, + * + * TODO : stream bitrates properties(optional) + * and bitrate mutual exclusion(optional) */ + asf_HeaderParse ( &p_sys->asfh, + p_sys->p_header, p_sys->i_header ); + msg_Dbg( p_access, "packet count=%"PRId64" packet size=%d", + p_sys->asfh.i_data_packets_count, + p_sys->asfh.i_min_data_packet_size ); + + if( p_sys->asfh.i_min_data_packet_size <= 0 ) + goto error; + + asf_StreamSelect( &p_sys->asfh, + var_CreateGetInteger( p_access, "mms-maxbitrate" ), + var_CreateGetBool( p_access, "mms-all" ), + var_CreateGetInteger( p_access, "audio" ), + var_CreateGetInteger( p_access, "video" ) ); + return VLC_SUCCESS; - socket_desc.i_type = NETWORK_TCP; - socket_desc.psz_server_addr = p_url->psz_host; - socket_desc.i_server_port = p_url->i_port; - socket_desc.psz_bind_addr = ""; - socket_desc.i_bind_port = 0; - p_input->p_private = (void*)&socket_desc; - if( !( p_network = module_Need( p_input, "network", psz_network ) ) ) +error: + if( p_sys->fd > 0 ) { - msg_Err( p_input, "failed to connect with server" ); - return NULL; + net_Close( p_sys->fd ); + p_sys->fd = -1; } - module_Unneed( p_input, p_network ); - p_socket->i_handle = socket_desc.i_handle; - p_input->i_mtu = socket_desc.i_mtu; - - msg_Dbg( p_input, - "connection with \"%s:%d\" successful", - p_url->psz_host, - p_url->i_port ); - - return p_socket; + return VLC_EGENERIC; } -/***************************************************************************** - * Read: read on a file descriptor, checking b_die periodically - *****************************************************************************/ -static ssize_t NetRead( input_thread_t *p_input, - input_socket_t *p_socket, - byte_t *p_buffer, size_t i_len ) +static void GetHeader( access_t *p_access ) { - struct timeval timeout; - fd_set fds; - ssize_t i_recv; - int i_ret; - - /* Initialize file descriptor set */ - FD_ZERO( &fds ); - FD_SET( p_socket->i_handle, &fds ); - - /* We'll wait 1 second if nothing happens */ - timeout.tv_sec = 1; - timeout.tv_usec = 0; - - /* Find if some data is available */ - while( ( i_ret = select( p_socket->i_handle + 1, &fds, - NULL, NULL, &timeout )) == 0 || -#ifdef HAVE_ERRNO_H - ( i_ret < 0 && errno == EINTR ) -#endif - ) + access_sys_t *p_sys = p_access->p_sys; + + /* Read the asf header */ + p_sys->i_header = 0; + free( p_sys->p_header ); + p_sys->p_header = NULL; + for( ;; ) { - FD_ZERO( &fds ); - FD_SET( p_socket->i_handle, &fds ); - timeout.tv_sec = 1; - timeout.tv_usec = 0; + chunk_t ck; + if( GetPacket( p_access, &ck ) || ck.i_type != 0x4824 ) + break; - if( p_input->b_die || p_input->b_error ) + if( ck.i_data > 0 ) { - return 0; + p_sys->i_header += ck.i_data; + p_sys->p_header = xrealloc( p_sys->p_header, p_sys->i_header ); + memcpy( &p_sys->p_header[p_sys->i_header - ck.i_data], + ck.p_data, ck.i_data ); } } + msg_Dbg( p_access, "complete header size=%d", p_sys->i_header ); +} - if( i_ret < 0 ) - { - msg_Err( p_input, "network select error (%s)", strerror(errno) ); - return -1; - } - - i_recv = recv( p_socket->i_handle, p_buffer, i_len, 0 ); - if( i_recv < 0 ) - { - msg_Err( p_input, "recv failed (%s)", strerror(errno) ); - } +/***************************************************************************** + * Start stream + ****************************************************************************/ +static int Start( access_t *p_access, uint64_t i_pos ) +{ + access_sys_t *p_sys = p_access->p_sys; + int i_streams = 0; + int i_streams_selected = 0; + int i; + char *psz = NULL; - return i_recv; -} + msg_Dbg( p_access, "starting stream" ); -static ssize_t NetWrite( input_thread_t *p_input, - input_socket_t *p_socket, - byte_t *p_buffer, size_t i_len ) -{ - struct timeval timeout; - fd_set fds; - ssize_t i_send; - int i_ret; - - /* Initialize file descriptor set */ - FD_ZERO( &fds ); - FD_SET( p_socket->i_handle, &fds ); - - /* We'll wait 1 second if nothing happens */ - timeout.tv_sec = 1; - timeout.tv_usec = 0; - - /* Find if some data is available */ - while( ( i_ret = select( p_socket->i_handle + 1, NULL, &fds, NULL, &timeout ) ) == 0 || -#ifdef HAVE_ERRNO_H - ( i_ret < 0 && errno == EINTR ) -#endif - ) + for( i = 1; i < 128; i++ ) { - FD_ZERO( &fds ); - FD_SET( p_socket->i_handle, &fds ); - timeout.tv_sec = 1; - timeout.tv_usec = 0; - - if( p_input->b_die || p_input->b_error ) - { - return 0; - } + if( p_sys->asfh.stream[i].i_cat == ASF_STREAM_UNKNOWN ) + continue; + i_streams++; + if( p_sys->asfh.stream[i].i_selected ) + i_streams_selected++; } - - if( i_ret < 0 ) + if( i_streams_selected <= 0 ) { - msg_Err( p_input, "network select error (%s)", strerror(errno) ); - return -1; + msg_Err( p_access, "no stream selected" ); + return VLC_EGENERIC; } - i_send = send( p_socket->i_handle, p_buffer, i_len, 0 ); + if( OpenConnection( p_access ) ) + return VLC_EGENERIC; - if( i_send < 0 ) + net_Printf( VLC_OBJECT(p_access), p_sys->fd, NULL, + "Accept: */*\r\n" + "User-Agent: "MMSH_USER_AGENT"\r\n" ); + if( p_sys->b_broadcast ) { - msg_Err( p_input, "send failed (%s)", strerror(errno) ); + net_Printf( VLC_OBJECT(p_access), p_sys->fd, NULL, + "Pragma: no-cache,rate=1.000000,request-context=%d\r\n", + p_sys->i_request_context++ ); } + else + { + net_Printf( VLC_OBJECT(p_access), p_sys->fd, NULL, + "Pragma: no-cache,rate=1.000000,stream-time=0,stream-offset=%u:%u,request-context=%d,max-duration=0\r\n", + (uint32_t)((i_pos >> 32)&0xffffffff), + (uint32_t)(i_pos&0xffffffff), + p_sys->i_request_context++ ); + } + net_Printf( VLC_OBJECT(p_access), p_sys->fd, NULL, + "Pragma: xPlayStrm=1\r\n" + "Pragma: xClientGUID={"GUID_FMT"}\r\n" + "Pragma: stream-switch-count=%d\r\n" + "Pragma: stream-switch-entry=", + GUID_PRINT( p_sys->guid ), + i_streams); - return i_send; -} - -static void NetClose( input_thread_t *p_input, input_socket_t *p_socket ) -{ -#if defined( WIN32 ) || defined( UNDER_CE ) - closesocket( p_socket->i_handle ); -#else - close( p_socket->i_handle ); -#endif - - free( p_socket ); -} - -static int http_next_line( uint8_t **pp_data, int *pi_data ) -{ - char *p, *p_end = *pp_data + *pi_data; - - for( p = *pp_data; p < p_end; p++ ) + for( i = 1; i < 128; i++ ) { - if( p + 1 < p_end && *p == '\n' ) + if( p_sys->asfh.stream[i].i_cat != ASF_STREAM_UNKNOWN ) { - *pi_data = p_end - p - 1; - *pp_data = p + 1; - return VLC_SUCCESS; - } - if( p + 2 < p_end && p[0] == '\r' && p[1] == '\n' ) - { - *pi_data = p_end - p - 2; - *pp_data = p + 2; - return VLC_SUCCESS; + int i_select = 2; + if( p_sys->asfh.stream[i].i_selected ) + { + i_select = 0; + } + net_Printf( VLC_OBJECT(p_access), p_sys->fd, NULL, + "ffff:%d:%d ", i, i_select ); } } - *pi_data = 0; - *pp_data = p_end; - return VLC_EGENERIC; -} + net_Printf( VLC_OBJECT(p_access), p_sys->fd, NULL, "\r\n" ); + net_Printf( VLC_OBJECT(p_access), p_sys->fd, NULL, + "Connection: Close\r\n" ); -static http_answer_t *http_answer_parse( uint8_t *p_data, int i_data ) -{ - http_answer_t *ans = malloc( sizeof( http_answer_t ) ); - http_field_t **pp_last; - char buffer[1024]; + if( net_Printf( VLC_OBJECT(p_access), p_sys->fd, NULL, "\r\n" ) < 0 ) + { + msg_Err( p_access, "failed to send request" ); + return VLC_EGENERIC; + } - if( sscanf( p_data, "HTTP/1.%d %d %s", &ans-> i_version, &ans->i_error, buffer ) < 3 ) + psz = net_Gets( p_access, p_sys->fd, NULL ); + if( psz == NULL ) { - free( ans ); - return NULL; + msg_Err( p_access, "cannot read data 0" ); + return VLC_EGENERIC; } - ans->psz_answer = strdup( buffer ); - fprintf( stderr, "version=%d error=%d answer=%s\n", ans-> i_version, ans->i_error, ans->psz_answer ); - ans->p_fields = NULL; - ans->i_body = 0; - ans->p_body = 0; - pp_last = &ans->p_fields; + if( atoi( &psz[9] ) >= 400 ) + { + msg_Err( p_access, "error: %s", psz ); + free( psz ); + return VLC_EGENERIC; + } + msg_Dbg( p_access, "HTTP reply '%s'", psz ); + free( psz ); + /* FIXME check HTTP code */ for( ;; ) { - http_field_t *p_field; - uint8_t *colon; - - if( http_next_line( &p_data, &i_data ) ) + char *psz = net_Gets( p_access, p_sys->fd, NULL ); + if( psz == NULL ) { - return ans; + msg_Err( p_access, "cannot read data 1" ); + return VLC_EGENERIC; } - if( !strncmp( p_data, "\r\n", 2 ) || !strncmp( p_data, "\n", 1 ) ) + if( *psz == '\0' ) { + free( psz ); break; } + msg_Dbg( p_access, "%s", psz ); + free( psz ); + } - colon = strstr( p_data, ": " ); - if( colon ) - { - uint8_t *end; - - end = strstr( colon, "\n" ) - 1; - if( *end != '\r' ) - { - end++; - } - - p_field = malloc( sizeof( http_field_t ) ); - p_field->psz_name = strndup( p_data, colon - p_data ); - p_field->psz_value = strndup( colon + 2, end - colon - 2 ); - p_field->p_next = NULL; + p_sys->i_packet_used = 0; + p_sys->i_packet_length = 0; - *pp_last = p_field; - pp_last = &p_field->p_next; + return VLC_SUCCESS; +} - fprintf( stderr, "field name=`%s' value=`%s'\n", p_field->psz_name, p_field->psz_value ); - } - } +/***************************************************************************** + * closing stream + *****************************************************************************/ +static void Stop( access_t *p_access ) +{ + access_sys_t *p_sys = p_access->p_sys; - if( http_next_line( &p_data, &i_data ) ) + msg_Dbg( p_access, "closing stream" ); + if( p_sys->fd > 0 ) { - return ans; + net_Close( p_sys->fd ); + p_sys->fd = -1; } - - ans->p_body = p_data; - ans->i_body = i_data; - fprintf( stderr, "body size=%d\n", i_data ); - - return ans; } -static void http_answer_free( http_answer_t *ans ) +/***************************************************************************** + * get packet + *****************************************************************************/ +static int GetPacket( access_t * p_access, chunk_t *p_ck ) { - http_field_t *p_field = ans->p_fields; + access_sys_t *p_sys = p_access->p_sys; + int restsize; - while( p_field ) - { - http_field_t *p_next; - - p_next = p_field->p_next; - free( p_field->psz_name ); - free( p_field->psz_value ); - free( p_field ); + /* chunk_t */ + memset( p_ck, 0, sizeof( chunk_t ) ); - p_field = p_next; + /* Read the chunk header */ + /* Some headers are short, like 0x4324. Reading 12 bytes will cause us + * to lose synchronization with the stream. Just read to the length + * (4 bytes), decode and then read up to 8 additional bytes to get the + * entire header. + */ + if( net_Read( p_access, p_sys->fd, NULL, p_sys->buffer, 4, true ) < 4 ) + { + msg_Err( p_access, "cannot read data 2" ); + return VLC_EGENERIC; } - free( ans->psz_answer ); - free( ans ); -} + p_ck->i_type = GetWLE( p_sys->buffer); + p_ck->i_size = GetWLE( p_sys->buffer + 2); -static http_field_t *http_field_find( http_field_t *p_field, char *psz_name ) -{ + restsize = p_ck->i_size; + if( restsize > 8 ) + restsize = 8; - while( p_field ) + if( net_Read( p_access, p_sys->fd, NULL, p_sys->buffer + 4, restsize, true ) < restsize ) { - if( !strcasecmp( p_field->psz_name, psz_name ) ) - { - return p_field; - } - - p_field = p_field->p_next; + msg_Err( p_access, "cannot read data 3" ); + return VLC_EGENERIC; } + p_ck->i_sequence = GetDWLE( p_sys->buffer + 4); + p_ck->i_unknown = GetWLE( p_sys->buffer + 8); - return NULL; -} -static char *http_field_get_value( http_answer_t *ans, char *psz_name ) -{ - http_field_t *p_field = ans->p_fields; + /* Set i_size2 to 8 if this header was short, since a real value won't be + * present in the buffer. Using 8 avoid reading additional data for the + * packet. + */ + if( restsize < 8 ) + p_ck->i_size2 = 8; + else + p_ck->i_size2 = GetWLE( p_sys->buffer + 10); - while( p_field ) + p_ck->p_data = p_sys->buffer + 12; + p_ck->i_data = p_ck->i_size2 - 8; + + if( p_ck->i_type == 0x4524 ) // Transfer complete { - if( !strcasecmp( p_field->psz_name, psz_name ) ) + if( p_ck->i_sequence == 0 ) { - return p_field->psz_value; + msg_Warn( p_access, "EOF" ); + return VLC_EGENERIC; + } + else + { + msg_Warn( p_access, "next stream following" ); + return VLC_EGENERIC; } - - p_field = p_field->p_next; } - - return NULL; -} - - - -static int chunk_parse( chunk_t *ck, uint8_t *p_data, int i_data ) -{ - if( i_data < 12 ) + else if( p_ck->i_type == 0x4324 ) { + /* 0x4324 is CHUNK_TYPE_RESET: a new stream will follow with a sequence of 0 */ + msg_Warn( p_access, "next stream following (reset) seq=%d", p_ck->i_sequence ); + return VLC_EGENERIC; + } + else if( (p_ck->i_type != 0x4824) && (p_ck->i_type != 0x4424) ) + { + msg_Err( p_access, "invalid chunk FATAL (0x%x)", p_ck->i_type ); return VLC_EGENERIC; } - ck->i_type = GetWLE( p_data ); - ck->i_size = GetWLE( p_data + 2); - ck->i_sequence = GetDWLE( p_data + 4); - ck->i_unknown = GetWLE( p_data + 8); - ck->i_size2 = GetWLE( p_data + 10); - - ck->p_data = p_data + 12; - ck->i_data = __MIN( i_data - 12, ck->i_size2 - 8 ); + if( (p_ck->i_data > 0) && + (net_Read( p_access, p_sys->fd, NULL, &p_sys->buffer[12], + p_ck->i_data, true ) < p_ck->i_data) ) + { + msg_Err( p_access, "cannot read data 4" ); + return VLC_EGENERIC; + } #if 0 - fprintf( stderr, "type=0x%x size=%d sequence=%d unknown=%d size2=%d data=%d\n", - ck->i_type, ck->i_size, ck->i_sequence, ck->i_unknown, ck->i_size2, ck->i_data ); + if( (p_sys->i_packet_sequence != 0) && + (p_ck->i_sequence != p_sys->i_packet_sequence) ) + { + msg_Warn( p_access, "packet lost ? (%d != %d)", p_ck->i_sequence, p_sys->i_packet_sequence ); + } #endif + + p_sys->i_packet_sequence = p_ck->i_sequence + 1; + p_sys->i_packet_used = 0; + p_sys->i_packet_length = p_ck->i_data; + p_sys->p_packet = p_ck->p_data; + return VLC_SUCCESS; } -