Commit f6ac7b66 authored by Laurent Aimar's avatar Laurent Aimar

* mmsh: added support of multiple files streamed with the same url (a

few web radios use that)
parent 90917b82
...@@ -53,6 +53,7 @@ static ssize_t Read( input_thread_t *, byte_t *, size_t ); ...@@ -53,6 +53,7 @@ static ssize_t Read( input_thread_t *, byte_t *, size_t );
static ssize_t ReadRedirect( input_thread_t *, byte_t *, size_t ); static ssize_t ReadRedirect( input_thread_t *, byte_t *, size_t );
static void Seek( input_thread_t *, off_t ); static void Seek( input_thread_t *, off_t );
static int Describe( input_thread_t *, char **ppsz_location );
static int Start( input_thread_t *, off_t ); static int Start( input_thread_t *, off_t );
static void Stop( input_thread_t * ); static void Stop( input_thread_t * );
static int GetPacket( input_thread_t *, chunk_t * ); static int GetPacket( input_thread_t *, chunk_t * );
...@@ -64,9 +65,7 @@ int E_( MMSHOpen ) ( input_thread_t *p_input ) ...@@ -64,9 +65,7 @@ int E_( MMSHOpen ) ( input_thread_t *p_input )
{ {
access_sys_t *p_sys; access_sys_t *p_sys;
char *psz;
char *psz_location = NULL; char *psz_location = NULL;
int i_code;
vlc_value_t val; vlc_value_t val;
...@@ -75,150 +74,41 @@ int E_( MMSHOpen ) ( input_thread_t *p_input ) ...@@ -75,150 +74,41 @@ int E_( MMSHOpen ) ( input_thread_t *p_input )
p_sys->i_proto = MMS_PROTO_HTTP; p_sys->i_proto = MMS_PROTO_HTTP;
p_sys->fd = -1; p_sys->fd = -1;
p_sys->i_request_context = 1;
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; p_sys->i_pos = 0;
p_sys->i_request_context = 1; p_sys->i_start= 0;
E_( GenerateGuid )( &p_sys->guid );
/* open a tcp connection */ /* open a tcp connection */
vlc_UrlParse( &p_sys->url, p_input->psz_name, 0 ); vlc_UrlParse( &p_sys->url, p_input->psz_name, 0 );
if( p_sys->url.psz_host == NULL && *p_sys->url.psz_host == '\0' ) if( p_sys->url.psz_host == NULL && *p_sys->url.psz_host == '\0' )
{ {
msg_Err( p_input, "invalid host" ); msg_Err( p_input, "invalid host" );
goto error; vlc_UrlClean( &p_sys->url );
free( p_sys );
return VLC_EGENERIC;
} }
if( p_sys->url.i_port <= 0 ) if( p_sys->url.i_port <= 0 )
{ {
p_sys->url.i_port = 80; p_sys->url.i_port = 80;
} }
if( ( p_sys->fd = net_OpenTCP( p_input, p_sys->url.psz_host, if( Describe( p_input, &psz_location ) )
p_sys->url.i_port ) ) < 0 )
{
msg_Err( p_input, "cannot connect to%s:%d", p_sys->url.psz_host, p_sys->url.i_port );
goto error;
}
/* send first request */
net_Printf( VLC_OBJECT(p_input), p_sys->fd,
"GET %s HTTP/1.0\r\n"
"Accept: */*\r\n"
"User-Agent: NSPlayer/4.1.0.3856\r\n"
"Host: %s:%d\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->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,
p_sys->i_request_context++,
GUID_PRINT( p_sys->guid ) );
if( net_Printf( VLC_OBJECT(p_input), p_sys->fd, "\r\n" ) < 0 )
{
msg_Err( p_input, "failed to send request" );
goto error;
}
/* Receive the http header */
if( ( psz = net_Gets( VLC_OBJECT(p_input), p_sys->fd ) ) == NULL )
{
msg_Err( p_input, "failed to read answer" );
goto error;
}
if( strncmp( psz, "HTTP/1.", 7 ) )
{
msg_Err( p_input, "invalid HTTP reply '%s'", psz );
free( psz );
goto error;
}
i_code = atoi( &psz[9] );
if( i_code >= 400 )
{
msg_Err( p_input, "error: %s", psz );
free( psz );
goto error;
}
msg_Dbg( p_input, "HTTP reply '%s'", psz );
free( psz );
for( ;; )
{
char *psz = net_Gets( p_input, p_sys->fd );
char *p;
if( psz == NULL )
{
msg_Err( p_input, "failed to read answer" );
goto error;
}
if( *psz == '\0' )
{
free( psz );
break;
}
if( ( p = strchr( psz, ':' ) ) == NULL )
{
msg_Err( p_input, "malformed header line: %s", psz );
free( psz );
goto error;
}
*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_input, "stream type = broadcast" );
p_sys->b_broadcast = VLC_TRUE;
}
else if( strstr( p, "seekable" ) )
{
msg_Dbg( p_input, "stream type = seekable" );
p_sys->b_broadcast = VLC_FALSE;
}
else
{ {
msg_Warn( p_input, "unknow stream types (%s)", p ); vlc_UrlClean( &p_sys->url );
p_sys->b_broadcast = VLC_FALSE; free( p_sys );
} return VLC_EGENERIC;
}
}
else if( !strcasecmp( psz, "Location" ) )
{
psz_location = strdup( p );
}
free( psz );
} }
/* Handle redirection */
/* Handle the redirection */ if( psz_location && *psz_location )
if( ( i_code == 301 || i_code == 302 ||
i_code == 303 || i_code == 307 ) &&
psz_location && *psz_location )
{ {
playlist_t * p_playlist; playlist_t * p_playlist = vlc_object_find( p_input, VLC_OBJECT_PLAYLIST, FIND_PARENT );
msg_Dbg( p_input, "redirection to %s", psz_location ); msg_Dbg( p_input, "redirection to %s", psz_location );
net_Close( p_sys->fd ); p_sys->fd = -1;
p_playlist = vlc_object_find( p_input, VLC_OBJECT_PLAYLIST, FIND_PARENT );
if( !p_playlist ) if( !p_playlist )
{ {
msg_Err( p_input, "redirection failed: can't find playlist" ); msg_Err( p_input, "redirection failed: can't find playlist" );
goto error; free( psz_location );
return VLC_EGENERIC;
} }
p_playlist->pp_items[p_playlist->i_index]->b_autodeletion = VLC_TRUE; p_playlist->pp_items[p_playlist->i_index]->b_autodeletion = VLC_TRUE;
playlist_Add( p_playlist, psz_location, psz_location, playlist_Add( p_playlist, psz_location, psz_location,
...@@ -226,6 +116,8 @@ int E_( MMSHOpen ) ( input_thread_t *p_input ) ...@@ -226,6 +116,8 @@ int E_( MMSHOpen ) ( input_thread_t *p_input )
p_playlist->i_index + 1 ); p_playlist->i_index + 1 );
vlc_object_release( p_playlist ); vlc_object_release( p_playlist );
free( psz_location );
p_input->pf_read = ReadRedirect; p_input->pf_read = ReadRedirect;
p_input->pf_seek = NULL; p_input->pf_seek = NULL;
p_input->pf_set_program = input_SetProgram; p_input->pf_set_program = input_SetProgram;
...@@ -243,56 +135,14 @@ int E_( MMSHOpen ) ( input_thread_t *p_input ) ...@@ -243,56 +135,14 @@ int E_( MMSHOpen ) ( input_thread_t *p_input )
return VLC_SUCCESS; return VLC_SUCCESS;
} }
/* Read the asf header */ /* Start playing */
p_sys->i_header = 0;
p_sys->p_header = NULL;
for( ;; )
{
chunk_t ck;
if( GetPacket( p_input, &ck ) ||
ck.i_type != 0x4824 )
{
break;
}
if( ck.i_data > 0 )
{
p_sys->i_header += ck.i_data;
p_sys->p_header = realloc( 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_input, "complete header size=%d", p_sys->i_header );
if( p_sys->i_header <= 0 )
{
msg_Err( p_input, "header size == 0" );
goto error;
}
/* close this connection */
net_Close( p_sys->fd ); p_sys->fd = -1;
/* *** 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 );
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( Start( p_input, 0 ) ) if( Start( p_input, 0 ) )
{ {
msg_Err( p_input, "cannot start stream" ); msg_Err( p_input, "cannot start stream" );
goto error; free( p_sys->p_header );
vlc_UrlClean( &p_sys->url );
free( p_sys );
return VLC_EGENERIC;
} }
/* *** set exported functions *** */ /* *** set exported functions *** */
...@@ -326,15 +176,6 @@ int E_( MMSHOpen ) ( input_thread_t *p_input ) ...@@ -326,15 +176,6 @@ int E_( MMSHOpen ) ( input_thread_t *p_input )
p_input->i_pts_delay = val.i_int * 1000; p_input->i_pts_delay = val.i_int * 1000;
return VLC_SUCCESS; return VLC_SUCCESS;
error:
vlc_UrlClean( &p_sys->url );
if( p_sys->fd > 0 )
{
net_Close( p_sys->fd );
}
free( p_sys );
return VLC_EGENERIC;
} }
/***************************************************************************** /*****************************************************************************
...@@ -348,6 +189,7 @@ void E_( MMSHClose )( input_thread_t *p_input ) ...@@ -348,6 +189,7 @@ void E_( MMSHClose )( input_thread_t *p_input )
Stop( p_input ); Stop( p_input );
free( p_sys ); free( p_sys );
} }
...@@ -413,10 +255,11 @@ static ssize_t Read ( input_thread_t * p_input, byte_t * p_buffer, ...@@ -413,10 +255,11 @@ static ssize_t Read ( input_thread_t * p_input, byte_t * p_buffer,
while( i_data < i_len ) while( i_data < i_len )
{ {
if( p_sys->i_pos < p_sys->i_header ) if( p_sys->i_pos < p_sys->i_start + p_sys->i_header )
{ {
i_copy = __MIN( p_sys->i_header - p_sys->i_pos, i_len - i_data ); int i_offset = p_sys->i_pos - p_sys->i_start;
memcpy( &p_buffer[i_data], &p_sys->p_header[p_sys->i_pos], i_copy ); i_copy = __MIN( p_sys->i_header - i_offset, i_len - i_data );
memcpy( &p_buffer[i_data], &p_sys->p_header[i_offset], i_copy );
i_data += i_copy; i_data += i_copy;
p_sys->i_pos += i_copy; p_sys->i_pos += i_copy;
...@@ -451,9 +294,33 @@ static ssize_t Read ( input_thread_t * p_input, byte_t * p_buffer, ...@@ -451,9 +294,33 @@ static ssize_t Read ( input_thread_t * p_input, byte_t * p_buffer,
chunk_t ck; chunk_t ck;
if( GetPacket( p_input, &ck ) ) if( GetPacket( p_input, &ck ) )
{ {
if( ck.i_type == 0x4524 && ck.i_sequence != 0 && p_sys->b_broadcast )
{
char *psz_location = NULL;
p_sys->i_start = p_sys->i_pos;
msg_Dbg( p_input, "stoping the stream" );
Stop( p_input );
msg_Dbg( p_input, "describe the stream" );
if( Describe( p_input, &psz_location ) )
{
msg_Err( p_input, "describe failed" );
return -1;
}
if( Start( p_input, 0 ) )
{
msg_Err( p_input, "Start failed" );
return -1; return -1;
} }
if( ck.i_type == 0x4824 ) }
else
{
return -1;
}
}
if( ck.i_type != 0x4424 )
{ {
p_sys->i_packet_used = 0; p_sys->i_packet_used = 0;
p_sys->i_packet_length = 0; p_sys->i_packet_length = 0;
...@@ -464,6 +331,201 @@ static ssize_t Read ( input_thread_t * p_input, byte_t * p_buffer, ...@@ -464,6 +331,201 @@ static ssize_t Read ( input_thread_t * p_input, byte_t * p_buffer,
return( i_data ); return( i_data );
} }
/*****************************************************************************
* Describe:
*****************************************************************************/
static int Describe( input_thread_t *p_input, char **ppsz_location )
{
access_sys_t *p_sys = p_input->p_access_data;
char *psz_location = NULL;
char *psz;
int i_code;
/* Reinit context */
p_sys->b_broadcast = VLC_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;
E_( GenerateGuid )( &p_sys->guid );
if( ( p_sys->fd = net_OpenTCP( p_input, p_sys->url.psz_host,
p_sys->url.i_port ) ) < 0 )
{
msg_Err( p_input, "cannot connect to%s:%d", p_sys->url.psz_host, p_sys->url.i_port );
goto error;
}
/* send first request */
net_Printf( VLC_OBJECT(p_input), p_sys->fd,
"GET %s HTTP/1.0\r\n"
"Accept: */*\r\n"
"User-Agent: NSPlayer/4.1.0.3856\r\n"
"Host: %s:%d\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->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,
p_sys->i_request_context++,
GUID_PRINT( p_sys->guid ) );
if( net_Printf( VLC_OBJECT(p_input), p_sys->fd, "\r\n" ) < 0 )
{
msg_Err( p_input, "failed to send request" );
goto error;
}
/* Receive the http header */
if( ( psz = net_Gets( VLC_OBJECT(p_input), p_sys->fd ) ) == NULL )
{
msg_Err( p_input, "failed to read answer" );
goto error;
}
if( strncmp( psz, "HTTP/1.", 7 ) )
{
msg_Err( p_input, "invalid HTTP reply '%s'", psz );
free( psz );
goto error;
}
i_code = atoi( &psz[9] );
if( i_code >= 400 )
{
msg_Err( p_input, "error: %s", psz );
free( psz );
goto error;
}
msg_Dbg( p_input, "HTTP reply '%s'", psz );
free( psz );
for( ;; )
{
char *psz = net_Gets( p_input, p_sys->fd );
char *p;
if( psz == NULL )
{
msg_Err( p_input, "failed to read answer" );
goto error;
}
if( *psz == '\0' )
{
free( psz );
break;
}
if( ( p = strchr( psz, ':' ) ) == NULL )
{
msg_Err( p_input, "malformed header line: %s", psz );
free( psz );
goto error;
}
*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_input, "stream type = broadcast" );
p_sys->b_broadcast = VLC_TRUE;
}
else if( strstr( p, "seekable" ) )
{
msg_Dbg( p_input, "stream type = seekable" );
p_sys->b_broadcast = VLC_FALSE;
}
else
{
msg_Warn( p_input, "unknow stream types (%s)", p );
p_sys->b_broadcast = VLC_FALSE;
}
}
}
else if( !strcasecmp( psz, "Location" ) )
{
psz_location = strdup( p );
}
free( psz );
}
/* Handle the redirection */
if( ( i_code == 301 || i_code == 302 ||
i_code == 303 || i_code == 307 ) &&
psz_location && *psz_location )
{
msg_Dbg( p_input, "redirection to %s", psz_location );
net_Close( p_sys->fd ); p_sys->fd = -1;
*ppsz_location = psz_location;
return VLC_SUCCESS;
}
/* Read the asf header */
p_sys->i_header = 0;
p_sys->p_header = NULL;
for( ;; )
{
chunk_t ck;
if( GetPacket( p_input, &ck ) ||
ck.i_type != 0x4824 )
{
break;
}
if( ck.i_data > 0 )
{
p_sys->i_header += ck.i_data;
p_sys->p_header = realloc( 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_input, "complete header size=%d", p_sys->i_header );
if( p_sys->i_header <= 0 )
{
msg_Err( p_input, "header size == 0" );
goto error;
}
/* close this connection */
net_Close( p_sys->fd ); p_sys->fd = -1;
/* *** 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 );
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" ) );
return VLC_SUCCESS;
error:
if( p_sys->fd > 0 )
{
net_Close( p_sys->fd );
p_sys->fd = -1;
}
return VLC_EGENERIC;
}
/***************************************************************************** /*****************************************************************************
* *
*****************************************************************************/ *****************************************************************************/
...@@ -595,7 +657,11 @@ static void Stop( input_thread_t *p_input ) ...@@ -595,7 +657,11 @@ static void Stop( input_thread_t *p_input )
access_sys_t *p_sys = p_input->p_access_data; access_sys_t *p_sys = p_input->p_access_data;
msg_Dbg( p_input, "closing stream" ); msg_Dbg( p_input, "closing stream" );
net_Close( p_sys->fd ); p_sys->fd = -1; if( p_sys->fd > 0 )
{
net_Close( p_sys->fd );
p_sys->fd = -1;
}
} }
/***************************************************************************** /*****************************************************************************
...@@ -605,6 +671,9 @@ static int GetPacket( input_thread_t * p_input, chunk_t *p_ck ) ...@@ -605,6 +671,9 @@ static int GetPacket( input_thread_t * p_input, chunk_t *p_ck )
{ {
access_sys_t *p_sys = p_input->p_access_data; access_sys_t *p_sys = p_input->p_access_data;
/* chunk_t */
memset( p_ck, 0, sizeof( chunk_t ) );
/* Read the chunk header */ /* Read the chunk header */
if( net_Read( p_input, p_sys->fd, p_sys->buffer, 12, VLC_TRUE ) < 12 ) if( net_Read( p_input, p_sys->fd, p_sys->buffer, 12, VLC_TRUE ) < 12 )
{ {
...@@ -621,17 +690,26 @@ static int GetPacket( input_thread_t * p_input, chunk_t *p_ck ) ...@@ -621,17 +690,26 @@ static int GetPacket( input_thread_t * p_input, chunk_t *p_ck )
p_ck->i_data = p_ck->i_size2 - 8; p_ck->i_data = p_ck->i_size2 - 8;
if( p_ck->i_type == 0x4524 ) // Transfer complete if( p_ck->i_type == 0x4524 ) // Transfer complete
{
if( p_ck->i_sequence == 0 )
{ {
msg_Warn( p_input, "EOF" ); msg_Warn( p_input, "EOF" );
return VLC_EGENERIC; return VLC_EGENERIC;
} }
else
{
msg_Warn( p_input, "Next stream follow but not supported" );
return VLC_EGENERIC;
}
}
else if( p_ck->i_type != 0x4824 && p_ck->i_type != 0x4424 ) else if( p_ck->i_type != 0x4824 && p_ck->i_type != 0x4424 )
{ {
msg_Err( p_input, "invalid chunk FATAL" ); msg_Err( p_input, "invalid chunk FATAL (0x%x)", p_ck->i_type );
return VLC_EGENERIC; return VLC_EGENERIC;
} }
if( net_Read( p_input, p_sys->fd, &p_sys->buffer[12], p_ck->i_data, VLC_TRUE ) < p_ck->i_data ) if( p_ck->i_data > 0 &&
net_Read( p_input, p_sys->fd, &p_sys->buffer[12], p_ck->i_data, VLC_TRUE ) < p_ck->i_data )
{ {
msg_Err( p_input, "cannot read data" ); msg_Err( p_input, "cannot read data" );
return VLC_EGENERIC; return VLC_EGENERIC;
...@@ -640,7 +718,7 @@ static int GetPacket( input_thread_t * p_input, chunk_t *p_ck ) ...@@ -640,7 +718,7 @@ static int GetPacket( input_thread_t * p_input, chunk_t *p_ck )
if( p_sys->i_packet_sequence != 0 && if( p_sys->i_packet_sequence != 0 &&
p_ck->i_sequence != p_sys->i_packet_sequence ) p_ck->i_sequence != p_sys->i_packet_sequence )
{ {
msg_Warn( p_input, "packet lost ?" ); msg_Warn( p_input, "packet lost ? (%d != %d)", p_ck->i_sequence, p_sys->i_packet_sequence );
} }
p_sys->i_packet_sequence = p_ck->i_sequence + 1; p_sys->i_packet_sequence = p_ck->i_sequence + 1;
......
...@@ -59,6 +59,7 @@ struct access_sys_t ...@@ -59,6 +59,7 @@ struct access_sys_t
unsigned int i_packet_length; unsigned int i_packet_length;
off_t i_pos; off_t i_pos;
off_t i_start;
asf_header_t asfh; asf_header_t asfh;
guid_t guid; guid_t guid;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment