/*****************************************************************************
- * control.c : Hanle control of the playlist & running through it
+ * control.c : Handle control of the playlist & running through it
*****************************************************************************
* Copyright (C) 1999-2004 the VideoLAN team
- * $Id: /local/vlc/0.8.6-playlist-vlm/src/playlist/playlist.c 13741 2006-03-21T19:29:39.792444Z zorglub $
+ * $Id$
*
* Authors: Samuel Hocevar <sam@zoy.org>
* Clément Stenac <zorglub@videolan.org>
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
-#include <vlc/vlc.h>
-#include <vlc/input.h>
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_common.h>
#include "vlc_playlist.h"
+#include "playlist_internal.h"
+#include <assert.h>
/*****************************************************************************
* Local prototypes
*****************************************************************************/
-int PlaylistVAControl( playlist_t * p_playlist, int i_query, va_list args );
-
-void PreparseEnqueueItemSub( playlist_t *, playlist_item_t * );
-
-playlist_item_t *playlist_RecursiveFindLast(playlist_t *p_playlist,
- playlist_item_t *p_node );
+static int PlaylistVAControl( playlist_t * p_playlist, int i_query, va_list args );
/*****************************************************************************
* Playlist control
*****************************************************************************/
-/**
- * Do a playlist action. Should be entered without playlist lock
- * \see playlist_Control
- */
-int playlist_LockControl( playlist_t * p_playlist, int i_query, ... )
+static vlc_mutex_t global_lock = VLC_STATIC_MUTEX;
+
+#undef pl_Get
+playlist_t *pl_Get (vlc_object_t *obj)
{
- va_list args;
- int i_result;
- va_start( args, i_query );
- vlc_mutex_lock( &p_playlist->object_lock );
- i_result = PlaylistVAControl( p_playlist, i_query, args );
- va_end( args );
- vlc_mutex_unlock( &p_playlist->object_lock );
- return i_result;
+ playlist_t *pl;
+ libvlc_int_t *p_libvlc = obj->p_libvlc;
+
+ vlc_mutex_lock (&global_lock);
+ pl = libvlc_priv (p_libvlc)->p_playlist;
+ assert (pl != NULL);
+
+ if (!libvlc_priv (p_libvlc)->playlist_active)
+ {
+ playlist_Activate (pl);
+ libvlc_priv (p_libvlc)->playlist_active = true;
+ }
+ vlc_mutex_unlock (&global_lock);
+ return pl;
}
-/**
- * Do a playlist action.
- * If there is something in the playlist then you can do playlist actions.
- * Should be entered with playlist lock. See include/vlc_playlist.h for
- * possible queries
- *
- * \param p_playlist the playlist to do the command on
- * \param i_query the command to do
- * \param variable number of arguments
- * \return VLC_SUCCESS or an error
- */
-int playlist_Control( playlist_t * p_playlist, int i_query, ... )
+void pl_Deactivate (libvlc_int_t *p_libvlc)
+{
+ bool deactivate;
+
+ vlc_mutex_lock (&global_lock);
+ deactivate = libvlc_priv (p_libvlc)->playlist_active;
+ vlc_mutex_unlock (&global_lock);
+
+ if (deactivate)
+ playlist_Deactivate (libvlc_priv (p_libvlc)->p_playlist);
+}
+
+void playlist_Lock( playlist_t *pl )
+{
+ vlc_mutex_lock( &pl_priv(pl)->lock );
+}
+
+void playlist_Unlock( playlist_t *pl )
+{
+ vlc_mutex_unlock( &pl_priv(pl)->lock );
+}
+
+void playlist_AssertLocked( playlist_t *pl )
+{
+ vlc_assert_locked( &pl_priv(pl)->lock );
+}
+
+int playlist_Control( playlist_t * p_playlist, int i_query,
+ bool b_locked, ... )
{
va_list args;
int i_result;
- va_start( args, i_query );
+ PL_LOCK_IF( !b_locked );
+ va_start( args, b_locked );
i_result = PlaylistVAControl( p_playlist, i_query, args );
va_end( args );
+ PL_UNLOCK_IF( !b_locked );
return i_result;
}
-int PlaylistVAControl( playlist_t * p_playlist, int i_query, va_list args )
+static int PlaylistVAControl( playlist_t * p_playlist, int i_query, va_list args )
{
- int i_view;
playlist_item_t *p_item, *p_node;
- vlc_value_t val;
- if( p_playlist->i_size <= 0 )
- {
+ PL_ASSERT_LOCKED;
+
+ if( !vlc_object_alive( p_playlist ) )
+ return VLC_EGENERIC;
+
+ if( playlist_IsEmpty( p_playlist ) && i_query != PLAYLIST_STOP )
return VLC_EGENERIC;
- }
switch( i_query )
{
case PLAYLIST_STOP:
- p_playlist->request.i_status = PLAYLIST_STOPPED;
- p_playlist->request.b_request = VLC_TRUE;
- p_playlist->request.p_item = NULL;
- break;
-
- case PLAYLIST_ITEMPLAY:
- p_item = (playlist_item_t *)va_arg( args, playlist_item_t * );
- if ( p_item == NULL || p_item->p_input->psz_uri == NULL )
- return VLC_EGENERIC;
- p_playlist->request.i_status = PLAYLIST_RUNNING;
- p_playlist->request.i_skip = 0;
- p_playlist->request.b_request = VLC_TRUE;
- p_playlist->request.p_item = p_item;
- p_playlist->request.p_node = p_playlist->status.p_node;
+ pl_priv(p_playlist)->request.i_status = PLAYLIST_STOPPED;
+ pl_priv(p_playlist)->request.b_request = true;
+ pl_priv(p_playlist)->request.p_item = NULL;
break;
+ // Node can be null, it will keep the same. Use with care ...
+ // Item null = take the first child of node
case PLAYLIST_VIEWPLAY:
- i_view = (int) va_arg( args, playlist_item_t *);
p_node = (playlist_item_t *)va_arg( args, playlist_item_t * );
p_item = (playlist_item_t *)va_arg( args, playlist_item_t * );
if ( p_node == NULL )
{
- p_node = p_playlist->status.p_node;
- if( !p_node )
- {
- p_playlist->status.i_status = PLAYLIST_STOPPED;
- p_playlist->request.b_request = VLC_TRUE;
- msg_Err( p_playlist, "null node" );
- return VLC_SUCCESS;
- }
+ p_node = get_current_status_node( p_playlist );
+ assert( p_node );
}
- p_playlist->request.i_status = PLAYLIST_RUNNING;
- p_playlist->request.i_skip = 0;
- p_playlist->request.b_request = VLC_TRUE;
- p_playlist->request.p_node = p_node;
- p_playlist->request.p_item = p_item;
+ pl_priv(p_playlist)->request.i_status = PLAYLIST_RUNNING;
+ pl_priv(p_playlist)->request.i_skip = 0;
+ pl_priv(p_playlist)->request.b_request = true;
+ pl_priv(p_playlist)->request.p_node = p_node;
+ pl_priv(p_playlist)->request.p_item = p_item;
+ if( p_item && var_GetBool( p_playlist, "random" ) )
+ pl_priv(p_playlist)->b_reset_currently_playing = true;
break;
case PLAYLIST_PLAY:
- p_playlist->request.i_status = PLAYLIST_RUNNING;
- p_playlist->request.b_request = VLC_TRUE;
-
- if( p_playlist->p_input )
+ if( pl_priv(p_playlist)->p_input )
{
- val.i_int = PLAYING_S;
- var_Set( p_playlist->p_input, "state", val );
+ var_SetInteger( pl_priv(p_playlist)->p_input, "state", PLAYING_S );
break;
}
- p_playlist->request.p_node = p_playlist->status.p_node;
- p_playlist->request.p_item = p_playlist->status.p_item;
- p_playlist->request.i_skip = 0;
- break;
-
- case PLAYLIST_AUTOPLAY:
- p_playlist->status.i_status = PLAYLIST_RUNNING;
- p_playlist->status.p_node = p_playlist->p_local_category;
- p_playlist->request.b_request = VLC_FALSE;
+ else
+ {
+ pl_priv(p_playlist)->request.i_status = PLAYLIST_RUNNING;
+ pl_priv(p_playlist)->request.b_request = true;
+ pl_priv(p_playlist)->request.p_node = get_current_status_node( p_playlist );
+ pl_priv(p_playlist)->request.p_item = get_current_status_item( p_playlist );
+ pl_priv(p_playlist)->request.i_skip = 0;
+ }
break;
case PLAYLIST_PAUSE:
- val.i_int = 0;
- if( p_playlist->p_input )
- var_Get( p_playlist->p_input, "state", &val );
+ if( !pl_priv(p_playlist)->p_input )
+ { /* FIXME: is this really useful without input? */
+ pl_priv(p_playlist)->status.i_status = PLAYLIST_PAUSED;
+ break;
+ }
- if( val.i_int == PAUSE_S )
+ if( var_GetInteger( pl_priv(p_playlist)->p_input, "state" ) == PAUSE_S )
{
- p_playlist->status.i_status = PLAYLIST_RUNNING;
- if( p_playlist->p_input )
- {
- val.i_int = PLAYING_S;
- var_Set( p_playlist->p_input, "state", val );
- }
+ pl_priv(p_playlist)->status.i_status = PLAYLIST_RUNNING;
+ var_SetInteger( pl_priv(p_playlist)->p_input, "state", PLAYING_S );
}
else
{
- p_playlist->status.i_status = PLAYLIST_PAUSED;
- if( p_playlist->p_input )
- {
- val.i_int = PAUSE_S;
- var_Set( p_playlist->p_input, "state", val );
- }
+ pl_priv(p_playlist)->status.i_status = PLAYLIST_PAUSED;
+ var_SetInteger( pl_priv(p_playlist)->p_input, "state", PAUSE_S );
}
break;
case PLAYLIST_SKIP:
- p_playlist->request.p_node = p_playlist->status.p_node;
- p_playlist->request.p_item = p_playlist->status.p_item;
- p_playlist->request.i_skip = (int) va_arg( args, int );
- p_playlist->request.b_request = VLC_TRUE;
+ pl_priv(p_playlist)->request.p_node = get_current_status_node( p_playlist );
+ pl_priv(p_playlist)->request.p_item = get_current_status_item( p_playlist );
+ pl_priv(p_playlist)->request.i_skip = (int) va_arg( args, int );
+ /* if already running, keep running */
+ if( pl_priv(p_playlist)->status.i_status != PLAYLIST_STOPPED )
+ pl_priv(p_playlist)->request.i_status = pl_priv(p_playlist)->status.i_status;
+ pl_priv(p_playlist)->request.b_request = true;
break;
default:
msg_Err( p_playlist, "unknown playlist query" );
return VLC_EBADVAR;
- break;
}
+ vlc_cond_signal( &pl_priv(p_playlist)->signal );
return VLC_SUCCESS;
}
* Preparse control
*****************************************************************************/
/** Enqueue an item for preparsing */
-int playlist_PreparseEnqueue( playlist_t *p_playlist,
- input_item_t *p_item )
+int playlist_PreparseEnqueue( playlist_t *p_playlist, input_item_t *p_item )
{
- vlc_mutex_lock( &p_playlist->p_preparse->object_lock );
- vlc_gc_incref( p_item );
- INSERT_ELEM( p_playlist->p_preparse->pp_waiting,
- p_playlist->p_preparse->i_waiting,
- p_playlist->p_preparse->i_waiting,
- p_item );
- vlc_mutex_unlock( &p_playlist->p_preparse->object_lock );
- return VLC_SUCCESS;
-}
+ playlist_private_t *p_sys = pl_priv(p_playlist);
-/** Enqueue a playlist item or a node for peparsing.
- * This function should be entered without playlist and preparser locks */
-int playlist_PreparseEnqueueItem( playlist_t *p_playlist,
- playlist_item_t *p_item )
-{
- vlc_mutex_lock( &p_playlist->object_lock );
- vlc_mutex_lock( &p_playlist->p_preparse->object_lock );
- PreparseEnqueueItemSub( p_playlist, p_item );
- vlc_mutex_unlock( &p_playlist->p_preparse->object_lock );
- vlc_mutex_unlock( &p_playlist->object_lock );
- return VLC_SUCCESS;
-}
+ if( p_sys->p_preparser )
+ playlist_preparser_Push( p_sys->p_preparser, p_item );
-void PreparseEnqueueItemSub( playlist_t *p_playlist,
- playlist_item_t *p_item )
-{
- int i;
- if( p_item->i_children == -1 )
- {
- vlc_gc_incref( p_item );
- INSERT_ELEM( p_playlist->p_preparse->pp_waiting,
- p_playlist->p_preparse->i_waiting,
- p_playlist->p_preparse->i_waiting,
- p_item->p_input );
- }
- else
- {
- for( i = 0; i < p_item->i_children; i++)
- {
- PreparseEnqueueItemSub( p_playlist,
- p_item->pp_children[i] );
- }
- }
-}
-
-/*****************************************************************************
- * Playback logic
- *****************************************************************************/
-
-/** This function calculates the next playlist item, depending
- * on the playlist course mode (forward, backward, random, view,...). */
-playlist_item_t * playlist_NextItem( playlist_t *p_playlist )
-{
- playlist_item_t *p_new = NULL;
- int i_skip,i;
-
- vlc_bool_t b_loop = var_GetBool( p_playlist, "loop" );
- vlc_bool_t b_random = var_GetBool( p_playlist, "random" );
- vlc_bool_t b_repeat = var_GetBool( p_playlist, "repeat" );
- vlc_bool_t b_playstop = var_GetBool( p_playlist, "play-and-stop" );
-
- /* Handle quickly a few special cases */
-
- /* No items to play */
- if( p_playlist->i_size == 0 )
- {
- msg_Info( p_playlist, "playlist is empty" );
- return NULL;
- }
-
- /* Repeat and play/stop */
- if( !p_playlist->request.b_request && b_repeat == VLC_TRUE &&
- p_playlist->status.p_item )
- {
- msg_Dbg( p_playlist,"repeating item" );
- return p_playlist->status.p_item;
- }
- if( !p_playlist->request.b_request && b_playstop == VLC_TRUE )
- {
- msg_Dbg( p_playlist,"stopping (play and stop)");
- return NULL;
- }
-
- if( !p_playlist->request.b_request && p_playlist->status.p_item &&
- p_playlist->status.p_item->i_flags & PLAYLIST_SKIP_FLAG )
- {
- msg_Dbg( p_playlist, "blocking item, stopping") ;
- return NULL;
- }
-
- /* Random case. This is an exception: if request, but request is skip +- 1
- * we don't go to next item but select a new random one. */
- if( b_random )
- msg_Err( p_playlist, "random unsupported" );
-#if 0
- &&
- ( !p_playlist->request.b_request ||
- ( p_playlist->request.b_request && ( p_playlist->request.p_item == NULL ||
- p_playlist->request.i_skip == 1 || p_playlist->request.i_skip == -1 ) ) ) )
- {
- /* how many items to choose from ? */
- i_count = 0;
- for ( i = 0; i < p_playlist->i_size; i++ )
- {
- if ( p_playlist->pp_items[i]->p_input->i_nb_played == 0 )
- i_count++;
- }
- /* Nothing left? */
- if ( i_count == 0 )
- {
- /* Don't loop? Exit! */
- if( !b_loop )
- return NULL;
- /* Otherwise reset the counter */
- for ( i = 0; i < p_playlist->i_size; i++ )
- {
- p_playlist->pp_items[i]->p_input->i_nb_played = 0;
- }
- i_count = p_playlist->i_size;
- }
- srand( (unsigned int)mdate() );
- i = rand() % i_count + 1 ;
- /* loop thru the list and count down the unplayed items to the selected one */
- for ( i_new = 0; i_new < p_playlist->i_size && i > 0; i_new++ )
- {
- if ( p_playlist->pp_items[i_new]->p_input->i_nb_played == 0 )
- i--;
- }
- i_new--;
-
- p_playlist->request.i_skip = 0;
- p_playlist->request.b_request = VLC_FALSE;
- return p_playlist->pp_items[i_new];
- }
-#endif
-
- /* Start the real work */
- if( p_playlist->request.b_request )
- {
- PL_DEBUG( "processing request" );
- p_new = p_playlist->request.p_item;
- i_skip = p_playlist->request.i_skip;
-
- p_playlist->status.p_node = p_playlist->request.p_node;
-
- /* If we are asked for a node, take its first item */
- if( i_skip == 0 &&
- ( p_new == NULL || p_new->i_children != -1 ) )
- {
- i_skip++;
- }
-
- if( i_skip > 0 )
- {
- for( i = i_skip; i > 0 ; i-- )
- {
- p_new = playlist_GetNextEnabledLeaf( p_playlist,
- p_playlist->request.p_node,
- p_new );
- if( p_new == NULL )
- {
- PL_DEBUG( "looping - restarting at beginning of node" );
- p_new = playlist_GetNextLeaf( p_playlist,
- p_playlist->request.p_node,
- NULL );
- if( p_new == NULL ) break;
- }
- }
- }
- else if( i_skip < 0 )
- {
- for( i = i_skip; i < 0 ; i++ )
- {
- p_new = playlist_GetPrevLeaf( p_playlist,
- p_playlist->request.p_node,
- p_new );
- if( p_new == NULL )
- {
- PL_DEBUG( "looping - restarting at end of node" );
- /** \bug This is needed because GetPrevLeaf does not loop
- * by itself */
- p_new = playlist_GetLastLeaf( p_playlist,
- p_playlist->request.p_node );
- }
- if( p_new == NULL ) break;
- }
- }
- /* Clear the request */
- p_playlist->request.b_request = VLC_FALSE;
- }
- /* "Automatic" item change ( next ) */
- else
- {
- PL_DEBUG( "changing item without a request" );
- /* Cant go to next from current item */
- if( p_playlist->status.p_item &&
- p_playlist->status.p_item->i_flags & PLAYLIST_SKIP_FLAG )
- return NULL;
-
- p_new = playlist_GetNextLeaf( p_playlist,
- p_playlist->status.p_node,
- p_playlist->status.p_item );
- if( p_new == NULL && b_loop )
- {
- PL_DEBUG( "looping" );
- p_new = playlist_GetNextLeaf( p_playlist,
- p_playlist->status.p_node,
- NULL );
- }
- /* The new item can't be autoselected */
- if( p_new != NULL && p_new->i_flags & PLAYLIST_SKIP_FLAG )
- return NULL;
- }
- if( p_new == NULL )
- {
- msg_Dbg( p_playlist, "did not find something to play" );
- }
- return p_new;
+ return VLC_SUCCESS;
}
-/** Start the input for an item */
-int playlist_PlayItem( playlist_t *p_playlist, playlist_item_t *p_item )
+int playlist_AskForArtEnqueue( playlist_t *p_playlist, input_item_t *p_item )
{
- vlc_value_t val;
- int i_activity = var_GetInteger( p_playlist, "activity") ;
-
- msg_Dbg( p_playlist, "creating new input thread" );
-
- p_item->p_input->i_nb_played++;
- p_playlist->status.p_item = p_item;
+ playlist_private_t *p_sys = pl_priv(p_playlist);
- p_playlist->status.i_status = PLAYLIST_RUNNING;
-
- var_SetInteger( p_playlist, "activity", i_activity +
- DEFAULT_INPUT_ACTIVITY );
- p_playlist->p_input = input_CreateThread( p_playlist, p_item->p_input );
-
- val.i_int = p_item->p_input->i_id;
- /* unlock the playlist to set the var...mmm */
- vlc_mutex_unlock( &p_playlist->object_lock);
- var_Set( p_playlist, "playlist-current", val);
- vlc_mutex_lock( &p_playlist->object_lock);
+ if( p_sys->p_fetcher )
+ playlist_fetcher_Push( p_sys->p_fetcher, p_item );
return VLC_SUCCESS;
}
+