engine.c 24.8 KB
Newer Older
1 2 3
/*****************************************************************************
 * engine.c : Run the playlist and handle its control
 *****************************************************************************
4
 * Copyright (C) 1999-2007 the VideoLAN team
Antoine Cellerier's avatar
Antoine Cellerier 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>
Clément Stenac's avatar
Clément Stenac 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"
Clément Stenac's avatar
Clément Stenac committed
35
#include "stream_output/stream_output.h" /* sout_DeleteInstance */
36 37 38 39 40 41

/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
static void VariablesInit( playlist_t *p_playlist );

Clément Stenac's avatar
Clément Stenac committed
42 43 44
static int RandomCallback( vlc_object_t *p_this, char const *psz_cmd,
                           vlc_value_t oldval, vlc_value_t newval, void *a )
{
45 46
    (void)psz_cmd; (void)oldval; (void)newval; (void)a;

Clément Stenac's avatar
Clément Stenac committed
47
    ((playlist_t*)p_this)->b_reset_currently_playing = VLC_TRUE;
48
    playlist_Signal( ((playlist_t*)p_this) );
Clément Stenac's avatar
Clément Stenac committed
49 50 51
    return VLC_SUCCESS;
}

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

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

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

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

    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;

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

Clément Stenac's avatar
Clément Stenac committed
88 89 90 91
    ARRAY_INIT( p_playlist->items );
    ARRAY_INIT( p_playlist->all_items );
    ARRAY_INIT( p_playlist->input_items );
    ARRAY_INIT( p_playlist->current );
92

Clément Stenac's avatar
Clément Stenac committed
93 94
    p_playlist->i_current_index = 0;
    p_playlist->b_reset_currently_playing = VLC_TRUE;
95
    p_playlist->last_rebuild_date = 0;
Clément Stenac's avatar
Clément Stenac committed
96

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

101 102
    p_playlist->b_doing_ml = VLC_FALSE;

103
    p_playlist->b_auto_preparse =
104
                        var_CreateGetBool( p_playlist, "auto-preparse" ) ;
105

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

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

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

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

122 123 124 125 126
    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;

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

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

136 137 138 139 140 141 142
        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;
    }
143 144 145

    /* Initial status */
    p_playlist->status.p_item = NULL;
146
    p_playlist->status.p_node = p_playlist->p_local_onelevel;
147 148 149 150 151 152
    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;

153 154
    b_save = p_playlist->b_auto_preparse;
    p_playlist->b_auto_preparse = VLC_FALSE;
155
    playlist_MLLoad( p_playlist );
156
    p_playlist->b_auto_preparse = VLC_TRUE;
157 158 159
    return p_playlist;
}

160 161 162 163 164 165 166
/**
 * Destroy playlist
 *
 * Destroy a playlist structure.
 * \param p_playlist the playlist object
 * \return nothing
 */
167 168 169 170 171
void playlist_Destroy( playlist_t *p_playlist )
{
    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
172
    var_Destroy( p_playlist, "intf-popupmenu" );
173 174
    var_Destroy( p_playlist, "intf-show" );
    var_Destroy( p_playlist, "play-and-stop" );
175
    var_Destroy( p_playlist, "play-and-exit" );
176 177 178 179 180 181
    var_Destroy( p_playlist, "random" );
    var_Destroy( p_playlist, "repeat" );
    var_Destroy( p_playlist, "loop" );
    var_Destroy( p_playlist, "activity" );

    vlc_mutex_destroy( &p_playlist->gc_lock );
182
    vlc_object_detach( p_playlist );
183
    vlc_object_release( p_playlist );
184
}
185

