1 /*****************************************************************************
2 * playlist.c : Playlist management functions
3 *****************************************************************************
4 * Copyright (C) 1999-2001 VideoLAN
5 * $Id: playlist.c,v 1.26 2002/12/06 06:42:24 babal 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 free( p_playlist->pp_items[i_pos]->psz_name );
212 /* XXX: what if the item is still in use? */
213 free( p_playlist->pp_items[i_pos] );
214 p_playlist->pp_items[i_pos] = p_item;
217 if( i_mode & PLAYLIST_GO )
219 p_playlist->i_index = i_pos;
220 if( p_playlist->p_input )
222 input_StopThread( p_playlist->p_input );
224 p_playlist->i_status = PLAYLIST_RUNNING;
227 vlc_mutex_unlock( &p_playlist->object_lock );
232 /*****************************************************************************
233 * playlist_Delete: delete an item from the playlist
234 *****************************************************************************
235 * Delete the item in the playlist with position i_pos.
236 *****************************************************************************/
237 int playlist_Delete( playlist_t * p_playlist, int i_pos )
239 vlc_mutex_lock( &p_playlist->object_lock );
241 if( i_pos >= 0 && i_pos < p_playlist->i_size )
243 msg_Dbg( p_playlist, "deleting playlist item « %s »",
244 p_playlist->pp_items[i_pos]->psz_name );
246 free( p_playlist->pp_items[i_pos]->psz_name );
247 /* XXX: what if the item is still in use? */
248 free( p_playlist->pp_items[i_pos] );
250 if( i_pos <= p_playlist->i_index )
252 p_playlist->i_index--;
255 /* Renumber the playlist */
256 REMOVE_ELEM( p_playlist->pp_items,
261 vlc_mutex_unlock( &p_playlist->object_lock );
266 /*****************************************************************************
267 * playlist_Command: do a playlist action
268 *****************************************************************************
270 *****************************************************************************/
271 void playlist_Command( playlist_t * p_playlist, int i_command, int i_arg )
273 vlc_mutex_lock( &p_playlist->object_lock );
278 p_playlist->i_status = PLAYLIST_STOPPED;
279 if( p_playlist->p_input )
281 input_StopThread( p_playlist->p_input );
286 p_playlist->i_status = PLAYLIST_RUNNING;
287 if ( p_playlist->p_input )
289 input_SetStatus( p_playlist->p_input, INPUT_STATUS_PLAY );
294 p_playlist->i_status = PLAYLIST_STOPPED;
295 SkipItem( p_playlist, i_arg );
296 if( p_playlist->p_input )
298 input_StopThread( p_playlist->p_input );
300 p_playlist->i_status = PLAYLIST_RUNNING;
304 if( i_arg >= 0 && i_arg < p_playlist->i_size )
306 p_playlist->i_index = i_arg;
307 if( p_playlist->p_input )
309 input_StopThread( p_playlist->p_input );
311 p_playlist->i_status = PLAYLIST_RUNNING;
316 msg_Err( p_playlist, "unknown playlist command" );
320 vlc_mutex_unlock( &p_playlist->object_lock );
325 /* Following functions are local */
327 /*****************************************************************************
328 * RunThread: main playlist thread
329 *****************************************************************************/
330 static void RunThread ( playlist_t *p_playlist )
332 /* Tell above that we're ready */
333 vlc_thread_ready( p_playlist );
335 while( !p_playlist->b_die )
337 vlc_mutex_lock( &p_playlist->object_lock );
339 /* If there is an input, check that it doesn't need to die. */
340 if( p_playlist->p_input )
342 /* This input is dead. Remove it ! */
343 if( p_playlist->p_input->b_dead )
345 input_thread_t *p_input;
347 /* Unlink current input */
348 p_input = p_playlist->p_input;
349 p_playlist->p_input = NULL;
350 vlc_object_detach( p_input );
352 /* Release the playlist lock, because we may get stuck
353 * in input_DestroyThread() for some time. */
354 vlc_mutex_unlock( &p_playlist->object_lock );
357 input_DestroyThread( p_input );
358 vlc_object_destroy( p_input );
361 /* This input is dying, let him do */
362 else if( p_playlist->p_input->b_die )
366 /* This input has finished, ask him to die ! */
367 else if( p_playlist->p_input->b_error
368 || p_playlist->p_input->b_eof )
370 /* Check for autodeletion */
371 if( p_playlist->pp_items[p_playlist->i_index]->b_autodeletion )
373 vlc_mutex_unlock( &p_playlist->object_lock );
374 playlist_Delete( p_playlist, p_playlist->i_index );
375 vlc_mutex_lock( &p_playlist->object_lock );
378 /* Select the next playlist item */
379 SkipItem( p_playlist, 1 );
381 /* Release the playlist lock, because we may get stuck
382 * in input_StopThread() for some time. */
383 vlc_mutex_unlock( &p_playlist->object_lock );
384 input_StopThread( p_playlist->p_input );
388 else if( p_playlist->i_status != PLAYLIST_STOPPED )
390 PlayItem( p_playlist );
393 vlc_mutex_unlock( &p_playlist->object_lock );
395 msleep( INTF_IDLE_SLEEP );
398 /* If there is an input, kill it */
401 vlc_mutex_lock( &p_playlist->object_lock );
403 if( p_playlist->p_input == NULL )
405 vlc_mutex_unlock( &p_playlist->object_lock );
409 if( p_playlist->p_input->b_dead )
411 input_thread_t *p_input;
413 /* Unlink current input */
414 p_input = p_playlist->p_input;
415 p_playlist->p_input = NULL;
416 vlc_object_detach( p_input );
417 vlc_mutex_unlock( &p_playlist->object_lock );
420 input_DestroyThread( p_input );
421 vlc_object_destroy( p_input );
424 else if( p_playlist->p_input->b_die )
426 /* This input is dying, leave him alone */
429 else if( p_playlist->p_input->b_error || p_playlist->p_input->b_eof )
431 vlc_mutex_unlock( &p_playlist->object_lock );
432 input_StopThread( p_playlist->p_input );
437 p_playlist->p_input->b_eof = 1;
440 vlc_mutex_unlock( &p_playlist->object_lock );
442 msleep( INTF_IDLE_SLEEP );
446 /*****************************************************************************
447 * SkipItem: go to Xth playlist item
448 *****************************************************************************
449 * This function calculates the position of the next playlist item, depending
450 * on the playlist course mode (forward, backward, random...).
451 *****************************************************************************/
452 static void SkipItem( playlist_t *p_playlist, int i_arg )
454 int i_oldindex = p_playlist->i_index;
457 /* If the playlist is empty, there is no current item */
458 if( p_playlist->i_size == 0 )
460 p_playlist->i_index = -1;
464 b_random = config_GetInt( p_playlist, "random" );
469 srand( (unsigned int)mdate() );
471 /* Simple random stuff - we cheat a bit to minimize the chances to
472 * get the same index again. */
473 i_arg = (int)((float)p_playlist->i_size * rand() / (RAND_MAX+1.0));
476 i_arg = (int)((float)p_playlist->i_size * rand() / (RAND_MAX+1.0));
480 p_playlist->i_index += i_arg;
483 if( p_playlist->i_index >= p_playlist->i_size )
485 if( p_playlist->i_status == PLAYLIST_STOPPED
487 || config_GetInt( p_playlist, "loop" ) )
489 p_playlist->i_index -= p_playlist->i_size
490 * ( p_playlist->i_index / p_playlist->i_size );
494 /* Don't loop by default: stop at playlist end */
495 p_playlist->i_index = i_oldindex;
496 p_playlist->i_status = PLAYLIST_STOPPED;
499 else if( p_playlist->i_index < 0 )
501 p_playlist->i_index = p_playlist->i_size - 1;
505 /*****************************************************************************
506 * PlayItem: play current playlist item
507 *****************************************************************************
508 * This function calculates the position of the next playlist item, depending
509 * on the playlist course mode (forward, backward, random...).
510 *****************************************************************************/
511 static void PlayItem( playlist_t *p_playlist )
513 if( p_playlist->i_index == -1 )
515 if( p_playlist->i_size == 0 )
520 SkipItem( p_playlist, 1 );
523 msg_Dbg( p_playlist, "creating new input thread" );
524 p_playlist->p_input = input_CreateThread( p_playlist,
525 p_playlist->pp_items[p_playlist->i_index], NULL );
528 /*****************************************************************************
529 * Poubellize: put an input thread in the trashcan
530 *****************************************************************************
532 *****************************************************************************/
533 static void Poubellize ( playlist_t *p_playlist, input_thread_t *p_input )