]> git.sesse.net Git - vlc/blob - src/playlist/engine.c
Remove debug not intended to be committed, however feel free to use it
[vlc] / src / playlist / engine.c
1 /*****************************************************************************
2  * engine.c : Run the playlist and handle its control
3  *****************************************************************************
4  * Copyright (C) 1999-2007 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
25 #include <vlc/vlc.h>
26 #include <vlc_vout.h>
27 #include <vlc_sout.h>
28 #include <vlc_playlist.h>
29 #include <vlc_interface.h>
30 #include "playlist_internal.h"
31 #include "stream_output/stream_output.h" /* sout_DeleteInstance */
32
33 /*****************************************************************************
34  * Local prototypes
35  *****************************************************************************/
36 static void VariablesInit( playlist_t *p_playlist );
37
38 static int RandomCallback( vlc_object_t *p_this, char const *psz_cmd,
39                            vlc_value_t oldval, vlc_value_t newval, void *a )
40 {
41     (void)psz_cmd; (void)oldval; (void)newval; (void)a;
42
43     ((playlist_t*)p_this)->b_reset_currently_playing = VLC_TRUE;
44     playlist_Signal( ((playlist_t*)p_this) );
45     return VLC_SUCCESS;
46 }
47
48 /**
49  * Create playlist
50  *
51  * Create a playlist structure.
52  * \param p_parent the vlc object that is to be the parent of this playlist
53  * \return a pointer to the created playlist, or NULL on error
54  */
55 playlist_t * playlist_Create( vlc_object_t *p_parent )
56 {
57     playlist_t *p_playlist;
58     vlc_bool_t b_save;
59     int i_tree;
60
61     /* Allocate structure */
62     p_playlist = vlc_object_create( p_parent, VLC_OBJECT_PLAYLIST );
63     if( !p_playlist )
64     {
65         msg_Err( p_parent, "out of memory" );
66         return NULL;
67     }
68
69     p_playlist->p_internal = malloc(sizeof(struct playlist_internal_t));
70     memset( p_playlist->p_internal, 0, sizeof(struct playlist_internal_t) );
71
72     p_parent->p_libvlc->p_playlist = p_playlist;
73
74     VariablesInit( p_playlist );
75
76     /* Initialise data structures */
77     vlc_mutex_init( p_playlist, &p_playlist->gc_lock );
78     p_playlist->i_last_playlist_id = 0;
79     p_playlist->i_last_input_id = 0;
80     p_playlist->p_input = NULL;
81
82     p_playlist->gc_date = 0;
83     p_playlist->b_cant_sleep = VLC_FALSE;
84
85     ARRAY_INIT( p_playlist->items );
86     ARRAY_INIT( p_playlist->all_items );
87     ARRAY_INIT( p_playlist->input_items );
88     ARRAY_INIT( p_playlist->current );
89
90     p_playlist->i_current_index = 0;
91     p_playlist->b_reset_currently_playing = VLC_TRUE;
92     p_playlist->last_rebuild_date = 0;
93
94     i_tree = var_CreateGetBool( p_playlist, "playlist-tree" );
95     p_playlist->b_always_tree = (i_tree == 1);
96     p_playlist->b_never_tree = (i_tree == 2);
97
98     p_playlist->b_doing_ml = VLC_FALSE;
99
100     p_playlist->b_auto_preparse =
101                         var_CreateGetBool( p_playlist, "auto-preparse") ;
102
103     p_playlist->p_root_category = playlist_NodeCreate( p_playlist, NULL, NULL,
104                                                        0 );
105     p_playlist->p_root_onelevel = playlist_NodeCreate( p_playlist, NULL, NULL,
106                                                        0 );
107
108     if( !p_playlist->p_root_category || !p_playlist->p_root_onelevel )
109         return NULL;
110
111     /* Create playlist and media library */
112     p_playlist->p_local_category = playlist_NodeCreate( p_playlist,
113                               _( "Playlist" ),p_playlist->p_root_category, 0 );
114     p_playlist->p_local_onelevel =  playlist_NodeCreate( p_playlist,
115                               _( "Playlist" ), p_playlist->p_root_onelevel, 0 );
116     p_playlist->p_local_category->i_flags |= PLAYLIST_RO_FLAG;
117     p_playlist->p_local_onelevel->i_flags |= PLAYLIST_RO_FLAG;
118
119     if( !p_playlist->p_local_category || !p_playlist->p_local_onelevel ||
120         !p_playlist->p_local_category->p_input ||
121         !p_playlist->p_local_onelevel->p_input )
122         return NULL;
123
124     /* Link the nodes together. Todo: actually create them from the same input*/
125     p_playlist->p_local_onelevel->p_input->i_id =
126         p_playlist->p_local_category->p_input->i_id;
127
128     if( config_GetInt( p_playlist, "media-library") )
129     {
130         p_playlist->p_ml_category =   playlist_NodeCreate( p_playlist,
131                          _( "Media Library" ), p_playlist->p_root_category, 0 );
132         p_playlist->p_ml_onelevel =  playlist_NodeCreate( p_playlist,
133                          _( "Media Library" ), p_playlist->p_root_onelevel, 0 );
134
135         if(!p_playlist->p_ml_category || !p_playlist->p_ml_onelevel)
136             return NULL;
137
138         p_playlist->p_ml_category->i_flags |= PLAYLIST_RO_FLAG;
139         p_playlist->p_ml_onelevel->i_flags |= PLAYLIST_RO_FLAG;
140         p_playlist->p_ml_onelevel->p_input->i_id =
141              p_playlist->p_ml_category->p_input->i_id;
142
143     }
144     else
145     {
146         p_playlist->p_ml_category = p_playlist->p_ml_onelevel = NULL;
147     }
148
149     /* Initial status */
150     p_playlist->status.p_item = NULL;
151     p_playlist->status.p_node = p_playlist->p_local_onelevel;
152     p_playlist->request.b_request = VLC_FALSE;
153     p_playlist->status.i_status = PLAYLIST_STOPPED;
154
155     p_playlist->i_sort = SORT_ID;
156     p_playlist->i_order = ORDER_NORMAL;
157
158     b_save = p_playlist->b_auto_preparse;
159     p_playlist->b_auto_preparse = VLC_FALSE;
160     playlist_MLLoad( p_playlist );
161     p_playlist->b_auto_preparse = VLC_TRUE;
162     return p_playlist;
163 }
164
165 void playlist_Destroy( playlist_t *p_playlist )
166 {
167     var_Destroy( p_playlist, "intf-change" );
168     var_Destroy( p_playlist, "item-change" );
169     var_Destroy( p_playlist, "playlist-current" );
170     var_Destroy( p_playlist, "intf-popmenu" );
171     var_Destroy( p_playlist, "intf-show" );
172     var_Destroy( p_playlist, "play-and-stop" );
173     var_Destroy( p_playlist, "play-and-exit" );
174     var_Destroy( p_playlist, "random" );
175     var_Destroy( p_playlist, "repeat" );
176     var_Destroy( p_playlist, "loop" );
177     var_Destroy( p_playlist, "activity" );
178
179     vlc_mutex_destroy( &p_playlist->gc_lock );
180     vlc_object_detach( p_playlist );
181     free( p_playlist->p_internal );
182     vlc_object_destroy( p_playlist );
183 }
184
185 /* Destroy remaining objects */
186 static void ObjectGarbageCollector( playlist_t *p_playlist, vlc_bool_t b_force )
187 {
188     vlc_object_t *p_obj;
189
190     if( !b_force )
191     {
192         if( mdate() - p_playlist->gc_date < 1000000 )
193         {
194             p_playlist->b_cant_sleep = VLC_TRUE;
195             return;
196         }
197         else if( p_playlist->gc_date == 0 )
198             return;
199     }
200
201     vlc_mutex_lock( &p_playlist->gc_lock );
202     while( ( p_obj = vlc_object_find( p_playlist, VLC_OBJECT_VOUT,
203                                                   FIND_CHILD ) ) )
204     {
205         if( p_obj->p_parent != VLC_OBJECT(p_playlist) )
206         {
207             vlc_object_release( p_obj );
208             break;
209         }
210         msg_Dbg( p_playlist, "garbage collector destroying 1 vout" );
211         vlc_object_detach( p_obj );
212         vlc_object_release( p_obj );
213         vout_Destroy( (vout_thread_t *)p_obj );
214     }
215     while( ( p_obj = vlc_object_find( p_playlist, VLC_OBJECT_SOUT,
216                                                   FIND_CHILD ) ) )
217     {
218         if( p_obj->p_parent != VLC_OBJECT(p_playlist) )
219         {
220             vlc_object_release( p_obj );
221             break;
222         }
223         msg_Dbg( p_playlist, "garbage collector destroying 1 sout" );
224         vlc_object_detach( p_obj );
225         vlc_object_release( p_obj );
226         sout_DeleteInstance( (sout_instance_t*)p_obj );
227     }
228     p_playlist->b_cant_sleep = VLC_FALSE;
229     vlc_mutex_unlock( &p_playlist->gc_lock );
230 }
231
232 /** Main loop for the playlist */
233 void playlist_MainLoop( playlist_t *p_playlist )
234 {
235     playlist_item_t *p_item = NULL;
236     vlc_bool_t b_playexit = var_GetBool( p_playlist, "play-and-exit" );
237     PL_LOCK;
238
239     if( p_playlist->b_reset_currently_playing &&
240         mdate() - p_playlist->last_rebuild_date > 30000 ) // 30 ms
241     {
242         ResetCurrentlyPlaying( p_playlist, var_GetBool( p_playlist, "random"),
243                              p_playlist->status.p_item );
244         p_playlist->last_rebuild_date = mdate();
245     }
246
247 check_input:
248     /* If there is an input, check that it doesn't need to die. */
249     if( p_playlist->p_input )
250     {
251         if( p_playlist->request.b_request && !p_playlist->p_input->b_die )
252         {
253             PL_DEBUG( "incoming request - stopping current input" );
254             input_StopThread( p_playlist->p_input );
255         }
256
257         /* This input is dead. Remove it ! */
258         if( p_playlist->p_input->b_dead )
259         {
260             int i_activity;
261             input_thread_t *p_input;
262             PL_DEBUG( "dead input" );
263
264             p_input = p_playlist->p_input;
265             p_playlist->p_input = NULL;
266
267             /* Release the playlist lock, because we may get stuck
268              * in input_DestroyThread() for some time. */
269             PL_UNLOCK
270
271             /* Destroy input */
272             input_DestroyThread( p_input );
273
274             PL_LOCK;
275
276             p_playlist->gc_date = mdate();
277             p_playlist->b_cant_sleep = VLC_TRUE;
278
279             if( p_playlist->status.p_item->i_flags
280                 & PLAYLIST_REMOVE_FLAG )
281             {
282                  PL_DEBUG( "%s was marked for deletion, deleting",
283                                  PLI_NAME( p_playlist->status.p_item  ) );
284                  playlist_ItemDelete( p_playlist->status.p_item );
285                  if( p_playlist->request.p_item == p_playlist->status.p_item )
286                      p_playlist->request.p_item = NULL;
287                  p_playlist->status.p_item = NULL;
288             }
289
290             i_activity= var_GetInteger( p_playlist, "activity") ;
291             var_SetInteger( p_playlist, "activity", i_activity -
292                             DEFAULT_INPUT_ACTIVITY );
293             goto check_input;
294         }
295         /* This input is dying, let it do */
296         else if( p_playlist->p_input->b_die )
297         {
298             PL_DEBUG( "dying input" );
299             PL_UNLOCK;
300             msleep( 25000 ); // 25 ms
301             PL_LOCK;
302             goto check_input;
303         }
304         /* This input has finished, ask it to die ! */
305         else if( p_playlist->p_input->b_error
306                   || p_playlist->p_input->b_eof )
307         {
308             PL_DEBUG( "finished input" );
309             input_StopThread( p_playlist->p_input );
310             /* No need to wait here, we'll wait in the p_input->b_die case */
311             goto check_input;
312         }
313         else if( p_playlist->p_input->i_state != INIT_S )
314         {
315             PL_UNLOCK;
316             ObjectGarbageCollector( p_playlist, VLC_FALSE );
317             PL_LOCK;
318         }
319     }
320     else
321     {
322         /* No input. Several cases
323          *  - No request, running status -> start new item
324          *  - No request, stopped status -> collect garbage
325          *  - Request, running requested -> start new item
326          *  - Request, stopped requested -> collect garbage
327          */
328          if( (!p_playlist->request.b_request &&
329               p_playlist->status.i_status != PLAYLIST_STOPPED) ||
330               ( p_playlist->request.b_request &&
331                 p_playlist->request.i_status != PLAYLIST_STOPPED ) )
332          {
333              msg_Dbg( p_playlist, "starting new item" );
334              p_item = playlist_NextItem( p_playlist );
335
336              if( p_item == NULL )
337              {
338                 msg_Dbg( p_playlist, "nothing to play" );
339                 if( b_playexit == VLC_TRUE )
340                 {
341                     msg_Info( p_playlist, "end of playlist, exiting" );
342                     vlc_object_kill( p_playlist->p_libvlc );
343                 }
344                 p_playlist->status.i_status = PLAYLIST_STOPPED;
345                 PL_UNLOCK
346
347                 ObjectGarbageCollector( p_playlist, VLC_TRUE );
348                 return;
349              }
350              playlist_PlayItem( p_playlist, p_item );
351          }
352          else
353          {
354             const vlc_bool_t b_gc_forced = p_playlist->status.i_status != PLAYLIST_STOPPED;
355
356             p_playlist->status.i_status = PLAYLIST_STOPPED;
357             if( p_playlist->status.p_item &&
358                 p_playlist->status.p_item->i_flags & PLAYLIST_REMOVE_FLAG )
359             {
360                 PL_DEBUG( "deleting item marked for deletion" );
361                 playlist_ItemDelete( p_playlist->status.p_item );
362                 p_playlist->status.p_item = NULL;
363             }
364
365             /* Collect garbage */
366             PL_UNLOCK;
367             ObjectGarbageCollector( p_playlist, b_gc_forced );
368             PL_LOCK;
369         }
370     }
371     PL_UNLOCK
372 }
373
374 /** Playlist dying last loop */
375 void playlist_LastLoop( playlist_t *p_playlist )
376 {
377     vlc_object_t *p_obj;
378
379     /* If there is an input, kill it */
380     while( 1 )
381     {
382         PL_LOCK
383
384         if( p_playlist->p_input == NULL )
385         {
386             PL_UNLOCK
387             break;
388         }
389
390         if( p_playlist->p_input->b_dead )
391         {
392             input_thread_t *p_input;
393
394             /* Unlink current input */
395             p_input = p_playlist->p_input;
396             p_playlist->p_input = NULL;
397             PL_UNLOCK
398
399             /* Destroy input */
400             input_DestroyThread( p_input );
401             continue;
402         }
403         else if( p_playlist->p_input->b_die )
404         {
405             /* This input is dying, leave it alone */
406             ;
407         }
408         else if( p_playlist->p_input->b_error || p_playlist->p_input->b_eof )
409         {
410             input_StopThread( p_playlist->p_input );
411             PL_UNLOCK
412             continue;
413         }
414         else
415         {
416             p_playlist->p_input->b_eof = 1;
417         }
418
419         PL_UNLOCK
420
421         msleep( INTF_IDLE_SLEEP );
422     }
423
424     /* close all remaining sout */
425     while( ( p_obj = vlc_object_find( p_playlist,
426                                       VLC_OBJECT_SOUT, FIND_CHILD ) ) )
427     {
428         vlc_object_detach( p_obj );
429         vlc_object_release( p_obj );
430         sout_DeleteInstance( (sout_instance_t*)p_obj );
431     }
432
433     /* close all remaining vout */
434     while( ( p_obj = vlc_object_find( p_playlist,
435                                       VLC_OBJECT_VOUT, FIND_CHILD ) ) )
436     {
437         vlc_object_detach( p_obj );
438         vlc_object_release( p_obj );
439         vout_Destroy( (vout_thread_t *)p_obj );
440     }
441
442     while( p_playlist->p_internal->i_asds )
443     {
444         playlist_ServicesDiscoveryRemove( p_playlist,
445                                           p_playlist->p_internal->pp_asds[0]->p_sd->psz_module );
446     }
447
448     playlist_MLDump( p_playlist );
449
450     PL_LOCK;
451     /* Go through all items, and simply free everything without caring
452      * about the tree structure. Do not decref, it will be done by doing
453      * the same thing on the input items array */
454     FOREACH_ARRAY( playlist_item_t *p_del, p_playlist->all_items )
455         free( p_del->pp_children );
456         free( p_del );
457     FOREACH_END();
458     ARRAY_RESET( p_playlist->all_items );
459
460     FOREACH_ARRAY( input_item_t *p_del, p_playlist->input_items )
461         input_ItemClean( p_del );
462         free( p_del );
463     FOREACH_END();
464     ARRAY_RESET( p_playlist->input_items );
465
466     ARRAY_RESET( p_playlist->items );
467     ARRAY_RESET( p_playlist->current );
468
469     PL_UNLOCK;
470 }
471
472 /** Main loop for preparser queue */
473 void playlist_PreparseLoop( playlist_preparse_t *p_obj )
474 {
475     playlist_t *p_playlist = (playlist_t *)p_obj->p_parent;
476     input_item_t *p_current;
477     int i_activity;
478     uint32_t i_m, i_o;
479
480     while( !p_playlist->b_die )
481     {
482         vlc_mutex_lock( &p_obj->object_lock );
483         while( p_obj->i_waiting == 0 )
484         {
485             vlc_cond_wait( &p_obj->object_wait, &p_obj->object_lock );
486             if( p_playlist->b_die )
487             {
488                 vlc_mutex_unlock( &p_obj->object_lock );
489                 return;
490             }
491         }
492
493         p_current = p_obj->pp_waiting[0];
494         REMOVE_ELEM( p_obj->pp_waiting, p_obj->i_waiting, 0 );
495         vlc_mutex_unlock( &p_obj->object_lock );
496
497         PL_LOCK;
498         if( p_current )
499         {
500             vlc_bool_t b_preparsed = VLC_FALSE;
501             if( strncmp( p_current->psz_uri, "http:", 5 ) &&
502                 strncmp( p_current->psz_uri, "rtsp:", 5 ) &&
503                 strncmp( p_current->psz_uri, "udp:", 4 ) &&
504                 strncmp( p_current->psz_uri, "mms:", 4 ) &&
505                 strncmp( p_current->psz_uri, "cdda:", 4 ) &&
506                 strncmp( p_current->psz_uri, "dvd:", 4 ) &&
507                 strncmp( p_current->psz_uri, "v4l:", 4 ) &&
508                 strncmp( p_current->psz_uri, "dshow:", 6 ) )
509             {
510                 b_preparsed = VLC_TRUE;
511                 stats_TimerStart( p_playlist, "Preparse run",
512                                   STATS_TIMER_PREPARSE );
513                 /* Do not preparse if it is already done (like by playing it) */
514                 if( !input_item_IsPreparsed( p_current ) )
515                 {
516                     PL_UNLOCK;
517                     input_Preparse( p_playlist, p_current );
518                     PL_LOCK;
519                 }
520                 stats_TimerStop( p_playlist, STATS_TIMER_PREPARSE );
521             }
522             PL_UNLOCK;
523             if( b_preparsed )
524             {
525                 input_item_SetPreparsed( p_current, VLC_TRUE );
526                 var_SetInteger( p_playlist, "item-change", p_current->i_id );
527             }
528             PL_LOCK;
529
530             /* If we haven't retrieved enough meta, add to secondary queue
531              * which will run the "meta fetchers".
532              * This only checks for meta, not for art
533              * \todo don't do this for things we won't get meta for, like vids
534              */
535             char *psz_arturl = input_item_GetArtURL( p_current );
536             char *psz_name = input_item_GetName( p_current );
537             if( !input_MetaSatisfied( p_playlist, p_current, &i_m, &i_o ) )
538             {
539                 preparse_item_t p;
540                 PL_DEBUG("need to fetch meta for %s", p_current->psz_name );
541                 p.p_item = p_current;
542                 p.b_fetch_art = VLC_FALSE;
543                 vlc_mutex_lock( &p_playlist->p_fetcher->object_lock );
544                 INSERT_ELEM( p_playlist->p_fetcher->p_waiting,
545                              p_playlist->p_fetcher->i_waiting,
546                              p_playlist->p_fetcher->i_waiting, p);
547                 vlc_mutex_unlock( &p_playlist->p_fetcher->object_lock );
548                 vlc_cond_signal( &p_playlist->p_fetcher->object_wait );
549             }
550             /* We already have all needed meta, but we need art right now */
551             else if( p_playlist->p_fetcher->i_art_policy == ALBUM_ART_ALL &&
552                      EMPTY_STR( psz_arturl ) )
553             {
554                 preparse_item_t p;
555                 PL_DEBUG("meta ok for %s, need to fetch art", psz_name );
556                 p.p_item = p_current;
557                 p.b_fetch_art = VLC_TRUE;
558                 vlc_mutex_lock( &p_playlist->p_fetcher->object_lock );
559                 INSERT_ELEM( p_playlist->p_fetcher->p_waiting,
560                              p_playlist->p_fetcher->i_waiting,
561                              p_playlist->p_fetcher->i_waiting, p);
562                 vlc_mutex_unlock( &p_playlist->p_fetcher->object_lock );
563                 vlc_cond_signal( &p_playlist->p_fetcher->object_wait );
564             }
565             else
566             {
567                 PL_DEBUG( "no fetch required for %s (art currently %s)",
568                           psz_name, psz_arturl );
569                 vlc_gc_decref( p_current );
570             }
571             free( psz_name );
572             free( psz_arturl );
573             PL_UNLOCK;
574         }
575         else
576             PL_UNLOCK;
577
578         vlc_mutex_lock( &p_obj->object_lock );
579         i_activity = var_GetInteger( p_playlist, "activity" );
580         if( i_activity < 0 ) i_activity = 0;
581         vlc_mutex_unlock( &p_obj->object_lock );
582         /* Sleep at least 1ms */
583         msleep( (i_activity+1) * 1000 );
584     }
585 }
586
587 /** Main loop for secondary preparser queue */
588 void playlist_FetcherLoop( playlist_fetcher_t *p_obj )
589 {
590     playlist_t *p_playlist = (playlist_t *)p_obj->p_parent;
591     vlc_bool_t b_fetch_art;
592     input_item_t *p_item;
593     int i_activity;
594
595     while( !p_playlist->b_die )
596     {
597         vlc_mutex_lock( &p_obj->object_lock );
598         while( p_obj->i_waiting == 0 )
599         {
600             vlc_cond_wait( &p_obj->object_wait, &p_obj->object_lock );
601             if( p_playlist->b_die )
602             {
603                 vlc_mutex_unlock( &p_obj->object_lock );
604                 return;
605             }
606         }
607
608         b_fetch_art = p_obj->p_waiting->b_fetch_art;
609         p_item = p_obj->p_waiting->p_item;
610         REMOVE_ELEM( p_obj->p_waiting, p_obj->i_waiting, 0 );
611         vlc_mutex_unlock( &p_obj->object_lock );
612         if( p_item )
613         {
614             if( !b_fetch_art )
615             {
616                 input_MetaFetch( p_playlist, p_item );
617                 var_SetInteger( p_playlist, "item-change", p_item->i_id );
618                 /*  Fetch right now */
619                 if( p_playlist->p_fetcher->i_art_policy == ALBUM_ART_ALL )
620                 {
621                     vlc_mutex_lock( &p_obj->object_lock );
622                     preparse_item_t p;
623                     p.p_item = p_item;
624                     p.b_fetch_art = VLC_TRUE;
625                     INSERT_ELEM( p_playlist->p_fetcher->p_waiting,
626                                  p_playlist->p_fetcher->i_waiting,
627                                  0, p );
628                     PL_DEBUG("meta fetched for %s, get art", p_item->psz_name);
629                     vlc_mutex_unlock( &p_obj->object_lock );
630                     continue;
631                 }
632                 else
633                     vlc_gc_decref( p_item );
634             }
635             else
636             {
637                 int i_ret;
638
639                 /* Check if it is not yet preparsed and if so wait for it (at most 0.5s)
640                  * (This can happen if we fetch art on play)
641                  * FIXME this doesn't work if we need to fetch meta before art ... */
642                 for( i_ret = 0; i_ret < 10 && !input_item_IsPreparsed( p_item ); i_ret++ )
643                 {
644                     vlc_bool_t b_break;
645                     PL_LOCK;
646                     b_break = ( !p_playlist->p_input || input_GetItem(p_playlist->p_input) != p_item  ||
647                                 p_playlist->p_input->b_die || p_playlist->p_input->b_eof || p_playlist->p_input->b_error );
648                     PL_UNLOCK;
649                     if( b_break )
650                         break;
651                     msleep( 50000 );
652                 }
653
654                 i_ret = input_ArtFind( p_playlist, p_item );
655                 if( i_ret == 1 )
656                 {
657                     PL_DEBUG("downloading art for %s", p_item->psz_name );
658                     if( input_DownloadAndCacheArt( p_playlist, p_item ) )
659                         input_item_SetArtNotFound( p_item, VLC_TRUE );
660                     else {
661                         input_item_SetArtFetched( p_item, VLC_TRUE );
662                         var_SetInteger( p_playlist, "item-change",
663                                         p_item->i_id );
664                     }
665                 }
666                 else if( i_ret == 0 ) /* Was in cache */
667                 {
668                     PL_DEBUG("found art for %s in cache", p_item->psz_name );
669                     input_item_SetArtFetched( p_item, VLC_TRUE );
670                     var_SetInteger( p_playlist, "item-change", p_item->i_id );
671                 }
672                 else
673                 {
674                     PL_DEBUG("art not found for %s", p_item->psz_name );
675                     input_item_SetArtNotFound( p_item, VLC_TRUE );
676                 }
677                 vlc_gc_decref( p_item );
678            }
679         }
680         vlc_mutex_lock( &p_obj->object_lock );
681         i_activity = var_GetInteger( p_playlist, "activity" );
682         if( i_activity < 0 ) i_activity = 0;
683         vlc_mutex_unlock( &p_obj->object_lock );
684         /* Sleep at least 1ms */
685         msleep( (i_activity+1) * 1000 );
686     }
687 }
688
689 static void VariablesInit( playlist_t *p_playlist )
690 {
691     vlc_value_t val;
692     /* These variables control updates */
693     var_Create( p_playlist, "intf-change", VLC_VAR_BOOL );
694     val.b_bool = VLC_TRUE;
695     var_Set( p_playlist, "intf-change", val );
696
697     var_Create( p_playlist, "item-change", VLC_VAR_INTEGER );
698     val.i_int = -1;
699     var_Set( p_playlist, "item-change", val );
700
701     var_Create( p_playlist, "item-deleted", VLC_VAR_INTEGER );
702     val.i_int = -1;
703     var_Set( p_playlist, "item-deleted", val );
704
705     var_Create( p_playlist, "item-append", VLC_VAR_ADDRESS );
706
707     var_Create( p_playlist, "playlist-current", VLC_VAR_INTEGER );
708     val.i_int = -1;
709     var_Set( p_playlist, "playlist-current", val );
710
711     var_Create( p_playlist, "intf-popupmenu", VLC_VAR_BOOL );
712
713     var_Create( p_playlist, "intf-show", VLC_VAR_BOOL );
714     val.b_bool = VLC_TRUE;
715     var_Set( p_playlist, "intf-show", val );
716
717     var_Create( p_playlist, "activity", VLC_VAR_INTEGER );
718     var_SetInteger( p_playlist, "activity", 0 );
719
720     /* Variables to control playback */
721     var_CreateGetBool( p_playlist, "play-and-stop" );
722     var_CreateGetBool( p_playlist, "play-and-exit" );
723     var_CreateGetBool( p_playlist, "random" );
724     var_CreateGetBool( p_playlist, "repeat" );
725     var_CreateGetBool( p_playlist, "loop" );
726
727     var_AddCallback( p_playlist, "random", RandomCallback, NULL );
728 }