1 /*****************************************************************************
2 * playlist.c : Playlist management functions
3 *****************************************************************************
4 * Copyright (C) 1999-2004 VideoLAN
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 #define PLAYLIST_PROFILE 1
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 static int ItemChange( vlc_object_t *, const char *,
51 vlc_value_t, vlc_value_t, void * );
53 int playlist_vaControl( playlist_t * p_playlist, int i_query, va_list args );
59 * Create a playlist structure.
60 * \param p_parent the vlc object that is to be the parent of this playlist
61 * \return a pointer to the created playlist, or NULL on error
63 playlist_t * __playlist_Create ( vlc_object_t *p_parent )
65 playlist_t *p_playlist;
66 playlist_view_t *p_view;
69 /* Allocate structure */
70 p_playlist = vlc_object_create( p_parent, VLC_OBJECT_PLAYLIST );
73 msg_Err( p_parent, "out of memory" );
77 /* These variables control updates */
78 var_Create( p_playlist, "intf-change", VLC_VAR_BOOL );
79 val.b_bool = VLC_TRUE;
80 var_Set( p_playlist, "intf-change", val );
82 var_Create( p_playlist, "item-change", VLC_VAR_INTEGER );
84 var_Set( p_playlist, "item-change", val );
86 var_Create( p_playlist, "item-deleted", VLC_VAR_INTEGER );
88 var_Set( p_playlist, "item-deleted", val );
90 var_Create( p_playlist, "item-append", VLC_VAR_ADDRESS );
92 var_Create( p_playlist, "playlist-current", VLC_VAR_INTEGER );
94 var_Set( p_playlist, "playlist-current", val );
96 var_Create( p_playlist, "intf-popupmenu", VLC_VAR_BOOL );
98 var_Create( p_playlist, "intf-show", VLC_VAR_BOOL );
99 val.b_bool = VLC_TRUE;
100 var_Set( p_playlist, "intf-show", val );
103 /* Variables to control playback */
104 var_CreateGetBool( p_playlist, "play-and-stop" );
105 var_CreateGetBool( p_playlist, "random" );
106 var_CreateGetBool( p_playlist, "repeat" );
107 var_CreateGetBool( p_playlist, "loop" );
109 /* Initialise data structures */
110 p_playlist->b_go_next = VLC_TRUE;
111 p_playlist->p_input = NULL;
113 p_playlist->request_date = 0;
115 p_playlist->i_views = 0;
116 p_playlist->pp_views = NULL;
118 p_playlist->i_index = -1;
119 p_playlist->i_size = 0;
120 p_playlist->pp_items = NULL;
122 playlist_ViewInsert( p_playlist, VIEW_CATEGORY, TITLE_CATEGORY );
123 playlist_ViewInsert( p_playlist, VIEW_SIMPLE, TITLE_SIMPLE );
124 playlist_ViewInsert( p_playlist, VIEW_ALL, TITLE_ALL );
126 p_view = playlist_ViewFind( p_playlist, VIEW_CATEGORY );
128 p_playlist->p_general = playlist_NodeCreate( p_playlist, VIEW_CATEGORY,
129 _( "General" ), p_view->p_root );
130 p_playlist->p_general->i_flags |= PLAYLIST_RO_FLAG;
132 /* Set startup status
133 * We set to simple view on startup for interfaces that don't do
135 p_view = playlist_ViewFind( p_playlist, VIEW_SIMPLE );
136 p_playlist->status.i_view = VIEW_SIMPLE;
137 p_playlist->status.p_item = NULL;
138 p_playlist->status.p_node = p_view->p_root;
139 p_playlist->request.b_request = VLC_FALSE;
140 p_playlist->status.i_status = PLAYLIST_STOPPED;
143 p_playlist->i_last_id = 0;
144 p_playlist->i_sort = SORT_ID;
145 p_playlist->i_order = ORDER_NORMAL;
147 /* Finally, launch the thread ! */
148 if( vlc_thread_create( p_playlist, "playlist", RunThread,
149 VLC_THREAD_PRIORITY_LOW, VLC_TRUE ) )
151 msg_Err( p_playlist, "cannot spawn playlist thread" );
152 vlc_object_destroy( p_playlist );
156 /* Preparsing stuff */
157 p_playlist->p_preparse = vlc_object_create( p_playlist,
158 sizeof( playlist_preparse_t ) );
159 if( !p_playlist->p_preparse )
161 msg_Err( p_playlist, "unable to create preparser" );
162 vlc_object_destroy( p_playlist );
166 p_playlist->p_preparse->i_waiting = 0;
167 p_playlist->p_preparse->pp_waiting = NULL;
168 vlc_mutex_init( p_playlist->p_preparse,
169 &p_playlist->p_preparse->object_lock );
171 vlc_object_attach( p_playlist->p_preparse, p_playlist );
172 if( vlc_thread_create( p_playlist->p_preparse, "preparser",
173 RunPreparse, VLC_THREAD_PRIORITY_LOW, VLC_TRUE ) )
175 msg_Err( p_playlist, "cannot spawn preparse thread" );
176 vlc_object_detach( p_playlist->p_preparse );
177 vlc_object_destroy( p_playlist->p_preparse );
181 /* The object has been initialized, now attach it */
182 vlc_object_attach( p_playlist, p_parent );
188 * Destroy the playlist.
190 * Delete all items in the playlist and free the playlist structure.
191 * \param p_playlist the playlist structure to destroy
192 * \return VLC_SUCCESS or an error
194 int playlist_Destroy( playlist_t * p_playlist )
197 p_playlist->b_die = 1;
199 for( i = 0 ; i< p_playlist->i_sds ; i++ )
201 playlist_ServicesDiscoveryRemove( p_playlist,
202 p_playlist->pp_sds[i]->psz_module );
205 vlc_thread_join( p_playlist->p_preparse );
206 vlc_thread_join( p_playlist );
208 vlc_object_detach( p_playlist->p_preparse );
210 vlc_mutex_destroy( &p_playlist->p_preparse->object_lock );
212 var_Destroy( p_playlist, "intf-change" );
213 var_Destroy( p_playlist, "item-change" );
214 var_Destroy( p_playlist, "playlist-current" );
215 var_Destroy( p_playlist, "intf-popmenu" );
216 var_Destroy( p_playlist, "intf-show" );
217 var_Destroy( p_playlist, "play-and-stop" );
218 var_Destroy( p_playlist, "random" );
219 var_Destroy( p_playlist, "repeat" );
220 var_Destroy( p_playlist, "loop" );
222 playlist_Clear( p_playlist );
224 for( i = p_playlist->i_views - 1; i >= 0 ; i-- )
226 playlist_view_t *p_view = p_playlist->pp_views[i];
227 if( p_view->psz_name )
228 free( p_view->psz_name );
229 playlist_ItemDelete( p_view->p_root );
230 REMOVE_ELEM( p_playlist->pp_views, p_playlist->i_views, i );
234 vlc_object_destroy( p_playlist->p_preparse );
235 vlc_object_destroy( p_playlist );
242 * Do a playlist action.
244 * If there is something in the playlist then you can do playlist actions.
246 * Playlist lock must not be taken when calling this function
248 * \param p_playlist the playlist to do the command on
249 * \param i_query the command to do
250 * \param variable number of arguments
251 * \return VLC_SUCCESS or an error
253 int playlist_Control( playlist_t * p_playlist, int i_query, ... )
257 va_start( args, i_query );
258 i_result = playlist_vaControl( p_playlist, i_query, args );
264 int playlist_vaControl( playlist_t * p_playlist, int i_query, va_list args )
266 playlist_view_t *p_view;
269 vlc_mutex_lock( &p_playlist->object_lock );
271 #ifdef PLAYLIST_PROFILE
272 p_playlist->request_date = mdate();
275 if( p_playlist->i_size <= 0 )
277 vlc_mutex_unlock( &p_playlist->object_lock );
284 p_playlist->status.i_status = PLAYLIST_STOPPED;
285 p_playlist->request.b_request = VLC_TRUE;
288 case PLAYLIST_ITEMPLAY:
289 p_playlist->status.i_status = PLAYLIST_RUNNING;
290 p_playlist->request.i_skip = 0;
291 p_playlist->request.b_request = VLC_TRUE;
292 p_playlist->request.p_item = (playlist_item_t *)va_arg( args,
294 p_playlist->request.i_view = p_playlist->status.i_view;
295 p_view = playlist_ViewFind( p_playlist, p_playlist->status.i_view );
298 p_playlist->request.p_node = p_view->p_root;
302 p_playlist->request.p_node = NULL;
306 case PLAYLIST_VIEWPLAY:
307 p_playlist->status.i_status = PLAYLIST_RUNNING;
308 p_playlist->request.i_skip = 0;
309 p_playlist->request.b_request = VLC_TRUE;
310 p_playlist->request.i_view = (int)va_arg( args,int );
311 p_playlist->request.p_node = (playlist_item_t *)va_arg( args,
313 p_playlist->request.p_item = (playlist_item_t *)va_arg( args,
316 /* If we select a node, play only it.
317 * If we select an item, continue */
318 if( p_playlist->request.p_item == NULL ||
319 ! p_playlist->request.p_node->i_flags & PLAYLIST_SKIP_FLAG )
321 p_playlist->b_go_next = VLC_FALSE;
325 p_playlist->b_go_next = VLC_TRUE;
330 p_playlist->status.i_status = PLAYLIST_RUNNING;
332 if( p_playlist->p_input )
334 val.i_int = PLAYING_S;
335 var_Set( p_playlist->p_input, "state", val );
339 /* FIXME : needed ? */
340 p_playlist->request.b_request = VLC_TRUE;
341 p_playlist->request.i_view = p_playlist->status.i_view;
342 p_playlist->request.p_node = p_playlist->status.p_node;
343 p_playlist->request.p_item = p_playlist->status.p_item;
344 p_playlist->request.i_skip = 0;
345 p_playlist->request.i_goto = -1;
348 case PLAYLIST_AUTOPLAY:
349 p_playlist->status.i_status = PLAYLIST_RUNNING;
351 p_playlist->request.b_request = VLC_FALSE;
356 if( p_playlist->p_input )
357 var_Get( p_playlist->p_input, "state", &val );
359 if( val.i_int == PAUSE_S )
361 p_playlist->status.i_status = PLAYLIST_RUNNING;
362 if( p_playlist->p_input )
364 val.i_int = PLAYING_S;
365 var_Set( p_playlist->p_input, "state", val );
370 p_playlist->status.i_status = PLAYLIST_PAUSED;
371 if( p_playlist->p_input )
374 var_Set( p_playlist->p_input, "state", val );
380 if( p_playlist->status.i_view > -1 )
382 p_playlist->request.p_node = p_playlist->status.p_node;
383 p_playlist->request.p_item = p_playlist->status.p_item;
385 p_playlist->request.i_skip = (int) va_arg( args, int );
386 p_playlist->request.b_request = VLC_TRUE;
390 p_playlist->status.i_status = PLAYLIST_RUNNING;
391 p_playlist->request.p_node = NULL;
392 p_playlist->request.p_item = NULL;
393 p_playlist->request.i_view = -1;
394 p_playlist->request.i_goto = (int) va_arg( args, int );
395 p_playlist->request.b_request = VLC_TRUE;
399 msg_Err( p_playlist, "unimplemented playlist query" );
404 vlc_mutex_unlock( &p_playlist->object_lock );
408 int playlist_PreparseEnqueue( playlist_t *p_playlist,
409 input_item_t *p_item )
411 vlc_mutex_lock( &p_playlist->p_preparse->object_lock );
412 INSERT_ELEM( p_playlist->p_preparse->pp_waiting,
413 p_playlist->p_preparse->i_waiting,
414 p_playlist->p_preparse->i_waiting,
416 vlc_mutex_unlock( &p_playlist->p_preparse->object_lock );
421 /* Destroy remaining objects */
422 static mtime_t ObjectGarbageCollector( playlist_t *p_playlist, int i_type,
423 mtime_t destroy_date )
427 if( destroy_date > mdate() ) return destroy_date;
429 if( destroy_date == 0 )
431 /* give a little time */
432 return mdate() + I64C(1000000);
436 while( ( p_obj = vlc_object_find( p_playlist, i_type, FIND_CHILD ) ) )
438 if( p_obj->p_parent != (vlc_object_t*)p_playlist )
440 /* only first child (ie unused) */
441 vlc_object_release( p_obj );
444 if( i_type == VLC_OBJECT_VOUT )
446 msg_Dbg( p_playlist, "garbage collector destroying 1 vout" );
447 vlc_object_detach( p_obj );
448 vlc_object_release( p_obj );
449 vout_Destroy( (vout_thread_t *)p_obj );
451 else if( i_type == VLC_OBJECT_SOUT )
453 vlc_object_release( p_obj );
454 sout_DeleteInstance( (sout_instance_t*)p_obj );
461 /*****************************************************************************
462 * RunThread: main playlist thread
463 *****************************************************************************/
464 static void RunThread ( playlist_t *p_playlist )
467 playlist_item_t *p_item;
469 mtime_t i_vout_destroyed_date = 0;
470 mtime_t i_sout_destroyed_date = 0;
472 playlist_item_t *p_autodelete_item = NULL;
474 /* Tell above that we're ready */
475 vlc_thread_ready( p_playlist );
477 while( !p_playlist->b_die )
479 vlc_mutex_lock( &p_playlist->object_lock );
481 /* First, check if we have something to do */
482 /* FIXME : this can be called several times */
483 if( p_playlist->request.b_request )
485 #ifdef PLAYLIST_PROFILE
486 msg_Dbg(p_playlist, "beginning processing of request, "
487 I64Fi" us ", mdate() - p_playlist->request_date );
489 /* Stop the existing input */
490 if( p_playlist->p_input )
492 input_StopThread( p_playlist->p_input );
494 /* The code below will start the next input for us */
495 if( p_playlist->status.i_status == PLAYLIST_STOPPED )
497 p_playlist->request.b_request = VLC_FALSE;
501 /* If there is an input, check that it doesn't need to die. */
502 if( p_playlist->p_input )
504 /* This input is dead. Remove it ! */
505 if( p_playlist->p_input->b_dead )
507 input_thread_t *p_input;
509 p_input = p_playlist->p_input;
510 p_playlist->p_input = NULL;
512 /* Release the playlist lock, because we may get stuck
513 * in input_DestroyThread() for some time. */
514 vlc_mutex_unlock( &p_playlist->object_lock );
517 input_DestroyThread( p_input );
519 /* Unlink current input
520 * (_after_ input_DestroyThread for vout garbage collector) */
521 vlc_object_detach( p_input );
524 vlc_object_destroy( p_input );
526 i_vout_destroyed_date = 0;
527 i_sout_destroyed_date = 0;
531 /* This input is dying, let him do */
532 else if( p_playlist->p_input->b_die )
536 /* This input has finished, ask him to die ! */
537 else if( p_playlist->p_input->b_error
538 || p_playlist->p_input->b_eof )
540 /* TODO FIXME XXX TODO FIXME XXX */
541 /* Check for autodeletion */
543 if( p_playlist->status.p_item->i_flags & PLAYLIST_DEL_FLAG )
545 p_autodelete_item = p_playlist->status.p_item;
547 input_StopThread( p_playlist->p_input );
548 /* Select the next playlist item */
549 vlc_mutex_unlock( &p_playlist->object_lock );
552 else if( p_playlist->p_input->i_state != INIT_S )
554 vlc_mutex_unlock( &p_playlist->object_lock );
555 i_vout_destroyed_date =
556 ObjectGarbageCollector( p_playlist, VLC_OBJECT_VOUT,
557 i_vout_destroyed_date );
558 i_sout_destroyed_date =
559 ObjectGarbageCollector( p_playlist, VLC_OBJECT_SOUT,
560 i_sout_destroyed_date );
561 vlc_mutex_lock( &p_playlist->object_lock );
564 else if( p_playlist->status.i_status != PLAYLIST_STOPPED )
566 /* Start another input.
567 * Get the next item to play */
568 p_item = NextItem( p_playlist );
574 if( p_autodelete_item )
576 playlist_Delete( p_playlist,
577 p_autodelete_item->input.i_id );
578 p_autodelete_item = NULL;
580 p_playlist->status.i_status = PLAYLIST_STOPPED;
581 vlc_mutex_unlock( &p_playlist->object_lock );
585 PlayItem( p_playlist, p_item );
587 if( p_autodelete_item )
589 playlist_Delete( p_playlist, p_autodelete_item->input.i_id );
590 p_autodelete_item = NULL;
593 else if( p_playlist->status.i_status == PLAYLIST_STOPPED )
595 /* Collect garbage */
596 vlc_mutex_unlock( &p_playlist->object_lock );
597 i_sout_destroyed_date =
598 ObjectGarbageCollector( p_playlist, VLC_OBJECT_SOUT, mdate() );
599 i_vout_destroyed_date =
600 ObjectGarbageCollector( p_playlist, VLC_OBJECT_VOUT, mdate() );
601 vlc_mutex_lock( &p_playlist->object_lock );
603 vlc_mutex_unlock( &p_playlist->object_lock );
605 msleep( INTF_IDLE_SLEEP / 2 );
607 /* Stop sleeping earlier if we have work */
608 /* TODO : statistics about this */
609 if ( p_playlist->request.b_request &&
610 p_playlist->status.i_status == PLAYLIST_RUNNING )
615 msleep( INTF_IDLE_SLEEP / 2 );
620 /* If there is an input, kill it */
623 vlc_mutex_lock( &p_playlist->object_lock );
625 if( p_playlist->p_input == NULL )
627 vlc_mutex_unlock( &p_playlist->object_lock );
631 if( p_playlist->p_input->b_dead )
633 input_thread_t *p_input;
635 /* Unlink current input */
636 p_input = p_playlist->p_input;
637 p_playlist->p_input = NULL;
638 vlc_mutex_unlock( &p_playlist->object_lock );
641 input_DestroyThread( p_input );
642 /* Unlink current input (_after_ input_DestroyThread for vout
643 * garbage collector)*/
644 vlc_object_detach( p_input );
647 vlc_object_destroy( p_input );
650 else if( p_playlist->p_input->b_die )
652 /* This input is dying, leave him alone */
655 else if( p_playlist->p_input->b_error || p_playlist->p_input->b_eof )
657 input_StopThread( p_playlist->p_input );
658 vlc_mutex_unlock( &p_playlist->object_lock );
663 p_playlist->p_input->b_eof = 1;
666 vlc_mutex_unlock( &p_playlist->object_lock );
668 msleep( INTF_IDLE_SLEEP );
671 /* close all remaining sout */
672 while( ( p_obj = vlc_object_find( p_playlist,
673 VLC_OBJECT_SOUT, FIND_CHILD ) ) )
675 vlc_object_release( p_obj );
676 sout_DeleteInstance( (sout_instance_t*)p_obj );
679 /* close all remaining vout */
680 while( ( p_obj = vlc_object_find( p_playlist,
681 VLC_OBJECT_VOUT, FIND_CHILD ) ) )
683 vlc_object_detach( p_obj );
684 vlc_object_release( p_obj );
685 vout_Destroy( (vout_thread_t *)p_obj );
689 /* Queue for items to preparse */
690 static void RunPreparse ( playlist_preparse_t *p_obj )
692 playlist_t *p_playlist = p_obj->p_parent;
695 /* Tell above that we're ready */
696 vlc_thread_ready( p_obj );
698 while( !p_playlist->b_die )
700 vlc_mutex_lock( &p_obj->object_lock );
702 if( p_obj->i_waiting > 0 )
704 input_Preparse( p_playlist, p_obj->pp_waiting[0] );
705 var_SetInteger( p_playlist, "item-change",
706 p_obj->pp_waiting[0]->i_id );
707 REMOVE_ELEM( p_obj->pp_waiting, p_obj->i_waiting, 0 );
709 b_sleep = ( p_obj->i_waiting == 0 );
711 vlc_mutex_unlock( &p_obj->object_lock );
713 if( p_obj->i_waiting == 0 )
715 msleep( INTF_IDLE_SLEEP );
720 /*****************************************************************************
722 *****************************************************************************
723 * This function calculates the next playlist item, depending
724 * on the playlist course mode (forward, backward, random, view,...).
725 *****************************************************************************/
726 static playlist_item_t * NextItem( playlist_t *p_playlist )
728 playlist_item_t *p_new = NULL;
729 int i_skip,i_goto,i, i_new, i_count ;
730 playlist_view_t *p_view;
732 vlc_bool_t b_loop = var_GetBool( p_playlist, "loop");
733 vlc_bool_t b_random = var_GetBool( p_playlist, "random" );
734 vlc_bool_t b_repeat = var_GetBool( p_playlist, "repeat" );
735 vlc_bool_t b_playstop = var_GetBool( p_playlist, "play-and-stop" );
737 #ifdef PLAYLIST_PROFILE
738 /* Calculate time needed */
739 int64_t start = mdate();
743 /* Handle quickly a few special cases */
745 /* No items to play */
746 if( p_playlist->i_size == 0 )
748 msg_Info( p_playlist, "playlist is empty" );
751 /* Nothing requested */
752 if( !p_playlist->request.b_request && p_playlist->status.p_item == NULL )
754 msg_Dbg( p_playlist,"nothing requested, starting" );
757 /* Repeat and play/stop */
758 if( !p_playlist->request.b_request && b_repeat == VLC_TRUE )
760 msg_Dbg( p_playlist,"repeating item" );
761 return p_playlist->status.p_item;
764 if( !p_playlist->request.b_request && b_playstop == VLC_TRUE )
766 msg_Dbg( p_playlist,"stopping (play and stop)");
770 if( !p_playlist->request.b_request && p_playlist->status.p_item &&
771 !(p_playlist->status.p_item->i_flags & PLAYLIST_SKIP_FLAG) )
773 msg_Dbg( p_playlist, "no-skip mode, stopping") ;
777 /* TODO: improve this (only use current node) */
778 /* TODO: use the "shuffled view" internally ? */
779 /* Random case. This is an exception: if request, but request is skip +- 1
780 * we don't go to next item but select a new random one. */
781 if( b_random && (!p_playlist->request.b_request ||
782 p_playlist->request.i_skip == 1 || p_playlist->request.i_skip == -1 ) )
784 srand( (unsigned int)mdate() );
786 for( i_count = 0; i_count < p_playlist->i_size - 1 ; i_count ++ )
789 (int)((float)p_playlist->i_size * rand() / (RAND_MAX+1.0));
790 /* Check if the item has not already been played */
791 if( p_playlist->pp_items[i_new]->i_nb_played == 0 )
794 if( i_count == p_playlist->i_size )
796 /* The whole playlist has been played: reset the counters */
799 p_playlist->pp_items[--i_count]->i_nb_played = 0;
806 p_playlist->request.i_skip = 0;
807 p_playlist->request.b_request = VLC_FALSE;
808 return p_playlist->pp_items[i_new];
811 /* Start the real work */
812 if( p_playlist->request.b_request )
814 msg_Dbg( p_playlist,"processing request" );
815 /* We are not playing from a view */
816 if( p_playlist->request.i_view == -1 )
818 #ifdef PLAYLIST_DEBUG
819 msg_Dbg( p_playlist, "non-view mode request");
821 /* Directly select the item, just like now */
822 i_skip = p_playlist->request.i_skip;
823 i_goto = p_playlist->request.i_goto;
825 if( p_playlist->i_index == -1 ) p_playlist->i_index = 0;
826 p_new = p_playlist->pp_items[p_playlist->i_index];
828 if( i_goto >= 0 && i_goto < p_playlist->i_size )
830 p_playlist->i_index = i_goto;
831 p_new = p_playlist->pp_items[p_playlist->i_index];
832 p_playlist->request.i_goto = -1;
837 if( p_playlist->i_index + i_skip < p_playlist->i_size &&
838 p_playlist->i_index + i_skip >= 0 )
840 p_playlist->i_index += i_skip;
841 p_new = p_playlist->pp_items[p_playlist->i_index];
847 #ifdef PLAYLIST_DEBUG
848 msg_Dbg( p_playlist, "view mode request" );
850 p_new = p_playlist->request.p_item;
851 i_skip = p_playlist->request.i_skip;
853 /* If we are asked for a node, take its first item */
854 if( p_playlist->request.p_item == NULL && i_skip == 0 )
859 p_view = playlist_ViewFind( p_playlist,p_playlist->request.i_view );
860 p_playlist->status.p_node = p_playlist->request.p_node;
861 p_playlist->status.i_view = p_playlist->request.i_view;
864 for( i = i_skip; i > 0 ; i-- )
866 p_new = playlist_FindNextFromParent( p_playlist,
867 p_playlist->request.i_view,
869 p_playlist->request.p_node,
871 if( p_new == NULL ) break;
874 else if( i_skip < 0 )
876 for( i = i_skip; i < 0 ; i++ )
878 p_new = playlist_FindPrevFromParent( p_playlist,
879 p_playlist->request.i_view,
881 p_playlist->request.p_node,
883 if( p_new == NULL ) break;
888 /* Clear the request */
889 p_playlist->request.b_request = VLC_FALSE;
891 /* "Automatic" item change ( next ) */
894 p_playlist->request_date = 0;
896 if( p_playlist->status.i_view == -1 )
898 if( p_playlist->i_index + 1 < p_playlist->i_size )
900 p_playlist->i_index++;
901 p_new = p_playlist->pp_items[p_playlist->i_index];
902 if( !(p_new->i_flags & PLAYLIST_SKIP_FLAG) )
909 msg_Dbg( p_playlist,"finished" );
913 /* We are playing with a view */
916 playlist_view_t *p_view =
917 playlist_ViewFind( p_playlist,
918 p_playlist->status.i_view );
919 fprintf(stderr,"Finding next of %s within %s, view %i\n",
920 p_playlist->status.p_item ? p_playlist->status.p_item->input.psz_name : "coincoin",
921 p_playlist->status.p_node->input.psz_name,
922 p_playlist->status.i_view) ;
923 p_new = playlist_FindNextFromParent( p_playlist,
924 p_playlist->status.i_view,
926 p_playlist->status.p_node,
927 p_playlist->status.p_item );
932 if( p_playlist->i_index >= 0 && p_new != NULL &&
933 p_playlist->pp_items[p_playlist->i_index] != p_new )
935 p_playlist->i_index = playlist_GetPositionById( p_playlist,
939 #ifdef PLAYLIST_PROFILE
940 msg_Dbg(p_playlist,"next item found in "I64Fi " us", mdate()-start );
943 if( p_new == NULL ) { msg_Info( p_playlist, "Nothing to play" ); }
952 static void SkipItem( playlist_t *p_playlist, int i_arg )
954 int i_oldindex = p_playlist->i_index;
955 vlc_bool_t b_random, b_repeat, b_loop;
962 p_playlist->i_index += i_arg;
966 if( p_playlist->i_index >= p_playlist->i_size )
968 if( p_playlist->i_status == PLAYLIST_STOPPED || b_random || b_loop )
970 p_playlist->i_index -= p_playlist->i_size
971 * ( p_playlist->i_index / p_playlist->i_size );
975 /* Don't loop by default: stop at playlist end */
976 p_playlist->i_index = i_oldindex;
977 p_playlist->i_status = PLAYLIST_STOPPED;
980 else if( p_playlist->i_index < 0 )
982 p_playlist->i_index = p_playlist->i_size - 1;
985 /* Check that the item is enabled */
986 if( p_playlist->pp_items[p_playlist->i_index]->b_enabled == VLC_FALSE &&
987 p_playlist->i_enabled != 0)
989 SkipItem( p_playlist , 1 );
995 /*****************************************************************************
996 * PlayItem: start the input thread for an item
997 ****************************************************************************/
998 static int PlayItem( playlist_t *p_playlist, playlist_item_t *p_item )
1002 msg_Dbg( p_playlist, "creating new input thread" );
1004 p_item->i_nb_played++;
1005 p_playlist->status.p_item = p_item;
1007 p_playlist->i_index = playlist_GetPositionById( p_playlist,
1008 p_item->input.i_id );
1010 #ifdef PLAYLIST_PROFILE
1011 if( p_playlist->request_date != 0 )
1013 msg_Dbg( p_playlist, "request processed after "I64Fi " us",
1014 mdate() - p_playlist->request_date );
1018 p_playlist->p_input = input_CreateThread( p_playlist, &p_item->input );
1020 var_AddCallback( p_playlist->p_input, "item-change",
1021 ItemChange, p_playlist );
1023 val.i_int = p_item->input.i_id;
1024 /* unlock the playlist to set the var...mmm */
1025 vlc_mutex_unlock( &p_playlist->object_lock);
1026 var_Set( p_playlist, "playlist-current", val);
1027 vlc_mutex_lock( &p_playlist->object_lock);
1033 /* Forward item change from input */
1034 static int ItemChange( vlc_object_t *p_obj, const char *psz_var,
1035 vlc_value_t oldval, vlc_value_t newval, void *param )
1037 playlist_t *p_playlist = (playlist_t *)param;
1040 //p_playlist->b_need_update = VLC_TRUE;
1041 var_SetInteger( p_playlist, "item-change", newval.i_int );
1044 /* FIXME: Make that automatic */
1045 playlist_ViewUpdate( p_playlist, VIEW_S_AUTHOR );