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