]> git.sesse.net Git - vlc/blobdiff - src/input/input.c
* Changed the way decoder_fifo_t works ;
[vlc] / src / input / input.c
index 5cac03052efebe993d3773c5a8ef87bf3e6918aa..219f4f98c7a6f00ea0fe0633966e28a67b64b1f1 100644 (file)
@@ -3,8 +3,8 @@
  * Read an MPEG2 stream, demultiplex and parse it before sending it to
  * decoders.
  *****************************************************************************
- * Copyright (C) 1998, 1999, 2000 VideoLAN
- * $Id: input.c,v 1.152 2001/11/09 13:49:26 massiot Exp $
+ * Copyright (C) 1998-2001 VideoLAN
+ * $Id: input.c,v 1.165 2001/12/12 02:13:50 sam Exp $
  *
  * Authors: Christophe Massiot <massiot@via.ecp.fr>
  *
 
 #ifdef WIN32
 #   include <winsock2.h>
+#   include <ws2tcpip.h>
 #elif !defined( SYS_BEOS ) && !defined( SYS_NTO )
 #   include <netdb.h>                                         /* hostent ... */
 #   include <sys/socket.h>
 #   include <netinet/in.h>
-#   include <arpa/inet.h>
+#   ifdef HAVE_ARPA_INET_H
+#       include <arpa/inet.h>                    /* inet_ntoa(), inet_aton() */
+#   endif
 #endif
 
 #ifdef HAVE_SYS_TIMES_H
 #   include <sys/times.h>
 #endif
 
-#include "config.h"
 #include "common.h"
+#include "intf_msg.h"
 #include "threads.h"
 #include "mtime.h"
+#include "tests.h"
 #include "netutils.h"
 #include "modules.h"
 
-#include "intf_msg.h"
 #include "intf_playlist.h"
 
 #include "stream_control.h"
@@ -76,8 +79,6 @@
 
 #include "interface.h"
 
-#include "main.h"
-
 /*****************************************************************************
  * Local prototypes
  *****************************************************************************/
@@ -261,6 +262,29 @@ static void RunThread( input_thread_t *p_input )
 
         vlc_mutex_lock( &p_input->stream.stream_lock );
 
+        if( p_input->stream.p_new_program )
+        {
+            if( p_input->pf_set_program != NULL )
+            {
+
+                p_input->pf_set_program( p_input, 
+                        p_input->stream.p_new_program );
+
+                for( i = 0; i < p_input->stream.i_pgrm_number; i++ )
+                {
+                    pgrm_descriptor_t * p_pgrm
+                                            = p_input->stream.pp_programs[i];
+                    /* Escape all decoders for the stream discontinuity they
+                     * will encounter. */
+                    input_EscapeDiscontinuity( p_input, p_pgrm );
+
+                    /* Reinitialize synchro. */
+                    p_pgrm->i_synchro_state = SYNCHRO_REINIT;
+                }
+            }
+            p_input->stream.p_new_program = NULL;
+        }
+        
         if( p_input->stream.p_new_area )
         {
             if( p_input->stream.b_seekable && p_input->pf_set_area != NULL )
@@ -406,6 +430,7 @@ static int InitThread( input_thread_t * p_input )
     p_input->pf_init_bit_stream= f.pf_init_bit_stream;
     p_input->pf_read          = f.pf_read;
     p_input->pf_set_area      = f.pf_set_area;
+    p_input->pf_set_program   = f.pf_set_program;
     p_input->pf_demux         = f.pf_demux;
     p_input->pf_new_packet    = f.pf_new_packet;
     p_input->pf_new_pes       = f.pf_new_pes;
@@ -416,8 +441,10 @@ static int InitThread( input_thread_t * p_input )
 
 #if !defined( SYS_BEOS ) && !defined( SYS_NTO )
     /* FIXME : this is waaaay too kludgy */
-    if( ( strlen( p_input->p_source ) > 3)
-          && !strncasecmp( p_input->p_source, "ts:", 3 ) )
+    if( ( strlen( p_input->p_source ) >= 10
+          && !strncasecmp( p_input->p_source, "udpstream:", 10 ) )
+          || ( strlen( p_input->p_source ) >= 4
+                && !strncasecmp( p_input->p_source, "udp:", 4 ) ) )
     {
         /* Network stream */
         NetworkOpen( p_input );
@@ -432,15 +459,27 @@ static int InitThread( input_thread_t * p_input )
     }
     else 
 #endif
-        if( ( strlen( p_input->p_source ) > 4 )
-              && !strncasecmp( p_input->p_source, "dvd:", 4 ) )
+        if( ( ( ( strlen( p_input->p_source ) > 4 )
+                  && !strncasecmp( p_input->p_source, "dvd:", 4 ) )
+              || TestMethod( INPUT_METHOD_VAR, "dvd" ) ) 
+             && f.pf_open != NULL )
     {
         /* DVD - this is THE kludge */
         f.pf_open( p_input );
         p_input->stream.i_method = INPUT_METHOD_DVD;
     }
+    else if( ( ( ( strlen( p_input->p_source ) > 8 )
+                   && !strncasecmp( p_input->p_source, "dvdread:", 8 ) )
+                || TestMethod( INPUT_METHOD_VAR, "dvdread" ) )
+             && f.pf_open != NULL )
+    {
+        /* DVDRead - this is THE kludge */
+        f.pf_open( p_input );
+        p_input->stream.i_method = INPUT_METHOD_DVD;
+    }
     else if( ( strlen( p_input->p_source ) > 4 )
-               && !strncasecmp( p_input->p_source, "vlc:", 4 ) )
+               && !strncasecmp( p_input->p_source, "vlc:", 4 )
+               && f.pf_open != NULL )
     {
         /* Dummy input - very kludgy */
         f.pf_open( p_input );
@@ -534,7 +573,6 @@ static void EndThread( input_thread_t * p_input )
 
     /* Release modules */
     module_Unneed( p_input->p_input_module );
-
 }
 
 /*****************************************************************************
@@ -546,8 +584,10 @@ static void CloseThread( input_thread_t * p_input )
 
 #if !defined( SYS_BEOS ) && !defined( SYS_NTO )
     /* Close stream */
