engine.c 25.1 KB
Newer Older
1
2
3
/*****************************************************************************
 * engine.c : Run the playlist and handle its control
 *****************************************************************************
4
 * Copyright (C) 1999-2007 the VideoLAN team
dionoea's avatar
2nd try    
dionoea committed
5
 * $Id$
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 *
 * Authors: Samuel Hocevar <sam@zoy.org>
 *          Clément Stenac <zorglub@videolan.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.
 *
 * 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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/
24

25
26
27
28
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

29
#include <vlc/vlc.h>
zorglub's avatar
zorglub committed
30
31
32
33
#include <vlc_vout.h>
#include <vlc_sout.h>
#include <vlc_playlist.h>
#include <vlc_interface.h>
34
#include "playlist_internal.h"
zorglub's avatar
zorglub committed
35
#include "stream_output/stream_output.h" /* sout_DeleteInstance */
36
37
38
39
40

/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
static void VariablesInit( playlist_t *p_playlist );
41
static void playlist_Destructor( vlc_object_t * p_this );
42

zorglub's avatar
zorglub committed
43
44
45
static int RandomCallback( vlc_object_t *p_this, char const *psz_cmd,
                           vlc_value_t oldval, vlc_value_t newval, void *a )
{
46
47
    (void)psz_cmd; (void)oldval; (void)newval; (void)a;

zorglub's avatar
zorglub committed
48
    ((playlist_t*)p_this)->b_reset_currently_playing = VLC_TRUE;
49
    playlist_Signal( ((playlist_t*)p_this) );
zorglub's avatar
zorglub committed
50
51
52
    return VLC_SUCCESS;
}

53
54
55
56
57
58
59
60
61
/**
 * Create playlist
 *
 * Create a playlist structure.
 * \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
 */
