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_legacy.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" ) )
96 bool effective
= playlist_Status(pl
) == PLAYLIST_RUNNING
;
98 msg_Dbg(obj
, "corked (%seffective)", effective
? "" : "in");
99 pl_priv(pl
)->cork_effective
= effective
;
100 playlist_Control(pl
, PLAYLIST_PAUSE
, pl_Locked
);
104 bool effective
= pl_priv(pl
)->cork_effective
;
106 msg_Dbg(obj
, "uncorked (%seffective)", effective
? "" : "in");
109 playlist_Control(pl
, PLAYLIST_RESUME
, pl_Locked
);
113 (void) var
; (void) dummy
;
117 static int RateCallback( vlc_object_t
*p_this
, char const *psz_cmd
,
118 vlc_value_t oldval
, vlc_value_t newval
, void *p
)
120 (void)psz_cmd
; (void)oldval
;(void)p
;
121 playlist_t
*p_playlist
= (playlist_t
*)p_this
;
125 if( pl_priv(p_playlist
)->p_input
)
126 var_SetFloat( pl_priv( p_playlist
)->p_input
, "rate", newval
.f_float
);
132 static int RateOffsetCallback( vlc_object_t
*obj
, char const *psz_cmd
,
133 vlc_value_t oldval
, vlc_value_t newval
, void *p_data
)
135 playlist_t
*p_playlist
= (playlist_t
*)obj
;
136 VLC_UNUSED(oldval
); VLC_UNUSED(p_data
); VLC_UNUSED(newval
);
138 static const float rates
[] = {
139 1.0/64, 1.0/32, 1.0/16, 1.0/8, 1.0/4, 1.0/3, 1.0/2, 2.0/3,
141 3.0/2, 2.0/1, 3.0/1, 4.0/1, 8.0/1, 16.0/1, 32.0/1, 64.0/1,
145 input_thread_t
*input
= pl_priv( p_playlist
)->p_input
;
146 float current_rate
= var_GetFloat( input
? VLC_OBJECT( input
) : obj
, "rate" );
149 const bool faster
= !strcmp( psz_cmd
, "rate-faster" );
150 float rate
= current_rate
* ( faster
? 1.1f
: 0.9f
);
152 /* find closest rate (if any) in the desired direction */
153 for( size_t i
= 0; i
< ARRAY_SIZE( rates
); ++i
)
155 if( ( faster
&& rates
[i
] > rate
) ||
156 (!faster
&& rates
[i
] >= rate
&& i
) )
158 rate
= faster
? rates
[i
] : rates
[i
-1];
163 msg_Dbg( p_playlist
, "adjusting rate from %f to %f (%s)",
164 current_rate
, rate
, faster
? "faster" : "slower" );
166 return var_SetFloat( p_playlist
, "rate", rate
);
169 static int VideoSplitterCallback( vlc_object_t
*p_this
, char const *psz_cmd
,
170 vlc_value_t oldval
, vlc_value_t newval
, void *p_data
)
172 playlist_t
*p_playlist
= (playlist_t
*)p_this
;
173 VLC_UNUSED(psz_cmd
); VLC_UNUSED(oldval
); VLC_UNUSED(p_data
); VLC_UNUSED(newval
);
177 /* Force the input to restart the video ES to force a vout recreation */
178 input_thread_t
*p_input
= pl_priv( p_playlist
)->p_input
;
181 const double f_position
= var_GetFloat( p_input
, "position" );
182 input_Control( p_input
, INPUT_RESTART_ES_BY_ID
, -VIDEO_ES
);
183 var_SetFloat( p_input
, "position", f_position
);
193 * Create a playlist structure.
194 * \param p_parent the vlc object that is to be the parent of this playlist
195 * \return a pointer to the created playlist, or NULL on error
197 playlist_t
*playlist_Create( vlc_object_t
*p_parent
)
199 playlist_t
*p_playlist
;
200 playlist_private_t
*p
;
202 /* Allocate structure */
203 p
= vlc_custom_create( p_parent
, sizeof( *p
), "playlist" );
207 p_playlist
= &p
->public_data
;
209 p
->input_tree
= NULL
;
212 vlc_list_init(&p
->sds
);
214 VariablesInit( p_playlist
);
215 vlc_mutex_init( &p
->lock
);
216 vlc_cond_init( &p
->signal
);
219 /* Initialise data structures */
220 pl_priv(p_playlist
)->i_last_playlist_id
= 0;
221 pl_priv(p_playlist
)->p_input
= NULL
;
223 ARRAY_INIT( p_playlist
->items
);
224 ARRAY_INIT( p_playlist
->current
);
226 p_playlist
->i_current_index
= 0;
227 pl_priv(p_playlist
)->b_reset_currently_playing
= true;
229 pl_priv(p_playlist
)->b_tree
= var_InheritBool( p_parent
, "playlist-tree" );
230 pl_priv(p_playlist
)->b_preparse
= var_InheritBool( p_parent
, "auto-preparse" );
232 p_playlist
->root
.p_input
= NULL
;
233 p_playlist
->root
.pp_children
= NULL
;
234 p_playlist
->root
.i_children
= 0;
235 p_playlist
->root
.i_nb_played
= 0;
236 p_playlist
->root
.i_id
= 0;
237 p_playlist
->root
.i_flags
= 0;
239 /* Create the root, playing items nodes */
240 playlist_item_t
*playing
;
243 playing
= playlist_NodeCreate( p_playlist
, _( "Playlist" ),
244 &p_playlist
->root
, PLAYLIST_END
,
245 PLAYLIST_RO_FLAG
|PLAYLIST_NO_INHERIT_FLAG
);
248 if( unlikely(playing
== NULL
) )
251 p_playlist
->p_playing
= playing
;
254 pl_priv(p_playlist
)->status
.p_item
= NULL
;
255 pl_priv(p_playlist
)->status
.p_node
= p_playlist
->p_playing
;
256 pl_priv(p_playlist
)->request
.b_request
= false;
257 p
->request
.input_dead
= false;
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
);
319 /* Release the current node */
320 set_current_status_node( p_playlist
, NULL
);
321 /* Release the current item */
322 set_current_status_item( p_playlist
, NULL
);
324 /* Destroy arrays completely - faster than one item at a time */
325 ARRAY_RESET( p_playlist
->items
);
326 ARRAY_RESET( p_playlist
->current
);
328 /* Remove all remaining items */
329 playlist_NodeDeleteExplicit( p_playlist
, p_playlist
->p_playing
,
330 PLAYLIST_DELETE_FORCE
);
332 assert( p_playlist
->root
.i_children
<= 0 );
335 vlc_cond_destroy( &p_sys
->signal
);
336 vlc_mutex_destroy( &p_sys
->lock
);
338 vlc_http_cookie_jar_t
*cookies
= var_GetAddress( p_playlist
, "http-cookies" );
341 var_Destroy( p_playlist
, "http-cookies" );
342 vlc_http_cookies_destroy( cookies
);
345 vlc_object_release( p_playlist
);
348 /** Get current playing input.
350 input_thread_t
*playlist_CurrentInputLocked( playlist_t
*p_playlist
)
354 input_thread_t
*p_input
= pl_priv(p_playlist
)->p_input
;
355 if( p_input
!= NULL
)
356 vlc_object_hold( p_input
);
361 /** Get current playing input.
363 input_thread_t
* playlist_CurrentInput( playlist_t
* p_playlist
)
365 input_thread_t
* p_input
;
367 p_input
= playlist_CurrentInputLocked( p_playlist
);
376 /** Accessor for status item and status nodes.
378 playlist_item_t
* get_current_status_item( playlist_t
* p_playlist
)
382 return pl_priv(p_playlist
)->status
.p_item
;
385 playlist_item_t
* get_current_status_node( playlist_t
* p_playlist
)
389 return pl_priv(p_playlist
)->status
.p_node
;
392 void set_current_status_item( playlist_t
* p_playlist
,
393 playlist_item_t
* p_item
)
397 pl_priv(p_playlist
)->status
.p_item
= p_item
;
400 void set_current_status_node( playlist_t
* p_playlist
,
401 playlist_item_t
* p_node
)
405 pl_priv(p_playlist
)->status
.p_node
= p_node
;
408 static void VariablesInit( playlist_t
*p_playlist
)
410 /* These variables control updates */
411 var_Create( p_playlist
, "item-change", VLC_VAR_ADDRESS
);
412 var_Create( p_playlist
, "leaf-to-parent", VLC_VAR_INTEGER
);
414 var_Create( p_playlist
, "playlist-item-append", VLC_VAR_ADDRESS
);
415 var_Create( p_playlist
, "playlist-item-deleted", VLC_VAR_ADDRESS
);
417 var_Create( p_playlist
, "input-current", VLC_VAR_ADDRESS
);
419 /* Variables to control playback */
420 var_Create( p_playlist
, "playlist-autostart", VLC_VAR_BOOL
| VLC_VAR_DOINHERIT
);
421 var_Create( p_playlist
, "random", VLC_VAR_BOOL
| VLC_VAR_DOINHERIT
);
422 var_AddCallback( p_playlist
, "random", RandomCallback
, NULL
);
423 var_Create( p_playlist
, "repeat", VLC_VAR_BOOL
| VLC_VAR_DOINHERIT
);
424 var_Create( p_playlist
, "loop", VLC_VAR_BOOL
| VLC_VAR_DOINHERIT
);
425 var_Create( p_playlist
, "corks", VLC_VAR_INTEGER
);
426 var_AddCallback( p_playlist
, "corks", CorksCallback
, NULL
);
428 var_Create( p_playlist
, "rate", VLC_VAR_FLOAT
| VLC_VAR_DOINHERIT
);
429 var_AddCallback( p_playlist
, "rate", RateCallback
, NULL
);
430 var_Create( p_playlist
, "rate-slower", VLC_VAR_VOID
);
431 var_AddCallback( p_playlist
, "rate-slower", RateOffsetCallback
, NULL
);
432 var_Create( p_playlist
, "rate-faster", VLC_VAR_VOID
);
433 var_AddCallback( p_playlist
, "rate-faster", RateOffsetCallback
, NULL
);
435 var_Create( p_playlist
, "video-splitter", VLC_VAR_STRING
| VLC_VAR_DOINHERIT
);
436 var_AddCallback( p_playlist
, "video-splitter", VideoSplitterCallback
, NULL
);
438 var_Create( p_playlist
, "video-filter", VLC_VAR_STRING
| VLC_VAR_DOINHERIT
);
439 var_Create( p_playlist
, "sub-source", VLC_VAR_STRING
| VLC_VAR_DOINHERIT
);
440 var_Create( p_playlist
, "sub-filter", VLC_VAR_STRING
| VLC_VAR_DOINHERIT
);
443 var_Create( p_playlist
, "sout", VLC_VAR_STRING
| VLC_VAR_DOINHERIT
);
444 var_Create( p_playlist
, "demux-filter", VLC_VAR_STRING
| VLC_VAR_DOINHERIT
);
447 var_Create( p_playlist
, "metadata-network-access", VLC_VAR_BOOL
| VLC_VAR_DOINHERIT
);
449 /* Variables to preserve video output parameters */
450 var_Create( p_playlist
, "fullscreen", VLC_VAR_BOOL
| VLC_VAR_DOINHERIT
);
451 var_Create( p_playlist
, "video-on-top", VLC_VAR_BOOL
| VLC_VAR_DOINHERIT
);
452 var_Create( p_playlist
, "video-wallpaper", VLC_VAR_BOOL
| VLC_VAR_DOINHERIT
);
454 /* Audio output parameters */
455 var_Create( p_playlist
, "audio-filter", VLC_VAR_STRING
| VLC_VAR_DOINHERIT
);
456 var_Create( p_playlist
, "audio-device", VLC_VAR_STRING
);
457 var_Create( p_playlist
, "mute", VLC_VAR_BOOL
);
458 var_Create( p_playlist
, "volume", VLC_VAR_FLOAT
);
459 var_SetFloat( p_playlist
, "volume", -1.f
);
461 var_Create( p_playlist
, "sub-text-scale",
462 VLC_VAR_INTEGER
| VLC_VAR_DOINHERIT
| VLC_VAR_ISCOMMAND
);
464 /* Callbacks between interfaces */
466 /* Create a variable for showing the right click menu */
467 var_Create( p_playlist
, "intf-popupmenu", VLC_VAR_BOOL
);
469 /* Create a variable for showing the fullscreen interface */
470 var_Create( p_playlist
, "intf-toggle-fscontrol", VLC_VAR_VOID
);
472 /* Create a variable for the Boss Key */
473 var_Create( p_playlist
, "intf-boss", VLC_VAR_VOID
);
475 /* Create a variable for showing the main interface */
476 var_Create( p_playlist
, "intf-show", VLC_VAR_VOID
);
479 playlist_item_t
* playlist_CurrentPlayingItem( playlist_t
* p_playlist
)
483 return pl_priv(p_playlist
)->status
.p_item
;
486 int playlist_Status( playlist_t
* p_playlist
)
488 input_thread_t
*p_input
= pl_priv(p_playlist
)->p_input
;
492 if( p_input
== NULL
)
493 return PLAYLIST_STOPPED
;
494 if( var_GetInteger( p_input
, "state" ) == PAUSE_S
)
495 return PLAYLIST_PAUSED
;
496 return PLAYLIST_RUNNING
;