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
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
#include <vlc/vlc.h>
Clément Stenac's avatar
Clément Stenac committed
26 27 28 29
#include <vlc_vout.h>
#include <vlc_sout.h>
#include <vlc_playlist.h>
#include <vlc_interface.h>
30
#include "playlist_internal.h"
Clément Stenac's avatar
Clément Stenac committed
31
#include "stream_output/stream_output.h" /* sout_DeleteInstance */
32 33 34 35 36 37

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

Clément Stenac's avatar
Clément Stenac committed
38 39 40
static int RandomCallback( vlc_object_t *p_this, char const *psz_cmd,
                           vlc_value_t oldval, vlc_value_t newval, void *a )
{
41 42
    (void)psz_cmd; (void)oldval; (void)newval; (void)a;

Clément Stenac's avatar
Clément Stenac committed
43
    ((playlist_t*)p_this)->b_reset_currently_playing = VLC_TRUE;
44
    playlist_Signal( ((playlist_t*)p_this) );
Clément Stenac's avatar
Clément Stenac committed
45 46 47
    return VLC_SUCCESS;
}

48 49 50 51 52 53 54 55 56 57
/**
 * 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;
58
    vlc_bool_t b_save;
59
    int i_tree;
60 61 62 63 64 65 66 67

    /* Allocate structure */
    p_playlist = vlc_object_create( p_parent, VLC_OBJECT_PLAYLIST );
    if( !p_playlist )
    {
        msg_Err( p_parent, "out of memory" );
        return NULL;
    }
68
    p_parent->p_libvlc->p_playlist = p_playlist;
69 70 71 72 73 74 75 76 77

    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;

78 79
    p_playlist->gc_date = 0;
    p_playlist->b_cant_sleep = VLC_FALSE;
80

Clément Stenac's avatar
Clément Stenac committed
81 82 83 84
    ARRAY_INIT( p_playlist->items );
    ARRAY_INIT( p_playlist->all_items );
    ARRAY_INIT( p_playlist->input_items );
    ARRAY_INIT( p_playlist->current );
85

Clément Stenac's avatar
Clément Stenac committed
86 87
    p_playlist->i_current_index = 0;
    p_playlist->b_reset_currently_playing = VLC_TRUE;
88
    p_playlist->last_rebuild_date = 0;
Clément Stenac's avatar
Clément Stenac committed
89

90 91 92 93
    i_tree = var_CreateGetBool( p_playlist, "playlist-tree" );
    p_playlist->b_always_tree = (i_tree == 1);
    p_playlist->b_never_tree = (i_tree == 2);

94 95
    p_playlist->b_doing_ml = VLC_FALSE;

96 97 98
    p_playlist->b_auto_preparse =
                        var_CreateGetBool( p_playlist, "auto-preparse") ;

99 100 101 102
    p_playlist->p_root_category = playlist_NodeCreate( p_playlist, NULL, NULL,
                                                       0 );
    p_playlist->p_root_onelevel = playlist_NodeCreate( p_playlist, NULL, NULL,
                                                       0 );
103

104 105 106
    if( !p_playlist->p_root_category || !p_playlist->p_root_onelevel )
        return NULL;

107 108
    /* Create playlist and media library */
    p_playlist->p_local_category = playlist_NodeCreate( p_playlist,
109
                              _( "Playlist" ),p_playlist->p_root_category, 0 );
110
    p_playlist->p_local_onelevel =  playlist_NodeCreate( p_playlist,
111
                              _( "Playlist" ), p_playlist->p_root_onelevel, 0 );
112 113 114
    p_playlist->p_local_category->i_flags |= PLAYLIST_RO_FLAG;
    p_playlist->p_local_onelevel->i_flags |= PLAYLIST_RO_FLAG;

115 116 117 118 119
    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;

120
    /* Link the nodes together. Todo: actually create them from the same input*/
121 122
    p_playlist->p_local_onelevel->p_input->i_id =
        p_playlist->p_local_category->p_input->i_id;
123 124 125 126

    if( config_GetInt( p_playlist, "media-library") )
    {
        p_playlist->p_ml_category =   playlist_NodeCreate( p_playlist,
127
                         _( "Media Library" ), p_playlist->p_root_category, 0 );
128
        p_playlist->p_ml_onelevel =  playlist_NodeCreate( p_playlist,
129
                         _( "Media Library" ), p_playlist->p_root_onelevel, 0 );
130 131 132 133

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

134 135 136 137
        p_playlist->p_ml_category->i_flags |= PLAYLIST_RO_FLAG;
        p_playlist->p_ml_onelevel->i_flags |= PLAYLIST_RO_FLAG;
        p_playlist->p_ml_onelevel->p_input->i_id =
             p_playlist->p_ml_category->p_input->i_id;
138

139 140 141 142 143
    }
    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
    b_save = p_playlist->b_auto_preparse;
    p_playlist->b_auto_preparse = VLC_FALSE;
