playlist.c 33.8 KB
Newer Older
1
2
3
/*****************************************************************************
 * playlist.c : Playlist management functions
 *****************************************************************************
4
 * Copyright (C) 1999-2004 VideoLAN
Jean-Paul Saman's avatar
Jean-Paul Saman committed
5
 * $Id$
6
7
 *
 * Authors: Samuel Hocevar <sam@zoy.org>
zorglub's avatar
zorglub committed
8
 *          Clment Stenac <zorglub@videolan.org>
9
10
11
12
13
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
14
 *
15
16
17
18
19
20
21
22
23
24
25
26
27
28
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
 *****************************************************************************/
#include <stdlib.h>                                      /* free(), strtol() */
#include <stdio.h>                                              /* sprintf() */
#include <string.h>                                            /* strerror() */

#include <vlc/vlc.h>
29
30
#include <vlc/vout.h>
#include <vlc/sout.h>
Sam Hocevar's avatar
Sam Hocevar committed
31
#include <vlc/input.h>
32

33
#include "vlc_playlist.h"
34

zorglub's avatar
zorglub committed
35
36
37
38
#define TITLE_CATEGORY N_( "By category" )
#define TITLE_SIMPLE   N_( "Manually added" )
#define TITLE_ALL      N_( "All items, unsorted" )

zorglub's avatar
zorglub committed
39
#undef PLAYLIST_PROFILE
zorglub's avatar
zorglub committed
40
#undef PLAYLIST_DEBUG
41

42
43
44
45
/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
static void RunThread ( playlist_t * );
46
static void RunPreparse( playlist_preparse_t * );
zorglub's avatar
zorglub committed
47
static playlist_item_t * NextItem  ( playlist_t * );
zorglub's avatar
zorglub committed
48
static int PlayItem  ( playlist_t *, playlist_item_t * );
zorglub's avatar
zorglub committed
49
50
51
52
53
54

static int ItemChange( vlc_object_t *, const char *,
                       vlc_value_t, vlc_value_t, void * );

int playlist_vaControl( playlist_t * p_playlist, int i_query, va_list args );

55

sigmunau's avatar
sigmunau committed
56
57
58
/**
 * Create playlist
 *
59
 * Create a playlist structure.
sigmunau's avatar
sigmunau committed
60
61
62
 * \param p_parent the vlc object that is to be the parent of this playlist
 * \return a pointer to the created playlist, or NULL on error
 */
63
playlist_t * __playlist_Create ( vlc_object_t *p_parent )
64
65
{
    playlist_t *p_playlist;
zorglub's avatar
zorglub committed
66
    playlist_view_t *p_view;
hartman's avatar
hartman committed
67
    vlc_value_t     val;
68
69
70
71
72
73
74
75
76

    /* Allocate structure */
    p_playlist = vlc_object_create( p_parent, VLC_OBJECT_PLAYLIST );
    if( !p_playlist )
    {
        msg_Err( p_parent, "out of memory" );
        return NULL;
    }

zorglub's avatar
zorglub committed
77
    /* These variables control updates */
hartman's avatar
hartman committed
78
79
80
81
    var_Create( p_playlist, "intf-change", VLC_VAR_BOOL );
    val.b_bool = VLC_TRUE;
    var_Set( p_playlist, "intf-change", val );

82
83
84
85
    var_Create( p_playlist, "item-change", VLC_VAR_INTEGER );
    val.i_int = -1;
    var_Set( p_playlist, "item-change", val );

86
87
88
89
    var_Create( p_playlist, "item-deleted", VLC_VAR_INTEGER );
    val.i_int = -1;
    var_Set( p_playlist, "item-deleted", val );

90
91
    var_Create( p_playlist, "item-append", VLC_VAR_ADDRESS );

92
93
94
95
    var_Create( p_playlist, "playlist-current", VLC_VAR_INTEGER );
    val.i_int = -1;
    var_Set( p_playlist, "playlist-current", val );

96
    var_Create( p_playlist, "intf-popupmenu", VLC_VAR_BOOL );
gbazin's avatar
   
gbazin committed
97

98
99
100
    var_Create( p_playlist, "intf-show", VLC_VAR_BOOL );
    val.b_bool = VLC_TRUE;
    var_Set( p_playlist, "intf-show", val );
101

102

zorglub's avatar
zorglub committed
103
104
105
106
107
    /* Variables to control playback */
    var_CreateGetBool( p_playlist, "play-and-stop" );
    var_CreateGetBool( p_playlist, "random" );
    var_CreateGetBool( p_playlist, "repeat" );
    var_CreateGetBool( p_playlist, "loop" );
108

zorglub's avatar
zorglub committed
109
    /* Initialise data structures */
110
    p_playlist->i_last_id = 0;
zorglub's avatar
zorglub committed
111
112
    p_playlist->b_go_next = VLC_TRUE;
    p_playlist->p_input = NULL;
113

zorglub's avatar
zorglub committed
114
115
116
117
    p_playlist->request_date = 0;

    p_playlist->i_views = 0;
    p_playlist->pp_views = NULL;
118

119
120
121
    p_playlist->i_index = -1;
    p_playlist->i_size = 0;
    p_playlist->pp_items = NULL;
122
123
    p_playlist->i_all_size = 0;
    p_playlist->pp_all_items = malloc(sizeof(playlist_item_t*));
Sam Hocevar's avatar
Sam Hocevar committed
124

zorglub's avatar
zorglub committed
125
126
127
128
129
130
131
132
    playlist_ViewInsert( p_playlist, VIEW_CATEGORY, TITLE_CATEGORY );
    playlist_ViewInsert( p_playlist, VIEW_SIMPLE, TITLE_SIMPLE );
    playlist_ViewInsert( p_playlist, VIEW_ALL, TITLE_ALL );

    p_view = playlist_ViewFind( p_playlist, VIEW_CATEGORY );

    p_playlist->p_general = playlist_NodeCreate( p_playlist, VIEW_CATEGORY,
                                        _( "General" ), p_view->p_root );
133
    p_playlist->p_general->i_flags |= PLAYLIST_RO_FLAG;
zorglub's avatar
zorglub committed
134
135
136
137
138
139
140
141
142
143
144
145

    /* Set startup status
     * We set to simple view on startup for interfaces that don't do
     * anything */
    p_view = playlist_ViewFind( p_playlist, VIEW_SIMPLE );
    p_playlist->status.i_view = VIEW_SIMPLE;
    p_playlist->status.p_item = NULL;
    p_playlist->status.p_node = p_view->p_root;
    p_playlist->request.b_request = VLC_FALSE;
    p_playlist->status.i_status = PLAYLIST_STOPPED;


hartman's avatar
hartman committed
146
147
    p_playlist->i_sort = SORT_ID;
    p_playlist->i_order = ORDER_NORMAL;
zorglub's avatar
zorglub committed
148

zorglub's avatar
zorglub committed
149
    /* Finally, launch the thread ! */
150
151
    if( vlc_thread_create( p_playlist, "playlist", RunThread,
                           VLC_THREAD_PRIORITY_LOW, VLC_TRUE ) )
152
153
154
155
156
157
    {
        msg_Err( p_playlist, "cannot spawn playlist thread" );
        vlc_object_destroy( p_playlist );
        return NULL;
    }

158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
    /* Preparsing stuff */
    p_playlist->p_preparse = vlc_object_create( p_playlist,
                                                sizeof( playlist_preparse_t ) );
    if( !p_playlist->p_preparse )
    {
        msg_Err( p_playlist, "unable to create preparser" );
        vlc_object_destroy( p_playlist );
        return NULL;
    }

    p_playlist->p_preparse->i_waiting = 0;
    p_playlist->p_preparse->pp_waiting = NULL;

    vlc_object_attach( p_playlist->p_preparse, p_playlist );
    if( vlc_thread_create( p_playlist->p_preparse, "preparser",
                           RunPreparse, VLC_THREAD_PRIORITY_LOW, VLC_TRUE ) )
    {
        msg_Err( p_playlist, "cannot spawn preparse thread" );
        vlc_object_detach( p_playlist->p_preparse );
        vlc_object_destroy( p_playlist->p_preparse );
        return NULL;
    }

181
182
183
    /* The object has been initialized, now attach it */
    vlc_object_attach( p_playlist, p_parent );

184
185
186
    return p_playlist;
}

