engine.c 23.7 KB
Newer Older
1 2 3
/*****************************************************************************
 * engine.c : Run the playlist and handle its control
 *****************************************************************************
4
 * Copyright (C) 1999-2008 the VideoLAN team
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
 *
 * 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.
 *****************************************************************************/
23

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

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

/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
static void VariablesInit( playlist_t *p_playlist );
40
static void playlist_Destructor( vlc_object_t * p_this );
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;

48
    ((playlist_t*)p_this)->b_reset_currently_playing = 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
    bool b_save;
65 66

    /* Allocate structure */
67
    p_playlist = vlc_custom_create( p_parent, sizeof( *p_playlist ),
68
                                    VLC_OBJECT_GENERIC, playlist_name );
69 70
    if( !p_playlist )
        return NULL;
71

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

74
    libvlc_priv(p_parent->p_libvlc)->p_playlist = p_playlist;
75 76 77 78

    VariablesInit( p_playlist );

    /* Initialise data structures */
79
    vlc_mutex_init( &p_playlist->gc_lock );
80 81 82
    p_playlist->i_last_playlist_id = 0;
    p_playlist->p_input = NULL;

83
    p_playlist->gc_date = 0;
84
    p_playlist->b_cant_sleep = false;
85

zorglub's avatar
zorglub committed
86 87 88
    ARRAY_INIT( p_playlist->items );
    ARRAY_INIT( p_playlist->all_items );
    ARRAY_INIT( p_playlist->current );
89

zorglub's avatar
zorglub committed
90
    p_playlist->i_current_index = 0;
91
    p_playlist->b_reset_currently_playing = true;
92
    p_playlist->last_rebuild_date = 0;
zorglub's avatar
zorglub committed
93

94
    p_playlist->b_tree = var_CreateGetBool( p_playlist, "playlist-tree" );
95

96
    p_playlist->b_doing_ml = false;
97

98
    p_playlist->b_auto_preparse =
ivoire's avatar
ivoire committed
99
                        var_CreateGetBool( p_playlist, "auto-preparse" ) ;
100

101
    p_playlist->p_root_category = playlist_NodeCreate( p_playlist, NULL, NULL,
102
                                    0, NULL );
103
    p_playlist->p_root_onelevel = playlist_NodeCreate( p_playlist, NULL, NULL,
104
                                    0, p_playlist->p_root_category->p_input );
105

106 107 108
    if( !p_playlist->p_root_category || !p_playlist->p_root_onelevel )
        return NULL;

109
    /* Create playlist and media library */
110
    PL_LOCK; /* playlist_NodesPairCreate will check for it */
111 112
    playlist_NodesPairCreate( p_playlist, _( "Playlist" ),
                            &p_playlist->p_local_category,
113
                            &p_playlist->p_local_onelevel, false );
114
    PL_UNLOCK;
115

116 117 118
    p_playlist->p_local_category->i_flags |= PLAYLIST_RO_FLAG;
    p_playlist->p_local_onelevel->i_flags |= PLAYLIST_RO_FLAG;

119 120 121 122 123
    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;

124 125
    if( config_GetInt( p_playlist, "media-library") )
    {
126
        PL_LOCK; /* playlist_NodesPairCreate will check for it */
127 128
        playlist_NodesPairCreate( p_playlist, _( "Media Library" ),
                            &p_playlist->p_ml_category,
129
                            &p_playlist->p_ml_onelevel, false );
130
        PL_UNLOCK;
131 132 133 134

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

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

    /* Initial status */
    p_playlist->status.p_item = NULL;
145
    p_playlist->status.p_node = p_playlist->p_local_onelevel;
146
    p_playlist->request.b_request = false;
147 148 149 150 151
    p_playlist->status.i_status = PLAYLIST_STOPPED;

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

152

153
    b_save = p_playlist->b_auto_preparse;
154
    p_playlist->b_auto_preparse = false;
155
    playlist_MLLoad( p_playlist );
156
    p_playlist->b_auto_preparse = true;
157

158 159
    vlc_object_set_destructor( p_playlist, playlist_Destructor );

160 161 162
    return p_playlist;
}