playlist_t * playlist_Create( vlc_object_t *p_parent )
{
62
    static const char playlist_name[] = "playlist";
63
    playlist_t *p_playlist;
64
    vlc_bool_t b_save;
65
    int i_tree;
66
67

    /* Allocate structure */
68
69
    p_playlist = vlc_custom_create( p_parent, sizeof( *p_playlist ),
                                    VLC_OBJECT_PLAYLIST, playlist_name );
70
71
72
73
74
    if( !p_playlist )
    {
        msg_Err( p_parent, "out of memory" );
        return NULL;
    }
75

76
    TAB_INIT( p_playlist->i_sds, p_playlist->pp_sds );
77

78
    p_parent->p_libvlc->p_playlist = p_playlist;
79
80
81
82
83
84
85
86

    VariablesInit( p_playlist );

    /* Initialise data structures */
    vlc_mutex_init( p_playlist, &p_playlist->gc_lock );
    p_playlist->i_last_playlist_id = 0;
    p_playlist->p_input = NULL;

87
88
    p_playlist->gc_date = 0;
    p_playlist->b_cant_sleep = VLC_FALSE;
89

zorglub's avatar
zorglub committed
90
91
92
    ARRAY_INIT( p_playlist->items );
    ARRAY_INIT( p_playlist->all_items );
    ARRAY_INIT( p_playlist->current );
93

zorglub's avatar
zorglub committed
94
95
    p_playlist->i_current_index = 0;
    p_playlist->b_reset_currently_playing = VLC_TRUE;
96
    p_playlist->last_rebuild_date = 0;
zorglub's avatar
zorglub committed
97

98
99
100
101
    i_tree = var_CreateGetBool( p_playlist, "playlist-tree" );
    p_playlist->b_always_tree = (i_tree == 1);
    p_playlist->b_never_tree = (i_tree == 2);

102
103
    p_playlist->b_doing_ml = VLC_FALSE;

104
    p_playlist->b_auto_preparse =
ivoire's avatar
ivoire committed
105
                        var_CreateGetBool( p_playlist, "auto-preparse" ) ;
106

107
    p_playlist->p_root_category = playlist_NodeCreate( p_playlist, NULL, NULL,
108
                                    0, NULL );
109
    p_playlist->p_root_onelevel = playlist_NodeCreate( p_playlist, NULL, NULL,
110
                                    0, p_playlist->p_root_category->p_input );
111

112
113
114
    if( !p_playlist->p_root_category || !p_playlist->p_root_onelevel )
        return NULL;

115
    /* Create playlist and media library */
116
117
118
119
    playlist_NodesPairCreate( p_playlist, _( "Playlist" ),
                            &p_playlist->p_local_category,
                            &p_playlist->p_local_onelevel, VLC_FALSE );

120
121
122
    p_playlist->p_local_category->i_flags |= PLAYLIST_RO_FLAG;
    p_playlist->p_local_onelevel->i_flags |= PLAYLIST_RO_FLAG;

123
124
125
126
127
    if( !p_playlist->p_local_category || !p_playlist->p_local_onelevel ||
        !p_playlist->p_local_category->p_input ||
        !p_playlist->p_local_onelevel->p_input )
        return NULL;

128
129
    if( config_GetInt( p_playlist, "media-library") )
    {
130
131
132
        playlist_NodesPairCreate( p_playlist, _( "Media Library" ),
                            &p_playlist->p_ml_category,
                            &p_playlist->p_ml_onelevel, VLC_FALSE );
133
134
135
136

        if(!p_playlist->p_ml_category || !p_playlist->p_ml_onelevel)
            return NULL;

137
138
139
140
141
142
143
        p_playlist->p_ml_category->i_flags |= PLAYLIST_RO_FLAG;
        p_playlist->p_ml_onelevel->i_flags |= PLAYLIST_RO_FLAG;
    }
    else
    {
        p_playlist->p_ml_category = p_playlist->p_ml_onelevel = NULL;
    }
144
145
146

    /* Initial status */
    p_playlist->status.p_item = NULL;
147
    p_playlist->status.p_node = p_playlist->p_local_onelevel;
148
149
150
151
152
153
    p_playlist->request.b_request = VLC_FALSE;
    p_playlist->status.i_status = PLAYLIST_STOPPED;

    p_playlist->i_sort = SORT_ID;
    p_playlist->i_order = ORDER_NORMAL;

154

155
156
    b_save = p_playlist->b_auto_preparse;
    p_playlist->b_auto_preparse = VLC_FALSE;
157
    playlist_MLLoad( p_playlist );
158
    p_playlist->b_auto_preparse = VLC_TRUE;
159
160
161

    vlc_object_set_destructor( p_playlist, playlist_Destructor );

162
163
164
    return p_playlist;
}

ivoire's avatar
ivoire committed
165
166
167
168
169
170
171
/**
 * Destroy playlist
 *
 * Destroy a playlist structure.
 * \param p_playlist the playlist object
 * \return nothing
 */
172
173
void playlist_Destroy( playlist_t *p_playlist )
{
174
    /* XXX: should go in the playlist destructor */
175
176
177
    var_Destroy( p_playlist, "intf-change" );
    var_Destroy( p_playlist, "item-change" );
    var_Destroy( p_playlist, "playlist-current" );
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
178
    var_Destroy( p_playlist, "intf-popupmenu" );
179
180
    var_Destroy( p_playlist, "intf-show" );
    var_Destroy( p_playlist, "play-and-stop" );
181
    var_Destroy( p_playlist, "play-and-exit" );
182
183
184
185
186
    var_Destroy( p_playlist, "random" );
    var_Destroy( p_playlist, "repeat" );
    var_Destroy( p_playlist, "loop" );
    var_Destroy( p_playlist, "activity" );

187
    vlc_object_release( p_playlist );
188
}
189