sigmunau's avatar
sigmunau committed
187
188
189
/**
 * Destroy the playlist.
 *
190
 * Delete all items in the playlist and free the playlist structure.
sigmunau's avatar
sigmunau committed
191
 * \param p_playlist the playlist structure to destroy
zorglub's avatar
zorglub committed
192
 * \return VLC_SUCCESS or an error
sigmunau's avatar
sigmunau committed
193
 */
zorglub's avatar
zorglub committed
194
int playlist_Destroy( playlist_t * p_playlist )
195
{
zorglub's avatar
zorglub committed
196
    int i;
197
198
    p_playlist->b_die = 1;

zorglub's avatar
zorglub committed
199
200
201
202
203
204
    for( i = 0 ; i< p_playlist->i_sds ; i++ )
    {
        playlist_ServicesDiscoveryRemove( p_playlist,
                                          p_playlist->pp_sds[i]->psz_module );
    }

205
    vlc_thread_join( p_playlist->p_preparse );
206
207
    vlc_thread_join( p_playlist );

208
209
    vlc_object_detach( p_playlist->p_preparse );

hartman's avatar
hartman committed
210
    var_Destroy( p_playlist, "intf-change" );
211
    var_Destroy( p_playlist, "item-change" );
212
213
214
    var_Destroy( p_playlist, "playlist-current" );
    var_Destroy( p_playlist, "intf-popmenu" );
    var_Destroy( p_playlist, "intf-show" );
hartman's avatar
hartman committed
215
    var_Destroy( p_playlist, "play-and-stop" );
216
217
218
    var_Destroy( p_playlist, "random" );
    var_Destroy( p_playlist, "repeat" );
    var_Destroy( p_playlist, "loop" );
hartman's avatar
hartman committed
219

zorglub's avatar
zorglub committed
220
    playlist_Clear( p_playlist );
221

zorglub's avatar
zorglub committed
222
    for( i = p_playlist->i_views - 1; i >= 0 ; i-- )
223
    {
zorglub's avatar
zorglub committed
224
225
226
227
228
229
        playlist_view_t *p_view = p_playlist->pp_views[i];
        if( p_view->psz_name )
            free( p_view->psz_name );
        playlist_ItemDelete( p_view->p_root );
        REMOVE_ELEM( p_playlist->pp_views, p_playlist->i_views, i );
        free( p_view );
230
231
    }

232
    vlc_object_destroy( p_playlist->p_preparse );
233
    vlc_object_destroy( p_playlist );
zorglub's avatar
zorglub committed
234
235

    return VLC_SUCCESS;
236
237
238
}


