]> git.sesse.net Git - vlc/blobdiff - src/playlist/thread.c
input: tickless pause
[vlc] / src / playlist / thread.c
index 5f22924ed4a1d4f7058f1a0eca6fe4eb4c922597..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,29 +45,18 @@ 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);
 
-    p_sys->p_input_resource = input_resource_New( VLC_OBJECT( p_playlist ) );
-    if( unlikely(p_sys->p_input_resource == NULL) )
-        abort();
-
-    /* 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" );
 }
 
 /**
@@ -80,9 +68,6 @@ void playlist_Deactivate( playlist_t *p_playlist )
 {
     playlist_private_t *p_sys = pl_priv(p_playlist);
 
-    if( p_sys->p_input_resource == NULL )
-        return; /* playlist was never activated... */
-
     PL_LOCK;
     /* 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
@@ -205,9 +190,8 @@ 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;
@@ -216,60 +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;
-
     assert( p_sys->p_input == NULL );
+    PL_UNLOCK;
 
-    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;
         }
     }
 
-    bool b_find_art = var_GetInteger( p_playlist, "album-art" )
-                                                      == ALBUM_ART_WHEN_PLAYED;
-    if( b_find_art )
-    {
-        char *psz_uri = input_item_GetURI( p_item->p_input );
-        if( psz_uri != NULL && (!strncmp( psz_uri, "directory:", 10 ) ||
-                                !strncmp( psz_uri, "vlc:", 4 )) )
-            b_find_art = false;
-        free( psz_uri );
-    }
+    var_SetAddress( p_playlist, "input-current", p_input_thread );
 
     /* TODO store art policy in playlist private data */
-    if( b_find_art )
-    {
-        char *psz_arturl = input_item_GetArtURL( p_input );
-        char *psz_name = input_item_GetName( p_input );
-        /* p_input->p_meta should not be null after a successful CreateThread */
-        bool b_has_art = !EMPTY_STR( psz_arturl );
+    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 ) )
-        {
-            PL_DEBUG( "requesting art for %s", psz_name );
-            playlist_AskForArtEnqueue( p_playlist, p_input );
-        }
-        free( psz_arturl );
-        free( psz_name );
+    if( !b_has_art || strncmp( psz_arturl, "attachment://", 13 ) )
+    {
+        PL_DEBUG( "requesting art for new input thread" );
+        libvlc_ArtRequest( p_playlist->p_libvlc, p_input, META_REQUEST_OPTION_NONE );
     }
+    free( psz_arturl );
+
+    PL_LOCK;
+    p_sys->p_input = p_input_thread;
     PL_UNLOCK;
+
     var_TriggerCallback( p_playlist, "activity" );
-    PL_LOCK;
 
-    return VLC_SUCCESS;
+    PL_LOCK;
+    return p_input_thread != NULL;
 }
 
 /**
@@ -283,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 */
@@ -293,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 ),
@@ -346,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;
                 }
             }
@@ -360,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 ) )
@@ -420,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 );
@@ -444,7 +441,7 @@ static void LoopInput( playlist_t *p_playlist )
     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...
@@ -455,6 +452,8 @@ static void LoopInput( playlist_t *p_playlist )
         PL_DEBUG( "dead input" );
         PL_UNLOCK;
 
+        var_SetAddress( p_playlist, "input-current", NULL );
+
         /* WARNING: Input resource manipulation and callback deletion are
          * incompatible with the playlist lock. */
         if( !var_InheritBool( p_input, "sout-keep" ) )
@@ -470,47 +469,21 @@ static void LoopInput( playlist_t *p_playlist )
     else if( p_input->b_error || p_input->b_eof )
     {
         PL_DEBUG( "finished input" );
-        input_Stop( p_input, false );
+        input_Stop( p_input );
     }
 
     vlc_cond_wait( &p_sys->signal, &p_sys->lock );
 }
 
-static void LoopRequest( playlist_t *p_playlist, int i_status )
+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
-    */
-    if( i_status == PLAYLIST_STOPPED )
-    {
-        p_sys->status.i_status = PLAYLIST_STOPPED;
-        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" );
-        ResyncCurrentIndex( p_playlist, p_item );
-        PlayItem( p_playlist, p_item );
-        return;
-    }
-
-    msg_Dbg( p_playlist, "nothing to play" );
-    p_sys->status.i_status = PLAYLIST_STOPPED;
+    if( p_item == NULL )
+        return false;
 
-    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 );
 }
 
 /**
@@ -521,32 +494,43 @@ static void *Thread ( void *data )
     playlist_t *p_playlist = data;
     playlist_private_t *p_sys = pl_priv(p_playlist);
 
-    playlist_Lock( p_playlist );
-    for( ;; )
+    PL_LOCK;
+    while( !p_sys->killed )
     {
-        while( p_sys->p_input != NULL )
-            LoopInput( p_playlist );
+        /* Playlist in stopped state */
+        assert(p_sys->p_input == NULL);
+
+        if( !p_sys->request.b_request )
+        {
+            vlc_cond_wait( &p_sys->signal, &p_sys->lock );
+            continue;
+        }
 
-        if( p_sys->killed )
-            break; /* THE END */
+        while( !p_sys->killed && Next( p_playlist ) )
+        {   /* Playlist in running state */
+            assert(p_sys->p_input != NULL);
 
-        const int status = p_sys->request.b_request ?
-                           p_sys->request.i_status : p_sys->status.i_status;
+            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 );
+        }
 
-        /* Destroy any video display if the playlist is supposed to stop */
-        if( status == PLAYLIST_STOPPED
-         && input_resource_HasVout( p_sys->p_input_resource ) )
+        /* 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;
-            continue; /* lost lock = lost state */
         }
-
-        LoopRequest( p_playlist, status );
     }
-    p_sys->status.i_status = PLAYLIST_STOPPED;
-    playlist_Unlock( p_playlist );
+    PL_UNLOCK;
 
     input_resource_Terminate( p_sys->p_input_resource );
     return NULL;