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