sigmunau's avatar
sigmunau committed
239
/**
240
 * Do a playlist action.
241
 *
242
 * If there is something in the playlist then you can do playlist actions.
zorglub's avatar
zorglub committed
243
244
245
 *
 * Playlist lock must not be taken when calling this function
 *
sigmunau's avatar
sigmunau committed
246
 * \param p_playlist the playlist to do the command on
zorglub's avatar
zorglub committed
247
248
249
 * \param i_query the command to do
 * \param variable number of arguments
 * \return VLC_SUCCESS or an error
sigmunau's avatar
sigmunau committed
250
 */
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
int playlist_LockControl( playlist_t * p_playlist, int i_query, ... )
{
    va_list args;
    int i_result;
    va_start( args, i_query );
    vlc_mutex_lock( &p_playlist->object_lock );
    i_result = playlist_vaControl( p_playlist, i_query, args );
    va_end( args );
    vlc_mutex_unlock( &p_playlist->object_lock );
    return i_result;
    
}

/**
 * Do a playlist action.
 *
 * If there is something in the playlist then you can do playlist actions.
 *
 * Playlist lock must be taken when calling this function
 *
 * \param p_playlist the playlist to do the command on
 * \param i_query the command to do
 * \param variable number of arguments
 * \return VLC_SUCCESS or an error
 */
zorglub's avatar
zorglub committed
276
int playlist_Control( playlist_t * p_playlist, int i_query, ... )
277
{
zorglub's avatar
zorglub committed
278
279
280
281
282
283
284
285
    va_list args;
    int i_result;
    va_start( args, i_query );
    i_result = playlist_vaControl( p_playlist, i_query, args );
    va_end( args );

    return i_result;
}
286

zorglub's avatar
zorglub committed
287
288
289
290
291
292
293
294
295
int playlist_vaControl( playlist_t * p_playlist, int i_query, va_list args )
{
    playlist_view_t *p_view;
    vlc_value_t val;

#ifdef PLAYLIST_PROFILE
    p_playlist->request_date = mdate();
#endif

296
297
    if( p_playlist->i_size <= 0 )
    {
zorglub's avatar
zorglub committed
298
        return VLC_EGENERIC;
299
300
    }

zorglub's avatar
zorglub committed
301
    switch( i_query )
302
303
    {
    case PLAYLIST_STOP:
zorglub's avatar
zorglub committed
304
305
306
307
308
309
310
311
312
313
314
315
        p_playlist->status.i_status = PLAYLIST_STOPPED;
        p_playlist->request.b_request = VLC_TRUE;
        break;

    case PLAYLIST_ITEMPLAY:
        p_playlist->status.i_status = PLAYLIST_RUNNING;
        p_playlist->request.i_skip = 0;
        p_playlist->request.b_request = VLC_TRUE;
        p_playlist->request.p_item = (playlist_item_t *)va_arg( args,
                                                   playlist_item_t *);
        p_playlist->request.i_view = p_playlist->status.i_view;
        p_view = playlist_ViewFind( p_playlist, p_playlist->status.i_view );
zorglub's avatar
zorglub committed
316
317
318
319
320
321
322
323
        if( p_view )
        {
            p_playlist->request.p_node = p_view->p_root;
        }
        else
        {
            p_playlist->request.p_node = NULL;
        }
zorglub's avatar
zorglub committed
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
        break;

    case PLAYLIST_VIEWPLAY:
        p_playlist->status.i_status = PLAYLIST_RUNNING;
        p_playlist->request.i_skip = 0;
        p_playlist->request.b_request = VLC_TRUE;
        p_playlist->request.i_view = (int)va_arg( args,int );
        p_playlist->request.p_node = (playlist_item_t *)va_arg( args,
                                                        playlist_item_t *);
        p_playlist->request.p_item = (playlist_item_t *)va_arg( args,
                                                        playlist_item_t *);

        /* If we select a node, play only it.
         * If we select an item, continue */
        if( p_playlist->request.p_item == NULL ||
            ! p_playlist->request.p_node->i_flags & PLAYLIST_SKIP_FLAG )
340
        {
zorglub's avatar
zorglub committed
341
342
343
344
345
            p_playlist->b_go_next = VLC_FALSE;
        }
        else
        {
            p_playlist->b_go_next = VLC_TRUE;
346
        }
347
        break;
348

349
    case PLAYLIST_PLAY:
zorglub's avatar
zorglub committed
350
351
        p_playlist->status.i_status = PLAYLIST_RUNNING;

gbazin's avatar
   
gbazin committed
352
353
        if( p_playlist->p_input )
        {
354
355
            val.i_int = PLAYING_S;
            var_Set( p_playlist->p_input, "state", val );
zorglub's avatar
zorglub committed
356
            break;
357
        }
zorglub's avatar
zorglub committed
358
359
360
361
362
363
364
365

        /* FIXME : needed ? */
        p_playlist->request.b_request = VLC_TRUE;
        p_playlist->request.i_view = p_playlist->status.i_view;
        p_playlist->request.p_node = p_playlist->status.p_node;
        p_playlist->request.p_item = p_playlist->status.p_item;
        p_playlist->request.i_skip = 0;
        p_playlist->request.i_goto = -1;
366
        break;
367

368
369
370
371
372
373
    case PLAYLIST_AUTOPLAY:
        p_playlist->status.i_status = PLAYLIST_RUNNING;

        p_playlist->request.b_request = VLC_FALSE;
        break;

374
    case PLAYLIST_PAUSE:
hartman's avatar
hartman committed
375
        val.i_int = 0;
376
        if( p_playlist->p_input )
hartman's avatar
hartman committed
377
378
379
            var_Get( p_playlist->p_input, "state", &val );

        if( val.i_int == PAUSE_S )
380
        {
zorglub's avatar
zorglub committed
381
            p_playlist->status.i_status = PLAYLIST_RUNNING;
hartman's avatar
hartman committed
382
383
384
385
386
387
388
389
            if( p_playlist->p_input )
            {
                val.i_int = PLAYING_S;
                var_Set( p_playlist->p_input, "state", val );
            }
        }
        else
        {
zorglub's avatar
zorglub committed
390
            p_playlist->status.i_status = PLAYLIST_PAUSED;
hartman's avatar
hartman committed
391
392
393
394
395
            if( p_playlist->p_input )
            {
                val.i_int = PAUSE_S;
                var_Set( p_playlist->p_input, "state", val );
            }
396
397
398
        }
        break;

399
    case PLAYLIST_SKIP:
zorglub's avatar
zorglub committed
400
        if( p_playlist->status.i_view > -1 )
401
        {
zorglub's avatar
zorglub committed
402
403
            p_playlist->request.p_node = p_playlist->status.p_node;
            p_playlist->request.p_item = p_playlist->status.p_item;
404
        }
zorglub's avatar
zorglub committed
405
406
        p_playlist->request.i_skip = (int) va_arg( args, int );
        p_playlist->request.b_request = VLC_TRUE;
407
408
409
        break;

    case PLAYLIST_GOTO:
zorglub's avatar
zorglub committed
410
411
412
413
414
415
        p_playlist->status.i_status = PLAYLIST_RUNNING;
        p_playlist->request.p_node = NULL;
        p_playlist->request.p_item = NULL;
        p_playlist->request.i_view = -1;
        p_playlist->request.i_goto = (int) va_arg( args, int );
        p_playlist->request.b_request = VLC_TRUE;
416
        break;
417

418
    default:
zorglub's avatar
zorglub committed
419
420
        msg_Err( p_playlist, "unimplemented playlist query" );
        return VLC_EBADVAR;
421
422
423
        break;
    }

zorglub's avatar
zorglub committed
424
    return VLC_SUCCESS;
425
}
426

