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
    p_playlist->i_all_size = 0;
123
    p_playlist->pp_all_items = 0;
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
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
275
int playlist_Control( playlist_t * p_playlist, int i_query, ... )
276
{
zorglub's avatar
zorglub committed
277
278
279
280
281
282
283
284
    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;
}
285

zorglub's avatar
zorglub committed
286
287
288
289
290
291
292
293
294
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

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

zorglub's avatar
zorglub committed
300
    switch( i_query )
301
302
    {
    case PLAYLIST_STOP:
zorglub's avatar
zorglub committed
303
304
305
306
307
308
309
310
311
312
313
314
        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
315
316
317
318
319
320
321
322
        if( p_view )
        {
            p_playlist->request.p_node = p_view->p_root;
        }
        else
        {
            p_playlist->request.p_node = NULL;
        }
zorglub's avatar
zorglub committed
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
        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 )
339
        {
zorglub's avatar
zorglub committed
340
341
342
343
344
            p_playlist->b_go_next = VLC_FALSE;
        }
        else
        {
            p_playlist->b_go_next = VLC_TRUE;
345
        }
346
        break;
347

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

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

        /* 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;
365
        break;
366

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

        p_playlist->request.b_request = VLC_FALSE;
        break;

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

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

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

    case PLAYLIST_GOTO:
zorglub's avatar
zorglub committed
409
410
411
412
413
414
        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;
415
        break;
416

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

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

426
427
428
429
430
431
432
433
434
435
436
437
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;
}

438

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

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

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

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

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

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

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

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

zorglub's avatar
zorglub committed
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
        /* 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;
            }
        }

519
520
521
        /* 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
522
            /* This input is dead. Remove it ! */
Sam Hocevar's avatar
Sam Hocevar committed
523
            if( p_playlist->p_input->b_dead )
524
525
526
527
528
            {
                input_thread_t *p_input;

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

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

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

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

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

                i_vout_destroyed_date = 0;
                i_sout_destroyed_date = 0;
546

547
                continue;
548
            }
Sam Hocevar's avatar
Sam Hocevar committed
549
550
551
552
553
554
            /* 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
555
556
            else if( p_playlist->p_input->b_error
                      || p_playlist->p_input->b_eof )
557
            {
zorglub's avatar
zorglub committed
558
559
                /* TODO FIXME XXX TODO FIXME XXX */
                /* Check for autodeletion */
560

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

zorglub's avatar
zorglub committed
588

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

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

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

zorglub's avatar
zorglub committed
623
624
625
626
627
628
629
630
631
632
633
        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 );
634
635
    }

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

638
    /* If there is an input, kill it */
639
    while( 1 )
640
    {
641
642
643
644
645
646
647
648
        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
649
        if( p_playlist->p_input->b_dead )
650
651
652
653
654
655
        {
            input_thread_t *p_input;

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

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

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

684
685
        vlc_mutex_unlock( &p_playlist->object_lock );

686
687
        msleep( INTF_IDLE_SLEEP );
    }
688

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

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

707
708
709
/* Queue for items to preparse */
static void RunPreparse ( playlist_preparse_t *p_obj )
{
710
    playlist_t *p_playlist = (playlist_t *)p_obj->p_parent;
711
712
713
714
715
716
717
718
719
720
721
    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 )
        {
722
            input_item_t *p_current = p_obj->pp_waiting[0];
723
            REMOVE_ELEM( p_obj->pp_waiting, p_obj->i_waiting, 0 );
724
725
726
727
            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 );
728
729
730
731
732
733
734
735
736
737
738
739
        }
        b_sleep = ( p_obj->i_waiting == 0 );

        vlc_mutex_unlock( &p_obj->object_lock );

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

740
/*****************************************************************************
zorglub's avatar
zorglub committed
741
 * NextItem
742
 *****************************************************************************
zorglub's avatar
zorglub committed
743
744
 * This function calculates the next playlist item, depending
 * on the playlist course mode (forward, backward, random, view,...).
745
 *****************************************************************************/
