]> git.sesse.net Git - vlc/blobdiff - src/control/media_instance.c
fix for libvlc_get_input_thread: check for null before locking. fixes #1522
[vlc] / src / control / media_instance.c
index e4d999440e4802ebf18fdf85a0cb359e824b607a..e7dc33d3aa09b231b8df190713f4124daa266684 100644 (file)
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  *****************************************************************************/
 
+#include "libvlc_internal.h"
+
 #include <vlc/libvlc.h>
 #include <vlc_demux.h>
 #include <vlc_input.h>
-#include "libvlc_internal.h"
 #include "libvlc.h"
 
+static int
+input_state_changed( vlc_object_t * p_this, char const * psz_cmd,
+                     vlc_value_t oldval, vlc_value_t newval,
+                     void * p_userdata );
+static int
+input_seekable_changed( vlc_object_t * p_this, char const * psz_cmd,
+                        vlc_value_t oldval, vlc_value_t newval,
+                        void * p_userdata );
+static int
+input_pausable_changed( vlc_object_t * p_this, char const * psz_cmd,
+                        vlc_value_t oldval, vlc_value_t newval,
+                        void * p_userdata );
+static int
+input_position_changed( vlc_object_t * p_this, char const * psz_cmd,
+                     vlc_value_t oldval, vlc_value_t newval,
+                     void * p_userdata );
+static int
+input_time_changed( vlc_object_t * p_this, char const * psz_cmd,
+                     vlc_value_t oldval, vlc_value_t newval,
+                     void * p_userdata );
+
+static const libvlc_state_t vlc_to_libvlc_state_array[] =
+{
+    [INIT_S]        = libvlc_Opening,
+    [OPENING_S]     = libvlc_Opening,
+    [BUFFERING_S]   = libvlc_Buffering,    
+    [PLAYING_S]     = libvlc_Playing,    
+    [PAUSE_S]       = libvlc_Paused,    
+    [END_S]         = libvlc_Ended,    
+    [ERROR_S]       = libvlc_Error,    
+};
+static inline libvlc_state_t vlc_to_libvlc_state( int vlc_state )
+{
+    if( vlc_state < 0 || vlc_state > 6 )
+        return libvlc_Stopped;
+
+    return vlc_to_libvlc_state_array[vlc_state];
+}
+
 /*
  * Release the associated input thread
  *
@@ -39,28 +79,27 @@ static void release_input_thread( libvlc_media_instance_t *p_mi )
     if( !p_mi || p_mi->i_input_id == -1 )
         return;
 
-    p_input_thread = (input_thread_t*)vlc_object_get(
-                                             p_mi->p_libvlc_instance->p_libvlc_int,
-                                             p_mi->i_input_id );
+    p_input_thread = (input_thread_t*)vlc_object_get( p_mi->i_input_id );
 
     p_mi->i_input_id = -1;
 
     if( !p_input_thread )
         return;
  
-    /* release for previous vlc_object_get */
-    vlc_object_release( p_input_thread );
-
-    /* release for initial p_input_thread yield (see _new()) */
-    vlc_object_release( p_input_thread );
 
     /* No one is tracking this input_thread appart us. Destroy it */
     if( p_mi->b_own_its_input_thread )
     {
+        var_DelCallback( p_input_thread, "state", input_state_changed, p_mi );
+        var_DelCallback( p_input_thread, "seekable", input_seekable_changed, p_mi );
+        var_DelCallback( p_input_thread, "pausable", input_pausable_changed, p_mi );
+        var_DelCallback( p_input_thread, "intf-change", input_position_changed, p_mi );
+        var_DelCallback( p_input_thread, "intf-change", input_time_changed, p_mi );
+
         /* We owned this one */
         input_StopThread( p_input_thread );
+
         var_Destroy( p_input_thread, "drawable" );
-        input_DestroyThread( p_input_thread );
     }
     else
     {
@@ -69,6 +108,9 @@ static void release_input_thread( libvlc_media_instance_t *p_mi )
          * revert that here. This will be deleted with the playlist API */
         vlc_object_release( p_input_thread );
     }
