X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fdemux%2Flivedotcom.cpp;h=b55359ec7d8ccbaf41fe4b616ff009205494abe8;hb=1a2878255a326bfaf90bea5badfe1a9f684338b3;hp=55f34c68caab1cd103a77d2aafc9baa187b9cab7;hpb=0e79f464081441bb979427207931010ad694691a;p=vlc diff --git a/modules/demux/livedotcom.cpp b/modules/demux/livedotcom.cpp index 55f34c68ca..b55359ec7d 100644 --- a/modules/demux/livedotcom.cpp +++ b/modules/demux/livedotcom.cpp @@ -1,7 +1,7 @@ /***************************************************************************** - * live.cpp : live.com support. + * live555.cpp : LIVE555 Streaming Media support. ***************************************************************************** - * Copyright (C) 2003-2004 the VideoLAN team + * Copyright (C) 2003-2006 the VideoLAN team * $Id$ * * Authors: Laurent Aimar @@ -18,7 +18,7 @@ * * 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. *****************************************************************************/ /***************************************************************************** @@ -64,16 +64,24 @@ static void Close( vlc_object_t * ); "value should be set in millisecond units." ) #define KASENNA_TEXT N_( "Kasenna RTSP dialect") -#define KASENNA_LONGTEXT N_( "Kasenna server speak an old and unstandard " \ +#define KASENNA_LONGTEXT N_( "Kasenna servers use an old and unstandard " \ "dialect of RTSP. When you set this parameter, VLC will try this dialect "\ - "for communication. In this mode you cannot talk to normal RTSP servers." ) + "for communication. In this mode you cannot connect to normal RTSP servers." ) + +#define USER_TEXT N_("RTSP user name") +#define USER_LONGTEXT N_("Allows you to modify the user name that will " \ + "be used for authenticating the connection.") +#define PASS_TEXT N_("RTSP password") +#define PASS_LONGTEXT N_("Allows you to modify the password that will be " \ + "used for the connection.") vlc_module_begin(); - set_description( _("live.com (RTSP/RTP/SDP) demuxer" ) ); + set_description( _("RTP/RTSP/SDP demuxer (using Live555)" ) ); set_capability( "demux2", 50 ); - set_shortname( "Live.com RTP/RTSP"); + set_shortname( "RTP/RTSP"); set_callbacks( Open, Close ); add_shortcut( "live" ); + add_shortcut( "livedotcom" ); set_category( CAT_INPUT ); set_subcategory( SUBCAT_INPUT_DEMUX ); @@ -86,10 +94,26 @@ vlc_module_begin(); add_bool( "rtsp-tcp", 0, NULL, N_("Use RTP over RTSP (TCP)"), N_("Use RTP over RTSP (TCP)"), VLC_TRUE ); + add_integer( "rtp-client-port", -1, NULL, + N_("Client port"), + N_("Port to use for the RTP source of the session"), VLC_TRUE ); +#if LIVEMEDIA_LIBRARY_VERSION_INT > 1130457500 + add_bool( "rtsp-http", 0, NULL, + N_("Tunnel RTSP and RTP over HTTP"), + N_("Tunnel RTSP and RTP over HTTP"), VLC_TRUE ); + add_integer( "rtsp-http-port", 80, NULL, + N_("HTTP tunnel port"), + 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 ); + 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 ); + add_string( "rtsp-pwd", NULL, NULL, PASS_TEXT, + PASS_LONGTEXT, VLC_TRUE ); vlc_module_end(); /* TODO: @@ -104,6 +128,7 @@ vlc_module_end(); /***************************************************************************** * Local prototypes *****************************************************************************/ + typedef struct { demux_t *p_demux; @@ -130,6 +155,14 @@ typedef struct } live_track_t; +struct timeout_thread_t +{ + VLC_COMMON_MEMBERS + + int64_t i_remain; + demux_sys_t *p_sys; +}; + struct demux_sys_t { char *p_sdp; /* XXX mallocated */ @@ -146,6 +179,7 @@ struct demux_sys_t mtime_t i_pcr; mtime_t i_pcr_start; mtime_t i_pcr_previous; + mtime_t i_pcr_repeatdate; int i_pcr_repeats; /* Asf */ @@ -156,6 +190,11 @@ struct demux_sys_t mtime_t i_length; mtime_t i_start; + /* timeout thread information */ + int i_timeout; /* session timeout value in seconds */ + vlc_bool_t b_timeout_call;/* mark to send an RTSP call to prevent server timeout */ + timeout_thread_t *p_timeout; /* the actual thread that makes sure we don't timeout */ + /* */ vlc_bool_t b_multicast; /* true if one of the tracks is multicasted */ vlc_bool_t b_no_data; /* true if we never receive any data */ @@ -168,7 +207,6 @@ static int Demux ( demux_t * ); static int Control( demux_t *, int, va_list ); static int ParseASF( demux_t * ); - static int RollOverTcp( demux_t * ); static void StreamRead( void *, unsigned int, unsigned int, @@ -176,6 +214,8 @@ static void StreamRead( void *, unsigned int, unsigned int, static void StreamClose( void * ); static void TaskInterrupt( void * ); +static void TimeoutPrevention( timeout_thread_t * ); + #if LIVEMEDIA_LIBRARY_VERSION_INT >= 1117756800 static unsigned char* parseH264ConfigStr( char const* configStr, unsigned int& configSize ); @@ -187,7 +227,7 @@ static unsigned char* parseH264ConfigStr( char const* configStr, static int Open ( vlc_object_t *p_this ) { demux_t *p_demux = (demux_t*)p_this; - demux_sys_t *p_sys; + demux_sys_t *p_sys = NULL; MediaSubsessionIterator *iter; MediaSubsession *sub; @@ -195,6 +235,9 @@ static int Open ( vlc_object_t *p_this ) 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; @@ -220,6 +263,8 @@ static int Open ( vlc_object_t *p_this ) p_demux->pf_demux = Demux; p_demux->pf_control= Control; p_demux->p_sys = p_sys = (demux_sys_t*)malloc( sizeof( demux_sys_t ) ); + if( !p_sys ) return VLC_ENOMEM; + p_sys->p_sdp = NULL; p_sys->scheduler = NULL; p_sys->env = NULL; @@ -230,12 +275,16 @@ static int Open ( vlc_object_t *p_this ) p_sys->i_pcr = 0; p_sys->i_pcr_start = 0; p_sys->i_pcr_previous = 0; + p_sys->i_pcr_repeatdate = 0; p_sys->i_pcr_repeats = 0; p_sys->i_length = 0; p_sys->i_start = 0; p_sys->p_out_asf = NULL; p_sys->b_no_data = VLC_TRUE; p_sys->i_no_data_ti = 0; + p_sys->p_timeout = NULL; + 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); @@ -261,9 +310,18 @@ static int Open ( vlc_object_t *p_this ) { 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" ) ) == NULL ) + "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() ); @@ -275,8 +333,15 @@ static int Open ( vlc_object_t *p_this ) psz_options = p_sys->rtsp->sendOptionsCmd( psz_url ); if( psz_options ) delete [] psz_options; - p_sdp = (uint8_t*)p_sys->rtsp->describeURL( psz_url, - NULL ) ; + 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)", @@ -285,6 +350,8 @@ static int Open ( vlc_object_t *p_this ) goto error; } free( psz_url ); + free( psz_user ); + free( psz_pwd ); /* malloc-ated copy */ p_sys->p_sdp = strdup( (char*)p_sdp ); @@ -342,6 +409,11 @@ static int Open ( vlc_object_t *p_this ) { 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" ) ) @@ -355,6 +427,13 @@ static int Open ( vlc_object_t *p_this ) 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 ) { @@ -397,6 +476,27 @@ static int Open ( vlc_object_t *p_this ) 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 ); + } } /* Create all es struct */ @@ -457,6 +557,20 @@ static int Open ( vlc_object_t *p_this ) { tk->fmt.i_codec = VLC_FOURCC( 'a', 'l', 'a', 'w' ); } + else if( !strncmp( sub->codecName(), "G726", 4 ) ) + { + tk->fmt.i_codec = VLC_FOURCC( 'g', '7', '2', '6' ); + tk->fmt.audio.i_rate = 8000; + tk->fmt.audio.i_channels = 1; + if( !strcmp( sub->codecName()+5, "40" ) ) + tk->fmt.i_bitrate = 40000; + else if( !strcmp( sub->codecName()+5, "32" ) ) + tk->fmt.i_bitrate = 32000; + else if( !strcmp( sub->codecName()+5, "24" ) ) + tk->fmt.i_bitrate = 24000; + else if( !strcmp( sub->codecName()+5, "16" ) ) + tk->fmt.i_bitrate = 16000; + } else if( !strcmp( sub->codecName(), "AMR" ) ) { tk->fmt.i_codec = VLC_FOURCC( 's', 'a', 'm', 'r' ); @@ -480,6 +594,12 @@ static int Open ( vlc_object_t *p_this ) memcpy( tk->fmt.p_extra, p_extra, i_extra ); delete[] p_extra; } + +#if LIVEMEDIA_LIBRARY_VERSION_INT >= 1141257600 + /* Because the "faad" decoder does not handle the LATM data length field + at the start of each returned LATM frame, tell the RTP source to omit it. */ + ((MPEG4LATMAudioRTPSource*)sub->rtpSource())->omitLATMDataLengthField(); +#endif } else if( !strcmp( sub->codecName(), "MPEG4-GENERIC" ) ) { @@ -651,6 +771,13 @@ error: if( p_sys->ms ) Medium::close( p_sys->ms ); if( p_sys->rtsp ) Medium::close( p_sys->rtsp ); if( p_sys->env ) RECLAIM_ENV(p_sys->env); + if( p_sys->p_timeout ) + { + p_sys->p_timeout->b_die = VLC_TRUE; + vlc_thread_join( p_sys->p_timeout ); + vlc_object_detach( p_sys->p_timeout ); + vlc_object_destroy( p_sys->p_timeout ); + } 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 ); @@ -688,6 +815,14 @@ static void Close( vlc_object_t *p_this ) Medium::close( p_sys->ms ); + if( p_sys->p_timeout ) + { + p_sys->p_timeout->b_die = VLC_TRUE; + vlc_thread_join( p_sys->p_timeout ); + vlc_object_detach( p_sys->p_timeout ); + 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->scheduler ) delete p_sys->scheduler; @@ -708,6 +843,16 @@ static int Demux( demux_t *p_demux ) mtime_t i_pcr = 0; int i; + /* Check if we need to send the server a Keep-A-Live signal */ + if( p_sys->b_timeout_call && p_sys->rtsp && p_sys->ms ) + { +#if LIVEMEDIA_LIBRARY_VERSION_INT >= 1138089600 + char *psz_bye = NULL; + p_sys->rtsp->getMediaSessionParameter( *p_sys->ms, NULL, psz_bye ); +#endif + p_sys->b_timeout_call = VLC_FALSE; + } + for( i = 0; i < p_sys->i_track; i++ ) { live_track_t *tk = p_sys->track[i]; @@ -741,17 +886,21 @@ static int Demux( demux_t *p_demux ) /* This tests for that, so we can later decide to end this session */ if( i_pcr > 0 && p_sys->i_pcr == p_sys->i_pcr_previous ) { + if( p_sys->i_pcr_repeats == 0 ) + p_sys->i_pcr_repeatdate = mdate(); p_sys->i_pcr_repeats++; } else { p_sys->i_pcr_previous = p_sys->i_pcr; + p_sys->i_pcr_repeatdate = 0; p_sys->i_pcr_repeats = 0; } - - if( p_sys->i_pcr_repeats > 5 ) + + if( p_sys->i_pcr_repeats > 5 && mdate() > p_sys->i_pcr_repeatdate + 1000000 ) { - /* 5 seemed a reasonable value. no basis whatsoever though */ + /* We need at least 5 repeats over at least a second of time before we EOF */ + msg_Dbg( p_demux, "suspect EOF due to end of VoD session" ); return 0; } @@ -879,23 +1028,16 @@ static int Control( demux_t *p_demux, int i_query, va_list args ) if( p_sys->rtsp && p_sys->i_length > 0 ) { - int i; - if( !p_sys->rtsp->playMediaSession( *p_sys->ms, time ) ) { - msg_Err( p_demux, "PLAY failed %s", p_sys->env->getResultMsg() ); + msg_Err( p_demux, "PLAY failed %s", + p_sys->env->getResultMsg() ); return VLC_EGENERIC; } p_sys->i_start = (mtime_t)(f * (double)p_sys->i_length); p_sys->i_pcr_start = 0; p_sys->i_pcr = 0; -#if 0 /* disabled. probably useless */ - for( i = 0; i < p_sys->i_track; i++ ) - { - p_sys->track[i]->i_pts = 0; - } -#endif return VLC_SUCCESS; } return VLC_SUCCESS; @@ -976,6 +1118,8 @@ static int RollOverTcp( demux_t *p_demux ) MediaSubsession *sub; char *psz_url; char *psz_options; + char *psz_user; + char *psz_pwd; uint8_t *p_sdp; int i_tk; @@ -990,7 +1134,7 @@ static int RollOverTcp( demux_t *p_demux ) /* Reopen rtsp client */ if( ( p_sys->rtsp = RTSPClient::createNew(*p_sys->env, 1/*verbose*/, - "VLC Media Player" ) ) == NULL ) + "VLC media player" ) ) == NULL ) { msg_Err( p_demux, "RTSPClient::createNew failed (%s)", p_sys->env->getResultMsg() ); @@ -1002,9 +1146,20 @@ static int RollOverTcp( demux_t *p_demux ) if( ( psz_options = p_sys->rtsp->sendOptionsCmd( psz_url ) ) ) delete [] psz_options; - p_sdp = (uint8_t*)p_sys->rtsp->describeURL( psz_url, - NULL, var_CreateGetBool( p_demux, "rtsp-kasenna" ) ); + 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" ) ); + free( psz_url ); + free( psz_user ); + free( psz_pwd ); + if( p_sdp == NULL ) { msg_Err( p_demux, "describeURL failed (%s)", @@ -1163,7 +1318,16 @@ static void StreamRead( void *p_private, unsigned int i_size, msg_Warn( p_demux, "buffer overflow" ); } /* FIXME could i_size be > buffer size ? */ - if( tk->fmt.i_codec == VLC_FOURCC('H','2','6','1') ) + if( tk->fmt.i_codec == VLC_FOURCC('s','a','m','r') || + tk->fmt.i_codec == VLC_FOURCC('s','a','w','b') ) + { + AMRAudioSource *amrSource = (AMRAudioSource*)tk->readSource; + + p_block = block_New( p_demux, i_size + 1 ); + p_block->p_buffer[0] = amrSource->lastFrameHeader(); + memcpy( p_block->p_buffer + 1, tk->p_buffer, i_size ); + } + else if( tk->fmt.i_codec == VLC_FOURCC('H','2','6','1') ) { #if LIVEMEDIA_LIBRARY_VERSION_INT >= 1081468800 H261VideoRTPSource *h261Source = (H261VideoRTPSource*)tk->rtpSource; @@ -1269,6 +1433,32 @@ static void TaskInterrupt( void *p_private ) p_demux->p_sys->event = 0xff; } +/***************************************************************************** + * + *****************************************************************************/ +static void TimeoutPrevention( timeout_thread_t *p_timeout ) +{ + p_timeout->b_die = VLC_FALSE; + p_timeout->i_remain = (int64_t)p_timeout->p_sys->i_timeout -2; + p_timeout->i_remain *= 1000000; + + vlc_thread_ready( p_timeout ); + + /* Avoid lock */ + while( !p_timeout->b_die ) + { + if( p_timeout->i_remain <= 0 ) + { + p_timeout->i_remain = (int64_t)p_timeout->p_sys->i_timeout -2; + p_timeout->i_remain *= 1000000; + p_timeout->p_sys->b_timeout_call = VLC_TRUE; + msg_Dbg( p_timeout, "reset the timeout timer" ); + } + p_timeout->i_remain -= 200000; + msleep( 200000 ); /* 200 ms */ + } +} + /***************************************************************************** * *****************************************************************************/