]> git.sesse.net Git - vlc/blob - src/playlist/playlist.c
* adpcm.c: add adpcm decoding support (Now just ms and ima adpcm from
[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.24 2002/12/03 16:29:04 gitan 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
109     vlc_mutex_lock( &p_playlist->object_lock );
110
111     /*
112      * CHECK_INSERT : checks if the item is already enqued before 
113      * enqueing it
114      */
115     if ( i_mode & PLAYLIST_CHECK_INSERT )
116     {
117          int j;
118        
119          if ( p_playlist->pp_items )
120          {
121              for ( j = 0; j < p_playlist->i_size; j++ )
122              {
123                  if ( !strcmp( p_playlist->pp_items[j]->psz_name, psz_target ) )
124                  {
125                       msg_Dbg( p_playlist, "item « %s » already enqued", 
126                                         psz_target );
127                       vlc_mutex_unlock( &p_playlist->object_lock );
128                       return 0;   
129                  }
130              }
131          }
132          i_mode &= ~PLAYLIST_CHECK_INSERT;
133          i_mode |= PLAYLIST_APPEND;
134     }
135
136     
137     msg_Dbg( p_playlist, "adding playlist item « %s »", psz_target );
138     
139     /* Create the new playlist item */
140     p_item = malloc( sizeof( playlist_item_t ) );
141     if( p_item == NULL )
142     {
143         msg_Err( p_playlist, "out of memory" );
144     }
145
146     p_item->psz_name = strdup( psz_target );
147     p_item->i_type = 0;
148     p_item->i_status = 0;
149     p_item->b_autodeletion = VLC_FALSE;
150
151
152     /* Do a few boundary checks and allocate space for the item */
153     if( i_pos == PLAYLIST_END )
154     {
155         if( i_mode & PLAYLIST_INSERT )
156         {
157             i_mode &= ~PLAYLIST_INSERT;
158             i_mode |= PLAYLIST_APPEND;
159         }
160
161         i_pos = p_playlist->i_size - 1;
162     }
163
164     if( !(i_mode & PLAYLIST_REPLACE)
165          || i_pos < 0 || i_pos >= p_playlist->i_size )
166     {
167         /* Additional boundary checks */
168         if( i_mode & PLAYLIST_APPEND )
169         {
170             i_pos++;
171         }
172
173         if( i_pos < 0 )
174         {
175             i_pos = 0;
176         }
177         else if( i_pos > p_playlist->i_size )
178         {
179             i_pos = p_playlist->i_size;
180         }
181
182         INSERT_ELEM( p_playlist->pp_items,
183                      p_playlist->i_size,
184                      i_pos,
185                      p_item );
186
187         if( p_playlist->i_index >= i_pos )
188         {
189             p_playlist->i_index++;
190         }
191     }
192     else
193     {
194         /* i_mode == PLAYLIST_REPLACE and 0 <= i_pos < p_playlist->i_size */
195         free( p_playlist->pp_items[i_pos]->psz_name );
196         /* XXX: what if the item is still in use? */
197         free( p_playlist->pp_items[i_pos] );
198         p_playlist->pp_items[i_pos] = p_item;
199     }
200
201     if( i_mode & PLAYLIST_GO )
202     {
203         p_playlist->i_index = i_pos;
204         if( p_playlist->p_input )
205         {
206             input_StopThread( p_playlist->p_input );
207         }
208         p_playlist->i_status = PLAYLIST_RUNNING;
209     }
210
211     vlc_mutex_unlock( &p_playlist->object_lock );
212
213     return 0;
214 }
215
216 /*****************************************************************************
217  * playlist_Delete: delete an item from the playlist
218  *****************************************************************************
219  * Delete the item in the playlist with position i_pos.
220  *****************************************************************************/
221 int playlist_Delete( playlist_t * p_playlist, int i_pos )
222 {
223     vlc_mutex_lock( &p_playlist->object_lock );
224
225     if( i_pos >= 0 && i_pos < p_playlist->i_size )
226     {
227         msg_Dbg( p_playlist, "deleting playlist item « %s »",
228                              p_playlist->pp_items[i_pos]->psz_name );
229
230         free( p_playlist->pp_items[i_pos]->psz_name );
231         /* XXX: what if the item is still in use? */
232         free( p_playlist->pp_items[i_pos] );
233
234         if( i_pos <= p_playlist->i_index )
235         {
236             p_playlist->i_index--;
237         }
238
239         /* Renumber the playlist */
240         REMOVE_ELEM( p_playlist->pp_items,
241                      p_playlist->i_size,
242                      i_pos );
243     }
244
245     vlc_mutex_unlock( &p_playlist->object_lock );
246
247     return 0;
248 }
249
250 /*****************************************************************************
251  * playlist_Command: do a playlist action
252  *****************************************************************************
253  *
254  *****************************************************************************/
255 void playlist_Command( playlist_t * p_playlist, int i_command, int i_arg )
256 {
257     vlc_mutex_lock( &p_playlist->object_lock );
258
259     switch( i_command )
260     {
261     case PLAYLIST_STOP:
262         p_playlist->i_status = PLAYLIST_STOPPED;
263         if( p_playlist->p_input )
264         {
265             input_StopThread( p_playlist->p_input );
266         }
267         break;
268
269     case PLAYLIST_PLAY:
270         p_playlist->i_status = PLAYLIST_RUNNING;
271         break;
272
273     case PLAYLIST_SKIP:
274         p_playlist->i_status = PLAYLIST_STOPPED;
275         SkipItem( p_playlist, i_arg );
276         if( p_playlist->p_input )
277         {
278             input_StopThread( p_playlist->p_input );
279         }
280         p_playlist->i_status = PLAYLIST_RUNNING;
281         break;
282
283     case PLAYLIST_GOTO:
284         if( i_arg >= 0 && i_arg < p_playlist->i_size )
285         {
286             p_playlist->i_index = i_arg;
287             if( p_playlist->p_input )
288             {
289                 input_StopThread( p_playlist->p_input );
290             }
291             p_playlist->i_status = PLAYLIST_RUNNING;
292         }
293         break;
294
295     default:
296         msg_Err( p_playlist, "unknown playlist command" );
297         break;
298     }
299
300     vlc_mutex_unlock( &p_playlist->object_lock );
301
302     return;
303 }
304
305 /* Following functions are local */
306
307 /*****************************************************************************
308  * RunThread: main playlist thread
309  *****************************************************************************/
310 static void RunThread ( playlist_t *p_playlist )
311 {
312     /* Tell above that we're ready */
313     vlc_thread_ready( p_playlist );
314
315     while( !p_playlist->b_die )
316     {
317         vlc_mutex_lock( &p_playlist->object_lock );
318
319         /* If there is an input, check that it doesn't need to die. */
320         if( p_playlist->p_input )
321         {
322             /* This input is dead. Remove it ! */
323             if( p_playlist->p_input->b_dead )
324             {
325                 input_thread_t *p_input;
326
327                 /* Unlink current input */
328                 p_input = p_playlist->p_input;
329                 p_playlist->p_input = NULL;
330                 vlc_object_detach( p_input );
331
332                 /* Release the playlist lock, because we may get stuck
333                  * in input_DestroyThread() for some time. */
334                 vlc_mutex_unlock( &p_playlist->object_lock );
335
336                 /* Destroy input */
337                 input_DestroyThread( p_input );
338                 vlc_object_destroy( p_input );
339                 continue;
340             }
341             /* This input is dying, let him do */
342             else if( p_playlist->p_input->b_die )
343             {
344                 ;
345             }
346             /* This input has finished, ask him to die ! */
347             else if( p_playlist->p_input->b_error
348                       || p_playlist->p_input->b_eof )
349             {
350                 /* Check for autodeletion */
351                 if( p_playlist->pp_items[p_playlist->i_index]->b_autodeletion )
352                 {
353                     vlc_mutex_unlock( &p_playlist->object_lock );
354                     playlist_Delete( p_playlist, p_playlist->i_index );
355                     vlc_mutex_lock( &p_playlist->object_lock );
356                 }
357
358                 /* Select the next playlist item */
359                 SkipItem( p_playlist, 1 );
360
361                 /* Release the playlist lock, because we may get stuck
362                  * in input_StopThread() for some time. */
363                 vlc_mutex_unlock( &p_playlist->object_lock );
364                 input_StopThread( p_playlist->p_input );
365                 continue;
366             }
367         }
368         else if( p_playlist->i_status != PLAYLIST_STOPPED )
369         {
370             PlayItem( p_playlist );
371         }
372
373         vlc_mutex_unlock( &p_playlist->object_lock );
374
375         msleep( INTF_IDLE_SLEEP );
376     }
377
378     /* If there is an input, kill it */
379     while( 1 )
380     {
381         vlc_mutex_lock( &p_playlist->object_lock );
382
383         if( p_playlist->p_input == NULL )
384         {
385             vlc_mutex_unlock( &p_playlist->object_lock );
386             break;
387         }
388
389         if( p_playlist->p_input->b_dead )
390         {
391             input_thread_t *p_input;
392
393             /* Unlink current input */
394             p_input = p_playlist->p_input;
395             p_playlist->p_input = NULL;
396             vlc_object_detach( p_input );
397             vlc_mutex_unlock( &p_playlist->object_lock );
398
399             /* Destroy input */
400             input_DestroyThread( p_input );
401             vlc_object_destroy( p_input );
402             continue;
403         }
404         else if( p_playlist->p_input->b_die )
405         {
406             /* This input is dying, leave him alone */
407             ;
408         }
409         else if( p_playlist->p_input->b_error || p_playlist->p_input->b_eof )
410         {
411             vlc_mutex_unlock( &p_playlist->object_lock );
412             input_StopThread( p_playlist->p_input );
413             continue;
414         }
415         else
416         {
417             p_playlist->p_input->b_eof = 1;
418         }
419
420         vlc_mutex_unlock( &p_playlist->object_lock );
421
422         msleep( INTF_IDLE_SLEEP );
423     }
424 }
425
426 /*****************************************************************************
427  * SkipItem: go to Xth playlist item
428  *****************************************************************************
429  * This function calculates the position of the next playlist item, depending
430  * on the playlist course mode (forward, backward, random...).
431  *****************************************************************************/
432 static void SkipItem( playlist_t *p_playlist, int i_arg )
433 {
434     int i_oldindex = p_playlist->i_index;
435     vlc_bool_t b_random;
436
437     /* If the playlist is empty, there is no current item */
438     if( p_playlist->i_size == 0 )
439     {
440         p_playlist->i_index = -1;
441         return;
442     }
443
444     b_random = config_GetInt( p_playlist, "random" );
445
446     /* Increment */
447     if( b_random )
448     {
449         srand( (unsigned int)mdate() );
450
451         /* Simple random stuff - we cheat a bit to minimize the chances to
452          * get the same index again. */
453         i_arg = (int)((float)p_playlist->i_size * rand() / (RAND_MAX+1.0));
454         if( i_arg == 0 )
455         {
456             i_arg = (int)((float)p_playlist->i_size * rand() / (RAND_MAX+1.0));
457         }
458     }
459
460     p_playlist->i_index += i_arg;
461
462     /* Boundary check */
463     if( p_playlist->i_index >= p_playlist->i_size )
464     {
465         if( p_playlist->i_status == PLAYLIST_STOPPED
466              || b_random
467              || config_GetInt( p_playlist, "loop" ) )
468         {
469             p_playlist->i_index -= p_playlist->i_size
470                          * ( p_playlist->i_index / p_playlist->i_size );
471         }
472         else
473         {
474             /* Don't loop by default: stop at playlist end */
475             p_playlist->i_index = i_oldindex;
476             p_playlist->i_status = PLAYLIST_STOPPED;
477         }
478     }
479     else if( p_playlist->i_index < 0 )
480     {
481         p_playlist->i_index = p_playlist->i_size - 1;
482     }
483 }
484
485 /*****************************************************************************
486  * PlayItem: play current playlist item
487  *****************************************************************************
488  * This function calculates the position of the next playlist item, depending
489  * on the playlist course mode (forward, backward, random...).
490  *****************************************************************************/
491 static void PlayItem( playlist_t *p_playlist )
492 {
493     if( p_playlist->i_index == -1 )
494     {
495         if( p_playlist->i_size == 0 )
496         {
497             return;
498         }
499
500         SkipItem( p_playlist, 1 );
501     }
502
503     msg_Dbg( p_playlist, "creating new input thread" );
504     p_playlist->p_input = input_CreateThread( p_playlist,
505                 p_playlist->pp_items[p_playlist->i_index], NULL );
506 }
507
508 /*****************************************************************************
509  * Poubellize: put an input thread in the trashcan
510  *****************************************************************************
511  * XXX: unused
512  *****************************************************************************/
513 static void Poubellize ( playlist_t *p_playlist, input_thread_t *p_input )
514 {
515     
516 }
517