ivoire's avatar
ivoire committed
163 164 165 166 167 168 169
/**
 * Destroy playlist
 *
 * Destroy a playlist structure.
 * \param p_playlist the playlist object
 * \return nothing
 */
170 171 172 173 174

static void playlist_Destructor( vlc_object_t * p_this )
{
    playlist_t * p_playlist = (playlist_t *)p_this;

175
    if( p_playlist->p_preparse )
176
    {
177
        vlc_object_release( p_playlist->p_preparse );
178
    }
179

180
    if( p_playlist->p_fetcher )
181
    {
182
        vlc_object_release( p_playlist->p_fetcher );
183
    }
184
    msg_Dbg( p_this, "Destroyed" );
185 186
}

187
/* Destroy remaining objects */
188
static void ObjectGarbageCollector( playlist_t *p_playlist, bool b_force )
189
{
190
    if( !b_force )
191
    {
192 193
        if( mdate() - p_playlist->gc_date < 1000000 )
        {
194
            p_playlist->b_cant_sleep = true;
195 196 197 198
            return;
        }
        else if( p_playlist->gc_date == 0 )
            return;
199
    }
200

201
    vlc_mutex_lock( &p_playlist->gc_lock );
202
    p_playlist->b_cant_sleep = false;
203
    vlc_mutex_unlock( &p_playlist->gc_lock );
204 205
}

206 207 208 209 210 211 212 213
/* Input Callback */
static void input_state_changed( const vlc_event_t * event, void * data )
{
    (void)event;
    playlist_t * p_playlist = data;
    playlist_Signal( p_playlist );
}

214 215 216 217 218 219 220 221 222 223 224
/* Input Callback */
static void input_selected_stream_changed( const vlc_event_t * event, void * data )
{
    (void)event;
    playlist_t * p_playlist = data;
    PL_LOCK;
    p_playlist->gc_date = mdate();
    vlc_object_signal_unlocked( p_playlist );
    PL_UNLOCK;
}

225 226 227
/* Internals */
void playlist_release_current_input( playlist_t * p_playlist )
{
228
    PL_ASSERT_LOCKED;
229 230 231 232

    if( !p_playlist->p_input ) return;

    input_thread_t * p_input = p_playlist->p_input;
233 234 235 236
    vlc_event_manager_t * p_em = input_get_event_manager( p_input );

    vlc_event_detach( p_em, vlc_InputStateChanged,
                      input_state_changed, p_playlist );
237 238
    vlc_event_detach( p_em, vlc_InputSelectedStreamChanged,
                      input_selected_stream_changed, p_playlist );
239 240 241 242 243 244 245 246 247 248 249 250
    p_playlist->p_input = NULL;

    /* Release the playlist lock, because we may get stuck
     * in vlc_object_release() for some time. */
    PL_UNLOCK;
    vlc_object_release( p_input );
    PL_LOCK;
}

void playlist_set_current_input(
    playlist_t * p_playlist, input_thread_t * p_input )
{
251
    PL_ASSERT_LOCKED;
252 253 254 255 256 257 258

    playlist_release_current_input( p_playlist );

    if( p_input )
    {
        vlc_object_yield( p_input );
        p_playlist->p_input = p_input;
259 260 261
        vlc_event_manager_t * p_em = input_get_event_manager( p_input );
        vlc_event_attach( p_em, vlc_InputStateChanged,
                          input_state_changed, p_playlist );
262 263
        vlc_event_attach( p_em, vlc_InputSelectedStreamChanged,
                          input_selected_stream_changed, p_playlist );
264 265 266
    }
}

267 268 269 270 271 272 273 274 275 276 277 278
/** Get current playing input.
 */
input_thread_t * playlist_CurrentInput( playlist_t * p_playlist )
{
    input_thread_t * p_input;
    PL_LOCK;
    p_input = p_playlist->p_input;
    if( p_input ) vlc_object_yield( p_input );
    PL_UNLOCK;
    return p_input;
}

