]> git.sesse.net Git - vlc/blobdiff - modules/access/dvdnav.c
dvdnav: fix memory leak.
[vlc] / modules / access / dvdnav.c
index d6bcb5aa711a98983bafc6bdce12282d2193dd0c..9f5cf88343e97120621a65c77f07df48a78f58cb 100644 (file)
 #include <vlc_access.h>
 #include <vlc_demux.h>
 #include <vlc_charset.h>
+#include <vlc_fs.h>
+#include <vlc_url.h>
 
 #include <vlc_dialog.h>
 
 #ifdef HAVE_UNISTD_H
 #   include <unistd.h>
 #endif
-#ifdef HAVE_SYS_TYPES_H
-#   include <sys/types.h>
-#endif
+#include <sys/types.h>
 #ifdef HAVE_SYS_STAT_H
 #   include <sys/stat.h>
 #endif
@@ -96,9 +96,7 @@ vlc_module_begin ()
     add_bool( "dvdnav-menu", true, NULL,
         MENU_TEXT, MENU_LONGTEXT, false )
     set_capability( "access_demux", 5 )
-    add_shortcut( "dvd" )
-    add_shortcut( "dvdnav" )
-    add_shortcut( "file" )
+    add_shortcut( "dvd", "dvdnav", "file" )
     set_callbacks( Open, Close )
 vlc_module_end ()
 
@@ -138,19 +136,26 @@ struct demux_sys_t
     uint8_t  palette[4][4];
     bool b_spu_change;
 
-    /* */
-    int i_aspect;
+    /* Aspect ration */
+    struct {
+        unsigned i_num;
+        unsigned i_den;
+    } sar;
 
+    /* */
     int           i_title;
     input_title_t **title;
 
     /* lenght of program group chain */
     mtime_t     i_pgc_length;
+    int         i_vobu_index;
+    int         i_vobu_flush;
 };
 
 static int Control( demux_t *, int, va_list );
 static int Demux( demux_t * );
 static int DemuxBlock( demux_t *, const uint8_t *, int );
+static void DemuxForceStill( demux_t * );
 
 static void DemuxTitles( demux_t * );
 static void ESSubtitleUpdate( demux_t * );
@@ -183,12 +188,12 @@ static int Open( vlc_object_t *p_this )
     int         i_angle;
     char        *psz_name;
     char        *psz_code;
-    vlc_value_t val;
 
-    if( !p_demux->psz_path || !*p_demux->psz_path )
+    if( !p_demux->psz_file || !*p_demux->psz_file )
     {
         /* Only when selected */
-        if( !p_this->b_force ) return VLC_EGENERIC;
+        if( !p_demux->psz_access || !*p_demux->psz_access )
+            return VLC_EGENERIC;
 
         psz_name = var_CreateGetString( p_this, "dvd" );
         if( !psz_name )
@@ -197,11 +202,14 @@ static int Open( vlc_object_t *p_this )
         }
     }
     else
-        psz_name = ToLocaleDup( p_demux->psz_path );
+        psz_name = ToLocaleDup( p_demux->psz_file );
 
 #ifdef WIN32
-    if( psz_name[0] && psz_name[1] == ':' &&
-        psz_name[2] == '\\' && psz_name[3] == '\0' ) psz_name[2] = '\0';
+    /* Remove trailing backslash, otherwise dvdnav_open will fail */
+    if( *psz_name && *(psz_name + strlen(psz_name) - 1) == '\\' )
+    {
+        *(psz_name + strlen(psz_name) - 1) = '\0';
+    }
 #endif
 
     /* Try some simple probing to avoid going through dvdnav_open too often */
@@ -226,10 +234,13 @@ static int Open( vlc_object_t *p_this )
     p_sys->b_reset_pcr = false;
 
     ps_track_init( p_sys->tk );
-    p_sys->i_aspect = -1;
+    p_sys->sar.i_num = 0;
+    p_sys->sar.i_den = 0;
     p_sys->i_mux_rate = 0;
     p_sys->i_pgc_length = 0;
     p_sys->b_spu_change = false;
