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 playlist_item_t * NextItem ( playlist_t * );
47 static int PlayItem ( playlist_t *, playlist_item_t * );
49 static int ItemChange( vlc_object_t *, const char *,
50 vlc_value_t, vlc_value_t, void * );
52 int playlist_vaControl( playlist_t * p_playlist, int i_query, va_list args );
58 * Create a playlist structure.
59 * \param p_parent the vlc object that is to be the parent of this playlist
60 * \return a pointer to the created playlist, or NULL on error
62 playlist_t * __playlist_Create ( vlc_object_t *p_parent )
64 playlist_t *p_playlist;
65 playlist_view_t *p_view;
68 /* Allocate structure */
69 p_playlist = vlc_object_create( p_parent, VLC_OBJECT_PLAYLIST );
72 msg_Err( p_parent, "out of memory" );
76 /* These variables control updates */
77 var_Create( p_playlist, "intf-change", VLC_VAR_BOOL );
78 val.b_bool = VLC_TRUE;
79 var_Set( p_playlist, "intf-change", val );
81 var_Create( p_playlist, "item-change", VLC_VAR_INTEGER );
83 var_Set( p_playlist, "item-change", val );
85 var_Create( p_playlist, "playlist-current", VLC_VAR_INTEGER );
87 var_Set( p_playlist, "playlist-current", val );
89 var_Create( p_playlist, "intf-popupmenu", VLC_VAR_BOOL );
91 var_Create( p_playlist, "intf-show", VLC_VAR_BOOL );
92 val.b_bool = VLC_TRUE;
93 var_Set( p_playlist, "intf-show", val );
96 /* Variables to control playback */
97 var_CreateGetBool( p_playlist, "play-and-stop" );
98 var_CreateGetBool( p_playlist, "random" );
99 var_CreateGetBool( p_playlist, "repeat" );
100 var_CreateGetBool( p_playlist, "loop" );
102 /* Initialise data structures */
103 p_playlist->b_go_next = VLC_TRUE;
104 p_playlist->p_input = NULL;
106 p_playlist->request_date = 0;
108 p_playlist->i_views = 0;
109 p_playlist->pp_views = NULL;
111 p_playlist->i_index = -1;
112 p_playlist->i_size = 0;
113 p_playlist->pp_items = NULL;
115 playlist_ViewInsert( p_playlist, VIEW_CATEGORY, TITLE_CATEGORY );
116 playlist_ViewInsert( p_playlist, VIEW_SIMPLE, TITLE_SIMPLE );
117 playlist_ViewInsert( p_playlist, VIEW_ALL, TITLE_ALL );
119 p_view = playlist_ViewFind( p_playlist, VIEW_CATEGORY );
121 p_playlist->p_general = playlist_NodeCreate( p_playlist, VIEW_CATEGORY,
122 _( "General" ), p_view->p_root );
124 /* Set startup status
125 * We set to simple view on startup for interfaces that don't do
127 p_view = playlist_ViewFind( p_playlist, VIEW_SIMPLE );
128 p_playlist->status.i_view = VIEW_SIMPLE;
129 p_playlist->status.p_item = NULL;
130 p_playlist->status.p_node = p_view->p_root;
131 p_playlist->request.b_request = VLC_FALSE;
132 p_playlist->status.i_status = PLAYLIST_STOPPED;
135 p_playlist->i_last_id = 0;
136 p_playlist->i_sort = SORT_ID;
137 p_playlist->i_order = ORDER_NORMAL;
139 /* Finally, launch the thread ! */
140 if( vlc_thread_create( p_playlist, "playlist", RunThread,
141 VLC_THREAD_PRIORITY_LOW, VLC_TRUE ) )
143 msg_Err( p_playlist, "cannot spawn playlist thread" );
144 vlc_object_destroy( p_playlist );
148 /* The object has been initialized, now attach it */
149 vlc_object_attach( p_playlist, p_parent );
155 * Destroy the playlist.
157 * Delete all items in the playlist and free the playlist structure.
158 * \param p_playlist the playlist structure to destroy
159 * \return VLC_SUCCESS or an error
161 int playlist_Destroy( playlist_t * p_playlist )
164 p_playlist->b_die = 1;
166 for( i = 0 ; i< p_playlist->i_sds ; i++ )
168 playlist_ServicesDiscoveryRemove( p_playlist,
169 p_playlist->pp_sds[i]->psz_module );
172 vlc_thread_join( p_playlist );
174 var_Destroy( p_playlist, "intf-change" );
175 var_Destroy( p_playlist, "item-change" );
176 var_Destroy( p_playlist, "playlist-current" );
177 var_Destroy( p_playlist, "intf-popmenu" );
178 var_Destroy( p_playlist, "intf-show" );
179 var_Destroy( p_playlist, "play-and-stop" );
180 var_Destroy( p_playlist, "random" );
181 var_Destroy( p_playlist, "repeat" );
182 var_Destroy( p_playlist, "loop" );
184 playlist_Clear( p_playlist );
186 for( i = p_playlist->i_views - 1; i >= 0 ; i-- )
188 playlist_view_t *p_view = p_playlist->pp_views[i];
189 if( p_view->psz_name )
190 free( p_view->psz_name );
191 playlist_ItemDelete( p_view->p_root );
192 REMOVE_ELEM( p_playlist->pp_views, p_playlist->i_views, i );
196 vlc_object_destroy( p_playlist );
203 * Do a playlist action.
205 * If there is something in the playlist then you can do playlist actions.
207 * Playlist lock must not be taken when calling this function
209 * \param p_playlist the playlist to do the command on
210 * \param i_query the command to do
211 * \param variable number of arguments
212 * \return VLC_SUCCESS or an error
214 int playlist_Control( playlist_t * p_playlist, int i_query, ... )
218 va_start( args, i_query );
219 i_result = playlist_vaControl( p_playlist, i_query, args );
225 int playlist_vaControl( playlist_t * p_playlist, int i_query, va_list args )
227 playlist_view_t *p_view;
230 vlc_mutex_lock( &p_playlist->object_lock );
232 #ifdef PLAYLIST_PROFILE
233 p_playlist->request_date = mdate();
236 if( p_playlist->i_size <= 0 )
238 vlc_mutex_unlock( &p_playlist->object_lock );
245 p_playlist->status.i_status = PLAYLIST_STOPPED;
246 p_playlist->request.b_request = VLC_TRUE;
249 case PLAYLIST_ITEMPLAY:
250 p_playlist->status.i_status = PLAYLIST_RUNNING;
251 p_playlist->request.i_skip = 0;
252 p_playlist->request.b_request = VLC_TRUE;
253 p_playlist->request.p_item = (playlist_item_t *)va_arg( args,
255 p_playlist->request.i_view = p_playlist->status.i_view;
256 p_view = playlist_ViewFind( p_playlist, p_playlist->status.i_view );
259 p_playlist->request.p_node = p_view->p_root;
263 p_playlist->request.p_node = NULL;
267 case PLAYLIST_VIEWPLAY:
268 p_playlist->status.i_status = PLAYLIST_RUNNING;
269 p_playlist->request.i_skip = 0;
270 p_playlist->request.b_request = VLC_TRUE;
271 p_playlist->request.i_view = (int)va_arg( args,int );
272 p_playlist->request.p_node = (playlist_item_t *)va_arg( args,
274 p_playlist->request.p_item = (playlist_item_t *)va_arg( args,
277 /* If we select a node, play only it.
278 * If we select an item, continue */
279 if( p_playlist->request.p_item == NULL ||
280 ! p_playlist->request.p_node->i_flags & PLAYLIST_SKIP_FLAG )
282 p_playlist->b_go_next = VLC_FALSE;
286 p_playlist->b_go_next = VLC_TRUE;
291 p_playlist->status.i_status = PLAYLIST_RUNNING;
293 if( p_playlist->p_input )
295 val.i_int = PLAYING_S;
296 var_Set( p_playlist->p_input, "state", val );
300 /* FIXME : needed ? */
301 p_playlist->request.b_request = VLC_TRUE;
302 p_playlist->request.i_view = p_playlist->status.i_view;
303 p_playlist->request.p_node = p_playlist->status.p_node;
304 p_playlist->request.p_item = p_playlist->status.p_item;
305 p_playlist->request.i_skip = 0;
306 p_playlist->request.i_goto = -1;
309 case PLAYLIST_AUTOPLAY:
310 p_playlist->status.i_status = PLAYLIST_RUNNING;
312 p_playlist->request.b_request = VLC_FALSE;
317 if( p_playlist->p_input )
318 var_Get( p_playlist->p_input, "state", &val );
320 if( val.i_int == PAUSE_S )
322 p_playlist->status.i_status = PLAYLIST_RUNNING;
323 if( p_playlist->p_input )
325 val.i_int = PLAYING_S;
326 var_Set( p_playlist->p_input, "state", val );
331 p_playlist->status.i_status = PLAYLIST_PAUSED;
332 if( p_playlist->p_input )
335 var_Set( p_playlist->p_input, "state", val );
341 if( p_playlist->status.i_view > -1 )
343 p_playlist->request.p_node = p_playlist->status.p_node;
344 p_playlist->request.p_item = p_playlist->status.p_item;
346 p_playlist->request.i_skip = (int) va_arg( args, int );
347 p_playlist->request.b_request = VLC_TRUE;
351 p_playlist->status.i_status = PLAYLIST_RUNNING;
352 p_playlist->request.p_node = NULL;
353 p_playlist->request.p_item = NULL;
354 p_playlist->request.i_view = -1;
355 p_playlist->request.i_goto = (int) va_arg( args, int );
356 p_playlist->request.b_request = VLC_TRUE;
360 msg_Err( p_playlist, "unimplemented playlist query" );
365 vlc_mutex_unlock( &p_playlist->object_lock );
366 fprintf(stderr,"control done, request is %i\n", p_playlist->request.b_request);
371 /* Destroy remaining objects */
372 static mtime_t ObjectGarbageCollector( playlist_t *p_playlist, int i_type,
373 mtime_t destroy_date )
377 if( destroy_date > mdate() ) return destroy_date;
379 if( destroy_date == 0 )
381 /* give a little time */
382 return mdate() + I64C(1000000);
386 while( ( p_obj = vlc_object_find( p_playlist, i_type, FIND_CHILD ) ) )
388 if( p_obj->p_parent != (vlc_object_t*)p_playlist )
390 /* only first child (ie unused) */
391 vlc_object_release( p_obj );
394 if( i_type == VLC_OBJECT_VOUT )
396 msg_Dbg( p_playlist, "garbage collector destroying 1 vout" );
397 vlc_object_detach( p_obj );
398 vlc_object_release( p_obj );
399 vout_Destroy( (vout_thread_t *)p_obj );
401 else if( i_type == VLC_OBJECT_SOUT )
403 vlc_object_release( p_obj );
404 sout_DeleteInstance( (sout_instance_t*)p_obj );
411 /*****************************************************************************
412 * RunThread: main playlist thread
413 *****************************************************************************/
414 static void RunThread ( playlist_t *p_playlist )
417 playlist_item_t *p_item;
419 mtime_t i_vout_destroyed_date = 0;
420 mtime_t i_sout_destroyed_date = 0;
422 playlist_item_t *p_autodelete_item = NULL;
424 /* Tell above that we're ready */
425 vlc_thread_ready( p_playlist );
427 while( !p_playlist->b_die )
429 vlc_mutex_lock( &p_playlist->object_lock );
431 /* First, check if we have something to do */
432 /* FIXME : this can be called several times */
433 if( p_playlist->request.b_request )
435 #ifdef PLAYLIST_PROFILE
436 msg_Dbg(p_playlist, "beginning processing of request, "
437 I64Fi" us ", mdate() - p_playlist->request_date );
439 /* Stop the existing input */
440 if( p_playlist->p_input )
442 input_StopThread( p_playlist->p_input );
444 /* The code below will start the next input for us */
445 if( p_playlist->status.i_status == PLAYLIST_STOPPED )
447 p_playlist->request.b_request = VLC_FALSE;
451 /* If there is an input, check that it doesn't need to die. */
452 if( p_playlist->p_input )
454 /* This input is dead. Remove it ! */
455 if( p_playlist->p_input->b_dead )
457 input_thread_t *p_input;
459 p_input = p_playlist->p_input;
460 p_playlist->p_input = NULL;
462 /* Release the playlist lock, because we may get stuck
463 * in input_DestroyThread() for some time. */
464 vlc_mutex_unlock( &p_playlist->object_lock );
467 input_DestroyThread( p_input );
469 /* Unlink current input
470 * (_after_ input_DestroyThread for vout garbage collector) */
471 vlc_object_detach( p_input );
474 vlc_object_destroy( p_input );
476 i_vout_destroyed_date = 0;
477 i_sout_destroyed_date = 0;
481 /* This input is dying, let him do */
482 else if( p_playlist->p_input->b_die )
486 /* This input has finished, ask him to die ! */
487 else if( p_playlist->p_input->b_error
488 || p_playlist->p_input->b_eof )
490 /* TODO FIXME XXX TODO FIXME XXX */
491 /* Check for autodeletion */
493 if( p_playlist->status.p_item->i_flags & PLAYLIST_DEL_FLAG )
495 p_autodelete_item = p_playlist->status.p_item;
497 input_StopThread( p_playlist->p_input );
498 /* Select the next playlist item */
499 vlc_mutex_unlock( &p_playlist->object_lock );
502 else if( p_playlist->p_input->i_state != INIT_S )
504 vlc_mutex_unlock( &p_playlist->object_lock );
505 i_vout_destroyed_date =
506 ObjectGarbageCollector( p_playlist, VLC_OBJECT_VOUT,
507 i_vout_destroyed_date );
508 i_sout_destroyed_date =
509 ObjectGarbageCollector( p_playlist, VLC_OBJECT_SOUT,
510 i_sout_destroyed_date );
511 vlc_mutex_lock( &p_playlist->object_lock );
514 else if( p_playlist->status.i_status != PLAYLIST_STOPPED )
516 /* Start another input.
517 * Get the next item to play */
518 p_item = NextItem( p_playlist );
524 if( p_autodelete_item )
526 vlc_mutex_unlock( &p_playlist->object_lock );
527 playlist_Delete( p_playlist,
528 p_autodelete_item->input.i_id );
529 vlc_mutex_lock( &p_playlist->object_lock );
530 p_autodelete_item = NULL;
532 p_playlist->status.i_status = PLAYLIST_STOPPED;
533 vlc_mutex_unlock( &p_playlist->object_lock );
537 PlayItem( p_playlist, p_item );
539 if( p_autodelete_item )
541 vlc_mutex_unlock( &p_playlist->object_lock );
542 playlist_Delete( p_playlist, p_autodelete_item->input.i_id );
543 vlc_mutex_lock( &p_playlist->object_lock );
544 p_autodelete_item = NULL;
547 else if( p_playlist->status.i_status == PLAYLIST_STOPPED )
549 /* Collect garbage */
550 vlc_mutex_unlock( &p_playlist->object_lock );
551 i_sout_destroyed_date =
552 ObjectGarbageCollector( p_playlist, VLC_OBJECT_SOUT, mdate() );
553 i_vout_destroyed_date =
554 ObjectGarbageCollector( p_playlist, VLC_OBJECT_VOUT, mdate() );
555 vlc_mutex_lock( &p_playlist->object_lock );
557 vlc_mutex_unlock( &p_playlist->object_lock );
559 msleep( INTF_IDLE_SLEEP / 2 );
561 /* Stop sleeping earlier if we have work */
562 /* TODO : statistics about this */
563 if ( p_playlist->request.b_request &&
564 p_playlist->status.i_status == PLAYLIST_RUNNING )
569 msleep( INTF_IDLE_SLEEP / 2 );
574 /* If there is an input, kill it */
577 vlc_mutex_lock( &p_playlist->object_lock );
579 if( p_playlist->p_input == NULL )
581 vlc_mutex_unlock( &p_playlist->object_lock );
585 if( p_playlist->p_input->b_dead )
587 input_thread_t *p_input;
589 /* Unlink current input */
590 p_input = p_playlist->p_input;
591 p_playlist->p_input = NULL;
592 vlc_mutex_unlock( &p_playlist->object_lock );
595 input_DestroyThread( p_input );
596 /* Unlink current input (_after_ input_DestroyThread for vout
597 * garbage collector)*/
598 vlc_object_detach( p_input );
601 vlc_object_destroy( p_input );
604 else if( p_playlist->p_input->b_die )
606 /* This input is dying, leave him alone */
609 else if( p_playlist->p_input->b_error || p_playlist->p_input->b_eof )
611 input_StopThread( p_playlist->p_input );
612 vlc_mutex_unlock( &p_playlist->object_lock );
617 p_playlist->p_input->b_eof = 1;
620 vlc_mutex_unlock( &p_playlist->object_lock );
622 msleep( INTF_IDLE_SLEEP );
625 /* close all remaining sout */
626 while( ( p_obj = vlc_object_find( p_playlist,
627 VLC_OBJECT_SOUT, FIND_CHILD ) ) )
629 vlc_object_release( p_obj );
630 sout_DeleteInstance( (sout_instance_t*)p_obj );
633 /* close all remaining vout */
634 while( ( p_obj = vlc_object_find( p_playlist,
635 VLC_OBJECT_VOUT, FIND_CHILD ) ) )
637 vlc_object_detach( p_obj );
638 vlc_object_release( p_obj );
639 vout_Destroy( (vout_thread_t *)p_obj );
643 /*****************************************************************************
645 *****************************************************************************
646 * This function calculates the next playlist item, depending
647 * on the playlist course mode (forward, backward, random, view,...).
648 *****************************************************************************/
649 static playlist_item_t * NextItem( playlist_t *p_playlist )
651 playlist_item_t *p_new = NULL;
652 int i_skip,i_goto,i, i_new, i_count ;
653 playlist_view_t *p_view;
655 vlc_bool_t b_loop = var_GetBool( p_playlist, "loop");
656 vlc_bool_t b_random = var_GetBool( p_playlist, "random" );
657 vlc_bool_t b_repeat = var_GetBool( p_playlist, "repeat" );
658 vlc_bool_t b_playstop = var_GetBool( p_playlist, "play-and-stop" );
660 #ifdef PLAYLIST_PROFILE
661 /* Calculate time needed */
662 int64_t start = mdate();
666 /* Handle quickly a few special cases */
668 /* No items to play */
669 if( p_playlist->i_size == 0 )
671 msg_Info( p_playlist, "playlist is empty" );
674 /* Nothing requested */
675 if( !p_playlist->request.b_request && p_playlist->status.p_item == NULL )
677 msg_Warn( p_playlist,"nothing requested" );
681 /* Repeat and play/stop */
682 if( !p_playlist->request.b_request && b_repeat == VLC_TRUE )
684 msg_Dbg( p_playlist,"repeating item" );
685 return p_playlist->status.p_item;
688 if( !p_playlist->request.b_request && b_playstop == VLC_TRUE )
690 msg_Dbg( p_playlist,"stopping (play and stop)");
694 if( !p_playlist->request.b_request &&
695 !(p_playlist->status.p_item->i_flags & PLAYLIST_SKIP_FLAG) )
697 msg_Dbg( p_playlist, "no-skip mode, stopping") ;
701 /* TODO: improve this (only use current node) */
702 /* TODO: use the "shuffled view" internally ? */
703 /* Random case. This is an exception: if request, but request is skip +- 1
704 * we don't go to next item but select a new random one. */
705 if( b_random && (!p_playlist->request.b_request ||
706 p_playlist->request.i_skip == 1 || p_playlist->request.i_skip == -1 ) )
708 srand( (unsigned int)mdate() );
710 for( i_count = 0; i_count < p_playlist->i_size - 1 ; i_count ++ )
713 (int)((float)p_playlist->i_size * rand() / (RAND_MAX+1.0));
714 /* Check if the item has not already been played */
715 if( p_playlist->pp_items[i_new]->i_nb_played == 0 )
718 if( i_count == p_playlist->i_size )
720 /* The whole playlist has been played: reset the counters */
723 p_playlist->pp_items[--i_count]->i_nb_played = 0;
730 p_playlist->request.i_skip = 0;
731 p_playlist->request.b_request = VLC_FALSE;
732 return p_playlist->pp_items[i_new];
735 /* Start the real work */
736 if( p_playlist->request.b_request )
738 msg_Dbg( p_playlist,"processing request" );
739 /* We are not playing from a view */
740 if( p_playlist->request.i_view == -1 )
742 #ifdef PLAYLIST_DEBUG
743 msg_Dbg( p_playlist, "non-view mode request");
745 /* Directly select the item, just like now */
746 i_skip = p_playlist->request.i_skip;
747 i_goto = p_playlist->request.i_goto;
749 if( p_playlist->i_index == -1 ) p_playlist->i_index = 0;
750 p_new = p_playlist->pp_items[p_playlist->i_index];
752 if( i_goto >= 0 && i_goto < p_playlist->i_size )
754 p_playlist->i_index = i_goto;
755 p_new = p_playlist->pp_items[p_playlist->i_index];
756 p_playlist->request.i_goto = -1;
761 if( p_playlist->i_index + i_skip < p_playlist->i_size &&
762 p_playlist->i_index + i_skip >= 0 )
764 p_playlist->i_index += i_skip;
765 p_new = p_playlist->pp_items[p_playlist->i_index];
771 #ifdef PLAYLIST_DEBUG
772 msg_Dbg( p_playlist, "view mode request" );
774 p_new = p_playlist->request.p_item;
775 i_skip = p_playlist->request.i_skip;
777 /* If we are asked for a node, take its first item */
778 if( p_playlist->request.p_item == NULL && i_skip == 0 )
783 p_view = playlist_ViewFind( p_playlist,p_playlist->request.i_view );
784 p_playlist->status.p_node = p_playlist->request.p_node;
785 p_playlist->status.i_view = p_playlist->request.i_view;
788 for( i = i_skip; i > 0 ; i-- )
790 p_new = playlist_FindNextFromParent( p_playlist,
791 p_playlist->request.i_view,
793 p_playlist->request.p_node,
795 if( p_new == NULL ) break;
798 else if( i_skip < 0 )
800 for( i = i_skip; i < 0 ; i++ )
802 p_new = playlist_FindPrevFromParent( p_playlist,
803 p_playlist->request.i_view,
805 p_playlist->request.p_node,
807 if( p_new == NULL ) break;
812 /* Clear the request */
813 p_playlist->request.b_request = VLC_FALSE;
815 /* "Automatic" item change ( next ) */
818 p_playlist->request_date = 0;
821 if( p_playlist->status.i_view == -1 )
823 if( p_playlist->i_index + 1 < p_playlist->i_size )
825 p_playlist->i_index++;
826 p_new = p_playlist->pp_items[p_playlist->i_index];
827 if( !(p_new->i_flags & PLAYLIST_SKIP_FLAG) )
834 msg_Dbg( p_playlist,"finished" );
838 /* We are playing with a view */
841 playlist_view_t *p_view =
842 playlist_ViewFind( p_playlist,
843 p_playlist->status.i_view );
844 p_new = playlist_FindNextFromParent( p_playlist,
845 p_playlist->status.i_view,
847 p_playlist->status.p_node,
848 p_playlist->status.p_item );
853 if( p_playlist->i_index >= 0 && p_new != NULL &&
854 p_playlist->pp_items[p_playlist->i_index] != p_new )
856 p_playlist->i_index = playlist_GetPositionById( p_playlist,
860 #ifdef PLAYLIST_PROFILE
861 msg_Dbg(p_playlist,"next item found in "I64Fi " us", mdate()-start );
864 if( p_new == NULL ) { msg_Info( p_playlist, "Nothing to play" ); }
873 static void SkipItem( playlist_t *p_playlist, int i_arg )
875 int i_oldindex = p_playlist->i_index;
876 vlc_bool_t b_random, b_repeat, b_loop;
883 p_playlist->i_index += i_arg;
887 if( p_playlist->i_index >= p_playlist->i_size )
889 if( p_playlist->i_status == PLAYLIST_STOPPED || b_random || b_loop )
891 p_playlist->i_index -= p_playlist->i_size
892 * ( p_playlist->i_index / p_playlist->i_size );
896 /* Don't loop by default: stop at playlist end */
897 p_playlist->i_index = i_oldindex;
898 p_playlist->i_status = PLAYLIST_STOPPED;
901 else if( p_playlist->i_index < 0 )
903 p_playlist->i_index = p_playlist->i_size - 1;
906 /* Check that the item is enabled */
907 if( p_playlist->pp_items[p_playlist->i_index]->b_enabled == VLC_FALSE &&
908 p_playlist->i_enabled != 0)
910 SkipItem( p_playlist , 1 );
916 /*****************************************************************************
917 * PlayItem: start the input thread for an item
918 ****************************************************************************/
919 static int PlayItem( playlist_t *p_playlist, playlist_item_t *p_item )
923 msg_Dbg( p_playlist, "creating new input thread" );
925 p_item->i_nb_played++;
926 p_playlist->status.p_item = p_item;
928 p_playlist->i_index = playlist_GetPositionById( p_playlist,
929 p_item->input.i_id );
931 #ifdef PLAYLIST_PROFILE
932 if( p_playlist->request_date != 0 )
934 msg_Dbg( p_playlist, "request processed after "I64Fi " us",
935 mdate() - p_playlist->request_date );
939 p_playlist->p_input = input_CreateThread( p_playlist, &p_item->input );
941 var_AddCallback( p_playlist->p_input, "item-change",
942 ItemChange, p_playlist );
944 val.i_int = p_item->input.i_id;
945 /* unlock the playlist to set the var...mmm */
946 vlc_mutex_unlock( &p_playlist->object_lock);
947 var_Set( p_playlist, "playlist-current", val);
948 vlc_mutex_lock( &p_playlist->object_lock);
954 /* Forward item change from input */
955 static int ItemChange( vlc_object_t *p_obj, const char *psz_var,
956 vlc_value_t oldval, vlc_value_t newval, void *param )
958 playlist_t *p_playlist = (playlist_t *)param;
961 //p_playlist->b_need_update = VLC_TRUE;
962 var_SetInteger( p_playlist, "item-change", newval.i_int );
965 /* FIXME: Make that automatic */
966 playlist_ViewUpdate( p_playlist, VIEW_S_AUTHOR );