279 280 281 282
/**
 * @}
 */

283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330
/** Accessor for status item and status nodes.
 */
playlist_item_t * get_current_status_item( playlist_t * p_playlist )
{
    PL_ASSERT_LOCKED;

    return p_playlist->status.p_item;
}

playlist_item_t * get_current_status_node( playlist_t * p_playlist )
{
    PL_ASSERT_LOCKED;

    return p_playlist->status.p_node;
}

void set_current_status_item( playlist_t * p_playlist,
    playlist_item_t * p_item )
{
    PL_ASSERT_LOCKED;

    if( p_playlist->status.p_item &&
        p_playlist->status.p_item->i_flags & PLAYLIST_REMOVE_FLAG &&
        p_playlist->status.p_item != p_item )
    {
         PL_DEBUG( "%s was marked for deletion, deleting",
                         PLI_NAME( p_playlist->status.p_item  ) );
         playlist_ItemDelete( p_playlist->status.p_item );
    }
    p_playlist->status.p_item = p_item;
}

void set_current_status_node( playlist_t * p_playlist,
    playlist_item_t * p_node )
{
    PL_ASSERT_LOCKED;

    if( p_playlist->status.p_node &&
        p_playlist->status.p_node->i_flags & PLAYLIST_REMOVE_FLAG &&
        p_playlist->status.p_node != p_node )
    {
         PL_DEBUG( "%s was marked for deletion, deleting",
                         PLI_NAME( p_playlist->status.p_node  ) );
         playlist_ItemDelete( p_playlist->status.p_node );
    }
    p_playlist->status.p_node = p_node;
}

ivoire's avatar
ivoire committed
331 332 333 334 335 336 337
/**
 * Main loop
 *
 * Main loop for the playlist
 * \param p_playlist the playlist object
 * \return nothing
 */
338 339 340
void playlist_MainLoop( playlist_t *p_playlist )
{
    playlist_item_t *p_item = NULL;
341
    bool b_playexit = var_GetBool( p_playlist, "play-and-exit" );
342
    PL_LOCK;
343

344 345
    if( p_playlist->b_reset_currently_playing &&
        mdate() - p_playlist->last_rebuild_date > 30000 ) // 30 ms
346
    {
ivoire's avatar
ivoire committed
347
        ResetCurrentlyPlaying( p_playlist, var_GetBool( p_playlist, "random" ),
348
                               get_current_status_item( p_playlist ) );
349
        p_playlist->last_rebuild_date = mdate();
350
    }
351

352
check_input:
353 354 355
    /* If there is an input, check that it doesn't need to die. */
    if( p_playlist->p_input )
    {
356 357 358 359 360 361
        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 );
        }

362 363 364 365 366
        /* This input is dead. Remove it ! */
        if( p_playlist->p_input->b_dead )
        {
            int i_activity;
            input_thread_t *p_input;
367 368 369
            sout_instance_t **pp_sout =
                &libvlc_priv(p_playlist->p_libvlc)->p_sout;

zorglub's avatar
zorglub committed
370
            PL_DEBUG( "dead input" );
371 372

            p_input = p_playlist->p_input;
373

374 375 376
            assert( *pp_sout == NULL );
            if( var_CreateGetBool( p_input, "sout-keep" ) )
                *pp_sout = input_DetachSout( p_input );
377 378

            /* Destroy input */
379
            playlist_release_current_input( p_playlist );
380 381

            p_playlist->gc_date = mdate();
382
            p_playlist->b_cant_sleep = true;
383

384
            set_current_status_item( p_playlist, NULL );
385

ivoire's avatar
ivoire committed
386
            i_activity= var_GetInteger( p_playlist, "activity" );
387 388
            var_SetInteger( p_playlist, "activity", i_activity -
                            DEFAULT_INPUT_ACTIVITY );
389

390
            goto check_input;
391 392 393 394
        }
        /* This input is dying, let it do */
        else if( p_playlist->p_input->b_die )
        {
zorglub's avatar
zorglub committed
395
            PL_DEBUG( "dying input" );
396
            PL_UNLOCK;
397
            msleep( INTF_IDLE_SLEEP );
398
            PL_LOCK;
399
            goto check_input;
400 401 402 403 404
        }
        /* 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
405
            PL_DEBUG( "finished input" );
406
            input_StopThread( p_playlist->p_input );
407 408
            /* No need to wait here, we'll wait in the p_input->b_die case */
            goto check_input;
