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