]> git.sesse.net Git - vlc/blobdiff - modules/demux/livedotcom.cpp
ALL: s/livedotcom/live555/
[vlc] / modules / demux / livedotcom.cpp
index 55f34c68caab1cd103a77d2aafc9baa187b9cab7..b55359ec7d8ccbaf41fe4b616ff009205494abe8 100644 (file)
@@ -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 <fenrir@via.ecp.fr>
@@ -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 */
+    }
+}
+
 /*****************************************************************************
  *
  *****************************************************************************/