1 /*****************************************************************************
2 * playlist.c : Playlist management functions
3 *****************************************************************************
4 * Copyright (C) 1999-2001 VideoLAN
5 * $Id: playlist.c,v 1.66 2003/11/29 11:12:46 fenrir Exp $
7 * Authors: Samuel Hocevar <sam@zoy.org>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 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 General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
22 *****************************************************************************/
23 #include <stdlib.h> /* free(), strtol() */
24 #include <stdio.h> /* sprintf() */
25 #include <string.h> /* strerror() */
31 #include "stream_control.h"
32 #include "input_ext-intf.h"
34 #include "vlc_playlist.h"
36 #define PLAYLIST_FILE_HEADER_0_5 "# vlc playlist file version 0.5"
37 #define PLAYLIST_FILE_HEADER_0_6 "# vlc playlist file version 0.6"
39 /*****************************************************************************
41 *****************************************************************************/
42 static void RunThread ( playlist_t * );
43 static void SkipItem ( playlist_t *, int );
44 static void PlayItem ( playlist_t * );
46 static void Poubellize ( playlist_t *, input_thread_t * );
51 * Create a playlist structure.
52 * \param p_parent the vlc object that is to be the parent of this playlist
53 * \return a pointer to the created playlist, or NULL on error
55 playlist_t * __playlist_Create ( vlc_object_t *p_parent )
57 playlist_t *p_playlist;
60 /* Allocate structure */
61 p_playlist = vlc_object_create( p_parent, VLC_OBJECT_PLAYLIST );
64 msg_Err( p_parent, "out of memory" );
68 var_Create( p_playlist, "intf-change", VLC_VAR_BOOL );
69 val.b_bool = VLC_TRUE;
70 var_Set( p_playlist, "intf-change", val );
72 var_Create( p_playlist, "intf-popupmenu", VLC_VAR_BOOL );
74 var_Create( p_playlist, "intf-show", VLC_VAR_BOOL );
75 val.b_bool = VLC_TRUE;
76 var_Set( p_playlist, "intf-show", val );
78 var_Create( p_playlist, "random", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
79 var_Create( p_playlist, "repeat", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
80 var_Create( p_playlist, "loop", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
82 p_playlist->p_input = NULL;
83 p_playlist->i_status = PLAYLIST_STOPPED;
84 p_playlist->i_index = -1;
85 p_playlist->i_size = 0;
86 p_playlist->pp_items = NULL;
88 p_playlist->i_groups = 0;
89 p_playlist->pp_groups = NULL;
90 p_playlist->i_max_id = 0;
92 playlist_CreateGroup( p_playlist, "Normal" );
94 if( vlc_thread_create( p_playlist, "playlist", RunThread,
95 VLC_THREAD_PRIORITY_LOW, VLC_TRUE ) )
97 msg_Err( p_playlist, "cannot spawn playlist thread" );
98 vlc_object_destroy( p_playlist );
102 /* The object has been initialized, now attach it */
103 vlc_object_attach( p_playlist, p_parent );
109 * Destroy the playlist.
111 * Delete all items in the playlist and free the playlist structure.
112 * \param p_playlist the playlist structure to destroy
114 void playlist_Destroy( playlist_t * p_playlist )
116 p_playlist->b_die = 1;
118 vlc_thread_join( p_playlist );
120 var_Destroy( p_playlist, "intf-change" );
122 while( p_playlist->i_groups > 0 )
124 playlist_DeleteGroup( p_playlist, p_playlist->pp_groups[0]->i_id );
127 while( p_playlist->i_size > 0 )
129 playlist_Delete( p_playlist, 0 );
132 vlc_object_destroy( p_playlist );
137 * Do a playlist action
139 * \param p_playlist the playlist to do the command on
140 * \param i_command the command to do
141 * \param i_arg the argument to the command. See playlist_command_t for details
143 void playlist_Command( playlist_t * p_playlist, playlist_command_t i_command,
148 vlc_mutex_lock( &p_playlist->object_lock );
153 p_playlist->i_status = PLAYLIST_STOPPED;
154 if( p_playlist->p_input )
156 input_StopThread( p_playlist->p_input );
161 p_playlist->i_status = PLAYLIST_RUNNING;
162 if( !p_playlist->p_input && p_playlist->i_enabled != 0 )
164 PlayItem( p_playlist );
166 if( p_playlist->p_input )
168 val.i_int = PLAYING_S;
169 var_Set( p_playlist->p_input, "state", val );
175 if( p_playlist->p_input )
176 var_Get( p_playlist->p_input, "state", &val );
178 if( val.i_int == PAUSE_S )
180 p_playlist->i_status = PLAYLIST_RUNNING;
181 if( p_playlist->p_input )
183 val.i_int = PLAYING_S;
184 var_Set( p_playlist->p_input, "state", val );
189 p_playlist->i_status = PLAYLIST_PAUSED;
190 if( p_playlist->p_input )
193 var_Set( p_playlist->p_input, "state", val );
199 p_playlist->i_status = PLAYLIST_STOPPED;
200 if( p_playlist->i_enabled == 0)
204 SkipItem( p_playlist, i_arg );
205 if( p_playlist->p_input )
207 input_StopThread( p_playlist->p_input );
209 p_playlist->i_status = PLAYLIST_RUNNING;
213 if( i_arg >= 0 && i_arg < p_playlist->i_size &&
214 p_playlist->i_enabled != 0 )
216 p_playlist->i_index = i_arg;
217 if( p_playlist->p_input )
219 input_StopThread( p_playlist->p_input );
221 p_playlist->i_status = PLAYLIST_RUNNING;
226 msg_Err( p_playlist, "unknown playlist command" );
230 vlc_mutex_unlock( &p_playlist->object_lock );
232 val.b_bool = VLC_TRUE;
233 var_Set( p_playlist, "intf-change", val );
237 /* Following functions are local */
239 static void ObjectGarbageCollector( playlist_t *p_playlist,
241 mtime_t *pi_obj_destroyed_date )
244 if( *pi_obj_destroyed_date > mdate() )
249 if( *pi_obj_destroyed_date == 0 )
251 /* give a little time */
252 *pi_obj_destroyed_date = mdate() + 300000LL;
256 while( ( p_obj = vlc_object_find( p_playlist,
260 if( p_obj->p_parent != (vlc_object_t*)p_playlist )
262 /* only first chiled (ie unused) */
263 vlc_object_release( p_obj );
266 if( i_type == VLC_OBJECT_VOUT )
268 msg_Dbg( p_playlist, "vout garbage collector destroying 1 vout" );
269 vlc_object_detach( p_obj );
270 vlc_object_release( p_obj );
271 vout_Destroy( (vout_thread_t *)p_obj );
273 else if( i_type == VLC_OBJECT_SOUT )
275 vlc_object_release( p_obj );
276 sout_DeleteInstance( (sout_instance_t*)p_obj );
279 *pi_obj_destroyed_date = 0;
283 /*****************************************************************************
284 * RunThread: main playlist thread
285 *****************************************************************************/
286 static void RunThread ( playlist_t *p_playlist )
291 mtime_t i_vout_destroyed_date = 0;
292 mtime_t i_sout_destroyed_date = 0;
294 /* Tell above that we're ready */
295 vlc_thread_ready( p_playlist );
297 while( !p_playlist->b_die )
299 vlc_mutex_lock( &p_playlist->object_lock );
301 /* If there is an input, check that it doesn't need to die. */
302 if( p_playlist->p_input )
304 /* This input is dead. Remove it ! */
305 if( p_playlist->p_input->b_dead )
307 input_thread_t *p_input;
309 p_input = p_playlist->p_input;
310 p_playlist->p_input = NULL;
312 /* Release the playlist lock, because we may get stuck
313 * in input_DestroyThread() for some time. */
314 vlc_mutex_unlock( &p_playlist->object_lock );
317 input_DestroyThread( p_input );
319 /* Unlink current input
320 * (_after_ input_DestroyThread for vout garbage collector) */
321 vlc_object_detach( p_input );
324 vlc_object_destroy( p_input );
326 i_vout_destroyed_date = 0;
327 i_sout_destroyed_date = 0;
330 /* This input is dying, let him do */
331 else if( p_playlist->p_input->b_die )
335 /* This input has finished, ask him to die ! */
336 else if( p_playlist->p_input->b_error
337 || p_playlist->p_input->b_eof )
339 /* Check for autodeletion */
340 if( p_playlist->pp_items[p_playlist->i_index]->b_autodeletion )
342 vlc_mutex_unlock( &p_playlist->object_lock );
343 playlist_Delete( p_playlist, p_playlist->i_index );
344 vlc_mutex_lock( &p_playlist->object_lock );
347 /* Select the next playlist item */
348 SkipItem( p_playlist, 1 );
350 input_StopThread( p_playlist->p_input );
351 vlc_mutex_unlock( &p_playlist->object_lock );
353 val.b_bool = VLC_TRUE;
354 var_Set( p_playlist, "intf-change", val );
357 else if( p_playlist->p_input->stream.control.i_status != INIT_S )
359 vlc_mutex_unlock( &p_playlist->object_lock );
360 ObjectGarbageCollector( p_playlist, VLC_OBJECT_VOUT,
361 &i_vout_destroyed_date );
362 ObjectGarbageCollector( p_playlist, VLC_OBJECT_SOUT,
363 &i_sout_destroyed_date );
364 vlc_mutex_lock( &p_playlist->object_lock );
367 else if( p_playlist->i_status != PLAYLIST_STOPPED )
369 SkipItem( p_playlist, 0 );
370 PlayItem( p_playlist );
372 else if( p_playlist->i_status == PLAYLIST_STOPPED )
374 vlc_mutex_unlock( &p_playlist->object_lock );
375 ObjectGarbageCollector( p_playlist, VLC_OBJECT_SOUT,
376 &i_sout_destroyed_date );
377 ObjectGarbageCollector( p_playlist, VLC_OBJECT_VOUT,
378 &i_vout_destroyed_date );
379 vlc_mutex_lock( &p_playlist->object_lock );
381 vlc_mutex_unlock( &p_playlist->object_lock );
383 msleep( INTF_IDLE_SLEEP );
386 /* If there is an input, kill it */
389 vlc_mutex_lock( &p_playlist->object_lock );
391 if( p_playlist->p_input == NULL )
393 vlc_mutex_unlock( &p_playlist->object_lock );
397 if( p_playlist->p_input->b_dead )
399 input_thread_t *p_input;
401 /* Unlink current input */
402 p_input = p_playlist->p_input;
403 p_playlist->p_input = NULL;
404 vlc_mutex_unlock( &p_playlist->object_lock );
407 input_DestroyThread( p_input );
408 /* Unlink current input (_after_ input_DestroyThread for vout
409 * garbage collector)*/
410 vlc_object_detach( p_input );
413 vlc_object_destroy( p_input );
416 else if( p_playlist->p_input->b_die )
418 /* This input is dying, leave him alone */
421 else if( p_playlist->p_input->b_error || p_playlist->p_input->b_eof )
423 input_StopThread( p_playlist->p_input );
424 vlc_mutex_unlock( &p_playlist->object_lock );
429 p_playlist->p_input->b_eof = 1;
432 vlc_mutex_unlock( &p_playlist->object_lock );
434 msleep( INTF_IDLE_SLEEP );
437 /* close all remaining sout */
438 while( ( p_obj = vlc_object_find( p_playlist,
439 VLC_OBJECT_SOUT, FIND_CHILD ) ) )
441 vlc_object_release( p_obj );
442 sout_DeleteInstance( (sout_instance_t*)p_obj );
445 /* close all remaining vout */
446 while( ( p_obj = vlc_object_find( p_playlist,
447 VLC_OBJECT_VOUT, FIND_CHILD ) ) )
449 vlc_object_detach( p_obj );
450 vlc_object_release( p_obj );
451 vout_Destroy( (vout_thread_t *)p_obj );
455 /*****************************************************************************
456 * SkipItem: go to Xth playlist item
457 *****************************************************************************
458 * This function calculates the position of the next playlist item, depending
459 * on the playlist course mode (forward, backward, random...).
460 *****************************************************************************/
461 static void SkipItem( playlist_t *p_playlist, int i_arg )
463 int i_oldindex = p_playlist->i_index;
464 vlc_bool_t b_random, b_repeat, b_loop;
467 /* If the playlist is empty, there is no current item */
468 if( p_playlist->i_size == 0 )
470 p_playlist->i_index = -1;
474 var_Get( p_playlist, "random", &val );
475 b_random = val.b_bool;
476 var_Get( p_playlist, "repeat", &val );
477 b_repeat = val.b_bool;
478 var_Get( p_playlist, "loop", &val );
484 srand( (unsigned int)mdate() );
486 /* Simple random stuff - we cheat a bit to minimize the chances to
487 * get the same index again. */
488 i_arg = (int)((float)p_playlist->i_size * rand() / (RAND_MAX+1.0));
491 i_arg = (int)((float)p_playlist->i_size * rand() / (RAND_MAX+1.0));
498 p_playlist->i_index += i_arg;
501 if( p_playlist->i_index >= p_playlist->i_size )
503 if( p_playlist->i_status == PLAYLIST_STOPPED
507 p_playlist->i_index -= p_playlist->i_size
508 * ( p_playlist->i_index / p_playlist->i_size );
512 /* Don't loop by default: stop at playlist end */
513 p_playlist->i_index = i_oldindex;
514 p_playlist->i_status = PLAYLIST_STOPPED;
517 else if( p_playlist->i_index < 0 )
519 p_playlist->i_index = p_playlist->i_size - 1;
522 /* Check that the item is enabled */
523 if( p_playlist->pp_items[p_playlist->i_index]->b_enabled == VLC_FALSE &&
524 p_playlist->i_enabled != 0)
526 SkipItem( p_playlist , 1 );
530 /*****************************************************************************
531 * PlayItem: play current playlist item
532 *****************************************************************************
533 * This function calculates the position of the next playlist item, depending
534 * on the playlist course mode (forward, backward, random...).
535 *****************************************************************************/
536 static void PlayItem( playlist_t *p_playlist )
538 if( p_playlist->i_index == -1 )
540 if( p_playlist->i_size == 0 || p_playlist->i_enabled == 0)
545 SkipItem( p_playlist, 1 );
548 if( p_playlist->i_enabled == 0)
553 msg_Dbg( p_playlist, "creating new input thread" );
554 p_playlist->p_input = input_CreateThread( p_playlist,
555 p_playlist->pp_items[p_playlist->i_index] );