+
+    /* release for previous vlc_object_get */
+    vlc_object_release( p_input_thread );
 }
 
 /*
@@ -82,6 +124,11 @@ input_thread_t *libvlc_get_input_thread( libvlc_media_instance_t *p_mi,
 {
     input_thread_t *p_input_thread;
 
+    if ( !p_mi )
+    {
+        RAISENULL( "Input is NULL" );
+    }
+
     vlc_mutex_lock( &p_mi->object_lock );
 
     if( !p_mi || p_mi->i_input_id == -1 )
@@ -90,9 +137,7 @@ input_thread_t *libvlc_get_input_thread( libvlc_media_instance_t *p_mi,
         RAISENULL( "Input is NULL" );
     }
 
-    p_input_thread = (input_thread_t*)vlc_object_get(
-                                             p_mi->p_libvlc_instance->p_libvlc_int,
-                                             p_mi->i_input_id );
+    p_input_thread = (input_thread_t*)vlc_object_get( p_mi->i_input_id );
     if( !p_input_thread )
     {
         vlc_mutex_unlock( &p_mi->object_lock );
@@ -111,23 +156,31 @@ input_state_changed( vlc_object_t * p_this, char const * psz_cmd,
                      vlc_value_t oldval, vlc_value_t newval,
                      void * p_userdata )
 {
+    VLC_UNUSED(oldval);
+    VLC_UNUSED(p_this);
+    VLC_UNUSED(psz_cmd);
     libvlc_media_instance_t * p_mi = p_userdata;
     libvlc_event_t event;
+    libvlc_event_type_t type = newval.i_int;
 
-    if( newval.i_int == oldval.i_int )
-        return VLC_SUCCESS; /* No change since last time, don't propagate */
-
-    switch ( newval.i_int )
+    switch ( type )
     {
         case END_S:
+            libvlc_media_descriptor_set_state( p_mi->p_md, libvlc_NothingSpecial, NULL);
             event.type = libvlc_MediaInstanceReachedEnd;
             break;
         case PAUSE_S:
+            libvlc_media_descriptor_set_state( p_mi->p_md, libvlc_Playing, NULL);
             event.type = libvlc_MediaInstancePaused;
             break;
         case PLAYING_S:
+            libvlc_media_descriptor_set_state( p_mi->p_md, libvlc_Playing, NULL);
             event.type = libvlc_MediaInstancePlayed;
             break;
+        case ERROR_S:
+            libvlc_media_descriptor_set_state( p_mi->p_md, libvlc_Error, NULL);
+            event.type = libvlc_MediaInstanceEncounteredError;
+            break;
         default:
             return VLC_SUCCESS;
     }
@@ -136,6 +189,44 @@ input_state_changed( vlc_object_t * p_this, char const * psz_cmd,
     return VLC_SUCCESS;
 }
 
+static int
+input_seekable_changed( vlc_object_t * p_this, char const * psz_cmd,
+                        vlc_value_t oldval, vlc_value_t newval,
+                        void * p_userdata )
+{
+    VLC_UNUSED(oldval);
+    VLC_UNUSED(p_this);
+    VLC_UNUSED(psz_cmd);
+    libvlc_media_instance_t * p_mi = p_userdata;
+    libvlc_event_t event;
+
+    libvlc_media_descriptor_set_state( p_mi->p_md, libvlc_NothingSpecial, NULL);
+    event.type = libvlc_MediaInstanceSeekableChanged;
+    event.u.media_instance_seekable_changed.new_seekable = newval.b_bool;
+
+    libvlc_event_send( p_mi->p_event_manager, &event );
+    return VLC_SUCCESS;
+}
+
+static int
+input_pausable_changed( vlc_object_t * p_this, char const * psz_cmd,
+                        vlc_value_t oldval, vlc_value_t newval,
+                        void * p_userdata )
+{
+    VLC_UNUSED(oldval);
+    VLC_UNUSED(p_this);
+    VLC_UNUSED(psz_cmd);
+    libvlc_media_instance_t * p_mi = p_userdata;
+    libvlc_event_t event;
+
+    libvlc_media_descriptor_set_state( p_mi->p_md, libvlc_NothingSpecial, NULL);
+    event.type = libvlc_MediaInstancePausableChanged;
+    event.u.media_instance_pausable_changed.new_pausable = newval.b_bool;
+
+    libvlc_event_send( p_mi->p_event_manager, &event );
+    return VLC_SUCCESS;
+}
+
 /*
  * input_position_changed (Private) (input var "intf-change" Callback)
  */