186
/* Destroy remaining objects */
187
static void ObjectGarbageCollector( playlist_t *p_playlist, vlc_bool_t b_force )
188 189 190
{
    vlc_object_t *p_obj;

191
    if( !b_force )
192
    {
193 194 195 196 197 198 199
        if( mdate() - p_playlist->gc_date < 1000000 )
        {
            p_playlist->b_cant_sleep = VLC_TRUE;
            return;
        }
        else if( p_playlist->gc_date == 0 )
            return;
200
    }
201

202 203 204
    vlc_mutex_lock( &p_playlist->gc_lock );
    while( ( p_obj = vlc_object_find( p_playlist, VLC_OBJECT_VOUT,
                                                  FIND_CHILD ) ) )
205
    {
206
        if( p_obj->p_parent != VLC_OBJECT(p_playlist) )
207 208 209 210 211 212 213 214
        {
            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 );
215
    }
216 217
    while( ( p_obj = vlc_object_find( p_playlist, VLC_OBJECT_SOUT,
                                                  FIND_CHILD ) ) )
218
    {
219
        if( p_obj->p_parent != VLC_OBJECT(p_playlist) )
220
        {
221 222
            vlc_object_release( p_obj );
            break;
223
        }
224 225
        msg_Dbg( p_playlist, "garbage collector destroying 1 sout" );
        vlc_object_detach( p_obj );
226 227
        vlc_object_release( p_obj );
        sout_DeleteInstance( (sout_instance_t*)p_obj );
228
    }
229 230
    p_playlist->b_cant_sleep = VLC_FALSE;
    vlc_mutex_unlock( &p_playlist->gc_lock );
231 232
}

233 234 235 236 237 238 239
/**
 * Main loop
 *
 * Main loop for the playlist
 * \param p_playlist the playlist object
 * \return nothing
 */
240 241 242
void playlist_MainLoop( playlist_t *p_playlist )
{
    playlist_item_t *p_item = NULL;
243
    vlc_bool_t b_playexit = var_GetBool( p_playlist, "play-and-exit" );
244
    PL_LOCK;
245

246 247
    if( p_playlist->b_reset_currently_playing &&
        mdate() - p_playlist->last_rebuild_date > 30000 ) // 30 ms
248
    {
249
        ResetCurrentlyPlaying( p_playlist, var_GetBool( p_playlist, "random" ),
250 251
                             p_playlist->status.p_item );
        p_playlist->last_rebuild_date = mdate();
252
    }
253

254
check_input:
255 256 257
    /* If there is an input, check that it doesn't need to die. */
    if( p_playlist->p_input )
    {
258 259 260 261 262 263
        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 );
        }

264 265 266 267 268
        /* This input is dead. Remove it ! */
        if( p_playlist->p_input->b_dead )
        {
            int i_activity;
            input_thread_t *p_input;
269
            PL_DEBUG( "dead input" );
270 271 272 273 274 275

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

            /* Release the playlist lock, because we may get stuck
             * in input_DestroyThread() for some time. */
276
            PL_UNLOCK;
277 278 279 280

            /* Destroy input */
            input_DestroyThread( p_input );

281 282 283 284
            PL_LOCK;

            p_playlist->gc_date = mdate();
            p_playlist->b_cant_sleep = VLC_TRUE;
285 286 287 288

            if( p_playlist->status.p_item->i_flags
                & PLAYLIST_REMOVE_FLAG )
            {
289 290
                 PL_DEBUG( "%s was marked for deletion, deleting",
                                 PLI_NAME( p_playlist->status.p_item  ) );
291
                 playlist_ItemDelete( p_playlist->status.p_item );
292 293
                 if( p_playlist->request.p_item == p_playlist->status.p_item )
                     p_playlist->request.p_item = NULL;
294 295 296
                 p_playlist->status.p_item = NULL;
            }

297
            i_activity= var_GetInteger( p_playlist, "activity" );
298 299
            var_SetInteger( p_playlist, "activity", i_activity -
                            DEFAULT_INPUT_ACTIVITY );
300
            goto check_input;
301 302 303 304
        }
        /* This input is dying, let it do */
        else if( p_playlist->p_input->b_die )
        {
305
            PL_DEBUG( "dying input" );
306
            PL_UNLOCK;
307
            msleep( INTF_IDLE_SLEEP );
308
            PL_LOCK;
309
            goto check_input;
310 311 312 313 314
        }
        /* This input has finished, ask it to die ! */
        else if( p_playlist->p_input->b_error
                  || p_playlist->p_input->b_eof )
        {
315
            PL_DEBUG( "finished input" );
316
            input_StopThread( p_playlist->p_input );
317 318
            /* No need to wait here, we'll wait in the p_input->b_die case */
            goto check_input;
319 320 321
        }
        else if( p_playlist->p_input->i_state != INIT_S )
        {
322
            PL_UNLOCK;
323
            ObjectGarbageCollector( p_playlist, VLC_FALSE );
324
            PL_LOCK;
325 326 327 328 329 330 331 332 333
        }
    }
    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