+    p_sys->i_vobu_index = 0;
+    p_sys->i_vobu_flush = 0;
 
     if( 1 )
     {
@@ -303,9 +314,7 @@ static int Open( vlc_object_t *p_this )
 
     DemuxTitles( p_demux );
 
-    var_Create( p_demux, "dvdnav-menu", VLC_VAR_BOOL|VLC_VAR_DOINHERIT );
-    var_Get( p_demux, "dvdnav-menu", &val );
-    if( val.b_bool )
+    if( var_CreateGetBool( p_demux, "dvdnav-menu" ) )
     {
         msg_Dbg( p_demux, "trying to go to dvd menu" );
 
@@ -330,16 +339,15 @@ static int Open( vlc_object_t *p_this )
         }
     }
 
-    var_Create( p_demux, "dvdnav-angle", VLC_VAR_INTEGER|VLC_VAR_DOINHERIT );
-    var_Get( p_demux, "dvdnav-angle", &val );
-    i_angle = val.i_int > 0 ? val.i_int : 1;
+    i_angle = var_CreateGetInteger( p_demux, "dvdnav-angle" );
+    if( i_angle <= 0 ) i_angle = 1;
 
     /* Update default_pts to a suitable value for dvdnav access */
     var_Create( p_demux, "dvdnav-caching", VLC_VAR_INTEGER|VLC_VAR_DOINHERIT );
 
     /* FIXME hack hack hack hack FIXME */
     /* Get p_input and create variable */
-    p_sys->p_input = vlc_object_find( p_demux, VLC_OBJECT_INPUT, FIND_PARENT );
+    p_sys->p_input = demux_GetParentInput( p_demux );
     var_Create( p_sys->p_input, "x-start", VLC_VAR_INTEGER );
     var_Create( p_sys->p_input, "y-start", VLC_VAR_INTEGER );
     var_Create( p_sys->p_input, "x-end", VLC_VAR_INTEGER );
@@ -409,6 +417,11 @@ static void Close( vlc_object_t *p_this )
         }
     }
 
+    /* Free the array of titles */
+    for( int i = 0; i < p_sys->i_title; i++ )
+        vlc_input_title_Delete( p_sys->title[i] );
+    TAB_CLEAN( p_sys->i_title, p_sys->title );
+
     dvdnav_close( p_sys->dvdnav );
     free( p_sys );
 }
@@ -609,6 +622,7 @@ static int Demux( demux_t *p_demux )
     {
     case DVDNAV_BLOCK_OK:   /* mpeg block */
         vlc_mutex_lock( &p_sys->still.lock );
+        vlc_timer_schedule( p_sys->still.timer, false, 0, 0 );
         p_sys->still.b_enabled = false;
         vlc_mutex_unlock( &p_sys->still.lock );
         if( p_sys->b_reset_pcr )
@@ -617,6 +631,12 @@ static int Demux( demux_t *p_demux )
             p_sys->b_reset_pcr = false;
         }
         DemuxBlock( p_demux, packet, i_len );
+        if( p_sys->i_vobu_index > 0 )
+        {
+            if( p_sys->i_vobu_flush == p_sys->i_vobu_index )
+                DemuxForceStill( p_demux );
+            p_sys->i_vobu_index++;
+        }
         break;
 
     case DVDNAV_NOP:    /* Nothing */
@@ -647,16 +667,7 @@ static int Demux( demux_t *p_demux )
 
         if( b_still_init )
         {
-            /* We send a dummy mpeg2 end of sequence to force still frame display */
-            static const uint8_t buffer[] = {
-                0x00, 0x00, 0x01, 0xe0, 0x00, 0x07,
-                0x80, 0x00, 0x00,
-                0x00, 0x00, 0x01, 0xB7,
-            };
-            DemuxBlock( p_demux, buffer, sizeof(buffer) );
-
-            bool b_empty;
-            es_out_Control( p_demux->out, ES_OUT_GET_EMPTY, &b_empty );
+            DemuxForceStill( p_demux );
             p_sys->b_reset_pcr = true;
         }
         msleep( 40000 );
@@ -729,10 +740,6 @@ static int Demux( demux_t *p_demux )
         msg_Dbg( p_demux, "     - vtsN=%d", event->new_vtsN );
         msg_Dbg( p_demux, "     - domain=%d", event->new_domain );
 
-        /* dvdnav_get_video_aspect / dvdnav_get_video_scale_permission */
-        /* TODO check if we always have VTS and CELL */
-        p_sys->i_aspect = dvdnav_get_video_aspect( p_sys->dvdnav );
-
         /* reset PCR */
         es_out_Control( p_demux->out, ES_OUT_RESET_PCR );
 