156
    playlist_MLLoad( p_playlist );
157
    p_playlist->b_auto_preparse = VLC_TRUE;
158 159 160 161 162 163 164 165 166 167 168
    return p_playlist;
}

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" );
    var_Destroy( p_playlist, "intf-popmenu" );
    var_Destroy( p_playlist, "intf-show" );
    var_Destroy( p_playlist, "play-and-stop" );
169
    var_Destroy( p_playlist, "play-and-exit" );
170 171 172 173 174 175
    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 );
176
    vlc_object_detach( p_playlist );
177 178
    vlc_object_destroy( p_playlist );
}
179

180
/* Destroy remaining objects */
181
static void ObjectGarbageCollector( playlist_t *p_playlist, vlc_bool_t b_force )
182 183 184
{
    vlc_object_t *p_obj;

185
    if( !b_force )
186
    {
187 188 189 190 191 192 193
        if( mdate() - p_playlist->gc_date < 1000000 )
        {
            p_playlist->b_cant_sleep = VLC_TRUE;
            return;
        }
        else if( p_playlist->gc_date == 0 )
            return;
194
    }
195

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

/** Main loop for the playlist */
void playlist_MainLoop( playlist_t *p_playlist )
{
    playlist_item_t *p_item = NULL;
231
    vlc_bool_t b_playexit = var_GetBool( p_playlist, "play-and-exit" );
232
    PL_LOCK;
233

234 235
    if( p_playlist->b_reset_currently_playing &&
        mdate() - p_playlist->last_rebuild_date > 30000 ) // 30 ms
236
    {
237 238 239
        ResetCurrentlyPlaying( p_playlist, var_GetBool( p_playlist, "random"),
                             p_playlist->status.p_item );
        p_playlist->last_rebuild_date = mdate();
240
    }
241

242
check_input:
243 244 245
    /* If there is an input, check that it doesn't need to die. */
    if( p_playlist->p_input )
    {
246 247 248 249 250 251
        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 );
        }

252 253 254 255 256
        /* This input is dead. Remove it ! */
        if( p_playlist->p_input->b_dead )
        {
            int i_activity;
            input_thread_t *p_input;
257
            PL_DEBUG( "dead input" );
258 259 260 261 262 263 264 265 266 267 268

            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. */
            PL_UNLOCK

            /* Destroy input */
            input_DestroyThread( p_input );

269 270 271 272
            PL_LOCK;

            p_playlist->gc_date = mdate();
            p_playlist->b_cant_sleep = VLC_TRUE;
273 274 275 276

            if( p_playlist->status.p_item->i_flags
                & PLAYLIST_REMOVE_FLAG )
            {
277 278
                 PL_DEBUG( "%s was marked for deletion, deleting",
                                 PLI_NAME( p_playlist->status.p_item  ) );
279
                 playlist_ItemDelete( p_playlist->status.p_item );
280 281
                 if( p_playlist->request.p_item == p_playlist->status.p_item )
                     p_playlist->request.p_item = NULL;
282 283 284 285 286 287
                 p_playlist->status.p_item = NULL;
            }

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

             if( p_item == NULL )
             {
                msg_Dbg( p_playlist, "nothing to play" );
334 335 336
                if( b_playexit == VLC_TRUE )
                {
                    msg_Info( p_playlist, "end of playlist, exiting" );
337
                    vlc_object_kill( p_playlist->p_libvlc );
338
                }
339 340
                p_playlist->status.i_status = PLAYLIST_STOPPED;
                PL_UNLOCK
341 342

                ObjectGarbageCollector( p_playlist, VLC_TRUE );
343 344 345 346 347 348
                return;
             }
             playlist_PlayItem( p_playlist, p_item );
         }
         else
         {
349 350
            const vlc_bool_t b_gc_forced = p_playlist->status.i_status != PLAYLIST_STOPPED;

351 352 353 354 355 356 357 358
            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;
            }
359

360 361
            /* Collect garbage */
            PL_UNLOCK;
362
            ObjectGarbageCollector( p_playlist, b_gc_forced );
363 364
            PL_LOCK;
        }
365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422
    }
    PL_UNLOCK
}