427
428
429
430
431
432
433
434
435
436
437
438
int playlist_PreparseEnqueue( playlist_t *p_playlist,
                              input_item_t *p_item )
{
    vlc_mutex_lock( &p_playlist->p_preparse->object_lock );
    INSERT_ELEM( p_playlist->p_preparse->pp_waiting,
                 p_playlist->p_preparse->i_waiting,
                 p_playlist->p_preparse->i_waiting,
                 p_item );
    vlc_mutex_unlock( &p_playlist->p_preparse->object_lock );
    return VLC_SUCCESS;
}

439

zorglub's avatar
zorglub committed
440
/* Destroy remaining objects */
441
442
static mtime_t ObjectGarbageCollector( playlist_t *p_playlist, int i_type,
                                       mtime_t destroy_date )
443
444
445
{
    vlc_object_t *p_obj;

gbazin's avatar
   
gbazin committed
446
    if( destroy_date > mdate() ) return destroy_date;
447
448

    if( destroy_date == 0 )
449
450
    {
        /* give a little time */
451
        return mdate() + I64C(1000000);
452
453
454
    }
    else
    {
455
        while( ( p_obj = vlc_object_find( p_playlist, i_type, FIND_CHILD ) ) )
456
457
458
        {
            if( p_obj->p_parent != (vlc_object_t*)p_playlist )
            {
459
                /* only first child (ie unused) */
460
461
462
463
464
                vlc_object_release( p_obj );
                break;
            }
            if( i_type == VLC_OBJECT_VOUT )
            {
465
                msg_Dbg( p_playlist, "garbage collector destroying 1 vout" );
466
467
468
469
470
471
472
473
474
475
                vlc_object_detach( p_obj );
                vlc_object_release( p_obj );
                vout_Destroy( (vout_thread_t *)p_obj );
            }
            else if( i_type == VLC_OBJECT_SOUT )
            {
                vlc_object_release( p_obj );
                sout_DeleteInstance( (sout_instance_t*)p_obj );
            }
        }
476
        return 0;
477
478
479
    }
}

480
481
482
483
484
/*****************************************************************************
 * RunThread: main playlist thread
 *****************************************************************************/
