Commit 799b2826 authored by Clément Stenac's avatar Clément Stenac

Playlist

 * Remove the random special case
 * Use the array of currently playing items for all cases
 * Convert array items to array API
 * Replace standard searches in sorted arrays by bsearches
 * Size is not yet fixed (next round).

Array
 * Add reset/value and bsearch functions
 * Add foreach helper
parent c925ed56
......@@ -118,7 +118,7 @@
}
/**
* Binary search in an array
* Binary search in a sorted array. The key must be comparable by < and >
* \param entries array of entries
* \param count number of entries
* \param elem key to check within an entry (like .id, or ->i_id)
......@@ -218,6 +218,11 @@ VLC_EXPORT( int, vlc_DictLookup, (dict_t *, int, const char * ) );
array.i_size = 0; \
array.p_elems = NULL;
#define ARRAY_RESET(array) \
array.i_alloc = 0; \
array.i_size = 0; \
free( array.p_elems ); array.p_elems = NULL;
#define ARRAY_APPEND(array, elem) { \
_ARRAY_GROW1(array); \
array.p_elems[array.i_size] = elem; \
......@@ -244,4 +249,17 @@ VLC_EXPORT( int, vlc_DictLookup, (dict_t *, int, const char * ) );
_ARRAY_SHRINK(array); \
}
#define ARRAY_VAL(array, pos) array.p_elems[pos]
#define ARRAY_BSEARCH(array, elem, zetype, key, answer) \
BSEARCH( array.p_elems, array.i_size, elem, zetype, key, answer)
#define FOREACH_ARRAY( item, array ) { \
int fe_idx; \
for( fe_idx = 0 ; fe_idx < array.i_size ; fe_idx++ ) \
{ \
item = array.p_elems[fe_idx];
#define FOREACH_END() } }
#endif
......@@ -26,13 +26,13 @@
#include <assert.h>
TYPEDEF_ARRAY(playlist_item_t*, playlist_item_array_t);
TYPEDEF_ARRAY(input_item_t*, input_item_array_t);
/**
* \file
* This file contain structures and function prototypes related
* to the playlist in vlc
*/
/**
* \file
* This file contain structures and function prototypes related
* to the playlist in vlc
*
* \defgroup vlc_playlist Playlist
* @{
*/
......@@ -102,20 +102,15 @@ struct playlist_t
/*@{*/
int i_enabled; /**< How many items are enabled ? */
/* Arrays of items */
int i_size; /**< total size of the list */
playlist_item_t ** pp_items; /**< array of pointers to the
* playlist items */
int i_all_size; /**< size of list of items and nodes */
playlist_item_t ** pp_all_items; /**< array of pointers to the
* playlist items and nodes */
int i_input_items;
input_item_t ** pp_input_items;
int i_random; /**< Number of candidates for random */
playlist_item_t ** pp_random; /**< Random candidate items */
int i_random_index; /**< Current random item */
vlc_bool_t b_reset_random; /**< Recreate random array ?*/
playlist_item_array_t items; /**< Arrays of items */
playlist_item_array_t all_items; /**< Array of items and nodes */
input_item_array_t input_items; /**< Array of input items */
playlist_item_array_t current; /**< Items currently being played */
int i_current_index; /**< Index in current array */
/** Reset current item ? */
vlc_bool_t b_reset_currently_playing;
int i_last_playlist_id; /**< Last id to an item */
int i_last_input_id ; /**< Last id on an input */
......@@ -345,7 +340,7 @@ static inline int playlist_MLAddExt( playlist_t *p_playlist,
i_duration, ppsz_options, i_options, VLC_FALSE );
}
/** Add an input item to the playlist node
/** Add an input item to the playlist node
* \see playlist_AddInput
*/
static inline int playlist_PlaylistAddInput( playlist_t* p_playlist,
......@@ -433,7 +428,7 @@ static inline vlc_bool_t playlist_IsEmpty( playlist_t * p_playlist )
{
vlc_bool_t b_empty;
vlc_mutex_lock( &p_playlist->object_lock );
b_empty = p_playlist->i_size == 0;
b_empty = p_playlist->items.i_size == 0;
vlc_mutex_unlock( &p_playlist->object_lock );
return( b_empty );
}
......
......@@ -796,13 +796,13 @@ static void PlayBookmark( intf_thread_t *p_intf, int i_num )
var_Get( p_intf, psz_bookmark_name, &val );
char *psz_bookmark = strdup( val.psz_string );
for( i = 0; i < p_playlist->i_size; i++)
for( i = 0; i < p_playlist->items.i_size; i++)
{
if( !strcmp( psz_bookmark,
p_playlist->pp_items[i]->p_input->psz_uri ) )
ARRAY_VAL( p_playlist->items,i )->p_input->psz_uri ) )
{
playlist_LockControl( p_playlist, PLAYLIST_VIEWPLAY, NULL,
p_playlist->pp_items[i] );
ARRAY_VAL( p_playlist->items, i ) );
break;
}
}
......
......@@ -398,19 +398,20 @@ void E_(MacroDo)( httpd_file_sys_t *p_args,
i_nb_items++;
}
for( i = p_sys->p_playlist->i_size - 1 ; i >= 0; i-- )
for( i = p_sys->p_playlist->items.i_size - 1 ; i >= 0; i-- )
{
/* Check if the item is in the keep list */
for( j = 0 ; j < i_nb_items ; j++ )
{
if( p_items[j] ==
p_sys->p_playlist->pp_items[i]->p_input->i_id )
ARRAY_VAL(p_sys->p_playlist->items,i)
->p_input->i_id)
break;
}
if( j == i_nb_items )
{
playlist_LockDeleteAllFromInput( p_sys->p_playlist,
p_sys->p_playlist->pp_items[i]->p_input->i_id );
p_sys->p_playlist->items.p_elems[i]->p_input->i_id );
msg_Dbg( p_intf, "requested playlist delete: %d",
i );
}
......
......@@ -68,7 +68,7 @@
playlist_t * p_playlist = pl_Yield( p_intf );
vlc_mutex_lock( &p_playlist->object_lock );
if( p_playlist->i_size <= 0 )
if( playlist_IsEmpty( p_playlist ) )
{
vlc_mutex_unlock( &p_playlist->object_lock );
vlc_object_release( p_playlist );
......@@ -812,7 +812,8 @@
else if( [[o_mi title] isEqualToString: _NS("Previous")] ||
[[o_mi title] isEqualToString: _NS("Next")] )
{
bEnabled = p_playlist->i_size > 1;
/** \todo fix i_size use */
bEnabled = p_playlist->items.i_size > 1;
}
else if( [[o_mi title] isEqualToString: _NS("Random")] )
{
......
......@@ -1067,7 +1067,8 @@ static VLCMain *_o_sharedMainInstance = nil;
vlc_bool_t b_chapters = VLC_FALSE;
playlist_t * p_playlist = pl_Yield( p_intf );
b_plmul = p_playlist->i_size > 1;
/** \todo fix i_size use */
b_plmul = p_playlist->items.i_size > 1;
vlc_object_release( p_playlist );
......
......@@ -464,14 +464,15 @@ NSLog( @"expandable" );
playlist_t *p_playlist = pl_Yield( VLCIntf );
if( p_playlist->i_size >= 2 )
/** \todo fix i_size use */
if( p_playlist->items.i_size >= 2 )
{
[o_status_field setStringValue: [NSString stringWithFormat:
_NS("%i items in the playlist"), p_playlist->i_size]];
_NS("%i items in the playlist"), p_playlist->items.i_size]];
}
else
{
if( p_playlist->i_size == 0 )
if( playlist_IsEmpty( p_playlist ) )
{
[o_status_field setStringValue: _NS("No items in the playlist")];
}
......@@ -1366,15 +1367,15 @@ NSLog( @"expandable" );
/* FIXME: playlist->i_size doesn't provide the correct number of items anymore
* check the playlist API for the fixed function, once zorglub implemented it -- fpk, 9/17/06 */
if( p_playlist->i_size >= 2 )
/** \todo fix i_size use */
if( p_playlist->items.i_size >= 2 )
{
[o_status_field setStringValue: [NSString stringWithFormat:
_NS("%i items in the playlist"), p_playlist->i_size]];
_NS("%i items in the playlist"), p_playlist->items.i_size]];
}
else
{
if( p_playlist->i_size == 0 )
if( playlist_IsEmpty( p_playlist ) )
{
[o_status_field setStringValue: _NS("No items in the playlist")];
}
......
......@@ -556,7 +556,7 @@ void MainInterface::stop()
}
void MainInterface::play()
{
if( !THEPL->i_size || !THEPL->i_enabled )
if( !playlist_IsEmpty(THEPL) || !THEPL->i_enabled )
{
/* The playlist is empty, open a file requester */
THEDP->simpleOpenDialog();
......
......@@ -354,7 +354,7 @@ QMenu *QVLCMenu::SDMenu( intf_thread_t *p_intf )
else \
MIM_SADD( qtr("Pause"), "", "", togglePlayPause() ) \
} \
else if( THEPL->i_size && THEPL->i_enabled ) \
else if( THEPL->items.i_size && THEPL->i_enabled ) \
MIM_SADD( qtr("Play"), "", "", togglePlayPause() ) \
\
QMenu *intfmenu = InterfacesMenu( p_intf, NULL ); \
......
......@@ -35,10 +35,8 @@ void CmdPlay::execute()
return;
}
if( pPlaylist->i_size )
{
if( !playlist_IsEmpty( pPlaylist ) )
playlist_Play( pPlaylist );
}
else
{
// If the playlist is empty, open a file requester instead
......
......@@ -220,7 +220,7 @@ mediacontrol_start( mediacontrol_Instance *self,
}
vlc_mutex_lock( &p_playlist->object_lock );
if( p_playlist->i_size )
if( p_playlist->items.i_size )
{
int i_from;
char *psz_from = NULL;
......@@ -360,13 +360,13 @@ mediacontrol_playlist_get_list( mediacontrol_Instance *self,
}
vlc_mutex_lock( &p_playlist->object_lock );
i_playlist_size = p_playlist->i_size;
i_playlist_size = p_playlist->items.i_size;
retval = mediacontrol_PlaylistSeq__alloc( i_playlist_size );
for( i_index = 0 ; i_index < i_playlist_size ; i_index++ )
{
retval->data[i_index] = strdup( p_playlist->pp_items[i_index]->p_input->psz_uri );
retval->data[i_index] = strdup( ARRAY_VAL(p_playlist->items, i_index)->p_input->psz_uri );
}
vlc_mutex_unlock( &p_playlist->object_lock );
......
......@@ -44,7 +44,7 @@ void libvlc_playlist_play( libvlc_instance_t *p_instance, int i_id,
assert( PL );
///\todo Handle additionnal options
if( PL->i_size == 0 ) RAISEVOID( "Empty playlist" );
if( PL->items.i_size == 0 ) RAISEVOID( "Empty playlist" );
if( i_id > 0 )
{
playlist_item_t *p_item = playlist_ItemGetById( PL,
......@@ -136,7 +136,7 @@ int libvlc_playlist_items_count( libvlc_instance_t *p_instance,
libvlc_exception_t *p_e )
{
assert( PL );
return PL->i_size;
return PL->items.i_size;
}
libvlc_input_t * libvlc_playlist_get_input( libvlc_instance_t *p_instance,
......
......@@ -76,15 +76,10 @@ static void input_ItemDestroy ( gc_object_t *p_this )
playlist_t *p_playlist = pl_Yield( p_obj );
input_ItemClean( p_input );
for( i = 0 ; i< p_playlist->i_input_items ; i++ )
{
if( p_playlist->pp_input_items[i]->i_id == p_input->i_id )
{
REMOVE_ELEM( p_playlist->pp_input_items,
p_playlist->i_input_items, i );
break;
}
}
ARRAY_BSEARCH( p_playlist->input_items,->i_id, int, p_input->i_id, i);
if( i != -1 )
ARRAY_REMOVE( p_playlist->input_items, i);
pl_Release( p_obj );
free( p_input );
}
......@@ -186,22 +181,10 @@ int input_ItemAddInfo( input_item_t *p_i,
input_item_t *input_ItemGetById( playlist_t *p_playlist, int i_id )
{
int i, i_top, i_bottom;
i_bottom = 0; i_top = p_playlist->i_input_items -1;
i = i_top /2 ;
while( p_playlist->pp_input_items[i]->i_id != i_id &&
i_top > i_bottom )
{
if( p_playlist->pp_input_items[i]->i_id < i_id )
i_bottom = i + 1;
else
i_top = i - 1;
i = i_bottom + ( i_top - i_bottom ) / 2;
}
if( p_playlist->pp_input_items[i]->i_id == i_id )
{
return p_playlist->pp_input_items[i];
}
int i;
ARRAY_BSEARCH( p_playlist->input_items, ->i_id, int, i_id, i);
if( i != -1 )
return ARRAY_VAL( p_playlist->input_items, i);
return NULL;
}
......@@ -228,9 +211,7 @@ input_item_t *input_ItemNewWithType( vlc_object_t *p_obj, const char *psz_uri,
PL_LOCK;
p_input->i_id = ++p_playlist->i_last_input_id;
TAB_APPEND( p_playlist->i_input_items,
p_playlist->pp_input_items,
p_input );
ARRAY_APPEND( p_playlist->input_items, p_input );
PL_UNLOCK;
pl_Release( p_obj );
......
......@@ -670,7 +670,7 @@ int VLC_PlaylistNumberOfItems( int i_object )
{
int i_size;
LIBVLC_PLAYLIST_FUNC;
i_size = p_libvlc->p_playlist->i_size;
i_size = p_libvlc->p_playlist->items.i_size;
LIBVLC_PLAYLIST_FUNC_END;
return i_size;
}
......
......@@ -84,10 +84,8 @@ int PlaylistVAControl( playlist_t * p_playlist, int i_query, va_list args )
playlist_item_t *p_item, *p_node;
vlc_value_t val;
if( p_playlist->i_size <= 0 )
{
if( p_playlist->items.i_size <= 0 )
return VLC_EGENERIC;
}
switch( i_query )
{
......@@ -100,7 +98,6 @@ int PlaylistVAControl( playlist_t * p_playlist, int i_query, va_list args )
// Node can be null, it will keep the same. Use with care ...
// Item null = take the first child of node
case PLAYLIST_VIEWPLAY:
p_playlist->b_reset_random = VLC_TRUE;
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 )
......@@ -113,6 +110,8 @@ int PlaylistVAControl( playlist_t * p_playlist, int i_query, va_list args )
p_playlist->request.b_request = VLC_TRUE;
p_playlist->request.p_node = p_node;
p_playlist->request.p_item = p_item;
if( p_item && var_GetBool( p_playlist, "random" ) )
p_playlist->b_reset_currently_playing = VLC_TRUE;
break;
case PLAYLIST_PLAY:
......@@ -261,6 +260,66 @@ void PreparseEnqueueItemSub( playlist_t *p_playlist,
* Playback logic
*****************************************************************************/
static void ResyncCurrentIndex(playlist_t *p_playlist, playlist_item_t *p_cur )
{
PL_DEBUG("resyncing on %s", PLI_NAME(p_cur) );
/* Simply resync index */
int i;
p_playlist->i_current_index = -1;
for( i = 0 ; i< p_playlist->current.i_size; i++ )
{
if( ARRAY_VAL(p_playlist->current, i) == p_cur )
{
p_playlist->i_current_index = i;
break;
}
}
PL_DEBUG("%s is at %i", PLI_NAME(p_cur), p_playlist->i_current_index );
}
static void ResetCurrentlyPlaying( playlist_t *p_playlist, vlc_bool_t b_random,
playlist_item_t *p_cur )
{
playlist_item_t *p_next = NULL;
PL_DEBUG("rebuilding array of current - root %s",
PLI_NAME(p_playlist->status.p_node) );
ARRAY_RESET(p_playlist->current);
p_playlist->i_current_index = -1;
while( 1 )
{
/** FIXME: this is *slow* */
p_next = playlist_GetNextLeaf( p_playlist,
p_playlist->status.p_node,
p_next, VLC_TRUE, VLC_FALSE );
if( p_next )
{
if( p_next == p_cur )
p_playlist->i_current_index = p_playlist->current.i_size;
ARRAY_APPEND( p_playlist->current, p_next);
}
else break;
}
PL_DEBUG("rebuild done - %i items, index %i", p_playlist->current.i_size,
p_playlist->i_current_index);
if( b_random )
{
/* Shuffle the array */
srand( (unsigned int)mdate() );
int swap = 0;
int j;
for( j = p_playlist->current.i_size - 1; j > 0; j-- )
{
swap++;
int i = rand() % (j+1); /* between 0 and j */
playlist_item_t *p_tmp;
p_tmp = ARRAY_VAL(p_playlist->current, i);
ARRAY_VAL(p_playlist->current,i) = ARRAY_VAL(p_playlist->current,j);
ARRAY_VAL(p_playlist->current,j) = p_tmp;
}
}
p_playlist->b_reset_currently_playing = VLC_FALSE;
}
/** 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 )
......@@ -275,7 +334,7 @@ playlist_item_t * playlist_NextItem( playlist_t *p_playlist )
/* Handle quickly a few special cases */
/* No items to play */
if( p_playlist->i_size == 0 )
if( p_playlist->items.i_size == 0 )
{
msg_Info( p_playlist, "playlist is empty" );
return NULL;
......@@ -308,74 +367,6 @@ playlist_item_t * playlist_NextItem( playlist_t *p_playlist )
}
}
/* 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.b_request &&
( p_playlist->request.p_item == NULL ||
p_playlist->request.i_skip == 1 ||
p_playlist->request.i_skip == -1 ) ) ) )
{
PL_DEBUG( "doing random, have %i items, currently at %i, reset %i\n",
p_playlist->i_random, p_playlist->i_random_index,
p_playlist->b_reset_random );
if( p_playlist->b_reset_random )
{
int j;
FREE( p_playlist->pp_random );
if( !p_playlist->b_reset_random && !b_loop ) goto end;
p_playlist->i_random = 0;
p_playlist->i_random_index = 0;
p_playlist->i_random = playlist_GetAllEnabledChildren(
p_playlist,
p_playlist->status.p_node,
&p_playlist->pp_random );
/* Shuffle the array */
srand( (unsigned int)mdate() );
int swap = 0;
for( j = p_playlist->i_random -1; j > 0; j-- )
{
swap++;
int i = rand() % (j+1); /* between 0 and j */
playlist_item_t *p_tmp;
p_tmp = p_playlist->pp_random[i];
p_playlist->pp_random[i] = p_playlist->pp_random[j];
p_playlist->pp_random[j] = p_tmp;
}
p_playlist->b_reset_random = VLC_FALSE;
PL_DEBUG( "random rebuilt, have %i items", p_playlist->i_random );
}
else
{
/* Go backward or forward */
if( !p_playlist->request.b_request || !p_playlist->request.p_item ||
p_playlist->request.i_skip == 1 )
p_playlist->i_random_index++;
else
p_playlist->i_random_index--;
/* Handle bounds situations */
if( p_playlist->i_random_index == -1 )
{
if( !b_loop || p_playlist->i_random == 0 ) goto end;
p_playlist->i_random_index = p_playlist->i_random - 1;
}
else if( p_playlist->i_random_index == p_playlist->i_random )
{
if( !b_loop || p_playlist->i_random == 0 ) goto end;
p_playlist->i_random_index = 0;
}
}
PL_DEBUG( "using random item %i", p_playlist->i_random_index );
if ( p_playlist->i_random == 0 ) goto end; /* Can this happen ?? */
p_new = p_playlist->pp_random[p_playlist->i_random_index];
end:
if( !p_new ) p_playlist->b_reset_random = VLC_TRUE;
p_playlist->request.i_skip = 0;
p_playlist->request.b_request = VLC_FALSE;
return p_new;
}
/* Start the real work */
if( p_playlist->request.b_request )
{
......@@ -385,47 +376,51 @@ end:
PLI_NAME( p_playlist->request.p_item ),
PLI_NAME( p_playlist->request.p_node ), i_skip );
if( p_playlist->request.p_node )
if( p_playlist->request.p_node &&
p_playlist->request.p_node != p_playlist->status.p_node )
{
p_playlist->status.p_node = p_playlist->request.p_node;
p_playlist->b_reset_currently_playing = VLC_TRUE;
}
/* If we are asked for a node, dont take it */
if( i_skip == 0 && ( p_new == NULL || p_new->i_children != -1 ) )
i_skip++;
if( i_skip > 0 )
if( p_playlist->b_reset_currently_playing )
ResetCurrentlyPlaying( p_playlist, b_random, p_new );
else if( p_new )
ResyncCurrentIndex( p_playlist, p_new );
else
p_playlist->i_current_index = -1;
if( p_playlist->current.i_size && i_skip > 0 )
{
for( i = i_skip; i > 0 ; i-- )
{
p_new = playlist_GetNextLeaf( p_playlist,
p_playlist->request.p_node,
p_new, VLC_TRUE, VLC_FALSE );
if( p_new == NULL )
p_playlist->i_current_index++;
if( p_playlist->i_current_index == p_playlist->current.i_size )
{
PL_DEBUG( "looping - restarting at beginning of node" );
p_new = playlist_GetNextLeaf( p_playlist,
p_playlist->request.p_node,
NULL, VLC_TRUE, VLC_FALSE);
if( p_new == NULL ) break;
p_playlist->i_current_index = 0;
}
}
p_new = ARRAY_VAL( p_playlist->current,
p_playlist->i_current_index );
}
else if( i_skip < 0 )
else if( p_playlist->current.i_size && i_skip < 0 )
{
for( i = i_skip; i < 0 ; i++ )
{
p_new = playlist_GetPrevLeaf( p_playlist,
p_playlist->request.p_node,
p_new, VLC_FALSE, VLC_FALSE );
if( p_new == NULL )
p_playlist->i_current_index--;
if( p_playlist->i_current_index == -1 )
{
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 );
p_playlist->i_current_index = p_playlist->current.i_size-1;
}
if( p_new == NULL ) break;
}
p_new = ARRAY_VAL( p_playlist->current,
p_playlist->i_current_index );
}
/* Clear the request */
p_playlist->request.b_request = VLC_FALSE;
......@@ -433,31 +428,31 @@ end:
/* "Automatic" item change ( next ) */
else
{
PL_DEBUG( "changing item without a request" );
PL_DEBUG( "changing item without a request (current %i/%i)",
p_playlist->i_current_index, p_playlist->current.i_size );
/* 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,
VLC_TRUE, VLC_FALSE );
if( p_new == NULL && b_loop )
if( p_playlist->b_reset_currently_playing )
ResetCurrentlyPlaying( p_playlist, b_random,
p_playlist->status.p_item );
p_playlist->i_current_index++;
if( p_playlist->i_current_index == p_playlist->current.i_size )
{
PL_DEBUG( "looping" );
p_new = playlist_GetNextLeaf( p_playlist,
p_playlist->status.p_node,
NULL, VLC_TRUE, VLC_FALSE );