]> git.sesse.net Git - vlc/blob - src/playlist/engine.c
Re-enable random.
[vlc] / src / playlist / engine.c
1 /*****************************************************************************
2  * engine.c : Run the playlist and handle its control
3  *****************************************************************************
4  * Copyright (C) 1999-2004 the VideoLAN team
5  * $Id: /local/vlc/0.8.6-playlist-vlm/src/playlist/playlist.c 13741 2006-03-21T19:29:39.792444Z zorglub  $
6  *
7  * Authors: Samuel Hocevar <sam@zoy.org>
8  *          ClĂ©ment Stenac <zorglub@videolan.org>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24 #include <vlc/vlc.h>
25 #include <vlc/vout.h>
26 #include <vlc/sout.h>
27 #include <vlc/input.h>
28 #include "vlc_playlist.h"
29 #include "vlc_interaction.h"
30
31 /*****************************************************************************
32  * Local prototypes
33  *****************************************************************************/
34 static void VariablesInit( playlist_t *p_playlist );
35
36 /**
37  * Create playlist
38  *
39  * Create a playlist structure.
40  * \param p_parent the vlc object that is to be the parent of this playlist
41  * \return a pointer to the created playlist, or NULL on error
42  */
43 playlist_t * playlist_Create( vlc_object_t *p_parent )
44 {
45     playlist_t *p_playlist;
46     int i_tree;
47
48     /* Allocate structure */
49     p_playlist = vlc_object_create( p_parent, VLC_OBJECT_PLAYLIST );
50     if( !p_playlist )
51     {
52         msg_Err( p_parent, "out of memory" );
53         return NULL;
54     }
55
56     VariablesInit( p_playlist );
57
58     /* Initialise data structures */
59     vlc_mutex_init( p_playlist, &p_playlist->gc_lock );
60     p_playlist->i_last_playlist_id = 0;
61     p_playlist->i_last_input_id = 0;
62     p_playlist->p_input = NULL;
63
64     p_playlist->i_vout_destroyed_date = 0;
65     p_playlist->i_sout_destroyed_date = 0;
66
67     p_playlist->i_size = 0;
68     p_playlist->pp_items = NULL;
69     p_playlist->i_all_size = 0;
70     p_playlist->pp_all_items = NULL;
71
72     p_playlist->i_input_items = 0;
73     p_playlist->pp_input_items = NULL;
74
75     p_playlist->i_random = 0;
76     p_playlist->pp_random = NULL;
77     p_playlist->i_random_index = 0;
78     p_playlist->b_reset_random = VLC_TRUE;
79
80     i_tree = var_CreateGetBool( p_playlist, "playlist-tree" );
81     p_playlist->b_always_tree = (i_tree == 1);
82     p_playlist->b_never_tree = (i_tree == 2);
83
84     p_playlist->p_root_category = playlist_NodeCreate( p_playlist, NULL, NULL);
85     p_playlist->p_root_onelevel = playlist_NodeCreate( p_playlist, NULL, NULL);
86
87     /* Create playlist and media library */
88     p_playlist->p_local_category = playlist_NodeCreate( p_playlist,
89                                  _( "Playlist" ),p_playlist->p_root_category );
90     p_playlist->p_local_onelevel =  playlist_NodeCreate( p_playlist,
91                                 _( "Playlist" ), p_playlist->p_root_onelevel );
92     p_playlist->p_local_category->i_flags |= PLAYLIST_RO_FLAG;
93     p_playlist->p_local_onelevel->i_flags |= PLAYLIST_RO_FLAG;
94
95     /* Link the nodes together. Todo: actually create them from the same input*/
96     p_playlist->p_local_onelevel->p_input->i_id =
97         p_playlist->p_local_category->p_input->i_id;
98
99     if( config_GetInt( p_playlist, "media-library") )
100     {
101         p_playlist->p_ml_category =   playlist_NodeCreate( p_playlist,
102                            _( "Media Library" ), p_playlist->p_root_category );
103         p_playlist->p_ml_onelevel =  playlist_NodeCreate( p_playlist,
104                            _( "Media Library" ), p_playlist->p_root_onelevel );
105         p_playlist->p_ml_category->i_flags |= PLAYLIST_RO_FLAG;
106         p_playlist->p_ml_onelevel->i_flags |= PLAYLIST_RO_FLAG;
107         p_playlist->p_ml_onelevel->p_input->i_id =
108              p_playlist->p_ml_category->p_input->i_id;
109     }
110     else
111     {
112         p_playlist->p_ml_category = p_playlist->p_ml_onelevel = NULL;
113     }
114
115     /* Initial status */
116     p_playlist->status.p_item = NULL;
117     p_playlist->status.p_node = p_playlist->p_root_onelevel;
118     p_playlist->request.b_request = VLC_FALSE;
119     p_playlist->status.i_status = PLAYLIST_STOPPED;
120
121     p_playlist->i_sort = SORT_ID;
122     p_playlist->i_order = ORDER_NORMAL;
123
124     vlc_object_attach( p_playlist, p_parent );
125     return p_playlist;
126 }
127
128 void playlist_Destroy( playlist_t *p_playlist )
129 {
130     while( p_playlist->i_sds )
131     {
132         playlist_ServicesDiscoveryRemove( p_playlist,
133                                           p_playlist->pp_sds[0]->psz_module );
134     }
135     vlc_thread_join( p_playlist->p_preparse );
136     vlc_thread_join( p_playlist );
137
138     vlc_object_detach( p_playlist->p_preparse );
139
140     var_Destroy( p_playlist, "intf-change" );
141     var_Destroy( p_playlist, "item-change" );
142     var_Destroy( p_playlist, "playlist-current" );
143     var_Destroy( p_playlist, "intf-popmenu" );
144     var_Destroy( p_playlist, "intf-show" );
145     var_Destroy( p_playlist, "play-and-stop" );
146     var_Destroy( p_playlist, "random" );
147     var_Destroy( p_playlist, "repeat" );
148     var_Destroy( p_playlist, "loop" );
149     var_Destroy( p_playlist, "activity" );
150
151     PL_LOCK;
152     playlist_NodeDelete( p_playlist, p_playlist->p_root_category, VLC_TRUE,
153                          VLC_TRUE );
154     playlist_NodeDelete( p_playlist, p_playlist->p_root_onelevel, VLC_TRUE,
155                          VLC_TRUE );
156     PL_UNLOCK;
157
158     if( p_playlist->p_stats )
159         free( p_playlist->p_stats );
160
161     vlc_mutex_destroy( &p_playlist->gc_lock );
162     vlc_object_destroy( p_playlist->p_preparse );
163     vlc_object_destroy( p_playlist );
164
165 }
166 /* Destroy remaining objects */
167 static mtime_t ObjectGarbageCollector( playlist_t *p_playlist, int i_type,
168                                        mtime_t destroy_date )
169 {
170     vlc_object_t *p_obj;
171
172     if( destroy_date > mdate() ) return destroy_date;
173
174     if( destroy_date == 0 )
175     {
176         /* give a little time */
177         return mdate() + I64C(1000000);
178     }
179     else
180     {
181         vlc_mutex_lock( &p_playlist->gc_lock );
182         while( ( p_obj = vlc_object_find( p_playlist, i_type, FIND_CHILD ) ) )
183         {
184             if( p_obj->p_parent != (vlc_object_t*)p_playlist )
185             {
186                 /* only first child (ie unused) */
187                 vlc_object_release( p_obj );
188                 break;
189             }
190             if( i_type == VLC_OBJECT_VOUT )
191             {
192                 msg_Dbg( p_playlist, "garbage collector destroying 1 vout" );
193                 vlc_object_detach( p_obj );
194                 vlc_object_release( p_obj );
195                 vout_Destroy( (vout_thread_t *)p_obj );
196             }
197             else if( i_type == VLC_OBJECT_SOUT )
198             {
199                 vlc_object_release( p_obj );
200                 sout_DeleteInstance( (sout_instance_t*)p_obj );
201             }
202         }
203         vlc_mutex_unlock( &p_playlist->gc_lock );
204         return 0;
205     }
206 }
207
208 /** Main loop for the playlist */
209 void playlist_MainLoop( playlist_t *p_playlist )
210 {
211     playlist_item_t *p_item = NULL;
212
213
214     PL_LOCK
215
216     /* First, check if we have something to do */
217     /* FIXME : this can be called several times */
218     if( p_playlist->request.b_request )
219     {
220         /* Stop the existing input */
221         if( p_playlist->p_input && !p_playlist->p_input->b_die )
222         {
223             PL_DEBUG( "incoming request - stopping current input" );
224             input_StopThread( p_playlist->p_input );
225         }
226     }
227
228     /* If there is an input, check that it doesn't need to die. */
229     if( p_playlist->p_input )
230     {
231         /* This input is dead. Remove it ! */
232         if( p_playlist->p_input->b_dead )
233         {
234             int i_activity;
235             input_thread_t *p_input;
236             PL_DEBUG( "dead input" );
237
238             p_input = p_playlist->p_input;
239             p_playlist->p_input = NULL;
240
241             /* Release the playlist lock, because we may get stuck
242              * in input_DestroyThread() for some time. */
243             PL_UNLOCK
244
245             /* Destroy input */
246             input_DestroyThread( p_input );
247
248             /* Unlink current input
249              * (_after_ input_DestroyThread for vout garbage collector) */
250             vlc_object_detach( p_input );
251
252             /* Destroy object */
253             vlc_object_destroy( p_input );
254
255             p_playlist->i_vout_destroyed_date = 0;
256             p_playlist->i_sout_destroyed_date = 0;
257
258             if( p_playlist->status.p_item->i_flags
259                 & PLAYLIST_REMOVE_FLAG )
260             {
261                  PL_DEBUG( "%s was marked for deletion, deleting",
262                                  PLI_NAME( p_playlist->status.p_item  ) );
263                  playlist_ItemDelete( p_playlist->status.p_item );
264                  if( p_playlist->request.p_item == p_playlist->status.p_item )
265                      p_playlist->request.p_item = NULL;
266                  p_playlist->status.p_item = NULL;
267             }
268
269             i_activity= var_GetInteger( p_playlist, "activity") ;
270             var_SetInteger( p_playlist, "activity", i_activity -
271                             DEFAULT_INPUT_ACTIVITY );
272
273             return;
274         }
275         /* This input is dying, let it do */
276         else if( p_playlist->p_input->b_die )
277         {
278             PL_DEBUG( "dying input" );
279         }
280         /* This input has finished, ask it to die ! */
281         else if( p_playlist->p_input->b_error
282                   || p_playlist->p_input->b_eof )
283         {
284             PL_DEBUG( "finished input" );
285             input_StopThread( p_playlist->p_input );
286             /* Select the next playlist item */
287             PL_UNLOCK
288             return;
289         }
290         else if( p_playlist->p_input->i_state != INIT_S )
291         {
292             PL_UNLOCK;
293             p_playlist->i_vout_destroyed_date =
294                 ObjectGarbageCollector( p_playlist, VLC_OBJECT_VOUT,
295                                         p_playlist->i_vout_destroyed_date );
296             p_playlist->i_sout_destroyed_date =
297                 ObjectGarbageCollector( p_playlist, VLC_OBJECT_SOUT,
298                                         p_playlist->i_sout_destroyed_date );
299             PL_LOCK
300         }
301     }
302     else
303     {
304         /* No input. Several cases
305          *  - No request, running status -> start new item
306          *  - No request, stopped status -> collect garbage
307          *  - Request, running requested -> start new item
308          *  - Request, stopped requested -> collect garbage
309          */
310          if( (!p_playlist->request.b_request &&
311               p_playlist->status.i_status != PLAYLIST_STOPPED) ||
312               ( p_playlist->request.b_request &&
313                 p_playlist->request.i_status != PLAYLIST_STOPPED ) )
314          {
315              msg_Dbg( p_playlist, "starting new item" );
316              stats_TimerStart( p_playlist, "Playlist walk",
317                                   STATS_TIMER_PLAYLIST_WALK );
318              p_item = playlist_NextItem( p_playlist );
319              stats_TimerStop( p_playlist, STATS_TIMER_PLAYLIST_WALK );
320
321              if( p_item == NULL )
322              {
323                 msg_Dbg( p_playlist, "nothing to play" );
324                 p_playlist->status.i_status = PLAYLIST_STOPPED;
325                 PL_UNLOCK
326                 return;
327              }
328              playlist_PlayItem( p_playlist, p_item );
329          }
330          else
331          {
332              if( p_playlist->status.p_item &&
333                  p_playlist->status.p_item->i_flags & PLAYLIST_REMOVE_FLAG )
334              {
335                  PL_DEBUG( "deleting item marked for deletion" );
336                  playlist_ItemDelete( p_playlist->status.p_item );
337                  p_playlist->status.p_item = NULL;
338              }
339
340              /* Collect garbage */
341              PL_UNLOCK
342              p_playlist->i_sout_destroyed_date =
343              ObjectGarbageCollector( p_playlist, VLC_OBJECT_SOUT, mdate() );
344              p_playlist->i_vout_destroyed_date =
345              ObjectGarbageCollector( p_playlist, VLC_OBJECT_VOUT, mdate() );
346              PL_LOCK
347          }
348     }
349     PL_UNLOCK
350 }
351
352 /** Playlist dying last loop */
353 void playlist_LastLoop( playlist_t *p_playlist )
354 {
355     vlc_object_t *p_obj;
356
357     /* If there is an input, kill it */
358     while( 1 )
359     {
360         PL_LOCK
361
362         if( p_playlist->p_input == NULL )
363         {
364             PL_UNLOCK
365             break;
366         }
367
368         if( p_playlist->p_input->b_dead )
369         {
370             input_thread_t *p_input;
371
372             /* Unlink current input */
373             p_input = p_playlist->p_input;
374             p_playlist->p_input = NULL;
375             PL_UNLOCK
376
377             /* Destroy input */
378             input_DestroyThread( p_input );
379             /* Unlink current input (_after_ input_DestroyThread for vout
380              * garbage collector)*/
381             vlc_object_detach( p_input );
382
383             /* Destroy object */
384             vlc_object_destroy( p_input );
385             continue;
386         }
387         else if( p_playlist->p_input->b_die )
388         {
389             /* This input is dying, leave it alone */
390             ;
391         }
392         else if( p_playlist->p_input->b_error || p_playlist->p_input->b_eof )
393         {
394             input_StopThread( p_playlist->p_input );
395             PL_UNLOCK
396             continue;
397         }
398         else
399         {
400             p_playlist->p_input->b_eof = 1;
401         }
402
403         PL_UNLOCK
404
405         msleep( INTF_IDLE_SLEEP );
406     }
407
408     /* close all remaining sout */
409     while( ( p_obj = vlc_object_find( p_playlist,
410                                       VLC_OBJECT_SOUT, FIND_CHILD ) ) )
411     {
412         vlc_object_release( p_obj );
413         sout_DeleteInstance( (sout_instance_t*)p_obj );
414     }
415
416     /* close all remaining vout */
417     while( ( p_obj = vlc_object_find( p_playlist,
418                                       VLC_OBJECT_VOUT, FIND_CHILD ) ) )
419     {
420         vlc_object_detach( p_obj );
421         vlc_object_release( p_obj );
422         vout_Destroy( (vout_thread_t *)p_obj );
423     }
424 }
425
426 /** Main loop for preparser queue */
427 void playlist_PreparseLoop( playlist_preparse_t *p_obj )
428 {
429     playlist_t *p_playlist = (playlist_t *)p_obj->p_parent;
430     int i_activity;
431
432     vlc_mutex_lock( &p_obj->object_lock );
433
434     if( p_obj->i_waiting > 0 )
435     {
436         input_item_t *p_current = p_playlist->p_preparse->pp_waiting[0];
437         REMOVE_ELEM( p_obj->pp_waiting, p_obj->i_waiting, 0 );
438         vlc_mutex_unlock( &p_obj->object_lock );
439         vlc_mutex_lock( &p_playlist->object_lock );
440         if( p_current )
441         {
442             vlc_bool_t b_preparsed = VLC_FALSE;
443             if( strncmp( p_current->psz_uri, "http:", 5 ) &&
444                 strncmp( p_current->psz_uri, "rtsp:", 5 ) &&
445                 strncmp( p_current->psz_uri, "udp:", 4 ) &&
446                 strncmp( p_current->psz_uri, "mms:", 4 ) &&
447                 strncmp( p_current->psz_uri, "cdda:", 4 ) &&
448                 strncmp( p_current->psz_uri, "dvd:", 4 ) &&
449                 strncmp( p_current->psz_uri, "v4l:", 4 ) &&
450                 strncmp( p_current->psz_uri, "dshow:", 6 ) )
451             {
452                 b_preparsed = VLC_TRUE;
453                 stats_TimerStart( p_playlist, "Preparse run",
454                                   STATS_TIMER_PREPARSE );
455                 input_Preparse( p_playlist, p_current );
456                 stats_TimerStop( p_playlist, STATS_TIMER_PREPARSE );
457             }
458             vlc_mutex_unlock( &p_playlist->object_lock );
459             if( b_preparsed )
460             {
461                 var_SetInteger( p_playlist, "item-change",
462                                 p_current->i_id );
463             }
464             vlc_gc_decref( p_current );
465         }
466         else
467         {
468             vlc_mutex_unlock( &p_playlist->object_lock );
469         }
470         vlc_mutex_lock( &p_obj->object_lock );
471         i_activity=  var_GetInteger( p_playlist, "activity" );
472         if( i_activity < 0 ) i_activity = 0;
473         vlc_mutex_unlock( &p_obj->object_lock );
474         msleep( (i_activity+1) * 1000 );
475         return;
476     }
477     vlc_mutex_unlock( &p_obj->object_lock );
478 }
479
480 static void VariablesInit( playlist_t *p_playlist )
481 {
482     vlc_value_t val;
483     /* These variables control updates */
484     var_Create( p_playlist, "intf-change", VLC_VAR_BOOL );
485     val.b_bool = VLC_TRUE;
486     var_Set( p_playlist, "intf-change", val );
487
488     var_Create( p_playlist, "item-change", VLC_VAR_INTEGER );
489     val.i_int = -1;
490     var_Set( p_playlist, "item-change", val );
491
492     var_Create( p_playlist, "item-deleted", VLC_VAR_INTEGER );
493     val.i_int = -1;
494     var_Set( p_playlist, "item-deleted", val );
495
496     var_Create( p_playlist, "item-append", VLC_VAR_ADDRESS );
497
498     var_Create( p_playlist, "playlist-current", VLC_VAR_INTEGER );
499     val.i_int = -1;
500     var_Set( p_playlist, "playlist-current", val );
501
502     var_Create( p_playlist, "intf-popupmenu", VLC_VAR_BOOL );
503
504     var_Create( p_playlist, "intf-show", VLC_VAR_BOOL );
505     val.b_bool = VLC_TRUE;
506     var_Set( p_playlist, "intf-show", val );
507
508     var_Create( p_playlist, "activity", VLC_VAR_INTEGER );
509     var_SetInteger( p_playlist, "activity", 0 );
510
511     /* Variables to control playback */
512     var_CreateGetBool( p_playlist, "play-and-stop" );
513     var_CreateGetBool( p_playlist, "random" );
514     var_CreateGetBool( p_playlist, "repeat" );
515     var_CreateGetBool( p_playlist, "loop" );
516 }