@@ -747,6 +754,28 @@ static int Demux( demux_t *p_demux )
             tk->b_seen = false;
         }
 
+#if defined(HAVE_DVDNAV_GET_VIDEO_RESOLUTION)
+        uint32_t i_width, i_height;
+        if( dvdnav_get_video_resolution( p_sys->dvdnav,
+                                         &i_width, &i_height ) )
+            i_width = i_height = 0;
+        switch( dvdnav_get_video_aspect( p_sys->dvdnav ) )
+        {
+        case 0:
+            p_sys->sar.i_num = 4 * i_height;
+            p_sys->sar.i_den = 3 * i_width;
+            break;
+        case 3:
+            p_sys->sar.i_num = 16 * i_height;
+            p_sys->sar.i_den =  9 * i_width;
+            break;
+        default:
+            p_sys->sar.i_num = 0;
+            p_sys->sar.i_den = 0;
+            break;
+        }
+#endif
+
         if( dvdnav_current_title_info( p_sys->dvdnav, &i_title,
                                        &i_part ) == DVDNAV_STATUS_OK )
         {
@@ -778,6 +807,8 @@ static int Demux( demux_t *p_demux )
 
         /* Store the lenght in time of the current PGC */
         p_sys->i_pgc_length = event->pgc_length / 90 * 1000;
+        p_sys->i_vobu_index = 0;
+        p_sys->i_vobu_flush = 0;
 
         /* FIXME is it correct or there is better way to know chapter change */
         if( dvdnav_current_title_info( p_sys->dvdnav, &i_title,
@@ -796,6 +827,22 @@ static int Demux( demux_t *p_demux )
 
     case DVDNAV_NAV_PACKET:
     {
+        p_sys->i_vobu_index = 1;
+        p_sys->i_vobu_flush = 0;
+
+        /* Look if we have need to force a flush (and when) */
+        const pci_gi_t *p_pci_gi = &dvdnav_get_current_nav_pci( p_sys->dvdnav )->pci_gi;
+        if( p_pci_gi->vobu_se_e_ptm != 0 && p_pci_gi->vobu_se_e_ptm < p_pci_gi->vobu_e_ptm )
+        {
+            const dsi_gi_t *p_dsi_gi = &dvdnav_get_current_nav_dsi( p_sys->dvdnav )->dsi_gi;
+            if( p_dsi_gi->vobu_3rdref_ea != 0 )
+                p_sys->i_vobu_flush = p_dsi_gi->vobu_3rdref_ea;
+            else if( p_dsi_gi->vobu_2ndref_ea != 0 )
+                p_sys->i_vobu_flush = p_dsi_gi->vobu_2ndref_ea;
+            else if( p_dsi_gi->vobu_1stref_ea != 0 )
+                p_sys->i_vobu_flush = p_dsi_gi->vobu_1stref_ea;
+        }
+
 #ifdef DVDNAV_DEBUG
         msg_Dbg( p_demux, "DVDNAV_NAV_PACKET" );
 #endif
@@ -833,6 +880,8 @@ static int Demux( demux_t *p_demux )
 
     case DVDNAV_HOP_CHANNEL:
         msg_Dbg( p_demux, "DVDNAV_HOP_CHANNEL" );
+        p_sys->i_vobu_index = 0;
+        p_sys->i_vobu_flush = 0;
         es_out_Control( p_demux->out, ES_OUT_RESET_PCR );
         break;
 
@@ -1085,17 +1134,17 @@ static int DemuxBlock( demux_t *p_demux, const uint8_t *pkt, int i_pkt )
     demux_sys_t *p_sys = p_demux->p_sys;
     const uint8_t     *p = pkt;
 
-    while( p < &pkt[i_pkt] )
+    while( (p - pkt) <= (i_pkt - 6) )
     {
+        /* ps_pkt_size() needs at least 6 bytes */
         int i_size = ps_pkt_size( p, &pkt[i_pkt] - p );
-        block_t *p_pkt;
         if( i_size <= 0 )
         {
             break;
         }
 
         /* Create a block */
-        p_pkt = block_New( p_demux, i_size );
+        block_t *p_pkt = block_New( p_demux, i_size );
         memcpy( p_pkt->p_buffer, p, i_size);
 
         /* Parse it and send it */
@@ -1164,6 +1213,22 @@ static int DemuxBlock( demux_t *p_demux, const uint8_t *pkt, int i_pkt )
     return VLC_SUCCESS;
 }
 
+/*****************************************************************************
+ * Force still images to be displayed by sending EOS and stopping buffering.
+ *****************************************************************************/
+static void DemuxForceStill( demux_t *p_demux )
+{
+    static const uint8_t buffer[] = {
+        0x00, 0x00, 0x01, 0xe0, 0x00, 0x07,
+        0x80, 0x00, 0x00,
+        0x00, 0x00, 0x01, 0xB7,
+    };
+    DemuxBlock( p_demux, buffer, sizeof(buffer) );
+
+    bool b_empty;
+    es_out_Control( p_demux->out, ES_OUT_GET_EMPTY, &b_empty );
+}
+
 /*****************************************************************************
  * ESNew: register a new elementary stream
  *****************************************************************************/
@@ -1184,16 +1249,8 @@ static void ESNew( demux_t *p_demux, int i_id )
     /* Add a new ES */
     if( tk->fmt.i_cat == VIDEO_ES )
     {
-        switch( p_sys->i_aspect )
-        {
-        case 1: tk->fmt.video.i_aspect = VOUT_ASPECT_FACTOR; break;
-        case 2: tk->fmt.video.i_aspect = VOUT_ASPECT_FACTOR * 4 / 3; break;
-        case 3: tk->fmt.video.i_aspect = VOUT_ASPECT_FACTOR * 16 / 9; break;
-        case 4: tk->fmt.video.i_aspect = VOUT_ASPECT_FACTOR * 221 / 10; break;
-        default:
-            tk->fmt.video.i_aspect = 0;
-            break;
-        }
+        tk->fmt.video.i_sar_num = p_sys->sar.i_num;
+        tk->fmt.video.i_sar_den = p_sys->sar.i_den;
         b_select = true;
     }
     else if( tk->fmt.i_cat == AUDIO_ES )
@@ -1276,9 +1333,11 @@ static void StillTimer( void *p_data )
     demux_sys_t    *p_sys = p_data;
 
     vlc_mutex_lock( &p_sys->still.lock );
-    assert( p_sys->still.b_enabled );
-    p_sys->still.b_enabled = false;
-    dvdnav_still_skip( p_sys->dvdnav );
+    if( likely(p_sys->still.b_enabled) )
+    {
+        p_sys->still.b_enabled = false;
+        dvdnav_still_skip( p_sys->dvdnav );
+    }
     vlc_mutex_unlock( &p_sys->still.lock );
 }
 
@@ -1290,8 +1349,8 @@ static int EventMouse( vlc_object_t *p_vout, char const *psz_var,
 
     /* FIXME? PCI usage thread safe? */
     pci_t *pci = dvdnav_get_current_nav_pci( p_sys->dvdnav );
-    int x = var_GetInteger( p_vout, "mouse-x" );
-    int y = var_GetInteger( p_vout, "mouse-y" );
+    int x = val.coords.x;
+    int y = val.coords.y;
 
     if( psz_var[6] == 'm' ) /* mouse-moved */
         dvdnav_mouse_select( p_sys->dvdnav, pci, x, y );
@@ -1302,8 +1361,8 @@ static int EventMouse( vlc_object_t *p_vout, char const *psz_var,
         ButtonUpdate( p_demux, true );
         dvdnav_mouse_activate( p_sys->dvdnav, pci, x, y );
     }
-
-    (void)oldval; (void)val;
+    (void)p_vout;
+    (void)oldval;
     return VLC_SUCCESS;
 }
 
@@ -1387,10 +1446,7 @@ static int ProbeDVD( demux_t *p_demux, char *psz_name )
         return VLC_SUCCESS;
     }
 
-    if( !strcmp( psz_name, "-" ) ) /* stdin -> file access */
-        return VLC_EGENERIC;
-
-    if( (i_fd = utf8_open( psz_name, O_RDONLY |O_NONBLOCK )) == -1 )
+    if( (i_fd = vlc_open( psz_name, O_RDONLY |O_NONBLOCK )) == -1 )
     {
         return VLC_SUCCESS; /* Let dvdnav_open() do the probing */
     }