]> git.sesse.net Git - vlc/blob - src/playlist/playlist.c
*experimental* input pre-parsing support.
[vlc] / src / playlist / playlist.c
1 /*****************************************************************************
2  * playlist.c : Playlist management functions
3  *****************************************************************************
4  * Copyright (C) 1999-2004 VideoLAN
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., 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() */
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 #define TITLE_CATEGORY N_( "By category" )
36 #define TITLE_SIMPLE   N_( "Manually added" )
37 #define TITLE_ALL      N_( "All items, unsorted" )
38
39 #define PLAYLIST_PROFILE 1
40 #undef PLAYLIST_DEBUG
41
42 /*****************************************************************************
43  * Local prototypes
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 * );
49
50 static int ItemChange( vlc_object_t *, const char *,
51                        vlc_value_t, vlc_value_t, void * );
52
53 int playlist_vaControl( playlist_t * p_playlist, int i_query, va_list args );
54
55
56 /**
57  * Create playlist
58  *
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
62  */
63 playlist_t * __playlist_Create ( vlc_object_t *p_parent )
64 {
65     playlist_t *p_playlist;
66     playlist_view_t *p_view;
67     vlc_value_t     val;
68
69     /* Allocate structure */
70     p_playlist = vlc_object_create( p_parent, VLC_OBJECT_PLAYLIST );
71     if( !p_playlist )
72     {
73         msg_Err( p_parent, "out of memory" );
74         return NULL;
75     }
76
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 );
81
82     var_Create( p_playlist, "item-change", VLC_VAR_INTEGER );
83     val.i_int = -1;
84     var_Set( p_playlist, "item-change", val );
85
86     var_Create( p_playlist, "item-append", VLC_VAR_ADDRESS );
87
88     var_Create( p_playlist, "playlist-current", VLC_VAR_INTEGER );
89     val.i_int = -1;
90     var_Set( p_playlist, "playlist-current", val );
91
92     var_Create( p_playlist, "intf-popupmenu", VLC_VAR_BOOL );
93
94     var_Create( p_playlist, "intf-show", VLC_VAR_BOOL );
95     val.b_bool = VLC_TRUE;
96     var_Set( p_playlist, "intf-show", val );
97
98
99     /* Variables to control playback */
100     var_CreateGetBool( p_playlist, "play-and-stop" );
101     var_CreateGetBool( p_playlist, "random" );
102     var_CreateGetBool( p_playlist, "repeat" );
103     var_CreateGetBool( p_playlist, "loop" );
104
105     /* Initialise data structures */
106     p_playlist->b_go_next = VLC_TRUE;
107     p_playlist->p_input = NULL;
108
109     p_playlist->request_date = 0;
110
111     p_playlist->i_views = 0;
112     p_playlist->pp_views = NULL;
113
114     p_playlist->i_index = -1;
115     p_playlist->i_size = 0;
116     p_playlist->pp_items = NULL;
117
118     playlist_ViewInsert( p_playlist, VIEW_CATEGORY, TITLE_CATEGORY );
119     playlist_ViewInsert( p_playlist, VIEW_SIMPLE, TITLE_SIMPLE );
120     playlist_ViewInsert( p_playlist, VIEW_ALL, TITLE_ALL );
121
122     p_view = playlist_ViewFind( p_playlist, VIEW_CATEGORY );
123
124     p_playlist->p_general = playlist_NodeCreate( p_playlist, VIEW_CATEGORY,
125                                         _( "General" ), p_view->p_root );
126
127     /* Set startup status
128      * We set to simple view on startup for interfaces that don't do
129      * anything */
130     p_view = playlist_ViewFind( p_playlist, VIEW_SIMPLE );
131     p_playlist->status.i_view = VIEW_SIMPLE;
132     p_playlist->status.p_item = NULL;
133     p_playlist->status.p_node = p_view->p_root;
134     p_playlist->request.b_request = VLC_FALSE;
135     p_playlist->status.i_status = PLAYLIST_STOPPED;
136
137
138     p_playlist->i_last_id = 0;
139     p_playlist->i_sort = SORT_ID;
140     p_playlist->i_order = ORDER_NORMAL;
141
142     /* Finally, launch the thread ! */
143     if( vlc_thread_create( p_playlist, "playlist", RunThread,
144                            VLC_THREAD_PRIORITY_LOW, VLC_TRUE ) )
145     {
146         msg_Err( p_playlist, "cannot spawn playlist thread" );
147         vlc_object_destroy( p_playlist );
148         return NULL;
149     }
150
151     /* Preparsing stuff */
152     p_playlist->p_preparse = vlc_object_create( p_playlist,
153                                                 sizeof( playlist_preparse_t ) );
154     if( !p_playlist->p_preparse )
155     {
156         msg_Err( p_playlist, "unable to create preparser" );
157         vlc_object_destroy( p_playlist );
158         return NULL;
159     }
160
161     p_playlist->p_preparse->i_waiting = 0;
162     p_playlist->p_preparse->pp_waiting = NULL;
163     vlc_mutex_init( p_playlist->p_preparse,
164                     &p_playlist->p_preparse->object_lock );
165
166     vlc_object_attach( p_playlist->p_preparse, p_playlist );
167     if( vlc_thread_create( p_playlist->p_preparse, "preparser",
168                            RunPreparse, VLC_THREAD_PRIORITY_LOW, VLC_TRUE ) )
169     {
170         msg_Err( p_playlist, "cannot spawn preparse thread" );
171         vlc_object_detach( p_playlist->p_preparse );
172         vlc_object_destroy( p_playlist->p_preparse );
173         return NULL;
174     }
175
176     /* The object has been initialized, now attach it */
177     vlc_object_attach( p_playlist, p_parent );
178
179     return p_playlist;
180 }
181
182 /**
183  * Destroy the playlist.
184  *
185  * Delete all items in the playlist and free the playlist structure.
186  * \param p_playlist the playlist structure to destroy
187  * \return VLC_SUCCESS or an error
188  */
189 int playlist_Destroy( playlist_t * p_playlist )
190 {
191     int i;
192     p_playlist->b_die = 1;
193
194     for( i = 0 ; i< p_playlist->i_sds ; i++ )
195     {
196         playlist_ServicesDiscoveryRemove( p_playlist,
197                                           p_playlist->pp_sds[i]->psz_module );
198     }
199
200     vlc_thread_join( p_playlist->p_preparse );
201     vlc_thread_join( p_playlist );
202
203     vlc_object_detach( p_playlist->p_preparse );
204
205     vlc_mutex_destroy( &p_playlist->p_preparse->object_lock );
206
207     var_Destroy( p_playlist, "intf-change" );
208     var_Destroy( p_playlist, "item-change" );
209     var_Destroy( p_playlist, "playlist-current" );
210     var_Destroy( p_playlist, "intf-popmenu" );
211     var_Destroy( p_playlist, "intf-show" );
212     var_Destroy( p_playlist, "play-and-stop" );
213     var_Destroy( p_playlist, "random" );
214     var_Destroy( p_playlist, "repeat" );
215     var_Destroy( p_playlist, "loop" );
216
217     playlist_Clear( p_playlist );
218
219     for( i = p_playlist->i_views - 1; i >= 0 ; i-- )
220     {
221         playlist_view_t *p_view = p_playlist->pp_views[i];
222         if( p_view->psz_name )
223             free( p_view->psz_name );
224         playlist_ItemDelete( p_view->p_root );
225         REMOVE_ELEM( p_playlist->pp_views, p_playlist->i_views, i );
226         free( p_view );
227     }
228
229     vlc_object_destroy( p_playlist->p_preparse );
230     vlc_object_destroy( p_playlist );
231
232     return VLC_SUCCESS;
233 }
234
235
236 /**
237  * Do a playlist action.
238  *
239  * If there is something in the playlist then you can do playlist actions.
240  *
241  * Playlist lock must not be taken when calling this function
242  *
243  * \param p_playlist the playlist to do the command on
244  * \param i_query the command to do
245  * \param variable number of arguments
246  * \return VLC_SUCCESS or an error
247  */
248 int playlist_Control( playlist_t * p_playlist, int i_query, ... )
249 {
250     va_list args;
251     int i_result;
252     va_start( args, i_query );
253     i_result = playlist_vaControl( p_playlist, i_query, args );
254     va_end( args );
255
256     return i_result;
257 }
258
259 int playlist_vaControl( playlist_t * p_playlist, int i_query, va_list args )
260 {
261     playlist_view_t *p_view;
262     vlc_value_t val;
263
264     vlc_mutex_lock( &p_playlist->object_lock );
265
266 #ifdef PLAYLIST_PROFILE
267     p_playlist->request_date = mdate();
268 #endif
269
270     if( p_playlist->i_size <= 0 )
271     {
272         vlc_mutex_unlock( &p_playlist->object_lock );
273         return VLC_EGENERIC;
274     }
275
276     switch( i_query )
277     {
278     case PLAYLIST_STOP:
279         p_playlist->status.i_status = PLAYLIST_STOPPED;
280         p_playlist->request.b_request = VLC_TRUE;
281         break;
282
283     case PLAYLIST_ITEMPLAY:
284         p_playlist->status.i_status = PLAYLIST_RUNNING;
285         p_playlist->request.i_skip = 0;
286         p_playlist->request.b_request = VLC_TRUE;
287         p_playlist->request.p_item = (playlist_item_t *)va_arg( args,
288                                                    playlist_item_t *);
289         p_playlist->request.i_view = p_playlist->status.i_view;
290         p_view = playlist_ViewFind( p_playlist, p_playlist->status.i_view );
291         if( p_view )
292         {
293             p_playlist->request.p_node = p_view->p_root;
294         }
295         else
296         {
297             p_playlist->request.p_node = NULL;
298         }
299         break;
300
301     case PLAYLIST_VIEWPLAY:
302         p_playlist->status.i_status = PLAYLIST_RUNNING;
303         p_playlist->request.i_skip = 0;
304         p_playlist->request.b_request = VLC_TRUE;
305         p_playlist->request.i_view = (int)va_arg( args,int );
306         p_playlist->request.p_node = (playlist_item_t *)va_arg( args,
307                                                         playlist_item_t *);
308         p_playlist->request.p_item = (playlist_item_t *)va_arg( args,
309                                                         playlist_item_t *);
310
311         /* If we select a node, play only it.
312          * If we select an item, continue */
313         if( p_playlist->request.p_item == NULL ||
314             ! p_playlist->request.p_node->i_flags & PLAYLIST_SKIP_FLAG )
315         {
316             p_playlist->b_go_next = VLC_FALSE;
317         }
318         else
319         {
320             p_playlist->b_go_next = VLC_TRUE;
321         }
322         break;
323
324     case PLAYLIST_PLAY:
325         p_playlist->status.i_status = PLAYLIST_RUNNING;
326
327         if( p_playlist->p_input )
328         {
329             val.i_int = PLAYING_S;
330             var_Set( p_playlist->p_input, "state", val );
331             break;
332         }
333
334         /* FIXME : needed ? */
335         p_playlist->request.b_request = VLC_TRUE;
336         p_playlist->request.i_view = p_playlist->status.i_view;
337         p_playlist->request.p_node = p_playlist->status.p_node;
338         p_playlist->request.p_item = p_playlist->status.p_item;
339         p_playlist->request.i_skip = 0;
340         p_playlist->request.i_goto = -1;
341         break;
342
343     case PLAYLIST_AUTOPLAY:
344         p_playlist->status.i_status = PLAYLIST_RUNNING;
345
346         p_playlist->request.b_request = VLC_FALSE;
347         break;
348
349     case PLAYLIST_PAUSE:
350         val.i_int = 0;
351         if( p_playlist->p_input )
352             var_Get( p_playlist->p_input, "state", &val );
353
354         if( val.i_int == PAUSE_S )
355         {
356             p_playlist->status.i_status = PLAYLIST_RUNNING;
357             if( p_playlist->p_input )
358             {
359                 val.i_int = PLAYING_S;
360                 var_Set( p_playlist->p_input, "state", val );
361             }
362         }
363         else
364         {
365             p_playlist->status.i_status = PLAYLIST_PAUSED;
366             if( p_playlist->p_input )
367             {
368                 val.i_int = PAUSE_S;
369                 var_Set( p_playlist->p_input, "state", val );
370             }
371         }
372         break;
373
374     case PLAYLIST_SKIP:
375         if( p_playlist->status.i_view > -1 )
376         {
377             p_playlist->request.p_node = p_playlist->status.p_node;
378             p_playlist->request.p_item = p_playlist->status.p_item;
379         }
380         p_playlist->request.i_skip = (int) va_arg( args, int );
381         p_playlist->request.b_request = VLC_TRUE;
382         break;
383
384     case PLAYLIST_GOTO:
385         p_playlist->status.i_status = PLAYLIST_RUNNING;
386         p_playlist->request.p_node = NULL;
387         p_playlist->request.p_item = NULL;
388         p_playlist->request.i_view = -1;
389         p_playlist->request.i_goto = (int) va_arg( args, int );
390         p_playlist->request.b_request = VLC_TRUE;
391         break;
392
393     default:
394         msg_Err( p_playlist, "unimplemented playlist query" );
395         return VLC_EBADVAR;
396         break;
397     }
398
399     vlc_mutex_unlock( &p_playlist->object_lock );
400     return VLC_SUCCESS;
401 }
402
403 int playlist_PreparseEnqueue( playlist_t *p_playlist,
404                               input_item_t *p_item )
405 {
406     vlc_mutex_lock( &p_playlist->p_preparse->object_lock );
407     INSERT_ELEM( p_playlist->p_preparse->pp_waiting,
408                  p_playlist->p_preparse->i_waiting,
409                  p_playlist->p_preparse->i_waiting,
410                  p_item );
411     vlc_mutex_unlock( &p_playlist->p_preparse->object_lock );
412     return VLC_SUCCESS;
413 }
414
415
416 /* Destroy remaining objects */
417 static mtime_t ObjectGarbageCollector( playlist_t *p_playlist, int i_type,
418                                        mtime_t destroy_date )
419 {
420     vlc_object_t *p_obj;
421
422     if( destroy_date > mdate() ) return destroy_date;
423
424     if( destroy_date == 0 )
425     {
426         /* give a little time */
427         return mdate() + I64C(1000000);
428     }
429     else
430     {
431         while( ( p_obj = vlc_object_find( p_playlist, i_type, FIND_CHILD ) ) )
432         {
433             if( p_obj->p_parent != (vlc_object_t*)p_playlist )
434             {
435                 /* only first child (ie unused) */
436                 vlc_object_release( p_obj );
437                 break;
438             }
439             if( i_type == VLC_OBJECT_VOUT )
440             {
441                 msg_Dbg( p_playlist, "garbage collector destroying 1 vout" );
442                 vlc_object_detach( p_obj );
443                 vlc_object_release( p_obj );
444                 vout_Destroy( (vout_thread_t *)p_obj );
445             }
446             else if( i_type == VLC_OBJECT_SOUT )
447             {
448                 vlc_object_release( p_obj );
449                 sout_DeleteInstance( (sout_instance_t*)p_obj );
450             }
451         }
452         return 0;
453     }
454 }
455
456 /*****************************************************************************
457  * RunThread: main playlist thread
458  *****************************************************************************/
459 static void RunThread ( playlist_t *p_playlist )
460 {
461     vlc_object_t *p_obj;
462     playlist_item_t *p_item;
463
464     mtime_t    i_vout_destroyed_date = 0;
465     mtime_t    i_sout_destroyed_date = 0;
466
467     playlist_item_t *p_autodelete_item = NULL;
468
469     /* Tell above that we're ready */
470     vlc_thread_ready( p_playlist );
471
472     while( !p_playlist->b_die )
473     {
474         vlc_mutex_lock( &p_playlist->object_lock );
475
476         /* First, check if we have something to do */
477         /* FIXME : this can be called several times */
478         if( p_playlist->request.b_request )
479         {
480 #ifdef PLAYLIST_PROFILE
481             msg_Dbg(p_playlist, "beginning processing of request, "
482                          I64Fi" us ", mdate() - p_playlist->request_date );
483 #endif
484             /* Stop the existing input */
485             if( p_playlist->p_input )
486             {
487                 input_StopThread( p_playlist->p_input );
488             }
489             /* The code below will start the next input for us */
490             if( p_playlist->status.i_status == PLAYLIST_STOPPED )
491             {
492                 p_playlist->request.b_request = VLC_FALSE;
493             }
494         }
495
496         /* If there is an input, check that it doesn't need to die. */
497         if( p_playlist->p_input )
498         {
499             /* This input is dead. Remove it ! */
500             if( p_playlist->p_input->b_dead )
501             {
502                 input_thread_t *p_input;
503
504                 p_input = p_playlist->p_input;
505                 p_playlist->p_input = NULL;
506
507                 /* Release the playlist lock, because we may get stuck
508                  * in input_DestroyThread() for some time. */
509                 vlc_mutex_unlock( &p_playlist->object_lock );
510
511                 /* Destroy input */
512                 input_DestroyThread( p_input );
513
514                 /* Unlink current input
515                  * (_after_ input_DestroyThread for vout garbage collector) */
516                 vlc_object_detach( p_input );
517
518                 /* Destroy object */
519                 vlc_object_destroy( p_input );
520
521                 i_vout_destroyed_date = 0;
522                 i_sout_destroyed_date = 0;
523
524                 continue;
525             }
526             /* This input is dying, let him do */
527             else if( p_playlist->p_input->b_die )
528             {
529                 ;
530             }
531             /* This input has finished, ask him to die ! */
532             else if( p_playlist->p_input->b_error
533                       || p_playlist->p_input->b_eof )
534             {
535                 /* TODO FIXME XXX TODO FIXME XXX */
536                 /* Check for autodeletion */
537
538                 if( p_playlist->status.p_item->i_flags & PLAYLIST_DEL_FLAG )
539                 {
540                     p_autodelete_item = p_playlist->status.p_item;
541                 }
542                 input_StopThread( p_playlist->p_input );
543                 /* Select the next playlist item */
544                 vlc_mutex_unlock( &p_playlist->object_lock );
545                 continue;
546             }
547             else if( p_playlist->p_input->i_state != INIT_S )
548             {
549                 vlc_mutex_unlock( &p_playlist->object_lock );
550                 i_vout_destroyed_date =
551                     ObjectGarbageCollector( p_playlist, VLC_OBJECT_VOUT,
552                                             i_vout_destroyed_date );
553                 i_sout_destroyed_date =
554                     ObjectGarbageCollector( p_playlist, VLC_OBJECT_SOUT,
555                                             i_sout_destroyed_date );
556                 vlc_mutex_lock( &p_playlist->object_lock );
557             }
558         }
559         else if( p_playlist->status.i_status != PLAYLIST_STOPPED )
560         {
561             /* Start another input.
562              * Get the next item to play */
563             p_item = NextItem( p_playlist );
564
565
566             /* We must stop */
567             if( p_item == NULL )
568             {
569                 if( p_autodelete_item )
570                 {
571                     vlc_mutex_unlock( &p_playlist->object_lock );
572                     playlist_Delete( p_playlist,
573                                      p_autodelete_item->input.i_id );
574                     vlc_mutex_lock( &p_playlist->object_lock );
575                     p_autodelete_item = NULL;
576                 }
577                 p_playlist->status.i_status = PLAYLIST_STOPPED;
578                 vlc_mutex_unlock( &p_playlist->object_lock );
579                 continue;
580             }
581
582             PlayItem( p_playlist, p_item );
583
584             if( p_autodelete_item )
585             {
586                 vlc_mutex_unlock( &p_playlist->object_lock );
587                 playlist_Delete( p_playlist, p_autodelete_item->input.i_id );
588                 vlc_mutex_lock( &p_playlist->object_lock );
589                 p_autodelete_item = NULL;
590             }
591         }
592         else if( p_playlist->status.i_status == PLAYLIST_STOPPED )
593         {
594             /* Collect garbage */
595             vlc_mutex_unlock( &p_playlist->object_lock );
596             i_sout_destroyed_date =
597                 ObjectGarbageCollector( p_playlist, VLC_OBJECT_SOUT, mdate() );
598             i_vout_destroyed_date =
599                 ObjectGarbageCollector( p_playlist, VLC_OBJECT_VOUT, mdate() );
600             vlc_mutex_lock( &p_playlist->object_lock );
601         }
602         vlc_mutex_unlock( &p_playlist->object_lock );
603
604         msleep( INTF_IDLE_SLEEP / 2 );
605
606         /* Stop sleeping earlier if we have work */
607         /* TODO : statistics about this */
608         if ( p_playlist->request.b_request &&
609                         p_playlist->status.i_status == PLAYLIST_RUNNING )
610         {
611             continue;
612         }
613
614         msleep( INTF_IDLE_SLEEP / 2 );
615     }
616
617     /* Playlist dying */
618
619     /* If there is an input, kill it */
620     while( 1 )
621     {
622         vlc_mutex_lock( &p_playlist->object_lock );
623
624         if( p_playlist->p_input == NULL )
625         {
626             vlc_mutex_unlock( &p_playlist->object_lock );
627             break;
628         }
629
630         if( p_playlist->p_input->b_dead )
631         {
632             input_thread_t *p_input;
633
634             /* Unlink current input */
635             p_input = p_playlist->p_input;
636             p_playlist->p_input = NULL;
637             vlc_mutex_unlock( &p_playlist->object_lock );
638
639             /* Destroy input */
640             input_DestroyThread( p_input );
641             /* Unlink current input (_after_ input_DestroyThread for vout
642              * garbage collector)*/
643             vlc_object_detach( p_input );
644
645             /* Destroy object */
646             vlc_object_destroy( p_input );
647             continue;
648         }
649         else if( p_playlist->p_input->b_die )
650         {
651             /* This input is dying, leave him alone */
652             ;
653         }
654         else if( p_playlist->p_input->b_error || p_playlist->p_input->b_eof )
655         {
656             input_StopThread( p_playlist->p_input );
657             vlc_mutex_unlock( &p_playlist->object_lock );
658             continue;
659         }
660         else
661         {
662             p_playlist->p_input->b_eof = 1;
663         }
664
665         vlc_mutex_unlock( &p_playlist->object_lock );
666
667         msleep( INTF_IDLE_SLEEP );
668     }
669
670     /* close all remaining sout */
671     while( ( p_obj = vlc_object_find( p_playlist,
672                                       VLC_OBJECT_SOUT, FIND_CHILD ) ) )
673     {
674         vlc_object_release( p_obj );
675         sout_DeleteInstance( (sout_instance_t*)p_obj );
676     }
677
678     /* close all remaining vout */
679     while( ( p_obj = vlc_object_find( p_playlist,
680                                       VLC_OBJECT_VOUT, FIND_CHILD ) ) )
681     {
682         vlc_object_detach( p_obj );
683         vlc_object_release( p_obj );
684         vout_Destroy( (vout_thread_t *)p_obj );
685     }
686 }
687
688 /* Queue for items to preparse */
689 static void RunPreparse ( playlist_preparse_t *p_obj )
690 {
691     playlist_t *p_playlist = p_obj->p_parent;
692     vlc_bool_t b_sleep;
693
694     /* Tell above that we're ready */
695     vlc_thread_ready( p_obj );
696
697     while( !p_playlist->b_die )
698     {
699         vlc_mutex_lock( &p_obj->object_lock );
700
701         if( p_obj->i_waiting > 0 )
702         {
703             input_Preparse( p_playlist, p_obj->pp_waiting[0] );
704             var_SetInteger( p_playlist, "item-change",
705                             p_obj->pp_waiting[0]->i_id );
706             REMOVE_ELEM( p_obj->pp_waiting, p_obj->i_waiting, 0 );
707         }
708         b_sleep = ( p_obj->i_waiting == 0 );
709
710         vlc_mutex_unlock( &p_obj->object_lock );
711
712         if( p_obj->i_waiting == 0 )
713         {
714             msleep( INTF_IDLE_SLEEP );
715         }
716     }
717 }
718
719 /*****************************************************************************
720  * NextItem
721  *****************************************************************************
722  * This function calculates the next playlist item, depending
723  * on the playlist course mode (forward, backward, random, view,...).
724  *****************************************************************************/
725 static playlist_item_t * NextItem( playlist_t *p_playlist )
726 {
727     playlist_item_t *p_new = NULL;
728     int i_skip,i_goto,i, i_new, i_count ;
729     playlist_view_t *p_view;
730
731     vlc_bool_t b_loop = var_GetBool( p_playlist, "loop");
732     vlc_bool_t b_random = var_GetBool( p_playlist, "random" );
733     vlc_bool_t b_repeat = var_GetBool( p_playlist, "repeat" );
734     vlc_bool_t b_playstop = var_GetBool( p_playlist, "play-and-stop" );
735
736 #ifdef PLAYLIST_PROFILE
737     /* Calculate time needed */
738     int64_t start = mdate();
739 #endif
740
741
742     /* Handle quickly a few special cases */
743
744     /* No items to play */
745     if( p_playlist->i_size == 0 )
746     {
747         msg_Info( p_playlist, "playlist is empty" );
748         return NULL;
749     }
750     /* Nothing requested */
751     if( !p_playlist->request.b_request && p_playlist->status.p_item == NULL )
752     {
753         msg_Dbg( p_playlist,"nothing requested, starting" );
754     }
755
756     /* Repeat and play/stop */
757     if( !p_playlist->request.b_request && b_repeat == VLC_TRUE )
758     {
759         msg_Dbg( p_playlist,"repeating item" );
760         return p_playlist->status.p_item;
761     }
762
763     if( !p_playlist->request.b_request && b_playstop == VLC_TRUE )
764     {
765         msg_Dbg( p_playlist,"stopping (play and stop)");
766         return NULL;
767     }
768
769     if( !p_playlist->request.b_request && p_playlist->status.p_item && 
770         !(p_playlist->status.p_item->i_flags & PLAYLIST_SKIP_FLAG) )
771     {
772         msg_Dbg( p_playlist, "no-skip mode, stopping") ;
773         return NULL;
774     }
775
776     /* TODO: improve this (only use current node) */
777     /* TODO: use the "shuffled view" internally ? */
778     /* Random case. This is an exception: if request, but request is skip +- 1
779      * we don't go to next item but select a new random one. */
780     if( b_random && (!p_playlist->request.b_request ||
781         p_playlist->request.i_skip == 1 || p_playlist->request.i_skip == -1 ) )
782     {
783         srand( (unsigned int)mdate() );
784         i_new = 0;
785         for( i_count = 0; i_count < p_playlist->i_size - 1 ; i_count ++ )
786         {
787             i_new =
788                 (int)((float)p_playlist->i_size * rand() / (RAND_MAX+1.0));
789             /* Check if the item has not already been played */
790             if( p_playlist->pp_items[i_new]->i_nb_played == 0 )
791                 break;
792         }
793         if( i_count == p_playlist->i_size )
794         {
795             /* The whole playlist has been played: reset the counters */
796             while( i_count > 0 )
797             {
798                 p_playlist->pp_items[--i_count]->i_nb_played = 0;
799             }
800            if( !b_loop )
801             {
802                 return NULL;
803             }
804         }
805         p_playlist->request.i_skip = 0;
806         p_playlist->request.b_request = VLC_FALSE;
807         return p_playlist->pp_items[i_new];
808    }
809
810     /* Start the real work */
811     if( p_playlist->request.b_request )
812     {
813         msg_Dbg( p_playlist,"processing request" );
814         /* We are not playing from a view */
815         if(  p_playlist->request.i_view == -1  )
816         {
817 #ifdef PLAYLIST_DEBUG
818             msg_Dbg( p_playlist, "non-view mode request");
819 #endif
820             /* Directly select the item, just like now */
821             i_skip = p_playlist->request.i_skip;
822             i_goto = p_playlist->request.i_goto;
823
824             if( p_playlist->i_index == -1 ) p_playlist->i_index = 0;
825             p_new = p_playlist->pp_items[p_playlist->i_index];
826
827             if( i_goto >= 0  && i_goto < p_playlist->i_size )
828             {
829                 p_playlist->i_index = i_goto;
830                 p_new = p_playlist->pp_items[p_playlist->i_index];
831                 p_playlist->request.i_goto = -1;
832             }
833
834             if( i_skip != 0 )
835             {
836                 if( p_playlist->i_index + i_skip < p_playlist->i_size &&
837                     p_playlist->i_index + i_skip >=  0 )
838                 {
839                     p_playlist->i_index += i_skip;
840                     p_new = p_playlist->pp_items[p_playlist->i_index];
841                 }
842             }
843         }
844         else
845         {
846 #ifdef PLAYLIST_DEBUG
847             msg_Dbg( p_playlist, "view mode request" );
848 #endif
849             p_new = p_playlist->request.p_item;
850             i_skip = p_playlist->request.i_skip;
851
852             /* If we are asked for a node, take its first item */
853             if( p_playlist->request.p_item == NULL && i_skip == 0 )
854             {
855                 i_skip++;
856             }
857
858             p_view = playlist_ViewFind( p_playlist,p_playlist->request.i_view );
859             p_playlist->status.p_node = p_playlist->request.p_node;
860             p_playlist->status.i_view = p_playlist->request.i_view;
861             if( i_skip > 0 )
862             {
863                 for( i = i_skip; i > 0 ; i-- )
864                 {
865                     p_new = playlist_FindNextFromParent( p_playlist,
866                                     p_playlist->request.i_view,
867                                     p_view->p_root,
868                                     p_playlist->request.p_node,
869                                     p_new );
870                     if( p_new == NULL ) break;
871                 }
872             }
873             else if( i_skip < 0 )
874             {
875                 for( i = i_skip; i < 0 ; i++ )
876                 {
877                     p_new = playlist_FindPrevFromParent( p_playlist,
878                                     p_playlist->request.i_view,
879                                     p_view->p_root,
880                                     p_playlist->request.p_node,
881                                     p_new );
882                     if( p_new == NULL ) break;
883                 }
884
885             }
886         }
887         /* Clear the request */
888         p_playlist->request.b_request = VLC_FALSE;
889     }
890     /* "Automatic" item change ( next ) */
891     else
892     {
893         p_playlist->request_date = 0;
894
895         if( p_playlist->status.i_view == -1 )
896         {
897             if( p_playlist->i_index + 1 < p_playlist->i_size )
898             {
899                 p_playlist->i_index++;
900                 p_new = p_playlist->pp_items[p_playlist->i_index];
901                 if( !(p_new->i_flags & PLAYLIST_SKIP_FLAG) )
902                 {
903                     return NULL;
904                 }
905             }
906             else
907             {
908                 msg_Dbg( p_playlist,"finished" );
909                 p_new = NULL;
910             }
911         }
912         /* We are playing with a view */
913         else
914         {
915             playlist_view_t *p_view =
916                     playlist_ViewFind( p_playlist,
917                                    p_playlist->status.i_view );
918             fprintf(stderr,"Finding next of %s within %s, view %i\n",
919                            p_playlist->status.p_item ?     p_playlist->status.p_item->input.psz_name : "coincoin",
920                                 p_playlist->status.p_node->input.psz_name,
921                                 p_playlist->status.i_view) ;
922             p_new = playlist_FindNextFromParent( p_playlist,
923                             p_playlist->status.i_view,
924                             p_view->p_root,
925                             p_playlist->status.p_node,
926                             p_playlist->status.p_item );
927         }
928     }
929
930     /* Reset index */
931     if( p_playlist->i_index >= 0 && p_new != NULL &&
932             p_playlist->pp_items[p_playlist->i_index] != p_new )
933     {
934         p_playlist->i_index = playlist_GetPositionById( p_playlist,
935                                                         p_new->input.i_id );
936     }
937
938 #ifdef PLAYLIST_PROFILE
939     msg_Dbg(p_playlist,"next item found in "I64Fi " us", mdate()-start );
940 #endif
941
942     if( p_new == NULL ) { msg_Info( p_playlist, "Nothing to play" ); }
943
944     return p_new;
945 }
946
947
948 #if 0
949
950
951 static void SkipItem( playlist_t *p_playlist, int i_arg )
952 {
953     int i_oldindex = p_playlist->i_index;
954     vlc_bool_t b_random, b_repeat, b_loop;
955     vlc_value_t val;
956     int i_count;
957
958     /* Increment */
959     else if( !b_repeat )
960     {
961         p_playlist->i_index += i_arg;
962     }
963
964     /* Boundary check */
965     if( p_playlist->i_index >= p_playlist->i_size )
966     {
967         if( p_playlist->i_status == PLAYLIST_STOPPED || b_random || b_loop )
968         {
969             p_playlist->i_index -= p_playlist->i_size
970                          * ( p_playlist->i_index / p_playlist->i_size );
971         }
972         else
973         {
974             /* Don't loop by default: stop at playlist end */
975             p_playlist->i_index = i_oldindex;
976             p_playlist->i_status = PLAYLIST_STOPPED;
977         }
978     }
979     else if( p_playlist->i_index < 0 )
980     {
981         p_playlist->i_index = p_playlist->i_size - 1;
982     }
983
984     /* Check that the item is enabled */
985     if( p_playlist->pp_items[p_playlist->i_index]->b_enabled == VLC_FALSE &&
986         p_playlist->i_enabled != 0)
987     {
988         SkipItem( p_playlist , 1 );
989     }
990 }
991
992 #endif
993
994 /*****************************************************************************
995  * PlayItem: start the input thread for an item
996  ****************************************************************************/
997 static int PlayItem( playlist_t *p_playlist, playlist_item_t *p_item )
998 {
999     vlc_value_t val;
1000
1001     msg_Dbg( p_playlist, "creating new input thread" );
1002
1003     p_item->i_nb_played++;
1004     p_playlist->status.p_item = p_item;
1005
1006     p_playlist->i_index = playlist_GetPositionById( p_playlist,
1007                                                     p_item->input.i_id );
1008
1009 #ifdef PLAYLIST_PROFILE
1010     if( p_playlist->request_date != 0 )
1011     {
1012         msg_Dbg( p_playlist, "request processed after "I64Fi " us",
1013                   mdate() - p_playlist->request_date );
1014     }
1015 #endif
1016
1017     p_playlist->p_input = input_CreateThread( p_playlist, &p_item->input );
1018
1019     var_AddCallback( p_playlist->p_input, "item-change",
1020                          ItemChange, p_playlist );
1021
1022     val.i_int = p_item->input.i_id;
1023     /* unlock the playlist to set the var...mmm */
1024     vlc_mutex_unlock( &p_playlist->object_lock);
1025     var_Set( p_playlist, "playlist-current", val);
1026     vlc_mutex_lock( &p_playlist->object_lock);
1027
1028     return VLC_SUCCESS;
1029
1030 }
1031
1032 /* Forward item change from input */
1033 static int ItemChange( vlc_object_t *p_obj, const char *psz_var,
1034                        vlc_value_t oldval, vlc_value_t newval, void *param )
1035 {
1036     playlist_t *p_playlist = (playlist_t *)param;
1037     int i_index;
1038
1039     //p_playlist->b_need_update = VLC_TRUE;
1040     var_SetInteger( p_playlist, "item-change", newval.i_int );
1041
1042     /* Update view */
1043     /* FIXME: Make that automatic */
1044     playlist_ViewUpdate( p_playlist, VIEW_S_AUTHOR );
1045
1046     return VLC_SUCCESS;
1047 }