1 /*****************************************************************************
2 * playlist.c : Playlist management functions
3 *****************************************************************************
4 * Copyright (C) 1999-2001 VideoLAN
5 * $Id: playlist.c,v 1.29 2002/12/07 22:15:10 gitan 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() */
29 #include "stream_control.h"
30 #include "input_ext-intf.h"
32 #include "vlc_playlist.h"
34 #define PLAYLIST_STOPPED 0
35 #define PLAYLIST_RUNNING 1
37 /*****************************************************************************
39 *****************************************************************************/
40 static void RunThread ( playlist_t * );
41 static void SkipItem ( playlist_t *, int );
42 static void PlayItem ( playlist_t * );
44 static void Poubellize ( playlist_t *, input_thread_t * );
46 /*****************************************************************************
47 * playlist_Create: create playlist
48 *****************************************************************************
49 * Create a playlist structure.
50 *****************************************************************************/
51 playlist_t * __playlist_Create ( vlc_object_t *p_parent )
53 playlist_t *p_playlist;
55 /* Allocate structure */
56 p_playlist = vlc_object_create( p_parent, VLC_OBJECT_PLAYLIST );
59 msg_Err( p_parent, "out of memory" );
63 p_playlist->p_input = NULL;
64 p_playlist->i_status = PLAYLIST_STOPPED;
65 p_playlist->i_index = -1;
66 p_playlist->i_size = 0;
67 p_playlist->pp_items = NULL;
69 if( vlc_thread_create( p_playlist, "playlist", RunThread,
70 VLC_THREAD_PRIORITY_LOW, VLC_TRUE ) )
72 msg_Err( p_playlist, "cannot spawn playlist thread" );
73 vlc_object_destroy( p_playlist );
77 /* The object has been initialized, now attach it */
78 vlc_object_attach( p_playlist, p_parent );
83 /*****************************************************************************
84 * playlist_Destroy: destroy the playlist
85 *****************************************************************************
86 * Delete all items in the playlist and free the playlist structure.
87 *****************************************************************************/
88 void playlist_Destroy( playlist_t * p_playlist )
90 p_playlist->b_die = 1;
92 vlc_thread_join( p_playlist );
94 vlc_object_destroy( p_playlist );
97 /*****************************************************************************
98 * playlist_Add: add an item to the playlist
99 *****************************************************************************
100 * Add an item to the playlist at position i_pos. If i_pos is PLAYLIST_END,
101 * add it at the end regardless of the playlist current size.
102 *****************************************************************************/
103 int playlist_Add( playlist_t *p_playlist, const char * psz_target,
104 int i_mode, int i_pos )
106 playlist_item_t * p_item;
108 p_item = malloc( sizeof( playlist_item_t ) );
111 msg_Err( p_playlist, "out of memory" );
114 p_item->psz_name = strdup( psz_target );
115 p_item->psz_uri = strdup( psz_target );
117 p_item->i_status = 0;
118 p_item->b_autodeletion = VLC_FALSE;
120 return playlist_AddItem( p_playlist, p_item, i_mode, i_pos );
124 int playlist_AddItem( playlist_t *p_playlist, playlist_item_t * p_item,
125 int i_mode, int i_pos)
128 vlc_mutex_lock( &p_playlist->object_lock );
131 * CHECK_INSERT : checks if the item is already enqued before
134 if ( i_mode & PLAYLIST_CHECK_INSERT )
138 if ( p_playlist->pp_items )
140 for ( j = 0; j < p_playlist->i_size; j++ )
142 if ( !strcmp( p_playlist->pp_items[j]->psz_uri, p_item->psz_uri ) )
144 if( p_item->psz_name )
146 free( p_item->psz_name );
148 if( p_item->psz_uri )
150 free( p_item->psz_uri );
153 vlc_mutex_unlock( &p_playlist->object_lock );
158 i_mode &= ~PLAYLIST_CHECK_INSERT;
159 i_mode |= PLAYLIST_APPEND;
163 msg_Dbg( p_playlist, "adding playlist item « %s »", p_item->psz_name );
165 /* Create the new playlist item */
168 /* Do a few boundary checks and allocate space for the item */
169 if( i_pos == PLAYLIST_END )
171 if( i_mode & PLAYLIST_INSERT )
173 i_mode &= ~PLAYLIST_INSERT;
174 i_mode |= PLAYLIST_APPEND;
177 i_pos = p_playlist->i_size - 1;
180 if( !(i_mode & PLAYLIST_REPLACE)
181 || i_pos < 0 || i_pos >= p_playlist->i_size )
183 /* Additional boundary checks */
184 if( i_mode & PLAYLIST_APPEND )
193 else if( i_pos > p_playlist->i_size )
195 i_pos = p_playlist->i_size;
198 INSERT_ELEM( p_playlist->pp_items,
203 if( p_playlist->i_index >= i_pos )
205 p_playlist->i_index++;
210 /* i_mode == PLAYLIST_REPLACE and 0 <= i_pos < p_playlist->i_size */
211 if( p_playlist->pp_items[i_pos]->psz_name )
213 free( p_playlist->pp_items[i_pos]->psz_name );
215 if( p_playlist->pp_items[i_pos]->psz_uri )
217 free( p_playlist->pp_items[i_pos]->psz_uri );
219 /* XXX: what if the item is still in use? */
220 free( p_playlist->pp_items[i_pos] );
221 p_playlist->pp_items[i_pos] = p_item;
224 if( i_mode & PLAYLIST_GO )
226 p_playlist->i_index = i_pos;
227 if( p_playlist->p_input )
229 input_StopThread( p_playlist->p_input );
231 p_playlist->i_status = PLAYLIST_RUNNING;
234 vlc_mutex_unlock( &p_playlist->object_lock );
239 /*****************************************************************************
240 * playlist_Delete: delete an item from the playlist
241 *****************************************************************************
242 * Delete the item in the playlist with position i_pos.
243 *****************************************************************************/
244 int playlist_Delete( playlist_t * p_playlist, int i_pos )
246 vlc_mutex_lock( &p_playlist->object_lock );
248 if( i_pos >= 0 && i_pos < p_playlist->i_size )
250 msg_Dbg( p_playlist, "deleting playlist item « %s »",
251 p_playlist->pp_items[i_pos]->psz_name );
253 if( p_playlist->pp_items[i_pos]->psz_name )
255 free( p_playlist->pp_items[i_pos]->psz_name );
257 if( p_playlist->pp_items[i_pos]->psz_uri )
259 free( p_playlist->pp_items[i_pos]->psz_uri );
262 /* XXX: what if the item is still in use? */
263 free( p_playlist->pp_items[i_pos] );
265 if( i_pos <= p_playlist->i_index )
267 p_playlist->i_index--;
270 /* Renumber the playlist */
271 REMOVE_ELEM( p_playlist->pp_items,
276 vlc_mutex_unlock( &p_playlist->object_lock );
281 /*****************************************************************************
282 * playlist_Command: do a playlist action
283 *****************************************************************************
285 *****************************************************************************/
286 void playlist_Command( playlist_t * p_playlist, int i_command, int i_arg )
288 vlc_mutex_lock( &p_playlist->object_lock );
293 p_playlist->i_status = PLAYLIST_STOPPED;
294 if( p_playlist->p_input )
296 input_StopThread( p_playlist->p_input );
301 p_playlist->i_status = PLAYLIST_RUNNING;
302 if ( p_playlist->p_input )
304 input_SetStatus( p_playlist->p_input, INPUT_STATUS_PLAY );
309 p_playlist->i_status = PLAYLIST_STOPPED;
310 SkipItem( p_playlist, i_arg );
311 if( p_playlist->p_input )
313 input_StopThread( p_playlist->p_input );
315 p_playlist->i_status = PLAYLIST_RUNNING;
319 if( i_arg >= 0 && i_arg < p_playlist->i_size )
321 p_playlist->i_index = i_arg;
322 if( p_playlist->p_input )
324 input_StopThread( p_playlist->p_input );
326 p_playlist->i_status = PLAYLIST_RUNNING;
331 msg_Err( p_playlist, "unknown playlist command" );
335 vlc_mutex_unlock( &p_playlist->object_lock );
340 /* Following functions are local */
342 /*****************************************************************************
343 * RunThread: main playlist thread
344 *****************************************************************************/
345 static void RunThread ( playlist_t *p_playlist )
347 /* Tell above that we're ready */
348 vlc_thread_ready( p_playlist );
350 while( !p_playlist->b_die )
352 vlc_mutex_lock( &p_playlist->object_lock );
354 /* If there is an input, check that it doesn't need to die. */
355 if( p_playlist->p_input )
357 /* This input is dead. Remove it ! */
358 if( p_playlist->p_input->b_dead )
360 input_thread_t *p_input;
362 /* Unlink current input */
363 p_input = p_playlist->p_input;
364 p_playlist->p_input = NULL;
365 vlc_object_detach( p_input );
367 /* Release the playlist lock, because we may get stuck
368 * in input_DestroyThread() for some time. */
369 vlc_mutex_unlock( &p_playlist->object_lock );
372 input_DestroyThread( p_input );
373 vlc_object_destroy( p_input );
376 /* This input is dying, let him do */
377 else if( p_playlist->p_input->b_die )
381 /* This input has finished, ask him to die ! */
382 else if( p_playlist->p_input->b_error
383 || p_playlist->p_input->b_eof )
385 /* Check for autodeletion */
386 if( p_playlist->pp_items[p_playlist->i_index]->b_autodeletion )
388 vlc_mutex_unlock( &p_playlist->object_lock );
389 playlist_Delete( p_playlist, p_playlist->i_index );
390 vlc_mutex_lock( &p_playlist->object_lock );
393 /* Select the next playlist item */
394 SkipItem( p_playlist, 1 );
396 /* Release the playlist lock, because we may get stuck
397 * in input_StopThread() for some time. */
398 vlc_mutex_unlock( &p_playlist->object_lock );
399 input_StopThread( p_playlist->p_input );
403 else if( p_playlist->i_status != PLAYLIST_STOPPED )
405 PlayItem( p_playlist );
408 vlc_mutex_unlock( &p_playlist->object_lock );
410 msleep( INTF_IDLE_SLEEP );
413 /* If there is an input, kill it */
416 vlc_mutex_lock( &p_playlist->object_lock );
418 if( p_playlist->p_input == NULL )
420 vlc_mutex_unlock( &p_playlist->object_lock );
424 if( p_playlist->p_input->b_dead )
426 input_thread_t *p_input;
428 /* Unlink current input */
429 p_input = p_playlist->p_input;
430 p_playlist->p_input = NULL;
431 vlc_object_detach( p_input );
432 vlc_mutex_unlock( &p_playlist->object_lock );
435 input_DestroyThread( p_input );
436 vlc_object_destroy( p_input );
439 else if( p_playlist->p_input->b_die )
441 /* This input is dying, leave him alone */
444 else if( p_playlist->p_input->b_error || p_playlist->p_input->b_eof )
446 vlc_mutex_unlock( &p_playlist->object_lock );
447 input_StopThread( p_playlist->p_input );
452 p_playlist->p_input->b_eof = 1;
455 vlc_mutex_unlock( &p_playlist->object_lock );
457 msleep( INTF_IDLE_SLEEP );
461 /*****************************************************************************
462 * SkipItem: go to Xth playlist item
463 *****************************************************************************
464 * This function calculates the position of the next playlist item, depending
465 * on the playlist course mode (forward, backward, random...).
466 *****************************************************************************/
467 static void SkipItem( playlist_t *p_playlist, int i_arg )
469 int i_oldindex = p_playlist->i_index;
472 /* If the playlist is empty, there is no current item */
473 if( p_playlist->i_size == 0 )
475 p_playlist->i_index = -1;
479 b_random = config_GetInt( p_playlist, "random" );
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));
495 p_playlist->i_index += i_arg;
498 if( p_playlist->i_index >= p_playlist->i_size )
500 if( p_playlist->i_status == PLAYLIST_STOPPED
502 || config_GetInt( p_playlist, "loop" ) )
504 p_playlist->i_index -= p_playlist->i_size
505 * ( p_playlist->i_index / p_playlist->i_size );
509 /* Don't loop by default: stop at playlist end */
510 p_playlist->i_index = i_oldindex;
511 p_playlist->i_status = PLAYLIST_STOPPED;
514 else if( p_playlist->i_index < 0 )
516 p_playlist->i_index = p_playlist->i_size - 1;
520 /*****************************************************************************
521 * PlayItem: play current playlist item
522 *****************************************************************************
523 * This function calculates the position of the next playlist item, depending
524 * on the playlist course mode (forward, backward, random...).
525 *****************************************************************************/
526 static void PlayItem( playlist_t *p_playlist )
528 if( p_playlist->i_index == -1 )
530 if( p_playlist->i_size == 0 )
535 SkipItem( p_playlist, 1 );
538 msg_Dbg( p_playlist, "creating new input thread" );
539 p_playlist->p_input = input_CreateThread( p_playlist,
540 p_playlist->pp_items[p_playlist->i_index] );
543 /*****************************************************************************
544 * Poubellize: put an input thread in the trashcan
545 *****************************************************************************
547 *****************************************************************************/
548 static void Poubellize ( playlist_t *p_playlist, input_thread_t *p_input )
550 msg_Dbg( p_playlist, "poubellizing input %i\n", p_input->i_object_id );