-    if( ( strlen( p_input->p_source ) > 3)
-          && !strncasecmp( p_input->p_source, "ts:", 3 ) )
+    if( ( strlen( p_input->p_source ) > 10
+          && !strncasecmp( p_input->p_source, "udpstream:", 10 ) )
+          || ( strlen( p_input->p_source ) > 4
+                && !strncasecmp( p_input->p_source, "udp:", 4 ) ) )
     {
         NetworkClose( p_input );
     }
@@ -558,8 +598,15 @@ static void CloseThread( input_thread_t * p_input )
     }
     else 
 #endif
-    if( ( strlen( p_input->p_source ) > 4 )
-          && !strncasecmp( p_input->p_source, "dvd:", 4 ) )
+    if( ( ( strlen( p_input->p_source ) > 4 )
+            && !strncasecmp( p_input->p_source, "dvd:", 4 ) )
+        || TestMethod( INPUT_METHOD_VAR, "dvd" ) )
+    {
+        f.pf_close( p_input );
+    }
+    else if( ( ( strlen( p_input->p_source ) > 8 )
+                 && !strncasecmp( p_input->p_source, "dvdread:", 8 ) )
+             || TestMethod( INPUT_METHOD_VAR, "dvdread" ) )
     {
         f.pf_close( p_input );
     }
