]> git.sesse.net Git - vlc/blob - src/playlist/playlist.c
* include/vlc/vlc.h:
[vlc] / src / playlist / playlist.c
1 /*****************************************************************************
2  * playlist.c : Playlist management functions
3  *****************************************************************************
4  * Copyright (C) 1999-2001 VideoLAN
5  * $Id: playlist.c,v 1.54 2003/09/20 19:37:54 hartman Exp $
6  *
7  * Authors: Samuel Hocevar <sam@zoy.org>
8  *
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.
13  *
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.
18  *
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() */
26
27 #include <vlc/vlc.h>
28 #include <vlc/vout.h>
29 #include <vlc/sout.h>
30
31 #include "stream_control.h"
32 #include "input_ext-intf.h"
33
34 #include "vlc_playlist.h"
35
36 #define PLAYLIST_FILE_HEADER_0_5  "# vlc playlist file version 0.5"
37
38 /*****************************************************************************
39  * Local prototypes
40  *****************************************************************************/
41 static void RunThread ( playlist_t * );
42 static void SkipItem  ( playlist_t *, int );
43 static void PlayItem  ( playlist_t * );
44
45 static void Poubellize ( playlist_t *, input_thread_t * );
46
47 /**
48  * Create playlist
49  *
50  * Create a playlist structure.
51  * \param p_parent the vlc object that is to be the parent of this playlist
52  * \return a pointer to the created playlist, or NULL on error
53  */
54 playlist_t * __playlist_Create ( vlc_object_t *p_parent )
55 {
56     playlist_t *p_playlist;
57     vlc_value_t     val;
58
59     /* Allocate structure */
60     p_playlist = vlc_object_create( p_parent, VLC_OBJECT_PLAYLIST );
61     if( !p_playlist )
62     {
63         msg_Err( p_parent, "out of memory" );
64         return NULL;
65     }
66
67     var_Create( p_playlist, "intf-change", VLC_VAR_BOOL );
68     val.b_bool = VLC_TRUE;
69     var_Set( p_playlist, "intf-change", val );
70
71     var_Create( p_playlist, "intf-popupmenu", VLC_VAR_BOOL );
72
73     var_Create( p_playlist, "intf-show", VLC_VAR_BOOL );
74     val.b_bool = VLC_TRUE;
75     var_Set( p_playlist, "intf-show", val );
76     
77     var_Create( p_playlist, "random", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
78     var_Create( p_playlist, "repeat", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
79     var_Create( p_playlist, "loop", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
80
81     p_playlist->p_input = NULL;
82     p_playlist->i_status = PLAYLIST_STOPPED;
83     p_playlist->i_index = -1;
84     p_playlist->i_size = 0;
85     p_playlist->pp_items = NULL;
86
87     if( vlc_thread_create( p_playlist, "playlist", RunThread,
88                            VLC_THREAD_PRIORITY_LOW, VLC_TRUE ) )
89     {
90         msg_Err( p_playlist, "cannot spawn playlist thread" );
91         vlc_object_destroy( p_playlist );
92         return NULL;
93     }
94
95     /* The object has been initialized, now attach it */
96     vlc_object_attach( p_playlist, p_parent );
97
98     return p_playlist;
99 }
100
101 /**
102  * Destroy the playlist.
103  *
104  * Delete all items in the playlist and free the playlist structure.
105  * \param p_playlist the playlist structure to destroy
106  */
107 void playlist_Destroy( playlist_t * p_playlist )
108 {
109     p_playlist->b_die = 1;
110
111     vlc_thread_join( p_playlist );
112
113     var_Destroy( p_playlist, "intf-change" );
114
115     vlc_object_destroy( p_playlist );
116 }
117
118 /**
119  * Add an MRL to the playlist. This is a simplified version of
120  * playlist_AddExt inculded for convenince. It equals calling playlist_AddExt
121  * with psz_name == psz_target and i_duration == -1
122  */
123
124 int playlist_Add( playlist_t *p_playlist, const char *psz_target,
125                   const char **ppsz_options, int i_options,
126                   int i_mode, int i_pos )
127 {
128     return playlist_AddExt( p_playlist, psz_target, psz_target, -1,
129                             ppsz_options, i_options, i_mode, i_pos );
130 }
131
132 /**
133  * Add a MRL into the playlist.
134  *
135  * \param p_playlist the playlist to add into
136  * \param psz_uri the mrl to add to the playlist
137  * \param psz_name a text giving a name or description of this item
138  * \param i_duration a hint about the duration of this item, in miliseconds, or
139  *        -1 if unknown.
140  * \param ppsz_options array of options
141  * \param i_options number of items in ppsz_options
142  * \param i_mode the mode used when adding
143  * \param i_pos the position in the playlist where to add. If this is
144  *        PLAYLIST_END the item will be added at the end of the playlist
145  *        regardless of it's size
146  * \return always returns 0
147 */
148 int playlist_AddExt( playlist_t *p_playlist, const char * psz_uri,
149                      const char * psz_name, mtime_t i_duration,
150                      const char **ppsz_options, int i_options, int i_mode,
151                      int i_pos )
152 {
153     playlist_item_t * p_item;
154
155     p_item = malloc( sizeof( playlist_item_t ) );
156     if( p_item == NULL )
157     {
158         msg_Err( p_playlist, "out of memory" );
159     }
160
161     p_item->psz_name = strdup( psz_name );
162     p_item->psz_uri  = strdup( psz_uri );
163     p_item->i_duration = i_duration;
164     p_item->i_type = 0;
165     p_item->i_status = 0;
166     p_item->b_autodeletion = VLC_FALSE;
167
168     p_item->ppsz_options = NULL;
169     p_item->i_options = i_options;
170
171     if( i_options )
172     {
173         int i;
174
175         p_item->ppsz_options = (char **)malloc( i_options * sizeof(char *) );
176         for( i = 0; i < i_options; i++ )
177             p_item->ppsz_options[i] = strdup( ppsz_options[i] );
178
179     }
180
181     return playlist_AddItem( p_playlist, p_item, i_mode, i_pos );
182 }
183
184 /**
185  * Add a playlist item into a playlist
186  *
187  * \param p_playlist the playlist to insert into
188  * \param p_item the playlist item to insert
189  * \param i_mode the mode used when adding
190  * \param i_pos the possition in the playlist where to add. If this is
191  *        PLAYLIST_END the item will be added at the end of the playlist
192  *        regardless of it's size
193  * \return always returns 0
194 */
195 int playlist_AddItem( playlist_t *p_playlist, playlist_item_t * p_item,
196                 int i_mode, int i_pos)
197 {
198     vlc_value_t     val;
199
200     vlc_mutex_lock( &p_playlist->object_lock );
201
202     /*
203      * CHECK_INSERT : checks if the item is already enqued before
204      * enqueing it
205      */
206     if ( i_mode & PLAYLIST_CHECK_INSERT )
207     {
208          int j;
209
210          if ( p_playlist->pp_items )
211          {
212              for ( j = 0; j < p_playlist->i_size; j++ )
213              {
214                  if ( !strcmp( p_playlist->pp_items[j]->psz_uri, p_item->psz_uri ) )
215                  {
216                       if( p_item->psz_name )
217                       {
218                           free( p_item->psz_name );
219                       }
220                       if( p_item->psz_uri )
221                       {
222                           free( p_item->psz_uri );
223                       }
224                       free( p_item );
225                       vlc_mutex_unlock( &p_playlist->object_lock );
226                       return 0;
227                  }
228              }
229          }
230          i_mode &= ~PLAYLIST_CHECK_INSERT;
231          i_mode |= PLAYLIST_APPEND;
232     }
233
234
235     msg_Dbg( p_playlist, "adding playlist item Â« %s Â» ( %s )", p_item->psz_name, p_item->psz_uri);
236
237     /* Create the new playlist item */
238
239
240     /* Do a few boundary checks and allocate space for the item */
241     if( i_pos == PLAYLIST_END )
242     {
243         if( i_mode & PLAYLIST_INSERT )
244         {
245             i_mode &= ~PLAYLIST_INSERT;
246             i_mode |= PLAYLIST_APPEND;
247         }
248
249         i_pos = p_playlist->i_size - 1;
250     }
251
252     if( !(i_mode & PLAYLIST_REPLACE)
253          || i_pos < 0 || i_pos >= p_playlist->i_size )
254     {
255         /* Additional boundary checks */
256         if( i_mode & PLAYLIST_APPEND )
257         {
258             i_pos++;
259         }
260
261         if( i_pos < 0 )
262         {
263             i_pos = 0;
264         }
265         else if( i_pos > p_playlist->i_size )
266         {
267             i_pos = p_playlist->i_size;
268         }
269
270         INSERT_ELEM( p_playlist->pp_items,
271                      p_playlist->i_size,
272                      i_pos,
273                      p_item );
274
275         if( p_playlist->i_index >= i_pos )
276         {
277             p_playlist->i_index++;
278         }
279     }
280     else
281     {
282         /* i_mode == PLAYLIST_REPLACE and 0 <= i_pos < p_playlist->i_size */
283         if( p_playlist->pp_items[i_pos]->psz_name )
284         {
285             free( p_playlist->pp_items[i_pos]->psz_name );
286         }
287         if( p_playlist->pp_items[i_pos]->psz_uri )
288         {
289             free( p_playlist->pp_items[i_pos]->psz_uri );
290         }
291         /* XXX: what if the item is still in use? */
292         free( p_playlist->pp_items[i_pos] );
293         p_playlist->pp_items[i_pos] = p_item;
294     }
295
296     if( i_mode & PLAYLIST_GO )
297     {
298         p_playlist->i_index = i_pos;
299         if( p_playlist->p_input )
300         {
301             input_StopThread( p_playlist->p_input );
302         }
303         p_playlist->i_status = PLAYLIST_RUNNING;
304     }
305
306     vlc_mutex_unlock( &p_playlist->object_lock );
307
308     val.b_bool = VLC_TRUE;
309     var_Set( p_playlist, "intf-change", val );
310
311     return 0;
312 }
313
314 /**
315  * delete an item from a playlist.
316  *
317  * \param p_playlist the playlist to remove from.
318  * \param i_pos the position of the item to remove
319  * \return returns 0
320  */
321 int playlist_Delete( playlist_t * p_playlist, int i_pos )
322 {
323     vlc_value_t     val;
324     vlc_mutex_lock( &p_playlist->object_lock );
325
326     if( i_pos >= 0 && i_pos < p_playlist->i_size )
327     {
328         msg_Dbg( p_playlist, "deleting playlist item Â« %s Â»",
329                              p_playlist->pp_items[i_pos]->psz_name );
330
331         if( p_playlist->pp_items[i_pos]->psz_name )
332         {
333             free( p_playlist->pp_items[i_pos]->psz_name );
334         }
335         if( p_playlist->pp_items[i_pos]->psz_uri )
336         {
337             free( p_playlist->pp_items[i_pos]->psz_uri );
338         }
339         if( p_playlist->pp_items[i_pos]->i_options )
340         {
341             int i;
342
343             for( i = 0; i < p_playlist->pp_items[i_pos]->i_options; i++ )
344                 free( p_playlist->pp_items[i_pos]->ppsz_options[i] );
345
346             free( p_playlist->pp_items[i_pos]->ppsz_options );
347         }
348
349         /* XXX: what if the item is still in use? */
350         free( p_playlist->pp_items[i_pos] );
351
352         if( i_pos <= p_playlist->i_index )
353         {
354             p_playlist->i_index--;
355         }
356
357         /* Renumber the playlist */
358         REMOVE_ELEM( p_playlist->pp_items,
359                      p_playlist->i_size,
360                      i_pos );
361     }
362
363     vlc_mutex_unlock( &p_playlist->object_lock );
364
365     val.b_bool = VLC_TRUE;
366     var_Set( p_playlist, "intf-change", val );
367
368     return 0;
369 }
370
371
372 /**
373  * Sort the playlist
374  *
375  */
376 int playlist_Sort( playlist_t * p_playlist , int i_type )
377 {
378     int i , i_small , i_position;
379     playlist_item_t *p_temp;
380
381     vlc_mutex_lock( &p_playlist->object_lock );
382
383     for( i_position = 0; i_position < p_playlist->i_size -1 ; i_position ++ )
384     {
385         i_small  = i_position;
386         for( i = i_position + 1 ; i<  p_playlist->i_size ; i++)
387         {
388             int i_test;
389
390             i_test = strcasecmp( p_playlist->pp_items[i]->psz_name,
391                                  p_playlist->pp_items[i_small]->psz_name );
392
393             if( ( i_type == SORT_NORMAL  && i_test < 0 ) ||
394                 ( i_type == SORT_REVERSE && i_test > 0 ) )
395             {
396                 i_small = i;
397             }
398
399             p_temp = p_playlist->pp_items[i_position];
400             p_playlist->pp_items[i_position] = p_playlist->pp_items[i_small];
401             p_playlist->pp_items[i_small] = p_temp;
402         }
403     }
404
405     for( i=0; i < p_playlist->i_size; i++ )
406     {
407         msg_Dbg( p_playlist, "%s", p_playlist->pp_items[i]->psz_name );
408     }
409
410     vlc_mutex_unlock( &p_playlist->object_lock );
411
412     return 0;
413 }
414
415 /**
416  * Move an item in a playlist
417  *
418  * Move the item in the playlist with position i_pos before the current item
419  * at position i_newpos.
420  * \param p_playlist the playlist to move items in
421  * \param i_pos the position of the item to move
422  * \param i_newpos the position of the item that will be behind the moved item
423  *        after the move
424  * \return returns 0
425  */
426 int playlist_Move( playlist_t * p_playlist, int i_pos, int i_newpos)
427 {
428     vlc_value_t     val;
429     vlc_mutex_lock( &p_playlist->object_lock );
430
431     /* take into account that our own row disappears. */
432     if ( i_pos < i_newpos ) i_newpos--;
433
434     if( i_pos >= 0 && i_newpos >=0 && i_pos <= p_playlist->i_size 
435                      && i_newpos <= p_playlist->i_size )
436     {
437         playlist_item_t * temp;
438
439         msg_Dbg( p_playlist, "moving playlist item Â« %s Â»",
440                              p_playlist->pp_items[i_pos]->psz_name );
441
442         if( i_pos == p_playlist->i_index )
443         {
444             p_playlist->i_index = i_newpos;
445         }
446         else if( i_pos > p_playlist->i_index && i_newpos <= p_playlist->i_index )
447         {
448             p_playlist->i_index++;
449         }
450         else if( i_pos < p_playlist->i_index && i_newpos >= p_playlist->i_index )
451         {
452             p_playlist->i_index--;
453         }
454
455         if ( i_pos < i_newpos )
456         {
457             temp = p_playlist->pp_items[i_pos];
458             while ( i_pos < i_newpos )
459             {
460                 p_playlist->pp_items[i_pos] = p_playlist->pp_items[i_pos+1];
461                 i_pos++;
462             }
463             p_playlist->pp_items[i_newpos] = temp;
464         }
465         else if ( i_pos > i_newpos )
466         {
467             temp = p_playlist->pp_items[i_pos];
468             while ( i_pos > i_newpos )
469             {
470                 p_playlist->pp_items[i_pos] = p_playlist->pp_items[i_pos-1];
471                 i_pos--;
472             }
473             p_playlist->pp_items[i_newpos] = temp;
474         }
475     }
476
477     vlc_mutex_unlock( &p_playlist->object_lock );
478
479     val.b_bool = VLC_TRUE;
480     var_Set( p_playlist, "intf-change", val );
481
482     return 0;
483 }
484
485 /**
486  * Do a playlist action
487  *
488  * \param p_playlist the playlist to do the command on
489  * \param i_command the command to do
490  * \param i_arg the argument to the command. See playlist_command_t for details
491  */
492  void playlist_Command( playlist_t * p_playlist, playlist_command_t i_command,
493                        int i_arg )
494 {
495     vlc_value_t val;
496
497     vlc_mutex_lock( &p_playlist->object_lock );
498
499     switch( i_command )
500     {
501     case PLAYLIST_STOP:
502         p_playlist->i_status = PLAYLIST_STOPPED;
503         if( p_playlist->p_input )
504         {
505             input_StopThread( p_playlist->p_input );
506         }
507         break;
508
509     case PLAYLIST_PLAY:
510         p_playlist->i_status = PLAYLIST_RUNNING;
511         if( !p_playlist->p_input )
512         {
513             PlayItem( p_playlist );
514         }
515         if( p_playlist->p_input )
516         {
517             val.i_int = PLAYING_S;
518             var_Set( p_playlist->p_input, "state", val );
519         }
520         break;
521
522     case PLAYLIST_PAUSE:
523         p_playlist->i_status = PLAYLIST_PAUSED;
524         if( p_playlist->p_input )
525         {
526             val.i_int = PAUSE_S;
527             var_Set( p_playlist->p_input, "state", val );
528         }
529         break;
530
531     case PLAYLIST_SKIP:
532         p_playlist->i_status = PLAYLIST_STOPPED;
533         SkipItem( p_playlist, i_arg );
534         if( p_playlist->p_input )
535         {
536             input_StopThread( p_playlist->p_input );
537         }
538         p_playlist->i_status = PLAYLIST_RUNNING;
539         break;
540
541     case PLAYLIST_GOTO:
542         if( i_arg >= 0 && i_arg < p_playlist->i_size )
543         {
544             p_playlist->i_index = i_arg;
545             if( p_playlist->p_input )
546             {
547                 input_StopThread( p_playlist->p_input );
548             }
549             p_playlist->i_status = PLAYLIST_RUNNING;
550         }
551         break;
552
553     default:
554         msg_Err( p_playlist, "unknown playlist command" );
555         break;
556     }
557
558     vlc_mutex_unlock( &p_playlist->object_lock );
559
560     return;
561 }
562 /* Following functions are local */
563
564 static void ObjectGarbageCollector( playlist_t *p_playlist,
565                                     int i_type,
566                                     vlc_bool_t *pb_obj_destroyed,
567                                     mtime_t *pi_obj_destroyed_date )
568 {
569     vlc_object_t *p_obj;
570     if( *pb_obj_destroyed || *pi_obj_destroyed_date > mdate() )
571     {
572         return;
573     }
574
575     if( *pi_obj_destroyed_date == 0 )
576     {
577         /* give a little time */
578         *pi_obj_destroyed_date = mdate() + 300000LL;
579     }
580     else
581     {
582         while( ( p_obj = vlc_object_find( p_playlist,
583                                            i_type,
584                                            FIND_CHILD ) ) )
585         {
586             if( p_obj->p_parent != (vlc_object_t*)p_playlist )
587             {
588                 /* only first chiled (ie unused) */
589                 vlc_object_release( p_obj );
590                 break;
591             }
592             if( i_type == VLC_OBJECT_VOUT )
593             {
594                 msg_Dbg( p_playlist, "vout garbage collector destroying 1 vout" );
595                 vlc_object_detach( p_obj );
596                 vlc_object_release( p_obj );
597                 vout_Destroy( (vout_thread_t *)p_obj );
598             }
599             else if( i_type == VLC_OBJECT_SOUT )
600             {
601                 vlc_object_release( p_obj );
602                 sout_DeleteInstance( (sout_instance_t*)p_obj );
603             }
604         }
605         *pb_obj_destroyed = VLC_TRUE;
606     }
607 }
608
609 /*****************************************************************************
610  * RunThread: main playlist thread
611  *****************************************************************************/
612 static void RunThread ( playlist_t *p_playlist )
613 {
614     vlc_object_t *p_obj;
615     vlc_bool_t b_vout_destroyed = VLC_FALSE; /*we do vout garbage collector */
616     mtime_t    i_vout_destroyed_date = 0;
617
618     vlc_bool_t b_sout_destroyed = VLC_FALSE; /*we do vout garbage collector */
619     mtime_t    i_sout_destroyed_date = 0;
620
621     /* Tell above that we're ready */
622     vlc_thread_ready( p_playlist );
623
624     while( !p_playlist->b_die )
625     {
626         vlc_mutex_lock( &p_playlist->object_lock );
627
628         /* If there is an input, check that it doesn't need to die. */
629         if( p_playlist->p_input )
630         {
631             /* This input is dead. Remove it ! */
632             if( p_playlist->p_input->b_dead )
633             {
634                 input_thread_t *p_input;
635
636                 p_input = p_playlist->p_input;
637                 p_playlist->p_input = NULL;
638
639                 /* Release the playlist lock, because we may get stuck
640                  * in input_DestroyThread() for some time. */
641                 vlc_mutex_unlock( &p_playlist->object_lock );
642
643                 /* Destroy input */
644                 input_DestroyThread( p_input );
645
646                 /* Unlink current input (_after_ input_DestroyThread for vout garbage collector)*/
647                 vlc_object_detach( p_input );
648
649                 /* Destroy object */
650                 vlc_object_destroy( p_input );
651
652                 b_vout_destroyed = VLC_FALSE;
653                 i_vout_destroyed_date = 0;
654                 b_sout_destroyed = VLC_FALSE;
655                 i_sout_destroyed_date = 0;
656                 continue;
657             }
658             /* This input is dying, let him do */
659             else if( p_playlist->p_input->b_die )
660             {
661                 ;
662             }
663             /* This input has finished, ask him to die ! */
664             else if( p_playlist->p_input->b_error
665                       || p_playlist->p_input->b_eof )
666             {
667                 /* Check for autodeletion */
668                 if( p_playlist->pp_items[p_playlist->i_index]->b_autodeletion )
669                 {
670                     vlc_mutex_unlock( &p_playlist->object_lock );
671                     playlist_Delete( p_playlist, p_playlist->i_index );
672                     vlc_mutex_lock( &p_playlist->object_lock );
673                 }
674
675                 /* Select the next playlist item */
676                 SkipItem( p_playlist, 1 );
677
678                 input_StopThread( p_playlist->p_input );
679                 vlc_mutex_unlock( &p_playlist->object_lock );
680                 continue;
681             }
682             else if( p_playlist->p_input->stream.control.i_status != INIT_S )
683             {
684                 vlc_mutex_unlock( &p_playlist->object_lock );
685                 ObjectGarbageCollector( p_playlist, VLC_OBJECT_VOUT,
686                                         &b_vout_destroyed,
687                                         &i_vout_destroyed_date );
688                 ObjectGarbageCollector( p_playlist, VLC_OBJECT_SOUT,
689                                         &b_sout_destroyed,
690                                         &i_sout_destroyed_date );
691                 vlc_mutex_lock( &p_playlist->object_lock );
692             }
693         }
694         else if( p_playlist->i_status != PLAYLIST_STOPPED )
695         {
696             SkipItem( p_playlist, 0 );
697             PlayItem( p_playlist );
698         }
699         else if( p_playlist->i_status == PLAYLIST_STOPPED )
700         {
701             vlc_mutex_unlock( &p_playlist->object_lock );
702             ObjectGarbageCollector( p_playlist, VLC_OBJECT_SOUT,
703                                     &b_sout_destroyed, &i_sout_destroyed_date );
704             ObjectGarbageCollector( p_playlist, VLC_OBJECT_VOUT,
705                                     &b_vout_destroyed, &i_vout_destroyed_date );
706             vlc_mutex_lock( &p_playlist->object_lock );
707         }
708         vlc_mutex_unlock( &p_playlist->object_lock );
709
710         msleep( INTF_IDLE_SLEEP );
711     }
712
713     /* If there is an input, kill it */
714     while( 1 )
715     {
716         vlc_mutex_lock( &p_playlist->object_lock );
717
718         if( p_playlist->p_input == NULL )
719         {
720             vlc_mutex_unlock( &p_playlist->object_lock );
721             break;
722         }
723
724         if( p_playlist->p_input->b_dead )
725         {
726             input_thread_t *p_input;
727
728             /* Unlink current input */
729             p_input = p_playlist->p_input;
730             p_playlist->p_input = NULL;
731             vlc_mutex_unlock( &p_playlist->object_lock );
732
733             /* Destroy input */
734             input_DestroyThread( p_input );
735             /* Unlink current input (_after_ input_DestroyThread for vout
736              * garbage collector)*/
737             vlc_object_detach( p_input );
738
739             /* Destroy object */
740             vlc_object_destroy( p_input );
741             continue;
742         }
743         else if( p_playlist->p_input->b_die )
744         {
745             /* This input is dying, leave him alone */
746             ;
747         }
748         else if( p_playlist->p_input->b_error || p_playlist->p_input->b_eof )
749         {
750             input_StopThread( p_playlist->p_input );
751             vlc_mutex_unlock( &p_playlist->object_lock );
752             continue;
753         }
754         else
755         {
756             p_playlist->p_input->b_eof = 1;
757         }
758
759         vlc_mutex_unlock( &p_playlist->object_lock );
760
761         msleep( INTF_IDLE_SLEEP );
762     }
763
764     /* close all remaining sout */
765     while( ( p_obj = vlc_object_find( p_playlist,
766                                       VLC_OBJECT_SOUT, FIND_CHILD ) ) )
767     {
768         vlc_object_release( p_obj );
769         sout_DeleteInstance( (sout_instance_t*)p_obj );
770     }
771
772     /* close all remaining vout */
773     while( ( p_obj = vlc_object_find( p_playlist,
774                                       VLC_OBJECT_VOUT, FIND_CHILD ) ) )
775     {
776         vlc_object_detach( p_obj );
777         vlc_object_release( p_obj );
778         vout_Destroy( (vout_thread_t *)p_obj );
779     }
780 }
781
782 /*****************************************************************************
783  * SkipItem: go to Xth playlist item
784  *****************************************************************************
785  * This function calculates the position of the next playlist item, depending
786  * on the playlist course mode (forward, backward, random...).
787  *****************************************************************************/
788 static void SkipItem( playlist_t *p_playlist, int i_arg )
789 {
790     int i_oldindex = p_playlist->i_index;
791     vlc_bool_t b_random, b_repeat, b_loop;
792     vlc_value_t val;
793
794     /* If the playlist is empty, there is no current item */
795     if( p_playlist->i_size == 0 )
796     {
797         p_playlist->i_index = -1;
798         return;
799     }
800
801     var_Get( p_playlist, "random", &val );
802     b_random = val.b_bool;
803     var_Get( p_playlist, "repeat", &val );
804     b_repeat = val.b_bool;
805     var_Get( p_playlist, "loop", &val );
806     b_loop = val.b_bool;
807
808     /* Increment */
809     if( b_random )
810     {
811         srand( (unsigned int)mdate() );
812
813         /* Simple random stuff - we cheat a bit to minimize the chances to
814          * get the same index again. */
815         i_arg = (int)((float)p_playlist->i_size * rand() / (RAND_MAX+1.0));
816         if( i_arg == 0 )
817         {
818             i_arg = (int)((float)p_playlist->i_size * rand() / (RAND_MAX+1.0));
819         }
820     }
821     if( b_repeat )
822     {
823         i_arg = 0;
824     }
825     p_playlist->i_index += i_arg;
826
827     /* Boundary check */
828     if( p_playlist->i_index >= p_playlist->i_size )
829     {
830         if( p_playlist->i_status == PLAYLIST_STOPPED
831              || b_random
832              || b_loop )
833         {
834             p_playlist->i_index -= p_playlist->i_size
835                          * ( p_playlist->i_index / p_playlist->i_size );
836         }
837         else
838         {
839             /* Don't loop by default: stop at playlist end */
840             p_playlist->i_index = i_oldindex;
841             p_playlist->i_status = PLAYLIST_STOPPED;
842         }
843     }
844     else if( p_playlist->i_index < 0 )
845     {
846         p_playlist->i_index = p_playlist->i_size - 1;
847     }
848
849     val.b_bool = VLC_TRUE;
850     var_Set( p_playlist, "intf-change", val );
851 }
852
853 /*****************************************************************************
854  * PlayItem: play current playlist item
855  *****************************************************************************
856  * This function calculates the position of the next playlist item, depending
857  * on the playlist course mode (forward, backward, random...).
858  *****************************************************************************/
859 static void PlayItem( playlist_t *p_playlist )
860 {
861     if( p_playlist->i_index == -1 )
862     {
863         if( p_playlist->i_size == 0 )
864         {
865             return;
866         }
867
868         SkipItem( p_playlist, 1 );
869     }
870
871     msg_Dbg( p_playlist, "creating new input thread" );
872     p_playlist->p_input = input_CreateThread( p_playlist,
873                                   p_playlist->pp_items[p_playlist->i_index] );
874 }
875
876 /*****************************************************************************
877  * Poubellize: put an input thread in the trashcan
878  *****************************************************************************
879  * XXX: unused
880  *****************************************************************************/
881 static void Poubellize ( playlist_t *p_playlist, input_thread_t *p_input )
882 {
883     msg_Dbg( p_playlist, "poubellizing input %i\n", p_input->i_object_id );
884 }
885
886 /*****************************************************************************
887  * playlist_LoadFile: load a playlist file.
888  ****************************************************************************/
889 int playlist_LoadFile( playlist_t * p_playlist, const char *psz_filename )
890 {
891     FILE *file;
892     char line[1024];
893     int i_current_status;
894     int i;
895
896     msg_Dbg( p_playlist, "opening playlist file %s", psz_filename );
897
898     file = fopen( psz_filename, "rt" );
899     if( !file )
900     {
901         msg_Err( p_playlist, "playlist file %s does not exist", psz_filename );
902         return -1;
903     }
904     fseek( file, 0L, SEEK_SET );
905
906     /* check the file is not empty */
907     if ( ! fgets( line, 1024, file ) )
908     {
909         msg_Err( p_playlist, "playlist file %s is empty", psz_filename );
910         fclose( file );
911         return -1;
912     }
913
914     /* get rid of line feed */
915     if( line[strlen(line)-1] == '\n' || line[strlen(line)-1] == '\r' )
916     {
917        line[strlen(line)-1] = (char)0;
918        if( line[strlen(line)-1] == '\r' ) line[strlen(line)-1] = (char)0;
919     }
920     /* check the file format is valid */
921     if ( strcmp ( line , PLAYLIST_FILE_HEADER_0_5 ) )
922     {
923         msg_Err( p_playlist, "playlist file %s format is unsupported"
924                 , psz_filename );
925         fclose( file );
926         return -1;
927     }
928
929     /* stop playing */
930     i_current_status = p_playlist->i_status;
931     if ( p_playlist->i_status != PLAYLIST_STOPPED )
932     {
933         playlist_Stop ( p_playlist );
934     }
935
936     /* delete current content of the playlist */
937     for( i = p_playlist->i_size - 1; i >= 0; i-- )
938     {
939         playlist_Delete ( p_playlist , i );
940     }
941
942     /* simply add each line */
943     while( fgets( line, 1024, file ) )
944     {
945        /* ignore comments or empty lines */
946        if( (line[0] == '#') || (line[0] == '\r') || (line[0] == '\n')
947                || (line[0] == (char)0) )
948            continue;
949
950        /* get rid of line feed */
951        if( line[strlen(line)-1] == '\n' || line[strlen(line)-1] == '\r' )
952        {
953            line[strlen(line)-1] = (char)0;
954            if( line[strlen(line)-1] == '\r' ) line[strlen(line)-1] = (char)0;
955        }
956
957        playlist_Add ( p_playlist , (char *)&line ,
958                       0, 0, PLAYLIST_APPEND , PLAYLIST_END );
959     }
960
961     /* start playing */
962     if ( i_current_status != PLAYLIST_STOPPED )
963     {
964         playlist_Play ( p_playlist );
965     }
966
967     fclose( file );
968
969     return 0;
970 }
971
972 /*****************************************************************************
973  * playlist_SaveFile: Save a playlist in a file.
974  *****************************************************************************/
975 int playlist_SaveFile( playlist_t * p_playlist, const char * psz_filename )
976 {
977     FILE *file;
978     int i;
979
980     vlc_mutex_lock( &p_playlist->object_lock );
981
982     msg_Dbg( p_playlist, "saving playlist file %s", psz_filename );
983
984     file = fopen( psz_filename, "wt" );
985     if( !file )
986     {
987         msg_Err( p_playlist , "could not create playlist file %s"
988                 , psz_filename );
989         return -1;
990     }
991
992     fprintf( file , PLAYLIST_FILE_HEADER_0_5 "\n" );
993
994     for ( i = 0 ; i < p_playlist->i_size ; i++ )
995     {
996         fprintf( file , p_playlist->pp_items[i]->psz_uri );
997         fprintf( file , "\n" );
998     }
999
1000     fclose( file );
1001
1002     vlc_mutex_unlock( &p_playlist->object_lock );
1003
1004     return 0;
1005 }