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