static void RunThread ( playlist_t *p_playlist )
{
485
    vlc_object_t *p_obj;
zorglub's avatar
zorglub committed
486
    playlist_item_t *p_item;
gbazin's avatar
   
gbazin committed
487

488
489
490
    mtime_t    i_vout_destroyed_date = 0;
    mtime_t    i_sout_destroyed_date = 0;

zorglub's avatar
zorglub committed
491
    playlist_item_t *p_autodelete_item = NULL;
492

493
494
495
    /* Tell above that we're ready */
    vlc_thread_ready( p_playlist );

496
497
    while( !p_playlist->b_die )
    {
498
499
        vlc_mutex_lock( &p_playlist->object_lock );

zorglub's avatar
zorglub committed
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
        /* First, check if we have something to do */
        /* FIXME : this can be called several times */
        if( p_playlist->request.b_request )
        {
#ifdef PLAYLIST_PROFILE
            msg_Dbg(p_playlist, "beginning processing of request, "
                         I64Fi" us ", mdate() - p_playlist->request_date );
#endif
            /* Stop the existing input */
            if( p_playlist->p_input )
            {
                input_StopThread( p_playlist->p_input );
            }
            /* The code below will start the next input for us */
            if( p_playlist->status.i_status == PLAYLIST_STOPPED )
            {
                p_playlist->request.b_request = VLC_FALSE;
            }
        }

520
521
522
        /* If there is an input, check that it doesn't need to die. */
        if( p_playlist->p_input )
        {
Sam Hocevar's avatar
Sam Hocevar committed
523
            /* This input is dead. Remove it ! */
Sam Hocevar's avatar
Sam Hocevar committed
524
            if( p_playlist->p_input->b_dead )
525
526
527
528
529
            {
                input_thread_t *p_input;

                p_input = p_playlist->p_input;
                p_playlist->p_input = NULL;
530
531
532
533

                /* Release the playlist lock, because we may get stuck
                 * in input_DestroyThread() for some time. */
                vlc_mutex_unlock( &p_playlist->object_lock );
534
535
536

                /* Destroy input */
                input_DestroyThread( p_input );
537

gbazin's avatar
   
gbazin committed
538
539
                /* Unlink current input
                 * (_after_ input_DestroyThread for vout garbage collector) */
540
541
542
                vlc_object_detach( p_input );

                /* Destroy object */
Sam Hocevar's avatar
Sam Hocevar committed
543
                vlc_object_destroy( p_input );
544
545
546

                i_vout_destroyed_date = 0;
                i_sout_destroyed_date = 0;
547

548
                continue;
549
            }
Sam Hocevar's avatar
Sam Hocevar committed
550
551
552
553
554
555
            /* This input is dying, let him do */
            else if( p_playlist->p_input->b_die )
            {
                ;
            }
            /* This input has finished, ask him to die ! */
Sam Hocevar's avatar
Sam Hocevar committed
556
557
            else if( p_playlist->p_input->b_error
                      || p_playlist->p_input->b_eof )
558
            {
zorglub's avatar
zorglub committed
559
560
                /* TODO FIXME XXX TODO FIXME XXX */
                /* Check for autodeletion */
561

zorglub's avatar
zorglub committed
562
                if( p_playlist->status.p_item->i_flags & PLAYLIST_DEL_FLAG )
563
                {
zorglub's avatar
zorglub committed
564
565
                    p_autodelete_item = p_playlist->status.p_item;
                }
zorglub's avatar
zorglub committed
566
567
568
569
                input_StopThread( p_playlist->p_input );
                /* Select the next playlist item */
                vlc_mutex_unlock( &p_playlist->object_lock );
                continue;
570
            }
Laurent Aimar's avatar
Laurent Aimar committed
571
            else if( p_playlist->p_input->i_state != INIT_S )
572
            {
573
                vlc_mutex_unlock( &p_playlist->object_lock );
574
575
576
                i_vout_destroyed_date =
                    ObjectGarbageCollector( p_playlist, VLC_OBJECT_VOUT,
                                            i_vout_destroyed_date );
gbazin's avatar
   
gbazin committed
577
                i_sout_destroyed_date =
578
579
                    ObjectGarbageCollector( p_playlist, VLC_OBJECT_SOUT,
                                            i_sout_destroyed_date );
580
                vlc_mutex_lock( &p_playlist->object_lock );
581
            }
582
        }
zorglub's avatar
zorglub committed
583
        else if( p_playlist->status.i_status != PLAYLIST_STOPPED )
584
        {
zorglub's avatar
zorglub committed
585
586
587
588
            /* Start another input.
             * Get the next item to play */
            p_item = NextItem( p_playlist );

zorglub's avatar
zorglub committed
589

zorglub's avatar
zorglub committed
590
591
            /* We must stop */
            if( p_item == NULL )
hartman's avatar
hartman committed
592
            {
zorglub's avatar
zorglub committed
593
594
595
596
597
598
                if( p_autodelete_item )
                {
                    playlist_Delete( p_playlist,
                                     p_autodelete_item->input.i_id );
                    p_autodelete_item = NULL;
                }
zorglub's avatar
zorglub committed
599
600
601
                p_playlist->status.i_status = PLAYLIST_STOPPED;
                vlc_mutex_unlock( &p_playlist->object_lock );
                continue;
hartman's avatar
hartman committed
602
            }
zorglub's avatar
zorglub committed
603
604

            PlayItem( p_playlist, p_item );
zorglub's avatar
zorglub committed
605
606
607
608
609
610

            if( p_autodelete_item )
            {
                playlist_Delete( p_playlist, p_autodelete_item->input.i_id );
                p_autodelete_item = NULL;
            }
611
        }
zorglub's avatar
zorglub committed
612
        else if( p_playlist->status.i_status == PLAYLIST_STOPPED )
613
        {
zorglub's avatar
zorglub committed
614
            /* Collect garbage */
615
            vlc_mutex_unlock( &p_playlist->object_lock );
616
617
618
619
            i_sout_destroyed_date =
                ObjectGarbageCollector( p_playlist, VLC_OBJECT_SOUT, mdate() );
            i_vout_destroyed_date =
                ObjectGarbageCollector( p_playlist, VLC_OBJECT_VOUT, mdate() );
620
            vlc_mutex_lock( &p_playlist->object_lock );
621
        }
622
623
        vlc_mutex_unlock( &p_playlist->object_lock );

zorglub's avatar
zorglub committed
624
625
626
627
628
629
630
631
632
633
634
        msleep( INTF_IDLE_SLEEP / 2 );

        /* Stop sleeping earlier if we have work */
        /* TODO : statistics about this */
        if ( p_playlist->request.b_request &&
                        p_playlist->status.i_status == PLAYLIST_RUNNING )
        {
            continue;
        }

        msleep( INTF_IDLE_SLEEP / 2 );
635
636
    }

zorglub's avatar
zorglub committed
637
638
    /* Playlist dying */

639
    /* If there is an input, kill it */
640
    while( 1 )
641
    {
642
643
644
645
646
647
648
649
        vlc_mutex_lock( &p_playlist->object_lock );

        if( p_playlist->p_input == NULL )
        {
            vlc_mutex_unlock( &p_playlist->object_lock );
            break;
        }

Sam Hocevar's avatar
Sam Hocevar committed
650
        if( p_playlist->p_input->b_dead )
651
652
653
654
655
656
        {
            input_thread_t *p_input;

            /* Unlink current input */
            p_input = p_playlist->p_input;
            p_playlist->p_input = NULL;
657
            vlc_mutex_unlock( &p_playlist->object_lock );
658
659
660

            /* Destroy input */
            input_DestroyThread( p_input );
661
662
663
664
665
            /* Unlink current input (_after_ input_DestroyThread for vout
             * garbage collector)*/
            vlc_object_detach( p_input );

            /* Destroy object */
Sam Hocevar's avatar
Sam Hocevar committed
666
            vlc_object_destroy( p_input );
667
            continue;
668
        }
Sam Hocevar's avatar
Sam Hocevar committed
669
670
        else if( p_playlist->p_input->b_die )
        {
671
            /* This input is dying, leave him alone */
Sam Hocevar's avatar
Sam Hocevar committed
672
673
            ;
        }
Sam Hocevar's avatar
Sam Hocevar committed
674
        else if( p_playlist->p_input->b_error || p_playlist->p_input->b_eof )
675
        {
Sam Hocevar's avatar
Sam Hocevar committed
676
            input_StopThread( p_playlist->p_input );
677
            vlc_mutex_unlock( &p_playlist->object_lock );
678
            continue;
679
680
681
682
683
684
        }
        else
        {
            p_playlist->p_input->b_eof = 1;
        }

685
686
        vlc_mutex_unlock( &p_playlist->object_lock );

687
688
        msleep( INTF_IDLE_SLEEP );
    }
689

690
    /* close all remaining sout */
691
    while( ( p_obj = vlc_object_find( p_playlist,
692
                                      VLC_OBJECT_SOUT, FIND_CHILD ) ) )
693
694
    {
        vlc_object_release( p_obj );
695
        sout_DeleteInstance( (sout_instance_t*)p_obj );
696
    }
697
698

    /* close all remaining vout */
699
    while( ( p_obj = vlc_object_find( p_playlist,
700
                                      VLC_OBJECT_VOUT, FIND_CHILD ) ) )
701
    {
702
        vlc_object_detach( p_obj );
703
        vlc_object_release( p_obj );
704
        vout_Destroy( (vout_thread_t *)p_obj );
705
    }
706
707
}

