]> git.sesse.net Git - vlc/blobdiff - src/playlist/thread.c
input: tickless pause
[vlc] / src / playlist / thread.c
index 2c05200f2590d46edafb89d688f37b0e0e85bf40..12a452d67cf247e243b78baeadc0a488efe40687 100644 (file)
@@ -33,7 +33,6 @@
 #include <vlc_interface.h>
 #include <vlc_playlist.h>
 #include <vlc_rand.h>
-#include "stream_output/stream_output.h"
 #include "playlist_internal.h"
 
 /*****************************************************************************
@@ -46,64 +45,46 @@ static void *Thread   ( void * );
  *****************************************************************************/
 
 /**
- * Create the main playlist threads.
- * Additionally to the playlist, this thread controls :
- *    - Statistics
- *    - VLM
- * \param p_parent
- * \return an object with a started thread
+ * Creates the main playlist thread.
  */
 void playlist_Activate( playlist_t *p_playlist )
 {
-    /* */
     playlist_private_t *p_sys = pl_priv(p_playlist);
 
-    /* Start the playlist thread */
     if( vlc_clone( &p_sys->thread, Thread, p_playlist,
                    VLC_THREAD_PRIORITY_LOW ) )
     {
         msg_Err( p_playlist, "cannot spawn playlist thread" );
+        abort();
     }
-    msg_Dbg( p_playlist, "playlist threads correctly activated" );
 }
 
+/**
+ * Stops the playlist forever (but do not destroy it yet).
+ * Any input is stopped.
+ * \return Nothing but waits for the playlist to be deactivated.
+ */
 void playlist_Deactivate( playlist_t *p_playlist )
 {
-    /* */
     playlist_private_t *p_sys = pl_priv(p_playlist);
 
-    msg_Dbg( p_playlist, "deactivating the playlist" );
-
     PL_LOCK;
-    vlc_object_kill( p_playlist );
-    vlc_cond_signal( &p_sys->signal );
-    PL_UNLOCK;
-
-    vlc_join( p_sys->thread, NULL );
-    assert( !p_sys->p_input );
-
-    /* release input resources */
-    if( p_sys->p_input_resource )
+    /* WARNING: There is a latent bug. It is assumed that only one thread will
+     * be waiting for playlist deactivation at a time. So far, that works
+     * as playlist_Deactivate() is only ever called while closing an
+     * interface and interfaces are shut down serially by intf_DestroyAll(). */
+    if( p_sys->killed )
     {
-        input_resource_Terminate( p_sys->p_input_resource );
-        input_resource_Release( p_sys->p_input_resource );
+        PL_UNLOCK;
+        return;
     }
-    p_sys->p_input_resource = NULL;
-
-    if( var_InheritBool( p_playlist, "media-library" ) )
-        playlist_MLDump( p_playlist );
-
-    PL_LOCK;
-
-    /* Release the current node */
-    set_current_status_node( p_playlist, NULL );
-
-    /* Release the current item */
-    set_current_status_item( p_playlist, NULL );
 
+    msg_Dbg( p_playlist, "deactivating the playlist" );
+    p_sys->killed = true;
+    vlc_cond_signal( &p_sys->signal );
     PL_UNLOCK;
 
-    msg_Dbg( p_playlist, "playlist correctly deactivated" );
+    vlc_join( p_sys->thread, NULL );
 }
 
 /* */
@@ -128,14 +109,6 @@ static int InputEvent( vlc_object_t *p_this, char const *psz_cmd,
     return VLC_SUCCESS;
 }
 
-static void UpdateActivity( playlist_t *p_playlist, int i_delta )
-{
-    PL_ASSERT_LOCKED;
-
-    const int i_activity = var_GetInteger( p_playlist, "activity" ) ;
-    var_SetInteger( p_playlist, "activity", i_activity + i_delta );
-}
-
 /**
  * Synchronise the current index of the playlist
  * to match the index of the current item.
@@ -144,7 +117,7 @@ static void UpdateActivity( playlist_t *p_playlist, int i_delta )
  * \param p_cur the current playlist item
  * \return nothing
  */
