]> git.sesse.net Git - vlc/blob - src/playlist/playlist.c
* src/playlist/playlist.c: fixed deadlock in playlist.
[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.23 2002/11/21 15:51:57 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
29 #include "stream_control.h"
30 #include "input_ext-intf.h"
31
32 #include "vlc_playlist.h"
33
34 #define PLAYLIST_STOPPED 0
35 #define PLAYLIST_RUNNING 1
36
37 /*****************************************************************************
38  * Local prototypes
39  *****************************************************************************/
40 static void RunThread ( playlist_t * );
41 static void SkipItem  ( playlist_t *, int );
42 static void PlayItem  ( playlist_t * );
43
44 static void Poubellize ( playlist_t *, input_thread_t * );
45
46 /*****************************************************************************
47  * playlist_Create: create playlist
48  *****************************************************************************
49  * Create a playlist structure.
50  *****************************************************************************/
51 playlist_t * __playlist_Create ( vlc_object_t *p_parent )
52 {
53     playlist_t *p_playlist;
54
55     /* Allocate structure */
56     p_playlist = vlc_object_create( p_parent, VLC_OBJECT_PLAYLIST );
57     if( !p_playlist )
58     {
59         msg_Err( p_parent, "out of memory" );
60         return NULL;
61     }
62
63     p_playlist->p_input = NULL;
64     p_playlist->i_status = PLAYLIST_STOPPED;
65     p_playlist->i_index = -1;
66     p_playlist->i_size = 0;
67     p_playlist->pp_items = NULL;
68
69     if( vlc_thread_create( p_playlist, "playlist", RunThread,
70                            VLC_THREAD_PRIORITY_LOW, VLC_TRUE ) )
71     {
72         msg_Err( p_playlist, "cannot spawn playlist thread" );
73         vlc_object_destroy( p_playlist );
74         return NULL;
75     }
76
77     /* The object has been initialized, now attach it */
78     vlc_object_attach( p_playlist, p_parent );
79
80     return p_playlist;
81 }
82
83 /*****************************************************************************
84  * playlist_Destroy: destroy the playlist
85  *****************************************************************************
86  * Delete all items in the playlist and free the playlist structure.
87  *****************************************************************************/
88 void playlist_Destroy( playlist_t * p_playlist )
89 {
90     p_playlist->b_die = 1;
91
92     vlc_thread_join( p_playlist );
93
94     vlc_object_destroy( p_playlist );
95 }
96
97 /*****************************************************************************
98  * playlist_Add: add an item to the playlist
99  *****************************************************************************
100  * Add an item to the playlist at position i_pos. If i_pos is PLAYLIST_END,
101  * add it at the end regardless of the playlist current size.
102  *****************************************************************************/
103 int playlist_Add( playlist_t *p_playlist, const char * psz_target,
104                                           int i_mode, int i_pos )
105 {
106     playlist_item_t *p_item;
107
108     msg_Dbg( p_playlist, "adding playlist item « %s »", psz_target );
109
110     /* Create the new playlist item */
111     p_item = malloc( sizeof( playlist_item_t ) );
112     if( p_item == NULL )
113     {
114         msg_Err( p_playlist, "out of memory" );
115     }
116
117     p_item->psz_name = strdup( psz_target );
118     p_item->i_type = 0;
119     p_item->i_status = 0;
120     p_item->b_autodeletion = VLC_FALSE;
121
122     vlc_mutex_lock( &p_playlist->object_lock );
123
124     /* Do a few boundary checks and allocate space for the item */
125     if( i_pos == PLAYLIST_END )
126     {
127         if( i_mode & PLAYLIST_INSERT )
128         {
129             i_mode &= ~PLAYLIST_INSERT;
130             i_mode |= PLAYLIST_APPEND;
131         }
132
133         i_pos = p_playlist->i_size - 1;
134     }
135
136     if( !(i_mode & PLAYLIST_REPLACE)
137          || i_pos < 0 || i_pos >= p_playlist->i_size )
138     {
139         /* Additional boundary checks */
140         if( i_mode & PLAYLIST_APPEND )
141         {
142             i_pos++;
143         }
144
145         if( i_pos < 0 )
146         {
147             i_pos = 0;
148         }
149         else if( i_pos > p_playlist->i_size )
150         {
151             i_pos = p_playlist->i_size;
152         }
153
154         INSERT_ELEM( p_playlist->pp_items,
155                      p_playlist->i_size,
156                      i_pos,
157                      p_item );
158
159         if( p_playlist->i_index >= i_pos )
160         {
161             p_playlist->i_index++;
162         }
163     }
164     else
165     {
166         /* i_mode == PLAYLIST_REPLACE and 0 <= i_pos < p_playlist->i_size */
167         free( p_playlist->pp_items[i_pos]->psz_name );
168         /* XXX: what if the item is still in use? */
169         free( p_playlist->pp_items[i_pos] );
170         p_playlist->pp_items[i_pos] = p_item;
171     }
172
173     if( i_mode & PLAYLIST_GO )
174     {
175         p_playlist->i_index = i_pos;
176         if( p_playlist->p_input )
177         {
178             input_StopThread( p_playlist->p_input );
179         }
180         p_playlist->i_status = PLAYLIST_RUNNING;
181     }
182
183     vlc_mutex_unlock( &p_playlist->object_lock );
184
185     return 0;
186 }
187
188 /*****************************************************************************
189  * playlist_Delete: delete an item from the playlist
190  *****************************************************************************
191  * Delete the item in the playlist with position i_pos.
192  *****************************************************************************/
193 int playlist_Delete( playlist_t * p_playlist, int i_pos )
194 {
195     vlc_mutex_lock( &p_playlist->object_lock );
196
197     if( i_pos >= 0 && i_pos < p_playlist->i_size )
198     {
199         msg_Dbg( p_playlist, "deleting playlist item « %s »",
200                              p_playlist->pp_items[i_pos]->psz_name );
201
202         free( p_playlist->pp_items[i_pos]->psz_name );
203         /* XXX: what if the item is still in use? */
204         free( p_playlist->pp_items[i_pos] );
205
206         if( i_pos <= p_playlist->i_index )
207         {
208             p_playlist->i_index--;
209         }
210
211         /* Renumber the playlist */
212         REMOVE_ELEM( p_playlist->pp_items,
213                      p_playlist->i_size,
214                      i_pos );
215     }
216
217     vlc_mutex_unlock( &p_playlist->object_lock );
218
219     return 0;
220 }
221
222 /*****************************************************************************
223  * playlist_Command: do a playlist action
224  *****************************************************************************
225  *
226  *****************************************************************************/
227 void playlist_Command( playlist_t * p_playlist, int i_command, int i_arg )
228 {
229     vlc_mutex_lock( &p_playlist->object_lock );
230
231     switch( i_command )
232     {
233     case PLAYLIST_STOP:
234         p_playlist->i_status = PLAYLIST_STOPPED;
235         if( p_playlist->p_input )
236         {
237             input_StopThread( p_playlist->p_input );
238         }
239         break;
240
241     case PLAYLIST_PLAY:
242         p_playlist->i_status = PLAYLIST_RUNNING;
243         break;
244
245     case PLAYLIST_SKIP:
246         p_playlist->i_status = PLAYLIST_STOPPED;
247         SkipItem( p_playlist, i_arg );
248         if( p_playlist->p_input )
249         {
250             input_StopThread( p_playlist->p_input );
251         }
252         p_playlist->i_status = PLAYLIST_RUNNING;
253         break;
254
255     case PLAYLIST_GOTO:
256         if( i_arg >= 0 && i_arg < p_playlist->i_size )
257         {
258             p_playlist->i_index = i_arg;
259             if( p_playlist->p_input )
260             {
261                 input_StopThread( p_playlist->p_input );
262             }
263             p_playlist->i_status = PLAYLIST_RUNNING;
264         }
265         break;
266
267     default:
268         msg_Err( p_playlist, "unknown playlist command" );
269         break;
270     }
271
272     vlc_mutex_unlock( &p_playlist->object_lock );
273
274     return;
275 }
276
277 /* Following functions are local */
278
279 /*****************************************************************************
280  * RunThread: main playlist thread
281  *****************************************************************************/
282 static void RunThread ( playlist_t *p_playlist )
283 {
284     /* Tell above that we're ready */
285     vlc_thread_ready( p_playlist );
286
287     while( !p_playlist->b_die )
288     {
289         vlc_mutex_lock( &p_playlist->object_lock );
290
291         /* If there is an input, check that it doesn't need to die. */
292         if( p_playlist->p_input )
293         {
294             /* This input is dead. Remove it ! */
295             if( p_playlist->p_input->b_dead )
296             {
297                 input_thread_t *p_input;
298
299                 /* Unlink current input */
300                 p_input = p_playlist->p_input;
301                 p_playlist->p_input = NULL;
302                 vlc_object_detach( p_input );
303
304                 /* Release the playlist lock, because we may get stuck
305                  * in input_DestroyThread() for some time. */
306                 vlc_mutex_unlock( &p_playlist->object_lock );
307
308                 /* Destroy input */
309                 input_DestroyThread( p_input );
310                 vlc_object_destroy( p_input );
311                 continue;
312             }
313             /* This input is dying, let him do */
314             else if( p_playlist->p_input->b_die )
315             {
316                 ;
317             }
318             /* This input has finished, ask him to die ! */
319             else if( p_playlist->p_input->b_error
320                       || p_playlist->p_input->b_eof )
321             {
322                 /* Check for autodeletion */
323                 if( p_playlist->pp_items[p_playlist->i_index]->b_autodeletion )
324                 {
325                     vlc_mutex_unlock( &p_playlist->object_lock );
326                     playlist_Delete( p_playlist, p_playlist->i_index );
327                     vlc_mutex_lock( &p_playlist->object_lock );
328                 }
329
330                 /* Select the next playlist item */
331                 SkipItem( p_playlist, 1 );
332
333                 /* Release the playlist lock, because we may get stuck
334                  * in input_StopThread() for some time. */
335                 vlc_mutex_unlock( &p_playlist->object_lock );
336                 input_StopThread( p_playlist->p_input );
337                 continue;
338             }
339         }
340         else if( p_playlist->i_status != PLAYLIST_STOPPED )
341         {
342             PlayItem( p_playlist );
343         }
344
345         vlc_mutex_unlock( &p_playlist->object_lock );
346
347         msleep( INTF_IDLE_SLEEP );
348     }
349
350     /* If there is an input, kill it */
351     while( 1 )
352     {
353         vlc_mutex_lock( &p_playlist->object_lock );
354
355         if( p_playlist->p_input == NULL )
356         {
357             vlc_mutex_unlock( &p_playlist->object_lock );
358             break;
359         }
360
361         if( p_playlist->p_input->b_dead )
362         {
363             input_thread_t *p_input;
364
365             /* Unlink current input */
366             p_input = p_playlist->p_input;
367             p_playlist->p_input = NULL;
368             vlc_object_detach( p_input );
369             vlc_mutex_unlock( &p_playlist->object_lock );
370
371             /* Destroy input */
372             input_DestroyThread( p_input );
373             vlc_object_destroy( p_input );
374             continue;
375         }
376         else if( p_playlist->p_input->b_die )
377         {
378             /* This input is dying, leave him alone */
379             ;
380         }
381         else if( p_playlist->p_input->b_error || p_playlist->p_input->b_eof )
382         {
383             vlc_mutex_unlock( &p_playlist->object_lock );
384             input_StopThread( p_playlist->p_input );
385             continue;
386         }
387         else
388         {
389             p_playlist->p_input->b_eof = 1;
390         }
391
392         vlc_mutex_unlock( &p_playlist->object_lock );
393
394         msleep( INTF_IDLE_SLEEP );
395     }
396 }
397
398 /*****************************************************************************
399  * SkipItem: go to Xth playlist item
400  *****************************************************************************
401  * This function calculates the position of the next playlist item, depending
402  * on the playlist course mode (forward, backward, random...).
403  *****************************************************************************/
404 static void SkipItem( playlist_t *p_playlist, int i_arg )
405 {
406     int i_oldindex = p_playlist->i_index;
407     vlc_bool_t b_random;
408
409     /* If the playlist is empty, there is no current item */
410     if( p_playlist->i_size == 0 )
411     {
412         p_playlist->i_index = -1;
413         return;
414     }
415
416     b_random = config_GetInt( p_playlist, "random" );
417
418     /* Increment */
419     if( b_random )
420     {
421         srand( (unsigned int)mdate() );
422
423         /* Simple random stuff - we cheat a bit to minimize the chances to
424          * get the same index again. */
425         i_arg = (int)((float)p_playlist->i_size * rand() / (RAND_MAX+1.0));
426         if( i_arg == 0 )
427         {
428             i_arg = (int)((float)p_playlist->i_size * rand() / (RAND_MAX+1.0));
429         }
430     }
431
432     p_playlist->i_index += i_arg;
433
434     /* Boundary check */
435     if( p_playlist->i_index >= p_playlist->i_size )
436     {
437         if( p_playlist->i_status == PLAYLIST_STOPPED
438              || b_random
439              || config_GetInt( p_playlist, "loop" ) )
440         {
441             p_playlist->i_index -= p_playlist->i_size
442                          * ( p_playlist->i_index / p_playlist->i_size );
443         }
444         else
445         {
446             /* Don't loop by default: stop at playlist end */
447             p_playlist->i_index = i_oldindex;
448             p_playlist->i_status = PLAYLIST_STOPPED;
449         }
450     }
451     else if( p_playlist->i_index < 0 )
452     {
453         p_playlist->i_index = p_playlist->i_size - 1;
454     }
455 }
456
457 /*****************************************************************************
458  * PlayItem: play current playlist item
459  *****************************************************************************
460  * This function calculates the position of the next playlist item, depending
461  * on the playlist course mode (forward, backward, random...).
462  *****************************************************************************/
463 static void PlayItem( playlist_t *p_playlist )
464 {
465     if( p_playlist->i_index == -1 )
466     {
467         if( p_playlist->i_size == 0 )
468         {
469             return;
470         }
471
472         SkipItem( p_playlist, 1 );
473     }
474
475     msg_Dbg( p_playlist, "creating new input thread" );
476     p_playlist->p_input = input_CreateThread( p_playlist,
477                 p_playlist->pp_items[p_playlist->i_index], NULL );
478 }
479
480 /*****************************************************************************
481  * Poubellize: put an input thread in the trashcan
482  *****************************************************************************
483  * XXX: unused
484  *****************************************************************************/
485 static void Poubellize ( playlist_t *p_playlist, input_thread_t *p_input )
486 {
487     
488 }
489