fetcher.c
上传用户:kjfoods
上传日期:2020-07-06
资源大小:29949k
文件大小:12k
源码类别:

midi

开发平台:

Unix_Linux

  1. /*****************************************************************************
  2.  * fetcher.c: Art fetcher thread.
  3.  *****************************************************************************
  4.  * Copyright © 1999-2009 the VideoLAN team
  5.  * $Id: c9384b4d2418cb1134f04042bfeca221b2d5879a $
  6.  *
  7.  * Authors: Samuel Hocevar <sam@zoy.org>
  8.  *          Clément Stenac <zorglub@videolan.org>
  9.  *
  10.  * This program is free software; you can redistribute it and/or modify
  11.  * it under the terms of the GNU General Public License as published by
  12.  * the Free Software Foundation; either version 2 of the License, or
  13.  * (at your option) any later version.
  14.  *
  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 General Public License for more details.
  19.  *
  20.  * You should have received a copy of the GNU General Public License
  21.  * along with this program; if not, write to the Free Software
  22.  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  23.  *****************************************************************************/
  24. #ifdef HAVE_CONFIG_H
  25. # include "config.h"
  26. #endif
  27. #include <vlc_common.h>
  28. #include <vlc_playlist.h>
  29. #include <vlc_stream.h>
  30. #include <limits.h>
  31. #include "art.h"
  32. #include "fetcher.h"
  33. #include "playlist_internal.h"
  34. /*****************************************************************************
  35.  * Structures/definitions
  36.  *****************************************************************************/
  37. struct playlist_fetcher_t
  38. {
  39.     VLC_COMMON_MEMBERS;
  40.     playlist_t      *p_playlist;
  41.     vlc_thread_t    thread;
  42.     vlc_mutex_t     lock;
  43.     vlc_cond_t      wait;
  44.     int             i_art_policy;
  45.     int             i_waiting;
  46.     input_item_t    **pp_waiting;
  47.     DECL_ARRAY(playlist_album_t) albums;
  48. };
  49. static void *Thread( void * );
  50. /*****************************************************************************
  51.  * Public functions
  52.  *****************************************************************************/
  53. playlist_fetcher_t *playlist_fetcher_New( playlist_t *p_playlist )
  54. {
  55.     playlist_fetcher_t *p_fetcher =
  56.         vlc_custom_create( p_playlist, sizeof(*p_fetcher),
  57.                            VLC_OBJECT_GENERIC, "playlist fetcher" );
  58.     if( !p_fetcher )
  59.         return NULL;
  60.     vlc_object_attach( p_fetcher, p_playlist );
  61.     p_fetcher->p_playlist = p_playlist;
  62.     vlc_mutex_init( &p_fetcher->lock );
  63.     vlc_cond_init( &p_fetcher->wait );
  64.     p_fetcher->i_waiting = 0;
  65.     p_fetcher->pp_waiting = NULL;
  66.     p_fetcher->i_art_policy = var_GetInteger( p_playlist, "album-art" );
  67.     ARRAY_INIT( p_fetcher->albums );
  68.     if( vlc_clone( &p_fetcher->thread, Thread, p_fetcher,
  69.                    VLC_THREAD_PRIORITY_LOW ) )
  70.     {
  71.         msg_Err( p_fetcher, "cannot spawn secondary preparse thread" );
  72.         vlc_object_release( p_fetcher );
  73.         return NULL;
  74.     }
  75.     return p_fetcher;
  76. }
  77. void playlist_fetcher_Push( playlist_fetcher_t *p_fetcher, input_item_t *p_item )
  78. {
  79.     vlc_gc_incref( p_item );
  80.     vlc_mutex_lock( &p_fetcher->lock );
  81.     INSERT_ELEM( p_fetcher->pp_waiting, p_fetcher->i_waiting,
  82.                  p_fetcher->i_waiting, p_item );
  83.     vlc_cond_signal( &p_fetcher->wait );
  84.     vlc_mutex_unlock( &p_fetcher->lock );
  85. }
  86. void playlist_fetcher_Delete( playlist_fetcher_t *p_fetcher )
  87. {
  88.     /* */
  89.     vlc_object_kill( p_fetcher );
  90.     /* Destroy the item meta-infos fetcher */
  91.     vlc_cancel( p_fetcher->thread );
  92.     vlc_join( p_fetcher->thread, NULL );
  93.     while( p_fetcher->i_waiting > 0 )
  94.     {   /* Any left-over unparsed item? */
  95.         vlc_gc_decref( p_fetcher->pp_waiting[0] );
  96.         REMOVE_ELEM( p_fetcher->pp_waiting, p_fetcher->i_waiting, 0 );
  97.     }
  98.     vlc_cond_destroy( &p_fetcher->wait );
  99.     vlc_mutex_destroy( &p_fetcher->lock );
  100.     vlc_object_release( p_fetcher );
  101. }
  102. /*****************************************************************************
  103.  * Privates functions
  104.  *****************************************************************************/
  105. /**
  106.  * This function locates the art associated to an input item.
  107.  * Return codes:
  108.  *   0 : Art is in cache or is a local file
  109.  *   1 : Art found, need to download
  110.  *  -X : Error/not found
  111.  */
  112. static int FindArt( playlist_fetcher_t *p_fetcher, input_item_t *p_item )
  113. {
  114.     int i_ret;
  115.     module_t *p_module;
  116.     char *psz_title, *psz_artist, *psz_album;
  117.     psz_artist = input_item_GetArtist( p_item );
  118.     psz_album = input_item_GetAlbum( p_item );
  119.     psz_title = input_item_GetTitle( p_item );
  120.     if( !psz_title )
  121.         psz_title = input_item_GetName( p_item );
  122.     if( !psz_title && !psz_artist && !psz_album )
  123.         return VLC_EGENERIC;
  124.     free( psz_title );
  125.     /* If we already checked this album in this session, skip */
  126.     if( psz_artist && psz_album )
  127.     {
  128.         FOREACH_ARRAY( playlist_album_t album, p_fetcher->albums )
  129.             if( !strcmp( album.psz_artist, psz_artist ) &&
  130.                 !strcmp( album.psz_album, psz_album ) )
  131.             {
  132.                 msg_Dbg( p_fetcher, " %s - %s has already been searched",
  133.                          psz_artist, psz_album );
  134.                 /* TODO-fenrir if we cache art filename too, we can go faster */
  135.                 free( psz_artist );
  136.                 free( psz_album );
  137.                 if( album.b_found )
  138.                 {
  139.                     if( !strncmp( album.psz_arturl, "file://", 7 ) )
  140.                         input_item_SetArtURL( p_item, album.psz_arturl );
  141.                     else /* Actually get URL from cache */
  142.                         playlist_FindArtInCache( p_item );
  143.                     return 0;
  144.                 }
  145.                 else
  146.                 {
  147.                     return VLC_EGENERIC;
  148.                 }
  149.             }
  150.         FOREACH_END();
  151.     }
  152.     free( psz_artist );
  153.     free( psz_album );
  154.     playlist_FindArtInCache( p_item );
  155.     char *psz_arturl = input_item_GetArtURL( p_item );
  156.     if( psz_arturl )
  157.     {
  158.         /* We already have an URL */
  159.         if( !strncmp( psz_arturl, "file://", strlen( "file://" ) ) )
  160.         {
  161.             free( psz_arturl );
  162.             return 0; /* Art is in cache, no need to go further */
  163.         }
  164.         free( psz_arturl );
  165.         /* Art need to be put in cache */
  166.         return 1;
  167.     }
  168.     /* */
  169.     psz_album = input_item_GetAlbum( p_item );
  170.     psz_artist = input_item_GetArtist( p_item );
  171.     if( psz_album && psz_artist )
  172.     {
  173.         msg_Dbg( p_fetcher, "searching art for %s - %s",
  174.              psz_artist, psz_album );
  175.     }
  176.     else
  177.     {
  178.         psz_title = input_item_GetTitle( p_item );
  179.         if( !psz_title )
  180.             psz_title = input_item_GetName( p_item );
  181.         msg_Dbg( p_fetcher, "searching art for %s", psz_title );
  182.         free( psz_title );
  183.     }
  184.     /* Fetch the art url */
  185.     p_fetcher->p_private = p_item;
  186.     p_module = module_need( p_fetcher, "art finder", NULL, false );
  187.     if( p_module )
  188.     {
  189.         module_unneed( p_fetcher, p_module );
  190.         i_ret = 1;
  191.     }
  192.     else
  193.     {
  194.         msg_Dbg( p_fetcher, "unable to find art" );
  195.         i_ret = VLC_EGENERIC;
  196.     }
  197.     /* Record this album */
  198.     if( psz_artist && psz_album )
  199.     {
  200.         playlist_album_t a;
  201.         a.psz_artist = psz_artist;
  202.         a.psz_album = psz_album;
  203.         a.psz_arturl = input_item_GetArtURL( p_item );
  204.         a.b_found = (i_ret == VLC_EGENERIC ? false : true );
  205.         ARRAY_APPEND( p_fetcher->albums, a );
  206.     }
  207.     else
  208.     {
  209.         free( psz_artist );
  210.         free( psz_album );
  211.     }
  212.     return i_ret;
  213. }
  214. /**
  215.  * Download the art using the URL or an art downloaded
  216.  * This function should be called only if data is not already in cache
  217.  */
  218. static int DownloadArt( playlist_fetcher_t *p_fetcher, input_item_t *p_item )
  219. {
  220.     char *psz_arturl = input_item_GetArtURL( p_item );
  221.     assert( *psz_arturl );
  222.     if( !strncmp( psz_arturl , "file://", 7 ) )
  223.     {
  224.         msg_Dbg( p_fetcher, "Album art is local file, no need to cache" );
  225.         free( psz_arturl );
  226.         return VLC_SUCCESS;
  227.     }
  228.     if( !strncmp( psz_arturl , "APIC", 4 ) )
  229.     {
  230.         msg_Warn( p_fetcher, "APIC fetch not supported yet" );
  231.         goto error;
  232.     }
  233.     stream_t *p_stream = stream_UrlNew( p_fetcher, psz_arturl );
  234.     if( !p_stream )
  235.         goto error;
  236.     uint8_t *p_data = NULL;
  237.     int i_data = 0;
  238.     for( ;; )
  239.     {
  240.         int i_read = 65536;
  241.         if( i_data >= INT_MAX - i_read )
  242.             break;
  243.         p_data = realloc( p_data, i_data + i_read );
  244.         if( !p_data )
  245.             break;
  246.         i_read = stream_Read( p_stream, &p_data[i_data], i_read );
  247.         if( i_read <= 0 )
  248.             break;
  249.         i_data += i_read;
  250.     }
  251.     stream_Delete( p_stream );
  252.     if( p_data && i_data > 0 )
  253.     {
  254.         char *psz_type = strrchr( psz_arturl, '.' );
  255.         if( psz_type && strlen( psz_type ) > 5 )
  256.             psz_type = NULL; /* remove extension if it's > to 4 characters */
  257.         playlist_SaveArt( p_fetcher->p_playlist, p_item, p_data, i_data, psz_type );
  258.     }
  259.     free( p_data );
  260.     free( psz_arturl );
  261.     return VLC_SUCCESS;
  262. error:
  263.     free( psz_arturl );
  264.     return VLC_EGENERIC;
  265. }
  266. static int InputEvent( vlc_object_t *p_this, char const *psz_cmd,
  267.                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
  268. {
  269.     VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
  270.     playlist_fetcher_t *p_fetcher = p_data;
  271.     if( newval.i_int == INPUT_EVENT_ITEM_META ||
  272.         newval.i_int == INPUT_EVENT_DEAD )
  273.         vlc_cond_signal( &p_fetcher->wait );
  274.     return VLC_SUCCESS;
  275. }
  276. /* Check if it is not yet preparsed and if so wait for it
  277.  * (at most 0.5s)
  278.  * (This can happen if we fetch art on play)
  279.  * FIXME this doesn't work if we need to fetch meta before art...
  280.  */
  281. static void WaitPreparsed( playlist_fetcher_t *p_fetcher, input_item_t *p_item )
  282. {
  283.     if( input_item_IsPreparsed( p_item ) )
  284.         return;
  285.     input_thread_t *p_input = playlist_CurrentInput( p_fetcher->p_playlist );
  286.     if( !p_input )
  287.         return;
  288.     if( input_GetItem( p_input ) != p_item )
  289.         goto exit;
  290.     var_AddCallback( p_input, "intf-event", InputEvent, p_fetcher );
  291.     const mtime_t i_deadline = mdate() + 500*1000;
  292.     while( !p_input->b_eof && !p_input->b_error && !input_item_IsPreparsed( p_item ) )
  293.     {
  294.         /* A bit weird, but input_item_IsPreparsed does held the protected value */
  295.         vlc_mutex_lock( &p_fetcher->lock );
  296.         vlc_cond_timedwait( &p_fetcher->wait, &p_fetcher->lock, i_deadline );
  297.         vlc_mutex_unlock( &p_fetcher->lock );
  298.         if( i_deadline <= mdate() )
  299.             break;
  300.     }
  301.     var_DelCallback( p_input, "intf-event", InputEvent, p_fetcher );
  302. exit:
  303.     vlc_object_release( p_input );
  304. }
  305. static void *Thread( void *p_data )
  306. {
  307.     playlist_fetcher_t *p_fetcher = p_data;
  308.     playlist_t *p_playlist = p_fetcher->p_playlist;
  309.     for( ;; )
  310.     {
  311.         input_item_t *p_item;
  312.         /* Be sure to be cancellable before our queue is empty */
  313.         vlc_testcancel();
  314.         /* */
  315.         vlc_mutex_lock( &p_fetcher->lock );
  316.         mutex_cleanup_push( &p_fetcher->lock );
  317.         while( p_fetcher->i_waiting == 0 )
  318.             vlc_cond_wait( &p_fetcher->wait, &p_fetcher->lock );
  319.         p_item = p_fetcher->pp_waiting[0];
  320.         REMOVE_ELEM( p_fetcher->pp_waiting, p_fetcher->i_waiting, 0 );
  321.         vlc_cleanup_run( );
  322.         if( !p_item )
  323.             continue;
  324.         /* */
  325.         int canc = vlc_savecancel();
  326.         /* Wait that the input item is preparsed if it is being played */
  327.         WaitPreparsed( p_fetcher, p_item );
  328.         /* */
  329.         if( !vlc_object_alive( p_fetcher ) )
  330.             goto end;
  331.         /* Find art, and download it if needed */
  332.         int i_ret = FindArt( p_fetcher, p_item );
  333.         /* */
  334.         if( !vlc_object_alive( p_fetcher ) )
  335.             goto end;
  336.         if( i_ret == 1 )
  337.             i_ret = DownloadArt( p_fetcher, p_item );
  338.         /* */
  339.         char *psz_name = input_item_GetName( p_item );
  340.         if( !i_ret ) /* Art is now in cache */
  341.         {
  342.             PL_DEBUG( "found art for %s in cache", psz_name );
  343.             input_item_SetArtFetched( p_item, true );
  344.             var_SetInteger( p_playlist, "item-change", p_item->i_id );
  345.         }
  346.         else
  347.         {
  348.             PL_DEBUG( "art not found for %s", psz_name );
  349.             input_item_SetArtNotFound( p_item, true );
  350.         }
  351.         free( psz_name );
  352.     end:
  353.         vlc_gc_decref( p_item );
  354.         vlc_restorecancel( canc );
  355.         int i_activity = var_GetInteger( p_playlist, "activity" );
  356.         if( i_activity < 0 ) i_activity = 0;
  357.         /* Sleep at least 1ms */
  358.         msleep( (i_activity+1) * 1000 );
  359.     }
  360.     return NULL;
  361. }