190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
static void playlist_Destructor( vlc_object_t * p_this )
{
    playlist_t * p_playlist = (playlist_t *)p_this;

    // Kill preparser
    if( p_playlist->p_preparse )
    {
        vlc_object_release( p_playlist->p_preparse );
    }

    // Kill meta fetcher
    if( p_playlist->p_fetcher )
    {
        vlc_object_release( p_playlist->p_fetcher );
    }
}

207
/* Destroy remaining objects */
208
static void ObjectGarbageCollector( playlist_t *p_playlist, vlc_bool_t b_force )
209
210
211
{
    vlc_object_t *p_obj;

212
    if( !b_force )
213
    {
214
215
216
217
218
219
220
        if( mdate() - p_playlist->gc_date < 1000000 )
        {
            p_playlist->b_cant_sleep = VLC_TRUE;
            return;
        }
        else if( p_playlist->gc_date == 0 )
            return;
221
    }
222

223
    vlc_mutex_lock( &p_playlist->gc_lock );
224
    while( ( p_obj = vlc_object_find( p_playlist->p_libvlc, VLC_OBJECT_VOUT,
225
                                                  FIND_CHILD ) ) )
226
    {
227
        if( p_obj->p_parent != VLC_OBJECT(p_playlist->p_libvlc) )
228
229
230
231
232
233
234
235
        {
            vlc_object_release( p_obj );
            break;
        }
        msg_Dbg( p_playlist, "garbage collector destroying 1 vout" );
        vlc_object_detach( p_obj );
        vlc_object_release( p_obj );
        vout_Destroy( (vout_thread_t *)p_obj );
236
    }
237
238
    while( ( p_obj = vlc_object_find( p_playlist, VLC_OBJECT_SOUT,
                                                  FIND_CHILD ) ) )
239
    {
240
        if( p_obj->p_parent != VLC_OBJECT(p_playlist) )
241
        {
242
243
            vlc_object_release( p_obj );
            break;
244
        }
245
246
        msg_Dbg( p_playlist, "garbage collector destroying 1 sout" );
        vlc_object_detach( p_obj );
247
248
        vlc_object_release( p_obj );
        sout_DeleteInstance( (sout_instance_t*)p_obj );
249
    }
250
251
    p_playlist->b_cant_sleep = VLC_FALSE;
    vlc_mutex_unlock( &p_playlist->gc_lock );
252
253
}

ivoire's avatar
ivoire committed
254
255
256
257
258
259
260
/**
 * Main loop
 *
 * Main loop for the playlist
 * \param p_playlist the playlist object
 * \return nothing
 */
