[vlc-devel] [PATCH 3/7] playlist/fetcher: refactor
Filip Roséen
filip at atch.se
Tue Mar 28 19:50:07 CEST 2017
The following changes refactors the fetcher to take advantange of the
newly introduced background_worker helper. The new implementation
should not only be easier to maintain, but it also adds some
advantages over the old implementation:
- The implementation has shrunk in size.
- A fetch-request can include a timeout.
- Given that there now is a background worker associated with each of
the different fetcher types (local, network, download):
- A slow download does not prevent the network-fetcher from
working the queue.
- A slow network-fetcher does not prevent further work in regards
of pending requests in the local fetcher.
- A fetch request can now be properly cancelled (most importantly
during VLC close).
- We no longer invoke modules with "meta fetcher" capability if the
item already has all metadata in terms of title, album, and artist.
- We no longer invoke modules with "art finder" capability of the
item already has vlc_meta_ArtworkUrl.
fixes: #18150
--
Changes since last submission:
- adjust to changes in terms of background-worker api
- add missing error-check in terms of metadata-probing
- slight refactor (logic is overall the same)
---
src/playlist/fetcher.c | 770 +++++++++++++++++++++----------------------------
1 file changed, 327 insertions(+), 443 deletions(-)
diff --git a/src/playlist/fetcher.c b/src/playlist/fetcher.c
index 1fdb64cee5..7e9f4124a5 100644
--- a/src/playlist/fetcher.c
+++ b/src/playlist/fetcher.c
@@ -1,12 +1,9 @@
/*****************************************************************************
- * fetcher.c: Art fetcher thread.
+ * fetcher.c
*****************************************************************************
- * Copyright © 1999-2009 VLC authors and VideoLAN
+ * Copyright © 2017-2017 VLC authors and VideoLAN
* $Id$
*
- * Authors: Samuel Hocevar <sam at zoy.org>
- * Clément Stenac <zorglub at videolan.org>
- *
* 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
@@ -21,555 +18,442 @@
* 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 <limits.h>
-#include <assert.h>
-
#include <vlc_common.h>
#include <vlc_stream.h>
-#include <vlc_meta_fetcher.h>
-#include <vlc_memory.h>
-#include <vlc_demux.h>
#include <vlc_modules.h>
#include <vlc_interrupt.h>
+#include <vlc_arrays.h>
+#include <vlc_atomic.h>
+#include <vlc_threads.h>
+#include <vlc_memstream.h>
+#include <vlc_meta_fetcher.h>
-#include "libvlc.h"
#include "art.h"
+#include "libvlc.h"
#include "fetcher.h"
#include "input/input_interface.h"
+#include "misc/background_worker.h"
+#include "misc/interrupt.h"
-/*****************************************************************************
- * Structures/definitions
- *****************************************************************************/
-typedef enum
-{
- PASS1_LOCAL = 0,
- PASS2_NETWORK
-} fetcher_pass_t;
-#define PASS_COUNT 2
+struct playlist_fetcher_t {
+ struct background_worker* local;
+ struct background_worker* network;
+ struct background_worker* downloader;
-typedef struct
-{
- char *psz_artist;
- char *psz_album;
- char *psz_arturl;
- bool b_found;
- meta_fetcher_scope_t e_scope; /* max scope */
-
-} playlist_album_t;
-
-typedef struct fetcher_entry_t fetcher_entry_t;
+ vlc_dictionary_t album_cache;
+ vlc_object_t* owner;
+ vlc_mutex_t lock;
+};
-struct fetcher_entry_t
-{
- input_item_t *p_item;
- input_item_meta_request_option_t i_options;
- fetcher_entry_t *p_next;
+struct fetcher_request {
+ input_item_t* item;
+ atomic_uint refs;
+ int options;
};
-struct playlist_fetcher_t
-{
- vlc_object_t *object;
- vlc_mutex_t lock;
- vlc_cond_t wait;
- bool b_live;
- vlc_interrupt_t *interrupt;
+struct fetcher_thread {
+ void (*pf_worker)( playlist_fetcher_t*, struct fetcher_request* );
- fetcher_entry_t *p_waiting_head[PASS_COUNT];
- fetcher_entry_t *p_waiting_tail[PASS_COUNT];
+ struct background_worker* worker;
+ struct fetcher_request* req;
+ playlist_fetcher_t* fetcher;
- DECL_ARRAY(playlist_album_t) albums;
- meta_fetcher_scope_t e_scope;
+ vlc_interrupt_t interrupt;
+ vlc_thread_t thread;
+ atomic_bool active;
};
-static void *Thread( void * );
-
-
-/*****************************************************************************
- * Public functions
- *****************************************************************************/
-playlist_fetcher_t *playlist_fetcher_New( vlc_object_t *parent )
+static char* CreateCacheKey( input_item_t* item )
{
- playlist_fetcher_t *p_fetcher = malloc( sizeof(*p_fetcher) );
- if( !p_fetcher )
- return NULL;
+ vlc_mutex_lock( &item->lock );
- p_fetcher->interrupt = vlc_interrupt_create();
- if( unlikely(p_fetcher->interrupt == NULL) )
+ if( !item->p_meta )
{
- free( p_fetcher );
+ vlc_mutex_unlock( &item->lock );
return NULL;
}
- p_fetcher->object = parent;
- vlc_mutex_init( &p_fetcher->lock );
- vlc_cond_init( &p_fetcher->wait );
- p_fetcher->b_live = false;
- if( var_InheritBool( parent, "metadata-network-access" ) )
- p_fetcher->e_scope = FETCHER_SCOPE_ANY;
- else
- p_fetcher->e_scope = FETCHER_SCOPE_LOCAL;
+ char const* artist = vlc_meta_Get( item->p_meta, vlc_meta_Artist );
+ char const* album = vlc_meta_Get( item->p_meta, vlc_meta_Album );
+ char* key;
- 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 );
+ /**
+ * Simple concatenation of artist and album can lead to the same key
+ * for entities that should not have such. Imagine { dogs, tick } and
+ * { dog, stick } */
+ if( !artist || !album || asprintf( &key, "%s:%zu:%s:%zu",
+ artist, strlen( artist ), album, strlen( album ) ) < 0 )
+ {
+ key = NULL;
+ }
+ vlc_mutex_unlock( &item->lock );
- return p_fetcher;
+ return key;
}
-void playlist_fetcher_Push( playlist_fetcher_t *p_fetcher, input_item_t *p_item,
- input_item_meta_request_option_t i_options )
+static void FreeCacheEntry( void* data, void* obj )
{
- 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 );
- /* 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 );
+ free( data );
+ VLC_UNUSED( obj );
}
-void playlist_fetcher_Delete( playlist_fetcher_t *p_fetcher )
+static int ReadAlbumCache( playlist_fetcher_t* fetcher, input_item_t* item )
{
- fetcher_entry_t *p_next;
+ char* key = CreateCacheKey( item );
- vlc_interrupt_kill(p_fetcher->interrupt);
+ if( key == NULL )
+ return VLC_EGENERIC;
+
+ vlc_mutex_lock( &fetcher->lock );
+ char const* art = vlc_dictionary_value_for_key( &fetcher->album_cache,
+ key );
+ if( art )
+ input_item_SetArtURL( item, art );
+ vlc_mutex_unlock( &fetcher->lock );
+
+ free( key );
+ return art ? VLC_SUCCESS : VLC_EGENERIC;
+}
+
+static void AddAlbumCache( playlist_fetcher_t* fetcher, input_item_t* item,
+ bool overwrite )
+{
+ char* art = input_item_GetArtURL( item );
+ char* key = CreateCacheKey( item );
- vlc_mutex_lock( &p_fetcher->lock );
- /* Remove any left-over item, the fetcher will exit */
- for ( int i_queue=0; i_queue<PASS_COUNT; i_queue++ )
+ if( key && art && strncasecmp( art, "attachment://", 13 ) )
{
- while( p_fetcher->p_waiting_head[i_queue] )
+ vlc_mutex_lock( &fetcher->lock );
+ if( overwrite || !vlc_dictionary_has_key( &fetcher->album_cache, key ) )
{
- 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;
+ vlc_dictionary_insert( &fetcher->album_cache, key, art );
+ art = NULL;
}
- p_fetcher->p_waiting_head[i_queue] = NULL;
+ vlc_mutex_unlock( &fetcher->lock );
}
- while( p_fetcher->b_live )
- vlc_cond_wait( &p_fetcher->wait, &p_fetcher->lock );
- vlc_mutex_unlock( &p_fetcher->lock );
+ free( art );
+ free( key );
+}
- vlc_cond_destroy( &p_fetcher->wait );
- vlc_mutex_destroy( &p_fetcher->lock );
+static int InvokeModule( playlist_fetcher_t* fetcher, input_item_t* item,
+ int scope, char const* type )
+{
+ meta_fetcher_t* mf = vlc_custom_create( fetcher->owner,
+ sizeof( *mf ), type );
+ if( unlikely( !mf ) )
+ return VLC_ENOMEM;
- vlc_interrupt_destroy( p_fetcher->interrupt );
+ mf->e_scope = scope;
+ mf->p_item = item;
- playlist_album_t album;
- FOREACH_ARRAY( album, p_fetcher->albums )
- free( album.psz_album );
- free( album.psz_artist );
- free( album.psz_arturl );
- FOREACH_END()
- ARRAY_RESET( p_fetcher->albums );
+ module_t* mf_module = module_need( mf, type, NULL, false );
- free( p_fetcher );
-}
+ if( mf_module )
+ module_unneed( mf, mf_module );
+ vlc_object_release( mf );
-/*****************************************************************************
- * Privates functions
- *****************************************************************************/
-/**
- * This function locates the art associated to an input item.
- * Return codes:
- * 0 : Art is in cache or is a local file
- * 1 : Art found, need to download
- * -X : Error/not found
- */
-static int FindArt( playlist_fetcher_t *p_fetcher, input_item_t *p_item )
+ return VLC_SUCCESS;
+}
+
+static int CheckMeta( input_item_t* item )
{
- int i_ret;
+ vlc_mutex_lock( &item->lock );
+ bool error = !item->p_meta ||
+ !vlc_meta_Get( item->p_meta, vlc_meta_Title ) ||
+ !vlc_meta_Get( item->p_meta, vlc_meta_Artist ) ||
+ !vlc_meta_Get( item->p_meta, vlc_meta_Album );
+ vlc_mutex_unlock( &item->lock );
+ return error;
+}
- 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 );
+static int CheckArt( input_item_t* item )
+{
+ vlc_mutex_lock( &item->lock );
+ bool error = !item->p_meta ||
+ !vlc_meta_Get( item->p_meta, vlc_meta_ArtworkURL );
+ vlc_mutex_unlock( &item->lock );
+ return error;
+}
- if( !psz_title && !psz_artist && !psz_album )
- return VLC_EGENERIC;
+static int SearchArt( playlist_fetcher_t* fetcher, input_item_t* item, int scope)
+{
+ InvokeModule( fetcher, item, scope, "art finder" );
+ return CheckArt( item );
+}
- free( psz_title );
+static int SearchByScope( playlist_fetcher_t* fetcher,
+ struct fetcher_request* req, int scope )
+{
+ input_item_t* item = req->item;
- /* If we already checked this album in this session, skip */
- if( psz_artist && psz_album )
+ if( CheckMeta( item ) &&
+ InvokeModule( fetcher, req->item, scope, "meta fetcher" ) )
{
- FOREACH_ARRAY( playlist_album_t album, p_fetcher->albums )
- if( !strcmp( album.psz_artist, psz_artist ) &&
- !strcmp( album.psz_album, psz_album ) )
- {
- 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 );
- free( psz_album );
- if( album.b_found )
- {
- if( !strncmp( album.psz_arturl, "file://", 7 ) )
- input_item_SetArtURL( p_item, album.psz_arturl );
- else /* Actually get URL from cache */
- playlist_FindArtInCache( p_item );
- return 0;
- }
- 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();
+ return VLC_EGENERIC;
}
- free( psz_artist );
- free( psz_album );
+ if( ! CheckArt( item ) ||
+ ! ReadAlbumCache( fetcher, item ) ||
+ ! playlist_FindArtInCacheUsingItemUID( item ) ||
+ ! playlist_FindArtInCache( item ) ||
+ ! SearchArt( fetcher, item, scope ) )
+ {
+ AddAlbumCache( fetcher, req->item, false );
+ background_worker_Push( fetcher->downloader, req, NULL, 0 );
+ return VLC_SUCCESS;
+ }
- if ( playlist_FindArtInCacheUsingItemUID( p_item ) != VLC_SUCCESS )
- playlist_FindArtInCache( p_item );
- else
- msg_Dbg( p_fetcher->object, "successfully retrieved arturl by uid" );
+ return VLC_EGENERIC;
+}
- char *psz_arturl = input_item_GetArtURL( p_item );
- if( psz_arturl )
- {
- /* We already have a URL */
- if( !strncmp( psz_arturl, "file://", strlen( "file://" ) ) )
- {
- free( psz_arturl );
- return 0; /* Art is in cache, no need to go further */
- }
+static void Downloader( playlist_fetcher_t* fetcher,
+ struct fetcher_request* req )
+{
+ ReadAlbumCache( fetcher, req->item );
- free( psz_arturl );
+ char *psz_arturl = input_item_GetArtURL( req->item );
+ if( !psz_arturl )
+ goto error;
- /* Art need to be put in cache */
- return 1;
- }
+ if( !strncasecmp( psz_arturl, "file://", 7 ) ||
+ !strncasecmp( psz_arturl, "attachment://", 13 ) )
+ goto out; /* no fetch required */
- /* */
- psz_album = input_item_GetAlbum( p_item );
- psz_artist = input_item_GetArtist( p_item );
- if( psz_album && psz_artist )
- {
- msg_Dbg( p_fetcher->object, "searching art for %s - %s",
- psz_artist, psz_album );
- }
- else
- {
- psz_title = input_item_GetTitle( p_item );
- if( !psz_title )
- psz_title = input_item_GetName( p_item );
+ stream_t* source = vlc_stream_NewURL( fetcher->owner, psz_arturl );
- msg_Dbg( p_fetcher->object, "searching art for %s", psz_title );
- free( psz_title );
- }
+ if( !source )
+ goto error;
- /* Fetch the art url */
- i_ret = VLC_EGENERIC;
+ struct vlc_memstream output_stream;
+ vlc_memstream_open( &output_stream );
- 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)
+ for( ;; )
{
- module_t *p_module;
+ char buffer[2048];
- p_finder->p_item = p_item;
- p_finder->e_scope = p_fetcher->e_scope;
+ int read = vlc_stream_Read( source, buffer, sizeof( buffer ) );
+ if( read <= 0 )
+ break;
- 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 );
+ if( (int)vlc_memstream_write( &output_stream, buffer, read ) < read )
+ break;
}
- /* Record this album */
- if( psz_artist && psz_album )
+ vlc_stream_Delete( source );
+
+ if( vlc_memstream_close( &output_stream ) )
+ goto error;
+
+ if( vlc_killed() )
{
- 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 );
- }
+ free( output_stream.ptr );
+ goto error;
}
- else
+
+ playlist_SaveArt( fetcher->owner, req->item, output_stream.ptr,
+ output_stream.length, NULL );
+
+ free( output_stream.ptr );
+ AddAlbumCache( fetcher, req->item, true );
+
+out:
+ if( psz_arturl )
{
- free( psz_artist );
- free( psz_album );
+ var_SetAddress( fetcher->owner, "item-change", req->item );
+ input_item_SetArtFetched( req->item, true );
}
- return i_ret;
+ free( psz_arturl );
+ return;
+
+error:
+ FREENULL( psz_arturl );
+ goto out;
}
-/**
- * Download the art using the URL or an art downloaded
- * This function should be called only if data is not already in cache
- */
-static int DownloadArt( playlist_fetcher_t *p_fetcher, input_item_t *p_item )
+static void SearchLocal( playlist_fetcher_t* fetcher, struct fetcher_request* req )
{
- char *psz_arturl = input_item_GetArtURL( p_item );
- assert( *psz_arturl );
+ if( SearchByScope( fetcher, req, FETCHER_SCOPE_LOCAL ) == VLC_SUCCESS )
+ return; /* done */
- if( !strncasecmp( psz_arturl , "file://", 7 ) )
+ if( var_InheritBool( fetcher->owner, "metadata-network-access" ) ||
+ req->options & META_REQUEST_OPTION_SCOPE_NETWORK )
{
- msg_Dbg( p_fetcher->object,
- "Album art is local file, no need to cache" );
- free( psz_arturl );
- return VLC_SUCCESS;
+ background_worker_Push( fetcher->network, req, NULL, 0 );
}
+ else input_item_SetArtNotFound( req->item, true );
+}
- if( !strncmp( psz_arturl , "APIC", 4 ) )
- {
- msg_Warn( p_fetcher->object, "APIC fetch not supported yet" );
- goto error;
- }
+static void SearchNetwork( playlist_fetcher_t* fetcher, struct fetcher_request* req )
+{
+ if( SearchByScope( fetcher, req, FETCHER_SCOPE_NETWORK ) )
+ input_item_SetArtNotFound( req->item, true );
+}
- stream_t *p_stream = vlc_stream_NewURL( p_fetcher->object, psz_arturl );
- if( !p_stream )
- goto error;
+static void RequestRelease( void* req_ )
+{
+ struct fetcher_request* req = req_;
- uint8_t *p_data = NULL;
- int i_data = 0;
- for( ;; )
- {
- int i_read = 65536;
+ if( atomic_fetch_sub( &req->refs, 1 ) != 1 )
+ return;
- if( i_data >= INT_MAX - i_read )
- break;
+ input_item_Release( req->item );
+ free( req );
+}
- p_data = realloc_or_free( p_data, i_data + i_read );
- if( !p_data )
- break;
+static void RequestHold( void* req_ )
+{
+ struct fetcher_request* req = req_;
+ atomic_fetch_add_explicit( &req->refs, 1, memory_order_relaxed );
+}
- i_read = vlc_stream_Read( p_stream, &p_data[i_data], i_read );
- if( i_read <= 0 )
- break;
+static void* FetcherThread( void* handle )
+{
+ struct fetcher_thread* th = handle;
+ vlc_interrupt_set( &th->interrupt );
- i_data += i_read;
- }
- vlc_stream_Delete( p_stream );
+ th->pf_worker( th->fetcher, th->req );
- if( p_data && i_data > 0 )
- {
- char *psz_type = strrchr( psz_arturl, '.' );
- if( psz_type && strlen( psz_type ) > 5 )
- psz_type = NULL; /* remove extension if it's > to 4 characters */
+ atomic_store( &th->active, false );
+ background_worker_RequestProbe( th->worker );
+ return NULL;
+}
- playlist_SaveArt( p_fetcher->object, p_item,
- p_data, i_data, psz_type );
- }
+static int StartWorker( playlist_fetcher_t* fetcher,
+ void( *pf_worker )( playlist_fetcher_t*, struct fetcher_request* ),
+ struct background_worker* bg, struct fetcher_request* req, void** handle )
+{
+ struct fetcher_thread* th = malloc( sizeof *th );
- free( p_data );
+ if( unlikely( !th ) )
+ return VLC_ENOMEM;
- free( psz_arturl );
- return VLC_SUCCESS;
+ th->req = req;
+ th->worker = bg;
+ th->fetcher = fetcher;
+ th->pf_worker = pf_worker;
-error:
- free( psz_arturl );
+ vlc_interrupt_init( &th->interrupt );
+ atomic_init( &th->active, true );
+
+ if( !vlc_clone( &th->thread, FetcherThread, th, VLC_THREAD_PRIORITY_LOW ) )
+ {
+ *handle = th;
+ return VLC_SUCCESS;
+ }
+
+ vlc_interrupt_deinit( &th->interrupt );
+ free( th );
return VLC_EGENERIC;
}
-/**
- * FetchMeta, run the "meta fetcher". They are going to do network
- * connections, and gather information upon the playing media.
- * (even artwork).
- */
-static void FetchMeta( playlist_fetcher_t *p_fetcher, input_item_t *p_item )
+static int ProbeWorker( void* fetcher_, void* th_ )
{
- meta_fetcher_t *p_finder =
- vlc_custom_create( p_fetcher->object, sizeof( *p_finder ), "art finder" );
- if ( !p_finder )
- return;
+ return !atomic_load( &((struct fetcher_thread*)th_)->active );
+ VLC_UNUSED( fetcher_ );
+}
+
+static void CloseWorker( void* fetcher_, void* th_ )
+{
+ struct fetcher_thread* th = th_;
+ VLC_UNUSED( fetcher_ );
+
+ vlc_interrupt_kill( &th->interrupt );
+ vlc_join( th->thread, NULL );
+ vlc_interrupt_deinit( &th->interrupt );
+ free( th );
+}
- p_finder->e_scope = p_fetcher->e_scope;
- p_finder->p_item = p_item;
+#define DEF_STARTER(name, worker) \
+static int Start ## name( void* fetcher_, void* req_, void** out ) { \
+ playlist_fetcher_t* fetcher = fetcher_; \
+ return StartWorker( fetcher, name, worker, req_, out ); }
- module_t *p_module = module_need( p_finder, "meta fetcher", NULL, false );
- if( p_module )
- module_unneed( p_finder, p_module );
+DEF_STARTER( SearchLocal, fetcher->local )
+DEF_STARTER(SearchNetwork, fetcher->network )
+DEF_STARTER( Downloader, fetcher->downloader )
- vlc_object_release( p_finder );
+static void WorkerInit( playlist_fetcher_t* fetcher,
+ struct background_worker** worker, int( *starter )( void*, void*, void** ) )
+{
+ struct background_worker_config conf = {
+ .default_timeout = 0,
+ .pf_start = starter,
+ .pf_probe = ProbeWorker,
+ .pf_stop = CloseWorker,
+ .pf_release = RequestRelease,
+ .pf_hold = RequestHold };
+
+ *worker = background_worker_New( fetcher, &conf );
}
-static void *Thread( void *p_data )
+playlist_fetcher_t* playlist_fetcher_New( vlc_object_t* owner )
{
- playlist_fetcher_t *p_fetcher = p_data;
- vlc_object_t *obj = p_fetcher->object;
- fetcher_pass_t e_pass = PASS1_LOCAL;
+ playlist_fetcher_t* fetcher = malloc( sizeof( *fetcher ) );
- vlc_interrupt_set(p_fetcher->interrupt);
+ if( unlikely( !fetcher ) )
+ return NULL;
- for( ;; )
+ fetcher->owner = owner;
+
+ WorkerInit( fetcher, &fetcher->local, StartSearchLocal );
+ WorkerInit( fetcher, &fetcher->network, StartSearchNetwork );
+ WorkerInit( fetcher, &fetcher->downloader, StartDownloader );
+
+ if( unlikely( !fetcher->local || !fetcher->network || !fetcher->downloader ) )
{
- fetcher_entry_t *p_entry = NULL;
+ if( fetcher->local )
+ background_worker_Delete( fetcher->local );
- vlc_mutex_lock( &p_fetcher->lock );
- for ( int i=0; i<PASS_COUNT; i++ )
- {
- if ( p_fetcher->p_waiting_head[i] )
- {
- e_pass = i;
- break;
- }
- }
+ if( fetcher->network )
+ background_worker_Delete( fetcher->network );
- 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
- {
- vlc_interrupt_set( NULL );
- p_fetcher->b_live = false;
- vlc_cond_signal( &p_fetcher->wait );
- }
- vlc_mutex_unlock( &p_fetcher->lock );
+ if( fetcher->downloader )
+ background_worker_Delete( fetcher->downloader );
- if( !p_entry )
- break;
+ free( fetcher );
+ return NULL;
+ }
- meta_fetcher_scope_t e_prev_scope = p_fetcher->e_scope;
+ vlc_mutex_init( &fetcher->lock );
+ vlc_dictionary_init( &fetcher->album_cache, 0 );
- /* 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. */
+ return fetcher;
+}
- int i_ret = -1;
+void playlist_fetcher_Push( playlist_fetcher_t* fetcher, input_item_t* item,
+ input_item_meta_request_option_t options )
+{
+ struct fetcher_request* req = malloc( sizeof *req );
- 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;
- }
- }
+ if( unlikely( !req ) )
+ return;
- p_fetcher->e_scope = e_prev_scope;
- /* */
- if ( i_ret != VLC_SUCCESS && (e_pass != PASS2_NETWORK) )
- {
- /* 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
- {
- /* */
- 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 );
- }
- }
- return NULL;
+ req->item = item;
+ req->options = options;
+
+ atomic_init( &req->refs, 1 );
+ input_item_Hold( item );
+
+ background_worker_Push( fetcher->local, req, NULL, 0 );
+ RequestRelease( req );
+}
+
+void playlist_fetcher_Delete( playlist_fetcher_t* fetcher )
+{
+ background_worker_Delete( fetcher->local );
+ background_worker_Delete( fetcher->network );
+ background_worker_Delete( fetcher->downloader );
+
+ vlc_dictionary_clear( &fetcher->album_cache, FreeCacheEntry, NULL );
+ vlc_mutex_destroy( &fetcher->lock );
+
+ free( fetcher );
}
--
2.12.1
More information about the vlc-devel
mailing list