-static void ResyncCurrentIndex( playlist_t *p_playlist, playlist_item_t *p_cur )
+void ResyncCurrentIndex( playlist_t *p_playlist, playlist_item_t *p_cur )
 {
     PL_ASSERT_LOCKED;
 
@@ -163,7 +136,14 @@ static void ResyncCurrentIndex( playlist_t *p_playlist, playlist_item_t *p_cur )
     PL_DEBUG( "%s is at %i", PLI_NAME( p_cur ), p_playlist->i_current_index );
 }
 
-static void ResetCurrentlyPlaying( playlist_t *p_playlist,
+/**
+ * Reset the currently playing playlist.
+ *
+ * \param p_playlist the playlist structure
+ * \param p_cur the current playlist item
+ * \return nothing
+ */
+void ResetCurrentlyPlaying( playlist_t *p_playlist,
                                    playlist_item_t *p_cur )
 {
     playlist_private_t *p_sys = pl_priv(p_playlist);
@@ -210,9 +190,8 @@ static void ResetCurrentlyPlaying( playlist_t *p_playlist,
  *
  * \param p_playlist the playlist object
  * \param p_item the item to play
- * \return nothing
  */
-static int PlayItem( playlist_t *p_playlist, playlist_item_t *p_item )
+static bool PlayItem( playlist_t *p_playlist, playlist_item_t *p_item )
 {
     playlist_private_t *p_sys = pl_priv(p_playlist);
     input_item_t *p_input = p_item->p_input;
@@ -221,67 +200,49 @@ static int PlayItem( playlist_t *p_playlist, playlist_item_t *p_item )
 
     msg_Dbg( p_playlist, "creating new input thread" );
 
-    p_input->i_nb_played++;
+    p_item->i_nb_played++;
     set_current_status_item( p_playlist, p_item );
-
-    p_sys->status.i_status = PLAYLIST_RUNNING;
-
-    UpdateActivity( p_playlist, DEFAULT_INPUT_ACTIVITY );
-
     assert( p_sys->p_input == NULL );
+    PL_UNLOCK;
 
-    if( !p_sys->p_input_resource )
-        p_sys->p_input_resource = input_resource_New( VLC_OBJECT( p_playlist ) );
-    input_thread_t *p_input_thread = input_Create( p_playlist, p_input, NULL, p_sys->p_input_resource );
-    if( p_input_thread )
+    input_thread_t *p_input_thread = input_Create( p_playlist, p_input, NULL,
+                                                   p_sys->p_input_resource );
+    if( likely(p_input_thread != NULL) )
     {
-        p_sys->p_input = p_input_thread;
-        var_AddCallback( p_input_thread, "intf-event", InputEvent, p_playlist );
-
-        var_SetAddress( p_playlist, "input-current", p_input_thread );
+        var_AddCallback( p_input_thread, "intf-event",
+                         InputEvent, p_playlist );
 
-        if( input_Start( p_sys->p_input ) )
+        if( input_Start( p_input_thread ) )
         {
+            var_DelCallback( p_input_thread, "intf-event",
+                             InputEvent, p_playlist );
             vlc_object_release( p_input_thread );
-            p_sys->p_input = p_input_thread = NULL;
+            p_input_thread = NULL;
         }
     }
 
-    char *psz_uri = input_item_GetURI( p_item->p_input );
-    if( psz_uri && ( !strncmp( psz_uri, "directory:", 10 ) ||
-                     !strncmp( psz_uri, "vlc:", 4 ) ) )
-    {
-        free( psz_uri );
-        return VLC_SUCCESS;
-    }
-    free( psz_uri );
+    var_SetAddress( p_playlist, "input-current", p_input_thread );
 
     /* TODO store art policy in playlist private data */
-    if( var_GetInteger( p_playlist, "album-art" ) == ALBUM_ART_WHEN_PLAYED )
+    char *psz_arturl = input_item_GetArtURL( p_input );
+    /* p_input->p_meta should not be null after a successful CreateThread */
+    bool b_has_art = !EMPTY_STR( psz_arturl );
+
+    if( !b_has_art || strncmp( psz_arturl, "attachment://", 13 ) )
     {
-        bool b_has_art;
+        PL_DEBUG( "requesting art for new input thread" );
+        libvlc_ArtRequest( p_playlist->p_libvlc, p_input, META_REQUEST_OPTION_NONE );
+    }
+    free( psz_arturl );
 
-        char *psz_arturl, *psz_name;
-        psz_arturl = input_item_GetArtURL( p_input );
-        psz_name = input_item_GetName( p_input );
+    PL_LOCK;
+    p_sys->p_input = p_input_thread;
+    PL_UNLOCK;
 
-        /* p_input->p_meta should not be null after a successfull CreateThread */
-        b_has_art = !EMPTY_STR( psz_arturl );
+    var_TriggerCallback( p_playlist, "activity" );
 
-        if( !b_has_art || strncmp( psz_arturl, "attachment://", 13 ) )
-        {
-            PL_DEBUG( "requesting art for %s", psz_name );
-            playlist_AskForArtEnqueue( p_playlist, p_input );
-        }
-        free( psz_arturl );
-        free( psz_name );
-    }
-    /* FIXME: this is not safe !!*/
-    PL_UNLOCK;
-    var_SetAddress( p_playlist, "item-current", p_input );
     PL_LOCK;
-
-    return VLC_SUCCESS;
+    return p_input_thread != NULL;
 }
 
 /**
@@ -295,6 +256,10 @@ static playlist_item_t *NextItem( playlist_t *p_playlist )
 {
     playlist_private_t *p_sys = pl_priv(p_playlist);
     playlist_item_t *p_new = NULL;
+    bool requested = p_sys->request.b_request;
+
+    /* Clear the request */
+    p_sys->request.b_request = false;
 
     /* Handle quickly a few special cases */
     /* No items to play */
@@ -305,9 +270,13 @@ static playlist_item_t *NextItem( playlist_t *p_playlist )
     }
 
     /* Start the real work */
-    if( p_sys->request.b_request )
+    if( requested )
     {
         p_new = p_sys->request.p_item;
+
+        if( p_new == NULL && p_sys->request.p_node == NULL )
+            return NULL; /* Stop request! */
+
         int i_skip = p_sys->request.i_skip;
         PL_DEBUG( "processing request item: %s, node: %s, skip: %i",
                         PLI_NAME( p_sys->request.p_item ),
@@ -358,6 +327,12 @@ static playlist_item_t *NextItem( playlist_t *p_playlist )
                 if( p_playlist->i_current_index >= p_playlist->current.i_size )
                 {
                     PL_DEBUG( "looping - restarting at beginning of node" );
+                    /* reshuffle playlist when end is reached */
+                    if( var_GetBool( p_playlist, "random" ) ) {
+                        PL_DEBUG( "reshuffle playlist" );
+                        ResetCurrentlyPlaying( p_playlist,
+                                get_current_status_item( p_playlist ) );
+                    }
                     p_playlist->i_current_index = 0;
                 }
             }
@@ -372,21 +347,25 @@ static playlist_item_t *NextItem( playlist_t *p_playlist )
                 if( p_playlist->i_current_index <= -1 )
                 {
                     PL_DEBUG( "looping - restarting at end of node" );
+                    /* reshuffle playlist when beginning is reached */
+                    if( var_GetBool( p_playlist, "random" ) ) {
+                        PL_DEBUG( "reshuffle playlist" );
+                        ResetCurrentlyPlaying( p_playlist,
+                                get_current_status_item( p_playlist ) );
+                    }
                     p_playlist->i_current_index = p_playlist->current.i_size-1;
                 }
             }
             p_new = ARRAY_VAL( p_playlist->current,
                                p_playlist->i_current_index );
         }