334 335 336 337 338
        */
        if( p_playlist->request.i_status != PLAYLIST_STOPPED )
        {
            msg_Dbg( p_playlist, "starting new item" );
            p_item = playlist_NextItem( p_playlist );
339

340 341
            if( p_item == NULL )
            {
342
                msg_Dbg( p_playlist, "nothing to play" );
343
                p_playlist->status.i_status = PLAYLIST_STOPPED;
344
                PL_UNLOCK;
345

346 347 348
                if( b_playexit == VLC_TRUE )
                {
                    msg_Info( p_playlist, "end of playlist, exiting" );
349
                    vlc_object_kill( p_playlist->p_libvlc );
350
                }
351
                ObjectGarbageCollector( p_playlist, VLC_TRUE );
352 353 354 355 356 357
                return;
             }
             playlist_PlayItem( p_playlist, p_item );
         }
         else
         {
358 359
            const vlc_bool_t b_gc_forced = p_playlist->status.i_status != PLAYLIST_STOPPED;

360 361 362 363 364 365 366 367
            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;
            }
368

369 370
            /* Collect garbage */
            PL_UNLOCK;
371
            ObjectGarbageCollector( p_playlist, b_gc_forced );
372 373
            PL_LOCK;
        }
374
    }
375
    PL_UNLOCK;
376 377
}

378 379 380 381 382 383 384 385

/**
 * Last loop
 *
 * The playlist is dying so do the last loop
 * \param p_playlist the playlist object
 * \return nothing
*/
386 387 388 389 390 391 392
void playlist_LastLoop( playlist_t *p_playlist )
{
    vlc_object_t *p_obj;

    /* If there is an input, kill it */
    while( 1 )
    {
393
        PL_LOCK;
394 395
        if( p_playlist->p_input == NULL )
        {
396
            PL_UNLOCK;
397 398 399 400 401 402 403 404 405 406
            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;
407
            PL_UNLOCK;
408 409 410 411 412 413 414 415 416 417 418 419 420

            /* Destroy input */
            input_DestroyThread( p_input );
            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 );
421
            PL_UNLOCK;
422 423 424 425 426 427
            continue;
        }
        else
        {
            p_playlist->p_input->b_eof = 1;
        }
428
        PL_UNLOCK;
429 430 431 432 433 434 435 436

        msleep( INTF_IDLE_SLEEP );
    }

    /* close all remaining sout */
    while( ( p_obj = vlc_object_find( p_playlist,
                                      VLC_OBJECT_SOUT, FIND_CHILD ) ) )
    {
437
        vlc_object_detach( p_obj );
438 439 440 441 442 443 444 445 446 447 448 449
        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 );
    }
450

451
    while( p_playlist->i_sds )
452 453
    {
        playlist_ServicesDiscoveryRemove( p_playlist,
454
                                          p_playlist->pp_sds[0]->p_sd->psz_module );
455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478
    }

    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;
479 480
}

481 482 483 484 485 486 487
/**
 * Preparse loop
 *
 * Main loop for preparser queue
 * \param p_obj items to preparse
 * \return nothing
 */
488 489 490
void playlist_PreparseLoop( playlist_preparse_t *p_obj )
{
    playlist_t *p_playlist = (playlist_t *)p_obj->p_parent;
491
    input_item_t *p_current;
492
    int i_activity;
Clément Stenac's avatar
Clément Stenac committed
493
    uint32_t i_m, i_o;
494

495
    while( !p_playlist->b_die )
496
    {
497 498 499 500 501 502 503 504 505 506
        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;
            }
        }
507

