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 "playlist_internal.h"
38 #include "input/resource.h"
40 /*****************************************************************************
42 *****************************************************************************/
43 static void VariablesInit( playlist_t
*p_playlist
);
45 static int RandomCallback( vlc_object_t
*p_this
, char const *psz_cmd
,
46 vlc_value_t oldval
, vlc_value_t newval
, void *a
)
48 (void)psz_cmd
; (void)oldval
; (void)newval
; (void)a
;
49 playlist_t
*p_playlist
= (playlist_t
*)p_this
;
50 bool random
= newval
.b_bool
;
55 pl_priv(p_playlist
)->b_reset_currently_playing
= true;
56 vlc_cond_signal( &pl_priv(p_playlist
)->signal
);
58 /* Shuffle and sync the playlist on activation of random mode.
59 * This preserves the current playing item, so that the user
60 * can return to it if needed. (See #4472)
62 playlist_private_t
*p_sys
= pl_priv(p_playlist
);
63 playlist_item_t
*p_new
= p_sys
->status
.p_item
;
64 ResetCurrentlyPlaying( p_playlist
, NULL
);
66 ResyncCurrentIndex( p_playlist
, p_new
);
74 * When there are one or more pending corks, playback should be paused.
75 * This is used for audio policy.
76 * \warning Always add and remove a cork with var_IncInteger() and var_DecInteger().
77 * var_Get() and var_Set() are prone to race conditions.
79 static int CorksCallback( vlc_object_t
*obj
, char const *var
,
80 vlc_value_t old
, vlc_value_t cur
, void *dummy
)
82 playlist_t
*pl
= (playlist_t
*)obj
;
84 msg_Dbg( obj
, "corks count: %"PRId64
" -> %"PRId64
, old
.i_int
, cur
.i_int
);
85 if( !old
.i_int
== !cur
.i_int
)
86 return VLC_SUCCESS
; /* nothing to do */
88 if( !var_InheritBool( obj
, "playlist-cork" ) )
93 msg_Dbg( obj
, "corked" );
98 msg_Dbg( obj
, "uncorked" );
99 playlist_Resume( pl
);
102 (void) var
; (void) dummy
;
106 static int RateCallback( vlc_object_t
*p_this
, char const *psz_cmd
,
107 vlc_value_t oldval
, vlc_value_t newval
, void *p
)
109 (void)psz_cmd
; (void)oldval
;(void)p
;
110 playlist_t
*p_playlist
= (playlist_t
*)p_this
;
114 if( pl_priv(p_playlist
)->p_input
)
115 var_SetFloat( pl_priv( p_playlist
)->p_input
, "rate", newval
.f_float
);
121 static int RateOffsetCallback( vlc_object_t
*obj
, char const *psz_cmd
,
122 vlc_value_t oldval
, vlc_value_t newval
, void *p_data
)
124 playlist_t
*p_playlist
= (playlist_t
*)obj
;
125 VLC_UNUSED(oldval
); VLC_UNUSED(p_data
); VLC_UNUSED(newval
);
127 static const float rates
[] = {
128 1.0/64, 1.0/32, 1.0/16, 1.0/8, 1.0/4, 1.0/3, 1.0/2, 2.0/3,
130 3.0/2, 2.0/1, 3.0/1, 4.0/1, 8.0/1, 16.0/1, 32.0/1, 64.0/1,
134 input_thread_t
*input
= pl_priv( p_playlist
)->p_input
;
135 float current_rate
= var_GetFloat( input
? VLC_OBJECT( input
) : obj
, "rate" );
138 const bool faster
= !strcmp( psz_cmd
, "rate-faster" );
139 float rate
= current_rate
* ( faster
? 1.1f
: 0.9f
);
141 /* find closest rate (if any) in the desired direction */
142 for( size_t i
= 0; i
< ARRAY_SIZE( rates
); ++i
)
144 if( ( faster
&& rates
[i
] > rate
) ||
145 (!faster
&& rates
[i
] >= rate
&& i
) )
147 rate
= faster
? rates
[i
] : rates
[i
-1];
152 msg_Dbg( p_playlist
, "adjusting rate from %f to %f (%s)",
153 current_rate
, rate
, faster
? "faster" : "slower" );
155 return var_SetFloat( p_playlist
, "rate", rate
);
158 static int VideoSplitterCallback( vlc_object_t
*p_this
, char const *psz_cmd
,
159 vlc_value_t oldval
, vlc_value_t newval
, void *p_data
)
161 playlist_t
*p_playlist
= (playlist_t
*)p_this
;
162 VLC_UNUSED(psz_cmd
); VLC_UNUSED(oldval
); VLC_UNUSED(p_data
); VLC_UNUSED(newval
);
166 /* Force the input to restart the video ES to force a vout recreation */
167 input_thread_t
*p_input
= pl_priv( p_playlist
)->p_input
;
170 const double f_position
= var_GetFloat( p_input
, "position" );
171 input_Control( p_input
, INPUT_RESTART_ES
, -VIDEO_ES
);
172 var_SetFloat( p_input
, "position", f_position
);
182 * Create a playlist structure.
183 * \param p_parent the vlc object that is to be the parent of this playlist
184 * \return a pointer to the created playlist, or NULL on error
186 playlist_t
*playlist_Create( vlc_object_t
*p_parent
)
188 playlist_t
*p_playlist
;
189 playlist_private_t
*p
;
191 /* Allocate structure */
192 p
= vlc_custom_create( p_parent
, sizeof( *p
), "playlist" );
196 p_playlist
= &p
->public_data
;
198 p
->input_tree
= NULL
;
201 TAB_INIT( pl_priv(p_playlist
)->i_sds
, pl_priv(p_playlist
)->pp_sds
);
203 VariablesInit( p_playlist
);
204 vlc_mutex_init( &p
->lock
);
205 vlc_cond_init( &p
->signal
);
208 /* Initialise data structures */
209 pl_priv(p_playlist
)->i_last_playlist_id
= 0;
210 pl_priv(p_playlist
)->p_input
= NULL
;
212 ARRAY_INIT( p_playlist
->items
);
213 ARRAY_INIT( p_playlist
->current
);
215 p_playlist
->i_current_index
= 0;
216 pl_priv(p_playlist
)->b_reset_currently_playing
= true;
218 pl_priv(p_playlist
)->b_tree
= var_InheritBool( p_parent
, "playlist-tree" );
219 pl_priv(p_playlist
)->b_preparse
= var_InheritBool( p_parent
, "auto-preparse" );
221 p_playlist
->root
.p_input
= NULL
;
222 p_playlist
->root
.pp_children
= NULL
;
223 p_playlist
->root
.i_children
= 0;
224 p_playlist
->root
.i_nb_played
= 0;
225 p_playlist
->root
.i_id
= 0;
226 p_playlist
->root
.i_flags
= 0;
228 /* Create the root, playing items and meida library nodes */
229 playlist_item_t
*playing
, *ml
;
232 playing
= playlist_NodeCreate( p_playlist
, _( "Playlist" ),
233 &p_playlist
->root
, PLAYLIST_END
,
234 PLAYLIST_RO_FLAG
|PLAYLIST_NO_INHERIT_FLAG
);
235 if( var_InheritBool( p_parent
, "media-library") )
236 ml
= playlist_NodeCreate( p_playlist
, _( "Media Library" ),
237 &p_playlist
->root
, PLAYLIST_END
,
238 PLAYLIST_RO_FLAG
|PLAYLIST_NO_INHERIT_FLAG
);
243 if( unlikely(playing
== NULL
) )
246 p_playlist
->p_playing
= playing
;
247 p_playlist
->p_media_library
= ml
;
250 pl_priv(p_playlist
)->status
.p_item
= NULL
;
251 pl_priv(p_playlist
)->status
.p_node
= p_playlist
->p_playing
;
252 pl_priv(p_playlist
)->request
.b_request
= false;
255 playlist_MLLoad( p_playlist
);
257 /* Input resources */
258 p
->p_input_resource
= input_resource_New( VLC_OBJECT( p_playlist
) );
259 if( unlikely(p
->p_input_resource
== NULL
) )
262 /* Audio output (needed for volume and device controls). */
263 audio_output_t
*aout
= input_resource_GetAout( p
->p_input_resource
);
265 input_resource_PutAout( p
->p_input_resource
, aout
);
267 /* Initialize the shared HTTP cookie jar */
269 cookies
.p_address
= vlc_http_cookies_new();
270 if ( likely(cookies
.p_address
) )
272 var_Create( p_playlist
, "http-cookies", VLC_VAR_ADDRESS
);
273 var_SetChecked( p_playlist
, "http-cookies", VLC_VAR_ADDRESS
, cookies
);
277 playlist_Activate (p_playlist
);
279 /* Add service discovery modules */
280 char *mods
= var_InheritString( p_playlist
, "services-discovery" );
284 while( (m
= strsep( &s
, " :," )) != NULL
)
285 playlist_ServicesDiscoveryAdd( p_playlist
, m
);
294 * This is not thread-safe. Any reference to the playlist is assumed gone.
295 * (In particular, all interface and services threads must have been joined).
297 * \param p_playlist the playlist object
299 void playlist_Destroy( playlist_t
*p_playlist
)
301 playlist_private_t
*p_sys
= pl_priv(p_playlist
);
303 /* Remove all services discovery */
304 playlist_ServicesDiscoveryKillAll( p_playlist
);
306 msg_Dbg( p_playlist
, "destroying" );
308 playlist_Deactivate( p_playlist
);
310 /* Release input resources */
311 assert( p_sys
->p_input
== NULL
);
312 input_resource_Release( p_sys
->p_input_resource
);
314 if( p_playlist
->p_media_library
!= NULL
)
315 playlist_MLDump( p_playlist
);
318 /* Release the current node */
319 set_current_status_node( p_playlist
, NULL
);
320 /* Release the current item */
321 set_current_status_item( p_playlist
, NULL
);
323 /* Destroy arrays completely - faster than one item at a time */
324 ARRAY_RESET( p_playlist
->items
);
325 ARRAY_RESET( p_playlist
->current
);
327 /* Remove all remaining items */
328 if( p_playlist
->p_media_library
!= NULL
)
330 playlist_NodeDeleteExplicit( p_playlist
, p_playlist
->p_media_library
,
331 PLAYLIST_DELETE_FORCE
);
334 playlist_NodeDeleteExplicit( p_playlist
, p_playlist
->p_playing
,
335 PLAYLIST_DELETE_FORCE
);
337 assert( p_playlist
->root
.i_children
<= 0 );
340 vlc_cond_destroy( &p_sys
->signal
);
341 vlc_mutex_destroy( &p_sys
->lock
);
343 vlc_http_cookie_jar_t
*cookies
= var_GetAddress( p_playlist
, "http-cookies" );
346 var_Destroy( p_playlist
, "http-cookies" );
347 vlc_http_cookies_destroy( cookies
);
350 vlc_object_release( p_playlist
);
353 /** Get current playing input.
355 input_thread_t
*playlist_CurrentInputLocked( playlist_t
*p_playlist
)
359 input_thread_t
*p_input
= pl_priv(p_playlist
)->p_input
;
360 if( p_input
!= NULL
)
361 vlc_object_hold( p_input
);
366 /** Get current playing input.
368 input_thread_t
* playlist_CurrentInput( playlist_t
* p_playlist
)
370 input_thread_t
* p_input
;
372 p_input
= playlist_CurrentInputLocked( p_playlist
);
381 /** Accessor for status item and status nodes.
383 playlist_item_t
* get_current_status_item( playlist_t
* p_playlist
)
387 return pl_priv(p_playlist
)->status
.p_item
;
390 playlist_item_t
* get_current_status_node( playlist_t
* p_playlist
)
394 return pl_priv(p_playlist
)->status
.p_node
;
397 void set_current_status_item( playlist_t
* p_playlist
,
398 playlist_item_t
* p_item
)
402 pl_priv(p_playlist
)->status
.p_item
= p_item
;
405 void set_current_status_node( playlist_t
* p_playlist
,
406 playlist_item_t
* p_node
)
410 pl_priv(p_playlist
)->status
.p_node
= p_node
;
413 static void VariablesInit( playlist_t
*p_playlist
)
415 /* These variables control updates */
416 var_Create( p_playlist
, "item-change", VLC_VAR_ADDRESS
);
417 var_Create( p_playlist
, "leaf-to-parent", VLC_VAR_INTEGER
);
419 var_Create( p_playlist
, "playlist-item-append", VLC_VAR_ADDRESS
);
420 var_Create( p_playlist
, "playlist-item-deleted", VLC_VAR_ADDRESS
);
422 var_Create( p_playlist
, "input-current", VLC_VAR_ADDRESS
);
424 /* Variables to control playback */
425 var_Create( p_playlist
, "playlist-autostart", VLC_VAR_BOOL
| VLC_VAR_DOINHERIT
);
426 var_Create( p_playlist
, "random", VLC_VAR_BOOL
| VLC_VAR_DOINHERIT
);
427 var_AddCallback( p_playlist
, "random", RandomCallback
, NULL
);
428 var_Create( p_playlist
, "repeat", VLC_VAR_BOOL
| VLC_VAR_DOINHERIT
);
429 var_Create( p_playlist
, "loop", VLC_VAR_BOOL
| VLC_VAR_DOINHERIT
);
430 var_Create( p_playlist
, "corks", VLC_VAR_INTEGER
);
431 var_AddCallback( p_playlist
, "corks", CorksCallback
, NULL
);
433 var_Create( p_playlist
, "rate", VLC_VAR_FLOAT
| VLC_VAR_DOINHERIT
);
434 var_AddCallback( p_playlist
, "rate", RateCallback
, NULL
);
435 var_Create( p_playlist
, "rate-slower", VLC_VAR_VOID
);
436 var_AddCallback( p_playlist
, "rate-slower", RateOffsetCallback
, NULL
);
437 var_Create( p_playlist
, "rate-faster", VLC_VAR_VOID
);
438 var_AddCallback( p_playlist
, "rate-faster", RateOffsetCallback
, NULL
);
440 var_Create( p_playlist
, "video-splitter", VLC_VAR_STRING
| VLC_VAR_DOINHERIT
);
441 var_AddCallback( p_playlist
, "video-splitter", VideoSplitterCallback
, NULL
);
443 var_Create( p_playlist
, "video-filter", VLC_VAR_STRING
| VLC_VAR_DOINHERIT
);
444 var_Create( p_playlist
, "sub-source", VLC_VAR_STRING
| VLC_VAR_DOINHERIT
);
445 var_Create( p_playlist
, "sub-filter", VLC_VAR_STRING
| VLC_VAR_DOINHERIT
);
448 var_Create( p_playlist
, "sout", VLC_VAR_STRING
| VLC_VAR_DOINHERIT
);
449 var_Create( p_playlist
, "demux-filter", VLC_VAR_STRING
| VLC_VAR_DOINHERIT
);
452 var_Create( p_playlist
, "metadata-network-access", VLC_VAR_BOOL
| VLC_VAR_DOINHERIT
);
454 /* Variables to preserve video output parameters */
455 var_Create( p_playlist
, "fullscreen", VLC_VAR_BOOL
| VLC_VAR_DOINHERIT
);
456 var_Create( p_playlist
, "video-on-top", VLC_VAR_BOOL
| VLC_VAR_DOINHERIT
);
457 var_Create( p_playlist
, "video-wallpaper", VLC_VAR_BOOL
| VLC_VAR_DOINHERIT
);
459 /* Audio output parameters */
460 var_Create( p_playlist
, "audio-filter", VLC_VAR_STRING
| VLC_VAR_DOINHERIT
);
461 var_Create( p_playlist
, "audio-device", VLC_VAR_STRING
);
462 var_Create( p_playlist
, "mute", VLC_VAR_BOOL
);
463 var_Create( p_playlist
, "volume", VLC_VAR_FLOAT
);
464 var_SetFloat( p_playlist
, "volume", -1.f
);
466 var_Create( p_playlist
, "sub-text-scale",
467 VLC_VAR_INTEGER
| VLC_VAR_DOINHERIT
| VLC_VAR_ISCOMMAND
);
470 playlist_item_t
* playlist_CurrentPlayingItem( playlist_t
* p_playlist
)
474 return pl_priv(p_playlist
)->status
.p_item
;
477 int playlist_Status( playlist_t
* p_playlist
)
479 input_thread_t
*p_input
= pl_priv(p_playlist
)->p_input
;
483 if( p_input
== NULL
)
484 return PLAYLIST_STOPPED
;
485 if( var_GetInteger( p_input
, "state" ) == PAUSE_S
)
486 return PLAYLIST_PAUSED
;
487 return PLAYLIST_RUNNING
;