-        /* Clear the request */
-        p_sys->request.b_request = false;
     }
     /* "Automatic" item change ( next ) */
     else
     {
         bool b_loop = var_GetBool( p_playlist, "loop" );
         bool b_repeat = var_GetBool( p_playlist, "repeat" );
-        bool b_playstop = var_GetBool( p_playlist, "play-and-stop" );
+        bool b_playstop = var_InheritBool( p_playlist, "play-and-stop" );
 
         /* Repeat and play/stop */
         if( b_repeat && get_current_status_item( p_playlist ) )
@@ -432,6 +411,12 @@ static playlist_item_t *NextItem( playlist_t *p_playlist )
         {
             if( !b_loop || p_playlist->current.i_size == 0 )
                 return NULL;
+            /* reshuffle after last item has been played */
+            if( var_GetBool( p_playlist, "random" ) ) {
+                PL_DEBUG( "reshuffle playlist" );
+                ResetCurrentlyPlaying( p_playlist,
+                                       get_current_status_item( p_playlist ) );
+            }
             p_playlist->i_current_index = 0;
         }
         PL_DEBUG( "using item %i", p_playlist->i_current_index );
@@ -446,112 +431,59 @@ static playlist_item_t *NextItem( playlist_t *p_playlist )
     return p_new;
 }
 
