omxil: add debug logs for buffer allocation.
[vlc.git] / src / playlist / fetcher.c
blobeb86994933bc0223232058a6eb42bd7856bade19
1 /*****************************************************************************
2 * fetcher.c: Art fetcher thread.
3 *****************************************************************************
4 * Copyright © 1999-2009 VLC authors and VideoLAN
5 * $Id$
7 * Authors: Samuel Hocevar <sam@zoy.org>
8 * Clément Stenac <zorglub@videolan.org>
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU Lesser General Public License as published by
12 * the Free Software Foundation; either version 2.1 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public License
21 * along with this program; if not, write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 *****************************************************************************/
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
28 #include <limits.h>
29 #include <assert.h>
31 #include <vlc_common.h>
32 #include <vlc_stream.h>
33 #include <vlc_meta_fetcher.h>
34 #include <vlc_memory.h>
35 #include <vlc_demux.h>
36 #include <vlc_modules.h>
38 #include "libvlc.h"
39 #include "art.h"
40 #include "fetcher.h"
41 #include "input/input_interface.h"
43 /*****************************************************************************
44 * Structures/definitions
45 *****************************************************************************/
46 typedef enum
48 PASS1_LOCAL = 0,
49 PASS2_NETWORK
50 } fetcher_pass_t;
51 #define PASS_COUNT 2
53 typedef struct
55 char *psz_artist;
56 char *psz_album;
57 char *psz_arturl;
58 bool b_found;
59 meta_fetcher_scope_t e_scope; /* max scope */
61 } playlist_album_t;
63 typedef struct fetcher_entry_t fetcher_entry_t;
65 struct fetcher_entry_t
67 input_item_t *p_item;
68 input_item_meta_request_option_t i_options;
69 fetcher_entry_t *p_next;
72 struct playlist_fetcher_t
74 vlc_object_t *object;
75 vlc_mutex_t lock;
76 vlc_cond_t wait;
77 bool b_live;
79 fetcher_entry_t *p_waiting_head[PASS_COUNT];
80 fetcher_entry_t *p_waiting_tail[PASS_COUNT];
82 DECL_ARRAY(playlist_album_t) albums;
83 meta_fetcher_scope_t e_scope;
86 static void *Thread( void * );
89 /*****************************************************************************
90 * Public functions
91 *****************************************************************************/
92 playlist_fetcher_t *playlist_fetcher_New( vlc_object_t *parent )
94 playlist_fetcher_t *p_fetcher = malloc( sizeof(*p_fetcher) );
95 if( !p_fetcher )
96 return NULL;
98 p_fetcher->object = parent;
99 vlc_mutex_init( &p_fetcher->lock );
100 vlc_cond_init( &p_fetcher->wait );
101 p_fetcher->b_live = false;
103 bool b_access = var_InheritBool( parent, "metadata-network-access" );
104 if ( !b_access )
105 b_access = ( var_InheritInteger( parent, "album-art" ) == ALBUM_ART_ALL );
107 p_fetcher->e_scope = ( b_access ) ? FETCHER_SCOPE_ANY : FETCHER_SCOPE_LOCAL;
109 memset( p_fetcher->p_waiting_head, 0, PASS_COUNT * sizeof(fetcher_entry_t *) );
110 memset( p_fetcher->p_waiting_tail, 0, PASS_COUNT * sizeof(fetcher_entry_t *) );
112 ARRAY_INIT( p_fetcher->albums );
114 return p_fetcher;
117 void playlist_fetcher_Push( playlist_fetcher_t *p_fetcher, input_item_t *p_item,
118 input_item_meta_request_option_t i_options )
120 fetcher_entry_t *p_entry = malloc( sizeof(fetcher_entry_t) );
121 if ( !p_entry ) return;
123 vlc_gc_incref( p_item );
124 p_entry->p_item = p_item;
125 p_entry->p_next = NULL;
126 p_entry->i_options = i_options;
127 vlc_mutex_lock( &p_fetcher->lock );
128 /* Append last */
129 if ( p_fetcher->p_waiting_head[PASS1_LOCAL] )
130 p_fetcher->p_waiting_tail[PASS1_LOCAL]->p_next = p_entry;
131 else
132 p_fetcher->p_waiting_head[PASS1_LOCAL] = p_entry;
133 p_fetcher->p_waiting_tail[PASS1_LOCAL] = p_entry;
135 if( !p_fetcher->b_live )
137 assert( p_fetcher->p_waiting_head[PASS1_LOCAL] );
138 if( vlc_clone_detach( NULL, Thread, p_fetcher,
139 VLC_THREAD_PRIORITY_LOW ) )
140 msg_Err( p_fetcher->object,
141 "cannot spawn secondary preparse thread" );
142 else
143 p_fetcher->b_live = true;
145 vlc_mutex_unlock( &p_fetcher->lock );
148 void playlist_fetcher_Delete( playlist_fetcher_t *p_fetcher )
150 fetcher_entry_t *p_next;
151 vlc_mutex_lock( &p_fetcher->lock );
152 /* Remove any left-over item, the fetcher will exit */
153 for ( int i_queue=0; i_queue<PASS_COUNT; i_queue++ )
155 while( p_fetcher->p_waiting_head[i_queue] )
157 p_next = p_fetcher->p_waiting_head[i_queue]->p_next;
158 vlc_gc_decref( p_fetcher->p_waiting_head[i_queue]->p_item );
159 free( p_fetcher->p_waiting_head[i_queue] );
160 p_fetcher->p_waiting_head[i_queue] = p_next;
162 p_fetcher->p_waiting_head[i_queue] = NULL;
165 while( p_fetcher->b_live )
166 vlc_cond_wait( &p_fetcher->wait, &p_fetcher->lock );
167 vlc_mutex_unlock( &p_fetcher->lock );
169 vlc_cond_destroy( &p_fetcher->wait );
170 vlc_mutex_destroy( &p_fetcher->lock );
172 free( p_fetcher );
176 /*****************************************************************************
177 * Privates functions
178 *****************************************************************************/
180 * This function locates the art associated to an input item.
181 * Return codes:
182 * 0 : Art is in cache or is a local file
183 * 1 : Art found, need to download
184 * -X : Error/not found
186 static int FindArt( playlist_fetcher_t *p_fetcher, input_item_t *p_item )
188 int i_ret;
190 playlist_album_t *p_album = NULL;
191 char *psz_artist = input_item_GetArtist( p_item );
192 char *psz_album = input_item_GetAlbum( p_item );
193 char *psz_title = input_item_GetTitle( p_item );
194 if( !psz_title )
195 psz_title = input_item_GetName( p_item );
197 if( !psz_title && !psz_artist && !psz_album )
198 return VLC_EGENERIC;
200 free( psz_title );
202 /* If we already checked this album in this session, skip */
203 if( psz_artist && psz_album )
205 FOREACH_ARRAY( playlist_album_t album, p_fetcher->albums )
206 if( !strcmp( album.psz_artist, psz_artist ) &&
207 !strcmp( album.psz_album, psz_album ) )
209 msg_Dbg( p_fetcher->object,
210 " %s - %s has already been searched",
211 psz_artist, psz_album );
212 /* TODO-fenrir if we cache art filename too, we can go faster */
213 free( psz_artist );
214 free( psz_album );
215 if( album.b_found )
217 if( !strncmp( album.psz_arturl, "file://", 7 ) )
218 input_item_SetArtURL( p_item, album.psz_arturl );
219 else /* Actually get URL from cache */
220 playlist_FindArtInCache( p_item );
221 return 0;
223 else if ( album.e_scope >= p_fetcher->e_scope )
225 return VLC_EGENERIC;
227 msg_Dbg( p_fetcher->object,
228 " will search at higher scope, if possible" );
229 p_album = &p_fetcher->albums.p_elems[fe_idx];
230 break;
232 FOREACH_END();
234 else
236 free( psz_artist );
237 free( psz_album );
240 if ( playlist_FindArtInCacheUsingItemUID( p_item ) != VLC_SUCCESS )
241 playlist_FindArtInCache( p_item );
242 else
243 msg_Dbg( p_fetcher->object, "successfully retrieved arturl by uid" );
245 char *psz_arturl = input_item_GetArtURL( p_item );
246 if( psz_arturl )
248 /* We already have a URL */
249 if( !strncmp( psz_arturl, "file://", strlen( "file://" ) ) )
251 free( psz_arturl );
252 return 0; /* Art is in cache, no need to go further */
255 free( psz_arturl );
257 /* Art need to be put in cache */
258 return 1;
261 /* */
262 psz_album = input_item_GetAlbum( p_item );
263 psz_artist = input_item_GetArtist( p_item );
264 if( psz_album && psz_artist )
266 msg_Dbg( p_fetcher->object, "searching art for %s - %s",
267 psz_artist, psz_album );
269 else
271 psz_title = input_item_GetTitle( p_item );
272 if( !psz_title )
273 psz_title = input_item_GetName( p_item );
275 msg_Dbg( p_fetcher->object, "searching art for %s", psz_title );
276 free( psz_title );
279 /* Fetch the art url */
280 i_ret = VLC_EGENERIC;
282 vlc_object_t *p_parent = p_fetcher->object;
283 meta_fetcher_t *p_finder =
284 vlc_custom_create( p_parent, sizeof( *p_finder ), "art finder" );
285 if( p_finder != NULL)
287 module_t *p_module;
289 p_finder->p_item = p_item;
290 p_finder->e_scope = p_fetcher->e_scope;
292 p_module = module_need( p_finder, "art finder", NULL, false );
293 if( p_module )
295 module_unneed( p_finder, p_module );
296 /* Try immediately if found in cache by download URL */
297 if( !playlist_FindArtInCache( p_item ) )
298 i_ret = 0;
299 else
300 i_ret = 1;
302 vlc_object_release( p_finder );
305 /* Record this album */
306 if( psz_artist && psz_album )
308 if ( p_album )
310 p_album->e_scope = p_fetcher->e_scope;
311 free( p_album->psz_arturl );
312 p_album->psz_arturl = input_item_GetArtURL( p_item );
313 p_album->b_found = (i_ret == VLC_EGENERIC ? false : true );
314 free( psz_artist );
315 free( psz_album );
317 else
319 playlist_album_t a;
320 a.psz_artist = psz_artist;
321 a.psz_album = psz_album;
322 a.psz_arturl = input_item_GetArtURL( p_item );
323 a.b_found = (i_ret == VLC_EGENERIC ? false : true );
324 a.e_scope = p_fetcher->e_scope;
325 ARRAY_APPEND( p_fetcher->albums, a );
328 else
330 free( psz_artist );
331 free( psz_album );
334 return i_ret;
338 * Download the art using the URL or an art downloaded
339 * This function should be called only if data is not already in cache
341 static int DownloadArt( playlist_fetcher_t *p_fetcher, input_item_t *p_item )
343 char *psz_arturl = input_item_GetArtURL( p_item );
344 assert( *psz_arturl );
346 if( !strncmp( psz_arturl , "file://", 7 ) )
348 msg_Dbg( p_fetcher->object,
349 "Album art is local file, no need to cache" );
350 free( psz_arturl );
351 return VLC_SUCCESS;
354 if( !strncmp( psz_arturl , "APIC", 4 ) )
356 msg_Warn( p_fetcher->object, "APIC fetch not supported yet" );
357 goto error;
360 stream_t *p_stream = stream_UrlNew( p_fetcher->object, psz_arturl );
361 if( !p_stream )
362 goto error;
364 uint8_t *p_data = NULL;
365 int i_data = 0;
366 for( ;; )
368 int i_read = 65536;
370 if( i_data >= INT_MAX - i_read )
371 break;
373 p_data = realloc_or_free( p_data, i_data + i_read );
374 if( !p_data )
375 break;
377 i_read = stream_Read( p_stream, &p_data[i_data], i_read );
378 if( i_read <= 0 )
379 break;
381 i_data += i_read;
383 stream_Delete( p_stream );
385 if( p_data && i_data > 0 )
387 char *psz_type = strrchr( psz_arturl, '.' );
388 if( psz_type && strlen( psz_type ) > 5 )
389 psz_type = NULL; /* remove extension if it's > to 4 characters */
391 playlist_SaveArt( p_fetcher->object, p_item,
392 p_data, i_data, psz_type );
395 free( p_data );
397 free( psz_arturl );
398 return VLC_SUCCESS;
400 error:
401 free( psz_arturl );
402 return VLC_EGENERIC;
406 * FetchMeta, run the "meta fetcher". They are going to do network
407 * connections, and gather information upon the playing media.
408 * (even artwork).
410 static void FetchMeta( playlist_fetcher_t *p_fetcher, input_item_t *p_item )
412 meta_fetcher_t *p_finder =
413 vlc_custom_create( p_fetcher->object, sizeof( *p_finder ), "art finder" );
414 if ( !p_finder )
415 return;
417 p_finder->e_scope = p_fetcher->e_scope;
418 p_finder->p_item = p_item;
420 module_t *p_module = module_need( p_finder, "meta fetcher", NULL, false );
421 if( p_module )
422 module_unneed( p_finder, p_module );
424 vlc_object_release( p_finder );
427 static void *Thread( void *p_data )
429 playlist_fetcher_t *p_fetcher = p_data;
430 vlc_object_t *obj = p_fetcher->object;
431 fetcher_pass_t e_pass = PASS1_LOCAL;
432 for( ;; )
434 fetcher_entry_t *p_entry = NULL;
436 vlc_mutex_lock( &p_fetcher->lock );
437 for ( int i=0; i<PASS_COUNT; i++ )
439 if ( p_fetcher->p_waiting_head[i] )
441 e_pass = i;
442 break;
446 if( p_fetcher->p_waiting_head[e_pass] )
448 p_entry = p_fetcher->p_waiting_head[e_pass];
449 p_fetcher->p_waiting_head[e_pass] = p_entry->p_next;
450 if ( p_entry->p_next == NULL )
451 p_fetcher->p_waiting_tail[e_pass] = NULL;
452 p_entry->p_next = NULL;
454 else
456 p_fetcher->b_live = false;
457 vlc_cond_signal( &p_fetcher->wait );
459 vlc_mutex_unlock( &p_fetcher->lock );
461 if( !p_entry )
462 break;
464 meta_fetcher_scope_t e_prev_scope = p_fetcher->e_scope;
466 /* scope override */
467 switch ( p_entry->i_options ) {
468 case META_REQUEST_OPTION_SCOPE_ANY:
469 p_fetcher->e_scope = FETCHER_SCOPE_ANY;
470 break;
471 case META_REQUEST_OPTION_SCOPE_LOCAL:
472 p_fetcher->e_scope = FETCHER_SCOPE_LOCAL;
473 break;
474 case META_REQUEST_OPTION_SCOPE_NETWORK:
475 p_fetcher->e_scope = FETCHER_SCOPE_NETWORK;
476 break;
477 case META_REQUEST_OPTION_NONE:
478 default:
479 break;
481 /* Triggers "meta fetcher", eventually fetch meta on the network.
482 * They are identical to "meta reader" expect that may actually
483 * takes time. That's why they are running here.
484 * The result of this fetch is not cached. */
486 int i_ret = -1;
488 if( e_pass == PASS1_LOCAL && ( p_fetcher->e_scope & FETCHER_SCOPE_LOCAL ) )
490 /* only fetch from local */
491 p_fetcher->e_scope = FETCHER_SCOPE_LOCAL;
493 else if( e_pass == PASS2_NETWORK && ( p_fetcher->e_scope & FETCHER_SCOPE_NETWORK ) )
495 /* only fetch from network */
496 p_fetcher->e_scope = FETCHER_SCOPE_NETWORK;
498 else
499 p_fetcher->e_scope = 0;
500 if ( p_fetcher->e_scope & FETCHER_SCOPE_ANY )
502 FetchMeta( p_fetcher, p_entry->p_item );
503 i_ret = FindArt( p_fetcher, p_entry->p_item );
504 switch( i_ret )
506 case 1: /* Found, need to dl */
507 i_ret = DownloadArt( p_fetcher, p_entry->p_item );
508 break;
509 case 0: /* Is in cache */
510 i_ret = VLC_SUCCESS;
511 //ft
512 default:// error
513 break;
517 p_fetcher->e_scope = e_prev_scope;
518 /* */
519 if ( i_ret != VLC_SUCCESS && (e_pass != PASS2_NETWORK) )
521 /* Move our entry to next pass queue */
522 vlc_mutex_lock( &p_fetcher->lock );
523 if ( p_fetcher->p_waiting_head[e_pass + 1] )
524 p_fetcher->p_waiting_tail[e_pass + 1]->p_next = p_entry;
525 else
526 p_fetcher->p_waiting_head[e_pass + 1] = p_entry;
527 p_fetcher->p_waiting_tail[e_pass + 1] = p_entry;
528 vlc_mutex_unlock( &p_fetcher->lock );
530 else
532 /* */
533 char *psz_name = input_item_GetName( p_entry->p_item );
534 if( i_ret == VLC_SUCCESS ) /* Art is now in cache */
536 msg_Dbg( obj, "found art for %s in cache", psz_name );
537 input_item_SetArtFetched( p_entry->p_item, true );
538 var_SetAddress( obj, "item-change", p_entry->p_item );
540 else
542 msg_Dbg( obj, "art not found for %s", psz_name );
543 input_item_SetArtNotFound( p_entry->p_item, true );
545 free( psz_name );
546 vlc_gc_decref( p_entry->p_item );
547 free( p_entry );
550 return NULL;