261
262
263
void playlist_MainLoop( playlist_t *p_playlist )
{
    playlist_item_t *p_item = NULL;
264
    vlc_bool_t b_playexit = var_GetBool( p_playlist, "play-and-exit" );
265
    PL_LOCK;
266

267
268
    if( p_playlist->b_reset_currently_playing &&
        mdate() - p_playlist->last_rebuild_date > 30000 ) // 30 ms
269
    {
ivoire's avatar
ivoire committed
270
        ResetCurrentlyPlaying( p_playlist, var_GetBool( p_playlist, "random" ),
271
272
                             p_playlist->status.p_item );
        p_playlist->last_rebuild_date = mdate();
273
    }
274

275
check_input:
276
277
278
    /* If there is an input, check that it doesn't need to die. */
    if( p_playlist->p_input )
    {
279
280
281
282
283
284
        if( p_playlist->request.b_request && !p_playlist->p_input->b_die )
        {
            PL_DEBUG( "incoming request - stopping current input" );
            input_StopThread( p_playlist->p_input );
        }

285
286
287
288
289
        /* This input is dead. Remove it ! */
        if( p_playlist->p_input->b_dead )
        {
            int i_activity;
            input_thread_t *p_input;
zorglub's avatar
zorglub committed
290
            PL_DEBUG( "dead input" );
291
292
293
294
295

            p_input = p_playlist->p_input;
            p_playlist->p_input = NULL;

            /* Release the playlist lock, because we may get stuck
296
             * in vlc_object_release() for some time. */
297
            PL_UNLOCK;
298
299

            /* Destroy input */
300
            vlc_object_release( p_input );
301

302
303
304
305
            PL_LOCK;

            p_playlist->gc_date = mdate();
            p_playlist->b_cant_sleep = VLC_TRUE;
306
307
308
309

            if( p_playlist->status.p_item->i_flags
                & PLAYLIST_REMOVE_FLAG )
            {
zorglub's avatar
zorglub committed
310
311
                 PL_DEBUG( "%s was marked for deletion, deleting",
                                 PLI_NAME( p_playlist->status.p_item  ) );
312
                 playlist_ItemDelete( p_playlist->status.p_item );
313
314
                 if( p_playlist->request.p_item == p_playlist->status.p_item )
                     p_playlist->request.p_item = NULL;
315
316
317
                 p_playlist->status.p_item = NULL;
            }

ivoire's avatar
ivoire committed
318
            i_activity= var_GetInteger( p_playlist, "activity" );
319
320
            var_SetInteger( p_playlist, "activity", i_activity -
                            DEFAULT_INPUT_ACTIVITY );
321
            goto check_input;
322
323
324
325
        }
        /* This input is dying, let it do */
        else if( p_playlist->p_input->b_die )
        {
zorglub's avatar
zorglub committed
326
            PL_DEBUG( "dying input" );
327
            PL_UNLOCK;
328
            msleep( INTF_IDLE_SLEEP );
329
            PL_LOCK;
330
            goto check_input;
331
332
333
334
335
        }
        /* This input has finished, ask it to die ! */
        else if( p_playlist->p_input->b_error
                  || p_playlist->p_input->b_eof )
        {
zorglub's avatar
zorglub committed
336
            PL_DEBUG( "finished input" );
337
            input_StopThread( p_playlist->p_input );
338
339
            /* No need to wait here, we'll wait in the p_input->b_die case */
            goto check_input;
340
341
342
        }
        else if( p_playlist->p_input->i_state != INIT_S )
        {
zorglub's avatar
zorglub committed
343
            PL_UNLOCK;
344
            ObjectGarbageCollector( p_playlist, VLC_FALSE );
345
            PL_LOCK;
346
347
348
349
350
351
352
353
354
        }
    }
    else
    {
        /* No input. Several cases
         *  - No request, running status -> start new item
         *  - No request, stopped status -> collect garbage
         *  - Request, running requested -> start new item
         *  - Request, stopped requested -> collect garbage
ivoire's avatar
ivoire committed
355
356
357
358
359
        */
        if( p_playlist->request.i_status != PLAYLIST_STOPPED )
        {
            msg_Dbg( p_playlist, "starting new item" );
            p_item = playlist_NextItem( p_playlist );
360

ivoire's avatar
ivoire committed
361
362
            if( p_item == NULL )
            {
363
                msg_Dbg( p_playlist, "nothing to play" );
364
                p_playlist->status.i_status = PLAYLIST_STOPPED;
365
                PL_UNLOCK;
366

367
368
369
                if( b_playexit == VLC_TRUE )
                {
                    msg_Info( p_playlist, "end of playlist, exiting" );
370
                    vlc_object_kill( p_playlist->p_libvlc );
371
                }
372
                ObjectGarbageCollector( p_playlist, VLC_TRUE );
373
374
375
376
377
378
                return;
             }
             playlist_PlayItem( p_playlist, p_item );
         }
         else
         {
379
380
            const vlc_bool_t b_gc_forced = p_playlist->status.i_status != PLAYLIST_STOPPED;

381
382
383
384
385
386
387
388
            p_playlist->status.i_status = PLAYLIST_STOPPED;
            if( p_playlist->status.p_item &&
                p_playlist->status.p_item->i_flags & PLAYLIST_REMOVE_FLAG )
            {
                PL_DEBUG( "deleting item marked for deletion" );
                playlist_ItemDelete( p_playlist->status.p_item );
                p_playlist->status.p_item = NULL;
            }
389

390
391
            /* Collect garbage */
            PL_UNLOCK;
392
            ObjectGarbageCollector( p_playlist, b_gc_forced );
393
394
            PL_LOCK;
        }
395
    }
396
    PL_UNLOCK;
397
398
}