@@ -144,32 +235,63 @@ input_position_changed( vlc_object_t * p_this, char const * psz_cmd,
                      vlc_value_t oldval, vlc_value_t newval,
                      void * p_userdata )
 {
+    VLC_UNUSED(oldval);
     libvlc_media_instance_t * p_mi = p_userdata;
     vlc_value_t val;
-    if (!strcmp(psz_cmd, "intf" /* "-change" no need to go further */))
+
+    if (!strncmp(psz_cmd, "intf", 4 /* "-change" no need to go further */))
     {
         input_thread_t * p_input = (input_thread_t *)p_this;
-        var_Get( p_input, "position", &val );
 
-        if ((val.i_time % I64C(500000)) != 0)
-            return VLC_SUCCESS; /* No need to have a better precision */
         var_Get( p_input, "state", &val );
-
         if( val.i_int != PLAYING_S )
             return VLC_SUCCESS; /* Don't send the position while stopped */
+
+        var_Get( p_input, "position", &val );
     }
     else
         val.i_time = newval.i_time;
 
     libvlc_event_t event;
     event.type = libvlc_MediaInstancePositionChanged;
-    event.u.media_instance_position_changed.new_position = val.i_time;
+    event.u.media_instance_position_changed.new_position = val.f_float;
 
     libvlc_event_send( p_mi->p_event_manager, &event );
     return VLC_SUCCESS;
 }
 
+/*
+ * input_time_changed (Private) (input var "intf-change" Callback)
+ */
+static int
+input_time_changed( vlc_object_t * p_this, char const * psz_cmd,
+                     vlc_value_t oldval, vlc_value_t newval,
+                     void * p_userdata )
+{
+    VLC_UNUSED(oldval);
+    libvlc_media_instance_t * p_mi = p_userdata;
+    vlc_value_t val;
+
+    if (!strncmp(psz_cmd, "intf", 4 /* "-change" no need to go further */))
+    {
+        input_thread_t * p_input = (input_thread_t *)p_this;
+    
+        var_Get( p_input, "state", &val );
+        if( val.i_int != PLAYING_S )
+            return VLC_SUCCESS; /* Don't send the position while stopped */
+
+        var_Get( p_input, "time", &val );
+    }
+    else
+        val.i_time = newval.i_time;
+
+    libvlc_event_t event;
+    event.type = libvlc_MediaInstanceTimeChanged;
+    event.u.media_instance_time_changed.new_time = val.i_time;
+    libvlc_event_send( p_mi->p_event_manager, &event );
+    return VLC_SUCCESS;
+}
+
 /**************************************************************************
  * Create a Media Instance object
  **************************************************************************/
@@ -213,12 +335,20 @@ libvlc_media_instance_new( libvlc_instance_t * p_libvlc_instance,
  
     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
             libvlc_MediaInstanceReachedEnd, p_e );
+    libvlc_event_manager_register_event_type( p_mi->p_event_manager,
+            libvlc_MediaInstanceEncounteredError, p_e );
     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
             libvlc_MediaInstancePaused, p_e );
     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
             libvlc_MediaInstancePlayed, p_e );
     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
             libvlc_MediaInstancePositionChanged, p_e );
+    libvlc_event_manager_register_event_type( p_mi->p_event_manager,
+            libvlc_MediaInstanceTimeChanged, p_e );
+    libvlc_event_manager_register_event_type( p_mi->p_event_manager,
+            libvlc_MediaInstanceSeekableChanged, p_e );
+    libvlc_event_manager_register_event_type( p_mi->p_event_manager,
+            libvlc_MediaInstancePausableChanged, p_e );
 
     return p_mi;
 }
@@ -307,6 +437,7 @@ void libvlc_media_instance_destroy( libvlc_media_instance_t *p_mi )
     if( libvlc_exception_raised( &p_e ) )
     {
         libvlc_event_manager_release( p_mi->p_event_manager );
+        libvlc_exception_clear( &p_e );
         free( p_mi );
         return; /* no need to worry about no input thread */
     }
