engine.c 25.2 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
62
/**
 * 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 )
{
    playlist_t *p_playlist;
63
    vlc_bool_t b_save;
64
    int i_tree;
65
66
67
68
69
70
71
72

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

74
    TAB_INIT( p_playlist->i_sds, p_playlist->pp_sds );
75

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

    VariablesInit( p_playlist );

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

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

zorglub's avatar
zorglub committed
89
90
91
92
    ARRAY_INIT( p_playlist->items );
    ARRAY_INIT( p_playlist->all_items );
    ARRAY_INIT( p_playlist->input_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
207
208
209
210
211
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 );
    }

    // Stats
    vlc_mutex_destroy( &p_playlist->p_stats->lock );
    if( p_playlist->p_stats )
        free( p_playlist->p_stats );
}

212
/* Destroy remaining objects */
213
static void ObjectGarbageCollector( playlist_t *p_playlist, vlc_bool_t b_force )
214
215
216
{
    vlc_object_t *p_obj;

217
    if( !b_force )
218
    {
219
220
221
222
223
224
225
        if( mdate() - p_playlist->gc_date < 1000000 )
        {
            p_playlist->b_cant_sleep = VLC_TRUE;
            return;
        }
        else if( p_playlist->gc_date == 0 )
            return;
226
    }
227

228
229
230
    vlc_mutex_lock( &p_playlist->gc_lock );
    while( ( p_obj = vlc_object_find( p_playlist, VLC_OBJECT_VOUT,
                                                  FIND_CHILD ) ) )
231
    {
232
        if( p_obj->p_parent != VLC_OBJECT(p_playlist) )
233
234
235
236
237
238
239
240
        {
            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 );
241
    }
242
243
    while( ( p_obj = vlc_object_find( p_playlist, VLC_OBJECT_SOUT,
                                                  FIND_CHILD ) ) )
244
    {
245
        if( p_obj->p_parent != VLC_OBJECT(p_playlist) )
246
        {
247
248
            vlc_object_release( p_obj );
            break;
249
        }
250
251
        msg_Dbg( p_playlist, "garbage collector destroying 1 sout" );
        vlc_object_detach( p_obj );
252
253
        vlc_object_release( p_obj );
        sout_DeleteInstance( (sout_instance_t*)p_obj );
254
    }
255
256
    p_playlist->b_cant_sleep = VLC_FALSE;
    vlc_mutex_unlock( &p_playlist->gc_lock );
257
258
}

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

272
273
    if( p_playlist->b_reset_currently_playing &&
        mdate() - p_playlist->last_rebuild_date > 30000 ) // 30 ms
274
    {
ivoire's avatar
ivoire committed
275
        ResetCurrentlyPlaying( p_playlist, var_GetBool( p_playlist, "random" ),
276
277
                             p_playlist->status.p_item );
        p_playlist->last_rebuild_date = mdate();
278
    }
279

280
check_input:
281
282
283
    /* If there is an input, check that it doesn't need to die. */
    if( p_playlist->p_input )
    {
284
285
286
287
288
289
        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 );
        }

290
291
292
293
294
        /* 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
295
            PL_DEBUG( "dead input" );
296
297
298
299
300

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

            /* Release the playlist lock, because we may get stuck
301
             * in vlc_object_release() for some time. */
302
            PL_UNLOCK;
303
304

            /* Destroy input */
305
            vlc_object_release( p_input );
306

307
308
309
310
            PL_LOCK;

            p_playlist->gc_date = mdate();
            p_playlist->b_cant_sleep = VLC_TRUE;
311
312
313
314

            if( p_playlist->status.p_item->i_flags
                & PLAYLIST_REMOVE_FLAG )
            {
zorglub's avatar
zorglub committed
315
316
                 PL_DEBUG( "%s was marked for deletion, deleting",
                                 PLI_NAME( p_playlist->status.p_item  ) );
317
                 playlist_ItemDelete( p_playlist->status.p_item );
318
319
                 if( p_playlist->request.p_item == p_playlist->status.p_item )
                     p_playlist->request.p_item = NULL;
320
321
322
                 p_playlist->status.p_item = NULL;
            }