708
709
710
/* Queue for items to preparse */
static void RunPreparse ( playlist_preparse_t *p_obj )
{
711
    playlist_t *p_playlist = (playlist_t *)p_obj->p_parent;
712
713
714
715
716
717
718
719
720
721
722
    vlc_bool_t b_sleep;

    /* Tell above that we're ready */
    vlc_thread_ready( p_obj );

    while( !p_playlist->b_die )
    {
        vlc_mutex_lock( &p_obj->object_lock );

        if( p_obj->i_waiting > 0 )
        {
723
            input_item_t *p_current = p_obj->pp_waiting[0];
724
            REMOVE_ELEM( p_obj->pp_waiting, p_obj->i_waiting, 0 );
725
726
727
728
            vlc_mutex_unlock( &p_obj->object_lock );
            input_Preparse( p_playlist, p_current );
            var_SetInteger( p_playlist, "item-change", p_current->i_id );
            vlc_mutex_lock( &p_obj->object_lock );
729
730
731
732
733
734
735
736
737
738
739
740
        }
        b_sleep = ( p_obj->i_waiting == 0 );

        vlc_mutex_unlock( &p_obj->object_lock );

        if( p_obj->i_waiting == 0 )
        {
            msleep( INTF_IDLE_SLEEP );
        }
    }
}

741
/*****************************************************************************
zorglub's avatar
zorglub committed
742
 * NextItem
743
 *****************************************************************************
zorglub's avatar
zorglub committed
744
745
 * This function calculates the next playlist item, depending
 * on the playlist course mode (forward, backward, random, view,...).
746
 *****************************************************************************/