409 410 411
        }
        else if( p_playlist->p_input->i_state != INIT_S )
        {
zorglub's avatar
zorglub committed
412
            PL_UNLOCK;
413
            ObjectGarbageCollector( p_playlist, false );
414
            PL_LOCK;
415 416 417 418 419 420 421 422 423
        }
    }
    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
424
        */
Rafaël Carré's avatar
Rafaël Carré committed
425 426 427
        int i_status = p_playlist->request.b_request ?
            p_playlist->request.i_status : p_playlist->status.i_status;
        if( i_status != PLAYLIST_STOPPED )
ivoire's avatar
ivoire committed
428 429 430
        {
            msg_Dbg( p_playlist, "starting new item" );
            p_item = playlist_NextItem( p_playlist );
431

ivoire's avatar
ivoire committed
432 433
            if( p_item == NULL )
            {
434
                msg_Dbg( p_playlist, "nothing to play" );
435
                p_playlist->status.i_status = PLAYLIST_STOPPED;
436
                PL_UNLOCK;
437

438
                if( b_playexit == true )
439 440
                {
                    msg_Info( p_playlist, "end of playlist, exiting" );
441
                    vlc_object_kill( p_playlist->p_libvlc );
442
                }
443
                ObjectGarbageCollector( p_playlist, true );
444
                return;
ivoire's avatar
ivoire committed
445 446 447 448 449
            }
            playlist_PlayItem( p_playlist, p_item );
        }
        else
        {
450
            const bool b_gc_forced = p_playlist->status.i_status != PLAYLIST_STOPPED;
451

452
            p_playlist->status.i_status = PLAYLIST_STOPPED;
453
            set_current_status_item( p_playlist, NULL );
454

455 456
            /* Collect garbage */
            PL_UNLOCK;
457
            ObjectGarbageCollector( p_playlist, b_gc_forced );
458 459
            PL_LOCK;
        }
460
    }
461
    PL_UNLOCK;
462 463
}

ivoire's avatar
ivoire committed
464 465 466 467 468 469 470
/**
 * Last loop
 *
 * The playlist is dying so do the last loop
 * \param p_playlist the playlist object
 * \return nothing
*/
471 472 473 474 475
void playlist_LastLoop( playlist_t *p_playlist )
{
    /* If there is an input, kill it */
    while( 1 )
    {
476
        PL_LOCK;
477 478
        if( p_playlist->p_input == NULL )
        {
479
            PL_UNLOCK;
480 481 482 483 484
            break;
        }

        if( p_playlist->p_input->b_dead )
        {
485 486
            /* remove input */
            playlist_release_current_input( p_playlist );
487

488 489 490
            /* sout-keep: no need to anything here.
             * The last input will destroy its sout, if any, by itself */

491
            PL_UNLOCK;
492 493 494 495 496 497 498 499 500 501
            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 );
502
            PL_UNLOCK;
503 504 505 506 507 508
            continue;
        }
        else
        {
            p_playlist->p_input->b_eof = 1;
        }
509
        PL_UNLOCK;
510 511 512 513

        msleep( INTF_IDLE_SLEEP );
    }

514
#ifdef ENABLE_SOUT
515 516 517 518
    /* close the remaining sout-keep (if there was no input atm) */
    sout_instance_t *p_sout = libvlc_priv (p_playlist->p_libvlc)->p_sout;
    if (p_sout)
        sout_DeleteInstance( p_sout );
519
#endif
520