ivoire's avatar
ivoire committed
323
            i_activity= var_GetInteger( p_playlist, "activity" );
324
325
            var_SetInteger( p_playlist, "activity", i_activity -
                            DEFAULT_INPUT_ACTIVITY );
326
            goto check_input;
327
328
329
330
        }
        /* This input is dying, let it do */
        else if( p_playlist->p_input->b_die )
        {
zorglub's avatar
zorglub committed
331
            PL_DEBUG( "dying input" );
332
            PL_UNLOCK;
333
            msleep( INTF_IDLE_SLEEP );
334
            PL_LOCK;
335
            goto check_input;
336
337
338
339
340
        }
        /* 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
341
            PL_DEBUG( "finished input" );
342
            input_StopThread( p_playlist->p_input );
343
344
            /* No need to wait here, we'll wait in the p_input->b_die case */
            goto check_input;
345
346
347
        }
        else if( p_playlist->p_input->i_state != INIT_S )
        {
zorglub's avatar
zorglub committed
348
            PL_UNLOCK;
349
            ObjectGarbageCollector( p_playlist, VLC_FALSE );
350
            PL_LOCK;
351
352
353
354
355
356
357
358
359
        }
    }
    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
360
361
362
363
364
        */
        if( p_playlist->request.i_status != PLAYLIST_STOPPED )
        {
            msg_Dbg( p_playlist, "starting new item" );
            p_item = playlist_NextItem( p_playlist );
365

ivoire's avatar
ivoire committed
366
367
            if( p_item == NULL )
            {
368
                msg_Dbg( p_playlist, "nothing to play" );
369
                p_playlist->status.i_status = PLAYLIST_STOPPED;
370
                PL_UNLOCK;
371

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

386
387
388
389
390
391
392
393
            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;
            }
394

395
396
            /* Collect garbage */
            PL_UNLOCK;
397
            ObjectGarbageCollector( p_playlist, b_gc_forced );
398
399
            PL_LOCK;
        }
400
    }
401
    PL_UNLOCK;
402
403
}

ivoire's avatar
ivoire committed
404
405
406
407
408
409
410
411

/**
 * Last loop
 *
 * The playlist is dying so do the last loop
 * \param p_playlist the playlist object
 * \return nothing
*/
412
413
414
415
416
417
418
void playlist_LastLoop( playlist_t *p_playlist )
{
    vlc_object_t *p_obj;

    /* If there is an input, kill it */
    while( 1 )
    {
419
        PL_LOCK;
420
421
        if( p_playlist->p_input == NULL )
        {
422
            PL_UNLOCK;
423
424
425
426
427
428
429
430
431
432
            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;
433
            PL_UNLOCK;
434
435

            /* Destroy input */
436
            vlc_object_release( p_input );
437
438
439
440
441
442
443
444
445
446
            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 );
447
            PL_UNLOCK;
448
449
450
451
452
453
            continue;
        }
        else
        {
            p_playlist->p_input->b_eof = 1;
        }
454
        PL_UNLOCK;
455
456
457
458
459
460
461
462

        msleep( INTF_IDLE_SLEEP );
    }

    /* close all remaining sout */
    while( ( p_obj = vlc_object_find( p_playlist,
                                      VLC_OBJECT_SOUT, FIND_CHILD ) ) )
    {
463
        vlc_object_detach( p_obj );
464
465
466
467
468
469
470
471
472
473
474
475
        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 );
    }
476

477
    while( p_playlist->i_sds )
478
479
    {
        playlist_ServicesDiscoveryRemove( p_playlist,
480
                                          p_playlist->pp_sds[0]->p_sd->psz_module );
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
    }

    playlist_MLDump( p_playlist );

    PL_LOCK;
    /* Go through all items, and simply free everything without caring
     * about the tree structure. Do not decref, it will be done by doing
     * the same thing on the input items array */
    FOREACH_ARRAY( playlist_item_t *p_del, p_playlist->all_items )
        free( p_del->pp_children );
        free( p_del );
    FOREACH_END();
    ARRAY_RESET( p_playlist->all_items );

    FOREACH_ARRAY( input_item_t *p_del, p_playlist->input_items )
        input_ItemClean( p_del );
        free( p_del );
    FOREACH_END();
    ARRAY_RESET( p_playlist->input_items );

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

    PL_UNLOCK;
