]> git.sesse.net Git - vlc/blob - src/playlist/control.c
Fix playlist crasher and simplify a few things
[vlc] / src / playlist / control.c
1 /*****************************************************************************
2  * control.c : Hanle control of the playlist & running through it
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/input.h>
26 #include "vlc_playlist.h"
27 #include <assert.h>
28
29 /*****************************************************************************
30  * Local prototypes
31  *****************************************************************************/
32 int PlaylistVAControl( playlist_t * p_playlist, int i_query, va_list args );
33
34 void PreparseEnqueueItemSub( playlist_t *, playlist_item_t * );
35
36 playlist_item_t *playlist_RecursiveFindLast(playlist_t *p_playlist,
37                                             playlist_item_t *p_node );
38
39 /*****************************************************************************
40  * Playlist control
41  *****************************************************************************/
42
43 /**
44  * Do a playlist action. Should be entered without playlist lock
45  * \see playlist_Control
46  */
47 int playlist_LockControl( playlist_t * p_playlist, int i_query, ... )
48 {
49     va_list args;
50     int i_result;
51     va_start( args, i_query );
52     vlc_mutex_lock( &p_playlist->object_lock );
53     i_result = PlaylistVAControl( p_playlist, i_query, args );
54     va_end( args );
55     vlc_mutex_unlock( &p_playlist->object_lock );
56     return i_result;
57 }
58
59 /**
60  * Do a playlist action.
61  * If there is something in the playlist then you can do playlist actions.
62  * Should be entered with playlist lock. See include/vlc_playlist.h for
63  * possible queries
64  *
65  * \param p_playlist the playlist to do the command on
66  * \param i_query the command to do
67  * \param variable number of arguments
68  * \return VLC_SUCCESS or an error
69  */
70 int playlist_Control( playlist_t * p_playlist, int i_query, ... )
71 {
72     va_list args;
73     int i_result;
74     va_start( args, i_query );
75     i_result = PlaylistVAControl( p_playlist, i_query, args );
76     va_end( args );
77
78     return i_result;
79 }
80
81 int PlaylistVAControl( playlist_t * p_playlist, int i_query, va_list args )
82 {
83     int i_view;
84     playlist_item_t *p_item, *p_node;
85     vlc_value_t val;
86
87     if( p_playlist->i_size <= 0 )
88     {
89         return VLC_EGENERIC;
90     }
91
92     switch( i_query )
93     {
94     case PLAYLIST_STOP:
95         p_playlist->request.i_status = PLAYLIST_STOPPED;
96         p_playlist->request.b_request = VLC_TRUE;
97         p_playlist->request.p_item = NULL;
98         break;
99
100     // Node can be null, it will keep the same. Use with care ...
101     // Item null = take the first child of node
102     case PLAYLIST_VIEWPLAY:
103         p_node = (playlist_item_t *)va_arg( args, playlist_item_t * );
104         p_item = (playlist_item_t *)va_arg( args, playlist_item_t * );
105         if ( p_node == NULL )
106         {
107             p_node = p_playlist->status.p_node;
108             assert( p_node );
109         }
110         p_playlist->request.i_status = PLAYLIST_RUNNING;
111         p_playlist->request.i_skip = 0;
112         p_playlist->request.b_request = VLC_TRUE;
113         p_playlist->request.p_node = p_node;
114         p_playlist->request.p_item = p_item;
115         break;
116
117     case PLAYLIST_PLAY:
118         if( p_playlist->p_input )
119         {
120             val.i_int = PLAYING_S;
121             var_Set( p_playlist->p_input, "state", val );
122             break;
123         }
124         else
125         {
126             p_playlist->request.i_status = PLAYLIST_RUNNING;
127             p_playlist->request.b_request = VLC_TRUE;
128             p_playlist->request.p_node = p_playlist->status.p_node;
129             p_playlist->request.p_item = p_playlist->status.p_item;
130             p_playlist->request.i_skip = 0;
131         }
132         break;
133
134     case PLAYLIST_AUTOPLAY:
135         // AUTOPLAY is an ugly hack for initial status.
136         // Hopefully it will disappear
137         p_playlist->status.i_status = PLAYLIST_RUNNING;
138         p_playlist->request.p_node = p_playlist->status.p_node;
139         p_playlist->request.b_request = VLC_FALSE;
140         break;
141
142     case PLAYLIST_PAUSE:
143         val.i_int = 0;
144         if( p_playlist->p_input )
145             var_Get( p_playlist->p_input, "state", &val );
146
147         if( val.i_int == PAUSE_S )
148         {
149             p_playlist->status.i_status = PLAYLIST_RUNNING;
150             if( p_playlist->p_input )
151             {
152                 val.i_int = PLAYING_S;
153                 var_Set( p_playlist->p_input, "state", val );
154             }
155         }
156         else
157         {
158             p_playlist->status.i_status = PLAYLIST_PAUSED;
159             if( p_playlist->p_input )
160             {
161                 val.i_int = PAUSE_S;
162                 var_Set( p_playlist->p_input, "state", val );
163             }
164         }
165         break;
166
167     case PLAYLIST_SKIP:
168         p_playlist->request.p_node = p_playlist->status.p_node;
169         p_playlist->request.p_item = p_playlist->status.p_item;
170         p_playlist->request.i_skip = (int) va_arg( args, int );
171         p_playlist->request.b_request = VLC_TRUE;
172         break;
173
174     default:
175         msg_Err( p_playlist, "unknown playlist query" );
176         return VLC_EBADVAR;
177         break;
178     }
179
180     return VLC_SUCCESS;
181 }
182
183 /*****************************************************************************
184  * Preparse control
185  *****************************************************************************/
186 /** Enqueue an item for preparsing */
187 int playlist_PreparseEnqueue( playlist_t *p_playlist,
188                               input_item_t *p_item )
189 {
190     vlc_mutex_lock( &p_playlist->p_preparse->object_lock );
191     vlc_gc_incref( p_item );
192     INSERT_ELEM( p_playlist->p_preparse->pp_waiting,
193                  p_playlist->p_preparse->i_waiting,
194                  p_playlist->p_preparse->i_waiting,
195                  p_item );
196     vlc_mutex_unlock( &p_playlist->p_preparse->object_lock );
197     return VLC_SUCCESS;
198 }
199
200 /** Enqueue a playlist item or a node for peparsing.
201  *  This function should be entered without playlist and preparser locks */
202 int playlist_PreparseEnqueueItem( playlist_t *p_playlist,
203                                   playlist_item_t *p_item )
204 {
205     vlc_mutex_lock( &p_playlist->object_lock );
206     vlc_mutex_lock( &p_playlist->p_preparse->object_lock );
207     PreparseEnqueueItemSub( p_playlist, p_item );
208     vlc_mutex_unlock( &p_playlist->p_preparse->object_lock );
209     vlc_mutex_unlock( &p_playlist->object_lock );
210     return VLC_SUCCESS;
211 }
212
213 void PreparseEnqueueItemSub( playlist_t *p_playlist,
214                              playlist_item_t *p_item )
215 {
216     int i;
217     if( p_item->i_children == -1 )
218     {
219         vlc_gc_incref( p_item );
220         INSERT_ELEM( p_playlist->p_preparse->pp_waiting,
221                      p_playlist->p_preparse->i_waiting,
222                      p_playlist->p_preparse->i_waiting,
223                      p_item->p_input );
224     }
225     else
226     {
227         for( i = 0; i < p_item->i_children; i++)
228         {
229             PreparseEnqueueItemSub( p_playlist,
230                                              p_item->pp_children[i] );
231         }
232     }
233 }
234
235 /*****************************************************************************
236  * Playback logic
237  *****************************************************************************/
238
239 /** This function calculates the next playlist item, depending
240  *  on the playlist course mode (forward, backward, random, view,...). */
241 playlist_item_t * playlist_NextItem( playlist_t *p_playlist )
242 {
243     playlist_item_t *p_new = NULL;
244     int i_skip,i;
245
246     vlc_bool_t b_loop = var_GetBool( p_playlist, "loop" );
247     vlc_bool_t b_random = var_GetBool( p_playlist, "random" );
248     vlc_bool_t b_repeat = var_GetBool( p_playlist, "repeat" );
249     vlc_bool_t b_playstop = var_GetBool( p_playlist, "play-and-stop" );
250
251     /* Handle quickly a few special cases */
252     /* No items to play */
253     if( p_playlist->i_size == 0 )
254     {
255         msg_Info( p_playlist, "playlist is empty" );
256         return NULL;
257     }
258
259     /* Repeat and play/stop */
260     if( !p_playlist->request.b_request && b_repeat == VLC_TRUE &&
261          p_playlist->status.p_item )
262     {
263         msg_Dbg( p_playlist,"repeating item" );
264         return p_playlist->status.p_item;
265     }
266     if( !p_playlist->request.b_request && b_playstop == VLC_TRUE )
267     {
268         msg_Dbg( p_playlist,"stopping (play and stop)");
269         return NULL;
270     }
271
272     if( !p_playlist->request.b_request && p_playlist->status.p_item &&
273          p_playlist->status.p_item->i_flags & PLAYLIST_SKIP_FLAG )
274     {
275         msg_Dbg( p_playlist, "blocking item, stopping") ;
276         return NULL;
277     }
278
279     /* Random case. This is an exception: if request, but request is skip +- 1
280      * we don't go to next item but select a new random one. */
281     if( b_random )
282         msg_Err( p_playlist, "random unsupported" );
283 #if 0
284             &&
285         ( !p_playlist->request.b_request ||
286         ( p_playlist->request.b_request && ( p_playlist->request.p_item == NULL ||
287           p_playlist->request.i_skip == 1 || p_playlist->request.i_skip == -1 ) ) ) )
288     {
289         /* how many items to choose from ? */
290         i_count = 0;
291         for ( i = 0; i < p_playlist->i_size; i++ )
292         {
293             if ( p_playlist->pp_items[i]->p_input->i_nb_played == 0 )
294                 i_count++;
295         }
296         /* Nothing left? */
297         if ( i_count == 0 )
298         {
299             /* Don't loop? Exit! */
300             if( !b_loop )
301                 return NULL;
302             /* Otherwise reset the counter */
303             for ( i = 0; i < p_playlist->i_size; i++ )
304             {
305                 p_playlist->pp_items[i]->p_input->i_nb_played = 0;
306             }
307             i_count = p_playlist->i_size;
308         }
309         srand( (unsigned int)mdate() );
310         i = rand() % i_count + 1 ;
311         /* loop thru the list and count down the unplayed items to the selected one */
312         for ( i_new = 0; i_new < p_playlist->i_size && i > 0; i_new++ )
313         {
314             if ( p_playlist->pp_items[i_new]->p_input->i_nb_played == 0 )
315                 i--;
316         }
317         i_new--;
318
319         p_playlist->request.i_skip = 0;
320         p_playlist->request.b_request = VLC_FALSE;
321         return p_playlist->pp_items[i_new];
322     }
323 #endif
324
325     /* Start the real work */
326     if( p_playlist->request.b_request )
327     {
328         PL_DEBUG( "processing request" );
329         p_new = p_playlist->request.p_item;
330         i_skip = p_playlist->request.i_skip;
331
332         if( p_playlist->request.p_node )
333             p_playlist->status.p_node = p_playlist->request.p_node;
334
335         /* If we are asked for a node, dont take it */
336         if( i_skip == 0 && ( p_new == NULL || p_new->i_children != -1 ) )
337             i_skip++;
338
339         if( i_skip > 0 )
340         {
341             for( i = i_skip; i > 0 ; i-- )
342             {
343                 p_new = playlist_GetNextEnabledLeaf( p_playlist,
344                                                      p_playlist->request.p_node,
345                                                      p_new );
346                 if( p_new == NULL )
347                 {
348                     PL_DEBUG( "looping - restarting at beginning of node" );
349                     p_new = playlist_GetNextLeaf( p_playlist,
350                                                   p_playlist->request.p_node,
351                                                   NULL );
352                     if( p_new == NULL ) break;
353                 }
354             }
355         }
356         else if( i_skip < 0 )
357         {
358             for( i = i_skip; i < 0 ; i++ )
359             {
360                 p_new = playlist_GetPrevLeaf( p_playlist,
361                                               p_playlist->request.p_node,
362                                               p_new );
363                 if( p_new == NULL )
364                 {
365                     PL_DEBUG( "looping - restarting at end of node" );
366                     /** \bug This is needed because GetPrevLeaf does not loop
367                       * by itself */
368                     p_new = playlist_GetLastLeaf( p_playlist,
369                                                  p_playlist->request.p_node );
370                 }
371                 if( p_new == NULL ) break;
372             }
373         }
374         /* Clear the request */
375         p_playlist->request.b_request = VLC_FALSE;
376     }
377     /* "Automatic" item change ( next ) */
378     else
379     {
380         PL_DEBUG( "changing item without a request" );
381         /* Cant go to next from current item */
382         if( p_playlist->status.p_item &&
383             p_playlist->status.p_item->i_flags & PLAYLIST_SKIP_FLAG )
384             return NULL;
385
386         p_new = playlist_GetNextLeaf( p_playlist,
387                                       p_playlist->status.p_node,
388                                       p_playlist->status.p_item );
389         if( p_new == NULL && b_loop )
390         {
391             PL_DEBUG( "looping" );
392             p_new = playlist_GetNextLeaf( p_playlist,
393                                           p_playlist->status.p_node,
394                                           NULL );
395         }
396         /* The new item can't be autoselected  */
397         if( p_new != NULL && p_new->i_flags & PLAYLIST_SKIP_FLAG )
398             return NULL;
399     }
400     if( p_new == NULL )
401     {
402         msg_Dbg( p_playlist, "did not find something to play" );
403     }
404     return p_new;
405 }
406
407 /** Start the input for an item */
408 int playlist_PlayItem( playlist_t *p_playlist, playlist_item_t *p_item )
409 {
410     vlc_value_t val;
411     int i_activity = var_GetInteger( p_playlist, "activity") ;
412
413     msg_Dbg( p_playlist, "creating new input thread" );
414
415     p_item->p_input->i_nb_played++;
416     p_playlist->status.p_item = p_item;
417
418     p_playlist->status.i_status = PLAYLIST_RUNNING;
419
420     var_SetInteger( p_playlist, "activity", i_activity +
421                     DEFAULT_INPUT_ACTIVITY );
422     p_playlist->p_input = input_CreateThread( p_playlist, p_item->p_input );
423
424     val.i_int = p_item->p_input->i_id;
425     /* unlock the playlist to set the var...mmm */
426     vlc_mutex_unlock( &p_playlist->object_lock);
427     var_Set( p_playlist, "playlist-current", val);
428     vlc_mutex_lock( &p_playlist->object_lock);
429
430     return VLC_SUCCESS;
431 }