playlist.c 19.3 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
8
9
10
11
12
 *
 * Authors: Samuel Hocevar <sam@zoy.org>
 *
 * 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.
13
 *
14
15
16
17
18
19
20
21
22
23
24
25
26
27
 * 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>
28
29
#include <vlc/vout.h>
#include <vlc/sout.h>
Sam Hocevar's avatar
Sam Hocevar committed
30
#include <vlc/input.h>
31
32
33
34

#include "stream_control.h"
#include "input_ext-intf.h"

35
#include "vlc_playlist.h"
36

37
38
#define PLAYLIST_FILE_HEADER_0_5  "# vlc playlist file version 0.5"

39
40
41
42
/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
static void RunThread ( playlist_t * );
43
44
45
static void SkipItem  ( playlist_t *, int );
static void PlayItem  ( playlist_t * );

sigmunau's avatar
sigmunau committed
46
47
48
/**
 * Create playlist
 *
49
 * Create a playlist structure.
sigmunau's avatar
sigmunau committed
50
51
52
 * \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
 */
53
playlist_t * __playlist_Create ( vlc_object_t *p_parent )
54
55
{
    playlist_t *p_playlist;
hartman's avatar
hartman committed
56
    vlc_value_t     val;
57
58
59
60
61
62
63
64
65

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

hartman's avatar
hartman committed
66
67
68
69
    var_Create( p_playlist, "intf-change", VLC_VAR_BOOL );
    val.b_bool = VLC_TRUE;
    var_Set( p_playlist, "intf-change", val );

70
71
72
73
74
75
76
77
    var_Create( p_playlist, "item-change", VLC_VAR_INTEGER );
    val.i_int = -1;
    var_Set( p_playlist, "item-change", val );

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

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

80
81
82
    var_Create( p_playlist, "intf-show", VLC_VAR_BOOL );
    val.b_bool = VLC_TRUE;
    var_Set( p_playlist, "intf-show", val );
83

84
85
86
87
88

    var_Create( p_playlist, "prevent-skip", VLC_VAR_BOOL );
    val.b_bool = VLC_FALSE;
    var_Set( p_playlist, "prevent-skip", val );

hartman's avatar
hartman committed
89
    var_Create( p_playlist, "play-and-stop", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
hartman's avatar
hartman committed
90
91
92
    var_Create( p_playlist, "random", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
    var_Create( p_playlist, "repeat", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
    var_Create( p_playlist, "loop", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
93

94
    p_playlist->p_input = NULL;
95
    p_playlist->i_status = PLAYLIST_STOPPED;
96
97
98
    p_playlist->i_index = -1;
    p_playlist->i_size = 0;
    p_playlist->pp_items = NULL;
Sam Hocevar's avatar
Sam Hocevar committed
99

zorglub's avatar
zorglub committed
100
101
    p_playlist->i_groups = 0;
    p_playlist->pp_groups = NULL;
102
103
    p_playlist->i_last_group = 0;
    p_playlist->i_last_id = 0;
hartman's avatar
hartman committed
104
105
    p_playlist->i_sort = SORT_ID;
    p_playlist->i_order = ORDER_NORMAL;
zorglub's avatar
zorglub committed
106

zorglub's avatar
zorglub committed
107
    playlist_CreateGroup( p_playlist, _("Normal") );
zorglub's avatar
zorglub committed
108

109
110
    if( vlc_thread_create( p_playlist, "playlist", RunThread,
                           VLC_THREAD_PRIORITY_LOW, VLC_TRUE ) )
111
112
113
114
115
116
    {
        msg_Err( p_playlist, "cannot spawn playlist thread" );
        vlc_object_destroy( p_playlist );
        return NULL;
    }

117
118
119
    /* The object has been initialized, now attach it */
    vlc_object_attach( p_playlist, p_parent );

120
121
122
    return p_playlist;
}

sigmunau's avatar
sigmunau committed
123
124
125
/**
 * Destroy the playlist.
 *
126
 * Delete all items in the playlist and free the playlist structure.
sigmunau's avatar
sigmunau committed
127
128
 * \param p_playlist the playlist structure to destroy
 */
129
130
131
132
133
134
void playlist_Destroy( playlist_t * p_playlist )
{
    p_playlist->b_die = 1;

    vlc_thread_join( p_playlist );

hartman's avatar
hartman committed
135
    var_Destroy( p_playlist, "intf-change" );
136
    var_Destroy( p_playlist, "item-change" );
137
138
139
140
    var_Destroy( p_playlist, "playlist-current" );
    var_Destroy( p_playlist, "intf-popmenu" );
    var_Destroy( p_playlist, "intf-show" );
    var_Destroy( p_playlist, "prevent-skip" );
hartman's avatar
hartman committed
141
    var_Destroy( p_playlist, "play-and-stop" );
142
143
144
    var_Destroy( p_playlist, "random" );
    var_Destroy( p_playlist, "repeat" );
    var_Destroy( p_playlist, "loop" );
hartman's avatar
hartman committed
145

146
147
148
149
150
151
152
153
154
155
    while( p_playlist->i_groups > 0 )
    {
        playlist_DeleteGroup( p_playlist, p_playlist->pp_groups[0]->i_id );
    }

    while( p_playlist->i_size > 0 )
    {
        playlist_Delete( p_playlist, 0 );
    }

156
157
158
159
    vlc_object_destroy( p_playlist );
}


sigmunau's avatar
sigmunau committed
160
/**
161
 * Do a playlist action.
162
 *
163
 * If there is something in the playlist then you can do playlist actions.
sigmunau's avatar
sigmunau committed
164
165
166
167
 * \param p_playlist the playlist to do the command on
 * \param i_command the command to do
 * \param i_arg the argument to the command. See playlist_command_t for details
 */
168
void playlist_Command( playlist_t * p_playlist, playlist_command_t i_command,
sigmunau's avatar
sigmunau committed
169
                       int i_arg )
170
{
171
172
    vlc_value_t val;

173
    vlc_mutex_lock( &p_playlist->object_lock );
Sam Hocevar's avatar
Sam Hocevar committed
174

175
176
177
178
179
180
    if( p_playlist->i_size <= 0 )
    {
        vlc_mutex_unlock( &p_playlist->object_lock );
        return;
    }

181
182
183
184
    switch( i_command )
    {
    case PLAYLIST_STOP:
        p_playlist->i_status = PLAYLIST_STOPPED;
185
186
187
        if( p_playlist->p_input )
        {
            input_StopThread( p_playlist->p_input );
188
            val.i_int = p_playlist->i_index;
zorglub's avatar
zorglub committed
189
190
            /* Does not matter if we unlock here */
            vlc_mutex_unlock( &p_playlist->object_lock );
191
            var_Set( p_playlist, "item-change",val );
zorglub's avatar
zorglub committed
192
            vlc_mutex_lock( &p_playlist->object_lock );
193
        }
194
        break;
195

196
197
    case PLAYLIST_PLAY:
        p_playlist->i_status = PLAYLIST_RUNNING;
zorglub's avatar
zorglub committed
198
        if( !p_playlist->p_input && p_playlist->i_enabled != 0 )
199
        {
200
            PlayItem( p_playlist );
gbazin's avatar
   
gbazin committed
201
202
203
        }
        if( p_playlist->p_input )
        {
204
205
            val.i_int = PLAYING_S;
            var_Set( p_playlist->p_input, "state", val );
206
        }
207
        break;
208

209
    case PLAYLIST_PAUSE:
hartman's avatar
hartman committed
210
        val.i_int = 0;
211
        if( p_playlist->p_input )
hartman's avatar
hartman committed
212
213
214
            var_Get( p_playlist->p_input, "state", &val );

        if( val.i_int == PAUSE_S )
215
        {
hartman's avatar
hartman committed
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
            p_playlist->i_status = PLAYLIST_RUNNING;
            if( p_playlist->p_input )
            {
                val.i_int = PLAYING_S;
                var_Set( p_playlist->p_input, "state", val );
            }
        }
        else
        {
            p_playlist->i_status = PLAYLIST_PAUSED;
            if( p_playlist->p_input )
            {
                val.i_int = PAUSE_S;
                var_Set( p_playlist->p_input, "state", val );
            }
231
232
233
        }
        break;

234
    case PLAYLIST_SKIP:
235
        p_playlist->i_status = PLAYLIST_STOPPED;
zorglub's avatar
zorglub committed
236
237
238
239
        if( p_playlist->i_enabled == 0)
        {
            break;
        }
240
241
        SkipItem( p_playlist, i_arg );
        if( p_playlist->p_input )
242
        {
243
244
245
246
247
248
            input_StopThread( p_playlist->p_input );
        }
        p_playlist->i_status = PLAYLIST_RUNNING;
        break;

    case PLAYLIST_GOTO:
zorglub's avatar
zorglub committed
249
250
        if( i_arg >= 0 && i_arg < p_playlist->i_size &&
            p_playlist->i_enabled != 0 )
251
252
253
254
255
256
        {
            p_playlist->i_index = i_arg;
            if( p_playlist->p_input )
            {
                input_StopThread( p_playlist->p_input );
            }
257
258
            val.b_bool = VLC_TRUE;
            var_Set( p_playlist, "prevent-skip", val );
259
260
261
            p_playlist->i_status = PLAYLIST_RUNNING;
        }
        break;
262

263
    default:
264
        msg_Err( p_playlist, "unknown playlist command" );
265
266
267
        break;
    }

268
    vlc_mutex_unlock( &p_playlist->object_lock );
269
#if 0
gbazin's avatar
   
gbazin committed
270
271
    val.b_bool = VLC_TRUE;
    var_Set( p_playlist, "intf-change", val );
272
#endif
273
274
    return;
}
275

276

277
278
static mtime_t ObjectGarbageCollector( playlist_t *p_playlist, int i_type,
                                       mtime_t destroy_date )
279
280
281
{
    vlc_object_t *p_obj;

gbazin's avatar
   
gbazin committed
282
    if( destroy_date > mdate() ) return destroy_date;
283
284

    if( destroy_date == 0 )
285
286
    {
        /* give a little time */
287
        return mdate() + I64C(1000000);
288
289
290
    }
    else
    {
291
        while( ( p_obj = vlc_object_find( p_playlist, i_type, FIND_CHILD ) ) )
292
293
294
        {
            if( p_obj->p_parent != (vlc_object_t*)p_playlist )
            {
295
                /* only first child (ie unused) */
296
297
298
299
300
                vlc_object_release( p_obj );
                break;
            }
            if( i_type == VLC_OBJECT_VOUT )
            {
301
                msg_Dbg( p_playlist, "garbage collector destroying 1 vout" );
302
303
304
305
306
307
308
309
310
311
                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 );
            }
        }
312
        return 0;
313
314
315
    }
}

316
317
318
319
320
/*****************************************************************************
 * RunThread: main playlist thread
 *****************************************************************************/
static void RunThread ( playlist_t *p_playlist )
{
321
    vlc_object_t *p_obj;
gbazin's avatar
   
gbazin committed
322
323
    vlc_value_t val;

324
325
326
    mtime_t    i_vout_destroyed_date = 0;
    mtime_t    i_sout_destroyed_date = 0;

327
328
329
    /* Tell above that we're ready */
    vlc_thread_ready( p_playlist );

330
331
    while( !p_playlist->b_die )
    {
332
333
        vlc_mutex_lock( &p_playlist->object_lock );

334
335
336
        /* 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
337
            /* This input is dead. Remove it ! */
Sam Hocevar's avatar
Sam Hocevar committed
338
            if( p_playlist->p_input->b_dead )
339
340
341
342
343
            {
                input_thread_t *p_input;

                p_input = p_playlist->p_input;
                p_playlist->p_input = NULL;
344
345
346
347

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

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

gbazin's avatar
   
gbazin committed
352
353
                /* Unlink current input
                 * (_after_ input_DestroyThread for vout garbage collector) */
354
355
356
                vlc_object_detach( p_input );

                /* Destroy object */
Sam Hocevar's avatar
Sam Hocevar committed
357
                vlc_object_destroy( p_input );
358
359
360

                i_vout_destroyed_date = 0;
                i_sout_destroyed_date = 0;
361
                continue;
362
            }
Sam Hocevar's avatar
Sam Hocevar committed
363
364
365
366
367
368
            /* 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
369
370
            else if( p_playlist->p_input->b_error
                      || p_playlist->p_input->b_eof )
371
            {
gbazin's avatar
   
gbazin committed
372
373
374
                /* Check for autodeletion */
                if( p_playlist->pp_items[p_playlist->i_index]->b_autodeletion )
                {
gbazin's avatar
   
gbazin committed
375
                    vlc_mutex_unlock( &p_playlist->object_lock );
gbazin's avatar
   
gbazin committed
376
                    playlist_Delete( p_playlist, p_playlist->i_index );
377
378
                    p_playlist->i_index++;
                    p_playlist->i_status = PLAYLIST_RUNNING;
gbazin's avatar
   
gbazin committed
379
                }
gbazin's avatar
   
gbazin committed
380
381
382
383
384
385
386
                else
                {
                    /* Select the next playlist item */
                    SkipItem( p_playlist, 1 );
                    input_StopThread( p_playlist->p_input );
                    vlc_mutex_unlock( &p_playlist->object_lock );
                }
387
                continue;
388
            }
389
390
            else if( p_playlist->p_input->stream.control.i_status != INIT_S )
            {
391
                vlc_mutex_unlock( &p_playlist->object_lock );
392
393
394
                i_vout_destroyed_date =
                    ObjectGarbageCollector( p_playlist, VLC_OBJECT_VOUT,
                                            i_vout_destroyed_date );
gbazin's avatar
   
gbazin committed
395
                i_sout_destroyed_date =
396
397
                    ObjectGarbageCollector( p_playlist, VLC_OBJECT_SOUT,
                                            i_sout_destroyed_date );
398
                vlc_mutex_lock( &p_playlist->object_lock );
399
            }
400
401
402
        }
        else if( p_playlist->i_status != PLAYLIST_STOPPED )
        {
hartman's avatar
hartman committed
403
404
            var_Get( p_playlist, "prevent-skip", &val );
            if( val.b_bool == VLC_FALSE )
405
406
407
408
            {
                SkipItem( p_playlist, 0 );
            }
            val.b_bool = VLC_TRUE;
hartman's avatar
hartman committed
409
410
411
412
413
414
            var_Set( p_playlist, "prevent-skip", val );
            var_Get( p_playlist, "play-and-stop", &val );
            if( val.b_bool == VLC_FALSE )
            {
                PlayItem( p_playlist );
            }
415
        }
416
417
        else if( p_playlist->i_status == PLAYLIST_STOPPED )
        {
418
            vlc_mutex_unlock( &p_playlist->object_lock );
419
420
421
422
            i_sout_destroyed_date =
                ObjectGarbageCollector( p_playlist, VLC_OBJECT_SOUT, mdate() );
            i_vout_destroyed_date =
                ObjectGarbageCollector( p_playlist, VLC_OBJECT_VOUT, mdate() );
423
            vlc_mutex_lock( &p_playlist->object_lock );
424
        }
425
426
        vlc_mutex_unlock( &p_playlist->object_lock );

427
428
429
430
        msleep( INTF_IDLE_SLEEP );
    }

    /* If there is an input, kill it */
431
    while( 1 )
432
    {
433
434
435
436
437
438
439
440
        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
441
        if( p_playlist->p_input->b_dead )
442
443
444
445
446
447
        {
            input_thread_t *p_input;

            /* Unlink current input */
            p_input = p_playlist->p_input;
            p_playlist->p_input = NULL;
448
            vlc_mutex_unlock( &p_playlist->object_lock );
449
450
451

            /* Destroy input */
            input_DestroyThread( p_input );
452
453
454
455
456
            /* Unlink current input (_after_ input_DestroyThread for vout
             * garbage collector)*/
            vlc_object_detach( p_input );

            /* Destroy object */
Sam Hocevar's avatar
Sam Hocevar committed
457
            vlc_object_destroy( p_input );
458
            continue;
459
        }
Sam Hocevar's avatar
Sam Hocevar committed
460
461
        else if( p_playlist->p_input->b_die )
        {
462
            /* This input is dying, leave him alone */
Sam Hocevar's avatar
Sam Hocevar committed
463
464
            ;
        }
Sam Hocevar's avatar
Sam Hocevar committed
465
        else if( p_playlist->p_input->b_error || p_playlist->p_input->b_eof )
466
        {
Sam Hocevar's avatar
Sam Hocevar committed
467
            input_StopThread( p_playlist->p_input );
468
            vlc_mutex_unlock( &p_playlist->object_lock );
469
            continue;
470
471
472
473
474
475
        }
        else
        {
            p_playlist->p_input->b_eof = 1;
        }

476
477
        vlc_mutex_unlock( &p_playlist->object_lock );

478
479
        msleep( INTF_IDLE_SLEEP );
    }
480

481
    /* close all remaining sout */
482
    while( ( p_obj = vlc_object_find( p_playlist,
483
                                      VLC_OBJECT_SOUT, FIND_CHILD ) ) )
484
485
    {
        vlc_object_release( p_obj );
486
        sout_DeleteInstance( (sout_instance_t*)p_obj );
487
    }
488
489

    /* close all remaining vout */
490
    while( ( p_obj = vlc_object_find( p_playlist,
491
                                      VLC_OBJECT_VOUT, FIND_CHILD ) ) )
492
    {
493
        vlc_object_detach( p_obj );
494
        vlc_object_release( p_obj );
495
        vout_Destroy( (vout_thread_t *)p_obj );
496
    }
497
498
}

499
500
501
502
503
504
505
506
507
/*****************************************************************************
 * SkipItem: go to Xth playlist item
 *****************************************************************************
 * This function calculates the position of the next playlist item, depending
 * on the playlist course mode (forward, backward, random...).
 *****************************************************************************/
static void SkipItem( playlist_t *p_playlist, int i_arg )
{
    int i_oldindex = p_playlist->i_index;
hartman's avatar
hartman committed
508
    vlc_bool_t b_random, b_repeat, b_loop;
509
    vlc_value_t val;
510
    int i_count;
511
512
513
514
515
516
517
518

    /* If the playlist is empty, there is no current item */
    if( p_playlist->i_size == 0 )
    {
        p_playlist->i_index = -1;
        return;
    }

hartman's avatar
hartman committed
519
520
521
522
523
524
    var_Get( p_playlist, "random", &val );
    b_random = val.b_bool;
    var_Get( p_playlist, "repeat", &val );
    b_repeat = val.b_bool;
    var_Get( p_playlist, "loop", &val );
    b_loop = val.b_bool;
525

526
    /* Increment */
527
    if( b_random )
528
    {
529
        srand( (unsigned int)mdate() );
530
        i_count = 0;
531
532
533
534
535
536
537
538
539
540
        while( i_count < p_playlist->i_size )
        {
            p_playlist->i_index =
                (int)((float)p_playlist->i_size * rand() / (RAND_MAX+1.0));
            /* Check if the item has not already been played */
            if( p_playlist->pp_items[p_playlist->i_index]->i_nb_played == 0 )
                break;
            i_count++;
        }
        if( i_count == p_playlist->i_size )
541
        {
542
543
544
545
546
547
548
549
550
            /* The whole playlist has been played: reset the counters */
            while( i_count > 0 )
            {
                p_playlist->pp_items[--i_count]->i_nb_played = 0;
            }
            if( !b_loop )
            {
                p_playlist->i_status = PLAYLIST_STOPPED;
            }
551
        }
552
    }
553
    else if( !b_repeat )
hartman's avatar
hartman committed
554
    {
555
        p_playlist->i_index += i_arg;
hartman's avatar
hartman committed
556
    }
557

558
559
560
    /* Boundary check */
    if( p_playlist->i_index >= p_playlist->i_size )
    {
561
        if( p_playlist->i_status == PLAYLIST_STOPPED || b_random || b_loop )
562
        {
563
564
            p_playlist->i_index -= p_playlist->i_size
                         * ( p_playlist->i_index / p_playlist->i_size );
565
566
567
568
569
570
571
572
573
574
575
576
        }
        else
        {
            /* Don't loop by default: stop at playlist end */
            p_playlist->i_index = i_oldindex;
            p_playlist->i_status = PLAYLIST_STOPPED;
        }
    }
    else if( p_playlist->i_index < 0 )
    {
        p_playlist->i_index = p_playlist->i_size - 1;
    }
577

zorglub's avatar
zorglub committed
578
    /* Check that the item is enabled */
579
580
    if( p_playlist->pp_items[p_playlist->i_index]->b_enabled == VLC_FALSE &&
        p_playlist->i_enabled != 0)
581
    {
zorglub's avatar
zorglub committed
582
583
        SkipItem( p_playlist , 1 );
    }
584
585
586
587
588
589
590
591
592
593
}

/*****************************************************************************
 * PlayItem: play current playlist item
 *****************************************************************************
 * This function calculates the position of the next playlist item, depending
 * on the playlist course mode (forward, backward, random...).
 *****************************************************************************/
static void PlayItem( playlist_t *p_playlist )
{
594
    playlist_item_t *p_item;
595
    vlc_value_t val;
596
597
    if( p_playlist->i_index == -1 )
    {
zorglub's avatar
zorglub committed
598
        if( p_playlist->i_size == 0 || p_playlist->i_enabled == 0)
599
600
601
602
603
        {
            return;
        }
        SkipItem( p_playlist, 1 );
    }
zorglub's avatar
zorglub committed
604
605
606
607
608
    if( p_playlist->i_enabled == 0)
    {
        return;
    }

609
    msg_Dbg( p_playlist, "creating new input thread" );
610
611
    p_item = p_playlist->pp_items[p_playlist->i_index];

612
    p_item->i_nb_played++;
613
    p_playlist->p_input = input_CreateThread( p_playlist, &p_item->input );
614
615

    val.i_int = p_playlist->i_index;
zorglub's avatar
zorglub committed
616
617
    /* unlock the playlist to set the var...mmm */
    vlc_mutex_unlock( &p_playlist->object_lock);
618
    var_Set( p_playlist, "playlist-current", val);
zorglub's avatar
zorglub committed
619
    vlc_mutex_lock( &p_playlist->object_lock);
620
}