zorglub's avatar
zorglub committed
747
static playlist_item_t * NextItem( playlist_t *p_playlist )
748
{
zorglub's avatar
zorglub committed
749
750
751
752
753
754
755
756
757
758
759
760
761
    playlist_item_t *p_new = NULL;
    int i_skip,i_goto,i, i_new, i_count ;
    playlist_view_t *p_view;

    vlc_bool_t b_loop = var_GetBool( p_playlist, "loop");
    vlc_bool_t b_random = var_GetBool( p_playlist, "random" );
    vlc_bool_t b_repeat = var_GetBool( p_playlist, "repeat" );
    vlc_bool_t b_playstop = var_GetBool( p_playlist, "play-and-stop" );

#ifdef PLAYLIST_PROFILE
    /* Calculate time needed */
    int64_t start = mdate();
#endif
762

zorglub's avatar
zorglub committed
763
764
765
766

    /* Handle quickly a few special cases */

    /* No items to play */
767
768
    if( p_playlist->i_size == 0 )
    {
zorglub's avatar
zorglub committed
769
770
771
772
773
774
        msg_Info( p_playlist, "playlist is empty" );
        return NULL;
    }
    /* Nothing requested */
    if( !p_playlist->request.b_request && p_playlist->status.p_item == NULL )
    {
zorglub's avatar
zorglub committed
775
        msg_Dbg( p_playlist,"nothing requested, starting" );
776
777
    }

zorglub's avatar
zorglub committed
778
779
780
781
782
783
    /* Repeat and play/stop */
    if( !p_playlist->request.b_request && b_repeat == VLC_TRUE )
    {
        msg_Dbg( p_playlist,"repeating item" );
        return p_playlist->status.p_item;
    }
784

zorglub's avatar
zorglub committed
785
786
787
788
789
790
    if( !p_playlist->request.b_request && b_playstop == VLC_TRUE )
    {
        msg_Dbg( p_playlist,"stopping (play and stop)");
        return NULL;
    }

791
    if( !p_playlist->request.b_request && p_playlist->status.p_item &&
zorglub's avatar
zorglub committed
792
793
794
795
796
797
        !(p_playlist->status.p_item->i_flags & PLAYLIST_SKIP_FLAG) )
    {
        msg_Dbg( p_playlist, "no-skip mode, stopping") ;
        return NULL;
    }

zorglub's avatar
zorglub committed
798
799
800
801
802
803
    /* TODO: improve this (only use current node) */
    /* TODO: use the "shuffled view" internally ? */
    /* Random case. This is an exception: if request, but request is skip +- 1
     * we don't go to next item but select a new random one. */
    if( b_random && (!p_playlist->request.b_request ||
        p_playlist->request.i_skip == 1 || p_playlist->request.i_skip == -1 ) )
804
    {
805
        srand( (unsigned int)mdate() );
zorglub's avatar
zorglub committed
806
807
        i_new = 0;
        for( i_count = 0; i_count < p_playlist->i_size - 1 ; i_count ++ )
808
        {
zorglub's avatar
zorglub committed
809
            i_new =
810
811
                (int)((float)p_playlist->i_size * rand() / (RAND_MAX+1.0));
            /* Check if the item has not already been played */
zorglub's avatar
zorglub committed
812
            if( p_playlist->pp_items[i_new]->i_nb_played == 0 )
813
814
815
                break;
        }
        if( i_count == p_playlist->i_size )
816
        {
817
818
819
820
821
            /* The whole playlist has been played: reset the counters */
            while( i_count > 0 )
            {
                p_playlist->pp_items[--i_count]->i_nb_played = 0;
            }
zorglub's avatar
zorglub committed
822
823
824
825
826
827
828
829
830
831
832
833
834
           if( !b_loop )
            {
                return NULL;
            }
        }
        p_playlist->request.i_skip = 0;
        p_playlist->request.b_request = VLC_FALSE;
        return p_playlist->pp_items[i_new];
   }

    /* Start the real work */
    if( p_playlist->request.b_request )
    {
zorglub's avatar
zorglub committed
835
#ifdef PLAYLIST_DEBUG
zorglub's avatar
zorglub committed
836
        msg_Dbg( p_playlist,"processing request" );
zorglub's avatar
zorglub committed
837
#endif
zorglub's avatar
zorglub committed
838
839
840
        /* We are not playing from a view */
        if(  p_playlist->request.i_view == -1  )
        {
zorglub's avatar
zorglub committed
841
842
843
#ifdef PLAYLIST_DEBUG
            msg_Dbg( p_playlist, "non-view mode request");
#endif
zorglub's avatar
zorglub committed
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
            /* Directly select the item, just like now */
            i_skip = p_playlist->request.i_skip;
            i_goto = p_playlist->request.i_goto;

            if( p_playlist->i_index == -1 ) p_playlist->i_index = 0;
            p_new = p_playlist->pp_items[p_playlist->i_index];

            if( i_goto >= 0  && i_goto < p_playlist->i_size )
            {
                p_playlist->i_index = i_goto;
                p_new = p_playlist->pp_items[p_playlist->i_index];
                p_playlist->request.i_goto = -1;
            }

            if( i_skip != 0 )
            {
                if( p_playlist->i_index + i_skip < p_playlist->i_size &&
                    p_playlist->i_index + i_skip >=  0 )
                {
                    p_playlist->i_index += i_skip;
                    p_new = p_playlist->pp_items[p_playlist->i_index];
                }
zorglub's avatar
zorglub committed
866
                p_playlist->request.i_skip = 0;
zorglub's avatar
zorglub committed
867
868
869
870
            }
        }
        else
        {
zorglub's avatar
zorglub committed
871
872
873
#ifdef PLAYLIST_DEBUG
            msg_Dbg( p_playlist, "view mode request" );
#endif
zorglub's avatar
zorglub committed
874
875
876
877
878
879
880
881
882
883
884
885
            p_new = p_playlist->request.p_item;
            i_skip = p_playlist->request.i_skip;

            /* If we are asked for a node, take its first item */
            if( p_playlist->request.p_item == NULL && i_skip == 0 )
            {
                i_skip++;
            }

            p_view = playlist_ViewFind( p_playlist,p_playlist->request.i_view );
            p_playlist->status.p_node = p_playlist->request.p_node;
            p_playlist->status.i_view = p_playlist->request.i_view;
886
887
888
889
890
            if( !p_view )
            {
                msg_Err( p_playlist, "p_view is NULL and should not! (FIXME)" );
            }
            else if( i_skip > 0 )
zorglub's avatar
zorglub committed
891
892
893
894
895
896
897
898
            {
                for( i = i_skip; i > 0 ; i-- )
                {
                    p_new = playlist_FindNextFromParent( p_playlist,
                                    p_playlist->request.i_view,
                                    p_view->p_root,
                                    p_playlist->request.p_node,
                                    p_new );
zorglub's avatar
zorglub committed
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
                    if( p_new == NULL )
                    {
                        if( b_loop )
                        {
                            p_new = playlist_FindNextFromParent( p_playlist,
                                      p_playlist->request.i_view,
                                      p_view->p_root,
                                      p_playlist->request.p_node,
                                      NULL );
                            if( p_new == NULL ) break;
                        }
                        else
                        {
                            break;
                        }
                    }
zorglub's avatar
zorglub committed
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
                }
            }
            else if( i_skip < 0 )
            {
                for( i = i_skip; i < 0 ; i++ )
                {
                    p_new = playlist_FindPrevFromParent( p_playlist,
                                    p_playlist->request.i_view,
                                    p_view->p_root,
                                    p_playlist->request.p_node,
                                    p_new );
                    if( p_new == NULL ) break;
                }

            }
        }
        /* Clear the request */
        p_playlist->request.b_request = VLC_FALSE;
    }
    /* "Automatic" item change ( next ) */
    else
    {
        p_playlist->request_date = 0;

        if( p_playlist->status.i_view == -1 )
        {
            if( p_playlist->i_index + 1 < p_playlist->i_size )
            {
                p_playlist->i_index++;
                p_new = p_playlist->pp_items[p_playlist->i_index];
945
946
947
948
                if( !(p_new->i_flags & PLAYLIST_SKIP_FLAG) )
                {
                    return NULL;
                }
zorglub's avatar
zorglub committed
949
950
            }
            else
951
            {
zorglub's avatar
zorglub committed
952
953
954
955
956
957
958
                if( b_loop && p_playlist->i_size > 0)
                {
                    p_playlist->i_index = 0;
                    p_new = p_playlist->pp_items[0];
                }
                else
                    p_new = NULL;
959
            }
960
        }
zorglub's avatar
zorglub committed
961
962
963
964
965
966
        /* We are playing with a view */
        else
        {
            playlist_view_t *p_view =
                    playlist_ViewFind( p_playlist,
                                   p_playlist->status.i_view );
967
968
969
970
971
972
973
            if( !p_view )
            {
                msg_Err( p_playlist, "p_view is NULL and should not! (FIXME)" );
            }
            else
            {
                p_new = playlist_FindNextFromParent( p_playlist,
zorglub's avatar
zorglub committed
974
975
976
977
                            p_playlist->status.i_view,
                            p_view->p_root,
                            p_playlist->status.p_node,
                            p_playlist->status.p_item );
978
979
980
                if( p_new == NULL && b_loop )
                {
                    p_new = playlist_FindNextFromParent( p_playlist,
zorglub's avatar
zorglub committed
981
982
983
984
                                   p_playlist->status.i_view,
                                   p_view->p_root,
                                   p_playlist->status.p_node,
                                   NULL );
985
                }
zorglub's avatar
zorglub committed
986
            }
zorglub's avatar
zorglub committed
987
988
989
990
991
992
993
994
995
        }
    }

    /* Reset index */
    if( p_playlist->i_index >= 0 && p_new != NULL &&
            p_playlist->pp_items[p_playlist->i_index] != p_new )
    {
        p_playlist->i_index = playlist_GetPositionById( p_playlist,
                                                        p_new->input.i_id );
996
    }
zorglub's avatar
zorglub committed
997
998

#ifdef PLAYLIST_PROFILE
zorglub's avatar
zorglub committed
999
    msg_Dbg(p_playlist,"next item found in "I64Fi " us", mdate()-start );
zorglub's avatar
zorglub committed
1000
1001
#endif

zorglub's avatar
zorglub committed
1002
1003
    if( p_new == NULL )
    {
zorglub's avatar
zorglub committed
1004
        msg_Info( p_playlist, "nothing to play" );
zorglub's avatar
zorglub committed
1005
    }
zorglub's avatar
zorglub committed
1006
1007
1008
    return p_new;
}

