1 /*****************************************************************************
2 * playlist.c : Playlist management functions
3 *****************************************************************************
4 * Copyright (C) 1999-2004 the VideoLAN team
7 * Authors: Samuel Hocevar <sam@zoy.org>
8 * Clément Stenac <zorglub@videolan.org>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
23 *****************************************************************************/
24 #include <stdlib.h> /* free(), strtol() */
25 #include <stdio.h> /* sprintf() */
26 #include <string.h> /* strerror() */
31 #include <vlc/input.h>
33 #include "vlc_playlist.h"
35 #define TITLE_CATEGORY N_( "By category" )
36 #define TITLE_SIMPLE N_( "Manually added" )
37 #define TITLE_ALL N_( "All items, unsorted" )
39 #undef PLAYLIST_PROFILE
42 /*****************************************************************************
44 *****************************************************************************/
45 static void RunThread ( playlist_t * );
46 static void RunPreparse( playlist_preparse_t * );
47 static playlist_item_t * NextItem ( playlist_t * );
48 static int PlayItem ( playlist_t *, playlist_item_t * );
50 int playlist_vaControl( playlist_t * p_playlist, int i_query, va_list args );
52 void playlist_PreparseEnqueueItemSub( playlist_t *, playlist_item_t * );
54 playlist_item_t *playlist_RecursiveFindLast(playlist_t *p_playlist,
55 playlist_item_t *p_node );
57 /*****************************************************************************
58 * Helper Function for NextItem
59 *****************************************************************************/
61 playlist_item_t *playlist_RecursiveFindLast(playlist_t *p_playlist,
62 playlist_item_t *p_node )
65 playlist_item_t *p_item;
66 for ( i = p_node->i_children - 1; i >= 0; i-- )
68 if( p_node->pp_children[i]->i_children == -1 )
69 return p_node->pp_children[i];
70 else if(p_node->pp_children[i]->i_children > 0)
72 p_item = playlist_RecursiveFindLast( p_playlist,
73 p_node->pp_children[i] );
87 * Create a playlist structure.
88 * \param p_parent the vlc object that is to be the parent of this playlist
89 * \return a pointer to the created playlist, or NULL on error
91 playlist_t * __playlist_Create ( vlc_object_t *p_parent )
93 playlist_t *p_playlist;
94 playlist_view_t *p_view;
97 /* Allocate structure */
98 p_playlist = vlc_object_create( p_parent, VLC_OBJECT_PLAYLIST );
101 msg_Err( p_parent, "out of memory" );
105 /* These variables control updates */
106 var_Create( p_playlist, "intf-change", VLC_VAR_BOOL );
107 val.b_bool = VLC_TRUE;
108 var_Set( p_playlist, "intf-change", val );
110 var_Create( p_playlist, "item-change", VLC_VAR_INTEGER );
112 var_Set( p_playlist, "item-change", val );
114 var_Create( p_playlist, "item-deleted", VLC_VAR_INTEGER );
116 var_Set( p_playlist, "item-deleted", val );
118 var_Create( p_playlist, "item-append", VLC_VAR_ADDRESS );
120 var_Create( p_playlist, "playlist-current", VLC_VAR_INTEGER );
122 var_Set( p_playlist, "playlist-current", val );
124 var_Create( p_playlist, "intf-popupmenu", VLC_VAR_BOOL );
126 var_Create( p_playlist, "intf-show", VLC_VAR_BOOL );
127 val.b_bool = VLC_TRUE;
128 var_Set( p_playlist, "intf-show", val );
131 /* Variables to control playback */
132 var_CreateGetBool( p_playlist, "play-and-stop" );
133 var_CreateGetBool( p_playlist, "random" );
134 var_CreateGetBool( p_playlist, "repeat" );
135 var_CreateGetBool( p_playlist, "loop" );
137 /* Initialise data structures */
138 vlc_mutex_init( p_playlist, &p_playlist->gc_lock );
139 p_playlist->i_last_id = 0;
140 p_playlist->b_go_next = VLC_TRUE;
141 p_playlist->p_input = NULL;
143 p_playlist->request_date = 0;
145 p_playlist->i_views = 0;
146 p_playlist->pp_views = NULL;
148 p_playlist->i_index = -1;
149 p_playlist->i_size = 0;
150 p_playlist->pp_items = NULL;
151 p_playlist->i_all_size = 0;
152 p_playlist->pp_all_items = 0;
154 playlist_ViewInsert( p_playlist, VIEW_CATEGORY, TITLE_CATEGORY );
155 playlist_ViewInsert( p_playlist, VIEW_ALL, TITLE_ALL );
157 p_view = playlist_ViewFind( p_playlist, VIEW_CATEGORY );
159 p_playlist->p_general =
160 playlist_NodeCreate( p_playlist, VIEW_CATEGORY,
161 _( "General" ), p_view->p_root );
162 p_playlist->p_general->i_flags |= PLAYLIST_RO_FLAG;
164 /* Set startup status
165 * We set to simple view on startup for interfaces that don't do
167 p_view = playlist_ViewFind( p_playlist, VIEW_CATEGORY );
168 p_playlist->status.i_view = VIEW_CATEGORY;
169 p_playlist->status.p_item = NULL;
170 p_playlist->status.p_node = p_view->p_root;
171 p_playlist->request.b_request = VLC_FALSE;
172 p_playlist->status.i_status = PLAYLIST_STOPPED;
175 p_playlist->i_sort = SORT_ID;
176 p_playlist->i_order = ORDER_NORMAL;
178 /* Finally, launch the thread ! */
179 if( vlc_thread_create( p_playlist, "playlist", RunThread,
180 VLC_THREAD_PRIORITY_LOW, VLC_TRUE ) )
182 msg_Err( p_playlist, "cannot spawn playlist thread" );
183 vlc_object_destroy( p_playlist );
187 /* Preparsing stuff */
188 p_playlist->p_preparse = vlc_object_create( p_playlist,
189 sizeof( playlist_preparse_t ) );
190 if( !p_playlist->p_preparse )
192 msg_Err( p_playlist, "unable to create preparser" );
193 vlc_object_destroy( p_playlist );
197 p_playlist->p_preparse->i_waiting = 0;
198 p_playlist->p_preparse->pp_waiting = NULL;
200 vlc_object_attach( p_playlist->p_preparse, p_playlist );
201 if( vlc_thread_create( p_playlist->p_preparse, "preparser",
202 RunPreparse, VLC_THREAD_PRIORITY_LOW, VLC_TRUE ) )
204 msg_Err( p_playlist, "cannot spawn preparse thread" );
205 vlc_object_detach( p_playlist->p_preparse );
206 vlc_object_destroy( p_playlist->p_preparse );
210 /* The object has been initialized, now attach it */
211 vlc_object_attach( p_playlist, p_parent );
217 * Destroy the playlist.
219 * Delete all items in the playlist and free the playlist structure.
220 * \param p_playlist the playlist structure to destroy
221 * \return VLC_SUCCESS or an error
223 int playlist_Destroy( playlist_t * p_playlist )
226 p_playlist->b_die = 1;
228 while( p_playlist->i_sds )
230 playlist_ServicesDiscoveryRemove( p_playlist,
231 p_playlist->pp_sds[0]->psz_module );
234 vlc_thread_join( p_playlist->p_preparse );
235 vlc_thread_join( p_playlist );
237 vlc_object_detach( p_playlist->p_preparse );
239 var_Destroy( p_playlist, "intf-change" );
240 var_Destroy( p_playlist, "item-change" );
241 var_Destroy( p_playlist, "playlist-current" );
242 var_Destroy( p_playlist, "intf-popmenu" );
243 var_Destroy( p_playlist, "intf-show" );
244 var_Destroy( p_playlist, "play-and-stop" );
245 var_Destroy( p_playlist, "random" );
246 var_Destroy( p_playlist, "repeat" );
247 var_Destroy( p_playlist, "loop" );
249 playlist_Clear( p_playlist );
251 for( i = p_playlist->i_views - 1; i >= 0 ; i-- )
253 playlist_view_t *p_view = p_playlist->pp_views[i];
254 if( p_view->psz_name )
255 free( p_view->psz_name );
256 playlist_ItemDelete( p_view->p_root );
257 REMOVE_ELEM( p_playlist->pp_views, p_playlist->i_views, i );
261 vlc_mutex_destroy( &p_playlist->gc_lock );
262 vlc_object_destroy( p_playlist->p_preparse );
263 vlc_object_destroy( p_playlist );
270 * Do a playlist action.
272 * If there is something in the playlist then you can do playlist actions.
274 * Playlist lock must not be taken when calling this function
276 * \param p_playlist the playlist to do the command on
277 * \param i_query the command to do
278 * \param variable number of arguments
279 * \return VLC_SUCCESS or an error
281 int playlist_LockControl( playlist_t * p_playlist, int i_query, ... )
285 va_start( args, i_query );
286 vlc_mutex_lock( &p_playlist->object_lock );
287 i_result = playlist_vaControl( p_playlist, i_query, args );
289 vlc_mutex_unlock( &p_playlist->object_lock );
294 * Do a playlist action.
296 * If there is something in the playlist then you can do playlist actions.
298 * Playlist lock must be taken when calling this function
300 * \param p_playlist the playlist to do the command on
301 * \param i_query the command to do
302 * \param variable number of arguments
303 * \return VLC_SUCCESS or an error
305 int playlist_Control( playlist_t * p_playlist, int i_query, ... )
309 va_start( args, i_query );
310 i_result = playlist_vaControl( p_playlist, i_query, args );
316 int playlist_vaControl( playlist_t * p_playlist, int i_query, va_list args )
318 playlist_view_t *p_view;
319 playlist_item_t *p_item, *p_node;
323 #ifdef PLAYLIST_PROFILE
324 p_playlist->request_date = mdate();
327 if( p_playlist->i_size <= 0 )
335 p_playlist->status.i_status = PLAYLIST_STOPPED;
336 p_playlist->request.b_request = VLC_TRUE;
337 p_playlist->request.p_item = NULL;
340 case PLAYLIST_ITEMPLAY:
341 p_item = (playlist_item_t *)va_arg( args, playlist_item_t * );
342 if ( p_item == NULL || p_item->input.psz_uri == NULL )
344 p_playlist->status.i_status = PLAYLIST_RUNNING;
345 p_playlist->request.i_skip = 0;
346 p_playlist->request.b_request = VLC_TRUE;
347 p_playlist->request.p_item = p_item;
348 p_playlist->request.i_view = p_playlist->status.i_view;
349 p_view = playlist_ViewFind( p_playlist, p_playlist->status.i_view );
352 p_playlist->request.p_node = p_view->p_root;
356 p_playlist->request.p_node = NULL;
360 case PLAYLIST_VIEWPLAY:
361 i_view = (int)va_arg( args,int );
362 p_node = (playlist_item_t *)va_arg( args, playlist_item_t * );
363 p_item = (playlist_item_t *)va_arg( args, playlist_item_t * );
364 if ( p_node == NULL ) //|| (p_item != NULL && p_item->input.psz_uri
367 p_playlist->status.i_status = PLAYLIST_STOPPED;
368 p_playlist->request.b_request = VLC_TRUE;
371 p_playlist->status.i_status = PLAYLIST_RUNNING;
372 p_playlist->request.i_skip = 0;
373 p_playlist->request.b_request = VLC_TRUE;
374 p_playlist->request.i_view = i_view;
375 p_playlist->request.p_node = p_node;
376 p_playlist->request.p_item = p_item;
378 /* Don't go further if the node doesn't want to */
379 if( ! p_playlist->request.p_node->i_flags & PLAYLIST_SKIP_FLAG )
381 p_playlist->b_go_next = VLC_FALSE;
385 p_playlist->b_go_next = VLC_TRUE;
390 p_playlist->status.i_status = PLAYLIST_RUNNING;
392 if( p_playlist->p_input )
394 val.i_int = PLAYING_S;
395 var_Set( p_playlist->p_input, "state", val );
399 /* FIXME : needed ? */
400 p_playlist->request.b_request = VLC_TRUE;
401 p_playlist->request.i_view = p_playlist->status.i_view;
402 p_playlist->request.p_node = p_playlist->status.p_node;
403 p_playlist->request.p_item = p_playlist->status.p_item;
404 p_playlist->request.i_skip = 0;
405 p_playlist->request.i_goto = -1;
408 case PLAYLIST_AUTOPLAY:
409 p_playlist->status.i_status = PLAYLIST_RUNNING;
410 p_playlist->status.p_node = p_playlist->p_general;
412 p_playlist->request.b_request = VLC_FALSE;
417 if( p_playlist->p_input )
418 var_Get( p_playlist->p_input, "state", &val );
420 if( val.i_int == PAUSE_S )
422 p_playlist->status.i_status = PLAYLIST_RUNNING;
423 if( p_playlist->p_input )
425 val.i_int = PLAYING_S;
426 var_Set( p_playlist->p_input, "state", val );
431 p_playlist->status.i_status = PLAYLIST_PAUSED;
432 if( p_playlist->p_input )
435 var_Set( p_playlist->p_input, "state", val );
441 p_playlist->request.i_view = p_playlist->status.i_view;
442 if( p_playlist->status.i_view > -1 )
444 p_playlist->request.p_node = p_playlist->status.p_node;
445 p_playlist->request.p_item = p_playlist->status.p_item;
449 p_playlist->request.p_node = NULL;
450 p_playlist->request.p_item = NULL;
452 p_playlist->request.i_skip = (int) va_arg( args, int );
453 p_playlist->request.b_request = VLC_TRUE;
457 p_playlist->status.i_status = PLAYLIST_RUNNING;
458 p_playlist->request.p_node = NULL;
459 p_playlist->request.p_item = NULL;
460 p_playlist->request.i_view = -1;
461 p_playlist->request.i_goto = (int) va_arg( args, int );
462 p_playlist->request.b_request = VLC_TRUE;
466 msg_Err( p_playlist, "unknown playlist query" );
474 int playlist_PreparseEnqueue( playlist_t *p_playlist,
475 input_item_t *p_item )
477 vlc_mutex_lock( &p_playlist->p_preparse->object_lock );
478 INSERT_ELEM( p_playlist->p_preparse->pp_waiting,
479 p_playlist->p_preparse->i_waiting,
480 p_playlist->p_preparse->i_waiting,
482 vlc_mutex_unlock( &p_playlist->p_preparse->object_lock );
486 /* Should only be called if playlist and preparser are locked */
487 void playlist_PreparseEnqueueItemSub( playlist_t *p_playlist,
488 playlist_item_t *p_item )
491 if( p_item->i_children == -1 )
493 INSERT_ELEM( p_playlist->p_preparse->pp_waiting,
494 p_playlist->p_preparse->i_waiting,
495 p_playlist->p_preparse->i_waiting,
500 for( i = 0; i < p_item->i_children; i++)
502 playlist_PreparseEnqueueItemSub( p_playlist,
503 p_item->pp_children[i] );
508 int playlist_PreparseEnqueueItem( playlist_t *p_playlist,
509 playlist_item_t *p_item )
511 vlc_mutex_lock( &p_playlist->object_lock );
512 vlc_mutex_lock( &p_playlist->p_preparse->object_lock );
513 playlist_PreparseEnqueueItemSub( p_playlist, p_item );
514 vlc_mutex_unlock( &p_playlist->p_preparse->object_lock );
515 vlc_mutex_unlock( &p_playlist->object_lock );
520 /* Destroy remaining objects */
521 static mtime_t ObjectGarbageCollector( playlist_t *p_playlist, int i_type,
522 mtime_t destroy_date )
526 if( destroy_date > mdate() ) return destroy_date;
528 if( destroy_date == 0 )
530 /* give a little time */
531 return mdate() + I64C(1000000);
535 vlc_mutex_lock( &p_playlist->gc_lock );
536 while( ( p_obj = vlc_object_find( p_playlist, i_type, FIND_CHILD ) ) )
538 if( p_obj->p_parent != (vlc_object_t*)p_playlist )
540 /* only first child (ie unused) */
541 vlc_object_release( p_obj );
544 if( i_type == VLC_OBJECT_VOUT )
546 msg_Dbg( p_playlist, "garbage collector destroying 1 vout" );
547 vlc_object_detach( p_obj );
548 vlc_object_release( p_obj );
549 vout_Destroy( (vout_thread_t *)p_obj );
551 else if( i_type == VLC_OBJECT_SOUT )
553 vlc_object_release( p_obj );
554 sout_DeleteInstance( (sout_instance_t*)p_obj );
557 vlc_mutex_unlock( &p_playlist->gc_lock );
562 /*****************************************************************************
563 * RunThread: main playlist thread
564 *****************************************************************************/
565 static void RunThread ( playlist_t *p_playlist )
568 playlist_item_t *p_item = NULL;
570 mtime_t i_vout_destroyed_date = 0;
571 mtime_t i_sout_destroyed_date = 0;
573 playlist_item_t *p_autodelete_item = NULL;
575 /* Tell above that we're ready */
576 vlc_thread_ready( p_playlist );
578 while( !p_playlist->b_die )
580 vlc_mutex_lock( &p_playlist->object_lock );
582 /* First, check if we have something to do */
583 /* FIXME : this can be called several times */
584 if( p_playlist->request.b_request )
586 #ifdef PLAYLIST_PROFILE
587 msg_Dbg(p_playlist, "beginning processing of request, "
588 I64Fi" us ", mdate() - p_playlist->request_date );
590 /* Stop the existing input */
591 if( p_playlist->p_input )
593 input_StopThread( p_playlist->p_input );
595 /* The code below will start the next input for us */
596 if( p_playlist->status.i_status == PLAYLIST_STOPPED )
598 p_playlist->request.b_request = VLC_FALSE;
602 /* If there is an input, check that it doesn't need to die. */
603 if( p_playlist->p_input )
605 /* This input is dead. Remove it ! */
606 if( p_playlist->p_input->b_dead )
608 input_thread_t *p_input;
610 p_input = p_playlist->p_input;
611 p_playlist->p_input = NULL;
613 /* Release the playlist lock, because we may get stuck
614 * in input_DestroyThread() for some time. */
615 vlc_mutex_unlock( &p_playlist->object_lock );
618 input_DestroyThread( p_input );
620 /* Unlink current input
621 * (_after_ input_DestroyThread for vout garbage collector) */
622 vlc_object_detach( p_input );
625 vlc_object_destroy( p_input );
627 i_vout_destroyed_date = 0;
628 i_sout_destroyed_date = 0;
630 if( p_playlist->status.p_item->i_flags
631 & PLAYLIST_REMOVE_FLAG )
633 playlist_ItemDelete( p_item );
634 p_playlist->status.p_item = NULL;
639 /* This input is dying, let it do */
640 else if( p_playlist->p_input->b_die )
644 /* This input has finished, ask it to die ! */
645 else if( p_playlist->p_input->b_error
646 || p_playlist->p_input->b_eof )
648 /* TODO FIXME XXX TODO FIXME XXX */
649 /* Check for autodeletion */
651 if( p_playlist->status.p_item->i_flags & PLAYLIST_DEL_FLAG )
653 p_autodelete_item = p_playlist->status.p_item;
655 input_StopThread( p_playlist->p_input );
656 /* Select the next playlist item */
657 vlc_mutex_unlock( &p_playlist->object_lock );
660 else if( p_playlist->p_input->i_state != INIT_S )
662 vlc_mutex_unlock( &p_playlist->object_lock );
663 i_vout_destroyed_date =
664 ObjectGarbageCollector( p_playlist, VLC_OBJECT_VOUT,
665 i_vout_destroyed_date );
666 i_sout_destroyed_date =
667 ObjectGarbageCollector( p_playlist, VLC_OBJECT_SOUT,
668 i_sout_destroyed_date );
669 vlc_mutex_lock( &p_playlist->object_lock );
672 else if( p_playlist->status.i_status != PLAYLIST_STOPPED )
674 /* Start another input.
675 * Get the next item to play */
676 p_item = NextItem( p_playlist );
681 if( p_autodelete_item )
683 playlist_Delete( p_playlist,
684 p_autodelete_item->input.i_id );
685 p_autodelete_item = NULL;
687 p_playlist->status.i_status = PLAYLIST_STOPPED;
688 vlc_mutex_unlock( &p_playlist->object_lock );
692 PlayItem( p_playlist, p_item );
694 if( p_autodelete_item )
696 playlist_Delete( p_playlist, p_autodelete_item->input.i_id );
697 p_autodelete_item = NULL;
700 else if( p_playlist->status.i_status == PLAYLIST_STOPPED )
702 if( p_item && p_playlist->status.p_item &&
703 p_playlist->status.p_item->i_flags & PLAYLIST_REMOVE_FLAG )
705 playlist_ItemDelete( p_item );
706 p_playlist->status.p_item = NULL;
709 /* Collect garbage */
710 vlc_mutex_unlock( &p_playlist->object_lock );
711 i_sout_destroyed_date =
712 ObjectGarbageCollector( p_playlist, VLC_OBJECT_SOUT, mdate() );
713 i_vout_destroyed_date =
714 ObjectGarbageCollector( p_playlist, VLC_OBJECT_VOUT, mdate() );
715 vlc_mutex_lock( &p_playlist->object_lock );
717 vlc_mutex_unlock( &p_playlist->object_lock );
719 msleep( INTF_IDLE_SLEEP / 2 );
721 /* Stop sleeping earlier if we have work */
722 /* TODO : statistics about this */
723 if ( p_playlist->request.b_request &&
724 p_playlist->status.i_status == PLAYLIST_RUNNING )
729 msleep( INTF_IDLE_SLEEP / 2 );
734 /* If there is an input, kill it */
737 vlc_mutex_lock( &p_playlist->object_lock );
739 if( p_playlist->p_input == NULL )
741 vlc_mutex_unlock( &p_playlist->object_lock );
745 if( p_playlist->p_input->b_dead )
747 input_thread_t *p_input;
749 /* Unlink current input */
750 p_input = p_playlist->p_input;
751 p_playlist->p_input = NULL;
752 vlc_mutex_unlock( &p_playlist->object_lock );
755 input_DestroyThread( p_input );
756 /* Unlink current input (_after_ input_DestroyThread for vout
757 * garbage collector)*/
758 vlc_object_detach( p_input );
761 vlc_object_destroy( p_input );
764 else if( p_playlist->p_input->b_die )
766 /* This input is dying, leave it alone */
769 else if( p_playlist->p_input->b_error || p_playlist->p_input->b_eof )
771 input_StopThread( p_playlist->p_input );
772 vlc_mutex_unlock( &p_playlist->object_lock );
777 p_playlist->p_input->b_eof = 1;
780 vlc_mutex_unlock( &p_playlist->object_lock );
782 msleep( INTF_IDLE_SLEEP );
785 /* close all remaining sout */
786 while( ( p_obj = vlc_object_find( p_playlist,
787 VLC_OBJECT_SOUT, FIND_CHILD ) ) )
789 vlc_object_release( p_obj );
790 sout_DeleteInstance( (sout_instance_t*)p_obj );
793 /* close all remaining vout */
794 while( ( p_obj = vlc_object_find( p_playlist,
795 VLC_OBJECT_VOUT, FIND_CHILD ) ) )
797 vlc_object_detach( p_obj );
798 vlc_object_release( p_obj );
799 vout_Destroy( (vout_thread_t *)p_obj );
803 /* Queue for items to preparse */
804 static void RunPreparse ( playlist_preparse_t *p_obj )
806 playlist_t *p_playlist = (playlist_t *)p_obj->p_parent;
809 /* Tell above that we're ready */
810 vlc_thread_ready( p_obj );
812 while( !p_playlist->b_die )
814 vlc_mutex_lock( &p_obj->object_lock );
816 if( p_obj->i_waiting > 0 )
818 input_item_t *p_current = p_obj->pp_waiting[0];
819 REMOVE_ELEM( p_obj->pp_waiting, p_obj->i_waiting, 0 );
820 vlc_mutex_unlock( &p_obj->object_lock );
821 input_Preparse( p_playlist, p_current );
822 var_SetInteger( p_playlist, "item-change", p_current->i_id );
823 vlc_mutex_lock( &p_obj->object_lock );
825 b_sleep = ( p_obj->i_waiting == 0 );
827 vlc_mutex_unlock( &p_obj->object_lock );
829 if( p_obj->i_waiting == 0 )
831 msleep( INTF_IDLE_SLEEP );
836 /*****************************************************************************
838 *****************************************************************************
839 * This function calculates the next playlist item, depending
840 * on the playlist course mode (forward, backward, random, view,...).
841 *****************************************************************************/
842 static playlist_item_t * NextItem( playlist_t *p_playlist )
844 playlist_item_t *p_new = NULL;
845 int i_skip,i_goto,i, i_new, i_count ;
846 playlist_view_t *p_view;
848 vlc_bool_t b_loop = var_GetBool( p_playlist, "loop" );
849 vlc_bool_t b_random = var_GetBool( p_playlist, "random" );
850 vlc_bool_t b_repeat = var_GetBool( p_playlist, "repeat" );
851 vlc_bool_t b_playstop = var_GetBool( p_playlist, "play-and-stop" );
853 #ifdef PLAYLIST_PROFILE
854 /* Calculate time needed */
855 int64_t start = mdate();
858 /* Handle quickly a few special cases */
860 /* No items to play */
861 if( p_playlist->i_size == 0 )
863 msg_Info( p_playlist, "playlist is empty" );
866 /* Nothing requested */
867 if( !p_playlist->request.b_request && p_playlist->status.p_item == NULL )
869 msg_Dbg( p_playlist,"nothing requested, starting" );
872 /* Repeat and play/stop */
873 if( !p_playlist->request.b_request && b_repeat == VLC_TRUE )
875 msg_Dbg( p_playlist,"repeating item" );
876 return p_playlist->status.p_item;
879 if( !p_playlist->request.b_request && b_playstop == VLC_TRUE )
881 msg_Dbg( p_playlist,"stopping (play and stop)");
885 if( !p_playlist->request.b_request && p_playlist->status.p_item &&
886 !( p_playlist->status.p_item->i_flags & PLAYLIST_SKIP_FLAG ) )
888 msg_Dbg( p_playlist, "no-skip mode, stopping") ;
892 /* TODO: improve this (only use current node) */
893 /* TODO: use the "shuffled view" internally ? */
894 /* Random case. This is an exception: if request, but request is skip +- 1
895 * we don't go to next item but select a new random one. */
897 ( !p_playlist->request.b_request ||
898 ( p_playlist->request.b_request && ( p_playlist->request.p_item == NULL ||
899 p_playlist->request.i_skip == 1 || p_playlist->request.i_skip == -1 ) ) ) )
901 /* how many items to choose from ? */
903 for ( i = 0; i < p_playlist->i_size; i++ )
905 if ( p_playlist->pp_items[i]->i_nb_played == 0 )
911 /* Don't loop? Exit! */
914 /* Otherwise reset the counter */
915 for ( i = 0; i < p_playlist->i_size; i++ )
917 p_playlist->pp_items[i]->i_nb_played = 0;
919 i_count = p_playlist->i_size;
921 srand( (unsigned int)mdate() );
922 i = rand() % i_count + 1 ;
923 /* loop thru the list and count down the unplayed items to the selected one */
924 for ( i_new = 0; i_new < p_playlist->i_size && i > 0; i_new++ )
926 if ( p_playlist->pp_items[i_new]->i_nb_played == 0 )
931 p_playlist->request.i_skip = 0;
932 p_playlist->request.b_request = VLC_FALSE;
933 return p_playlist->pp_items[i_new];
936 /* Start the real work */
937 if( p_playlist->request.b_request )
939 #ifdef PLAYLIST_DEBUG
940 msg_Dbg( p_playlist,"processing request" );
942 /* We are not playing from a view */
943 if( p_playlist->request.i_view == -1 )
945 #ifdef PLAYLIST_DEBUG
946 msg_Dbg( p_playlist, "non-view mode request");
948 /* Directly select the item, just like now */
949 p_new = p_playlist->request.p_item;
950 i_skip = p_playlist->request.i_skip;
951 i_goto = p_playlist->request.i_goto;
953 if( p_playlist->i_index < 0 ) p_playlist->i_index = 0;
955 p_new = p_playlist->pp_items[p_playlist->i_index];
957 if( i_goto >= 0 && i_goto < p_playlist->i_size )
959 p_playlist->i_index = i_goto;
960 p_new = p_playlist->pp_items[p_playlist->i_index];
961 p_playlist->request.i_goto = -1;
966 if( p_playlist->i_index + i_skip < p_playlist->i_size &&
967 p_playlist->i_index + i_skip >= 0 )
969 p_playlist->i_index += i_skip;
970 p_new = p_playlist->pp_items[p_playlist->i_index];
972 p_playlist->request.i_skip = 0;
977 #ifdef PLAYLIST_DEBUG
978 msg_Dbg( p_playlist, "view mode request" );
980 p_new = p_playlist->request.p_item;
981 i_skip = p_playlist->request.i_skip;
983 /* If we are asked for a node, take its first item */
984 if( p_playlist->request.p_item == NULL && i_skip == 0 )
989 p_view = playlist_ViewFind( p_playlist,p_playlist->request.i_view );
990 p_playlist->status.p_node = p_playlist->request.p_node;
991 p_playlist->status.i_view = p_playlist->request.i_view;
994 msg_Err( p_playlist, "p_view is NULL and should not! (requested view is %i", p_playlist->request.i_view );
996 else if( i_skip > 0 )
998 for( i = i_skip; i > 0 ; i-- )
1000 p_new = playlist_FindNextFromParent( p_playlist,
1001 p_playlist->request.i_view,
1003 p_playlist->request.p_node,
1007 #ifdef PLAYLIST_DEBUG
1008 msg_Dbg( p_playlist, "looping" );
1010 p_new = playlist_FindNextFromParent( p_playlist,
1011 p_playlist->request.i_view,
1015 if( p_new == NULL ) break;
1019 else if( i_skip < 0 )
1021 for( i = i_skip; i < 0 ; i++ )
1023 p_new = playlist_FindPrevFromParent( p_playlist,
1024 p_playlist->request.i_view,
1026 p_playlist->request.p_node,
1030 /* We reach the beginning of the playlist.
1031 Go back to the last item. */
1032 p_new = playlist_RecursiveFindLast( p_playlist,
1035 if( p_new == NULL ) break;
1040 /* Clear the request */
1041 p_playlist->request.b_request = VLC_FALSE;
1043 /* "Automatic" item change ( next ) */
1046 p_playlist->request_date = 0;
1048 if( p_playlist->status.i_view == -1 )
1050 #ifdef PLAYLIST_DEBUG
1051 msg_Dbg( p_playlist, "no request - old mode" );
1053 if( p_playlist->i_index + 1 < p_playlist->i_size )
1055 p_playlist->i_index++;
1056 p_new = p_playlist->pp_items[p_playlist->i_index];
1057 if( !( p_new->i_flags & PLAYLIST_SKIP_FLAG ) )
1064 if( b_loop && p_playlist->i_size > 0)
1066 p_playlist->i_index = 0;
1067 p_new = p_playlist->pp_items[0];
1073 /* We are playing with a view */
1076 #ifdef PLAYLIST_DEBUG
1077 msg_Dbg( p_playlist,"no request - from a view" );
1079 playlist_view_t *p_view =
1080 playlist_ViewFind( p_playlist,
1081 p_playlist->status.i_view );
1084 msg_Err( p_playlist, "p_view is NULL and should not! (FIXME)" );
1088 p_new = playlist_FindNextFromParent( p_playlist,
1089 p_playlist->status.i_view,
1091 p_playlist->status.p_node,
1092 p_playlist->status.p_item );
1093 if( p_new == NULL && b_loop )
1095 #ifdef PLAYLIST_DEBUG
1096 msg_Dbg( p_playlist, "looping" );
1098 p_new = playlist_FindNextFromParent( p_playlist,
1099 p_playlist->status.i_view,
1104 if( p_new != NULL && !(p_new->i_flags & PLAYLIST_SKIP_FLAG) )
1111 if( p_playlist->i_index >= 0 && p_new != NULL &&
1112 p_playlist->pp_items[p_playlist->i_index] != p_new )
1114 p_playlist->i_index = playlist_GetPositionById( p_playlist,
1115 p_new->input.i_id );
1118 #ifdef PLAYLIST_PROFILE
1119 msg_Dbg(p_playlist,"next item found in "I64Fi " us", mdate()-start );
1124 msg_Info( p_playlist, "nothing to play" );
1129 /*****************************************************************************
1130 * PlayItem: start the input thread for an item
1131 ****************************************************************************/
1132 static int PlayItem( playlist_t *p_playlist, playlist_item_t *p_item )
1136 msg_Dbg( p_playlist, "creating new input thread" );
1138 p_item->i_nb_played++;
1139 p_playlist->status.p_item = p_item;
1141 p_playlist->i_index = playlist_GetPositionById( p_playlist,
1142 p_item->input.i_id );
1144 #ifdef PLAYLIST_PROFILE
1145 if( p_playlist->request_date != 0 )
1147 msg_Dbg( p_playlist, "request processed after "I64Fi " us",
1148 mdate() - p_playlist->request_date );
1152 p_playlist->p_input = input_CreateThread( p_playlist, &p_item->input );
1154 val.i_int = p_item->input.i_id;
1155 /* unlock the playlist to set the var...mmm */
1156 vlc_mutex_unlock( &p_playlist->object_lock);
1157 var_Set( p_playlist, "playlist-current", val);
1158 vlc_mutex_lock( &p_playlist->object_lock);