X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fplaylist%2Ffetcher.c;h=41e0c32ed08d1323792b5ee9371868c1daa52ad8;hb=062267bf2dea5bb919d09ab6b16472445854961b;hp=f8d8a422a761934057acb2eb61ac7a7d6bc1bb5e;hpb=14f37b2101842fa6e427f962f689db74eff6faba;p=vlc diff --git a/src/playlist/fetcher.c b/src/playlist/fetcher.c index f8d8a422a7..41e0c32ed0 100644 --- a/src/playlist/fetcher.c +++ b/src/playlist/fetcher.c @@ -1,57 +1,86 @@ /***************************************************************************** * fetcher.c: Art fetcher thread. ***************************************************************************** - * Copyright © 1999-2009 the VideoLAN team + * Copyright © 1999-2009 VLC authors and VideoLAN * $Id$ * * Authors: Samuel Hocevar * Clément Stenac * - * 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 + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 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. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser 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. + * You should have received a copy of the GNU Lesser 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. *****************************************************************************/ #ifdef HAVE_CONFIG_H # include "config.h" #endif +#include +#include + #include -#include #include -#include +#include +#include +#include +#include +#include "libvlc.h" #include "art.h" #include "fetcher.h" -#include "playlist_internal.h" - +#include "input/input_interface.h" /***************************************************************************** * Structures/definitions *****************************************************************************/ -struct playlist_fetcher_t +typedef enum +{ + PASS1_LOCAL = 0, + PASS2_NETWORK +} fetcher_pass_t; +#define PASS_COUNT 2 + +typedef struct { - VLC_COMMON_MEMBERS; + char *psz_artist; + char *psz_album; + char *psz_arturl; + bool b_found; + meta_fetcher_scope_t e_scope; /* max scope */ + +} playlist_album_t; - playlist_t *p_playlist; +typedef struct fetcher_entry_t fetcher_entry_t; - vlc_thread_t thread; +struct fetcher_entry_t +{ + input_item_t *p_item; + input_item_meta_request_option_t i_options; + fetcher_entry_t *p_next; +}; + +struct playlist_fetcher_t +{ + vlc_object_t *object; vlc_mutex_t lock; vlc_cond_t wait; - int i_art_policy; - int i_waiting; - input_item_t **pp_waiting; + bool b_live; + + fetcher_entry_t *p_waiting_head[PASS_COUNT]; + fetcher_entry_t *p_waiting_tail[PASS_COUNT]; DECL_ARRAY(playlist_album_t) albums; + meta_fetcher_scope_t e_scope; }; static void *Thread( void * ); @@ -60,63 +89,87 @@ static void *Thread( void * ); /***************************************************************************** * Public functions *****************************************************************************/ -playlist_fetcher_t *playlist_fetcher_New( playlist_t *p_playlist ) +playlist_fetcher_t *playlist_fetcher_New( vlc_object_t *parent ) { - playlist_fetcher_t *p_fetcher = - vlc_custom_create( p_playlist, sizeof(*p_fetcher), - VLC_OBJECT_GENERIC, "playlist fetcher" ); - + playlist_fetcher_t *p_fetcher = malloc( sizeof(*p_fetcher) ); if( !p_fetcher ) return NULL; - vlc_object_attach( p_fetcher, p_playlist ); - p_fetcher->p_playlist = p_playlist; + p_fetcher->object = parent; vlc_mutex_init( &p_fetcher->lock ); vlc_cond_init( &p_fetcher->wait ); - p_fetcher->i_waiting = 0; - p_fetcher->pp_waiting = NULL; - p_fetcher->i_art_policy = var_GetInteger( p_playlist, "album-art" ); - ARRAY_INIT( p_fetcher->albums ); + p_fetcher->b_live = false; - if( vlc_clone( &p_fetcher->thread, Thread, p_fetcher, - VLC_THREAD_PRIORITY_LOW ) ) - { - msg_Err( p_fetcher, "cannot spawn secondary preparse thread" ); - vlc_object_release( p_fetcher ); - return NULL; - } + bool b_access = var_InheritBool( parent, "metadata-network-access" ); + if ( !b_access ) + b_access = ( var_InheritInteger( parent, "album-art" ) == ALBUM_ART_ALL ); + + p_fetcher->e_scope = ( b_access ) ? FETCHER_SCOPE_ANY : FETCHER_SCOPE_LOCAL; + + memset( p_fetcher->p_waiting_head, 0, PASS_COUNT * sizeof(fetcher_entry_t *) ); + memset( p_fetcher->p_waiting_tail, 0, PASS_COUNT * sizeof(fetcher_entry_t *) ); + + ARRAY_INIT( p_fetcher->albums ); return p_fetcher; } -void playlist_fetcher_Push( playlist_fetcher_t *p_fetcher, input_item_t *p_item ) +void playlist_fetcher_Push( playlist_fetcher_t *p_fetcher, input_item_t *p_item, + input_item_meta_request_option_t i_options ) { - vlc_gc_incref( p_item ); + fetcher_entry_t *p_entry = malloc( sizeof(fetcher_entry_t) ); + if ( !p_entry ) return; + vlc_gc_incref( p_item ); + p_entry->p_item = p_item; + p_entry->p_next = NULL; + p_entry->i_options = i_options; vlc_mutex_lock( &p_fetcher->lock ); - INSERT_ELEM( p_fetcher->pp_waiting, p_fetcher->i_waiting, - p_fetcher->i_waiting, p_item ); - vlc_cond_signal( &p_fetcher->wait ); + /* Append last */ + if ( p_fetcher->p_waiting_head[PASS1_LOCAL] ) + p_fetcher->p_waiting_tail[PASS1_LOCAL]->p_next = p_entry; + else + p_fetcher->p_waiting_head[PASS1_LOCAL] = p_entry; + p_fetcher->p_waiting_tail[PASS1_LOCAL] = p_entry; + + if( !p_fetcher->b_live ) + { + assert( p_fetcher->p_waiting_head[PASS1_LOCAL] ); + if( vlc_clone_detach( NULL, Thread, p_fetcher, + VLC_THREAD_PRIORITY_LOW ) ) + msg_Err( p_fetcher->object, + "cannot spawn secondary preparse thread" ); + else + p_fetcher->b_live = true; + } vlc_mutex_unlock( &p_fetcher->lock ); } void playlist_fetcher_Delete( playlist_fetcher_t *p_fetcher ) { - /* */ - vlc_object_kill( p_fetcher ); + fetcher_entry_t *p_next; + vlc_mutex_lock( &p_fetcher->lock ); + /* Remove any left-over item, the fetcher will exit */ + for ( int i_queue=0; i_queuep_waiting_head[i_queue] ) + { + p_next = p_fetcher->p_waiting_head[i_queue]->p_next; + vlc_gc_decref( p_fetcher->p_waiting_head[i_queue]->p_item ); + free( p_fetcher->p_waiting_head[i_queue] ); + p_fetcher->p_waiting_head[i_queue] = p_next; + } + p_fetcher->p_waiting_head[i_queue] = NULL; + } - /* Destroy the item meta-infos fetcher */ - vlc_cancel( p_fetcher->thread ); - vlc_join( p_fetcher->thread, NULL ); + while( p_fetcher->b_live ) + vlc_cond_wait( &p_fetcher->wait, &p_fetcher->lock ); + vlc_mutex_unlock( &p_fetcher->lock ); - while( p_fetcher->i_waiting > 0 ) - { /* Any left-over unparsed item? */ - vlc_gc_decref( p_fetcher->pp_waiting[0] ); - REMOVE_ELEM( p_fetcher->pp_waiting, p_fetcher->i_waiting, 0 ); - } vlc_cond_destroy( &p_fetcher->wait ); vlc_mutex_destroy( &p_fetcher->lock ); - vlc_object_release( p_fetcher ); + + free( p_fetcher ); } @@ -133,12 +186,11 @@ void playlist_fetcher_Delete( playlist_fetcher_t *p_fetcher ) static int FindArt( playlist_fetcher_t *p_fetcher, input_item_t *p_item ) { int i_ret; - module_t *p_module; - char *psz_title, *psz_artist, *psz_album; - psz_artist = input_item_GetArtist( p_item ); - psz_album = input_item_GetAlbum( p_item ); - psz_title = input_item_GetTitle( p_item ); + playlist_album_t *p_album = NULL; + char *psz_artist = input_item_GetArtist( p_item ); + char *psz_album = input_item_GetAlbum( p_item ); + char *psz_title = input_item_GetTitle( p_item ); if( !psz_title ) psz_title = input_item_GetName( p_item ); @@ -154,7 +206,8 @@ static int FindArt( playlist_fetcher_t *p_fetcher, input_item_t *p_item ) if( !strcmp( album.psz_artist, psz_artist ) && !strcmp( album.psz_album, psz_album ) ) { - msg_Dbg( p_fetcher, " %s - %s has already been searched", + msg_Dbg( p_fetcher->object, + " %s - %s has already been searched", psz_artist, psz_album ); /* TODO-fenrir if we cache art filename too, we can go faster */ free( psz_artist ); @@ -167,22 +220,32 @@ static int FindArt( playlist_fetcher_t *p_fetcher, input_item_t *p_item ) playlist_FindArtInCache( p_item ); return 0; } - else + else if ( album.e_scope >= p_fetcher->e_scope ) { return VLC_EGENERIC; } + msg_Dbg( p_fetcher->object, + " will search at higher scope, if possible" ); + p_album = &p_fetcher->albums.p_elems[fe_idx]; + + psz_artist = psz_album = NULL; + break; } FOREACH_END(); } + free( psz_artist ); free( psz_album ); - playlist_FindArtInCache( p_item ); + if ( playlist_FindArtInCacheUsingItemUID( p_item ) != VLC_SUCCESS ) + playlist_FindArtInCache( p_item ); + else + msg_Dbg( p_fetcher->object, "successfully retrieved arturl by uid" ); char *psz_arturl = input_item_GetArtURL( p_item ); if( psz_arturl ) { - /* We already have an URL */ + /* We already have a URL */ if( !strncmp( psz_arturl, "file://", strlen( "file://" ) ) ) { free( psz_arturl ); @@ -200,8 +263,8 @@ static int FindArt( playlist_fetcher_t *p_fetcher, input_item_t *p_item ) psz_artist = input_item_GetArtist( p_item ); if( psz_album && psz_artist ) { - msg_Dbg( p_fetcher, "searching art for %s - %s", - psz_artist, psz_album ); + msg_Dbg( p_fetcher->object, "searching art for %s - %s", + psz_artist, psz_album ); } else { @@ -209,35 +272,58 @@ static int FindArt( playlist_fetcher_t *p_fetcher, input_item_t *p_item ) if( !psz_title ) psz_title = input_item_GetName( p_item ); - msg_Dbg( p_fetcher, "searching art for %s", psz_title ); + msg_Dbg( p_fetcher->object, "searching art for %s", psz_title ); free( psz_title ); } /* Fetch the art url */ - p_fetcher->p_private = p_item; - - p_module = module_need( p_fetcher, "art finder", NULL, false ); + i_ret = VLC_EGENERIC; - if( p_module ) + vlc_object_t *p_parent = p_fetcher->object; + meta_fetcher_t *p_finder = + vlc_custom_create( p_parent, sizeof( *p_finder ), "art finder" ); + if( p_finder != NULL) { - module_unneed( p_fetcher, p_module ); - i_ret = 1; - } - else - { - msg_Dbg( p_fetcher, "unable to find art" ); - i_ret = VLC_EGENERIC; + module_t *p_module; + + p_finder->p_item = p_item; + p_finder->e_scope = p_fetcher->e_scope; + + p_module = module_need( p_finder, "art finder", NULL, false ); + if( p_module ) + { + module_unneed( p_finder, p_module ); + /* Try immediately if found in cache by download URL */ + if( !playlist_FindArtInCache( p_item ) ) + i_ret = 0; + else + i_ret = 1; + } + vlc_object_release( p_finder ); } /* Record this album */ if( psz_artist && psz_album ) { - playlist_album_t a; - a.psz_artist = psz_artist; - a.psz_album = psz_album; - a.psz_arturl = input_item_GetArtURL( p_item ); - a.b_found = (i_ret == VLC_EGENERIC ? false : true ); - ARRAY_APPEND( p_fetcher->albums, a ); + if ( p_album ) + { + p_album->e_scope = p_fetcher->e_scope; + free( p_album->psz_arturl ); + p_album->psz_arturl = input_item_GetArtURL( p_item ); + p_album->b_found = (i_ret == VLC_EGENERIC ? false : true ); + free( psz_artist ); + free( psz_album ); + } + else + { + playlist_album_t a; + a.psz_artist = psz_artist; + a.psz_album = psz_album; + a.psz_arturl = input_item_GetArtURL( p_item ); + a.b_found = (i_ret == VLC_EGENERIC ? false : true ); + a.e_scope = p_fetcher->e_scope; + ARRAY_APPEND( p_fetcher->albums, a ); + } } else { @@ -259,18 +345,19 @@ static int DownloadArt( playlist_fetcher_t *p_fetcher, input_item_t *p_item ) if( !strncmp( psz_arturl , "file://", 7 ) ) { - msg_Dbg( p_fetcher, "Album art is local file, no need to cache" ); + msg_Dbg( p_fetcher->object, + "Album art is local file, no need to cache" ); free( psz_arturl ); return VLC_SUCCESS; } if( !strncmp( psz_arturl , "APIC", 4 ) ) { - msg_Warn( p_fetcher, "APIC fetch not supported yet" ); + msg_Warn( p_fetcher->object, "APIC fetch not supported yet" ); goto error; } - stream_t *p_stream = stream_UrlNew( p_fetcher, psz_arturl ); + stream_t *p_stream = stream_UrlNew( p_fetcher->object, psz_arturl ); if( !p_stream ) goto error; @@ -283,7 +370,7 @@ static int DownloadArt( playlist_fetcher_t *p_fetcher, input_item_t *p_item ) if( i_data >= INT_MAX - i_read ) break; - p_data = realloc( p_data, i_data + i_read ); + p_data = realloc_or_free( p_data, i_data + i_read ); if( !p_data ) break; @@ -301,7 +388,8 @@ static int DownloadArt( playlist_fetcher_t *p_fetcher, input_item_t *p_item ) if( psz_type && strlen( psz_type ) > 5 ) psz_type = NULL; /* remove extension if it's > to 4 characters */ - playlist_SaveArt( p_fetcher->p_playlist, p_item, p_data, i_data, psz_type ); + playlist_SaveArt( p_fetcher->object, p_item, + p_data, i_data, psz_type ); } free( p_data ); @@ -314,131 +402,150 @@ error: return VLC_EGENERIC; } - -static int InputEvent( vlc_object_t *p_this, char const *psz_cmd, - vlc_value_t oldval, vlc_value_t newval, void *p_data ) -{ - VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); - playlist_fetcher_t *p_fetcher = p_data; - - if( newval.i_int == INPUT_EVENT_ITEM_META || - newval.i_int == INPUT_EVENT_DEAD ) - vlc_cond_signal( &p_fetcher->wait ); - - return VLC_SUCCESS; -} - - -/* 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... +/** + * FetchMeta, run the "meta fetcher". They are going to do network + * connections, and gather information upon the playing media. + * (even artwork). */ -static void WaitPreparsed( playlist_fetcher_t *p_fetcher, input_item_t *p_item ) +static void FetchMeta( playlist_fetcher_t *p_fetcher, input_item_t *p_item ) { - if( input_item_IsPreparsed( p_item ) ) - return; - - input_thread_t *p_input = playlist_CurrentInput( p_fetcher->p_playlist ); - if( !p_input ) + meta_fetcher_t *p_finder = + vlc_custom_create( p_fetcher->object, sizeof( *p_finder ), "art finder" ); + if ( !p_finder ) return; - if( input_GetItem( p_input ) != p_item ) - goto exit; + p_finder->e_scope = p_fetcher->e_scope; + p_finder->p_item = p_item; - var_AddCallback( p_input, "intf-event", InputEvent, p_fetcher ); - - const mtime_t i_deadline = mdate() + 500*1000; - - while( !p_input->b_eof && !p_input->b_error && !input_item_IsPreparsed( p_item ) ) - { - /* A bit weird, but input_item_IsPreparsed does held the protected value */ - vlc_mutex_lock( &p_fetcher->lock ); - vlc_cond_timedwait( &p_fetcher->wait, &p_fetcher->lock, i_deadline ); - vlc_mutex_unlock( &p_fetcher->lock ); - - if( i_deadline <= mdate() ) - break; - } - - var_DelCallback( p_input, "intf-event", InputEvent, p_fetcher ); + module_t *p_module = module_need( p_finder, "meta fetcher", NULL, false ); + if( p_module ) + module_unneed( p_finder, p_module ); -exit: - vlc_object_release( p_input ); + vlc_object_release( p_finder ); } static void *Thread( void *p_data ) { playlist_fetcher_t *p_fetcher = p_data; - playlist_t *p_playlist = p_fetcher->p_playlist; - + vlc_object_t *obj = p_fetcher->object; + fetcher_pass_t e_pass = PASS1_LOCAL; for( ;; ) { - input_item_t *p_item; - - /* Be sure to be cancellable before our queue is empty */ - vlc_testcancel(); + fetcher_entry_t *p_entry = NULL; - /* */ vlc_mutex_lock( &p_fetcher->lock ); - mutex_cleanup_push( &p_fetcher->lock ); - - while( p_fetcher->i_waiting == 0 ) - vlc_cond_wait( &p_fetcher->wait, &p_fetcher->lock ); - - p_item = p_fetcher->pp_waiting[0]; - REMOVE_ELEM( p_fetcher->pp_waiting, p_fetcher->i_waiting, 0 ); - vlc_cleanup_run( ); - - if( !p_item ) - continue; + for ( int i=0; ip_waiting_head[i] ) + { + e_pass = i; + break; + } + } - /* */ - int canc = vlc_savecancel(); + if( p_fetcher->p_waiting_head[e_pass] ) + { + p_entry = p_fetcher->p_waiting_head[e_pass]; + p_fetcher->p_waiting_head[e_pass] = p_entry->p_next; + if ( p_entry->p_next == NULL ) + p_fetcher->p_waiting_tail[e_pass] = NULL; + p_entry->p_next = NULL; + } + else + { + p_fetcher->b_live = false; + vlc_cond_signal( &p_fetcher->wait ); + } + vlc_mutex_unlock( &p_fetcher->lock ); - /* Wait that the input item is preparsed if it is being played */ - WaitPreparsed( p_fetcher, p_item ); + if( !p_entry ) + break; - /* */ - if( !vlc_object_alive( p_fetcher ) ) - goto end; + meta_fetcher_scope_t e_prev_scope = p_fetcher->e_scope; - /* Find art, and download it if needed */ - int i_ret = FindArt( p_fetcher, p_item ); + /* scope override */ + switch ( p_entry->i_options ) { + case META_REQUEST_OPTION_SCOPE_ANY: + p_fetcher->e_scope = FETCHER_SCOPE_ANY; + break; + case META_REQUEST_OPTION_SCOPE_LOCAL: + p_fetcher->e_scope = FETCHER_SCOPE_LOCAL; + break; + case META_REQUEST_OPTION_SCOPE_NETWORK: + p_fetcher->e_scope = FETCHER_SCOPE_NETWORK; + break; + case META_REQUEST_OPTION_NONE: + default: + break; + } + /* Triggers "meta fetcher", eventually fetch meta on the network. + * They are identical to "meta reader" expect that may actually + * takes time. That's why they are running here. + * The result of this fetch is not cached. */ - /* */ - if( !vlc_object_alive( p_fetcher ) ) - goto end; + int i_ret = -1; - if( i_ret == 1 ) - i_ret = DownloadArt( p_fetcher, p_item ); + if( e_pass == PASS1_LOCAL && ( p_fetcher->e_scope & FETCHER_SCOPE_LOCAL ) ) + { + /* only fetch from local */ + p_fetcher->e_scope = FETCHER_SCOPE_LOCAL; + } + else if( e_pass == PASS2_NETWORK && ( p_fetcher->e_scope & FETCHER_SCOPE_NETWORK ) ) + { + /* only fetch from network */ + p_fetcher->e_scope = FETCHER_SCOPE_NETWORK; + } + else + p_fetcher->e_scope = 0; + if ( p_fetcher->e_scope & FETCHER_SCOPE_ANY ) + { + FetchMeta( p_fetcher, p_entry->p_item ); + i_ret = FindArt( p_fetcher, p_entry->p_item ); + switch( i_ret ) + { + case 1: /* Found, need to dl */ + i_ret = DownloadArt( p_fetcher, p_entry->p_item ); + break; + case 0: /* Is in cache */ + i_ret = VLC_SUCCESS; + //ft + default:// error + break; + } + } + p_fetcher->e_scope = e_prev_scope; /* */ - char *psz_name = input_item_GetName( p_item ); - if( !i_ret ) /* Art is now in cache */ + if ( i_ret != VLC_SUCCESS && (e_pass != PASS2_NETWORK) ) { - PL_DEBUG( "found art for %s in cache", psz_name ); - input_item_SetArtFetched( p_item, true ); - var_SetAddress( p_playlist, "item-change", p_item ); + /* Move our entry to next pass queue */ + vlc_mutex_lock( &p_fetcher->lock ); + if ( p_fetcher->p_waiting_head[e_pass + 1] ) + p_fetcher->p_waiting_tail[e_pass + 1]->p_next = p_entry; + else + p_fetcher->p_waiting_head[e_pass + 1] = p_entry; + p_fetcher->p_waiting_tail[e_pass + 1] = p_entry; + vlc_mutex_unlock( &p_fetcher->lock ); } else { - PL_DEBUG( "art not found for %s", psz_name ); - input_item_SetArtNotFound( p_item, true ); + /* */ + char *psz_name = input_item_GetName( p_entry->p_item ); + if( i_ret == VLC_SUCCESS ) /* Art is now in cache */ + { + msg_Dbg( obj, "found art for %s in cache", psz_name ); + input_item_SetArtFetched( p_entry->p_item, true ); + var_SetAddress( obj, "item-change", p_entry->p_item ); + } + else + { + msg_Dbg( obj, "art not found for %s", psz_name ); + input_item_SetArtNotFound( p_entry->p_item, true ); + } + free( psz_name ); + vlc_gc_decref( p_entry->p_item ); + free( p_entry ); } - free( psz_name ); - - end: - vlc_gc_decref( p_item ); - - vlc_restorecancel( canc ); - - int i_activity = var_GetInteger( p_playlist, "activity" ); - if( i_activity < 0 ) i_activity = 0; - /* Sleep at least 1ms */ - msleep( (i_activity+1) * 1000 ); } return NULL; } - -