1 /*****************************************************************************
2 * engine.c : Run the playlist and handle its control
3 *****************************************************************************
4 * Copyright (C) 1999-2008 VLC authors and VideoLAN
6 * Authors: Samuel Hocevar <sam@zoy.org>
7 * Clément Stenac <zorglub@videolan.org>
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU Lesser General Public License as published by
11 * the Free Software Foundation; either version 2.1 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this program; if not, write to the Free Software Foundation,
21 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
31 #include <vlc_common.h>
32 #include <vlc_arrays.h>
34 #include <vlc_playlist.h>
35 #include <vlc_interface.h>
37 #include <vlc_renderer_discovery.h>
38 #include "playlist_internal.h"
39 #include "input/resource.h"
41 /*****************************************************************************
43 *****************************************************************************/
44 static void VariablesInit( playlist_t
*p_playlist
);
46 static int RandomCallback( vlc_object_t
*p_this
, char const *psz_cmd
,
47 vlc_value_t oldval
, vlc_value_t newval
, void *a
)
49 (void)psz_cmd
; (void)oldval
; (void)newval
; (void)a
;
50 playlist_t
*p_playlist
= (playlist_t
*)p_this
;
51 bool random
= newval
.b_bool
;
56 pl_priv(p_playlist
)->b_reset_currently_playing
= true;
57 vlc_cond_signal( &pl_priv(p_playlist
)->signal
);
59 /* Shuffle and sync the playlist on activation of random mode.
60 * This preserves the current playing item, so that the user
61 * can return to it if needed. (See #4472)
63 playlist_private_t
*p_sys
= pl_priv(p_playlist
);
64 playlist_item_t
*p_new
= p_sys
->status
.p_item
;
65 ResetCurrentlyPlaying( p_playlist
, NULL
);
67 ResyncCurrentIndex( p_playlist
, p_new
);
75 * When there are one or more pending corks, playback should be paused.
76 * This is used for audio policy.
77 * \warning Always add and remove a cork with var_IncInteger() and var_DecInteger().
78 * var_Get() and var_Set() are prone to race conditions.
80 static int CorksCallback( vlc_object_t
*obj
, char const *var
,
81 vlc_value_t old
, vlc_value_t cur
, void *dummy
)
83 playlist_t
*pl
= (playlist_t
*)obj
;
85 msg_Dbg( obj
, "corks count: %"PRId64
" -> %"PRId64
, old
.i_int
, cur
.i_int
);
86 if( !old
.i_int
== !cur
.i_int
)
87 return VLC_SUCCESS
; /* nothing to do */
89 if( !var_InheritBool( obj
, "playlist-cork" ) )
94 msg_Dbg( obj
, "corked" );
99 msg_Dbg( obj
, "uncorked" );
100 playlist_Resume( pl
);
103 (void) var
; (void) dummy
;
107 static int RateCallback( vlc_object_t
*p_this
, char const *psz_cmd
,
108 vlc_value_t oldval
, vlc_value_t newval
, void *p
)
110 (void)psz_cmd
; (void)oldval
;(void)p
;
111 playlist_t
*p_playlist
= (playlist_t
*)p_this
;
115 if( pl_priv(p_playlist
)->p_input
)
116 var_SetFloat( pl_priv( p_playlist
)->p_input
, "rate", newval
.f_float
);
122 static int RateOffsetCallback( vlc_object_t
*obj
, char const *psz_cmd
,
123 vlc_value_t oldval
, vlc_value_t newval
, void *p_data
)
125 playlist_t
*p_playlist
= (playlist_t
*)obj
;
126 VLC_UNUSED(oldval
); VLC_UNUSED(p_data
); VLC_UNUSED(newval
);
128 static const float rates
[] = {
129 1.0/64, 1.0/32, 1.0/16, 1.0/8, 1.0/4, 1.0/3, 1.0/2, 2.0/3,
131 3.0/2, 2.0/1, 3.0/1, 4.0/1, 8.0/1, 16.0/1, 32.0/1, 64.0/1,
135 input_thread_t
*input
= pl_priv( p_playlist
)->p_input
;
136 float current_rate
= var_GetFloat( input
? VLC_OBJECT( input
) : obj
, "rate" );
139 const bool faster
= !strcmp( psz_cmd
, "rate-faster" );
140 float rate
= current_rate
* ( faster
? 1.1f
: 0.9f
);
142 /* find closest rate (if any) in the desired direction */
143 for( size_t i
= 0; i
< ARRAY_SIZE( rates
); ++i
)
145 if( ( faster
&& rates
[i
] > rate
) ||
146 (!faster
&& rates
[i
] >= rate
&& i
) )
148 rate
= faster
? rates
[i
] : rates
[i
-1];
153 msg_Dbg( p_playlist
, "adjusting rate from %f to %f (%s)",
154 current_rate
, rate
, faster
? "faster" : "slower" );
156 return var_SetFloat( p_playlist
, "rate", rate
);
159 static int VideoSplitterCallback( vlc_object_t
*p_this
, char const *psz_cmd
,
160 vlc_value_t oldval
, vlc_value_t newval
, void *p_data
)
162 playlist_t
*p_playlist
= (playlist_t
*)p_this
;
163 VLC_UNUSED(psz_cmd
); VLC_UNUSED(oldval
); VLC_UNUSED(p_data
); VLC_UNUSED(newval
);
167 /* Force the input to restart the video ES to force a vout recreation */
168 input_thread_t
*p_input
= pl_priv( p_playlist
)->p_input
;
171 const double f_position
= var_GetFloat( p_input
, "position" );
172 input_Control( p_input
, INPUT_RESTART_ES
, -VIDEO_ES
);
173 var_SetFloat( p_input
, "position", f_position
);
183 * Create a playlist structure.
184 * \param p_parent the vlc object that is to be the parent of this playlist
185 * \return a pointer to the created playlist, or NULL on error
187 playlist_t
*playlist_Create( vlc_object_t
*p_parent
)
189 playlist_t
*p_playlist
;
190 playlist_private_t
*p
;
192 /* Allocate structure */
193 p
= vlc_custom_create( p_parent
, sizeof( *p
), "playlist" );
197 p_playlist
= &p
->public_data
;
199 p
->input_tree
= NULL
;
202 TAB_INIT( pl_priv(p_playlist
)->i_sds
, pl_priv(p_playlist
)->pp_sds
);
204 VariablesInit( p_playlist
);
205 vlc_mutex_init( &p
->lock
);
206 vlc_cond_init( &p
->signal
);
209 /* Initialise data structures */
210 pl_priv(p_playlist
)->i_last_playlist_id
= 0;
211 pl_priv(p_playlist
)->p_input
= NULL
;
213 ARRAY_INIT( p_playlist
->items
);
214 ARRAY_INIT( p_playlist
->current
);
216 p_playlist
->i_current_index
= 0;
217 pl_priv(p_playlist
)->b_reset_currently_playing
= true;
219 pl_priv(p_playlist
)->b_tree
= var_InheritBool( p_parent
, "playlist-tree" );
220 pl_priv(p_playlist
)->b_preparse
= var_InheritBool( p_parent
, "auto-preparse" );
222 p_playlist
->root
.p_input
= NULL
;
223 p_playlist
->root
.pp_children
= NULL
;
224 p_playlist
->root
.i_children
= 0;
225 p_playlist
->root
.i_nb_played
= 0;
226 p_playlist
->root
.i_id
= 0;
227 p_playlist
->root
.i_flags
= 0;
229 /* Create the root, playing items and meida library nodes */
230 playlist_item_t
*playing
, *ml
;
233 playing
= playlist_NodeCreate( p_playlist
, _( "Playlist" ),
234 &p_playlist
->root
, PLAYLIST_END
,
235 PLAYLIST_RO_FLAG
|PLAYLIST_NO_INHERIT_FLAG
);
236 if( var_InheritBool( p_parent
, "media-library") )
237 ml
= playlist_NodeCreate( p_playlist
, _( "Media Library" ),
238 &p_playlist
->root
, PLAYLIST_END
,
239 PLAYLIST_RO_FLAG
|PLAYLIST_NO_INHERIT_FLAG
);
244 if( unlikely(playing
== NULL
) )
247 p_playlist
->p_playing
= playing
;
248 p_playlist
->p_media_library
= ml
;
251 pl_priv(p_playlist
)->status
.p_item
= NULL
;
252 pl_priv(p_playlist
)->status
.p_node
= p_playlist
->p_playing
;
253 pl_priv(p_playlist
)->request
.b_request
= false;
254 p
->request
.input_dead
= false;
257 playlist_MLLoad( p_playlist
);
259 /* Input resources */
260 p
->p_input_resource
= input_resource_New( VLC_OBJECT( p_playlist
) );
261 if( unlikely(p
->p_input_resource
== NULL
) )
264 /* Audio output (needed for volume and device controls). */
265 audio_output_t
*aout
= input_resource_GetAout( p
->p_input_resource
);
267 input_resource_PutAout( p
->p_input_resource
, aout
);
269 /* Initialize the shared HTTP cookie jar */
271 cookies
.p_address
= vlc_http_cookies_new();
272 if ( likely(cookies
.p_address
) )
274 var_Create( p_playlist
, "http-cookies", VLC_VAR_ADDRESS
);
275 var_SetChecked( p_playlist
, "http-cookies", VLC_VAR_ADDRESS
, cookies
);
279 playlist_Activate (p_playlist
);
281 /* Add service discovery modules */
282 char *mods
= var_InheritString( p_playlist
, "services-discovery" );
286 while( (m
= strsep( &s
, " :," )) != NULL
)
287 playlist_ServicesDiscoveryAdd( p_playlist
, m
);
296 * This is not thread-safe. Any reference to the playlist is assumed gone.
297 * (In particular, all interface and services threads must have been joined).
299 * \param p_playlist the playlist object
301 void playlist_Destroy( playlist_t
*p_playlist
)
303 playlist_private_t
*p_sys
= pl_priv(p_playlist
);
305 /* Remove all services discovery */
306 playlist_ServicesDiscoveryKillAll( p_playlist
);
308 msg_Dbg( p_playlist
, "destroying" );
310 playlist_Deactivate( p_playlist
);
312 /* Release input resources */
313 assert( p_sys
->p_input
== NULL
);
314 input_resource_Release( p_sys
->p_input_resource
);
315 if( p_sys
->p_renderer
)
316 vlc_renderer_item_release( p_sys
->p_renderer
);
318 if( p_playlist
->p_media_library
!= NULL
)
319 playlist_MLDump( p_playlist
);
322 /* Release the current node */
323 set_current_status_node( p_playlist
, NULL
);
324 /* Release the current item */
325 set_current_status_item( p_playlist
, NULL
);
327 /* Destroy arrays completely - faster than one item at a time */
328 ARRAY_RESET( p_playlist
->items
);
329 ARRAY_RESET( p_playlist
->current
);
331 /* Remove all remaining items */
332 if( p_playlist
->p_media_library
!= NULL
)
334 playlist_NodeDeleteExplicit( p_playlist
, p_playlist
->p_media_library
,
335 PLAYLIST_DELETE_FORCE
);
338 playlist_NodeDeleteExplicit( p_playlist
, p_playlist
->p_playing
,
339 PLAYLIST_DELETE_FORCE
);
341 assert( p_playlist
->root
.i_children
<= 0 );
344 vlc_cond_destroy( &p_sys
->signal
);
345 vlc_mutex_destroy( &p_sys
->lock
);
347 vlc_http_cookie_jar_t
*cookies
= var_GetAddress( p_playlist
, "http-cookies" );
350 var_Destroy( p_playlist
, "http-cookies" );
351 vlc_http_cookies_destroy( cookies
);
354 vlc_object_release( p_playlist
);
357 /** Get current playing input.
359 input_thread_t
*playlist_CurrentInputLocked( playlist_t
*p_playlist
)
363 input_thread_t
*p_input
= pl_priv(p_playlist
)->p_input
;
364 if( p_input
!= NULL
)
365 vlc_object_hold( p_input
);
370 /** Get current playing input.
372 input_thread_t
* playlist_CurrentInput( playlist_t
* p_playlist
)
374 input_thread_t
* p_input
;
376 p_input
= playlist_CurrentInputLocked( p_playlist
);
385 /** Accessor for status item and status nodes.
387 playlist_item_t
* get_current_status_item( playlist_t
* p_playlist
)
391 return pl_priv(p_playlist
)->status
.p_item
;
394 playlist_item_t
* get_current_status_node( playlist_t
* p_playlist
)
398 return pl_priv(p_playlist
)->status
.p_node
;
401 void set_current_status_item( playlist_t
* p_playlist
,
402 playlist_item_t
* p_item
)
406 pl_priv(p_playlist
)->status
.p_item
= p_item
;
409 void set_current_status_node( playlist_t
* p_playlist
,
410 playlist_item_t
* p_node
)
414 pl_priv(p_playlist
)->status
.p_node
= p_node
;
417 static void VariablesInit( playlist_t
*p_playlist
)
419 /* These variables control updates */
420 var_Create( p_playlist
, "item-change", VLC_VAR_ADDRESS
);
421 var_Create( p_playlist
, "leaf-to-parent", VLC_VAR_INTEGER
);
423 var_Create( p_playlist
, "playlist-item-append", VLC_VAR_ADDRESS
);
424 var_Create( p_playlist
, "playlist-item-deleted", VLC_VAR_ADDRESS
);
426 var_Create( p_playlist
, "input-current", VLC_VAR_ADDRESS
);
428 /* Variables to control playback */
429 var_Create( p_playlist
, "playlist-autostart", VLC_VAR_BOOL
| VLC_VAR_DOINHERIT
);
430 var_Create( p_playlist
, "random", VLC_VAR_BOOL
| VLC_VAR_DOINHERIT
);
431 var_AddCallback( p_playlist
, "random", RandomCallback
, NULL
);
432 var_Create( p_playlist
, "repeat", VLC_VAR_BOOL
| VLC_VAR_DOINHERIT
);
433 var_Create( p_playlist
, "loop", VLC_VAR_BOOL
| VLC_VAR_DOINHERIT
);
434 var_Create( p_playlist
, "corks", VLC_VAR_INTEGER
);
435 var_AddCallback( p_playlist
, "corks", CorksCallback
, NULL
);
437 var_Create( p_playlist
, "rate", VLC_VAR_FLOAT
| VLC_VAR_DOINHERIT
);
438 var_AddCallback( p_playlist
, "rate", RateCallback
, NULL
);
439 var_Create( p_playlist
, "rate-slower", VLC_VAR_VOID
);
440 var_AddCallback( p_playlist
, "rate-slower", RateOffsetCallback
, NULL
);
441 var_Create( p_playlist
, "rate-faster", VLC_VAR_VOID
);
442 var_AddCallback( p_playlist
, "rate-faster", RateOffsetCallback
, NULL
);
444 var_Create( p_playlist
, "video-splitter", VLC_VAR_STRING
| VLC_VAR_DOINHERIT
);
445 var_AddCallback( p_playlist
, "video-splitter", VideoSplitterCallback
, NULL
);
447 var_Create( p_playlist
, "video-filter", VLC_VAR_STRING
| VLC_VAR_DOINHERIT
);
448 var_Create( p_playlist
, "sub-source", VLC_VAR_STRING
| VLC_VAR_DOINHERIT
);
449 var_Create( p_playlist
, "sub-filter", VLC_VAR_STRING
| VLC_VAR_DOINHERIT
);
452 var_Create( p_playlist
, "sout", VLC_VAR_STRING
| VLC_VAR_DOINHERIT
);
453 var_Create( p_playlist
, "demux-filter", VLC_VAR_STRING
| VLC_VAR_DOINHERIT
);
456 var_Create( p_playlist
, "metadata-network-access", VLC_VAR_BOOL
| VLC_VAR_DOINHERIT
);
458 /* Variables to preserve video output parameters */
459 var_Create( p_playlist
, "fullscreen", VLC_VAR_BOOL
| VLC_VAR_DOINHERIT
);
460 var_Create( p_playlist
, "video-on-top", VLC_VAR_BOOL
| VLC_VAR_DOINHERIT
);
461 var_Create( p_playlist
, "video-wallpaper", VLC_VAR_BOOL
| VLC_VAR_DOINHERIT
);
463 /* Audio output parameters */
464 var_Create( p_playlist
, "audio-filter", VLC_VAR_STRING
| VLC_VAR_DOINHERIT
);
465 var_Create( p_playlist
, "audio-device", VLC_VAR_STRING
);
466 var_Create( p_playlist
, "mute", VLC_VAR_BOOL
);
467 var_Create( p_playlist
, "volume", VLC_VAR_FLOAT
);
468 var_SetFloat( p_playlist
, "volume", -1.f
);
470 var_Create( p_playlist
, "sub-text-scale",
471 VLC_VAR_INTEGER
| VLC_VAR_DOINHERIT
| VLC_VAR_ISCOMMAND
);
474 playlist_item_t
* playlist_CurrentPlayingItem( playlist_t
* p_playlist
)
478 return pl_priv(p_playlist
)->status
.p_item
;
481 int playlist_Status( playlist_t
* p_playlist
)
483 input_thread_t
*p_input
= pl_priv(p_playlist
)->p_input
;
487 if( p_input
== NULL
)
488 return PLAYLIST_STOPPED
;
489 if( var_GetInteger( p_input
, "state" ) == PAUSE_S
)
490 return PLAYLIST_PAUSED
;
491 return PLAYLIST_RUNNING
;