/** Playlist dying last loop */
void playlist_LastLoop( playlist_t *p_playlist )
{
    vlc_object_t *p_obj;

    /* If there is an input, kill it */
    while( 1 )
    {
        PL_LOCK

        if( p_playlist->p_input == NULL )
        {
            PL_UNLOCK
            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;
            PL_UNLOCK

            /* 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 );
            PL_UNLOCK
            continue;
        }
        else
        {
            p_playlist->p_input->b_eof = 1;
        }

        PL_UNLOCK

        msleep( INTF_IDLE_SLEEP );
    }

    /* close all remaining sout */
    while( ( p_obj = vlc_object_find( p_playlist,
                                      VLC_OBJECT_SOUT, FIND_CHILD ) ) )
    {
423
        vlc_object_detach( p_obj );
424 425 426 427 428 429 430 431 432 433 434 435
        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 );
    }
436

437
    while( p_playlist->i_asds )
438 439
    {
        playlist_ServicesDiscoveryRemove( p_playlist,
440
                                          p_playlist->pp_asds[0]->p_sd->psz_module );
441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464
    }

    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;
465 466 467 468 469 470
}

/** Main loop for preparser queue */
void playlist_PreparseLoop( playlist_preparse_t *p_obj )
{
    playlist_t *p_playlist = (playlist_t *)p_obj->p_parent;
471
    input_item_t *p_current;
472
    int i_activity;
Clément Stenac's avatar
Clément Stenac committed
473
    uint32_t i_m, i_o;
474

475
    while( !p_playlist->b_die )
476
    {
477 478 479 480 481 482 483 484 485 486 487 488
        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;
            }
        }

        p_current = p_obj->pp_waiting[0];
489 490
        REMOVE_ELEM( p_obj->pp_waiting, p_obj->i_waiting, 0 );
        vlc_mutex_unlock( &p_obj->object_lock );
491

Clément Stenac's avatar
Clément Stenac committed
492
        PL_LOCK;
493 494 495 496 497 498 499 500 501 502 503 504 505 506 507
        if( p_current )
        {
            vlc_bool_t b_preparsed = VLC_FALSE;
            if( strncmp( p_current->psz_uri, "http:", 5 ) &&
                strncmp( p_current->psz_uri, "rtsp:", 5 ) &&
                strncmp( p_current->psz_uri, "udp:", 4 ) &&
                strncmp( p_current->psz_uri, "mms:", 4 ) &&
                strncmp( p_current->psz_uri, "cdda:", 4 ) &&
                strncmp( p_current->psz_uri, "dvd:", 4 ) &&
                strncmp( p_current->psz_uri, "v4l:", 4 ) &&
                strncmp( p_current->psz_uri, "dshow:", 6 ) )
            {
                b_preparsed = VLC_TRUE;
                stats_TimerStart( p_playlist, "Preparse run",
                                  STATS_TIMER_PREPARSE );
508
                /* Do not preparse if it is already done (like by playing it) */
509
                if( !input_item_IsPreparsed( p_current ) )
510 511 512 513 514
                {
                    PL_UNLOCK;
                    input_Preparse( p_playlist, p_current );
                    PL_LOCK;
                }
515 516
                stats_TimerStop( p_playlist, STATS_TIMER_PREPARSE );
            }
Clément Stenac's avatar
Clément Stenac committed
517
            PL_UNLOCK;
518 519
            if( b_preparsed )
            {
520
                input_item_SetPreparsed( p_current, VLC_TRUE );
521
                var_SetInteger( p_playlist, "item-change", p_current->i_id );
522
            }
523
            PL_LOCK;
524

Clément Stenac's avatar
Clément Stenac committed
525
            /* If we haven't retrieved enough meta, add to secondary queue
526 527 528
             * 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
529
             */
530
            if( !input_MetaSatisfied( p_playlist, p_current, &i_m, &i_o ) )
Clément Stenac's avatar
Clément Stenac committed
531 532
            {
                preparse_item_t p;
533
                PL_DEBUG("need to fetch meta for %s", p_current->psz_name );
534 535
                p.p_item = p_current;
                p.b_fetch_art = VLC_FALSE;
536 537 538
                vlc_mutex_lock( &p_playlist->p_fetcher->object_lock );
                INSERT_ELEM( p_playlist->p_fetcher->p_waiting,
                             p_playlist->p_fetcher->i_waiting,
539
                             p_playlist->p_fetcher->i_waiting, p);
540 541 542 543
                vlc_mutex_unlock( &p_playlist->p_fetcher->object_lock );
                vlc_cond_signal( &p_playlist->p_fetcher->object_wait );
            }
            /* We already have all needed meta, but we need art right now */
544 545
            else if( p_playlist->p_fetcher->i_art_policy == ALBUM_ART_ALL &&
                     EMPTY_STR( input_item_GetArtURL( p_current ) ) )
546 547 548 549 550 551 552
            {
                preparse_item_t p;
                PL_DEBUG("meta ok for %s, need to fetch art",
                                                         p_current->psz_name );
                p.p_item = p_current;
                p.b_fetch_art = VLC_TRUE;
                vlc_mutex_lock( &p_playlist->p_fetcher->object_lock );
553 554 555
                INSERT_ELEM( p_playlist->p_fetcher->p_waiting,
                             p_playlist->p_fetcher->i_waiting,
                             p_playlist->p_fetcher->i_waiting, p);
556 557
                vlc_mutex_unlock( &p_playlist->p_fetcher->object_lock );
                vlc_cond_signal( &p_playlist->p_fetcher->object_wait );
Clément Stenac's avatar
Clément Stenac committed
558
            }
559
            else
560 561
            {
                PL_DEBUG( "no fetch required for %s (art currently %s)",
562
                          p_current->psz_name,
563
                          input_item_GetArtURL( p_current ));
Clément Stenac's avatar
Clément Stenac committed
564
                vlc_gc_decref( p_current );
565
            }
566
            PL_UNLOCK;
567 568
        }
        else
569 570
            PL_UNLOCK;

571
        vlc_mutex_lock( &p_obj->object_lock );
572
        i_activity = var_GetInteger( p_playlist, "activity" );
573
        if( i_activity < 0 ) i_activity = 0;
574
        vlc_mutex_unlock( &p_obj->object_lock );
575
        /* Sleep at least 1ms */
576 577 578 579
        msleep( (i_activity+1) * 1000 );
    }
}