-static int LoopInput( playlist_t *p_playlist )
+static void LoopInput( playlist_t *p_playlist )
 {
     playlist_private_t *p_sys = pl_priv(p_playlist);
     input_thread_t *p_input = p_sys->p_input;
 
-    if( !p_input )
-        return VLC_EGENERIC;
+    assert( p_input != NULL );
 
-    if( ( p_sys->request.b_request || !vlc_object_alive( p_playlist ) ) && !p_input->b_die )
+    if( p_sys->request.b_request || p_sys->killed )
     {
         PL_DEBUG( "incoming request - stopping current input" );
-        input_Stop( p_input, true );
+        input_Stop( p_input );
     }
 
+#warning Unsynchronized access to *p_input flags...
     /* This input is dead. Remove it ! */
     if( p_input->b_dead )
     {
+        p_sys->p_input = NULL;
         PL_DEBUG( "dead input" );
-
         PL_UNLOCK;
-        /* We can unlock as we return VLC_EGENERIC (no event will be lost) */
 
-        /* input_resource_t must be manipulated without playlist lock */
-        if( !var_CreateGetBool( p_input, "sout-keep" ) )
-            input_resource_TerminateSout( p_sys->p_input_resource );
+        var_SetAddress( p_playlist, "input-current", NULL );
 
-        /* The DelCallback must be issued without playlist lock */
+        /* WARNING: Input resource manipulation and callback deletion are
+         * incompatible with the playlist lock. */
+        if( !var_InheritBool( p_input, "sout-keep" ) )
+            input_resource_TerminateSout( p_sys->p_input_resource );
         var_DelCallback( p_input, "intf-event", InputEvent, p_playlist );
 
-        PL_LOCK;
-
-        p_sys->p_input = NULL;
         input_Close( p_input );
-
-        UpdateActivity( p_playlist, -DEFAULT_INPUT_ACTIVITY );
-
-        return VLC_EGENERIC;
-    }
-    /* This input is dying, let it do */
-    else if( p_input->b_die )
-    {
-        PL_DEBUG( "dying input" );
+        var_TriggerCallback( p_playlist, "activity" );
+        PL_LOCK;
+        return;
     }
     /* This input has finished, ask it to die ! */
     else if( p_input->b_error || p_input->b_eof )
     {
         PL_DEBUG( "finished input" );
-        input_Stop( p_input, false );
+        input_Stop( p_input );
     }
-    return VLC_SUCCESS;
+
+    vlc_cond_wait( &p_sys->signal, &p_sys->lock );
 }
 
