1 /*****************************************************************************
2 * control.c : Handle control of the playlist & running through it
3 *****************************************************************************
4 * Copyright (C) 1999-2004 the VideoLAN team
5 * $Id: /local/vlc/0.8.6-playlist-vlm/src/playlist/playlist.c 13741 2006-03-21T19:29:39.792444Z zorglub $
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 *****************************************************************************/
29 #include "vlc_playlist.h"
30 #include "playlist_internal.h"
33 /*****************************************************************************
35 *****************************************************************************/
36 static int PlaylistVAControl( playlist_t
* p_playlist
, int i_query
, va_list args
);
38 static void PreparseEnqueueItemSub( playlist_t
*, playlist_item_t
* );
40 /*****************************************************************************
42 *****************************************************************************/
44 playlist_t
*__pl_Yield( vlc_object_t
*p_this
)
46 playlist_t
*pl
= p_this
->p_libvlc
->p_playlist
;
48 vlc_object_yield( pl
);
52 void __pl_Release( vlc_object_t
*p_this
)
54 playlist_t
*pl
= p_this
->p_libvlc
->p_playlist
;
56 vlc_object_release( pl
);
59 int playlist_Control( playlist_t
* p_playlist
, int i_query
,
60 vlc_bool_t b_locked
, ... )
64 va_start( args
, b_locked
);
65 if( !b_locked
) PL_LOCK
;
66 i_result
= PlaylistVAControl( p_playlist
, i_query
, args
);
68 if( !b_locked
) PL_UNLOCK
;
73 static int PlaylistVAControl( playlist_t
* p_playlist
, int i_query
, va_list args
)
75 playlist_item_t
*p_item
, *p_node
;
78 if( playlist_IsEmpty( p_playlist
) )
84 p_playlist
->request
.i_status
= PLAYLIST_STOPPED
;
85 p_playlist
->request
.b_request
= VLC_TRUE
;
86 p_playlist
->request
.p_item
= NULL
;
89 // Node can be null, it will keep the same. Use with care ...
90 // Item null = take the first child of node
91 case PLAYLIST_VIEWPLAY
:
92 p_node
= (playlist_item_t
*)va_arg( args
, playlist_item_t
* );
93 p_item
= (playlist_item_t
*)va_arg( args
, playlist_item_t
* );
96 p_node
= p_playlist
->status
.p_node
;
99 p_playlist
->request
.i_status
= PLAYLIST_RUNNING
;
100 p_playlist
->request
.i_skip
= 0;
101 p_playlist
->request
.b_request
= VLC_TRUE
;
102 p_playlist
->request
.p_node
= p_node
;
103 p_playlist
->request
.p_item
= p_item
;
104 if( p_item
&& var_GetBool( p_playlist
, "random" ) )
105 p_playlist
->b_reset_currently_playing
= VLC_TRUE
;
109 if( p_playlist
->p_input
)
111 val
.i_int
= PLAYING_S
;
112 var_Set( p_playlist
->p_input
, "state", val
);
117 p_playlist
->request
.i_status
= PLAYLIST_RUNNING
;
118 p_playlist
->request
.b_request
= VLC_TRUE
;
119 p_playlist
->request
.p_node
= p_playlist
->status
.p_node
;
120 p_playlist
->request
.p_item
= p_playlist
->status
.p_item
;
121 p_playlist
->request
.i_skip
= 0;
125 case PLAYLIST_AUTOPLAY
:
126 // AUTOPLAY is an ugly hack for initial status.
127 // Hopefully it will disappear
128 p_playlist
->status
.i_status
= PLAYLIST_RUNNING
;
129 p_playlist
->request
.p_node
= p_playlist
->status
.p_node
;
130 p_playlist
->request
.b_request
= VLC_FALSE
;
135 if( p_playlist
->p_input
)
136 var_Get( p_playlist
->p_input
, "state", &val
);
138 if( val
.i_int
== PAUSE_S
)
140 p_playlist
->status
.i_status
= PLAYLIST_RUNNING
;
141 if( p_playlist
->p_input
)
143 val
.i_int
= PLAYING_S
;
144 var_Set( p_playlist
->p_input
, "state", val
);
149 p_playlist
->status
.i_status
= PLAYLIST_PAUSED
;
150 if( p_playlist
->p_input
)
153 var_Set( p_playlist
->p_input
, "state", val
);
159 p_playlist
->request
.p_node
= p_playlist
->status
.p_node
;
160 p_playlist
->request
.p_item
= p_playlist
->status
.p_item
;
161 p_playlist
->request
.i_skip
= (int) va_arg( args
, int );
162 /* if already running, keep running */
163 if( p_playlist
->status
.i_status
!= PLAYLIST_STOPPED
)
164 p_playlist
->request
.i_status
= p_playlist
->status
.i_status
;
165 p_playlist
->request
.b_request
= VLC_TRUE
;
169 msg_Err( p_playlist
, "unknown playlist query" );
173 vlc_cond_signal( &p_playlist
->object_wait
);
178 /*****************************************************************************
180 *****************************************************************************/
181 /** Enqueue an item for preparsing */
182 int playlist_PreparseEnqueue( playlist_t
*p_playlist
,
183 input_item_t
*p_item
)
185 vlc_mutex_lock( &p_playlist
->p_preparse
->object_lock
);
186 vlc_gc_incref( p_item
);
187 INSERT_ELEM( p_playlist
->p_preparse
->pp_waiting
,
188 p_playlist
->p_preparse
->i_waiting
,
189 p_playlist
->p_preparse
->i_waiting
,
191 vlc_cond_signal( &p_playlist
->p_preparse
->object_wait
);
192 vlc_mutex_unlock( &p_playlist
->p_preparse
->object_lock
);
196 /** Enqueue a playlist item or a node for peparsing.
197 * This function should be entered without playlist and preparser locks */
198 int playlist_PreparseEnqueueItem( playlist_t
*p_playlist
,
199 playlist_item_t
*p_item
)
201 vlc_mutex_lock( &p_playlist
->object_lock
);
202 vlc_mutex_lock( &p_playlist
->p_preparse
->object_lock
);
203 PreparseEnqueueItemSub( p_playlist
, p_item
);
204 vlc_mutex_unlock( &p_playlist
->p_preparse
->object_lock
);
205 vlc_mutex_unlock( &p_playlist
->object_lock
);
209 int playlist_AskForArtEnqueue( playlist_t
*p_playlist
,
210 input_item_t
*p_item
)
215 p
.b_fetch_art
= VLC_TRUE
;
217 vlc_mutex_lock( &p_playlist
->p_fetcher
->object_lock
);
218 for( i
= 0; i
< p_playlist
->p_fetcher
->i_waiting
&&
219 p_playlist
->p_fetcher
->p_waiting
->b_fetch_art
== VLC_TRUE
;
221 vlc_gc_incref( p_item
);
222 INSERT_ELEM( p_playlist
->p_fetcher
->p_waiting
,
223 p_playlist
->p_fetcher
->i_waiting
,
225 vlc_cond_signal( &p_playlist
->p_fetcher
->object_wait
);
226 vlc_mutex_unlock( &p_playlist
->p_fetcher
->object_lock
);
230 static void PreparseEnqueueItemSub( playlist_t
*p_playlist
,
231 playlist_item_t
*p_item
)
234 if( p_item
->i_children
== -1 )
236 vlc_gc_incref( p_item
);
237 INSERT_ELEM( p_playlist
->p_preparse
->pp_waiting
,
238 p_playlist
->p_preparse
->i_waiting
,
239 p_playlist
->p_preparse
->i_waiting
,
244 for( i
= 0; i
< p_item
->i_children
; i
++)
246 PreparseEnqueueItemSub( p_playlist
, p_item
->pp_children
[i
] );
251 /*****************************************************************************
253 *****************************************************************************/
255 static void ResyncCurrentIndex(playlist_t
*p_playlist
, playlist_item_t
*p_cur
)
257 PL_DEBUG("resyncing on %s", PLI_NAME(p_cur
) );
258 /* Simply resync index */
260 p_playlist
->i_current_index
= -1;
261 for( i
= 0 ; i
< p_playlist
->current
.i_size
; i
++ )
263 if( ARRAY_VAL(p_playlist
->current
, i
) == p_cur
)
265 p_playlist
->i_current_index
= i
;
269 PL_DEBUG("%s is at %i", PLI_NAME(p_cur
), p_playlist
->i_current_index
);
272 void ResetCurrentlyPlaying( playlist_t
*p_playlist
, vlc_bool_t b_random
,
273 playlist_item_t
*p_cur
)
275 playlist_item_t
*p_next
= NULL
;
276 stats_TimerStart( p_playlist
, "Items array build",
277 STATS_TIMER_PLAYLIST_BUILD
);
278 PL_DEBUG("rebuilding array of current - root %s",
279 PLI_NAME(p_playlist
->status
.p_node
) );
280 ARRAY_RESET(p_playlist
->current
);
281 p_playlist
->i_current_index
= -1;
284 /** FIXME: this is *slow* */
285 p_next
= playlist_GetNextLeaf( p_playlist
,
286 p_playlist
->status
.p_node
,
287 p_next
, VLC_TRUE
, VLC_FALSE
);
290 if( p_next
== p_cur
)
291 p_playlist
->i_current_index
= p_playlist
->current
.i_size
;
292 ARRAY_APPEND( p_playlist
->current
, p_next
);
296 PL_DEBUG("rebuild done - %i items, index %i", p_playlist
->current
.i_size
,
297 p_playlist
->i_current_index
);
300 /* Shuffle the array */
301 srand( (unsigned int)mdate() );
304 for( j
= p_playlist
->current
.i_size
- 1; j
> 0; j
-- )
307 int i
= rand() % (j
+1); /* between 0 and j */
308 playlist_item_t
*p_tmp
;
309 p_tmp
= ARRAY_VAL(p_playlist
->current
, i
);
310 ARRAY_VAL(p_playlist
->current
,i
) = ARRAY_VAL(p_playlist
->current
,j
);
311 ARRAY_VAL(p_playlist
->current
,j
) = p_tmp
;
314 p_playlist
->b_reset_currently_playing
= VLC_FALSE
;
315 stats_TimerStop( p_playlist
, STATS_TIMER_PLAYLIST_BUILD
);
318 /** This function calculates the next playlist item, depending
319 * on the playlist course mode (forward, backward, random, view,...). */
320 playlist_item_t
* playlist_NextItem( playlist_t
*p_playlist
)
322 playlist_item_t
*p_new
= NULL
;
325 vlc_bool_t b_loop
= var_GetBool( p_playlist
, "loop" );
326 vlc_bool_t b_random
= var_GetBool( p_playlist
, "random" );
327 vlc_bool_t b_repeat
= var_GetBool( p_playlist
, "repeat" );
328 vlc_bool_t b_playstop
= var_GetBool( p_playlist
, "play-and-stop" );
330 /* Handle quickly a few special cases */
331 /* No items to play */
332 if( p_playlist
->items
.i_size
== 0 )
334 msg_Info( p_playlist
, "playlist is empty" );
338 /* Repeat and play/stop */
339 if( !p_playlist
->request
.b_request
&& b_repeat
== VLC_TRUE
&&
340 p_playlist
->status
.p_item
)
342 msg_Dbg( p_playlist
,"repeating item" );
343 return p_playlist
->status
.p_item
;
345 if( !p_playlist
->request
.b_request
&& b_playstop
== VLC_TRUE
)
347 msg_Dbg( p_playlist
,"stopping (play and stop)");
351 if( !p_playlist
->request
.b_request
&& p_playlist
->status
.p_item
)
353 playlist_item_t
*p_parent
= p_playlist
->status
.p_item
;
356 if( p_parent
->i_flags
& PLAYLIST_SKIP_FLAG
)
358 msg_Dbg( p_playlist
, "blocking item, stopping") ;
361 p_parent
= p_parent
->p_parent
;
365 /* Start the real work */
366 if( p_playlist
->request
.b_request
)
368 p_new
= p_playlist
->request
.p_item
;
369 i_skip
= p_playlist
->request
.i_skip
;
370 PL_DEBUG( "processing request item %s node %s skip %i",
371 PLI_NAME( p_playlist
->request
.p_item
),
372 PLI_NAME( p_playlist
->request
.p_node
), i_skip
);
374 if( p_playlist
->request
.p_node
&&
375 p_playlist
->request
.p_node
!= p_playlist
->status
.p_node
)
377 p_playlist
->status
.p_node
= p_playlist
->request
.p_node
;
378 p_playlist
->b_reset_currently_playing
= VLC_TRUE
;
381 /* If we are asked for a node, dont take it */
382 if( i_skip
== 0 && ( p_new
== NULL
|| p_new
->i_children
!= -1 ) )
385 if( p_playlist
->b_reset_currently_playing
)
386 /* A bit too bad to reset twice ... */
387 ResetCurrentlyPlaying( p_playlist
, b_random
, p_new
);
389 ResyncCurrentIndex( p_playlist
, p_new
);
391 p_playlist
->i_current_index
= -1;
393 if( p_playlist
->current
.i_size
&& (i_skip
> 0) )
395 if( p_playlist
->i_current_index
< -1 )
396 p_playlist
->i_current_index
= -1;
397 for( i
= i_skip
; i
> 0 ; i
-- )
399 p_playlist
->i_current_index
++;
400 if( p_playlist
->i_current_index
>= p_playlist
->current
.i_size
)
402 PL_DEBUG( "looping - restarting at beginning of node" );
403 p_playlist
->i_current_index
= 0;
406 p_new
= ARRAY_VAL( p_playlist
->current
,
407 p_playlist
->i_current_index
);
409 else if( p_playlist
->current
.i_size
&& (i_skip
< 0) )
411 for( i
= i_skip
; i
< 0 ; i
++ )
413 p_playlist
->i_current_index
--;
414 if( p_playlist
->i_current_index
<= -1 )
416 PL_DEBUG( "looping - restarting at end of node" );
417 p_playlist
->i_current_index
= p_playlist
->current
.i_size
-1;
420 p_new
= ARRAY_VAL( p_playlist
->current
,
421 p_playlist
->i_current_index
);
423 /* Clear the request */
424 p_playlist
->request
.b_request
= VLC_FALSE
;
426 /* "Automatic" item change ( next ) */
429 PL_DEBUG( "changing item without a request (current %i/%i)",
430 p_playlist
->i_current_index
, p_playlist
->current
.i_size
);
431 /* Cant go to next from current item */
432 if( p_playlist
->status
.p_item
&&
433 p_playlist
->status
.p_item
->i_flags
& PLAYLIST_SKIP_FLAG
)
436 if( p_playlist
->b_reset_currently_playing
)
437 ResetCurrentlyPlaying( p_playlist
, b_random
,
438 p_playlist
->status
.p_item
);
440 p_playlist
->i_current_index
++;
441 if( p_playlist
->i_current_index
== p_playlist
->current
.i_size
)
443 if( !b_loop
|| p_playlist
->current
.i_size
== 0 ) return NULL
;
444 p_playlist
->i_current_index
= 0;
446 PL_DEBUG( "using item %i", p_playlist
->i_current_index
);
447 if ( p_playlist
->current
.i_size
== 0 ) return NULL
;
449 p_new
= ARRAY_VAL( p_playlist
->current
, p_playlist
->i_current_index
);
450 /* The new item can't be autoselected */
451 if( p_new
!= NULL
&& p_new
->i_flags
& PLAYLIST_SKIP_FLAG
)
457 /** Start the input for an item */
458 int playlist_PlayItem( playlist_t
*p_playlist
, playlist_item_t
*p_item
)
461 input_item_t
*p_input
= p_item
->p_input
;
462 int i_activity
= var_GetInteger( p_playlist
, "activity") ;
464 msg_Dbg( p_playlist
, "creating new input thread" );
466 p_input
->i_nb_played
++;
467 p_playlist
->status
.p_item
= p_item
;
469 p_playlist
->status
.i_status
= PLAYLIST_RUNNING
;
471 var_SetInteger( p_playlist
, "activity", i_activity
+
472 DEFAULT_INPUT_ACTIVITY
);
473 p_playlist
->p_input
= input_CreateThread( p_playlist
, p_input
);
475 char *psz_uri
= input_item_GetURI( p_item
->p_input
);
476 if( psz_uri
&& ( !strncmp( psz_uri
, "directory:", 10 ) ||
477 !strncmp( psz_uri
, "vlc:", 4 ) ) )
484 if( p_playlist
->p_fetcher
&&
485 p_playlist
->p_fetcher
->i_art_policy
== ALBUM_ART_WHEN_PLAYED
)
487 vlc_bool_t b_has_art
;
489 char *psz_arturl
, *psz_name
;
490 psz_arturl
= input_item_GetArtURL( p_input
);
491 psz_name
= input_item_GetName( p_input
);
493 /* p_input->p_meta should not be null after a successfull CreateThread*/
494 b_has_art
= !EMPTY_STR( psz_arturl
);
498 PL_DEBUG( "requesting art for %s", psz_name
);
499 playlist_AskForArtEnqueue( p_playlist
, p_input
);
505 val
.i_int
= p_input
->i_id
;
506 vlc_mutex_unlock( &p_playlist
->object_lock
);
507 var_Set( p_playlist
, "playlist-current", val
);
508 vlc_mutex_lock( &p_playlist
->object_lock
);