1009
/*****************************************************************************
zorglub's avatar
zorglub committed
1010
1011
 * PlayItem: start the input thread for an item
 ****************************************************************************/
zorglub's avatar
zorglub committed
1012
static int PlayItem( playlist_t *p_playlist, playlist_item_t *p_item )
1013
{
1014
    vlc_value_t val;
zorglub's avatar
zorglub committed
1015

1016
    msg_Dbg( p_playlist, "creating new input thread" );
1017

1018
    p_item->i_nb_played++;
zorglub's avatar
zorglub committed
1019
1020
    p_playlist->status.p_item = p_item;

1021
1022
1023
    p_playlist->i_index = playlist_GetPositionById( p_playlist,
                                                    p_item->input.i_id );

zorglub's avatar
zorglub committed
1024
1025
1026
1027
1028
1029
1030
1031
#ifdef PLAYLIST_PROFILE
    if( p_playlist->request_date != 0 )
    {
        msg_Dbg( p_playlist, "request processed after "I64Fi " us",
                  mdate() - p_playlist->request_date );
    }
#endif

1032
    p_playlist->p_input = input_CreateThread( p_playlist, &p_item->input );
1033

zorglub's avatar
zorglub committed
1034
1035
1036
1037
    var_AddCallback( p_playlist->p_input, "item-change",
                         ItemChange, p_playlist );

    val.i_int = p_item->input.i_id;
zorglub's avatar
zorglub committed
1038
1039
    /* unlock the playlist to set the var...mmm */
    vlc_mutex_unlock( &p_playlist->object_lock);
1040
    var_Set( p_playlist, "playlist-current", val);
zorglub's avatar
zorglub committed
1041
    vlc_mutex_lock( &p_playlist->object_lock);
zorglub's avatar
zorglub committed
1042

zorglub's avatar
zorglub committed
1043
1044
    return VLC_SUCCESS;

zorglub's avatar
zorglub committed
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
}

/* Forward item change from input */
static int ItemChange( vlc_object_t *p_obj, const char *psz_var,
                       vlc_value_t oldval, vlc_value_t newval, void *param )
{
    playlist_t *p_playlist = (playlist_t *)param;

    //p_playlist->b_need_update = VLC_TRUE;
    var_SetInteger( p_playlist, "item-change", newval.i_int );

    /* Update view */
    /* FIXME: Make that automatic */
1058
//    playlist_ViewUpdate( p_playlist, VIEW_S_AUTHOR );
zorglub's avatar
zorglub committed
1059
1060

    return VLC_SUCCESS;
1061
}