1 /*****************************************************************************
2 * playlist.c : Playlist management functions
3 *****************************************************************************
4 * Copyright (C) 1999-2001 VideoLAN
5 * $Id: playlist.c,v 1.31 2003/01/29 11:34:11 jlj 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_FILE_HEADER_0_5 "# vlc playlist file version 0.5"
36 # define PLAYLIST_FILE_EOL "\r\n"
38 # define PLAYLIST_FILE_EOL "\n"
41 /*****************************************************************************
43 *****************************************************************************/
44 static void RunThread ( playlist_t * );
45 static void SkipItem ( playlist_t *, int );
46 static void PlayItem ( playlist_t * );
48 static void Poubellize ( playlist_t *, input_thread_t * );
50 /*****************************************************************************
51 * playlist_Create: create playlist
52 *****************************************************************************
53 * Create a playlist structure.
54 *****************************************************************************/
55 playlist_t * __playlist_Create ( vlc_object_t *p_parent )
57 playlist_t *p_playlist;
59 /* Allocate structure */
60 p_playlist = vlc_object_create( p_parent, VLC_OBJECT_PLAYLIST );
63 msg_Err( p_parent, "out of memory" );
67 p_playlist->p_input = NULL;
68 p_playlist->i_status = PLAYLIST_STOPPED;
69 p_playlist->i_index = -1;
70 p_playlist->i_size = 0;
71 p_playlist->pp_items = NULL;
73 if( vlc_thread_create( p_playlist, "playlist", RunThread,
74 VLC_THREAD_PRIORITY_LOW, VLC_TRUE ) )
76 msg_Err( p_playlist, "cannot spawn playlist thread" );
77 vlc_object_destroy( p_playlist );
81 /* The object has been initialized, now attach it */
82 vlc_object_attach( p_playlist, p_parent );
87 /*****************************************************************************
88 * playlist_Destroy: destroy the playlist
89 *****************************************************************************
90 * Delete all items in the playlist and free the playlist structure.
91 *****************************************************************************/
92 void playlist_Destroy( playlist_t * p_playlist )
94 p_playlist->b_die = 1;
96 vlc_thread_join( p_playlist );
98 vlc_object_destroy( p_playlist );
101 /*****************************************************************************
102 * playlist_Add: add an item to the playlist
103 *****************************************************************************
104 * Add an item to the playlist at position i_pos. If i_pos is PLAYLIST_END,
105 * add it at the end regardless of the playlist current size.
106 *****************************************************************************/
107 int playlist_Add( playlist_t *p_playlist, const char * psz_target,
108 int i_mode, int i_pos )
110 playlist_item_t * p_item;
112 p_item = malloc( sizeof( playlist_item_t ) );
115 msg_Err( p_playlist, "out of memory" );
118 p_item->psz_name = strdup( psz_target );
119 p_item->psz_uri = strdup( psz_target );
121 p_item->i_status = 0;
122 p_item->b_autodeletion = VLC_FALSE;
124 return playlist_AddItem( p_playlist, p_item, i_mode, i_pos );
128 int playlist_AddItem( playlist_t *p_playlist, playlist_item_t * p_item,
129 int i_mode, int i_pos)
132 vlc_mutex_lock( &p_playlist->object_lock );
135 * CHECK_INSERT : checks if the item is already enqued before
138 if ( i_mode & PLAYLIST_CHECK_INSERT )
142 if ( p_playlist->pp_items )
144 for ( j = 0; j < p_playlist->i_size; j++ )
146 if ( !strcmp( p_playlist->pp_items[j]->psz_uri, p_item->psz_uri ) )
148 if( p_item->psz_name )
150 free( p_item->psz_name );
152 if( p_item->psz_uri )
154 free( p_item->psz_uri );
157 vlc_mutex_unlock( &p_playlist->object_lock );
162 i_mode &= ~PLAYLIST_CHECK_INSERT;
163 i_mode |= PLAYLIST_APPEND;
167 msg_Dbg( p_playlist, "adding playlist item « %s »", p_item->psz_name );
169 /* Create the new playlist item */
172 /* Do a few boundary checks and allocate space for the item */
173 if( i_pos == PLAYLIST_END )
175 if( i_mode & PLAYLIST_INSERT )
177 i_mode &= ~PLAYLIST_INSERT;
178 i_mode |= PLAYLIST_APPEND;
181 i_pos = p_playlist->i_size - 1;
184 if( !(i_mode & PLAYLIST_REPLACE)
185 || i_pos < 0 || i_pos >= p_playlist->i_size )
187 /* Additional boundary checks */
188 if( i_mode & PLAYLIST_APPEND )
197 else if( i_pos > p_playlist->i_size )
199 i_pos = p_playlist->i_size;
202 INSERT_ELEM( p_playlist->pp_items,
207 if( p_playlist->i_index >= i_pos )
209 p_playlist->i_index++;
214 /* i_mode == PLAYLIST_REPLACE and 0 <= i_pos < p_playlist->i_size */
215 if( p_playlist->pp_items[i_pos]->psz_name )
217 free( p_playlist->pp_items[i_pos]->psz_name );
219 if( p_playlist->pp_items[i_pos]->psz_uri )
221 free( p_playlist->pp_items[i_pos]->psz_uri );
223 /* XXX: what if the item is still in use? */
224 free( p_playlist->pp_items[i_pos] );
225 p_playlist->pp_items[i_pos] = p_item;
228 if( i_mode & PLAYLIST_GO )
230 p_playlist->i_index = i_pos;
231 if( p_playlist->p_input )
233 input_StopThread( p_playlist->p_input );
235 p_playlist->i_status = PLAYLIST_RUNNING;
238 vlc_mutex_unlock( &p_playlist->object_lock );
243 /*****************************************************************************
244 * playlist_Delete: delete an item from the playlist
245 *****************************************************************************
246 * Delete the item in the playlist with position i_pos.
247 *****************************************************************************/
248 int playlist_Delete( playlist_t * p_playlist, int i_pos )
250 vlc_mutex_lock( &p_playlist->object_lock );
252 if( i_pos >= 0 && i_pos < p_playlist->i_size )
254 msg_Dbg( p_playlist, "deleting playlist item « %s »",
255 p_playlist->pp_items[i_pos]->psz_name );
257 if( p_playlist->pp_items[i_pos]->psz_name )
259 free( p_playlist->pp_items[i_pos]->psz_name );
261 if( p_playlist->pp_items[i_pos]->psz_uri )
263 free( p_playlist->pp_items[i_pos]->psz_uri );
266 /* XXX: what if the item is still in use? */
267 free( p_playlist->pp_items[i_pos] );
269 if( i_pos <= p_playlist->i_index )
271 p_playlist->i_index--;
274 /* Renumber the playlist */
275 REMOVE_ELEM( p_playlist->pp_items,
280 vlc_mutex_unlock( &p_playlist->object_lock );
285 /*****************************************************************************
286 * playlist_Command: do a playlist action
287 *****************************************************************************
289 *****************************************************************************/
290 void playlist_Command( playlist_t * p_playlist, int i_command, int i_arg )
292 vlc_mutex_lock( &p_playlist->object_lock );
297 p_playlist->i_status = PLAYLIST_STOPPED;
298 if( p_playlist->p_input )
300 input_StopThread( p_playlist->p_input );
305 p_playlist->i_status = PLAYLIST_RUNNING;
306 if( p_playlist->p_input )
308 input_SetStatus( p_playlist->p_input, INPUT_STATUS_PLAY );
313 p_playlist->i_status = PLAYLIST_PAUSED;
314 if( p_playlist->p_input )
316 input_SetStatus( p_playlist->p_input, INPUT_STATUS_PAUSE );
321 p_playlist->i_status = PLAYLIST_STOPPED;
322 SkipItem( p_playlist, i_arg );
323 if( p_playlist->p_input )
325 input_StopThread( p_playlist->p_input );
327 p_playlist->i_status = PLAYLIST_RUNNING;
331 if( i_arg >= 0 && i_arg < p_playlist->i_size )
333 p_playlist->i_index = i_arg;
334 if( p_playlist->p_input )
336 input_StopThread( p_playlist->p_input );
338 p_playlist->i_status = PLAYLIST_RUNNING;
343 msg_Err( p_playlist, "unknown playlist command" );
347 vlc_mutex_unlock( &p_playlist->object_lock );
352 /* Following functions are local */
354 /*****************************************************************************
355 * RunThread: main playlist thread
356 *****************************************************************************/
357 static void RunThread ( playlist_t *p_playlist )
359 /* Tell above that we're ready */
360 vlc_thread_ready( p_playlist );
362 while( !p_playlist->b_die )
364 vlc_mutex_lock( &p_playlist->object_lock );
366 /* If there is an input, check that it doesn't need to die. */
367 if( p_playlist->p_input )
369 /* This input is dead. Remove it ! */
370 if( p_playlist->p_input->b_dead )
372 input_thread_t *p_input;
374 /* Unlink current input */
375 p_input = p_playlist->p_input;
376 p_playlist->p_input = NULL;
377 vlc_object_detach( p_input );
379 /* Release the playlist lock, because we may get stuck
380 * in input_DestroyThread() for some time. */
381 vlc_mutex_unlock( &p_playlist->object_lock );
384 input_DestroyThread( p_input );
385 vlc_object_destroy( p_input );
388 /* This input is dying, let him do */
389 else if( p_playlist->p_input->b_die )
393 /* This input has finished, ask him to die ! */
394 else if( p_playlist->p_input->b_error
395 || p_playlist->p_input->b_eof )
397 /* Check for autodeletion */
398 if( p_playlist->pp_items[p_playlist->i_index]->b_autodeletion )
400 vlc_mutex_unlock( &p_playlist->object_lock );
401 playlist_Delete( p_playlist, p_playlist->i_index );
402 vlc_mutex_lock( &p_playlist->object_lock );
405 /* Select the next playlist item */
406 SkipItem( p_playlist, 1 );
408 /* Release the playlist lock, because we may get stuck
409 * in input_StopThread() for some time. */
410 vlc_mutex_unlock( &p_playlist->object_lock );
411 input_StopThread( p_playlist->p_input );
415 else if( p_playlist->i_status != PLAYLIST_STOPPED )
417 PlayItem( p_playlist );
420 vlc_mutex_unlock( &p_playlist->object_lock );
422 msleep( INTF_IDLE_SLEEP );
425 /* If there is an input, kill it */
428 vlc_mutex_lock( &p_playlist->object_lock );
430 if( p_playlist->p_input == NULL )
432 vlc_mutex_unlock( &p_playlist->object_lock );
436 if( p_playlist->p_input->b_dead )
438 input_thread_t *p_input;
440 /* Unlink current input */
441 p_input = p_playlist->p_input;
442 p_playlist->p_input = NULL;
443 vlc_object_detach( p_input );
444 vlc_mutex_unlock( &p_playlist->object_lock );
447 input_DestroyThread( p_input );
448 vlc_object_destroy( p_input );
451 else if( p_playlist->p_input->b_die )
453 /* This input is dying, leave him alone */
456 else if( p_playlist->p_input->b_error || p_playlist->p_input->b_eof )
458 vlc_mutex_unlock( &p_playlist->object_lock );
459 input_StopThread( p_playlist->p_input );
464 p_playlist->p_input->b_eof = 1;
467 vlc_mutex_unlock( &p_playlist->object_lock );
469 msleep( INTF_IDLE_SLEEP );
473 /*****************************************************************************
474 * SkipItem: go to Xth playlist item
475 *****************************************************************************
476 * This function calculates the position of the next playlist item, depending
477 * on the playlist course mode (forward, backward, random...).
478 *****************************************************************************/
479 static void SkipItem( playlist_t *p_playlist, int i_arg )
481 int i_oldindex = p_playlist->i_index;
484 /* If the playlist is empty, there is no current item */
485 if( p_playlist->i_size == 0 )
487 p_playlist->i_index = -1;
491 b_random = config_GetInt( p_playlist, "random" );
496 srand( (unsigned int)mdate() );
498 /* Simple random stuff - we cheat a bit to minimize the chances to
499 * get the same index again. */
500 i_arg = (int)((float)p_playlist->i_size * rand() / (RAND_MAX+1.0));
503 i_arg = (int)((float)p_playlist->i_size * rand() / (RAND_MAX+1.0));
507 p_playlist->i_index += i_arg;
510 if( p_playlist->i_index >= p_playlist->i_size )
512 if( p_playlist->i_status == PLAYLIST_STOPPED
514 || config_GetInt( p_playlist, "loop" ) )
516 p_playlist->i_index -= p_playlist->i_size
517 * ( p_playlist->i_index / p_playlist->i_size );
521 /* Don't loop by default: stop at playlist end */
522 p_playlist->i_index = i_oldindex;
523 p_playlist->i_status = PLAYLIST_STOPPED;
526 else if( p_playlist->i_index < 0 )
528 p_playlist->i_index = p_playlist->i_size - 1;
532 /*****************************************************************************
533 * PlayItem: play current playlist item
534 *****************************************************************************
535 * This function calculates the position of the next playlist item, depending
536 * on the playlist course mode (forward, backward, random...).
537 *****************************************************************************/
538 static void PlayItem( playlist_t *p_playlist )
540 if( p_playlist->i_index == -1 )
542 if( p_playlist->i_size == 0 )
547 SkipItem( p_playlist, 1 );
550 msg_Dbg( p_playlist, "creating new input thread" );
551 p_playlist->p_input = input_CreateThread( p_playlist,
552 p_playlist->pp_items[p_playlist->i_index] );
555 /*****************************************************************************
556 * Poubellize: put an input thread in the trashcan
557 *****************************************************************************
559 *****************************************************************************/
560 static void Poubellize ( playlist_t *p_playlist, input_thread_t *p_input )
562 msg_Dbg( p_playlist, "poubellizing input %i\n", p_input->i_object_id );
565 /*****************************************************************************
566 * playlist_LoadFile: load a playlist file.
567 ****************************************************************************/
568 int playlist_LoadFile( playlist_t * p_playlist, const char *psz_filename )
572 int i_current_status;
575 msg_Dbg( p_playlist, "opening playlist file %s", psz_filename );
577 file = fopen( psz_filename, "rt" );
580 msg_Err( p_playlist, "playlist file %s does not exist", psz_filename );
583 fseek( file, 0L, SEEK_SET );
585 /* check the file is not empty */
586 if ( ! fgets( line, 1024, file ) )
588 msg_Err( p_playlist, "playlist file %s is empty", psz_filename );
593 /* get rid of line feed */
594 if( line[strlen(line)-1] == '\n' || line[strlen(line)-1] == '\r' )
596 line[strlen(line)-1] = (char)0;
597 if( line[strlen(line)-1] == '\r' ) line[strlen(line)-1] = (char)0;
599 /* check the file format is valid */
600 if ( strcmp ( line , PLAYLIST_FILE_HEADER_0_5 ) )
602 msg_Err( p_playlist, "playlist file %s format is unsupported"
609 i_current_status = p_playlist->i_status;
610 if ( p_playlist->i_status != PLAYLIST_STOPPED )
612 playlist_Stop ( p_playlist );
615 /* delete current content of the playlist */
616 for( i = p_playlist->i_size - 1; i >= 0; i-- )
618 playlist_Delete ( p_playlist , i );
621 /* simply add each line */
622 while( fgets( line, 1024, file ) )
624 /* ignore comments or empty lines */
625 if( (line[0] == '#') || (line[0] == '\r') || (line[0] == '\n')
626 || (line[0] == (char)0) )
629 /* get rid of line feed */
630 if( line[strlen(line)-1] == '\n' || line[strlen(line)-1] == '\r' )
632 line[strlen(line)-1] = (char)0;
633 if( line[strlen(line)-1] == '\r' ) line[strlen(line)-1] = (char)0;
636 playlist_Add ( p_playlist , (char*) &line , PLAYLIST_APPEND , PLAYLIST_END );
640 if ( i_current_status != PLAYLIST_STOPPED )
642 playlist_Play ( p_playlist );
650 /*****************************************************************************
651 * playlist_SaveFile: Save a playlist in a file.
652 *****************************************************************************/
653 int playlist_SaveFile( playlist_t * p_playlist, const char * psz_filename )
658 vlc_mutex_lock( &p_playlist->object_lock );
660 msg_Dbg( p_playlist, "saving playlist file %s", psz_filename );
662 file = fopen( psz_filename, "wt" );
665 msg_Err( p_playlist , "could not create playlist file %s"
670 fprintf( file , PLAYLIST_FILE_HEADER_0_5 PLAYLIST_FILE_EOL );
672 for ( i = 0 ; i < p_playlist->i_size ; i++ )
674 fprintf( file , p_playlist->pp_items[i]->psz_uri );
675 fprintf( file , PLAYLIST_FILE_EOL );
680 vlc_mutex_unlock( &p_playlist->object_lock );