]> git.sesse.net Git - vlc/blob - src/playlist/control.c
* Fix skip in initial status
[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         /* if already running, keep running */
172         if( p_playlist->status.i_status != PLAYLIST_STOPPED )
173             p_playlist->request.i_status = p_playlist->status.i_status;
174         p_playlist->request.b_request = VLC_TRUE;
175         break;
176
177     default:
178         msg_Err( p_playlist, "unknown playlist query" );
179         return VLC_EBADVAR;
180         break;
181     }
182
183     return VLC_SUCCESS;
184 }
185
186 /*****************************************************************************
187  * Preparse control
188  *****************************************************************************/
189 /** Enqueue an item for preparsing */
190 int playlist_PreparseEnqueue( playlist_t *p_playlist,
191                               input_item_t *p_item )
192 {
193     vlc_mutex_lock( &p_playlist->p_preparse->object_lock );
194     vlc_gc_incref( p_item );
195     INSERT_ELEM( p_playlist->p_preparse->pp_waiting,
196                  p_playlist->p_preparse->i_waiting,
197                  p_playlist->p_preparse->i_waiting,
198                  p_item );
199     vlc_mutex_unlock( &p_playlist->p_preparse->object_lock );
200     return VLC_SUCCESS;
201 }
202
203 /** Enqueue a playlist item or a node for peparsing.
204  *  This function should be entered without playlist and preparser locks */
205 int playlist_PreparseEnqueueItem( playlist_t *p_playlist,
206                                   playlist_item_t *p_item )
207 {
208     vlc_mutex_lock( &p_playlist->object_lock );
209     vlc_mutex_lock( &p_playlist->p_preparse->object_lock );
210     PreparseEnqueueItemSub( p_playlist, p_item );
211     vlc_mutex_unlock( &p_playlist->p_preparse->object_lock );
212     vlc_mutex_unlock( &p_playlist->object_lock );
213     return VLC_SUCCESS;
214 }
215
216 void PreparseEnqueueItemSub( playlist_t *p_playlist,
217                              playlist_item_t *p_item )
218 {
219     int i;
220     if( p_item->i_children == -1 )
221     {
222         vlc_gc_incref( p_item );
223         INSERT_ELEM( p_playlist->p_preparse->pp_waiting,
224                      p_playlist->p_preparse->i_waiting,
225                      p_playlist->p_preparse->i_waiting,
226                      p_item->p_input );
227     }
228     else
229     {
230         for( i = 0; i < p_item->i_children; i++)
231         {
232             PreparseEnqueueItemSub( p_playlist,
233                                              p_item->pp_children[i] );
234         }
235     }
236 }
237
238 /*****************************************************************************
239  * Playback logic
240  *****************************************************************************/
241
242 /** This function calculates the next playlist item, depending
243  *  on the playlist course mode (forward, backward, random, view,...). */
244 playlist_item_t * playlist_NextItem( playlist_t *p_playlist )
245 {
246     playlist_item_t *p_new = NULL;
247     int i_skip,i;
248
249     vlc_bool_t b_loop = var_GetBool( p_playlist, "loop" );
250     vlc_bool_t b_random = var_GetBool( p_playlist, "random" );
251     vlc_bool_t b_repeat = var_GetBool( p_playlist, "repeat" );
252     vlc_bool_t b_playstop = var_GetBool( p_playlist, "play-and-stop" );
253
254     /* Handle quickly a few special cases */
255     /* No items to play */
256     if( p_playlist->i_size == 0 )
257     {
258         msg_Info( p_playlist, "playlist is empty" );
259         return NULL;
260     }
261
262     /* Repeat and play/stop */
263     if( !p_playlist->request.b_request && b_repeat == VLC_TRUE &&
264          p_playlist->status.p_item )
265     {
266         msg_Dbg( p_playlist,"repeating item" );
267         return p_playlist->status.p_item;
268     }
269     if( !p_playlist->request.b_request && b_playstop == VLC_TRUE )
270     {
271         msg_Dbg( p_playlist,"stopping (play and stop)");
272         return NULL;
273     }
274
275     if( !p_playlist->request.b_request && p_playlist->status.p_item &&
276          p_playlist->status.p_item->i_flags & PLAYLIST_SKIP_FLAG )
277     {
278         msg_Dbg( p_playlist, "blocking item, stopping") ;
279         return NULL;
280     }
281
282     /* Random case. This is an exception: if request, but request is skip +- 1
283      * we don't go to next item but select a new random one. */
284     if( b_random )
285         msg_Err( p_playlist, "random unsupported" );
286 #if 0
287             &&
288         ( !p_playlist->request.b_request ||
289         ( p_playlist->request.b_request && ( p_playlist->request.p_item == NULL ||
290           p_playlist->request.i_skip == 1 || p_playlist->request.i_skip == -1 ) ) ) )
291     {
292         /* how many items to choose from ? */
293         i_count = 0;
294         for ( i = 0; i < p_playlist->i_size; i++ )
295         {
296             if ( p_playlist->pp_items[i]->p_input->i_nb_played == 0 )
297                 i_count++;
298         }
299         /* Nothing left? */
300         if ( i_count == 0 )
301         {
302             /* Don't loop? Exit! */
303             if( !b_loop )
304                 return NULL;
305             /* Otherwise reset the counter */
306             for ( i = 0; i < p_playlist->i_size; i++ )
307             {
308                 p_playlist->pp_items[i]->p_input->i_nb_played = 0;
309             }
310             i_count = p_playlist->i_size;
311         }
312         srand( (unsigned int)mdate() );
313         i = rand() % i_count + 1 ;
314         /* loop thru the list and count down the unplayed items to the selected one */
315         for ( i_new = 0; i_new < p_playlist->i_size && i > 0; i_new++ )
316         {
317             if ( p_playlist->pp_items[i_new]->p_input->i_nb_played == 0 )
318                 i--;
319         }
320         i_new--;
321
322         p_playlist->request.i_skip = 0;
323         p_playlist->request.b_request = VLC_FALSE;
324         return p_playlist->pp_items[i_new];
325     }
326 #endif
327
328     /* Start the real work */
329     if( p_playlist->request.b_request )
330     {
331         PL_DEBUG( "processing request" );
332         p_new = p_playlist->request.p_item;
333         i_skip = p_playlist->request.i_skip;
334
335         if( p_playlist->request.p_node )
336             p_playlist->status.p_node = p_playlist->request.p_node;
337
338         /* If we are asked for a node, dont take it */
339         if( i_skip == 0 && ( p_new == NULL || p_new->i_children != -1 ) )
340             i_skip++;
341
342         if( i_skip > 0 )
343         {
344             for( i = i_skip; i > 0 ; i-- )
345             {
346                 p_new = playlist_GetNextEnabledLeaf( p_playlist,
347                                                      p_playlist->request.p_node,
348                                                      p_new );
349                 if( p_new == NULL )
350                 {
351                     PL_DEBUG( "looping - restarting at beginning of node" );
352                     p_new = playlist_GetNextLeaf( p_playlist,
353                                                   p_playlist->request.p_node,
354                                                   NULL );
355                     if( p_new == NULL ) break;
356                 }
357             }
358         }
359         else if( i_skip < 0 )
360         {
361             for( i = i_skip; i < 0 ; i++ )
362             {
363                 p_new = playlist_GetPrevLeaf( p_playlist,
364                                               p_playlist->request.p_node,
365                                               p_new );
366                 if( p_new == NULL )
367                 {
368                     PL_DEBUG( "looping - restarting at end of node" );
369                     /** \bug This is needed because GetPrevLeaf does not loop
370                       * by itself */
371                     p_new = playlist_GetLastLeaf( p_playlist,
372                                                  p_playlist->request.p_node );
373                 }
374                 if( p_new == NULL ) break;
375             }
376         }
377         /* Clear the request */
378         p_playlist->request.b_request = VLC_FALSE;
379     }
380     /* "Automatic" item change ( next ) */
381     else
382     {
383         PL_DEBUG( "changing item without a request" );
384         /* Cant go to next from current item */
385         if( p_playlist->status.p_item &&
386             p_playlist->status.p_item->i_flags & PLAYLIST_SKIP_FLAG )
387             return NULL;
388
389         p_new = playlist_GetNextLeaf( p_playlist,
390                                       p_playlist->status.p_node,
391                                       p_playlist->status.p_item );
392         if( p_new == NULL && b_loop )
393         {
394             PL_DEBUG( "looping" );
395             p_new = playlist_GetNextLeaf( p_playlist,
396                                           p_playlist->status.p_node,
397                                           NULL );
398         }
399         /* The new item can't be autoselected  */
400         if( p_new != NULL && p_new->i_flags & PLAYLIST_SKIP_FLAG )
401             return NULL;
402     }
403     if( p_new == NULL )
404     {
405         msg_Dbg( p_playlist, "did not find something to play" );
406     }
407     return p_new;
408 }
409
410 /** Start the input for an item */
411 int playlist_PlayItem( playlist_t *p_playlist, playlist_item_t *p_item )
412 {
413     vlc_value_t val;
414     int i_activity = var_GetInteger( p_playlist, "activity") ;
415
416     msg_Dbg( p_playlist, "creating new input thread" );
417
418     p_item->p_input->i_nb_played++;
419     p_playlist->status.p_item = p_item;
420
421     p_playlist->status.i_status = PLAYLIST_RUNNING;
422
423     var_SetInteger( p_playlist, "activity", i_activity +
424                     DEFAULT_INPUT_ACTIVITY );
425     p_playlist->p_input = input_CreateThread( p_playlist, p_item->p_input );
426
427     val.i_int = p_item->p_input->i_id;
428     /* unlock the playlist to set the var...mmm */
429     vlc_mutex_unlock( &p_playlist->object_lock);
430     var_Set( p_playlist, "playlist-current", val);
431     vlc_mutex_lock( &p_playlist->object_lock);
432
433     return VLC_SUCCESS;
434 }