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