zorglub's avatar
zorglub committed
746
static playlist_item_t * NextItem( playlist_t *p_playlist )
747
{
zorglub's avatar
zorglub committed
748
749
750
751
752
753
754
755
756
757
758
759
760
    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
761

zorglub's avatar
zorglub committed
762
763
764
765

    /* Handle quickly a few special cases */

    /* No items to play */
766
767
    if( p_playlist->i_size == 0 )
    {
zorglub's avatar
zorglub committed
768
769
770
771
772
773
        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
774
        msg_Dbg( p_playlist,"nothing requested, starting" );
775
776
    }

zorglub's avatar
zorglub committed
777
778
779
780
781
782
    /* 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;
    }
783

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

790
    if( !p_playlist->request.b_request && p_playlist->status.p_item &&
zorglub's avatar
zorglub committed
791
792
793
794
795
796
        !(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
797
798
799
800
801
802
    /* 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 ) )
803
    {
804
        srand( (unsigned int)mdate() );
zorglub's avatar
zorglub committed
805
806
        i_new = 0;
        for( i_count = 0; i_count < p_playlist->i_size - 1 ; i_count ++ )
807
        {
zorglub's avatar
zorglub committed
808
            i_new =
809
810
                (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
811
            if( p_playlist->pp_items[i_new]->i_nb_played == 0 )
812
813
814
                break;
        }
        if( i_count == p_playlist->i_size )
815
        {
816
817
818
819
820
            /* 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
821
822
823
824
825
826
827
828
829
830
831
832
833
           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
834
#ifdef PLAYLIST_DEBUG
zorglub's avatar
zorglub committed
835
        msg_Dbg( p_playlist,"processing request" );
zorglub's avatar
zorglub committed
836
#endif
zorglub's avatar
zorglub committed
837
838
839
        /* We are not playing from a view */
        if(  p_playlist->request.i_view == -1  )
        {
zorglub's avatar
zorglub committed
840
841
842
#ifdef PLAYLIST_DEBUG
            msg_Dbg( p_playlist, "non-view mode request");
#endif
zorglub's avatar
zorglub committed
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
            /* 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
865
                p_playlist->request.i_skip = 0;
zorglub's avatar
zorglub committed
866
867
868
869
            }
        }
        else
        {
zorglub's avatar
zorglub committed
870
871
872
#ifdef PLAYLIST_DEBUG
            msg_Dbg( p_playlist, "view mode request" );
#endif
zorglub's avatar
zorglub committed
873
874
875
876
877
878
879
880
881
882
883
884
            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;
885
886
887
888
889
            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
890
891
892
893
894
895
896
897
            {
                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
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
                    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
914
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
                }
            }
            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];
944
945
946
947
                if( !(p_new->i_flags & PLAYLIST_SKIP_FLAG) )
                {
                    return NULL;
                }
zorglub's avatar
zorglub committed
948
949
            }
            else
950
            {
zorglub's avatar
zorglub committed
951
952
953
954
955
956
957
                if( b_loop && p_playlist->i_size > 0)
                {
                    p_playlist->i_index = 0;
                    p_new = p_playlist->pp_items[0];
                }
                else
                    p_new = NULL;
958
            }
959
        }
zorglub's avatar
zorglub committed
960
961
962
963
964
965
        /* We are playing with a view */
        else
        {
            playlist_view_t *p_view =
                    playlist_ViewFind( p_playlist,
                                   p_playlist->status.i_view );
966
967
968
969
970
971
972
            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
973
974
975
976
                            p_playlist->status.i_view,
                            p_view->p_root,
                            p_playlist->status.p_node,
                            p_playlist->status.p_item );
977
978
979
                if( p_new == NULL && b_loop )
                {
                    p_new = playlist_FindNextFromParent( p_playlist,
zorglub's avatar
zorglub committed
980
981
982
983
                                   p_playlist->status.i_view,
                                   p_view->p_root,
                                   p_playlist->status.p_node,
                                   NULL );
984
                }
zorglub's avatar
zorglub committed
985
            }
zorglub's avatar
zorglub committed
986
987
988
989
990
991
992
993
994
        }
    }

    /* 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 );
995
    }
zorglub's avatar
zorglub committed
996
997

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

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

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

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

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

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

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

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

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

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

zorglub's avatar
zorglub committed
1042
1043
    return VLC_SUCCESS;

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

/* 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 */
1057
//    playlist_ViewUpdate( p_playlist, VIEW_S_AUTHOR );
zorglub's avatar
zorglub committed
1058
1059

    return VLC_SUCCESS;
1060
}