]> git.sesse.net Git - vlc/blob - src/playlist/playlist.c
Compute global input/output stats (Refs:#473)
[vlc] / src / playlist / playlist.c
1 /*****************************************************************************
2  * playlist.c : Playlist management functions
3  *****************************************************************************
4  * Copyright (C) 1999-2004 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Samuel Hocevar <sam@zoy.org>
8  *          ClĂ©ment Stenac <zorglub@videolan.org>
9  *
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.
14  *
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.
19  *
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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24 #include <stdlib.h>                                      /* free(), strtol() */
25 #include <stdio.h>                                              /* sprintf() */
26 #include <string.h>                                            /* strerror() */
27
28 #include <vlc/vlc.h>
29 #include <vlc/vout.h>
30 #include <vlc/sout.h>
31 #include <vlc/input.h>
32
33 #include "vlc_playlist.h"
34
35 #include "vlc_interaction.h"
36
37 #define TITLE_CATEGORY N_( "By category" )
38 #define TITLE_SIMPLE   N_( "Manually added" )
39 #define TITLE_ALL      N_( "All items, unsorted" )
40
41 #undef PLAYLIST_PROFILE
42 #undef PLAYLIST_DEBUG
43
44 /*****************************************************************************
45  * Local prototypes
46  *****************************************************************************/
47 static void RunThread ( playlist_t * );
48 static void RunPreparse( playlist_preparse_t * );
49 static playlist_item_t * NextItem  ( playlist_t * );
50 static int PlayItem  ( playlist_t *, playlist_item_t * );
51
52 int playlist_vaControl( playlist_t * p_playlist, int i_query, va_list args );
53
54 void playlist_PreparseEnqueueItemSub( playlist_t *, playlist_item_t * );
55
56 playlist_item_t *playlist_RecursiveFindLast(playlist_t *p_playlist,
57                                             playlist_item_t *p_node );
58
59 /*****************************************************************************
60  * Helper Function for NextItem
61  *****************************************************************************/
62
63 playlist_item_t *playlist_RecursiveFindLast(playlist_t *p_playlist,
64                                             playlist_item_t *p_node )
65 {
66     int i;
67     playlist_item_t *p_item;
68     for ( i = p_node->i_children - 1; i >= 0; i-- )
69     {
70         if( p_node->pp_children[i]->i_children == -1 )
71             return p_node->pp_children[i];
72         else if(p_node->pp_children[i]->i_children > 0)
73         {
74              p_item = playlist_RecursiveFindLast( p_playlist,
75                                         p_node->pp_children[i] );
76             if ( p_item != NULL )
77                 return p_item;
78         }
79         else if( i == 0 )
80             return NULL;
81     }
82     return NULL;
83 }
84
85
86 /**
87  * Create playlist
88  *
89  * Create a playlist structure.
90  * \param p_parent the vlc object that is to be the parent of this playlist
91  * \return a pointer to the created playlist, or NULL on error
92  */
93 playlist_t * __playlist_Create ( vlc_object_t *p_parent )
94 {
95     playlist_t *p_playlist;
96     playlist_view_t *p_view;
97     vlc_value_t     val;
98
99     /* Allocate structure */
100     p_playlist = vlc_object_create( p_parent, VLC_OBJECT_PLAYLIST );
101     if( !p_playlist )
102     {
103         msg_Err( p_parent, "out of memory" );
104         return NULL;
105     }
106
107     /* These variables control updates */
108     var_Create( p_playlist, "intf-change", VLC_VAR_BOOL );
109     val.b_bool = VLC_TRUE;
110     var_Set( p_playlist, "intf-change", val );
111
112     var_Create( p_playlist, "item-change", VLC_VAR_INTEGER );
113     val.i_int = -1;
114     var_Set( p_playlist, "item-change", val );
115
116     var_Create( p_playlist, "item-deleted", VLC_VAR_INTEGER );
117     val.i_int = -1;
118     var_Set( p_playlist, "item-deleted", val );
119
120     var_Create( p_playlist, "item-append", VLC_VAR_ADDRESS );
121
122     var_Create( p_playlist, "playlist-current", VLC_VAR_INTEGER );
123     val.i_int = -1;
124     var_Set( p_playlist, "playlist-current", val );
125
126     var_Create( p_playlist, "intf-popupmenu", VLC_VAR_BOOL );
127
128     var_Create( p_playlist, "intf-show", VLC_VAR_BOOL );
129     val.b_bool = VLC_TRUE;
130     var_Set( p_playlist, "intf-show", val );
131
132
133     /* Variables to control playback */
134     var_CreateGetBool( p_playlist, "play-and-stop" );
135     var_CreateGetBool( p_playlist, "random" );
136     var_CreateGetBool( p_playlist, "repeat" );
137     var_CreateGetBool( p_playlist, "loop" );
138
139     /* Initialise data structures */
140     vlc_mutex_init( p_playlist, &p_playlist->gc_lock );
141     p_playlist->i_last_id = 0;
142     p_playlist->b_go_next = VLC_TRUE;
143     p_playlist->p_input = NULL;
144
145     p_playlist->request_date = 0;
146
147     p_playlist->i_views = 0;
148     p_playlist->pp_views = NULL;
149
150     p_playlist->i_index = -1;
151     p_playlist->i_size = 0;
152     p_playlist->pp_items = NULL;
153     p_playlist->i_all_size = 0;
154     p_playlist->pp_all_items = 0;
155
156     playlist_ViewInsert( p_playlist, VIEW_CATEGORY, TITLE_CATEGORY );
157     playlist_ViewInsert( p_playlist, VIEW_ALL, TITLE_ALL );
158
159     p_view = playlist_ViewFind( p_playlist, VIEW_CATEGORY );
160
161     p_playlist->p_general =
162         playlist_NodeCreate( p_playlist, VIEW_CATEGORY,
163                              _( "General" ), p_view->p_root );
164     p_playlist->p_general->i_flags |= PLAYLIST_RO_FLAG;
165
166     /* Set startup status
167      * We set to simple view on startup for interfaces that don't do
168      * anything */
169     p_view = playlist_ViewFind( p_playlist, VIEW_CATEGORY );
170     p_playlist->status.i_view = VIEW_CATEGORY;
171     p_playlist->status.p_item = NULL;
172     p_playlist->status.p_node = p_view->p_root;
173     p_playlist->request.b_request = VLC_FALSE;
174     p_playlist->status.i_status = PLAYLIST_STOPPED;
175
176     p_playlist->i_sort = SORT_ID;
177     p_playlist->i_order = ORDER_NORMAL;
178
179     p_playlist->p_stats = (global_stats_t *)malloc( sizeof( global_stats_t ) );
180     vlc_mutex_init( p_playlist, &p_playlist->p_stats->lock );
181
182     /* Finally, launch the thread ! */
183     if( vlc_thread_create( p_playlist, "playlist", RunThread,
184                            VLC_THREAD_PRIORITY_LOW, VLC_TRUE ) )
185     {
186         msg_Err( p_playlist, "cannot spawn playlist thread" );
187         vlc_object_destroy( p_playlist );
188         return NULL;
189     }
190
191     /* Preparsing stuff */
192     p_playlist->p_preparse = vlc_object_create( p_playlist,
193                                                 sizeof( playlist_preparse_t ) );
194     if( !p_playlist->p_preparse )
195     {
196         msg_Err( p_playlist, "unable to create preparser" );
197         vlc_object_destroy( p_playlist );
198         return NULL;
199     }
200
201     // Preparse
202     p_playlist->p_preparse->i_waiting = 0;
203     p_playlist->p_preparse->pi_waiting = NULL;
204
205     // Interaction
206     p_playlist->p_interaction = NULL;
207
208     vlc_object_attach( p_playlist->p_preparse, p_playlist );
209     if( vlc_thread_create( p_playlist->p_preparse, "preparser",
210                            RunPreparse, VLC_THREAD_PRIORITY_LOW, VLC_TRUE ) )
211     {
212         msg_Err( p_playlist, "cannot spawn preparse thread" );
213         vlc_object_detach( p_playlist->p_preparse );
214         vlc_object_destroy( p_playlist->p_preparse );
215         return NULL;
216     }
217
218     /* The object has been initialized, now attach it */
219     vlc_object_attach( p_playlist, p_parent );
220
221     return p_playlist;
222 }
223
224 /**
225  * Destroy the playlist.
226  *
227  * Delete all items in the playlist and free the playlist structure.
228  * \param p_playlist the playlist structure to destroy
229  * \return VLC_SUCCESS or an error
230  */
231 int playlist_Destroy( playlist_t * p_playlist )
232 {
233     int i;
234     p_playlist->b_die = 1;
235
236     while( p_playlist->i_sds )
237     {
238         playlist_ServicesDiscoveryRemove( p_playlist,
239                                           p_playlist->pp_sds[0]->psz_module );
240     }
241
242     if( p_playlist->p_interaction )
243     {
244         intf_InteractionDestroy( p_playlist->p_interaction );
245     }
246
247     vlc_thread_join( p_playlist->p_preparse );
248     vlc_thread_join( p_playlist );
249
250     vlc_object_detach( p_playlist->p_preparse );
251
252     var_Destroy( p_playlist, "intf-change" );
253     var_Destroy( p_playlist, "item-change" );
254     var_Destroy( p_playlist, "playlist-current" );
255     var_Destroy( p_playlist, "intf-popmenu" );
256     var_Destroy( p_playlist, "intf-show" );
257     var_Destroy( p_playlist, "play-and-stop" );
258     var_Destroy( p_playlist, "random" );
259     var_Destroy( p_playlist, "repeat" );
260     var_Destroy( p_playlist, "loop" );
261
262     playlist_Clear( p_playlist );
263
264     for( i = p_playlist->i_views - 1; i >= 0 ; i-- )
265     {
266         playlist_view_t *p_view = p_playlist->pp_views[i];
267         if( p_view->psz_name )
268             free( p_view->psz_name );
269         playlist_ItemDelete( p_view->p_root );
270         REMOVE_ELEM( p_playlist->pp_views, p_playlist->i_views, i );
271         free( p_view );
272     }
273
274     vlc_mutex_destroy( &p_playlist->gc_lock );
275     vlc_object_destroy( p_playlist->p_preparse );
276     vlc_object_destroy( p_playlist );
277
278     return VLC_SUCCESS;
279 }
280
281
282 /**
283  * Do a playlist action.
284  *
285  * If there is something in the playlist then you can do playlist actions.
286  *
287  * Playlist lock must not be taken when calling this function
288  *
289  * \param p_playlist the playlist to do the command on
290  * \param i_query the command to do
291  * \param variable number of arguments
292  * \return VLC_SUCCESS or an error
293  */
294 int playlist_LockControl( playlist_t * p_playlist, int i_query, ... )
295 {
296     va_list args;
297     int i_result;
298     va_start( args, i_query );
299     vlc_mutex_lock( &p_playlist->object_lock );
300     i_result = playlist_vaControl( p_playlist, i_query, args );
301     va_end( args );
302     vlc_mutex_unlock( &p_playlist->object_lock );
303     return i_result;
304 }
305
306 /**
307  * Do a playlist action.
308  *
309  * If there is something in the playlist then you can do playlist actions.
310  *
311  * Playlist lock must be taken when calling this function
312  *
313  * \param p_playlist the playlist to do the command on
314  * \param i_query the command to do
315  * \param variable number of arguments
316  * \return VLC_SUCCESS or an error
317  */
318 int playlist_Control( playlist_t * p_playlist, int i_query, ... )
319 {
320     va_list args;
321     int i_result;
322     va_start( args, i_query );
323     i_result = playlist_vaControl( p_playlist, i_query, args );
324     va_end( args );
325
326     return i_result;
327 }
328
329 int playlist_vaControl( playlist_t * p_playlist, int i_query, va_list args )
330 {
331     playlist_view_t *p_view;
332     playlist_item_t *p_item, *p_node;
333     int i_view;
334     vlc_value_t val;
335
336 #ifdef PLAYLIST_PROFILE
337     p_playlist->request_date = mdate();
338 #endif
339
340     if( p_playlist->i_size <= 0 )
341     {
342         return VLC_EGENERIC;
343     }
344
345     switch( i_query )
346     {
347     case PLAYLIST_STOP:
348         p_playlist->status.i_status = PLAYLIST_STOPPED;
349         p_playlist->request.b_request = VLC_TRUE;
350         p_playlist->request.p_item = NULL;
351         break;
352
353     case PLAYLIST_ITEMPLAY:
354         p_item = (playlist_item_t *)va_arg( args, playlist_item_t * );
355         if ( p_item == NULL || p_item->input.psz_uri == NULL )
356             return VLC_EGENERIC;
357         p_playlist->status.i_status = PLAYLIST_RUNNING;
358         p_playlist->request.i_skip = 0;
359         p_playlist->request.b_request = VLC_TRUE;
360         p_playlist->request.p_item = p_item;
361         p_playlist->request.i_view = p_playlist->status.i_view;
362         p_view = playlist_ViewFind( p_playlist, p_playlist->status.i_view );
363         if( p_view )
364         {
365             p_playlist->request.p_node = p_view->p_root;
366         }
367         else
368         {
369             p_playlist->request.p_node = NULL;
370         }
371         break;
372
373     case PLAYLIST_VIEWPLAY:
374         i_view = (int)va_arg( args,int );
375         p_node = (playlist_item_t *)va_arg( args, playlist_item_t * );
376         p_item = (playlist_item_t *)va_arg( args, playlist_item_t * );
377         if ( p_node == NULL ) //|| (p_item != NULL && p_item->input.psz_uri
378                                 //                         == NULL ))
379         {
380             p_playlist->status.i_status = PLAYLIST_STOPPED;
381             p_playlist->request.b_request = VLC_TRUE;
382             return VLC_SUCCESS;
383         }
384         p_playlist->status.i_status = PLAYLIST_RUNNING;
385         p_playlist->request.i_skip = 0;
386         p_playlist->request.b_request = VLC_TRUE;
387         p_playlist->request.i_view = i_view;
388         p_playlist->request.p_node = p_node;
389         p_playlist->request.p_item = p_item;
390
391         /* Don't go further if the node doesn't want to */
392         if( ! p_playlist->request.p_node->i_flags & PLAYLIST_SKIP_FLAG )
393         {
394             p_playlist->b_go_next = VLC_FALSE;
395         }
396         else
397         {
398             p_playlist->b_go_next = VLC_TRUE;
399         }
400         break;
401
402     case PLAYLIST_PLAY:
403         p_playlist->status.i_status = PLAYLIST_RUNNING;
404
405         if( p_playlist->p_input )
406         {
407             val.i_int = PLAYING_S;
408             var_Set( p_playlist->p_input, "state", val );
409             break;
410         }
411
412         /* FIXME : needed ? */
413         p_playlist->request.b_request = VLC_TRUE;
414         p_playlist->request.i_view = p_playlist->status.i_view;
415         p_playlist->request.p_node = p_playlist->status.p_node;
416         p_playlist->request.p_item = p_playlist->status.p_item;
417         p_playlist->request.i_skip = 0;
418         p_playlist->request.i_goto = -1;
419         break;
420
421     case PLAYLIST_AUTOPLAY:
422         p_playlist->status.i_status = PLAYLIST_RUNNING;
423         p_playlist->status.p_node = p_playlist->p_general;
424
425         p_playlist->request.b_request = VLC_FALSE;
426         break;
427
428     case PLAYLIST_PAUSE:
429         val.i_int = 0;
430         if( p_playlist->p_input )
431             var_Get( p_playlist->p_input, "state", &val );
432
433         if( val.i_int == PAUSE_S )
434         {
435             p_playlist->status.i_status = PLAYLIST_RUNNING;
436             if( p_playlist->p_input )
437             {
438                 val.i_int = PLAYING_S;
439                 var_Set( p_playlist->p_input, "state", val );
440             }
441         }
442         else
443         {
444             p_playlist->status.i_status = PLAYLIST_PAUSED;
445             if( p_playlist->p_input )
446             {
447                 val.i_int = PAUSE_S;
448                 var_Set( p_playlist->p_input, "state", val );
449             }
450         }
451         break;
452
453     case PLAYLIST_SKIP:
454         p_playlist->request.i_view = p_playlist->status.i_view;
455         if( p_playlist->status.i_view > -1 )
456         {
457             p_playlist->request.p_node = p_playlist->status.p_node;
458             p_playlist->request.p_item = p_playlist->status.p_item;
459         }
460         else
461         {
462             p_playlist->request.p_node = NULL;
463             p_playlist->request.p_item = NULL;
464         }
465         p_playlist->request.i_skip = (int) va_arg( args, int );
466         p_playlist->request.b_request = VLC_TRUE;
467         break;
468
469     case PLAYLIST_GOTO:
470         p_playlist->status.i_status = PLAYLIST_RUNNING;
471         p_playlist->request.p_node = NULL;
472         p_playlist->request.p_item = NULL;
473         p_playlist->request.i_view = -1;
474         p_playlist->request.i_goto = (int) va_arg( args, int );
475         p_playlist->request.b_request = VLC_TRUE;
476         break;
477
478     default:
479         msg_Err( p_playlist, "unknown playlist query" );
480         return VLC_EBADVAR;
481         break;
482     }
483
484     return VLC_SUCCESS;
485 }
486
487 int playlist_PreparseEnqueue( playlist_t *p_playlist,
488                               input_item_t *p_item )
489 {
490     vlc_mutex_lock( &p_playlist->p_preparse->object_lock );
491     INSERT_ELEM( p_playlist->p_preparse->pi_waiting,
492                  p_playlist->p_preparse->i_waiting,
493                  p_playlist->p_preparse->i_waiting,
494                  p_item->i_id );
495     vlc_mutex_unlock( &p_playlist->p_preparse->object_lock );
496     return VLC_SUCCESS;
497 }
498
499 /* Should only be called if playlist and preparser are locked */
500 void playlist_PreparseEnqueueItemSub( playlist_t *p_playlist,
501                                      playlist_item_t *p_item )
502 {
503     int i;
504     if( p_item->i_children == -1 )
505     {
506         INSERT_ELEM( p_playlist->p_preparse->pi_waiting,
507                      p_playlist->p_preparse->i_waiting,
508                      p_playlist->p_preparse->i_waiting,
509                      (p_item->input.i_id) );
510     }
511     else
512     {
513         for( i = 0; i < p_item->i_children; i++)
514         {
515             playlist_PreparseEnqueueItemSub( p_playlist,
516                                              p_item->pp_children[i] );
517         }
518     }
519 }
520
521 int playlist_PreparseEnqueueItem( playlist_t *p_playlist,
522                                   playlist_item_t *p_item )
523 {
524     vlc_mutex_lock( &p_playlist->object_lock );
525     vlc_mutex_lock( &p_playlist->p_preparse->object_lock );
526     playlist_PreparseEnqueueItemSub( p_playlist, p_item );
527     vlc_mutex_unlock( &p_playlist->p_preparse->object_lock );
528     vlc_mutex_unlock( &p_playlist->object_lock );
529     return VLC_SUCCESS;
530 }
531
532
533 /* Destroy remaining objects */
534 static mtime_t ObjectGarbageCollector( playlist_t *p_playlist, int i_type,
535                                        mtime_t destroy_date )
536 {
537     vlc_object_t *p_obj;
538
539     if( destroy_date > mdate() ) return destroy_date;
540
541     if( destroy_date == 0 )
542     {
543         /* give a little time */
544         return mdate() + I64C(1000000);
545     }
546     else
547     {
548         vlc_mutex_lock( &p_playlist->gc_lock );
549         while( ( p_obj = vlc_object_find( p_playlist, i_type, FIND_CHILD ) ) )
550         {
551             if( p_obj->p_parent != (vlc_object_t*)p_playlist )
552             {
553                 /* only first child (ie unused) */
554                 vlc_object_release( p_obj );
555                 break;
556             }
557             if( i_type == VLC_OBJECT_VOUT )
558             {
559                 msg_Dbg( p_playlist, "garbage collector destroying 1 vout" );
560                 vlc_object_detach( p_obj );
561                 vlc_object_release( p_obj );
562                 vout_Destroy( (vout_thread_t *)p_obj );
563             }
564             else if( i_type == VLC_OBJECT_SOUT )
565             {
566                 vlc_object_release( p_obj );
567                 sout_DeleteInstance( (sout_instance_t*)p_obj );
568             }
569         }
570         vlc_mutex_unlock( &p_playlist->gc_lock );
571         return 0;
572     }
573 }
574
575 /*****************************************************************************
576  * RunThread: main playlist thread
577  *****************************************************************************/
578 static void RunThread ( playlist_t *p_playlist )
579 {
580     vlc_object_t *p_obj;
581     playlist_item_t *p_item = NULL;
582
583     mtime_t    i_vout_destroyed_date = 0;
584     mtime_t    i_sout_destroyed_date = 0;
585
586     int i_loops = 0;
587
588     playlist_item_t *p_autodelete_item = NULL;
589
590     /* Tell above that we're ready */
591     vlc_thread_ready( p_playlist );
592
593     while( !p_playlist->b_die )
594     {
595         i_loops++;
596         if( p_playlist->p_interaction )
597         {
598             stats_TimerStart( p_playlist, "Interaction thread" );
599             intf_InteractionManage( p_playlist );
600             stats_TimerStop( p_playlist, "Interaction thread" );
601         }
602
603         if( i_loops %5 == 0 && p_playlist->p_stats )
604         {
605             stats_ComputeGlobalStats( p_playlist, p_playlist->p_stats );
606         }
607
608         vlc_mutex_lock( &p_playlist->object_lock );
609
610         /* First, check if we have something to do */
611         /* FIXME : this can be called several times */
612         if( p_playlist->request.b_request )
613         {
614 #ifdef PLAYLIST_PROFILE
615             msg_Dbg(p_playlist, "beginning processing of request, "
616                          I64Fi" us ", mdate() - p_playlist->request_date );
617 #endif
618             /* Stop the existing input */
619             if( p_playlist->p_input )
620             {
621                 input_StopThread( p_playlist->p_input );
622             }
623             /* The code below will start the next input for us */
624             if( p_playlist->status.i_status == PLAYLIST_STOPPED )
625             {
626                 p_playlist->request.b_request = VLC_FALSE;
627             }
628         }
629
630         /* If there is an input, check that it doesn't need to die. */
631         if( p_playlist->p_input )
632         {
633             if( i_loops % 5 == 0 )
634             {
635                 stats_ComputeInputStats( p_playlist->p_input,
636                                   p_playlist->p_input->input.p_item->p_stats );
637 //                stats_DumpInputStats(
638 //                            p_playlist->p_input->input.p_item->p_stats );
639             }
640
641             /* This input is dead. Remove it ! */
642             if( p_playlist->p_input->b_dead )
643             {
644                 input_thread_t *p_input;
645
646                 p_input = p_playlist->p_input;
647                 p_playlist->p_input = NULL;
648
649                 /* Release the playlist lock, because we may get stuck
650                  * in input_DestroyThread() for some time. */
651                 vlc_mutex_unlock( &p_playlist->object_lock );
652
653                 /* Destroy input */
654                 input_DestroyThread( p_input );
655
656                 /* Unlink current input
657                  * (_after_ input_DestroyThread for vout garbage collector) */
658                 vlc_object_detach( p_input );
659
660                 /* Destroy object */
661                 vlc_object_destroy( p_input );
662
663                 i_vout_destroyed_date = 0;
664                 i_sout_destroyed_date = 0;
665
666                 if( p_playlist->status.p_item->i_flags
667                     & PLAYLIST_REMOVE_FLAG )
668                 {
669                      playlist_ItemDelete( p_item );
670                      p_playlist->status.p_item = NULL;
671                 }
672
673                 continue;
674             }
675             /* This input is dying, let it do */
676             else if( p_playlist->p_input->b_die )
677             {
678                 ;
679             }
680             /* This input has finished, ask it to die ! */
681             else if( p_playlist->p_input->b_error
682                       || p_playlist->p_input->b_eof )
683             {
684                 /* TODO FIXME XXX TODO FIXME XXX */
685                 /* Check for autodeletion */
686
687                 if( p_playlist->status.p_item->i_flags & PLAYLIST_DEL_FLAG )
688                 {
689                     p_autodelete_item = p_playlist->status.p_item;
690                 }
691                 input_StopThread( p_playlist->p_input );
692                 /* Select the next playlist item */
693                 vlc_mutex_unlock( &p_playlist->object_lock );
694                 continue;
695             }
696             else if( p_playlist->p_input->i_state != INIT_S )
697             {
698                 vlc_mutex_unlock( &p_playlist->object_lock );
699                 i_vout_destroyed_date =
700                     ObjectGarbageCollector( p_playlist, VLC_OBJECT_VOUT,
701                                             i_vout_destroyed_date );
702                 i_sout_destroyed_date =
703                     ObjectGarbageCollector( p_playlist, VLC_OBJECT_SOUT,
704                                             i_sout_destroyed_date );
705                 vlc_mutex_lock( &p_playlist->object_lock );
706             }
707         }
708         else if( p_playlist->status.i_status != PLAYLIST_STOPPED )
709         {
710             /* Start another input.
711              * Get the next item to play */
712             stats_TimerStart( p_playlist, "Playlist walk" );
713             p_item = NextItem( p_playlist );
714             stats_TimerStop( p_playlist, "Playlist walk" );
715
716             /* We must stop */
717             if( p_item == NULL )
718             {
719                 if( p_autodelete_item )
720                 {
721                     playlist_Delete( p_playlist,
722                                      p_autodelete_item->input.i_id );
723                     p_autodelete_item = NULL;
724                 }
725                 p_playlist->status.i_status = PLAYLIST_STOPPED;
726                 vlc_mutex_unlock( &p_playlist->object_lock );
727                 continue;
728             }
729
730             PlayItem( p_playlist, p_item );
731
732             if( p_autodelete_item )
733             {
734                 playlist_Delete( p_playlist, p_autodelete_item->input.i_id );
735                 p_autodelete_item = NULL;
736             }
737         }
738         else if( p_playlist->status.i_status == PLAYLIST_STOPPED )
739         {
740             if( p_item && p_playlist->status.p_item &&
741                 p_playlist->status.p_item->i_flags & PLAYLIST_REMOVE_FLAG )
742             {
743                  playlist_ItemDelete( p_item );
744                  p_playlist->status.p_item = NULL;
745             }
746
747             /* Collect garbage */
748             vlc_mutex_unlock( &p_playlist->object_lock );
749             i_sout_destroyed_date =
750                 ObjectGarbageCollector( p_playlist, VLC_OBJECT_SOUT, mdate() );
751             i_vout_destroyed_date =
752                 ObjectGarbageCollector( p_playlist, VLC_OBJECT_VOUT, mdate() );
753             vlc_mutex_lock( &p_playlist->object_lock );
754         }
755         vlc_mutex_unlock( &p_playlist->object_lock );
756
757         msleep( INTF_IDLE_SLEEP / 2 );
758
759         /* Stop sleeping earlier if we have work */
760         /* TODO : statistics about this */
761         if ( p_playlist->request.b_request &&
762                         p_playlist->status.i_status == PLAYLIST_RUNNING )
763         {
764             continue;
765         }
766
767         msleep( INTF_IDLE_SLEEP / 2 );
768     }
769
770     /* Playlist dying */
771
772     /* If there is an input, kill it */
773     while( 1 )
774     {
775         vlc_mutex_lock( &p_playlist->object_lock );
776
777         if( p_playlist->p_input == NULL )
778         {
779             vlc_mutex_unlock( &p_playlist->object_lock );
780             break;
781         }
782
783         if( p_playlist->p_input->b_dead )
784         {
785             input_thread_t *p_input;
786
787             /* Unlink current input */
788             p_input = p_playlist->p_input;
789             p_playlist->p_input = NULL;
790             vlc_mutex_unlock( &p_playlist->object_lock );
791
792             /* Destroy input */
793             input_DestroyThread( p_input );
794             /* Unlink current input (_after_ input_DestroyThread for vout
795              * garbage collector)*/
796             vlc_object_detach( p_input );
797
798             /* Destroy object */
799             vlc_object_destroy( p_input );
800             continue;
801         }
802         else if( p_playlist->p_input->b_die )
803         {
804             /* This input is dying, leave it alone */
805             ;
806         }
807         else if( p_playlist->p_input->b_error || p_playlist->p_input->b_eof )
808         {
809             input_StopThread( p_playlist->p_input );
810             vlc_mutex_unlock( &p_playlist->object_lock );
811             continue;
812         }
813         else
814         {
815             p_playlist->p_input->b_eof = 1;
816         }
817
818         vlc_mutex_unlock( &p_playlist->object_lock );
819
820         msleep( INTF_IDLE_SLEEP );
821     }
822
823     /* close all remaining sout */
824     while( ( p_obj = vlc_object_find( p_playlist,
825                                       VLC_OBJECT_SOUT, FIND_CHILD ) ) )
826     {
827         vlc_object_release( p_obj );
828         sout_DeleteInstance( (sout_instance_t*)p_obj );
829     }
830
831     /* close all remaining vout */
832     while( ( p_obj = vlc_object_find( p_playlist,
833                                       VLC_OBJECT_VOUT, FIND_CHILD ) ) )
834     {
835         vlc_object_detach( p_obj );
836         vlc_object_release( p_obj );
837         vout_Destroy( (vout_thread_t *)p_obj );
838     }
839 }
840
841 /* Queue for items to preparse */
842 static void RunPreparse ( playlist_preparse_t *p_obj )
843 {
844     playlist_t *p_playlist = (playlist_t *)p_obj->p_parent;
845     vlc_bool_t b_sleep;
846
847     /* Tell above that we're ready */
848     vlc_thread_ready( p_obj );
849
850     while( !p_playlist->b_die )
851     {
852         vlc_mutex_lock( &p_obj->object_lock );
853
854         if( p_obj->i_waiting > 0 )
855         {
856             int i_current_id = p_obj->pi_waiting[0];
857             playlist_item_t *p_current;
858             REMOVE_ELEM( p_obj->pi_waiting, p_obj->i_waiting, 0 );
859             vlc_mutex_unlock( &p_obj->object_lock );
860             vlc_mutex_lock( &p_playlist->object_lock );
861
862             p_current = playlist_ItemGetById( p_playlist, i_current_id );
863             if( p_current )
864             {
865                 vlc_bool_t b_preparsed = VLC_FALSE;
866                 if( strncmp( p_current->input.psz_uri, "http:", 5 ) &&
867                     strncmp( p_current->input.psz_uri, "rtsp:", 5 ) &&
868                     strncmp( p_current->input.psz_uri, "udp:", 4 ) &&
869                     strncmp( p_current->input.psz_uri, "mms:", 4 ) &&
870                     strncmp( p_current->input.psz_uri, "cdda:", 4 ) &&
871                     strncmp( p_current->input.psz_uri, "dvd:", 4 ) &&
872                     strncmp( p_current->input.psz_uri, "v4l:", 4 ) &&
873                     strncmp( p_current->input.psz_uri, "dshow:", 6 ) )
874                 {
875                     b_preparsed = VLC_TRUE;
876                     stats_TimerStart( p_playlist, "Preparse run" );
877                     input_Preparse( p_playlist, &p_current->input );
878                     stats_TimerStop( p_playlist, "Preparse run" );
879                 }
880                 vlc_mutex_unlock( &p_playlist->object_lock );
881                 if( b_preparsed )
882                 {
883                     var_SetInteger( p_playlist, "item-change",
884                                     p_current->input.i_id );
885                 }
886             }
887             else
888                 vlc_mutex_unlock( &p_playlist->object_lock );
889             vlc_mutex_lock( &p_obj->object_lock );
890         }
891         b_sleep = ( p_obj->i_waiting == 0 );
892
893         vlc_mutex_unlock( &p_obj->object_lock );
894
895         if( p_obj->i_waiting == 0 )
896         {
897             msleep( INTF_IDLE_SLEEP );
898         }
899     }
900 }
901
902 /*****************************************************************************
903  * NextItem
904  *****************************************************************************
905  * This function calculates the next playlist item, depending
906  * on the playlist course mode (forward, backward, random, view,...).
907  *****************************************************************************/
908 static playlist_item_t * NextItem( playlist_t *p_playlist )
909 {
910     playlist_item_t *p_new = NULL;
911     int i_skip,i_goto,i, i_new, i_count ;
912     playlist_view_t *p_view;
913
914     vlc_bool_t b_loop = var_GetBool( p_playlist, "loop" );
915     vlc_bool_t b_random = var_GetBool( p_playlist, "random" );
916     vlc_bool_t b_repeat = var_GetBool( p_playlist, "repeat" );
917     vlc_bool_t b_playstop = var_GetBool( p_playlist, "play-and-stop" );
918
919 #ifdef PLAYLIST_PROFILE
920     /* Calculate time needed */
921     int64_t start = mdate();
922 #endif
923     /* Handle quickly a few special cases */
924
925     /* No items to play */
926     if( p_playlist->i_size == 0 )
927     {
928         msg_Info( p_playlist, "playlist is empty" );
929         return NULL;
930     }
931     /* Nothing requested */
932     if( !p_playlist->request.b_request && p_playlist->status.p_item == NULL )
933     {
934         msg_Dbg( p_playlist,"nothing requested, starting" );
935     }
936
937     /* Repeat and play/stop */
938     if( !p_playlist->request.b_request && b_repeat == VLC_TRUE &&
939          p_playlist->status.p_item )
940     {
941         msg_Dbg( p_playlist,"repeating item" );
942         return p_playlist->status.p_item;
943     }
944
945     if( !p_playlist->request.b_request && b_playstop == VLC_TRUE )
946     {
947         msg_Dbg( p_playlist,"stopping (play and stop)");
948         return NULL;
949     }
950
951     if( !p_playlist->request.b_request && p_playlist->status.p_item &&
952         !( p_playlist->status.p_item->i_flags & PLAYLIST_SKIP_FLAG ) )
953     {
954         msg_Dbg( p_playlist, "no-skip mode, stopping") ;
955         return NULL;
956     }
957
958     /* TODO: improve this (only use current node) */
959     /* TODO: use the "shuffled view" internally ? */
960     /* Random case. This is an exception: if request, but request is skip +- 1
961      * we don't go to next item but select a new random one. */
962     if( b_random &&
963         ( !p_playlist->request.b_request ||
964         ( p_playlist->request.b_request && ( p_playlist->request.p_item == NULL ||
965           p_playlist->request.i_skip == 1 || p_playlist->request.i_skip == -1 ) ) ) )
966     {
967         /* how many items to choose from ? */
968         i_count = 0;
969         for ( i = 0; i < p_playlist->i_size; i++ )
970         {
971             if ( p_playlist->pp_items[i]->i_nb_played == 0 )
972                 i_count++;
973         }
974         /* Nothing left? */
975         if ( i_count == 0 )
976         {
977             /* Don't loop? Exit! */
978             if( !b_loop )
979                 return NULL;
980             /* Otherwise reset the counter */
981             for ( i = 0; i < p_playlist->i_size; i++ )
982             {
983                 p_playlist->pp_items[i]->i_nb_played = 0;
984             }
985             i_count = p_playlist->i_size;
986         }
987         srand( (unsigned int)mdate() );
988         i = rand() % i_count + 1 ;
989         /* loop thru the list and count down the unplayed items to the selected one */
990         for ( i_new = 0; i_new < p_playlist->i_size && i > 0; i_new++ )
991         {
992             if ( p_playlist->pp_items[i_new]->i_nb_played == 0 )
993                 i--;
994         }
995         i_new--;
996
997         p_playlist->request.i_skip = 0;
998         p_playlist->request.b_request = VLC_FALSE;
999         return p_playlist->pp_items[i_new];
1000     }
1001
1002     /* Start the real work */
1003     if( p_playlist->request.b_request )
1004     {
1005 #ifdef PLAYLIST_DEBUG
1006         msg_Dbg( p_playlist,"processing request" );
1007 #endif
1008         /* We are not playing from a view */
1009         if( p_playlist->request.i_view == -1  )
1010         {
1011 #ifdef PLAYLIST_DEBUG
1012             msg_Dbg( p_playlist, "non-view mode request");
1013 #endif
1014             /* Directly select the item, just like now */
1015             p_new = p_playlist->request.p_item;
1016             i_skip = p_playlist->request.i_skip;
1017             i_goto = p_playlist->request.i_goto;
1018
1019             if( p_playlist->i_index < 0 ) p_playlist->i_index = 0;
1020             if ( p_new == NULL )
1021                 p_new = p_playlist->pp_items[p_playlist->i_index];
1022
1023             if( i_goto >= 0  && i_goto < p_playlist->i_size )
1024             {
1025                 p_playlist->i_index = i_goto;
1026                 p_new = p_playlist->pp_items[p_playlist->i_index];
1027                 p_playlist->request.i_goto = -1;
1028             }
1029
1030             if( i_skip != 0 )
1031             {
1032                 if( p_playlist->i_index + i_skip < p_playlist->i_size &&
1033                     p_playlist->i_index + i_skip >=  0 )
1034                 {
1035                     p_playlist->i_index += i_skip;
1036                     p_new = p_playlist->pp_items[p_playlist->i_index];
1037                 }
1038                 p_playlist->request.i_skip = 0;
1039             }
1040             if( !( p_new->i_flags & PLAYLIST_SKIP_FLAG ) )
1041             {
1042                 return NULL;
1043             }
1044         }
1045         else
1046         {
1047 #ifdef PLAYLIST_DEBUG
1048             msg_Dbg( p_playlist, "view mode request" );
1049 #endif
1050             p_new = p_playlist->request.p_item;
1051             i_skip = p_playlist->request.i_skip;
1052
1053             /* If we are asked for a node, take its first item */
1054             if( p_playlist->request.p_item == NULL && i_skip == 0 )
1055             {
1056                 i_skip++;
1057             }
1058
1059             p_view = playlist_ViewFind( p_playlist,p_playlist->request.i_view );
1060             p_playlist->status.p_node = p_playlist->request.p_node;
1061             p_playlist->status.i_view = p_playlist->request.i_view;
1062             if( !p_view )
1063             {
1064                 msg_Err( p_playlist, "p_view is NULL and should not! (requested view is %i", p_playlist->request.i_view );
1065             }
1066             else if( i_skip > 0 )
1067             {
1068                 for( i = i_skip; i > 0 ; i-- )
1069                 {
1070                     p_new = playlist_FindNextFromParent( p_playlist,
1071                                     p_playlist->request.i_view,
1072                                     p_view->p_root,
1073                                     p_playlist->request.p_node,
1074                                     p_new );
1075                     if( p_new == NULL )
1076                     {
1077 #ifdef PLAYLIST_DEBUG
1078                         msg_Dbg( p_playlist, "looping" );
1079 #endif
1080                         p_new = playlist_FindNextFromParent( p_playlist,
1081                                   p_playlist->request.i_view,
1082                                   p_view->p_root,
1083                                   p_view->p_root,
1084                                   NULL );
1085                         if( p_new == NULL ) break;
1086                     }
1087                 }
1088             }
1089             else if( i_skip < 0 )
1090             {
1091                 for( i = i_skip; i < 0 ; i++ )
1092                 {
1093                     p_new = playlist_FindPrevFromParent( p_playlist,
1094                                     p_playlist->request.i_view,
1095                                     p_view->p_root,
1096                                     p_playlist->request.p_node,
1097                                     p_new );
1098                     if( p_new == NULL )
1099                     {
1100                     /* We reach the beginning of the playlist.
1101                        Go back to the last item. */
1102                         p_new = playlist_RecursiveFindLast( p_playlist,
1103                                                             p_view->p_root );
1104                     }
1105                     if( p_new == NULL ) break;
1106                 }
1107
1108             }
1109         }
1110         /* Clear the request */
1111         p_playlist->request.b_request = VLC_FALSE;
1112     }
1113     /* "Automatic" item change ( next ) */
1114     else
1115     {
1116         p_playlist->request_date = 0;
1117
1118         if( p_playlist->status.i_view == -1 )
1119         {
1120 #ifdef PLAYLIST_DEBUG
1121         msg_Dbg( p_playlist, "no request - old mode" );
1122 #endif
1123             if( p_playlist->i_index + 1 < p_playlist->i_size )
1124             {
1125                 p_playlist->i_index++;
1126                 p_new = p_playlist->pp_items[p_playlist->i_index];
1127                 if( !( p_new->i_flags & PLAYLIST_SKIP_FLAG ) )
1128                 {
1129                     return NULL;
1130                 }
1131             }
1132             else
1133             {
1134                 if( b_loop && p_playlist->i_size > 0)
1135                 {
1136                     p_playlist->i_index = 0;
1137                     p_new = p_playlist->pp_items[0];
1138                 }
1139                 else
1140                     p_new = NULL;
1141             }
1142         }
1143         /* We are playing with a view */
1144         else
1145         {
1146 #ifdef PLAYLIST_DEBUG
1147             msg_Dbg( p_playlist,"no request - from a view" );
1148 #endif
1149             playlist_view_t *p_view =
1150                     playlist_ViewFind( p_playlist,
1151                                    p_playlist->status.i_view );
1152             if( !p_view )
1153             {
1154                 msg_Err( p_playlist, "p_view is NULL and should not! (FIXME)" );
1155             }
1156             else
1157             {
1158                 p_new = playlist_FindNextFromParent( p_playlist,
1159                             p_playlist->status.i_view,
1160                             p_view->p_root,
1161                             p_playlist->status.p_node,
1162                             p_playlist->status.p_item );
1163                 if( p_new == NULL && b_loop )
1164                 {
1165 #ifdef PLAYLIST_DEBUG
1166                     msg_Dbg( p_playlist, "looping" );
1167 #endif
1168                     p_new = playlist_FindNextFromParent( p_playlist,
1169                                    p_playlist->status.i_view,
1170                                    p_view->p_root,
1171                                    p_view->p_root,
1172                                    NULL );
1173                 }
1174                 if( p_new != NULL && !(p_new->i_flags & PLAYLIST_SKIP_FLAG) )
1175                     return NULL;
1176             }
1177         }
1178     }
1179
1180     /* Reset index */
1181     if( p_playlist->i_index >= 0 && p_new != NULL &&
1182             p_playlist->pp_items[p_playlist->i_index] != p_new )
1183     {
1184         p_playlist->i_index = playlist_GetPositionById( p_playlist,
1185                                                         p_new->input.i_id );
1186     }
1187
1188 #ifdef PLAYLIST_PROFILE
1189     msg_Dbg(p_playlist,"next item found in "I64Fi " us", mdate()-start );
1190 #endif
1191
1192     if( p_new == NULL )
1193     {
1194         msg_Info( p_playlist, "nothing to play" );
1195     }
1196
1197     return p_new;
1198 }
1199
1200 /*****************************************************************************
1201  * PlayItem: start the input thread for an item
1202  ****************************************************************************/
1203 static int PlayItem( playlist_t *p_playlist, playlist_item_t *p_item )
1204 {
1205     vlc_value_t val;
1206
1207     msg_Dbg( p_playlist, "creating new input thread" );
1208
1209     p_item->i_nb_played++;
1210     p_playlist->status.p_item = p_item;
1211
1212     p_playlist->i_index = playlist_GetPositionById( p_playlist,
1213                                                     p_item->input.i_id );
1214
1215 #ifdef PLAYLIST_PROFILE
1216     if( p_playlist->request_date != 0 )
1217     {
1218         msg_Dbg( p_playlist, "request processed after "I64Fi " us",
1219                   mdate() - p_playlist->request_date );
1220     }
1221 #endif
1222
1223     p_playlist->p_input = input_CreateThread( p_playlist, &p_item->input );
1224
1225     val.i_int = p_item->input.i_id;
1226     /* unlock the playlist to set the var...mmm */
1227     vlc_mutex_unlock( &p_playlist->object_lock);
1228     var_Set( p_playlist, "playlist-current", val);
1229     vlc_mutex_lock( &p_playlist->object_lock);
1230
1231     return VLC_SUCCESS;
1232
1233 }