521 522 523 524 525 526 527 528 529 530 531 532
    /* Core should have terminated all SDs before the playlist */
    /* TODO: It fails to do so when not playing anything -- Courmisch */
    playlist_ServicesDiscoveryKillAll( p_playlist );
    playlist_MLDump( p_playlist );

    vlc_object_kill( p_playlist->p_preparse );
    vlc_thread_join( p_playlist->p_preparse );
    vlc_object_kill( p_playlist->p_fetcher );
    vlc_thread_join( p_playlist->p_fetcher );

    PL_LOCK;

533 534 535 536 537
    /* Release the current node */
    set_current_status_node( p_playlist, NULL );

    /* Release the current item */
    set_current_status_item( p_playlist, NULL );
538

539 540
    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
541
        vlc_gc_decref( p_del->p_input );
542 543 544 545 546 547 548 549
        free( p_del );
    FOREACH_END();
    ARRAY_RESET( p_playlist->all_items );

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

    PL_UNLOCK;
550 551
}

ivoire's avatar
ivoire committed
552 553 554 555 556 557 558
/**
 * Preparse loop
 *
 * Main loop for preparser queue
 * \param p_obj items to preparse
 * \return nothing
 */
559 560
void playlist_PreparseLoop( playlist_preparse_t *p_obj )
{
561
    playlist_t *p_playlist = (playlist_t *)p_obj->p_parent;
562
    input_item_t *p_current;
563 564
    int i_activity;

565 566 567
    vlc_object_lock( p_obj );

    while( vlc_object_alive( p_obj ) )
568
    {
569
        if( p_obj->i_waiting == 0 )
570
        {
571 572
            vlc_object_wait( p_obj );
            continue;
573
        }
574

575
        p_current = p_obj->pp_waiting[0];
576
        REMOVE_ELEM( p_obj->pp_waiting, p_obj->i_waiting, 0 );
577
        vlc_object_unlock( p_obj );
578

zorglub's avatar
zorglub committed
579
        PL_LOCK;
580 581
        if( p_current )
        {
582
            if( p_current->i_type == ITEM_TYPE_FILE )
583 584 585
            {
                stats_TimerStart( p_playlist, "Preparse run",
                                  STATS_TIMER_PREPARSE );
586
                /* Do not preparse if it is already done (like by playing it) */
587
                if( !input_item_IsPreparsed( p_current ) )
588 589 590 591 592
                {
                    PL_UNLOCK;
                    input_Preparse( p_playlist, p_current );
                    PL_LOCK;
                }
593
                stats_TimerStop( p_playlist, STATS_TIMER_PREPARSE );
594
                PL_UNLOCK;
595
                input_item_SetPreparsed( p_current, true );
zorglub's avatar
zorglub committed
596
                var_SetInteger( p_playlist, "item-change", p_current->i_id );
597
                PL_LOCK;
598
            }
zorglub's avatar
zorglub committed
599
            /* If we haven't retrieved enough meta, add to secondary queue
600 601 602
             * 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
603
             */
Rafaël Carré's avatar
Rafaël Carré committed
604 605
            char *psz_arturl = input_item_GetArtURL( p_current );
            char *psz_name = input_item_GetName( p_current );
Rafaël Carré's avatar
Rafaël Carré committed
606
            if( p_playlist->p_fetcher->i_art_policy == ALBUM_ART_ALL &&
607
                        ( !psz_arturl || strncmp( psz_arturl, "file://", 7 ) ) )
608
            {
Rafaël Carré's avatar
Rafaël Carré committed
609
                PL_DEBUG("meta ok for %s, need to fetch art", psz_name );
610
                vlc_object_lock( p_playlist->p_fetcher );
611 612 613 614 615 616 617 618 619
                if( vlc_object_alive( p_playlist->p_fetcher ) )
                {
                    INSERT_ELEM( p_playlist->p_fetcher->pp_waiting,
                        p_playlist->p_fetcher->i_waiting,
                        p_playlist->p_fetcher->i_waiting, p_current);
                    vlc_object_signal_unlocked( p_playlist->p_fetcher );
                }
                else
                    vlc_gc_decref( p_current );
620
                vlc_object_unlock( p_playlist->p_fetcher );
zorglub's avatar
zorglub committed
621
            }
622
            else
623 624
            {
                PL_DEBUG( "no fetch required for %s (art currently %s)",
Rafaël Carré's avatar
Rafaël Carré committed
625
                          psz_name, psz_arturl );
zorglub's avatar
zorglub committed
626
                vlc_gc_decref( p_current );
627
            }
Rafaël Carré's avatar
Rafaël Carré committed
628 629
            free( psz_name );
            free( psz_arturl );
630
            PL_UNLOCK;
631 632
        }
        else
633 634
            PL_UNLOCK;

635
        vlc_object_lock( p_obj );
636
        i_activity = var_GetInteger( p_playlist, "activity" );
637
        if( i_activity < 0 ) i_activity = 0;
638
        vlc_object_unlock( p_obj );
639
        /* Sleep at least 1ms */
640
        msleep( (i_activity+1) * 1000 );
641
        vlc_object_lock( p_obj );
642
    }
643

644
    while( p_obj->i_waiting > 0 )
645
    {
646
        vlc_gc_decref( p_obj->pp_waiting[0] );
647 648 649
        REMOVE_ELEM( p_obj->pp_waiting, p_obj->i_waiting, 0 );
    }

650
    vlc_object_unlock( p_obj );
651 652
}

