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