-static void LoopRequest( playlist_t *p_playlist )
+static bool Next( playlist_t *p_playlist )
 {
-    playlist_private_t *p_sys = pl_priv(p_playlist);
-    assert( !p_sys->p_input );
-
-    /* No input. Several cases
-     *  - No request, running status -> start new item
-     *  - No request, stopped status -> collect garbage
-     *  - Request, running requested -> start new item
-     *  - Request, stopped requested -> collect garbage
-    */
-    const int i_status = p_sys->request.b_request ?
-                         p_sys->request.i_status : p_sys->status.i_status;
-
-    if( i_status == PLAYLIST_STOPPED || !vlc_object_alive( p_playlist ) )
-    {
-        p_sys->status.i_status = PLAYLIST_STOPPED;
-
-        if( p_sys->p_input_resource &&
-            input_resource_HasVout( p_sys->p_input_resource ) )
-        {
-            /* XXX We can unlock if we don't issue the wait as we will be
-             * call again without anything else done between the calls */
-            PL_UNLOCK;
-
-            /* input_resource_t must be manipulated without playlist lock */
-            input_resource_TerminateVout( p_sys->p_input_resource );
-
-            PL_LOCK;
-        }
-        else
-        {
-            if( vlc_object_alive( p_playlist ) )
-                vlc_cond_wait( &p_sys->signal, &p_sys->lock );
-        }
-        return;
-    }
-
     playlist_item_t *p_item = NextItem( p_playlist );
-    if( p_item )
-    {
-        msg_Dbg( p_playlist, "starting playback of the new playlist item" );
-        PlayItem( p_playlist, p_item );
-        return;
-    }
+    if( p_item == NULL )
+        return false;
 
-    msg_Dbg( p_playlist, "nothing to play" );
-    p_sys->status.i_status = PLAYLIST_STOPPED;
-
-    if( var_GetBool( p_playlist, "play-and-exit" ) )
-    {
-        msg_Info( p_playlist, "end of playlist, exiting" );
-        libvlc_Quit( p_playlist->p_libvlc );
-    }
+    msg_Dbg( p_playlist, "starting playback of new item" );
+    ResyncCurrentIndex( p_playlist, p_item );
+    return PlayItem( p_playlist, p_item );
 }
 
 /**
@@ -562,26 +494,44 @@ static void *Thread ( void *data )
     playlist_t *p_playlist = data;
     playlist_private_t *p_sys = pl_priv(p_playlist);
 
-    playlist_Lock( p_playlist );
-    while( vlc_object_alive( p_playlist ) || p_sys->p_input )
+    PL_LOCK;
+    while( !p_sys->killed )
     {
-        /* FIXME: what's that ! */
-        if( p_sys->b_reset_currently_playing &&
-            mdate() - p_sys->last_rebuild_date > 30000 ) // 30 ms
+        /* Playlist in stopped state */
+        assert(p_sys->p_input == NULL);
+
+        if( !p_sys->request.b_request )
         {
-            ResetCurrentlyPlaying( p_playlist,
-                                   get_current_status_item( p_playlist ) );
-            p_sys->last_rebuild_date = mdate();
+            vlc_cond_wait( &p_sys->signal, &p_sys->lock );
+            continue;
         }
 
-        /* If there is an input, check that it doesn't need to die. */
-        while( !LoopInput( p_playlist ) )
-            vlc_cond_wait( &p_sys->signal, &p_sys->lock );
+        while( !p_sys->killed && Next( p_playlist ) )
+        {   /* Playlist in running state */
+            assert(p_sys->p_input != NULL);
+
+            do
+                LoopInput( p_playlist );
+            while( p_sys->p_input != NULL );
+        }
+
+        msg_Dbg( p_playlist, "nothing to play" );
+        if( var_InheritBool( p_playlist, "play-and-exit" ) )
+        {
+            msg_Info( p_playlist, "end of playlist, exiting" );
+            libvlc_Quit( p_playlist->p_libvlc );
+        }
 
-        LoopRequest( p_playlist );
+        /* Destroy any video display now (XXX: ugly hack) */
+        if( input_resource_HasVout( p_sys->p_input_resource ) )
+        {
+            PL_UNLOCK; /* Mind: NO LOCKS while manipulating input resources! */
+            input_resource_TerminateVout( p_sys->p_input_resource );
+            PL_LOCK;
+        }
     }
-    playlist_Unlock( p_playlist );
+    PL_UNLOCK;
 
+    input_resource_Terminate( p_sys->p_input_resource );
     return NULL;
 }
-