engine.c 23.5 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 111
    playlist_NodesPairCreate( p_playlist, _( "Playlist" ),
                            &p_playlist->p_local_category,
112
                            &p_playlist->p_local_onelevel, false );
113

114 115 116
    p_playlist->p_local_category->i_flags |= PLAYLIST_RO_FLAG;
    p_playlist->p_local_onelevel->i_flags |= PLAYLIST_RO_FLAG;

117 118 119 120 121
    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;

122 123
    if( config_GetInt( p_playlist, "media-library") )
    {
124 125
        playlist_NodesPairCreate( p_playlist, _( "Media Library" ),
                            &p_playlist->p_ml_category,
126
                            &p_playlist->p_ml_onelevel, false );
127 128 129 130

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

131 132 133 134 135 136 137
        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;
    }
138 139 140

    /* Initial status */
    p_playlist->status.p_item = NULL;
141
    p_playlist->status.p_node = p_playlist->p_local_onelevel;
142
    p_playlist->request.b_request = false;
143 144 145 146 147
    p_playlist->status.i_status = PLAYLIST_STOPPED;

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

148

149
    b_save = p_playlist->b_auto_preparse;
150
    p_playlist->b_auto_preparse = false;
151
    playlist_MLLoad( p_playlist );
152
    p_playlist->b_auto_preparse = true;
153

154 155
    vlc_object_set_destructor( p_playlist, playlist_Destructor );

156 157 158
    return p_playlist;
}

ivoire's avatar
ivoire committed
159 160 161 162 163 164 165
/**
 * Destroy playlist
 *
 * Destroy a playlist structure.
 * \param p_playlist the playlist object
 * \return nothing
 */
166 167 168 169 170

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

171
    if( p_playlist->p_preparse )
172
    {
173
        vlc_object_release( p_playlist->p_preparse );
174
    }
175

176
    if( p_playlist->p_fetcher )
177
    {
178
        vlc_object_release( p_playlist->p_fetcher );
179
    }
180
    msg_Dbg( p_this, "Destroyed" );
181 182
}

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

197
    vlc_mutex_lock( &p_playlist->gc_lock );
198
    p_playlist->b_cant_sleep = false;
199
    vlc_mutex_unlock( &p_playlist->gc_lock );
200 201
}

202 203 204 205 206 207 208 209
/* 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 );
}

210 211 212 213 214 215 216 217 218 219 220
/* 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;
}

221 222 223
/* Internals */
void playlist_release_current_input( playlist_t * p_playlist )
{
224
    PL_ASSERT_LOCKED;
225 226 227 228

    if( !p_playlist->p_input ) return;

    input_thread_t * p_input = p_playlist->p_input;
229 230 231 232
    vlc_event_manager_t * p_em = input_get_event_manager( p_input );

    vlc_event_detach( p_em, vlc_InputStateChanged,
                      input_state_changed, p_playlist );
233 234
    vlc_event_detach( p_em, vlc_InputSelectedStreamChanged,
                      input_selected_stream_changed, p_playlist );
235 236 237 238 239 240 241 242 243 244 245 246
    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 )
{
247
    PL_ASSERT_LOCKED;
248 249 250 251 252 253 254

    playlist_release_current_input( p_playlist );

    if( p_input )
    {
        vlc_object_yield( p_input );
        p_playlist->p_input = p_input;
255 256 257
        vlc_event_manager_t * p_em = input_get_event_manager( p_input );
        vlc_event_attach( p_em, vlc_InputStateChanged,
                          input_state_changed, p_playlist );
258 259
        vlc_event_attach( p_em, vlc_InputSelectedStreamChanged,
                          input_selected_stream_changed, p_playlist );
260 261 262
    }
}

263 264 265 266 267 268 269 270 271 272 273 274
/** 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;
}

275 276 277 278
/**
 * @}
 */

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
/** 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
327 328 329 330 331 332 333
/**
 * Main loop
 *
 * Main loop for the playlist
 * \param p_playlist the playlist object
 * \return nothing
 */
334 335 336
void playlist_MainLoop( playlist_t *p_playlist )
{
    playlist_item_t *p_item = NULL;
337
    bool b_playexit = var_GetBool( p_playlist, "play-and-exit" );
338
    PL_LOCK;
339

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

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

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

zorglub's avatar
zorglub committed
366
            PL_DEBUG( "dead input" );
367 368

            p_input = p_playlist->p_input;
369

370 371 372
            assert( *pp_sout == NULL );
            if( var_CreateGetBool( p_input, "sout-keep" ) )
                *pp_sout = input_DetachSout( p_input );
373 374

            /* Destroy input */
375
            playlist_release_current_input( p_playlist );
376 377

            p_playlist->gc_date = mdate();
378
            p_playlist->b_cant_sleep = true;
379

380
            set_current_status_item( p_playlist, NULL );
381

ivoire's avatar
ivoire committed
382
            i_activity= var_GetInteger( p_playlist, "activity" );
383 384
            var_SetInteger( p_playlist, "activity", i_activity -
                            DEFAULT_INPUT_ACTIVITY );
385

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

ivoire's avatar
ivoire committed
428 429
            if( p_item == NULL )
            {
430
                msg_Dbg( p_playlist, "nothing to play" );
431
                p_playlist->status.i_status = PLAYLIST_STOPPED;
432
                PL_UNLOCK;
433

434
                if( b_playexit == true )
435 436
                {
                    msg_Info( p_playlist, "end of playlist, exiting" );
437
                    vlc_object_kill( p_playlist->p_libvlc );
438
                }
439
                ObjectGarbageCollector( p_playlist, true );
440 441 442 443 444 445
                return;
             }
             playlist_PlayItem( p_playlist, p_item );
         }
         else
         {
446
            const bool b_gc_forced = p_playlist->status.i_status != PLAYLIST_STOPPED;
447

448
            p_playlist->status.i_status = PLAYLIST_STOPPED;
449
            set_current_status_item( p_playlist, NULL );
450

451 452
            /* Collect garbage */
            PL_UNLOCK;
453
            ObjectGarbageCollector( p_playlist, b_gc_forced );
454 455
            PL_LOCK;
        }