580
/** Main loop for secondary preparser queue */
581
void playlist_FetcherLoop( playlist_fetcher_t *p_obj )
582 583
{
    playlist_t *p_playlist = (playlist_t *)p_obj->p_parent;
584 585 586
    vlc_bool_t b_fetch_art;
    input_item_t *p_item;
    int i_activity;
587

588
    while( !p_playlist->b_die )
589
    {
590 591 592 593 594 595 596 597 598 599 600 601 602
        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;
603
        REMOVE_ELEM( p_obj->p_waiting, p_obj->i_waiting, 0 );
604
        vlc_mutex_unlock( &p_obj->object_lock );
605
        if( p_item )
606
        {
Clément Stenac's avatar
Clément Stenac committed
607 608 609
            if( !b_fetch_art )
            {
                input_MetaFetch( p_playlist, p_item );
610 611
                var_SetInteger( p_playlist, "item-change", p_item->i_id );
                /*  Fetch right now */
612
                if( p_playlist->p_fetcher->i_art_policy == ALBUM_ART_ALL )
613 614 615 616 617
                {
                    vlc_mutex_lock( &p_obj->object_lock );
                    preparse_item_t p;
                    p.p_item = p_item;
                    p.b_fetch_art = VLC_TRUE;
618 619
                    INSERT_ELEM( p_playlist->p_fetcher->p_waiting,
                                 p_playlist->p_fetcher->i_waiting,
620
                                 0, p );
621
                    PL_DEBUG("meta fetched for %s, get art", p_item->psz_name);
622
                    vlc_mutex_unlock( &p_obj->object_lock );
623
                    continue;
624 625 626
                }
                else
                    vlc_gc_decref( p_item );
Clément Stenac's avatar
Clément Stenac committed
627 628
            }
            else
629
            {
630 631 632 633 634
                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 ... */
635
                for( i_ret = 0; i_ret < 10 && !input_item_IsPreparsed( p_item ); i_ret++ )
636 637 638 639 640 641 642 643 644 645 646 647
                {
                    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 );
                }
                
                i_ret = input_ArtFind( p_playlist, p_item );
648 649 650
                if( i_ret == 1 )
                {
                    PL_DEBUG("downloading art for %s", p_item->psz_name );
651
                    if( input_DownloadAndCacheArt( p_playlist, p_item ) )
652
                        input_item_SetArtNotFound( p_item, VLC_TRUE );
653
                    else {
654
                        input_item_SetArtFetched( p_item, VLC_TRUE );
655 656 657
                        var_SetInteger( p_playlist, "item-change",
                                        p_item->i_id );
                    }
658 659 660 661
                }
                else if( i_ret == 0 ) /* Was in cache */
                {
                    PL_DEBUG("found art for %s in cache", p_item->psz_name );
662
                    input_item_SetArtFetched( p_item, VLC_TRUE );
663
                    var_SetInteger( p_playlist, "item-change", p_item->i_id );
664 665 666 667
                }
                else
                {
                    PL_DEBUG("art not found for %s", p_item->psz_name );
668
                    input_item_SetArtNotFound( p_item, VLC_TRUE );
669
                }
670 671
                vlc_gc_decref( p_item );
           }
672
        }
673 674 675 676 677 678
        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 );
679 680 681
    }
}

682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714
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" );
715
    var_CreateGetBool( p_playlist, "play-and-exit" );
716 717 718
    var_CreateGetBool( p_playlist, "random" );
    var_CreateGetBool( p_playlist, "repeat" );
    var_CreateGetBool( p_playlist, "loop" );
Clément Stenac's avatar
Clément Stenac committed
719 720

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