399
400
static void ML_Decref( playlist_item_t *p_node )
{
401
402
    vlc_gc_decref( p_node->p_input );

403
404
405
406
407
    int i;
    if( p_node->i_children > 0 )
        for( i = 0 ; i < p_node->i_children ; i++ )
            ML_Decref( p_node->pp_children[i] );
}
ivoire's avatar
ivoire committed
408
409
410
411
412
413
414
415

/**
 * Last loop
 *
 * The playlist is dying so do the last loop
 * \param p_playlist the playlist object
 * \return nothing
*/
416
417
418
419
420
421
422
void playlist_LastLoop( playlist_t *p_playlist )
{
    vlc_object_t *p_obj;

    /* If there is an input, kill it */
    while( 1 )
    {
423
        PL_LOCK;
424
425
        if( p_playlist->p_input == NULL )
        {
426
            PL_UNLOCK;
427
428
429
430
431
432
433
434
435
436
            break;
        }

        if( p_playlist->p_input->b_dead )
        {
            input_thread_t *p_input;

            /* Unlink current input */
            p_input = p_playlist->p_input;
            p_playlist->p_input = NULL;
437
            PL_UNLOCK;
438
439

            /* Destroy input */
440
            vlc_object_release( p_input );
441
442
443
444
445
446
447
448
449
450
            continue;
        }
        else if( p_playlist->p_input->b_die )
        {
            /* This input is dying, leave it alone */
            ;
        }
        else if( p_playlist->p_input->b_error || p_playlist->p_input->b_eof )
        {
            input_StopThread( p_playlist->p_input );
451
            PL_UNLOCK;
452
453
454
455
456
457
            continue;
        }
        else
        {
            p_playlist->p_input->b_eof = 1;
        }
458
        PL_UNLOCK;
459
460
461
462
463
464
465
466

        msleep( INTF_IDLE_SLEEP );
    }

    /* close all remaining sout */
    while( ( p_obj = vlc_object_find( p_playlist,
                                      VLC_OBJECT_SOUT, FIND_CHILD ) ) )
    {
467
        vlc_object_detach( p_obj );
468
469
470
471
472
473
474
475
476
477
478
479
        vlc_object_release( p_obj );
        sout_DeleteInstance( (sout_instance_t*)p_obj );
    }

    /* close all remaining vout */
    while( ( p_obj = vlc_object_find( p_playlist,
                                      VLC_OBJECT_VOUT, FIND_CHILD ) ) )
    {
        vlc_object_detach( p_obj );
        vlc_object_release( p_obj );
        vout_Destroy( (vout_thread_t *)p_obj );
    }
480

481
    while( p_playlist->i_sds )
482
483
    {
        playlist_ServicesDiscoveryRemove( p_playlist,
484
                                          p_playlist->pp_sds[0]->p_sd->psz_module );
485
486
    }

487
    vlc_gc_incref( p_playlist->p_ml_category->p_input );
488
    playlist_MLDump( p_playlist );
489
490
    /* We don't need the media library anymore */
    ML_Decref( p_playlist->p_ml_category );
491
492
493
494

    PL_LOCK;
    FOREACH_ARRAY( playlist_item_t *p_del, p_playlist->all_items )
        free( p_del->pp_children );
Rafaël Carré's avatar
Rafaël Carré committed
495
        vlc_gc_decref( p_del->p_input );
496
497
498
499
500
501
502
503
        free( p_del );
    FOREACH_END();
    ARRAY_RESET( p_playlist->all_items );

    ARRAY_RESET( p_playlist->items );
    ARRAY_RESET( p_playlist->current );

    PL_UNLOCK;
504
505
}