ivoire's avatar
ivoire committed
653 654 655 656 657 658 659
/**
 * Fetcher loop
 *
 * Main loop for secondary preparser queue
 * \param p_obj items to preparse
 * \return nothing
 */
660
void playlist_FetcherLoop( playlist_fetcher_t *p_obj )
661 662
{
    playlist_t *p_playlist = (playlist_t *)p_obj->p_parent;
663 664
    input_item_t *p_item;
    int i_activity;
665

666
    vlc_object_lock( p_obj );
667 668

    while( vlc_object_alive( p_obj ) )
669
    {
670
        if( p_obj->i_waiting == 0 )
671
        {
672 673
            vlc_object_wait( p_obj );
            continue;
674 675
        }

Rafaël Carré's avatar
Rafaël Carré committed
676 677
        p_item = p_obj->pp_waiting[0];
        REMOVE_ELEM( p_obj->pp_waiting, p_obj->i_waiting, 0 );
678
        vlc_object_unlock( p_obj );
dionoea's avatar
dionoea committed
679
        if( p_item )
680
        {
Rafaël Carré's avatar
Rafaël Carré committed
681 682 683 684 685 686
            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 ... */
            for( i_ret = 0; i_ret < 10 && !input_item_IsPreparsed( p_item ); i_ret++ )
zorglub's avatar
zorglub committed
687
            {
Rafaël Carré's avatar
Rafaël Carré committed
688 689 690 691 692 693 694 695 696
                bool 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 );
            }
697

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

732
    while( p_obj->i_waiting > 0 )
733
    {
734
        vlc_gc_decref( p_obj->pp_waiting[0] );
735 736 737
        REMOVE_ELEM( p_obj->pp_waiting, p_obj->i_waiting, 0 );
    }

738
    vlc_object_unlock( p_obj );
739 740
}

741 742 743 744 745
static void VariablesInit( playlist_t *p_playlist )
{
    vlc_value_t val;
    /* These variables control updates */
    var_Create( p_playlist, "intf-change", VLC_VAR_BOOL );
746
    val.b_bool = true;
747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767
    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, "activity", VLC_VAR_INTEGER );
    var_SetInteger( p_playlist, "activity", 0 );

    /* Variables to control playback */
    var_CreateGetBool( p_playlist, "play-and-stop" );
768
    var_CreateGetBool( p_playlist, "play-and-exit" );
769 770 771
    var_CreateGetBool( p_playlist, "random" );
    var_CreateGetBool( p_playlist, "repeat" );
    var_CreateGetBool( p_playlist, "loop" );
zorglub's avatar
zorglub committed
772 773

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