@@ -376,6 +507,9 @@ void libvlc_media_instance_set_media_descriptor(
 
     release_input_thread( p_mi );
 
+    if( p_mi->p_md )
+        libvlc_media_descriptor_set_state( p_mi->p_md, libvlc_NothingSpecial, NULL );
+
     libvlc_media_descriptor_release( p_mi->p_md );
 
     if( !p_md )
@@ -453,9 +587,16 @@ void libvlc_media_instance_play( libvlc_media_instance_t *p_mi,
         return;
     }
 
-    p_input_thread = input_CreateThread( p_mi->p_libvlc_instance->p_libvlc_int,
-                                         p_mi->p_md->p_input_item );
-    p_mi->i_input_id = p_input_thread->i_object_id;
+    p_mi->i_input_id = input_Read( p_mi->p_libvlc_instance->p_libvlc_int,
+                                   p_mi->p_md->p_input_item, VLC_FALSE );
+
+    p_input_thread = (input_thread_t*)vlc_object_get( p_mi->i_input_id );
+
+    if( !p_input_thread )
+    {
+        return;
+        vlc_mutex_unlock( &p_mi->object_lock );
+    }
 
     if( p_mi->drawable )
     {
@@ -465,11 +606,12 @@ void libvlc_media_instance_play( libvlc_media_instance_t *p_mi,
         var_Set( p_input_thread, "drawable", val );
     }
     var_AddCallback( p_input_thread, "state", input_state_changed, p_mi );
+    var_AddCallback( p_input_thread, "seekable", input_seekable_changed, p_mi );
+    var_AddCallback( p_input_thread, "pausable", input_pausable_changed, p_mi );
     var_AddCallback( p_input_thread, "intf-change", input_position_changed, p_mi );
+    var_AddCallback( p_input_thread, "intf-change", input_time_changed, p_mi );
 
-    /* will be released in media_instance_release() */
-    vlc_object_yield( p_input_thread );
-
+    vlc_object_release( p_input_thread );
     vlc_mutex_unlock( &p_mi->object_lock );
 }
 
@@ -484,7 +626,18 @@ void libvlc_media_instance_pause( libvlc_media_instance_t *p_mi,
     if( !p_input_thread )
         return;
 
-    input_Control( p_input_thread, INPUT_SET_STATE, PAUSE_S );
+    int state = var_GetInteger( p_input_thread, "state" );
+
+    if( state == PLAYING_S )
+    {
+        if( libvlc_media_instance_can_pause( p_mi, p_e ) )
+            input_Control( p_input_thread, INPUT_SET_STATE, PAUSE_S );
+        else
+            libvlc_media_instance_stop( p_mi, p_e );
+    }
+    else
+        input_Control( p_input_thread, INPUT_SET_STATE, PLAYING_S );
+
     vlc_object_release( p_input_thread );
 }
 
@@ -494,7 +647,18 @@ void libvlc_media_instance_pause( libvlc_media_instance_t *p_mi,
 void libvlc_media_instance_stop( libvlc_media_instance_t *p_mi,
                                  libvlc_exception_t *p_e )
 {
-    //libvlc_exception_raise( p_e, "Not implemented" );
+    if( p_mi->b_own_its_input_thread )
+        release_input_thread( p_mi ); /* This will stop the input thread */
+    else
+    {
+        input_thread_t * p_input_thread = libvlc_get_input_thread( p_mi, p_e );
+
+        if( !p_input_thread )
+            return;
+
+        input_StopThread( p_input_thread );
+        vlc_object_release( p_input_thread );
+    }
 }
 
 /**************************************************************************
@@ -504,13 +668,24 @@ void libvlc_media_instance_set_drawable( libvlc_media_instance_t *p_mi,
                                          libvlc_drawable_t drawable,
                                          libvlc_exception_t *p_e )
 {
+    (void)p_e;
     p_mi->drawable = drawable;
 }
 
+/**************************************************************************
+ * Get Drawable
+ **************************************************************************/
+libvlc_drawable_t
+libvlc_media_instance_get_drawable ( libvlc_media_instance_t *p_mi, libvlc_exception_t *p_e )
+{
+    (void)p_e;
+    return p_mi->drawable;
+}
+
 /**************************************************************************
  * Getters for stream information
  **************************************************************************/
-vlc_int64_t libvlc_media_instance_get_length(
+libvlc_time_t libvlc_media_instance_get_length(
                              libvlc_media_instance_t *p_mi,
                              libvlc_exception_t *p_e )
 {
@@ -527,7 +702,7 @@ vlc_int64_t libvlc_media_instance_get_length(
     return (val.i_time+500LL)/1000LL;
 }
 
-vlc_int64_t libvlc_media_instance_get_time(
+libvlc_time_t libvlc_media_instance_get_time(
                                    libvlc_media_instance_t *p_mi,
                                    libvlc_exception_t *p_e )
 {
@@ -545,7 +720,7 @@ vlc_int64_t libvlc_media_instance_get_time(
 
 void libvlc_media_instance_set_time(
                                  libvlc_media_instance_t *p_mi,
-                                 vlc_int64_t time,
+                                 libvlc_time_t time,
                                  libvlc_exception_t *p_e )
 {
     input_thread_t *p_input_thread;
@@ -594,6 +769,57 @@ float libvlc_media_instance_get_position(
     return val.f_float;
 }
 
+void libvlc_media_instance_set_chapter(
+                                 libvlc_media_instance_t *p_mi,
+                                 int chapter,
+                                 libvlc_exception_t *p_e )
+{
+    input_thread_t *p_input_thread;
+    vlc_value_t val;
+    val.i_int = chapter;
+
+    p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
+    if( !p_input_thread )
+        return;
+
+    var_Set( p_input_thread, "chapter", val );
+    vlc_object_release( p_input_thread );
+}
+
+int libvlc_media_instance_get_chapter(
+                                 libvlc_media_instance_t *p_mi,
+                                 libvlc_exception_t *p_e )
+{
+    input_thread_t *p_input_thread;
+    vlc_value_t val;
+
+    p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
+    if( !p_input_thread )
+        return -1.0;
+
+    var_Get( p_input_thread, "chapter", &val );
+    vlc_object_release( p_input_thread );
+
+    return val.i_int;
+}
+
+int libvlc_media_instance_get_chapter_count(
+                                 libvlc_media_instance_t *p_mi,
+                                 libvlc_exception_t *p_e )
+{
+    input_thread_t *p_input_thread;
+    vlc_value_t val;
+
+    p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
+    if( !p_input_thread )
+        return -1.0;
+
+    var_Change( p_input_thread, "chapter", VLC_VAR_CHOICESCOUNT, &val, NULL );
+    vlc_object_release( p_input_thread );
+
+    return val.i_int;
+}
+
 float libvlc_media_instance_get_fps(
                                  libvlc_media_instance_t *p_mi,
                                  libvlc_exception_t *p_e)
@@ -666,17 +892,6 @@ float libvlc_media_instance_get_rate(
     return (float)1000.0f/val.i_int;
 }
 
-static libvlc_state_t vlc_to_libvlc_state[] =
-{
-    [INIT_S]        = libvlc_Opening,
-    [OPENING_S]     = libvlc_Opening,
-    [BUFFERING_S]   = libvlc_Buffering,    
-    [PLAYING_S]     = libvlc_Playing,    
-    [PAUSE_S]       = libvlc_Paused,    
-    [END_S]         = libvlc_Ended,    
-    [ERROR_S]       = libvlc_Error,    
-};
-
 libvlc_state_t libvlc_media_instance_get_state(
                                  libvlc_media_instance_t *p_mi,
                                  libvlc_exception_t *p_e )
@@ -686,13 +901,57 @@ libvlc_state_t libvlc_media_instance_get_state(
 
     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
     if ( !p_input_thread )
+    {
+        /* We do return the right value, no need to throw an exception */
+        if( libvlc_exception_raised( p_e ) )
+            libvlc_exception_clear( p_e );
         return libvlc_Stopped;
+    }
 
     var_Get( p_input_thread, "state", &val );
     vlc_object_release( p_input_thread );
 
-    if( val.i_int < 0 || val.i_int > 6 )
-        return libvlc_Stopped;
+    return vlc_to_libvlc_state(val.i_int);
+}
+
+vlc_bool_t libvlc_media_instance_is_seekable(
+                                 libvlc_media_instance_t *p_mi,
+                                 libvlc_exception_t *p_e )
+{
+    input_thread_t *p_input_thread;
+    vlc_value_t val;
+
+    p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
+    if ( !p_input_thread )
+    {
+        /* We do return the right value, no need to throw an exception */
+        if( libvlc_exception_raised( p_e ) )
+            libvlc_exception_clear( p_e );
+        return VLC_FALSE;
+    }
+    var_Get( p_input_thread, "seekable", &val );
+    vlc_object_release( p_input_thread );
+
+    return val.b_bool;
+}
+
+vlc_bool_t libvlc_media_instance_can_pause(
+                                 libvlc_media_instance_t *p_mi,
+                                 libvlc_exception_t *p_e )
+{
+    input_thread_t *p_input_thread;
+    vlc_value_t val;
+
+    p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
+    if ( !p_input_thread )
+    {
+        /* We do return the right value, no need to throw an exception */
+        if( libvlc_exception_raised( p_e ) )
+            libvlc_exception_clear( p_e );
+        return VLC_FALSE;
+    }
+    var_Get( p_input_thread, "can-pause", &val );
+    vlc_object_release( p_input_thread );
 
-    return vlc_to_libvlc_state[val.i_int];
+    return val.b_bool;
 }