]> git.sesse.net Git - vlc/blob - src/playlist/playlist.c
* modules/demux/ogg.c: support for DEMUX_GET_TIME. External subtitles files should...
[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.56 2003/09/24 10:21:32 zorglub 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     vlc_mutex_unlock( &p_playlist->object_lock );
404
405     return 0;
406 }
407
408 /**
409  * Move an item in a playlist
410  *
411  * Move the item in the playlist with position i_pos before the current item
412  * at position i_newpos.
413  * \param p_playlist the playlist to move items in
414  * \param i_pos the position of the item to move
415  * \param i_newpos the position of the item that will be behind the moved item
416  *        after the move
417  * \return returns 0
418  */
419 int playlist_Move( playlist_t * p_playlist, int i_pos, int i_newpos)
420 {
421     vlc_value_t     val;
422     vlc_mutex_lock( &p_playlist->object_lock );
423
424     /* take into account that our own row disappears. */
425     if ( i_pos < i_newpos ) i_newpos--;
426
427     if( i_pos >= 0 && i_newpos >=0 && i_pos <= p_playlist->i_size 
428                      && i_newpos <= p_playlist->i_size )
429     {
430         playlist_item_t * temp;
431
432         msg_Dbg( p_playlist, "moving playlist item Â« %s Â»",
433                              p_playlist->pp_items[i_pos]->psz_name );
434
435         if( i_pos == p_playlist->i_index )
436         {
437             p_playlist->i_index = i_newpos;
438         }
439         else if( i_pos > p_playlist->i_index && i_newpos <= p_playlist->i_index )
440         {
441             p_playlist->i_index++;
442         }
443         else if( i_pos < p_playlist->i_index && i_newpos >= p_playlist->i_index )
444         {
445             p_playlist->i_index--;
446         }
447
448         if ( i_pos < i_newpos )
449         {
450             temp = p_playlist->pp_items[i_pos];
451             while ( i_pos < i_newpos )
452             {
453                 p_playlist->pp_items[i_pos] = p_playlist->pp_items[i_pos+1];
454                 i_pos++;
455             }
456             p_playlist->pp_items[i_newpos] = temp;
457         }
458         else if ( i_pos > i_newpos )
459         {
460             temp = p_playlist->pp_items[i_pos];
461             while ( i_pos > i_newpos )
462             {
463                 p_playlist->pp_items[i_pos] = p_playlist->pp_items[i_pos-1];
464                 i_pos--;
465             }
466             p_playlist->pp_items[i_newpos] = temp;
467         }
468     }
469
470     vlc_mutex_unlock( &p_playlist->object_lock );
471
472     val.b_bool = VLC_TRUE;
473     var_Set( p_playlist, "intf-change", val );
474
475     return 0;
476 }
477
478 /**
479  * Do a playlist action
480  *
481  * \param p_playlist the playlist to do the command on
482  * \param i_command the command to do
483  * \param i_arg the argument to the command. See playlist_command_t for details
484  */
485  void playlist_Command( playlist_t * p_playlist, playlist_command_t i_command,
486                        int i_arg )
487 {
488     vlc_value_t val;
489
490     vlc_mutex_lock( &p_playlist->object_lock );
491
492     switch( i_command )
493     {
494     case PLAYLIST_STOP:
495         p_playlist->i_status = PLAYLIST_STOPPED;
496         if( p_playlist->p_input )
497         {
498             input_StopThread( p_playlist->p_input );
499         }
500         break;
501
502     case PLAYLIST_PLAY:
503         p_playlist->i_status = PLAYLIST_RUNNING;
504         if( !p_playlist->p_input )
505         {
506             PlayItem( p_playlist );
507         }
508         if( p_playlist->p_input )
509         {
510             val.i_int = PLAYING_S;
511             var_Set( p_playlist->p_input, "state", val );
512         }
513         break;
514
515     case PLAYLIST_PAUSE:
516         p_playlist->i_status = PLAYLIST_PAUSED;
517         if( p_playlist->p_input )
518         {
519             val.i_int = PAUSE_S;
520             var_Set( p_playlist->p_input, "state", val );
521         }
522         break;
523
524     case PLAYLIST_SKIP:
525         p_playlist->i_status = PLAYLIST_STOPPED;
526         SkipItem( p_playlist, i_arg );
527         if( p_playlist->p_input )
528         {
529             input_StopThread( p_playlist->p_input );
530         }
531         p_playlist->i_status = PLAYLIST_RUNNING;
532         break;
533
534     case PLAYLIST_GOTO:
535         if( i_arg >= 0 && i_arg < p_playlist->i_size )
536         {
537             p_playlist->i_index = i_arg;
538             if( p_playlist->p_input )
539             {
540                 input_StopThread( p_playlist->p_input );
541             }
542             p_playlist->i_status = PLAYLIST_RUNNING;
543         }
544         break;
545
546     default:
547         msg_Err( p_playlist, "unknown playlist command" );
548         break;
549     }
550
551     vlc_mutex_unlock( &p_playlist->object_lock );
552
553     return;
554 }
555 /* Following functions are local */
556
557 static void ObjectGarbageCollector( playlist_t *p_playlist,
558                                     int i_type,
559                                     vlc_bool_t *pb_obj_destroyed,
560                                     mtime_t *pi_obj_destroyed_date )
561 {
562     vlc_object_t *p_obj;
563     if( *pb_obj_destroyed || *pi_obj_destroyed_date > mdate() )
564     {
565         return;
566     }
567
568     if( *pi_obj_destroyed_date == 0 )
569     {
570         /* give a little time */
571         *pi_obj_destroyed_date = mdate() + 300000LL;
572     }
573     else
574     {
575         while( ( p_obj = vlc_object_find( p_playlist,
576                                            i_type,
577                                            FIND_CHILD ) ) )
578         {
579             if( p_obj->p_parent != (vlc_object_t*)p_playlist )
580             {
581                 /* only first chiled (ie unused) */
582                 vlc_object_release( p_obj );
583                 break;
584             }
585             if( i_type == VLC_OBJECT_VOUT )
586             {
587                 msg_Dbg( p_playlist, "vout garbage collector destroying 1 vout" );
588                 vlc_object_detach( p_obj );
589                 vlc_object_release( p_obj );
590                 vout_Destroy( (vout_thread_t *)p_obj );
591             }
592             else if( i_type == VLC_OBJECT_SOUT )
593             {
594                 vlc_object_release( p_obj );
595                 sout_DeleteInstance( (sout_instance_t*)p_obj );
596             }
597         }
598         *pb_obj_destroyed = VLC_TRUE;
599     }
600 }
601
602 /*****************************************************************************
603  * RunThread: main playlist thread
604  *****************************************************************************/
605 static void RunThread ( playlist_t *p_playlist )
606 {
607     vlc_object_t *p_obj;
608     vlc_bool_t b_vout_destroyed = VLC_FALSE; /*we do vout garbage collector */
609     mtime_t    i_vout_destroyed_date = 0;
610
611     vlc_bool_t b_sout_destroyed = VLC_FALSE; /*we do vout garbage collector */
612     mtime_t    i_sout_destroyed_date = 0;
613
614     /* Tell above that we're ready */
615     vlc_thread_ready( p_playlist );
616
617     while( !p_playlist->b_die )
618     {
619         vlc_mutex_lock( &p_playlist->object_lock );
620
621         /* If there is an input, check that it doesn't need to die. */
622         if( p_playlist->p_input )
623         {
624             /* This input is dead. Remove it ! */
625             if( p_playlist->p_input->b_dead )
626             {
627                 input_thread_t *p_input;
628
629                 p_input = p_playlist->p_input;
630                 p_playlist->p_input = NULL;
631
632                 /* Release the playlist lock, because we may get stuck
633                  * in input_DestroyThread() for some time. */
634                 vlc_mutex_unlock( &p_playlist->object_lock );
635
636                 /* Destroy input */
637                 input_DestroyThread( p_input );
638
639                 /* Unlink current input (_after_ input_DestroyThread for vout garbage collector)*/
640                 vlc_object_detach( p_input );
641
642                 /* Destroy object */
643                 vlc_object_destroy( p_input );
644
645                 b_vout_destroyed = VLC_FALSE;
646                 i_vout_destroyed_date = 0;
647                 b_sout_destroyed = VLC_FALSE;
648                 i_sout_destroyed_date = 0;
649                 continue;
650             }
651             /* This input is dying, let him do */
652             else if( p_playlist->p_input->b_die )
653             {
654                 ;
655             }
656             /* This input has finished, ask him to die ! */
657             else if( p_playlist->p_input->b_error
658                       || p_playlist->p_input->b_eof )
659             {
660                 /* Check for autodeletion */
661                 if( p_playlist->pp_items[p_playlist->i_index]->b_autodeletion )
662                 {
663                     vlc_mutex_unlock( &p_playlist->object_lock );
664                     playlist_Delete( p_playlist, p_playlist->i_index );
665                     vlc_mutex_lock( &p_playlist->object_lock );
666                 }
667
668                 /* Select the next playlist item */
669                 SkipItem( p_playlist, 1 );
670
671                 input_StopThread( p_playlist->p_input );
672                 vlc_mutex_unlock( &p_playlist->object_lock );
673                 continue;
674             }
675             else if( p_playlist->p_input->stream.control.i_status != INIT_S )
676             {
677                 vlc_mutex_unlock( &p_playlist->object_lock );
678                 ObjectGarbageCollector( p_playlist, VLC_OBJECT_VOUT,
679                                         &b_vout_destroyed,
680                                         &i_vout_destroyed_date );
681                 ObjectGarbageCollector( p_playlist, VLC_OBJECT_SOUT,
682                                         &b_sout_destroyed,
683                                         &i_sout_destroyed_date );
684                 vlc_mutex_lock( &p_playlist->object_lock );
685             }
686         }
687         else if( p_playlist->i_status != PLAYLIST_STOPPED )
688         {
689             SkipItem( p_playlist, 0 );
690             PlayItem( p_playlist );
691         }
692         else if( p_playlist->i_status == PLAYLIST_STOPPED )
693         {
694             vlc_mutex_unlock( &p_playlist->object_lock );
695             ObjectGarbageCollector( p_playlist, VLC_OBJECT_SOUT,
696                                     &b_sout_destroyed, &i_sout_destroyed_date );
697             ObjectGarbageCollector( p_playlist, VLC_OBJECT_VOUT,
698                                     &b_vout_destroyed, &i_vout_destroyed_date );
699             vlc_mutex_lock( &p_playlist->object_lock );
700         }
701         vlc_mutex_unlock( &p_playlist->object_lock );
702
703         msleep( INTF_IDLE_SLEEP );
704     }
705
706     /* If there is an input, kill it */
707     while( 1 )
708     {
709         vlc_mutex_lock( &p_playlist->object_lock );
710
711         if( p_playlist->p_input == NULL )
712         {
713             vlc_mutex_unlock( &p_playlist->object_lock );
714             break;
715         }
716
717         if( p_playlist->p_input->b_dead )
718         {
719             input_thread_t *p_input;
720
721             /* Unlink current input */
722             p_input = p_playlist->p_input;
723             p_playlist->p_input = NULL;
724             vlc_mutex_unlock( &p_playlist->object_lock );
725
726             /* Destroy input */
727             input_DestroyThread( p_input );
728             /* Unlink current input (_after_ input_DestroyThread for vout
729              * garbage collector)*/
730             vlc_object_detach( p_input );
731
732             /* Destroy object */
733             vlc_object_destroy( p_input );
734             continue;
735         }
736         else if( p_playlist->p_input->b_die )
737         {
738             /* This input is dying, leave him alone */
739             ;
740         }
741         else if( p_playlist->p_input->b_error || p_playlist->p_input->b_eof )
742         {
743             input_StopThread( p_playlist->p_input );
744             vlc_mutex_unlock( &p_playlist->object_lock );
745             continue;
746         }
747         else
748         {
749             p_playlist->p_input->b_eof = 1;
750         }
751
752         vlc_mutex_unlock( &p_playlist->object_lock );
753
754         msleep( INTF_IDLE_SLEEP );
755     }
756
757     /* close all remaining sout */
758     while( ( p_obj = vlc_object_find( p_playlist,
759                                       VLC_OBJECT_SOUT, FIND_CHILD ) ) )
760     {
761         vlc_object_release( p_obj );
762         sout_DeleteInstance( (sout_instance_t*)p_obj );
763     }
764
765     /* close all remaining vout */
766     while( ( p_obj = vlc_object_find( p_playlist,
767                                       VLC_OBJECT_VOUT, FIND_CHILD ) ) )
768     {
769         vlc_object_detach( p_obj );
770         vlc_object_release( p_obj );
771         vout_Destroy( (vout_thread_t *)p_obj );
772     }
773 }
774
775 /*****************************************************************************
776  * SkipItem: go to Xth playlist item
777  *****************************************************************************
778  * This function calculates the position of the next playlist item, depending
779  * on the playlist course mode (forward, backward, random...).
780  *****************************************************************************/
781 static void SkipItem( playlist_t *p_playlist, int i_arg )
782 {
783     int i_oldindex = p_playlist->i_index;
784     vlc_bool_t b_random, b_repeat, b_loop;
785     vlc_value_t val;
786
787     /* If the playlist is empty, there is no current item */
788     if( p_playlist->i_size == 0 )
789     {
790         p_playlist->i_index = -1;
791         return;
792     }
793
794     var_Get( p_playlist, "random", &val );
795     b_random = val.b_bool;
796     var_Get( p_playlist, "repeat", &val );
797     b_repeat = val.b_bool;
798     var_Get( p_playlist, "loop", &val );
799     b_loop = val.b_bool;
800
801     /* Increment */
802     if( b_random )
803     {
804         srand( (unsigned int)mdate() );
805
806         /* Simple random stuff - we cheat a bit to minimize the chances to
807          * get the same index again. */
808         i_arg = (int)((float)p_playlist->i_size * rand() / (RAND_MAX+1.0));
809         if( i_arg == 0 )
810         {
811             i_arg = (int)((float)p_playlist->i_size * rand() / (RAND_MAX+1.0));
812         }
813     }
814     if( b_repeat )
815     {
816         i_arg = 0;
817     }
818     p_playlist->i_index += i_arg;
819
820     /* Boundary check */
821     if( p_playlist->i_index >= p_playlist->i_size )
822     {
823         if( p_playlist->i_status == PLAYLIST_STOPPED
824              || b_random
825              || b_loop )
826         {
827             p_playlist->i_index -= p_playlist->i_size
828                          * ( p_playlist->i_index / p_playlist->i_size );
829         }
830         else
831         {
832             /* Don't loop by default: stop at playlist end */
833             p_playlist->i_index = i_oldindex;
834             p_playlist->i_status = PLAYLIST_STOPPED;
835         }
836     }
837     else if( p_playlist->i_index < 0 )
838     {
839         p_playlist->i_index = p_playlist->i_size - 1;
840     }
841
842     val.b_bool = VLC_TRUE;
843     var_Set( p_playlist, "intf-change", val );
844 }
845
846 /*****************************************************************************
847  * PlayItem: play current playlist item
848  *****************************************************************************
849  * This function calculates the position of the next playlist item, depending
850  * on the playlist course mode (forward, backward, random...).
851  *****************************************************************************/
852 static void PlayItem( playlist_t *p_playlist )
853 {
854     if( p_playlist->i_index == -1 )
855     {
856         if( p_playlist->i_size == 0 )
857         {
858             return;
859         }
860
861         SkipItem( p_playlist, 1 );
862     }
863
864     msg_Dbg( p_playlist, "creating new input thread" );
865     p_playlist->p_input = input_CreateThread( p_playlist,
866                                   p_playlist->pp_items[p_playlist->i_index] );
867 }
868
869 /*****************************************************************************
870  * Poubellize: put an input thread in the trashcan
871  *****************************************************************************
872  * XXX: unused
873  *****************************************************************************/
874 static void Poubellize ( playlist_t *p_playlist, input_thread_t *p_input )
875 {
876     msg_Dbg( p_playlist, "poubellizing input %i\n", p_input->i_object_id );
877 }
878
879 /*****************************************************************************
880  * playlist_LoadFile: load a playlist file.
881  ****************************************************************************/
882 int playlist_LoadFile( playlist_t * p_playlist, const char *psz_filename )
883 {
884     FILE *file;
885     char line[1024];
886     int i_current_status;
887     int i;
888
889     msg_Dbg( p_playlist, "opening playlist file %s", psz_filename );
890
891     file = fopen( psz_filename, "rt" );
892     if( !file )
893     {
894         msg_Err( p_playlist, "playlist file %s does not exist", psz_filename );
895         return -1;
896     }
897     fseek( file, 0L, SEEK_SET );
898
899     /* check the file is not empty */
900     if ( ! fgets( line, 1024, file ) )
901     {
902         msg_Err( p_playlist, "playlist file %s is empty", psz_filename );
903         fclose( file );
904         return -1;
905     }
906
907     /* get rid of line feed */
908     if( line[strlen(line)-1] == '\n' || line[strlen(line)-1] == '\r' )
909     {
910        line[strlen(line)-1] = (char)0;
911        if( line[strlen(line)-1] == '\r' ) line[strlen(line)-1] = (char)0;
912     }
913     /* check the file format is valid */
914     if ( strcmp ( line , PLAYLIST_FILE_HEADER_0_5 ) )
915     {
916         msg_Err( p_playlist, "playlist file %s format is unsupported"
917                 , psz_filename );
918         fclose( file );
919         return -1;
920     }
921
922     /* stop playing */
923     i_current_status = p_playlist->i_status;
924     if ( p_playlist->i_status != PLAYLIST_STOPPED )
925     {
926         playlist_Stop ( p_playlist );
927     }
928
929     /* delete current content of the playlist */
930     for( i = p_playlist->i_size - 1; i >= 0; i-- )
931     {
932         playlist_Delete ( p_playlist , i );
933     }
934
935     /* simply add each line */
936     while( fgets( line, 1024, file ) )
937     {
938        /* ignore comments or empty lines */
939        if( (line[0] == '#') || (line[0] == '\r') || (line[0] == '\n')
940                || (line[0] == (char)0) )
941            continue;
942
943        /* get rid of line feed */
944        if( line[strlen(line)-1] == '\n' || line[strlen(line)-1] == '\r' )
945        {
946            line[strlen(line)-1] = (char)0;
947            if( line[strlen(line)-1] == '\r' ) line[strlen(line)-1] = (char)0;
948        }
949
950        playlist_Add ( p_playlist , (char *)&line ,
951                       0, 0, PLAYLIST_APPEND , PLAYLIST_END );
952     }
953
954     /* start playing */
955     if ( i_current_status != PLAYLIST_STOPPED )
956     {
957         playlist_Play ( p_playlist );
958     }
959
960     fclose( file );
961
962     return 0;
963 }
964
965 /*****************************************************************************
966  * playlist_SaveFile: Save a playlist in a file.
967  *****************************************************************************/
968 int playlist_SaveFile( playlist_t * p_playlist, const char * psz_filename )
969 {
970     FILE *file;
971     int i;
972
973     vlc_mutex_lock( &p_playlist->object_lock );
974
975     msg_Dbg( p_playlist, "saving playlist file %s", psz_filename );
976
977     file = fopen( psz_filename, "wt" );
978     if( !file )
979     {
980         msg_Err( p_playlist , "could not create playlist file %s"
981                 , psz_filename );
982         return -1;
983     }
984
985     fprintf( file , PLAYLIST_FILE_HEADER_0_5 "\n" );
986
987     for ( i = 0 ; i < p_playlist->i_size ; i++ )
988     {
989         fprintf( file , p_playlist->pp_items[i]->psz_uri );
990         fprintf( file , "\n" );
991     }
992
993     fclose( file );
994
995     vlc_mutex_unlock( &p_playlist->object_lock );
996
997     return 0;
998 }