508
        p_current = p_obj->pp_waiting[0];
509 510
        REMOVE_ELEM( p_obj->pp_waiting, p_obj->i_waiting, 0 );
        vlc_mutex_unlock( &p_obj->object_lock );
511

Clément Stenac's avatar
Clément Stenac committed
512
        PL_LOCK;
513 514
        if( p_current )
        {
515
            if( p_current->i_type == ITEM_TYPE_FILE )
516 517 518
            {
                stats_TimerStart( p_playlist, "Preparse run",
                                  STATS_TIMER_PREPARSE );
519
                /* Do not preparse if it is already done (like by playing it) */
520
                if( !input_item_IsPreparsed( p_current ) )
521 522 523 524 525
                {
                    PL_UNLOCK;
                    input_Preparse( p_playlist, p_current );
                    PL_LOCK;
                }
526
                stats_TimerStop( p_playlist, STATS_TIMER_PREPARSE );
527
                PL_UNLOCK;
528
                input_item_SetPreparsed( p_current, VLC_TRUE );
529
                var_SetInteger( p_playlist, "item-change", p_current->i_id );
530
                PL_LOCK;
531
            }
Clément Stenac's avatar
Clément Stenac committed
532
            /* If we haven't retrieved enough meta, add to secondary queue
533 534 535
             * 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
536
             */
537 538
            char *psz_arturl = input_item_GetArtURL( p_current );
            char *psz_name = input_item_GetName( p_current );
539
            if( !input_MetaSatisfied( p_playlist, p_current, &i_m, &i_o ) )
Clément Stenac's avatar
Clément Stenac committed
540 541
            {
                preparse_item_t p;
542
                PL_DEBUG( "need to fetch meta for %s", p_current->psz_name );
543 544
                p.p_item = p_current;
                p.b_fetch_art = VLC_FALSE;
545 546 547
                vlc_mutex_lock( &p_playlist->p_fetcher->object_lock );
                INSERT_ELEM( p_playlist->p_fetcher->p_waiting,
                             p_playlist->p_fetcher->i_waiting,
548
                             p_playlist->p_fetcher->i_waiting, p);
549
                vlc_cond_signal( &p_playlist->p_fetcher->object_wait );
550
                vlc_mutex_unlock( &p_playlist->p_fetcher->object_lock );
551 552
            }
            /* We already have all needed meta, but we need art right now */
553
            else if( p_playlist->p_fetcher->i_art_policy == ALBUM_ART_ALL &&
554
                        ( !psz_arturl || strncmp( psz_arturl, "file://", 7 ) ) )
555 556
            {
                preparse_item_t p;
557
                PL_DEBUG("meta ok for %s, need to fetch art", psz_name );
558 559 560
                p.p_item = p_current;
                p.b_fetch_art = VLC_TRUE;
                vlc_mutex_lock( &p_playlist->p_fetcher->object_lock );
561 562 563
                INSERT_ELEM( p_playlist->p_fetcher->p_waiting,
                             p_playlist->p_fetcher->i_waiting,
                             p_playlist->p_fetcher->i_waiting, p);
564
                vlc_cond_signal( &p_playlist->p_fetcher->object_wait );
565
                vlc_mutex_unlock( &p_playlist->p_fetcher->object_lock );
Clément Stenac's avatar
Clément Stenac committed
566
            }
567
            else
568 569
            {
                PL_DEBUG( "no fetch required for %s (art currently %s)",
570
                          psz_name, psz_arturl );
Clément Stenac's avatar
Clément Stenac committed
571
                vlc_gc_decref( p_current );
572
            }
573 574
            free( psz_name );
            free( psz_arturl );
575
            PL_UNLOCK;
576 577
        }
        else
578 579
            PL_UNLOCK;

580
        vlc_mutex_lock( &p_obj->object_lock );
581
        i_activity = var_GetInteger( p_playlist, "activity" );
582
        if( i_activity < 0 ) i_activity = 0;
583
        vlc_mutex_unlock( &p_obj->object_lock );
584
        /* Sleep at least 1ms */
585 586 587 588
        msleep( (i_activity+1) * 1000 );
    }
}