456
    }
457
    PL_UNLOCK;
458 459
}

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

        if( p_playlist->p_input->b_dead )
        {
481 482
            /* remove input */
            playlist_release_current_input( p_playlist );
483

484 485 486
            /* sout-keep: no need to anything here.
             * The last input will destroy its sout, if any, by itself */

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

        msleep( INTF_IDLE_SLEEP );
    }

510
#ifdef ENABLE_SOUT
511 512 513 514
    /* 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 );
515
#endif
516

517 518 519 520 521 522 523 524 525 526 527 528
    /* 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;

529 530 531 532 533
    /* Release the current node */
    set_current_status_node( p_playlist, NULL );

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

535 536
    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
537
        vlc_gc_decref( p_del->p_input );
538 539 540 541 542 543 544 545
        free( p_del );
    FOREACH_END();
    ARRAY_RESET( p_playlist->all_items );

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

    PL_UNLOCK;
546 547
}

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

561 562 563
    vlc_object_lock( p_obj );

    while( vlc_object_alive( p_obj ) )
564
    {
565
        if( p_obj->i_waiting == 0 )
566
        {
567 568
            vlc_object_wait( p_obj );
            continue;
569
        }
570

571
        p_current = p_obj->pp_waiting[0];
572
        REMOVE_ELEM( p_obj->pp_waiting, p_obj->i_waiting, 0 );
573
        vlc_object_unlock( p_obj );
574

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

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

640
    while( p_obj->i_waiting > 0 )
641
    {
642
        vlc_gc_decref( p_obj->pp_waiting[0] );
643 644 645
        REMOVE_ELEM( p_obj->pp_waiting, p_obj->i_waiting, 0 );
    }

646
    vlc_object_unlock( p_obj );
647 648
}

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

662
    vlc_object_lock( p_obj );
663 664

    while( vlc_object_alive( p_obj ) )
665
    {
666
        if( p_obj->i_waiting == 0 )
667
        {
668 669
            vlc_object_wait( p_obj );
            continue;
670 671
        }

Rafaël Carré's avatar
Rafaël Carré committed
672 673
        p_item = p_obj->pp_waiting[0];
        REMOVE_ELEM( p_obj->pp_waiting, p_obj->i_waiting, 0 );
674
        vlc_object_unlock( p_obj );
dionoea's avatar
dionoea committed
675
        if( p_item )
676
        {
Rafaël Carré's avatar
Rafaël Carré committed
677 678 679 680 681 682
            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
683
            {
Rafaël Carré's avatar
Rafaël Carré committed
684 685 686 687 688 689 690 691 692
                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 );
            }
693

Rafaël Carré's avatar
Rafaël Carré committed
694 695 696 697 698 699 700 701 702 703
            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 );
704
                }
Rafaël Carré's avatar
Rafaël Carré committed
705 706 707 708 709 710
            }
            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
711 712
            }
            else
dionoea's avatar
dionoea committed
713
            {
Rafaël Carré's avatar
Rafaël Carré committed
714 715 716 717
                PL_DEBUG( "art not found for %s", p_item->psz_name );
                input_item_SetArtNotFound( p_item, true );
            }
            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
        vlc_object_lock( p_obj );
726
    }
727

728
    while( p_obj->i_waiting > 0 )
729
    {
730
        vlc_gc_decref( p_obj->pp_waiting[0] );
731 732 733
        REMOVE_ELEM( p_obj->pp_waiting, p_obj->i_waiting, 0 );
    }

734
    vlc_object_unlock( p_obj );
735 736
}

737 738 739 740 741
static void VariablesInit( playlist_t *p_playlist )
{
    vlc_value_t val;
    /* These variables control updates */
    var_Create( p_playlist, "intf-change", VLC_VAR_BOOL );
742
    val.b_bool = true;
743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763
    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" );
764
    var_CreateGetBool( p_playlist, "play-and-exit" );
765 766 767
    var_CreateGetBool( p_playlist, "random" );
    var_CreateGetBool( p_playlist, "repeat" );
    var_CreateGetBool( p_playlist, "loop" );
zorglub's avatar
zorglub committed
768 769

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