Commit 32cdd8cb authored by hartman's avatar hartman
Browse files

* refactoring of live555 module.

  - less code duplication
  - automaticly try HTTP tunneling if describe fails
  - upon authentication failure, ask for a new user/passwd and try again (untested due to lack of a rtsp source)
  - fix a bug with adding tracks introduced in [16114]
  - fix a bug closing the RTSPClient
  - disabled the QT VoD detection because it's unreliable like hell.

  TODO:
  - test with a WMS RTSP session
  - test against a paswd. protected RTSP session
  - find a better way for error communication with live (parsing random strings is so 1990)
  - find a way to relay Control() calls to a subdemux (for time/pos in TS)
  - add the HREF support of the 0.8.5-href branch
  - better flushing etc. during the PAUSE (ideas anyone?)
parent a4f7a771
......@@ -28,6 +28,8 @@
#include <vlc/vlc.h>
#include <vlc/input.h>
#include "vlc_interaction.h"
#include "network.h"
#include <iostream>
......@@ -106,24 +108,16 @@ vlc_module_begin();
N_("Port to use for tunneling the RTSP/RTP over HTTP."),
VLC_TRUE );
#endif
add_integer( "rtsp-caching", 4 * DEFAULT_PTS_DELAY / 1000, NULL,
CACHING_TEXT, CACHING_LONGTEXT, VLC_TRUE );
add_bool( "rtsp-kasenna", VLC_FALSE, NULL, KASENNA_TEXT,
KASENNA_LONGTEXT, VLC_TRUE );
add_integer("rtsp-caching", 4 * DEFAULT_PTS_DELAY / 1000, NULL,
CACHING_TEXT, CACHING_LONGTEXT, VLC_TRUE );
add_bool( "rtsp-kasenna", VLC_FALSE, NULL, KASENNA_TEXT,
KASENNA_LONGTEXT, VLC_TRUE );
add_string( "rtsp-user", NULL, NULL, USER_TEXT,
USER_LONGTEXT, VLC_TRUE );
USER_LONGTEXT, VLC_TRUE );
add_string( "rtsp-pwd", NULL, NULL, PASS_TEXT,
PASS_LONGTEXT, VLC_TRUE );
PASS_LONGTEXT, VLC_TRUE );
vlc_module_end();
/* TODO:
* - Improve support of PS/TS
* - Support X-QT/X-QUICKTIME generic codec for audio.
*
* - Check memory leak, delete/free -> still one when using rtsp-tcp but I'm
* not sure if it comes from me.
*
*/
/*****************************************************************************
* Local prototypes
......@@ -151,7 +145,7 @@ typedef struct
char waiting;
mtime_t i_pts;
int64_t i_pts;
} live_track_t;
......@@ -165,8 +159,8 @@ struct timeout_thread_t
struct demux_sys_t
{
char *p_sdp; /* XXX mallocated */
char *psz_path; /* URL-encoded path */
char *p_sdp; /* XXX mallocated */
char *psz_path; /* URL-encoded path */
MediaSession *ms;
TaskScheduler *scheduler;
......@@ -176,10 +170,10 @@ struct demux_sys_t
/* */
int i_track;
live_track_t **track; /* XXX mallocated */
mtime_t i_pcr;
mtime_t i_pcr_start;
mtime_t i_pcr_previous;
mtime_t i_pcr_repeatdate;
int64_t i_pcr;
int64_t i_pcr_start;
int64_t i_pcr_previous;
int64_t i_pcr_repeatdate;
int i_pcr_repeats;
/* Asf */
......@@ -187,8 +181,8 @@ struct demux_sys_t
stream_t *p_out_asf;
/* */
mtime_t i_length;
mtime_t i_start;
int64_t i_length;
int64_t i_start;
/* timeout thread information */
int i_timeout; /* session timeout value in seconds */
......@@ -206,12 +200,15 @@ struct demux_sys_t
static int Demux ( demux_t * );
static int Control( demux_t *, int, va_list );
static int ParseASF( demux_t * );
static int RollOverTcp( demux_t * );
static int Connect ( demux_t * );
static int SessionsSetup( demux_t * );
static int Play ( demux_t *);
static int ParseASF ( demux_t * );
static int RollOverTcp ( demux_t * );
static void StreamRead( void *, unsigned int, unsigned int,
struct timeval, unsigned int );
static void StreamClose( void * );
static void StreamRead ( void *, unsigned int, unsigned int,
struct timeval, unsigned int );
static void StreamClose ( void * );
static void TaskInterrupt( void * );
static void TimeoutPrevention( timeout_thread_t * );
......@@ -228,24 +225,16 @@ static int Open ( vlc_object_t *p_this )
{
demux_t *p_demux = (demux_t*)p_this;
demux_sys_t *p_sys = NULL;
MediaSubsessionIterator *iter;
MediaSubsession *sub;
vlc_bool_t b_rtsp_tcp;
uint8_t *p_peek;
char *psz_user = NULL;
char *psz_pwd = NULL;
int i_sdp;
int i_sdp_max;
uint8_t *p_sdp;
MediaSubsessionIterator *iter = NULL;
MediaSubsession *sub = NULL;
int i_return;
if( p_demux->s )
{
/* See if it looks like a SDP
v, o, s fields are mandatory and in this order */
uint8_t *p_peek;
if( stream_Peek( p_demux->s, &p_peek, 7 ) < 7 ) return VLC_EGENERIC;
if( memcmp( (char*)p_peek, "v=0\r\n", 5 ) &&
......@@ -286,8 +275,7 @@ static int Open ( vlc_object_t *p_this )
p_sys->i_timeout = 0;
p_sys->b_timeout_call = VLC_FALSE;
p_sys->b_multicast = VLC_FALSE;
p_sys->psz_path = strdup(p_demux->psz_path);
p_sys->psz_path = strdup( p_demux->psz_path );
if( ( p_sys->scheduler = BasicTaskScheduler::createNew() ) == NULL )
{
......@@ -306,68 +294,13 @@ static int Open ( vlc_object_t *p_this )
while( (p = strchr( p, ' ' )) != NULL ) *p = '+';
}
if( p_demux->s == NULL && !strcasecmp( p_demux->psz_access, "rtsp" ) )
{
char *psz_url;
char *psz_options;
#if LIVEMEDIA_LIBRARY_VERSION_INT > 1130457500
int i_http_port = 0;
if( var_CreateGetBool( p_demux, "rtsp-http" ) )
i_http_port = var_CreateGetInteger( p_demux, "rtsp-http-port" );
if( ( p_sys->rtsp = RTSPClient::createNew(*p_sys->env, 1/*verbose*/,
"VLC media player", i_http_port ) ) == NULL )
#else
if( ( p_sys->rtsp = RTSPClient::createNew(*p_sys->env, 1/*verbose*/,
"VLC media player" ) ) == NULL )
#endif
{
msg_Err( p_demux, "RTSPClient::createNew failed (%s)",
p_sys->env->getResultMsg() );
goto error;
}
psz_url = (char*)malloc( strlen( p_sys->psz_path ) + 8 );
sprintf( psz_url, "rtsp://%s", p_sys->psz_path );
psz_options = p_sys->rtsp->sendOptionsCmd( psz_url );
if( psz_options ) delete [] psz_options;
psz_user = var_CreateGetString( p_demux, "rtsp-user" );
psz_pwd = var_CreateGetString( p_demux, "rtsp-pwd" );
if ((*psz_user) && (*psz_pwd))
p_sdp = (uint8_t*)p_sys->rtsp->describeWithPassword( psz_url,
psz_user, psz_pwd);
else
p_sdp = (uint8_t*)p_sys->rtsp->describeURL( psz_url,
NULL, var_CreateGetBool( p_demux, "rtsp-kasenna" ) );
if( p_sdp == NULL )
{
msg_Err( p_demux, "describeURL failed (%s)",
p_sys->env->getResultMsg() );
free( psz_url );
goto error;
}
free( psz_url );
free( psz_user );
free( psz_pwd );
/* malloc-ated copy */
p_sys->p_sdp = strdup( (char*)p_sdp );
delete[] p_sdp;
msg_Dbg( p_demux, "sdp=%s\n", p_sys->p_sdp );
}
else if( p_demux->s == NULL && !strcasecmp( p_demux->psz_access, "sdp" ) )
{
p_sys->p_sdp = strdup( p_sys->psz_path );
}
else
if( p_demux->s != NULL )
{
/* Gather the complete sdp file */
i_sdp = 0;
i_sdp_max = 1000;
p_sdp = (uint8_t*)malloc( i_sdp_max );
int i_sdp = 0;
int i_sdp_max = 1000;
uint8_t *p_sdp = (uint8_t*) malloc( i_sdp_max );
for( ;; )
{
int i_read = stream_Read( p_demux->s, &p_sdp[i_sdp],
......@@ -392,119 +325,53 @@ static int Open ( vlc_object_t *p_this )
p_sdp = (uint8_t*)realloc( p_sdp, i_sdp_max );
}
p_sys->p_sdp = (char*)p_sdp;
msg_Dbg( p_demux, "sdp=%s\n", p_sys->p_sdp );
}
if( !( p_sys->ms = MediaSession::createNew( *p_sys->env, p_sys->p_sdp ) ) )
else if( p_demux->s == NULL && !strcasecmp( p_demux->psz_access, "sdp" ) )
{
/* sdp:// link from SAP */
p_sys->p_sdp = strdup( p_sys->psz_path );
}
else if( ( i_return = Connect( p_demux ) ) != VLC_SUCCESS )
{
msg_Err( p_demux, "MediaSession::createNew failed" );
msg_Err( p_demux, "Failed to connect with rtsp://%s", p_sys->psz_path );
goto error;
}
b_rtsp_tcp = var_CreateGetBool( p_demux, "rtsp-tcp" );
/* Initialise each media subsession */
iter = new MediaSubsessionIterator( *p_sys->ms );
while( ( sub = iter->next() ) != NULL )
if( p_sys->p_sdp == NULL )
{
unsigned int i_buffer = 0;
Boolean bInit;
int i_client_port;
i_client_port = var_CreateGetInteger( p_demux, "rtp-client-port" );
if( i_client_port != -1 )
sub->setClientPortNum( i_client_port );
/* Value taken from mplayer */
if( !strcmp( sub->mediumName(), "audio" ) )
i_buffer = 100000;
else if( !strcmp( sub->mediumName(), "video" ) )
i_buffer = 2000000;
else
continue;
if( !strcmp( sub->codecName(), "X-ASF-PF" ) )
bInit = sub->initiate( 4 ); /* Constant ? */
else
bInit = sub->initiate();
if( strcasestr( sub->codecName(), "REAL" ) )
{
msg_Info( p_demux, "real codec detected, using real-RTSP instead" );
delete iter;
goto error;
}
if( !bInit )
{
msg_Warn( p_demux, "RTP subsession '%s/%s' failed (%s)",
sub->mediumName(), sub->codecName(),
p_sys->env->getResultMsg() );
}
else
{
if( sub->rtpSource() )
{
int fd = sub->rtpSource()->RTPgs()->socketNum();
/* Increase the buffer size */
increaseReceiveBufferTo( *p_sys->env, fd, i_buffer );
}
msg_Dbg( p_demux, "RTP subsession '%s/%s'", sub->mediumName(),
sub->codecName() );
/* Issue the SETUP */
if( p_sys->rtsp )
{
p_sys->rtsp->setupMediaSubsession( *sub, False,
b_rtsp_tcp ? True : False );
}
if( !p_sys->b_multicast )
{
/* Check, because we need diff. rollover behaviour for multicast */
p_sys->b_multicast = IsMulticastAddress( sub->connectionEndpointAddress() );
}
}
msg_Err( p_demux, "Failed to retrieve the RTSP Session Description" );
goto error;
}
if( p_sys->rtsp )
/* Create the session from the SDP */
if( !( p_sys->ms = MediaSession::createNew( *p_sys->env, p_sys->p_sdp ) ) )
{
/* The PLAY */
if( !p_sys->rtsp->playMediaSession( *p_sys->ms ) )
{
msg_Err( p_demux, "PLAY failed %s", p_sys->env->getResultMsg() );
delete iter;
goto error;
}
/* Retrieve the timeout value and set up a timeout prevention thread */
#if LIVEMEDIA_LIBRARY_VERSION_INT >= 1138089600
p_sys->i_timeout = p_sys->rtsp->sessionTimeoutParameter();
#endif
if( p_sys->i_timeout > 0 )
{
msg_Dbg( p_demux, "We have a timeout of %d seconds", p_sys->i_timeout );
p_sys->p_timeout = (timeout_thread_t *)vlc_object_create( p_demux, sizeof(timeout_thread_t) );
p_sys->p_timeout->p_sys = p_demux->p_sys; /* lol, object recursion :D */
if( vlc_thread_create( p_sys->p_timeout, "liveMedia-timeout", TimeoutPrevention,
VLC_THREAD_PRIORITY_LOW, VLC_TRUE ) )
{
msg_Err( p_demux, "cannot spawn liveMedia timeout thread" );
delete iter;
vlc_object_destroy( p_sys->p_timeout );
goto error;
}
msg_Dbg( p_demux, "spawned timeout thread" );
vlc_object_attach( p_sys->p_timeout, p_demux );
}
msg_Err( p_demux, "Could not create the RTSP Session: %s",
p_sys->env->getResultMsg() );
goto error;
}
/* Retrieve the duration if possible */
p_sys->i_length = (int64_t)( p_sys->ms->playEndTime() * 1000000.0 );
if( p_sys->i_length < 0 )
p_sys->i_length = -1;
if( ( i_return = SessionsSetup( p_demux ) ) != VLC_SUCCESS )
{
msg_Err( p_demux, "Nothing to play for rtsp://%s", p_sys->psz_path );
goto error;
}
if( ( i_return = Play( p_demux ) ) != VLC_SUCCESS )
goto error;
/* Create all es struct */
iter->reset();
iter = new MediaSubsessionIterator( *p_sys->ms );
while( ( sub = iter->next() ) != NULL )
{
live_track_t *tk;
/* Check if we will receive data from this subsession for this track */
if( sub->readSource() == NULL ) continue;
tk = (live_track_t*)malloc( sizeof( live_track_t ) );
......@@ -713,7 +580,10 @@ static int Open ( vlc_object_t *p_this )
}
}
tk->p_es = es_out_Add( p_demux->out, &tk->fmt );
if( !tk->b_quicktime && !tk->b_muxed && !tk->b_asf )
{
tk->p_es = es_out_Add( p_demux->out, &tk->fmt );
}
if( sub->rtcpInstance() != NULL )
{
......@@ -724,13 +594,15 @@ static int Open ( vlc_object_t *p_this )
{
tk->readSource = sub->readSource();
tk->rtpSource = sub->rtpSource();
/* Append */
p_sys->track = (live_track_t**)realloc( p_sys->track, sizeof( live_track_t ) * ( p_sys->i_track + 1 ) );
p_sys->track[p_sys->i_track++] = tk;
}
else
{
/* BUG ??? */
msg_Err( p_demux, "unusable RTSP track. this should not happen" );
free( tk );
}
}
......@@ -744,29 +616,15 @@ static int Open ( vlc_object_t *p_this )
goto error;
}
p_sys->i_length = (mtime_t)(p_sys->ms->playEndTime() * 1000000.0);
if( p_sys->i_length < 0 )
{
p_sys->i_length = 0;
}
else if( p_sys->i_length > 0 )
{
/* FIXME */
/* p_input->stream.p_selected_area->i_size = 1000;*/ /* needed for now */
}
if( p_sys->i_track <= 0 )
{
msg_Err( p_demux, "no codec supported, aborting" );
goto error;
}
return VLC_SUCCESS;
error:
if( p_sys->p_out_asf ) stream_DemuxDelete( p_sys->p_out_asf );
if( p_sys->ms ) Medium::close( p_sys->ms );
if( p_sys->rtsp ) Medium::close( p_sys->rtsp );
if( p_sys->rtsp ) RTSPClient::close( p_sys->rtsp );
if( p_sys->env ) RECLAIM_ENV(p_sys->env);
if( p_sys->p_timeout )
{
......@@ -820,14 +678,251 @@ static void Close( vlc_object_t *p_this )
vlc_object_destroy( p_sys->p_timeout );
}
if( p_sys->rtsp ) Medium::close( p_sys->rtsp );
if( p_sys->env ) RECLAIM_ENV(p_sys->env);
if( p_sys->rtsp ) RTSPClient::close( p_sys->rtsp );
if( p_sys->env ) RECLAIM_ENV( p_sys->env );
if( p_sys->scheduler ) delete p_sys->scheduler;
if( p_sys->p_sdp ) free( p_sys->p_sdp );
if( p_sys->psz_path ) free( p_sys->psz_path );
free( p_sys );
}
/*****************************************************************************
* Connect: connects to the RTSP server to setup the session DESCRIBE
*****************************************************************************/
static int Connect( demux_t *p_demux )
{
demux_sys_t *p_sys = p_demux->p_sys;
Authenticator authenticator;
char *psz_user = NULL;
char *psz_pwd = NULL;
char *psz_url = NULL;
char *psz_options = NULL;
char *p_sdp = NULL;
int i_http_port = 0;
int i_ret = VLC_SUCCESS;
createnew:
if( var_CreateGetBool( p_demux, "rtsp-http" ) )
i_http_port = var_CreateGetInteger( p_demux, "rtsp-http-port" );
#if LIVEMEDIA_LIBRARY_VERSION_INT > 1130457500
if( ( p_sys->rtsp = RTSPClient::createNew(*p_sys->env, 1 /*verbose*/,
"VLC media player", i_http_port ) ) == NULL )
#else
if( ( p_sys->rtsp = RTSPClient::createNew(*p_sys->env, 1 /*verbose*/,
"VLC media player" ) ) == NULL )
#endif
{
msg_Err( p_demux, "RTSPClient::createNew failed (%s)",
p_sys->env->getResultMsg() );
return VLC_EGENERIC;
}
psz_url = (char*)malloc( strlen( p_sys->psz_path ) + 8 );
sprintf( psz_url, "rtsp://%s", p_sys->psz_path );
psz_options = p_sys->rtsp->sendOptionsCmd( psz_url );
if( psz_options ) delete [] psz_options;
psz_user = var_CreateGetString( p_demux, "rtsp-user" );
psz_pwd = var_CreateGetString( p_demux, "rtsp-pwd" );
describe:
authenticator.setUsernameAndPassword( (const char*)psz_user, (const char*)psz_pwd );
p_sdp = p_sys->rtsp->describeURL( psz_url,
&authenticator, var_CreateGetBool( p_demux, "rtsp-kasenna" ) );
if( psz_user ) free( psz_user );
if( psz_pwd ) free( psz_pwd );
if( p_sdp == NULL )
{
/* failure occured */
int i_code = 0;
const char *psz_error = p_sys->env->getResultMsg();
msg_Dbg( p_demux, "DESCRIBE failed with %d: %s", i_code, psz_error );
sscanf( psz_error, "%*sRTSP/%*s%3u", &i_code );
if( i_code == 401 )
{
char *psz_login = NULL; char *psz_password = NULL;
msg_Dbg( p_demux, "authentication failed" );
i_ret = intf_UserLoginPassword( p_demux, _("RTSP authentication"),
_("Please enter a valid login name and a password."),
&psz_login, &psz_password );
if( i_ret == DIALOG_OK_YES )
{
msg_Dbg( p_demux, "retrying with user=%s, pwd=%s",
psz_login, psz_password );
if( psz_login ) psz_user = psz_login;
if( psz_password ) psz_pwd = psz_password;
goto describe;
}
if( psz_login ) free( psz_login );
if( psz_password ) free( psz_password );
}
else if( !var_CreateGetBool( p_demux, "rtsp-http" ) )
{
/* Perhaps a firewall is being annoying. Try HTTP tunneling mode */
vlc_value_t val;
val.b_bool = VLC_TRUE;
msg_Dbg( p_demux, "we will now try HTTP tunneling mode" );
var_Set( p_demux, "rtsp-http", val );
if( psz_url ) free( psz_url );
if( p_sys->rtsp ) RTSPClient::close( p_sys->rtsp );
goto createnew;
}
i_ret = VLC_EGENERIC;
}
if( psz_url ) free( psz_url );
/* malloc-ated copy */
if( p_sys->p_sdp ) free( p_sys->p_sdp );
p_sys->p_sdp = strdup( (char*)p_sdp );
delete[] p_sdp;
return i_ret;
}
/*****************************************************************************
* Connect: prepares the subsessions and does the SETUP
*****************************************************************************/
static int SessionsSetup( demux_t *p_demux )
{
demux_sys_t *p_sys = p_demux->p_sys;
MediaSubsessionIterator *iter = NULL;
MediaSubsession *sub = NULL;
vlc_bool_t b_rtsp_tcp = VLC_FALSE;
int i_client_port;
int i_active_sessions = 0;
int i_return = VLC_SUCCESS;
unsigned int i_buffer = 0;
unsigned const thresh = 200000; /* RTP reorder threshold .2 second (default .1) */
b_rtsp_tcp = var_CreateGetBool( p_demux, "rtsp-tcp" );
i_client_port = var_CreateGetInteger( p_demux, "rtp-client-port" );
/* Initialise each media subsession */
iter = new MediaSubsessionIterator( *p_sys->ms );
while( ( sub = iter->next() ) != NULL )
{
Boolean bInit;
/* Value taken from mplayer */
if( !strcmp( sub->mediumName(), "audio" ) )
i_buffer = 100000;
else if( !strcmp( sub->mediumName(), "video" ) )
i_buffer = 2000000;
else continue;
if( i_client_port != -1 )
{
sub->setClientPortNum( i_client_port );
i_client_port += 2;
}
if( strcasestr( sub->codecName(), "REAL" ) )
{
msg_Info( p_demux, "real codec detected, using real-RTSP instead" );
i_return = VLC_EGENERIC;
break;
}
if( !strcmp( sub->codecName(), "X-ASF-PF" ) )
bInit = sub->initiate( 4 ); /* Constant ? */
else
bInit = sub->initiate();
if( !bInit )
{
msg_Warn( p_demux, "RTP subsession '%s/%s' failed (%s)",
sub->mediumName(), sub->codecName(),
p_sys->env->getResultMsg() );
}
else
{
if( sub->rtpSource() != NULL )
{
int fd = sub->rtpSource()->RTPgs()->socketNum();
/* Increase the buffer size */
if( i_buffer > 0 )
increaseReceiveBufferTo( *p_sys->env, fd<