505
506
}

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

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

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

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

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

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

628
    while( !p_playlist->b_die )
629
    {
630
631
632
633
634
635
636
637
638
639
640
641
642
        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
643
        REMOVE_ELEM( p_obj->p_waiting, p_obj->i_waiting, 0 );
644
        vlc_mutex_unlock( &p_obj->object_lock );
dionoea's avatar
dionoea committed
645
        if( p_item )
646
        {
zorglub's avatar
zorglub committed
647
648
            if( !b_fetch_art )
            {
ivoire's avatar
ivoire committed
649
                /* If the user doesn't want us to fetch meta automatically
650
651
652
653
654
655
656
                 * 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 );
                }

657
                /*  Fetch right now */
658
                if( p_playlist->p_fetcher->i_art_policy == ALBUM_ART_ALL )
659
660
661
662
663
                {
                    vlc_mutex_lock( &p_obj->object_lock );
                    preparse_item_t p;
                    p.p_item = p_item;
                    p.b_fetch_art = VLC_TRUE;
664
665
                    INSERT_ELEM( p_playlist->p_fetcher->p_waiting,
                                 p_playlist->p_fetcher->i_waiting,
666
                                 0, p );
ivoire's avatar
ivoire committed
667
                    PL_DEBUG( "meta fetched for %s, get art", p_item->psz_name );
668
                    vlc_mutex_unlock( &p_obj->object_lock );
669
                    continue;
670
671
672
                }
                else
                    vlc_gc_decref( p_item );
zorglub's avatar
zorglub committed
673
674
            }
            else
dionoea's avatar
dionoea committed
675
            {
676
677
678
679
680
                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 ... */
681
                for( i_ret = 0; i_ret < 10 && !input_item_IsPreparsed( p_item ); i_ret++ )
682
683
684
685
686
687
688
689
690
691
                {
                    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 );
                }
692

693
                i_ret = input_ArtFind( p_playlist, p_item );
694
695
                if( i_ret == 1 )
                {
ivoire's avatar
ivoire committed
696
                    PL_DEBUG( "downloading art for %s", p_item->psz_name );
697
                    if( input_DownloadAndCacheArt( p_playlist, p_item ) )
698
                        input_item_SetArtNotFound( p_item, VLC_TRUE );
699
                    else {
700
                        input_item_SetArtFetched( p_item, VLC_TRUE );
701
702
703
                        var_SetInteger( p_playlist, "item-change",
                                        p_item->i_id );
                    }
704
705
706
                }
                else if( i_ret == 0 ) /* Was in cache */
                {
ivoire's avatar
ivoire committed
707
                    PL_DEBUG( "found art for %s in cache", p_item->psz_name );
708
                    input_item_SetArtFetched( p_item, VLC_TRUE );
709
                    var_SetInteger( p_playlist, "item-change", p_item->i_id );
710
711
712
                }
                else
                {
ivoire's avatar
ivoire committed
713
                    PL_DEBUG( "art not found for %s", p_item->psz_name );
714
                    input_item_SetArtNotFound( p_item, VLC_TRUE );
715
                }
716
717
                vlc_gc_decref( p_item );
           }
718
        }
719
        vlc_object_lock( p_obj );
720
721
        i_activity = var_GetInteger( p_playlist, "activity" );
        if( i_activity < 0 ) i_activity = 0;
722
        vlc_object_unlock( p_obj );
723
724
        /* Sleep at least 1ms */
        msleep( (i_activity+1) * 1000 );
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
760
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" );
761
    var_CreateGetBool( p_playlist, "play-and-exit" );
762
763
764
    var_CreateGetBool( p_playlist, "random" );
    var_CreateGetBool( p_playlist, "repeat" );
    var_CreateGetBool( p_playlist, "loop" );
zorglub's avatar
zorglub committed
765
766

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