@@ -629,7 +676,14 @@ static void FileOpen( input_thread_t * p_input )
     {
         int i_size = strlen( psz_name );
 
-        if( ( i_size > 4 )
+        if( ( i_size > 8 )
+            && !strncasecmp( psz_name, "dvdread:", 8 ) )
+        {
+            /* get rid of the 'dvdread:' stuff and try again */
+            psz_name += 8;
+            i_stat = stat( psz_name, &stat_info );
+        }
+        else if( ( i_size > 4 )
             && !strncasecmp( psz_name, "dvd:", 4 ) )
         {
             /* get rid of the 'dvd:' stuff and try again */
@@ -722,127 +776,123 @@ static void FileClose( input_thread_t * p_input )
 static void NetworkOpen( input_thread_t * p_input )
 {
     char                *psz_server = NULL;
-    char                *psz_broadcast = NULL;
-    int                 i_port = 0;
+    char                *psz_bind = NULL;
+    int                 i_server_port = 0;
+    int                 i_bind_port = 0;
     int                 i_opt;
     int                 i_opt_size;
     struct sockaddr_in  sock;
-    unsigned int        i_mc_group;
-
-#ifdef WIN32
-    WSADATA Data;
-    int i_err;
-#endif
-    
-#ifdef WIN32
-    /* WinSock Library Init. */
-    i_err = WSAStartup( MAKEWORD( 1, 1 ), &Data );
 
-    if( i_err )
-    {
-        intf_ErrMsg( "input: can't initiate WinSocks, error %i", i_err );
-        return ;
-    }
-#endif
-    
-    /* Get the remote server */
+    /* Get the remote server. Syntax is :
+     * udp[stream]:[/][/][serveraddr[:serverport]][@[bindaddr]:[bindport]] */
     if( p_input->p_source != NULL )
     {
-        psz_server = p_input->p_source;
+        char * psz_parser = p_input->p_source;
+        char * psz_server_port = NULL;
+        char * psz_bind_port = NULL;
 
         /* Skip the protocol name */
-        while( *psz_server && *psz_server != ':' )
+        while( *psz_parser && *psz_parser != ':' )
         {
-            psz_server++;
+            psz_parser++;
         }
 
         /* Skip the "://" part */
-        while( *psz_server && (*psz_server == ':' || *psz_server == '/') )
+        while( *psz_parser && (*psz_parser == ':' || *psz_parser == '/') )
         {
-            psz_server++;
+            psz_parser++;
         }
 
-        /* Found a server name */
-        if( *psz_server )
+        if( *psz_parser && *psz_parser != '@' )
         {
-            char *psz_port = psz_server;
+            /* Found server */
+            psz_server = psz_parser;
 
-            /* Skip the hostname part */
-            while( *psz_port && *psz_port != ':' && *psz_port != '/' )
+            while( *psz_parser && *psz_parser != ':' && *psz_parser != '@' )
             {
-                psz_port++;
+                psz_parser++;
             }
 
-            /* Found a port name or a broadcast addres */
-            if( *psz_port )
+            if( *psz_parser == ':' )
             {
-                /* That's a port name */
-                if( *psz_port == ':' )
-                {
-                    *psz_port = '\0';
-                    psz_port++;
-                    i_port = atoi( psz_port );
-                }
+                /* Found server port */
+                *psz_parser = '\0'; /* Terminate server name */
+                psz_parser++;
+                psz_server_port = psz_parser;
 
-                /* Search for '/' just after the port in case
-                 * we also have a broadcast address */
-                psz_broadcast = psz_port;
-                while( *psz_broadcast && *psz_broadcast != '/' )
+                while( *psz_parser && *psz_parser != '@' )
                 {
-                    psz_broadcast++;
+                    psz_parser++;
                 }
+            }
+        }
 
-                if( *psz_broadcast )
-                {
-                    *psz_broadcast = '\0';
-                    psz_broadcast++;
-                    while( *psz_broadcast && *psz_broadcast == '/' )
-                    {
-                        psz_broadcast++;
-                    }
-                }
-                else
+        if( *psz_parser == '@' )
+        {
+            /* Found bind address or bind port */
+            *psz_parser = '\0'; /* Terminate server port or name if necessary */
+            psz_parser++;
+
+            if( *psz_parser && *psz_parser != ':' )
+            {
+                /* Found bind address */
+                psz_bind = psz_parser;
+
+                while( *psz_parser && *psz_parser != ':' )
                 {
-                    psz_broadcast = NULL;
+                    psz_parser++;
                 }
             }
+
+            if( *psz_parser == ':' )
+            {
+                /* Found bind port */
+                *psz_parser = '\0'; /* Terminate bind address if necessary */
+                psz_parser++;
+
+                psz_bind_port = psz_parser;
+            }
         }
-        else
+
+        /* Convert ports format */
+        if( psz_server_port != NULL )
         {
-            psz_server = NULL;
+            i_server_port = strtol( psz_server_port, &psz_parser, 10 );
+            if( *psz_parser )
+            {
+                intf_ErrMsg( "input error: cannot parse server port near %s",
+                             psz_parser );
+                p_input->b_error = 1;
+                return;
+            }
         }
-    }
 
-    /* Check that we got a valid server */
-    if( psz_server == NULL )
-    {
-        psz_server = main_GetPszVariable( INPUT_SERVER_VAR, 
-                                          INPUT_SERVER_DEFAULT );
+        if( psz_bind_port != NULL )
+        {
+            i_bind_port = strtol( psz_bind_port, &psz_parser, 10 );
+            if( *psz_parser )
+            {
+                intf_ErrMsg( "input error: cannot parse bind port near %s",
+                             psz_parser );
+                p_input->b_error = 1;
+                return;
+            }
+        }
     }
-
-    /* Check that we got a valid port */
-    if( i_port == 0 )
+    else
     {
-        i_port = main_GetIntVariable( INPUT_PORT_VAR, INPUT_PORT_DEFAULT );
+        /* This is required or NetworkClose will never be called */
+        p_input->p_source = "ts: network input";
     }
 
-    if( psz_broadcast == NULL )
+    /* Check that we got a valid port */
+    if( i_bind_port == 0 )
     {
-        /* Are we broadcasting ? */
-        if( main_GetIntVariable( INPUT_BROADCAST_VAR,
-                                 INPUT_BROADCAST_DEFAULT ) )
-        {
-            psz_broadcast = main_GetPszVariable( INPUT_BCAST_ADDR_VAR,
-                                                 INPUT_BCAST_ADDR_DEFAULT );
-        }
-        else
-        {
-            psz_broadcast = NULL; 
-        }
+        i_bind_port = main_GetIntVariable( INPUT_PORT_VAR, INPUT_PORT_DEFAULT );
     }
 
-    intf_WarnMsg( 2, "input: server=%s port=%d broadcast=%s",
-                     psz_server, i_port, psz_broadcast );
+    intf_WarnMsg( 2, "input: server=%s:%d local=%s:%d",
+                     psz_server, i_server_port, psz_bind, i_bind_port );
 
     /* Open a SOCK_DGRAM (UDP) socket, in the AF_INET domain, automatic (0)
      * protocol */
@@ -872,11 +922,8 @@ static void NetworkOpen( input_thread_t * p_input )
     if( setsockopt( p_input->i_handle, SOL_SOCKET, SO_RCVBUF,
                     (void *) &i_opt, sizeof( i_opt ) ) == -1 )
     {
-        intf_ErrMsg( "input error: can't configure socket (SO_RCVBUF: %s)", 
-                     strerror(errno));
-        close( p_input->i_handle );
-        p_input->b_error = 1;
-        return;
+        intf_WarnMsg( 1, "input warning: can't configure socket (SO_RCVBUF: %s)", 
+                         strerror(errno));
     }
 
     /* Check if we really got what we have asked for, because Linux, etc.
@@ -887,21 +934,17 @@ static void NetworkOpen( input_thread_t * p_input )
     if( getsockopt( p_input->i_handle, SOL_SOCKET, SO_RCVBUF,
                     (void*) &i_opt, &i_opt_size ) == -1 )
     {
-        intf_ErrMsg( "input error: can't configure socket (SO_RCVBUF: %s)", 
-                     strerror(errno));
-        close( p_input->i_handle );
-        p_input->b_error = 1;
-        return;
+        intf_WarnMsg( 1, "input warning: can't query socket (SO_RCVBUF: %s)", 
+                         strerror(errno));
     }
-    
-    if( i_opt < 0x80000 )
+    else if( i_opt < 0x80000 )
     {
-        intf_WarnMsg( 1, "input warning: socket receive buffer size just 0x%x"
-                         " instead of 0x%x bytes.", i_opt, 0x80000 );
+        intf_WarnMsg( 1, "input warning: socket buffer size is 0x%x"
+                         " instead of 0x%x", i_opt, 0x80000 );
     }
 
     /* Build the local socket */
-    if ( network_BuildLocalAddr( &sock, i_port, psz_broadcast ) == -1 )
+    if ( network_BuildAddr( &sock, psz_bind, i_bind_port ) == -1 )
     {
         intf_ErrMsg( "input error: can't build local address" );
         close( p_input->i_handle );
@@ -909,15 +952,6 @@ static void NetworkOpen( input_thread_t * p_input )
         return;
     }
 
-    /* Required for IP_ADD_MEMBERSHIP */
-    i_mc_group = sock.sin_addr.s_addr;
-
-#if defined( WIN32 )
-    sock.sin_addr.s_addr = INADDR_ANY;
-    
-#define IN_MULTICAST(a)         IN_CLASSD(a)
-#endif
-
     /* Bind it */
     if( bind( p_input->i_handle, (struct sockaddr *)&sock, 
               sizeof( sock ) ) < 0 )
@@ -928,38 +962,51 @@ static void NetworkOpen( input_thread_t * p_input )
         return;
     }
 
+    /* Allow broadcast reception if we bound on INADDR_ANY */
+    if( psz_bind == NULL )
+    {
+        i_opt = 1;
+        if( setsockopt( p_input->i_handle, SOL_SOCKET, SO_BROADCAST,
+                        (void*) &i_opt, sizeof( i_opt ) ) == -1 )
+        {
+            intf_WarnMsg( 1, "input warning: can't configure socket (SO_BROADCAST: %s)", 
+                             strerror(errno));
+        }
+    }
+
     /* Join the multicast group if the socket is a multicast address */
+#ifndef IN_MULTICAST
+#   define IN_MULTICAST(a)         IN_CLASSD(a)
+#endif
 
-#ifndef WIN32    
-    if( IN_MULTICAST( ntohl(i_mc_group) ) )
+    if( IN_MULTICAST( ntohl(sock.sin_addr.s_addr) ) )
     {
         struct ip_mreq imr;
 
-        imr.imr_interface.s_addr = htonl(INADDR_ANY);
-        imr.imr_multiaddr.s_addr = i_mc_group;
+        imr.imr_interface.s_addr = INADDR_ANY;
+        imr.imr_multiaddr.s_addr = sock.sin_addr.s_addr;
         if( setsockopt( p_input->i_handle, IPPROTO_IP, IP_ADD_MEMBERSHIP,
                         (char*)&imr, sizeof(struct ip_mreq) ) == -1 )
         {
             intf_ErrMsg( "input error: failed to join IP multicast group (%s)",
                          strerror(errno) );
-            close( p_input->i_handle);
+            close( p_input->i_handle );
             p_input->b_error = 1;
             return;
         }
     }
     
-    /* Build socket for remote connection */
-    if ( network_BuildRemoteAddr( &sock, psz_server ) == -1 )
+    if( psz_server != NULL )
     {
-        intf_ErrMsg( "input error: can't build remote address" );
-        close( p_input->i_handle );
-        p_input->b_error = 1;
-        return;
-    }
+        /* Build socket for remote connection */
+        if ( network_BuildAddr( &sock, psz_server, i_server_port ) == -1 )
+        {
+            intf_ErrMsg( "input error: can't build remote address" );
+            close( p_input->i_handle );
+            p_input->b_error = 1;
+            return;
+        }
 
-    /* Only connect if the user has passed a valid host */
-    if( sock.sin_addr.s_addr != INADDR_ANY )
-    {
         /* Connect the socket */
         if( connect( p_input->i_handle, (struct sockaddr *) &sock,
                      sizeof( sock ) ) == (-1) )
@@ -971,7 +1018,6 @@ static void NetworkOpen( input_thread_t * p_input )
             return;
         }
     }
-#endif
 
     p_input->stream.b_pace_control = 0;
     p_input->stream.b_seekable = 0;
@@ -986,11 +1032,9 @@ static void NetworkOpen( input_thread_t * p_input )
  *****************************************************************************/
 static void NetworkClose( input_thread_t * p_input )
 {
-    close( p_input->i_handle );
+    intf_WarnMsg( 2, "input: closing network target `%s'", p_input->p_source );
 
-#ifdef WIN32 
-    WSACleanup();
-#endif
+    close( p_input->i_handle );
 }
 
 /*****************************************************************************
@@ -1006,22 +1050,6 @@ static void HTTPOpen( input_thread_t * p_input )
     struct sockaddr_in  sock;
     char                psz_buffer[256];
 
-#ifdef WIN32
-    WSADATA Data;
-    int i_err;
-#endif
-    
-#ifdef WIN32
-    /* WinSock Library Init. */
-    i_err = WSAStartup( MAKEWORD( 1, 1 ), &Data );
-
-    if( i_err )
-    {
-        intf_ErrMsg( "input: can't initiate WinSocks, error %i", i_err );
-        return ;
-    }
-#endif
-    
     /* Get the remote server */
     if( p_input->p_source != NULL )
     {
@@ -1190,26 +1218,24 @@ static void HTTPOpen( input_thread_t * p_input )
         }
 
         /* Build socket for proxy connection */
-        if ( network_BuildRemoteAddr( &sock, psz_proxy ) == -1 )
+        if ( network_BuildAddr( &sock, psz_proxy, i_proxy_port ) == -1 )
         {
             intf_ErrMsg( "input error: can't build remote address" );
             close( p_input->i_handle );
             p_input->b_error = 1;
             return;
         }
-        sock.sin_port = htons( i_proxy_port );
     }
     else
     {
         /* No proxy, direct connection */
-        if ( network_BuildRemoteAddr( &sock, psz_server ) == -1 )
+        if ( network_BuildAddr( &sock, psz_server, i_port ) == -1 )
         {
             intf_ErrMsg( "input error: can't build remote address" );
             close( p_input->i_handle );
             p_input->b_error = 1;
             return;
         }
-        sock.sin_port = htons( i_port );
     }
 
     /* Connect the socket */