589 590 591 592 593 594 595
/**
 * Fetcher loop
 *
 * Main loop for secondary preparser queue
 * \param p_obj items to preparse
 * \return nothing
 */
596
void playlist_FetcherLoop( playlist_fetcher_t *p_obj )
597 598
{
    playlist_t *p_playlist = (playlist_t *)p_obj->p_parent;
599 600 601
    vlc_bool_t b_fetch_art;
    input_item_t *p_item;
    int i_activity;
602

603
    while( !p_playlist->b_die )
604
    {
605 606 607 608 609 610 611 612 613 614 615 616 617
        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;
618
        REMOVE_ELEM( p_obj->p_waiting, p_obj->i_waiting, 0 );
619
        vlc_mutex_unlock( &p_obj->object_lock );
620
        if( p_item )
621
        {
Clément Stenac's avatar
Clément Stenac committed
622 623
            if( !b_fetch_art )
            {
624
                /* If the user doesn't want us to fetch meta automatically
625 626 627 628 629 630 631
                 * 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 );
                }

632
                /*  Fetch right now */
633
                if( p_playlist->p_fetcher->i_art_policy == ALBUM_ART_ALL )
634 635 636 637 638
                {
                    vlc_mutex_lock( &p_obj->object_lock );
                    preparse_item_t p;
                    p.p_item = p_item;
                    p.b_fetch_art = VLC_TRUE;
639 640
                    INSERT_ELEM( p_playlist->p_fetcher->p_waiting,
                                 p_playlist->p_fetcher->i_waiting,
641
                                 0, p );
642
                    PL_DEBUG( "meta fetched for %s, get art", p_item->psz_name );
643
                    vlc_mutex_unlock( &p_obj->object_lock );
644
                    continue;
645 646 647
                }
                else
                    vlc_gc_decref( p_item );
Clément Stenac's avatar
Clément Stenac committed
648 649
            }
            else
650
            {
651 652 653 654 655
                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 ... */
656
                for( i_ret = 0; i_ret < 10 && !input_item_IsPreparsed( p_item ); i_ret++ )
657 658 659 660 661 662 663 664 665 666
                {
                    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 );
                }
667

668
                i_ret = input_ArtFind( p_playlist, p_item );
669 670
                if( i_ret == 1 )
                {
671
                    PL_DEBUG( "downloading art for %s", p_item->psz_name );
672
                    if( input_DownloadAndCacheArt( p_playlist, p_item ) )
673
                        input_item_SetArtNotFound( p_item, VLC_TRUE );
674
                    else {
675
                        input_item_SetArtFetched( p_item, VLC_TRUE );
676 677 678
                        var_SetInteger( p_playlist, "item-change",
                                        p_item->i_id );
                    }
679 680 681
                }
                else if( i_ret == 0 ) /* Was in cache */
                {
682
                    PL_DEBUG( "found art for %s in cache", p_item->psz_name );
683
                    input_item_SetArtFetched( p_item, VLC_TRUE );
684
                    var_SetInteger( p_playlist, "item-change", p_item->i_id );
685 686 687
                }
                else
                {
688
                    PL_DEBUG( "art not found for %s", p_item->psz_name );
689
                    input_item_SetArtNotFound( p_item, VLC_TRUE );
690
                }
691 692
                vlc_gc_decref( p_item );
           }
693
        }
694 695 696 697 698 699
        vlc_mutex_lock( &p_obj->object_lock );
        i_activity = var_GetInteger( p_playlist, "activity" );
        if( i_activity < 0 ) i_activity = 0;
        vlc_mutex_unlock( &p_obj->object_lock );
        /* Sleep at least 1ms */
        msleep( (i_activity+1) * 1000 );
700 701 702
    }
}

703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735
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" );
736
    var_CreateGetBool( p_playlist, "play-and-exit" );
737 738 739
    var_CreateGetBool( p_playlist, "random" );
    var_CreateGetBool( p_playlist, "repeat" );
    var_CreateGetBool( p_playlist, "loop" );
Clément Stenac's avatar
Clément Stenac committed
740 741

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