/*****************************************************************************
* playlist.c : Playlist management functions
*****************************************************************************
- * Copyright (C) 1999-2004 VideoLAN
+ * Copyright (C) 1999-2004 the VideoLAN team
* $Id$
*
* Authors: Samuel Hocevar <sam@zoy.org>
#include "vlc_playlist.h"
+#include "vlc_interaction.h"
+
#define TITLE_CATEGORY N_( "By category" )
#define TITLE_SIMPLE N_( "Manually added" )
#define TITLE_ALL N_( "All items, unsorted" )
static playlist_item_t * NextItem ( playlist_t * );
static int PlayItem ( playlist_t *, playlist_item_t * );
-static int ItemChange( vlc_object_t *, const char *,
- vlc_value_t, vlc_value_t, void * );
-
int playlist_vaControl( playlist_t * p_playlist, int i_query, va_list args );
+void playlist_PreparseEnqueueItemSub( playlist_t *, playlist_item_t * );
+
+playlist_item_t *playlist_RecursiveFindLast(playlist_t *p_playlist,
+ playlist_item_t *p_node );
+
+/*****************************************************************************
+ * Helper Function for NextItem
+ *****************************************************************************/
+
+playlist_item_t *playlist_RecursiveFindLast(playlist_t *p_playlist,
+ playlist_item_t *p_node )
+{
+ int i;
+ playlist_item_t *p_item;
+ for ( i = p_node->i_children - 1; i >= 0; i-- )
+ {
+ if( p_node->pp_children[i]->i_children == -1 )
+ return p_node->pp_children[i];
+ else if(p_node->pp_children[i]->i_children > 0)
+ {
+ p_item = playlist_RecursiveFindLast( p_playlist,
+ p_node->pp_children[i] );
+ if ( p_item != NULL )
+ return p_item;
+ }
+ else if( i == 0 )
+ return NULL;
+ }
+ return NULL;
+}
+
/**
* Create playlist
var_CreateGetBool( p_playlist, "loop" );
/* Initialise data structures */
+ vlc_mutex_init( p_playlist, &p_playlist->gc_lock );
p_playlist->i_last_id = 0;
p_playlist->b_go_next = VLC_TRUE;
p_playlist->p_input = NULL;
p_view = playlist_ViewFind( p_playlist, VIEW_CATEGORY );
- p_playlist->p_general = playlist_NodeCreate( p_playlist, VIEW_CATEGORY,
- _( "General" ), p_view->p_root );
+ p_playlist->p_general =
+ playlist_NodeCreate( p_playlist, VIEW_CATEGORY,
+ _( "General" ), p_view->p_root );
p_playlist->p_general->i_flags |= PLAYLIST_RO_FLAG;
/* Set startup status
return NULL;
}
+ // Preparse
p_playlist->p_preparse->i_waiting = 0;
p_playlist->p_preparse->pp_waiting = NULL;
+ // Interaction
+ p_playlist->p_interaction = NULL;
+
vlc_object_attach( p_playlist->p_preparse, p_playlist );
if( vlc_thread_create( p_playlist->p_preparse, "preparser",
RunPreparse, VLC_THREAD_PRIORITY_LOW, VLC_TRUE ) )
int i;
p_playlist->b_die = 1;
- for( i = 0 ; i< p_playlist->i_sds ; i++ )
+ while( p_playlist->i_sds )
{
playlist_ServicesDiscoveryRemove( p_playlist,
- p_playlist->pp_sds[i]->psz_module );
+ p_playlist->pp_sds[0]->psz_module );
+ }
+
+ if( p_playlist->p_interaction )
+ {
+ intf_InteractionDestroy( p_playlist->p_interaction );
}
vlc_thread_join( p_playlist->p_preparse );
free( p_view );
}
+ vlc_mutex_destroy( &p_playlist->gc_lock );
vlc_object_destroy( p_playlist->p_preparse );
vlc_object_destroy( p_playlist );
int playlist_vaControl( playlist_t * p_playlist, int i_query, va_list args )
{
playlist_view_t *p_view;
+ playlist_item_t *p_item, *p_node;
+ int i_view;
vlc_value_t val;
#ifdef PLAYLIST_PROFILE
case PLAYLIST_STOP:
p_playlist->status.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->input.psz_uri == NULL )
+ return VLC_EGENERIC;
p_playlist->status.i_status = PLAYLIST_RUNNING;
p_playlist->request.i_skip = 0;
p_playlist->request.b_request = VLC_TRUE;
- p_playlist->request.p_item = (playlist_item_t *)va_arg( args,
- playlist_item_t *);
+ p_playlist->request.p_item = p_item;
p_playlist->request.i_view = p_playlist->status.i_view;
p_view = playlist_ViewFind( p_playlist, p_playlist->status.i_view );
if( p_view )
break;
case PLAYLIST_VIEWPLAY:
+ i_view = (int)va_arg( args,int );
+ 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_item != NULL && p_item->input.psz_uri
+ // == NULL ))
+ {
+ p_playlist->status.i_status = PLAYLIST_STOPPED;
+ p_playlist->request.b_request = VLC_TRUE;
+ return VLC_SUCCESS;
+ }
p_playlist->status.i_status = PLAYLIST_RUNNING;
p_playlist->request.i_skip = 0;
p_playlist->request.b_request = VLC_TRUE;
- p_playlist->request.i_view = (int)va_arg( args,int );
- p_playlist->request.p_node = (playlist_item_t *)va_arg( args,
- playlist_item_t *);
- p_playlist->request.p_item = (playlist_item_t *)va_arg( args,
- playlist_item_t *);
-
- /* If we select a node, play only it.
- * If we select an item, continue */
- if( p_playlist->request.p_item == NULL ||
- ! p_playlist->request.p_node->i_flags & PLAYLIST_SKIP_FLAG )
+ p_playlist->request.i_view = i_view;
+ p_playlist->request.p_node = p_node;
+ p_playlist->request.p_item = p_item;
+
+ /* Don't go further if the node doesn't want to */
+ if( ! p_playlist->request.p_node->i_flags & PLAYLIST_SKIP_FLAG )
{
p_playlist->b_go_next = VLC_FALSE;
}
case PLAYLIST_AUTOPLAY:
p_playlist->status.i_status = PLAYLIST_RUNNING;
+ p_playlist->status.p_node = p_playlist->p_general;
p_playlist->request.b_request = VLC_FALSE;
break;
break;
case PLAYLIST_SKIP:
+ p_playlist->request.i_view = p_playlist->status.i_view;
if( p_playlist->status.i_view > -1 )
{
p_playlist->request.p_node = p_playlist->status.p_node;
p_playlist->request.p_item = p_playlist->status.p_item;
}
+ else
+ {
+ p_playlist->request.p_node = NULL;
+ p_playlist->request.p_item = NULL;
+ }
p_playlist->request.i_skip = (int) va_arg( args, int );
p_playlist->request.b_request = VLC_TRUE;
break;
break;
default:
- msg_Err( p_playlist, "unimplemented playlist query" );
+ msg_Err( p_playlist, "unknown playlist query" );
return VLC_EBADVAR;
break;
}
return VLC_SUCCESS;
}
+/* Should only be called if playlist and preparser are locked */
+void playlist_PreparseEnqueueItemSub( playlist_t *p_playlist,
+ playlist_item_t *p_item )
+{
+ int i;
+ if( p_item->i_children == -1 )
+ {
+ INSERT_ELEM( p_playlist->p_preparse->pp_waiting,
+ p_playlist->p_preparse->i_waiting,
+ p_playlist->p_preparse->i_waiting,
+ &(p_item->input) );
+ }
+ else
+ {
+ for( i = 0; i < p_item->i_children; i++)
+ {
+ playlist_PreparseEnqueueItemSub( p_playlist,
+ p_item->pp_children[i] );
+ }
+ }
+}
+
+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 );
+ playlist_PreparseEnqueueItemSub( p_playlist, p_item );
+ vlc_mutex_unlock( &p_playlist->p_preparse->object_lock );
+ vlc_mutex_unlock( &p_playlist->object_lock );
+ return VLC_SUCCESS;
+}
+
/* Destroy remaining objects */
static mtime_t ObjectGarbageCollector( playlist_t *p_playlist, int i_type,
}
else
{
+ vlc_mutex_lock( &p_playlist->gc_lock );
while( ( p_obj = vlc_object_find( p_playlist, i_type, FIND_CHILD ) ) )
{
if( p_obj->p_parent != (vlc_object_t*)p_playlist )
sout_DeleteInstance( (sout_instance_t*)p_obj );
}
}
+ vlc_mutex_unlock( &p_playlist->gc_lock );
return 0;
}
}
static void RunThread ( playlist_t *p_playlist )
{
vlc_object_t *p_obj;
- playlist_item_t *p_item;
+ playlist_item_t *p_item = NULL;
mtime_t i_vout_destroyed_date = 0;
mtime_t i_sout_destroyed_date = 0;
while( !p_playlist->b_die )
{
+ if( p_playlist->p_interaction )
+ {
+ intf_InteractionManage( p_playlist );
+ }
+
vlc_mutex_lock( &p_playlist->object_lock );
/* First, check if we have something to do */
/* If there is an input, check that it doesn't need to die. */
if( p_playlist->p_input )
{
+ stats_ComputeInputStats( p_playlist->p_input,
+ p_playlist->p_input->input.p_item->p_stats );
+
/* This input is dead. Remove it ! */
if( p_playlist->p_input->b_dead )
{
continue;
}
- /* This input is dying, let him do */
+ /* This input is dying, let it do */
else if( p_playlist->p_input->b_die )
{
;
}
- /* This input has finished, ask him to die ! */
+ /* This input has finished, ask it to die ! */
else if( p_playlist->p_input->b_error
|| p_playlist->p_input->b_eof )
{
* Get the next item to play */
p_item = NextItem( p_playlist );
-
/* We must stop */
if( p_item == NULL )
{
}
else if( p_playlist->status.i_status == PLAYLIST_STOPPED )
{
+ if( p_item && p_playlist->status.p_item &&
+ p_playlist->status.p_item->i_flags & PLAYLIST_REMOVE_FLAG )
+ {
+ playlist_ItemDelete( p_item );
+ p_playlist->status.p_item = NULL;
+ }
+
/* Collect garbage */
vlc_mutex_unlock( &p_playlist->object_lock );
i_sout_destroyed_date =
}
else if( p_playlist->p_input->b_die )
{
- /* This input is dying, leave him alone */
+ /* This input is dying, leave it alone */
;
}
else if( p_playlist->p_input->b_error || p_playlist->p_input->b_eof )
int i_skip,i_goto,i, i_new, i_count ;
playlist_view_t *p_view;
- vlc_bool_t b_loop = var_GetBool( p_playlist, "loop");
+ 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" );
int64_t start = mdate();
#endif
-
/* Handle quickly a few special cases */
/* No items to play */
}
/* Repeat and play/stop */
- if( !p_playlist->request.b_request && b_repeat == VLC_TRUE )
+ 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 && p_playlist->status.p_item &&
- !(p_playlist->status.p_item->i_flags & PLAYLIST_SKIP_FLAG) )
+ !( p_playlist->status.p_item->i_flags & PLAYLIST_SKIP_FLAG ) )
{
msg_Dbg( p_playlist, "no-skip mode, stopping") ;
return NULL;
/* TODO: use the "shuffled view" internally ? */
/* 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 && (!p_playlist->request.b_request ||
- p_playlist->request.i_skip == 1 || p_playlist->request.i_skip == -1 ) )
+ if( b_random &&
+ ( !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 ) ) ) )
{
- srand( (unsigned int)mdate() );
- i_new = 0;
- for( i_count = 0; i_count < p_playlist->i_size - 1 ; i_count ++ )
+ /* how many items to choose from ? */
+ i_count = 0;
+ for ( i = 0; i < p_playlist->i_size; i++ )
{
- i_new =
- (int)((float)p_playlist->i_size * rand() / (RAND_MAX+1.0));
- /* Check if the item has not already been played */
- if( p_playlist->pp_items[i_new]->i_nb_played == 0 )
- break;
+ if ( p_playlist->pp_items[i]->i_nb_played == 0 )
+ i_count++;
}
- if( i_count == p_playlist->i_size )
+ /* Nothing left? */
+ if ( i_count == 0 )
{
- /* The whole playlist has been played: reset the counters */
- while( i_count > 0 )
- {
- p_playlist->pp_items[--i_count]->i_nb_played = 0;
- }
- if( !b_loop )
- {
+ /* 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]->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]->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];
- }
+ }
/* Start the real work */
if( p_playlist->request.b_request )
msg_Dbg( p_playlist,"processing request" );
#endif
/* We are not playing from a view */
- if( p_playlist->request.i_view == -1 )
+ if( p_playlist->request.i_view == -1 )
{
#ifdef PLAYLIST_DEBUG
msg_Dbg( p_playlist, "non-view mode request");
#endif
/* Directly select the item, just like now */
+ p_new = p_playlist->request.p_item;
i_skip = p_playlist->request.i_skip;
i_goto = p_playlist->request.i_goto;
- if( p_playlist->i_index == -1 ) p_playlist->i_index = 0;
- p_new = p_playlist->pp_items[p_playlist->i_index];
+ if( p_playlist->i_index < 0 ) p_playlist->i_index = 0;
+ if ( p_new == NULL )
+ p_new = p_playlist->pp_items[p_playlist->i_index];
if( i_goto >= 0 && i_goto < p_playlist->i_size )
{
}
p_playlist->request.i_skip = 0;
}
+ if( !( p_new->i_flags & PLAYLIST_SKIP_FLAG ) )
+ {
+ return NULL;
+ }
}
else
{
p_playlist->status.i_view = p_playlist->request.i_view;
if( !p_view )
{
- msg_Err( p_playlist, "p_view is NULL and should not! (FIXME)" );
+ msg_Err( p_playlist, "p_view is NULL and should not! (requested view is %i", p_playlist->request.i_view );
}
else if( i_skip > 0 )
{
p_new );
if( p_new == NULL )
{
- if( b_loop )
- {
- p_new = playlist_FindNextFromParent( p_playlist,
- p_playlist->request.i_view,
- p_view->p_root,
- p_playlist->request.p_node,
- NULL );
- if( p_new == NULL ) break;
- }
- else
- {
- break;
- }
+#ifdef PLAYLIST_DEBUG
+ msg_Dbg( p_playlist, "looping" );
+#endif
+ p_new = playlist_FindNextFromParent( p_playlist,
+ p_playlist->request.i_view,
+ p_view->p_root,
+ p_view->p_root,
+ NULL );
+ if( p_new == NULL ) break;
}
}
}
p_view->p_root,
p_playlist->request.p_node,
p_new );
+ if( p_new == NULL )
+ {
+ /* We reach the beginning of the playlist.
+ Go back to the last item. */
+ p_new = playlist_RecursiveFindLast( p_playlist,
+ p_view->p_root );
+ }
if( p_new == NULL ) break;
}
if( p_playlist->status.i_view == -1 )
{
+#ifdef PLAYLIST_DEBUG
+ msg_Dbg( p_playlist, "no request - old mode" );
+#endif
if( p_playlist->i_index + 1 < p_playlist->i_size )
{
p_playlist->i_index++;
p_new = p_playlist->pp_items[p_playlist->i_index];
- if( !(p_new->i_flags & PLAYLIST_SKIP_FLAG) )
+ if( !( p_new->i_flags & PLAYLIST_SKIP_FLAG ) )
{
return NULL;
}
/* We are playing with a view */
else
{
+#ifdef PLAYLIST_DEBUG
+ msg_Dbg( p_playlist,"no request - from a view" );
+#endif
playlist_view_t *p_view =
playlist_ViewFind( p_playlist,
p_playlist->status.i_view );
p_playlist->status.p_item );
if( p_new == NULL && b_loop )
{
+#ifdef PLAYLIST_DEBUG
+ msg_Dbg( p_playlist, "looping" );
+#endif
p_new = playlist_FindNextFromParent( p_playlist,
p_playlist->status.i_view,
p_view->p_root,
- p_playlist->status.p_node,
+ p_view->p_root,
NULL );
}
+ if( p_new != NULL && !(p_new->i_flags & PLAYLIST_SKIP_FLAG) )
+ return NULL;
}
}
}
p_playlist->p_input = input_CreateThread( p_playlist, &p_item->input );
- var_AddCallback( p_playlist->p_input, "item-change",
- ItemChange, p_playlist );
-
val.i_int = p_item->input.i_id;
/* unlock the playlist to set the var...mmm */
vlc_mutex_unlock( &p_playlist->object_lock);
return VLC_SUCCESS;
}
-
-/* Forward item change from input */
-static int ItemChange( vlc_object_t *p_obj, const char *psz_var,
- vlc_value_t oldval, vlc_value_t newval, void *param )
-{
- playlist_t *p_playlist = (playlist_t *)param;
-
- //p_playlist->b_need_update = VLC_TRUE;
- var_SetInteger( p_playlist, "item-change", newval.i_int );
-
- /* Update view */
- /* FIXME: Make that automatic */
-// playlist_ViewUpdate( p_playlist, VIEW_S_AUTHOR );
-
- return VLC_SUCCESS;
-}