ivoire's avatar
ivoire committed
506
507
508
509
510
511
512
/**
 * Preparse loop
 *
 * Main loop for preparser queue
 * \param p_obj items to preparse
 * \return nothing
 */
513
514
515
void playlist_PreparseLoop( playlist_preparse_t *p_obj )
{
    playlist_t *p_playlist = (playlist_t *)p_obj->p_parent;
516
    input_item_t *p_current;
517
    int i_activity;
zorglub's avatar
zorglub committed
518
    uint32_t i_m, i_o;
519

520
    while( !p_playlist->b_die )
521
    {
522
        vlc_object_lock( p_obj );
523
524
        while( p_obj->i_waiting == 0 )
        {
525
            if( vlc_object_wait( p_obj ) || p_playlist->b_die )
526
            {
527
                vlc_object_unlock( p_obj );
528
529
530
                return;
            }
        }
531

532
        p_current = p_obj->pp_waiting[0];
533
        REMOVE_ELEM( p_obj->pp_waiting, p_obj->i_waiting, 0 );
534
        vlc_object_unlock( p_obj );
535

zorglub's avatar
zorglub committed
536
        PL_LOCK;
537
538
        if( p_current )
        {
539
            if( p_current->i_type == ITEM_TYPE_FILE )
540
541
542
            {
                stats_TimerStart( p_playlist, "Preparse run",
                                  STATS_TIMER_PREPARSE );
543
                /* Do not preparse if it is already done (like by playing it) */
544
                if( !input_item_IsPreparsed( p_current ) )
545
546
547
548
549
                {
                    PL_UNLOCK;
                    input_Preparse( p_playlist, p_current );
                    PL_LOCK;
                }
550
                stats_TimerStop( p_playlist, STATS_TIMER_PREPARSE );
551
                PL_UNLOCK;
552
                input_item_SetPreparsed( p_current, VLC_TRUE );
zorglub's avatar
zorglub committed
553
                var_SetInteger( p_playlist, "item-change", p_current->i_id );
554
                PL_LOCK;
555
            }
zorglub's avatar
zorglub committed
556
            /* If we haven't retrieved enough meta, add to secondary queue
557
558
559
             * which will run the "meta fetchers".
             * This only checks for meta, not for art
             * \todo don't do this for things we won't get meta for, like vids
560
             */
Rafaël Carré's avatar
Rafaël Carré committed
561
562
            char *psz_arturl = input_item_GetArtURL( p_current );
            char *psz_name = input_item_GetName( p_current );
563
            if( !input_MetaSatisfied( p_playlist, p_current, &i_m, &i_o ) )
zorglub's avatar
zorglub committed
564
565
            {
                preparse_item_t p;
ivoire's avatar
ivoire committed
566
                PL_DEBUG( "need to fetch meta for %s", p_current->psz_name );
dionoea's avatar
dionoea committed
567
568
                p.p_item = p_current;
                p.b_fetch_art = VLC_FALSE;
569
                vlc_object_lock( p_playlist->p_fetcher );
570
571
                INSERT_ELEM( p_playlist->p_fetcher->p_waiting,
                             p_playlist->p_fetcher->i_waiting,
572
                             p_playlist->p_fetcher->i_waiting, p);
573
574
                vlc_object_signal_unlocked( p_playlist->p_fetcher );
                vlc_object_unlock( p_playlist->p_fetcher );
575
576
            }
            /* We already have all needed meta, but we need art right now */
577
            else if( p_playlist->p_fetcher->i_art_policy == ALBUM_ART_ALL &&
578
                        ( !psz_arturl || strncmp( psz_arturl, "file://", 7 ) ) )
579
580
            {
                preparse_item_t p;
Rafaël Carré's avatar
Rafaël Carré committed
581
                PL_DEBUG("meta ok for %s, need to fetch art", psz_name );
582
583
                p.p_item = p_current;
                p.b_fetch_art = VLC_TRUE;
584
                vlc_object_lock( p_playlist->p_fetcher );
585
586
587
                INSERT_ELEM( p_playlist->p_fetcher->p_waiting,
                             p_playlist->p_fetcher->i_waiting,
                             p_playlist->p_fetcher->i_waiting, p);
588
589
                vlc_object_signal_unlocked( p_playlist->p_fetcher );
                vlc_object_unlock( p_playlist->p_fetcher );
zorglub's avatar
zorglub committed
590
            }
591
            else
592
593
            {
                PL_DEBUG( "no fetch required for %s (art currently %s)",
Rafaël Carré's avatar
Rafaël Carré committed
594
                          psz_name, psz_arturl );
zorglub's avatar
zorglub committed
595
                vlc_gc_decref( p_current );
596
            }
Rafaël Carré's avatar
Rafaël Carré committed
597
598
            free( psz_name );
            free( psz_arturl );
599
            PL_UNLOCK;
600
601
        }
        else
602
603
            PL_UNLOCK;

604
        vlc_object_lock( p_obj );
605
        i_activity = var_GetInteger( p_playlist, "activity" );
606
        if( i_activity < 0 ) i_activity = 0;
607
        vlc_object_unlock( p_obj );
608
        /* Sleep at least 1ms */
609
610
611
612
        msleep( (i_activity+1) * 1000 );
    }
}

