1 /*****************************************************************************
2 * engine.c : Run the playlist and handle its control
3 *****************************************************************************
4 * Copyright (C) 1999-2007 the VideoLAN team
7 * Authors: Samuel Hocevar <sam@zoy.org>
8 * Clément Stenac <zorglub@videolan.org>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 *****************************************************************************/
32 #include <vlc_playlist.h>
33 #include <vlc_interface.h>
34 #include "playlist_internal.h"
35 #include "stream_output/stream_output.h" /* sout_DeleteInstance */
37 /*****************************************************************************
39 *****************************************************************************/
40 static void VariablesInit( playlist_t
*p_playlist
);
42 static int RandomCallback( vlc_object_t
*p_this
, char const *psz_cmd
,
43 vlc_value_t oldval
, vlc_value_t newval
, void *a
)
45 (void)psz_cmd
; (void)oldval
; (void)newval
; (void)a
;
47 ((playlist_t
*)p_this
)->b_reset_currently_playing
= VLC_TRUE
;
48 playlist_Signal( ((playlist_t
*)p_this
) );
55 * Create a playlist structure.
56 * \param p_parent the vlc object that is to be the parent of this playlist
57 * \return a pointer to the created playlist, or NULL on error
59 playlist_t
* playlist_Create( vlc_object_t
*p_parent
)
61 playlist_t
*p_playlist
;
65 /* Allocate structure */
66 p_playlist
= vlc_object_create( p_parent
, VLC_OBJECT_PLAYLIST
);
69 msg_Err( p_parent
, "out of memory" );
73 TAB_INIT( p_playlist
->i_sds
, p_playlist
->pp_sds
);
75 p_parent
->p_libvlc
->p_playlist
= p_playlist
;
77 VariablesInit( p_playlist
);
79 /* Initialise data structures */
80 vlc_mutex_init( p_playlist
, &p_playlist
->gc_lock
);
81 p_playlist
->i_last_playlist_id
= 0;
82 p_playlist
->i_last_input_id
= 0;
83 p_playlist
->p_input
= NULL
;
85 p_playlist
->gc_date
= 0;
86 p_playlist
->b_cant_sleep
= VLC_FALSE
;
88 ARRAY_INIT( p_playlist
->items
);
89 ARRAY_INIT( p_playlist
->all_items
);
90 ARRAY_INIT( p_playlist
->input_items
);
91 ARRAY_INIT( p_playlist
->current
);
93 p_playlist
->i_current_index
= 0;
94 p_playlist
->b_reset_currently_playing
= VLC_TRUE
;
95 p_playlist
->last_rebuild_date
= 0;
97 i_tree
= var_CreateGetBool( p_playlist
, "playlist-tree" );
98 p_playlist
->b_always_tree
= (i_tree
== 1);
99 p_playlist
->b_never_tree
= (i_tree
== 2);
101 p_playlist
->b_doing_ml
= VLC_FALSE
;
103 p_playlist
->b_auto_preparse
=
104 var_CreateGetBool( p_playlist
, "auto-preparse") ;
106 p_playlist
->p_root_category
= playlist_NodeCreate( p_playlist
, NULL
, NULL
,
108 p_playlist
->p_root_onelevel
= playlist_NodeCreate( p_playlist
, NULL
, NULL
,
109 0, p_playlist
->p_root_category
->p_input
);
111 if( !p_playlist
->p_root_category
|| !p_playlist
->p_root_onelevel
)
114 /* Create playlist and media library */
115 playlist_NodesPairCreate( p_playlist
, _( "Playlist" ),
116 &p_playlist
->p_local_category
,
117 &p_playlist
->p_local_onelevel
, VLC_FALSE
);
119 p_playlist
->p_local_category
->i_flags
|= PLAYLIST_RO_FLAG
;
120 p_playlist
->p_local_onelevel
->i_flags
|= PLAYLIST_RO_FLAG
;
122 if( !p_playlist
->p_local_category
|| !p_playlist
->p_local_onelevel
||
123 !p_playlist
->p_local_category
->p_input
||
124 !p_playlist
->p_local_onelevel
->p_input
)
127 if( config_GetInt( p_playlist
, "media-library") )
129 playlist_NodesPairCreate( p_playlist
, _( "Media Library" ),
130 &p_playlist
->p_ml_category
,
131 &p_playlist
->p_ml_onelevel
, VLC_FALSE
);
133 if(!p_playlist
->p_ml_category
|| !p_playlist
->p_ml_onelevel
)
136 p_playlist
->p_ml_category
->i_flags
|= PLAYLIST_RO_FLAG
;
137 p_playlist
->p_ml_onelevel
->i_flags
|= PLAYLIST_RO_FLAG
;
141 p_playlist
->p_ml_category
= p_playlist
->p_ml_onelevel
= NULL
;
145 p_playlist
->status
.p_item
= NULL
;
146 p_playlist
->status
.p_node
= p_playlist
->p_local_onelevel
;
147 p_playlist
->request
.b_request
= VLC_FALSE
;
148 p_playlist
->status
.i_status
= PLAYLIST_STOPPED
;
150 p_playlist
->i_sort
= SORT_ID
;
151 p_playlist
->i_order
= ORDER_NORMAL
;
153 b_save
= p_playlist
->b_auto_preparse
;
154 p_playlist
->b_auto_preparse
= VLC_FALSE
;
155 playlist_MLLoad( p_playlist
);
156 p_playlist
->b_auto_preparse
= VLC_TRUE
;
160 void playlist_Destroy( playlist_t
*p_playlist
)
162 var_Destroy( p_playlist
, "intf-change" );
163 var_Destroy( p_playlist
, "item-change" );
164 var_Destroy( p_playlist
, "playlist-current" );
165 var_Destroy( p_playlist
, "intf-popupmenu" );
166 var_Destroy( p_playlist
, "intf-show" );
167 var_Destroy( p_playlist
, "play-and-stop" );
168 var_Destroy( p_playlist
, "play-and-exit" );
169 var_Destroy( p_playlist
, "random" );
170 var_Destroy( p_playlist
, "repeat" );
171 var_Destroy( p_playlist
, "loop" );
172 var_Destroy( p_playlist
, "activity" );
174 vlc_mutex_destroy( &p_playlist
->gc_lock
);
175 vlc_object_detach( p_playlist
);
176 vlc_object_destroy( p_playlist
);
179 /* Destroy remaining objects */
180 static void ObjectGarbageCollector( playlist_t
*p_playlist
, vlc_bool_t b_force
)
186 if( mdate() - p_playlist
->gc_date
< 1000000 )
188 p_playlist
->b_cant_sleep
= VLC_TRUE
;
191 else if( p_playlist
->gc_date
== 0 )
195 vlc_mutex_lock( &p_playlist
->gc_lock
);
196 while( ( p_obj
= vlc_object_find( p_playlist
, VLC_OBJECT_VOUT
,
199 if( p_obj
->p_parent
!= VLC_OBJECT(p_playlist
) )
201 vlc_object_release( p_obj
);
204 msg_Dbg( p_playlist
, "garbage collector destroying 1 vout" );
205 vlc_object_detach( p_obj
);
206 vlc_object_release( p_obj
);
207 vout_Destroy( (vout_thread_t
*)p_obj
);
209 while( ( p_obj
= vlc_object_find( p_playlist
, VLC_OBJECT_SOUT
,
212 if( p_obj
->p_parent
!= VLC_OBJECT(p_playlist
) )
214 vlc_object_release( p_obj
);
217 msg_Dbg( p_playlist
, "garbage collector destroying 1 sout" );
218 vlc_object_detach( p_obj
);
219 vlc_object_release( p_obj
);
220 sout_DeleteInstance( (sout_instance_t
*)p_obj
);
222 p_playlist
->b_cant_sleep
= VLC_FALSE
;
223 vlc_mutex_unlock( &p_playlist
->gc_lock
);
226 /** Main loop for the playlist */
227 void playlist_MainLoop( playlist_t
*p_playlist
)
229 playlist_item_t
*p_item
= NULL
;
230 vlc_bool_t b_playexit
= var_GetBool( p_playlist
, "play-and-exit" );
233 if( p_playlist
->b_reset_currently_playing
&&
234 mdate() - p_playlist
->last_rebuild_date
> 30000 ) // 30 ms
236 ResetCurrentlyPlaying( p_playlist
, var_GetBool( p_playlist
, "random"),
237 p_playlist
->status
.p_item
);
238 p_playlist
->last_rebuild_date
= mdate();
242 /* If there is an input, check that it doesn't need to die. */
243 if( p_playlist
->p_input
)
245 if( p_playlist
->request
.b_request
&& !p_playlist
->p_input
->b_die
)
247 PL_DEBUG( "incoming request - stopping current input" );
248 input_StopThread( p_playlist
->p_input
);
251 /* This input is dead. Remove it ! */
252 if( p_playlist
->p_input
->b_dead
)
255 input_thread_t
*p_input
;
256 PL_DEBUG( "dead input" );
258 p_input
= p_playlist
->p_input
;
259 p_playlist
->p_input
= NULL
;
261 /* Release the playlist lock, because we may get stuck
262 * in input_DestroyThread() for some time. */
266 input_DestroyThread( p_input
);
270 p_playlist
->gc_date
= mdate();
271 p_playlist
->b_cant_sleep
= VLC_TRUE
;
273 if( p_playlist
->status
.p_item
->i_flags
274 & PLAYLIST_REMOVE_FLAG
)
276 PL_DEBUG( "%s was marked for deletion, deleting",
277 PLI_NAME( p_playlist
->status
.p_item
) );
278 playlist_ItemDelete( p_playlist
->status
.p_item
);
279 if( p_playlist
->request
.p_item
== p_playlist
->status
.p_item
)
280 p_playlist
->request
.p_item
= NULL
;
281 p_playlist
->status
.p_item
= NULL
;
284 i_activity
= var_GetInteger( p_playlist
, "activity") ;
285 var_SetInteger( p_playlist
, "activity", i_activity
-
286 DEFAULT_INPUT_ACTIVITY
);
289 /* This input is dying, let it do */
290 else if( p_playlist
->p_input
->b_die
)
292 PL_DEBUG( "dying input" );
294 msleep( INTF_IDLE_SLEEP
);
298 /* This input has finished, ask it to die ! */
299 else if( p_playlist
->p_input
->b_error
300 || p_playlist
->p_input
->b_eof
)
302 PL_DEBUG( "finished input" );
303 input_StopThread( p_playlist
->p_input
);
304 /* No need to wait here, we'll wait in the p_input->b_die case */
307 else if( p_playlist
->p_input
->i_state
!= INIT_S
)
310 ObjectGarbageCollector( p_playlist
, VLC_FALSE
);
316 /* No input. Several cases
317 * - No request, running status -> start new item
318 * - No request, stopped status -> collect garbage
319 * - Request, running requested -> start new item
320 * - Request, stopped requested -> collect garbage
322 if( (!p_playlist
->request
.b_request
&&
323 p_playlist
->status
.i_status
!= PLAYLIST_STOPPED
) ||
324 ( p_playlist
->request
.b_request
&&
325 p_playlist
->request
.i_status
!= PLAYLIST_STOPPED
) )
327 msg_Dbg( p_playlist
, "starting new item" );
328 p_item
= playlist_NextItem( p_playlist
);
332 msg_Dbg( p_playlist
, "nothing to play" );
333 p_playlist
->status
.i_status
= PLAYLIST_STOPPED
;
336 if( b_playexit
== VLC_TRUE
)
338 msg_Info( p_playlist
, "end of playlist, exiting" );
339 vlc_object_kill( p_playlist
->p_libvlc
);
341 ObjectGarbageCollector( p_playlist
, VLC_TRUE
);
344 playlist_PlayItem( p_playlist
, p_item
);
348 const vlc_bool_t b_gc_forced
= p_playlist
->status
.i_status
!= PLAYLIST_STOPPED
;
350 p_playlist
->status
.i_status
= PLAYLIST_STOPPED
;
351 if( p_playlist
->status
.p_item
&&
352 p_playlist
->status
.p_item
->i_flags
& PLAYLIST_REMOVE_FLAG
)
354 PL_DEBUG( "deleting item marked for deletion" );
355 playlist_ItemDelete( p_playlist
->status
.p_item
);
356 p_playlist
->status
.p_item
= NULL
;
359 /* Collect garbage */
361 ObjectGarbageCollector( p_playlist
, b_gc_forced
);
368 /** Playlist dying last loop */
369 void playlist_LastLoop( playlist_t
*p_playlist
)
373 /* If there is an input, kill it */
377 if( p_playlist
->p_input
== NULL
)
383 if( p_playlist
->p_input
->b_dead
)
385 input_thread_t
*p_input
;
387 /* Unlink current input */
388 p_input
= p_playlist
->p_input
;
389 p_playlist
->p_input
= NULL
;
393 input_DestroyThread( p_input
);
396 else if( p_playlist
->p_input
->b_die
)
398 /* This input is dying, leave it alone */
401 else if( p_playlist
->p_input
->b_error
|| p_playlist
->p_input
->b_eof
)
403 input_StopThread( p_playlist
->p_input
);
409 p_playlist
->p_input
->b_eof
= 1;
413 msleep( INTF_IDLE_SLEEP
);
416 /* close all remaining sout */
417 while( ( p_obj
= vlc_object_find( p_playlist
,
418 VLC_OBJECT_SOUT
, FIND_CHILD
) ) )
420 vlc_object_detach( p_obj
);
421 vlc_object_release( p_obj
);
422 sout_DeleteInstance( (sout_instance_t
*)p_obj
);
425 /* close all remaining vout */
426 while( ( p_obj
= vlc_object_find( p_playlist
,
427 VLC_OBJECT_VOUT
, FIND_CHILD
) ) )
429 vlc_object_detach( p_obj
);
430 vlc_object_release( p_obj
);
431 vout_Destroy( (vout_thread_t
*)p_obj
);
434 while( p_playlist
->i_sds
)
436 playlist_ServicesDiscoveryRemove( p_playlist
,
437 p_playlist
->pp_sds
[0]->p_sd
->psz_module
);
440 playlist_MLDump( p_playlist
);
443 /* Go through all items, and simply free everything without caring
444 * about the tree structure. Do not decref, it will be done by doing
445 * the same thing on the input items array */
446 FOREACH_ARRAY( playlist_item_t
*p_del
, p_playlist
->all_items
)
447 free( p_del
->pp_children
);
450 ARRAY_RESET( p_playlist
->all_items
);
452 FOREACH_ARRAY( input_item_t
*p_del
, p_playlist
->input_items
)
453 input_ItemClean( p_del
);
456 ARRAY_RESET( p_playlist
->input_items
);
458 ARRAY_RESET( p_playlist
->items
);
459 ARRAY_RESET( p_playlist
->current
);
464 /** Main loop for preparser queue */
465 void playlist_PreparseLoop( playlist_preparse_t
*p_obj
)
467 playlist_t
*p_playlist
= (playlist_t
*)p_obj
->p_parent
;
468 input_item_t
*p_current
;
472 while( !p_playlist
->b_die
)
474 vlc_mutex_lock( &p_obj
->object_lock
);
475 while( p_obj
->i_waiting
== 0 )
477 vlc_cond_wait( &p_obj
->object_wait
, &p_obj
->object_lock
);
478 if( p_playlist
->b_die
)
480 vlc_mutex_unlock( &p_obj
->object_lock
);
485 p_current
= p_obj
->pp_waiting
[0];
486 REMOVE_ELEM( p_obj
->pp_waiting
, p_obj
->i_waiting
, 0 );
487 vlc_mutex_unlock( &p_obj
->object_lock
);
492 if( p_current
->i_type
== ITEM_TYPE_FILE
)
494 stats_TimerStart( p_playlist
, "Preparse run",
495 STATS_TIMER_PREPARSE
);
496 /* Do not preparse if it is already done (like by playing it) */
497 if( !input_item_IsPreparsed( p_current
) )
500 input_Preparse( p_playlist
, p_current
);
503 stats_TimerStop( p_playlist
, STATS_TIMER_PREPARSE
);
505 input_item_SetPreparsed( p_current
, VLC_TRUE
);
506 var_SetInteger( p_playlist
, "item-change", p_current
->i_id
);
509 /* If we haven't retrieved enough meta, add to secondary queue
510 * which will run the "meta fetchers".
511 * This only checks for meta, not for art
512 * \todo don't do this for things we won't get meta for, like vids
514 char *psz_arturl
= input_item_GetArtURL( p_current
);
515 char *psz_name
= input_item_GetName( p_current
);
516 if( !input_MetaSatisfied( p_playlist
, p_current
, &i_m
, &i_o
) )
519 PL_DEBUG("need to fetch meta for %s", p_current
->psz_name
);
520 p
.p_item
= p_current
;
521 p
.b_fetch_art
= VLC_FALSE
;
522 vlc_mutex_lock( &p_playlist
->p_fetcher
->object_lock
);
523 INSERT_ELEM( p_playlist
->p_fetcher
->p_waiting
,
524 p_playlist
->p_fetcher
->i_waiting
,
525 p_playlist
->p_fetcher
->i_waiting
, p
);
526 vlc_cond_signal( &p_playlist
->p_fetcher
->object_wait
);
527 vlc_mutex_unlock( &p_playlist
->p_fetcher
->object_lock
);
529 /* We already have all needed meta, but we need art right now */
530 else if( p_playlist
->p_fetcher
->i_art_policy
== ALBUM_ART_ALL
&&
531 ( !psz_arturl
|| strncmp( psz_arturl
, "file://", 7 ) ) )
534 PL_DEBUG("meta ok for %s, need to fetch art", psz_name
);
535 p
.p_item
= p_current
;
536 p
.b_fetch_art
= VLC_TRUE
;
537 vlc_mutex_lock( &p_playlist
->p_fetcher
->object_lock
);
538 INSERT_ELEM( p_playlist
->p_fetcher
->p_waiting
,
539 p_playlist
->p_fetcher
->i_waiting
,
540 p_playlist
->p_fetcher
->i_waiting
, p
);
541 vlc_cond_signal( &p_playlist
->p_fetcher
->object_wait
);
542 vlc_mutex_unlock( &p_playlist
->p_fetcher
->object_lock
);
546 PL_DEBUG( "no fetch required for %s (art currently %s)",
547 psz_name
, psz_arturl
);
548 vlc_gc_decref( p_current
);
557 vlc_mutex_lock( &p_obj
->object_lock
);
558 i_activity
= var_GetInteger( p_playlist
, "activity" );
559 if( i_activity
< 0 ) i_activity
= 0;
560 vlc_mutex_unlock( &p_obj
->object_lock
);
561 /* Sleep at least 1ms */
562 msleep( (i_activity
+1) * 1000 );
566 /** Main loop for secondary preparser queue */
567 void playlist_FetcherLoop( playlist_fetcher_t
*p_obj
)
569 playlist_t
*p_playlist
= (playlist_t
*)p_obj
->p_parent
;
570 vlc_bool_t b_fetch_art
;
571 input_item_t
*p_item
;
574 while( !p_playlist
->b_die
)
576 vlc_mutex_lock( &p_obj
->object_lock
);
577 while( p_obj
->i_waiting
== 0 )
579 vlc_cond_wait( &p_obj
->object_wait
, &p_obj
->object_lock
);
580 if( p_playlist
->b_die
)
582 vlc_mutex_unlock( &p_obj
->object_lock
);
587 b_fetch_art
= p_obj
->p_waiting
->b_fetch_art
;
588 p_item
= p_obj
->p_waiting
->p_item
;
589 REMOVE_ELEM( p_obj
->p_waiting
, p_obj
->i_waiting
, 0 );
590 vlc_mutex_unlock( &p_obj
->object_lock
);
595 /* If the user doesn't want us to fetch meta automatically
597 if( p_playlist
->p_fetcher
->b_fetch_meta
)
599 input_MetaFetch( p_playlist
, p_item
);
600 var_SetInteger( p_playlist
, "item-change", p_item
->i_id
);
603 /* Fetch right now */
604 if( p_playlist
->p_fetcher
->i_art_policy
== ALBUM_ART_ALL
)
606 vlc_mutex_lock( &p_obj
->object_lock
);
609 p
.b_fetch_art
= VLC_TRUE
;
610 INSERT_ELEM( p_playlist
->p_fetcher
->p_waiting
,
611 p_playlist
->p_fetcher
->i_waiting
,
613 PL_DEBUG("meta fetched for %s, get art", p_item
->psz_name
);
614 vlc_mutex_unlock( &p_obj
->object_lock
);
618 vlc_gc_decref( p_item
);
624 /* Check if it is not yet preparsed and if so wait for it (at most 0.5s)
625 * (This can happen if we fetch art on play)
626 * FIXME this doesn't work if we need to fetch meta before art ... */
627 for( i_ret
= 0; i_ret
< 10 && !input_item_IsPreparsed( p_item
); i_ret
++ )
631 b_break
= ( !p_playlist
->p_input
|| input_GetItem(p_playlist
->p_input
) != p_item
||
632 p_playlist
->p_input
->b_die
|| p_playlist
->p_input
->b_eof
|| p_playlist
->p_input
->b_error
);
639 i_ret
= input_ArtFind( p_playlist
, p_item
);
642 PL_DEBUG("downloading art for %s", p_item
->psz_name
);
643 if( input_DownloadAndCacheArt( p_playlist
, p_item
) )
644 input_item_SetArtNotFound( p_item
, VLC_TRUE
);
646 input_item_SetArtFetched( p_item
, VLC_TRUE
);
647 var_SetInteger( p_playlist
, "item-change",
651 else if( i_ret
== 0 ) /* Was in cache */
653 PL_DEBUG("found art for %s in cache", p_item
->psz_name
);
654 input_item_SetArtFetched( p_item
, VLC_TRUE
);
655 var_SetInteger( p_playlist
, "item-change", p_item
->i_id
);
659 PL_DEBUG("art not found for %s", p_item
->psz_name
);
660 input_item_SetArtNotFound( p_item
, VLC_TRUE
);
662 vlc_gc_decref( p_item
);
665 vlc_mutex_lock( &p_obj
->object_lock
);
666 i_activity
= var_GetInteger( p_playlist
, "activity" );
667 if( i_activity
< 0 ) i_activity
= 0;
668 vlc_mutex_unlock( &p_obj
->object_lock
);
669 /* Sleep at least 1ms */
670 msleep( (i_activity
+1) * 1000 );
674 static void VariablesInit( playlist_t
*p_playlist
)
677 /* These variables control updates */
678 var_Create( p_playlist
, "intf-change", VLC_VAR_BOOL
);
679 val
.b_bool
= VLC_TRUE
;
680 var_Set( p_playlist
, "intf-change", val
);
682 var_Create( p_playlist
, "item-change", VLC_VAR_INTEGER
);
684 var_Set( p_playlist
, "item-change", val
);
686 var_Create( p_playlist
, "item-deleted", VLC_VAR_INTEGER
);
688 var_Set( p_playlist
, "item-deleted", val
);
690 var_Create( p_playlist
, "item-append", VLC_VAR_ADDRESS
);
692 var_Create( p_playlist
, "playlist-current", VLC_VAR_INTEGER
);
694 var_Set( p_playlist
, "playlist-current", val
);
696 var_Create( p_playlist
, "intf-popupmenu", VLC_VAR_BOOL
);
698 var_Create( p_playlist
, "intf-show", VLC_VAR_BOOL
);
699 val
.b_bool
= VLC_TRUE
;
700 var_Set( p_playlist
, "intf-show", val
);
702 var_Create( p_playlist
, "activity", VLC_VAR_INTEGER
);
703 var_SetInteger( p_playlist
, "activity", 0 );
705 /* Variables to control playback */
706 var_CreateGetBool( p_playlist
, "play-and-stop" );
707 var_CreateGetBool( p_playlist
, "play-and-exit" );
708 var_CreateGetBool( p_playlist
, "random" );
709 var_CreateGetBool( p_playlist
, "repeat" );
710 var_CreateGetBool( p_playlist
, "loop" );
712 var_AddCallback( p_playlist
, "random", RandomCallback
, NULL
);