ivoire's avatar
ivoire committed
613
614
615
616
617
618
619
/**
 * Fetcher loop
 *
 * Main loop for secondary preparser queue
 * \param p_obj items to preparse
 * \return nothing
 */
620
void playlist_FetcherLoop( playlist_fetcher_t *p_obj )
621
622
{
    playlist_t *p_playlist = (playlist_t *)p_obj->p_parent;
623
624
625
    vlc_bool_t b_fetch_art;
    input_item_t *p_item;
    int i_activity;
626

627
    while( !p_playlist->b_die )
628
    {
629
630
631
632
633
634
635
636
637
638
639
640
641
        vlc_mutex_lock( &p_obj->object_lock );
        while( p_obj->i_waiting == 0 )
        {
            vlc_cond_wait( &p_obj->object_wait, &p_obj->object_lock );
            if( p_playlist->b_die )
            {
                vlc_mutex_unlock( &p_obj->object_lock );
                return;
            }
        }

        b_fetch_art = p_obj->p_waiting->b_fetch_art;
        p_item = p_obj->p_waiting->p_item;
dionoea's avatar
dionoea committed
642
        REMOVE_ELEM( p_obj->p_waiting, p_obj->i_waiting, 0 );
643
        vlc_mutex_unlock( &p_obj->object_lock );
dionoea's avatar
dionoea committed
644
        if( p_item )
645
        {
zorglub's avatar
zorglub committed
646
647
            if( !b_fetch_art )
            {
ivoire's avatar
ivoire committed
648
                /* If the user doesn't want us to fetch meta automatically
649
650
651
652
653
654
655
                 * abort here. */
                if( p_playlist->p_fetcher->b_fetch_meta )
                {
                    input_MetaFetch( p_playlist, p_item );
                    var_SetInteger( p_playlist, "item-change", p_item->i_id );
                }

656
                /*  Fetch right now */
657
                if( p_playlist->p_fetcher->i_art_policy == ALBUM_ART_ALL )
658
659
660
661
662
                {
                    vlc_mutex_lock( &p_obj->object_lock );
                    preparse_item_t p;
                    p.p_item = p_item;
                    p.b_fetch_art = VLC_TRUE;
663
664
                    INSERT_ELEM( p_playlist->p_fetcher->p_waiting,
                                 p_playlist->p_fetcher->i_waiting,
665
                                 0, p );
ivoire's avatar
ivoire committed
666
                    PL_DEBUG( "meta fetched for %s, get art", p_item->psz_name );
667
                    vlc_mutex_unlock( &p_obj->object_lock );
668
                    continue;
669
670
671
                }
                else
                    vlc_gc_decref( p_item );
zorglub's avatar
zorglub committed
672
673
            }
            else
dionoea's avatar
dionoea committed
674
            {
675
676
677
678
679
                int i_ret;

                /* Check if it is not yet preparsed and if so wait for it (at most 0.5s)
                 * (This can happen if we fetch art on play)
                 * FIXME this doesn't work if we need to fetch meta before art ... */
680
                for( i_ret = 0; i_ret < 10 && !input_item_IsPreparsed( p_item ); i_ret++ )
681
682
683
684
685
686
687
688
689
690
                {
                    vlc_bool_t b_break;
                    PL_LOCK;
                    b_break = ( !p_playlist->p_input || input_GetItem(p_playlist->p_input) != p_item  ||
                                p_playlist->p_input->b_die || p_playlist->p_input->b_eof || p_playlist->p_input->b_error );
                    PL_UNLOCK;
                    if( b_break )
                        break;
                    msleep( 50000 );
                }
691

692
                i_ret = input_ArtFind( p_playlist, p_item );
693
694
                if( i_ret == 1 )
                {
ivoire's avatar
ivoire committed
695
                    PL_DEBUG( "downloading art for %s", p_item->psz_name );
696
                    if( input_DownloadAndCacheArt( p_playlist, p_item ) )
697
                        input_item_SetArtNotFound( p_item, VLC_TRUE );
698
                    else {
699
                        input_item_SetArtFetched( p_item, VLC_TRUE );
700
701
702
                        var_SetInteger( p_playlist, "item-change",
                                        p_item->i_id );
                    }
703
704
705
                }
                else if( i_ret == 0 ) /* Was in cache */
                {
ivoire's avatar
ivoire committed
706
                    PL_DEBUG( "found art for %s in cache", p_item->psz_name );
707
                    input_item_SetArtFetched( p_item, VLC_TRUE );
708
                    var_SetInteger( p_playlist, "item-change", p_item->i_id );
709
710
711
                }
                else
                {
ivoire's avatar
ivoire committed
712
                    PL_DEBUG( "art not found for %s", p_item->psz_name );
713
                    input_item_SetArtNotFound( p_item, VLC_TRUE );
714
                }
715
716
                vlc_gc_decref( p_item );
           }
717
        }
718
        vlc_object_lock( p_obj );
719
720
        i_activity = var_GetInteger( p_playlist, "activity" );
        if( i_activity < 0 ) i_activity = 0;
721
        vlc_object_unlock( p_obj );
722
723
        /* Sleep at least 1ms */
        msleep( (i_activity+1) * 1000 );
724
725
726
    }
}

727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
static void VariablesInit( playlist_t *p_playlist )
{
    vlc_value_t val;
    /* These variables control updates */
    var_Create( p_playlist, "intf-change", VLC_VAR_BOOL );
    val.b_bool = VLC_TRUE;
    var_Set( p_playlist, "intf-change", val );

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

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

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

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

    var_Create( p_playlist, "intf-popupmenu", VLC_VAR_BOOL );

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

    var_Create( p_playlist, "activity", VLC_VAR_INTEGER );
    var_SetInteger( p_playlist, "activity", 0 );

    /* Variables to control playback */
    var_CreateGetBool( p_playlist, "play-and-stop" );
760
    var_CreateGetBool( p_playlist, "play-and-exit" );
761
762
763
    var_CreateGetBool( p_playlist, "random" );
    var_CreateGetBool( p_playlist, "repeat" );
    var_CreateGetBool( p_playlist, "loop" );
zorglub's avatar
zorglub committed
764
765

    var_AddCallback( p_playlist, "random", RandomCallback, NULL );
766
}