medialibrary: Add thumbnailing support

This commit is contained in:
Hugo Beauzée-Luyssen 2018-10-26 17:31:55 +02:00
parent 7f604351f6
commit f1128968b3
6 changed files with 190 additions and 5 deletions

View File

@ -182,6 +182,9 @@ typedef struct vlc_ml_media_t
char* psz_title;
char* psz_artwork_mrl;
/* True if a thumbnail is available, or if thumbnail generation was
* attempted but failed */
bool b_artwork_generated;
bool b_is_favorite;
union
@ -431,6 +434,7 @@ enum vlc_ml_control
VLC_ML_MEDIA_GET_MEDIA_PLAYBACK_PREF, /**< arg1: media id; arg2: vlc_ml_playback_pref; arg3: char**; */
VLC_ML_MEDIA_SET_MEDIA_PLAYBACK_PREF, /**< arg1: media id; arg2: vlc_ml_playback_pref; arg3: const char*; */
VLC_ML_MEDIA_SET_THUMBNAIL, /**< arg1: media id; arg2: const char*; */
VLC_ML_MEDIA_GENERATE_THUMBNAIL, /**< arg1: media id; */
VLC_ML_MEDIA_ADD_EXTERNAL_MRL, /**< arg1: media id; arg2: const char*; arg3: type(vlc_ml_file_type_t) */
};
@ -568,6 +572,13 @@ enum vlc_ml_event_type
* increase once all discovery operations are completed.
*/
VLC_ML_EVENT_PARSING_PROGRESS_UPDATED,
/**
* Sent after a media thumbnail was generated, or if it's generation failed.
* The media is stored in vlc_ml_event_t::media_thumbnail_generated::p_media
* and the success state is stored in
* vlc_ml_event_t::media_thumbnail_generated::b_success
*/
VLC_ML_EVENT_MEDIA_THUMBNAIL_GENERATED,
};
typedef struct vlc_ml_event_t
@ -632,6 +643,11 @@ typedef struct vlc_ml_event_t
{
bool b_idle;
} background_idle_changed;
struct
{
const vlc_ml_media_t* p_media;
bool b_success;
} media_thumbnail_generated;
};
} vlc_ml_event_t;
@ -815,6 +831,11 @@ static inline int vlc_ml_media_set_thumbnail( vlc_medialibrary_t* p_ml, int64_t
return vlc_ml_control( p_ml, VLC_ML_MEDIA_SET_THUMBNAIL, i_media_id, psz_mrl );
}
static inline int vlc_ml_media_generate_thumbnail( vlc_medialibrary_t* p_ml, int64_t i_media_id )
{
return vlc_ml_control( p_ml, VLC_ML_MEDIA_GENERATE_THUMBNAIL, i_media_id );
}
static inline int vlc_ml_media_add_external_mrl( vlc_medialibrary_t* p_ml, int64_t i_media_id,
const char* psz_mrl, int i_type )
{

View File

@ -105,6 +105,7 @@ libmedialibrary_plugin_la_SOURCES = \
misc/medialibrary/medialib.cpp \
misc/medialibrary/MetadataExtractor.cpp \
misc/medialibrary/entities.cpp \
misc/medialibrary/Thumbnailer.cpp \
misc/medialibrary/medialibrary.h
libmedialibrary_plugin_la_CXXFLAGS = $(AM_CXXFLAGS) $(MEDIALIBRARY_CFLAGS)

View File

@ -0,0 +1,109 @@
/*****************************************************************************
* Thumbnailer.cpp: medialibrary thumbnailer implementation using libvlccore
*****************************************************************************
* Copyright © 2018 VLC authors, VideoLAN and VideoLabs
*
* 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 Lesser General Public License for more details.
*
* 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 "medialibrary.h"
#include <vlc_thumbnailer.h>
#include <vlc_fs.h>
#include <vlc_block.h>
#include <vlc_url.h>
#include <vlc_cxx_helpers.hpp>
Thumbnailer::Thumbnailer( vlc_medialibrary_module_t* ml, std::string thumbnailsDir )
: m_ml( ml )
, m_thumbnailDir( std::move( thumbnailsDir ) )
, m_thumbnailer( nullptr, &vlc_thumbnailer_Release )
{
m_thumbnailer.reset( vlc_thumbnailer_Create( VLC_OBJECT( ml ) ) );
if ( unlikely( m_thumbnailer == nullptr ) )
throw std::runtime_error( "Failed to instantiate a vlc_thumbnailer_t" );
}
struct ThumbnailerCtx
{
~ThumbnailerCtx()
{
if ( item != nullptr )
input_item_Release( item );
if ( thumbnail != nullptr )
picture_Release( thumbnail );
}
vlc::threads::condition_variable cond;
vlc::threads::mutex mutex;
input_item_t* item;
bool done;
picture_t* thumbnail;
};
static void onThumbnailComplete( void* data, picture_t* thumbnail )
{
ThumbnailerCtx* ctx = static_cast<ThumbnailerCtx*>( data );
{
vlc::threads::mutex_locker lock( ctx->mutex );
ctx->done = true;
ctx->thumbnail = thumbnail ? picture_Hold( thumbnail ) : nullptr;
}
ctx->cond.signal();
}
bool Thumbnailer::generate( medialibrary::MediaPtr media, const std::string& mrl )
{
ThumbnailerCtx ctx{};
ctx.item = input_item_New( mrl.c_str(), media->title().c_str() );
if ( unlikely( ctx.item == nullptr ) )
return false;
ctx.done = false;
{
vlc::threads::mutex_locker lock( ctx.mutex );
vlc_thumbnailer_RequestByPos( m_thumbnailer.get(), .3f,
VLC_THUMBNAILER_SEEK_FAST, ctx.item,
VLC_TICK_FROM_SEC( 3 ),
&onThumbnailComplete, &ctx );
while ( ctx.done == false )
ctx.cond.wait( ctx.mutex );
}
if ( ctx.thumbnail == nullptr )
return false;
block_t* block;
if ( picture_Export( VLC_OBJECT( m_ml ), &block, nullptr, ctx.thumbnail,
VLC_CODEC_JPEG, 512, 320 ) != VLC_SUCCESS )
return false;
auto blockPtr = vlc::wrap_cptr( block, &block_Release );
std::string outputPath = m_thumbnailDir + std::to_string( media->id() ) + ".jpg";
auto f = vlc::wrap_cptr( vlc_fopen( outputPath.c_str(), "wb" ), &fclose );
if ( f == nullptr )
return false;
if ( fwrite( block->p_buffer, block->i_buffer, 1, f.get() ) != 1 )
return false;
auto thumbnailMrl = vlc::wrap_cptr( vlc_path2uri( outputPath.c_str(), nullptr ) );
if ( thumbnailMrl == nullptr )
return false;
return media->setThumbnail( thumbnailMrl.get() );
}

View File

@ -215,12 +215,22 @@ bool Convert( const medialibrary::IMedia* input, vlc_ml_media_t& output )
if ( input->isThumbnailGenerated() == true )
{
output.psz_artwork_mrl = strdup( input->thumbnail().c_str() );
if ( unlikely( output.psz_artwork_mrl == nullptr ) )
return false;
output.b_artwork_generated = true;
const auto& thumbnail = input->thumbnail();
if ( thumbnail.empty() == true )
output.psz_artwork_mrl = nullptr;
else
{
output.psz_artwork_mrl = strdup( thumbnail.c_str() );
if ( unlikely( output.psz_artwork_mrl == nullptr ) )
return false;
}
}
else
{
output.psz_artwork_mrl = nullptr;
output.b_artwork_generated = false;
}
return true;
}

View File

@ -269,8 +269,20 @@ void MediaLibrary::onBackgroundTasksIdleChanged( bool idle )
m_vlc_ml->cbs->pf_send_event( m_vlc_ml, &ev );
}
void MediaLibrary::onMediaThumbnailReady( medialibrary::MediaPtr, bool )
void MediaLibrary::onMediaThumbnailReady( medialibrary::MediaPtr media, bool success )
{
vlc_ml_event_t ev;
ev.i_type = VLC_ML_EVENT_MEDIA_THUMBNAIL_GENERATED;
ev.media_thumbnail_generated.b_success = success;
auto mPtr = vlc::wrap_cptr<vlc_ml_media_t>(
static_cast<vlc_ml_media_t*>( malloc( sizeof( vlc_ml_media_t ) ) ),
vlc_ml_media_release );
if ( unlikely( mPtr == nullptr ) )
return;
ev.media_thumbnail_generated.p_media = mPtr.get();
if ( Convert( media.get(), *mPtr ) == false )
return;
m_vlc_ml->cbs->pf_send_event( m_vlc_ml, &ev );
}
MediaLibrary::MediaLibrary( vlc_medialibrary_module_t* ml )
@ -291,8 +303,9 @@ bool MediaLibrary::Start()
auto userDir = vlc::wrap_cptr( config_GetUserDir( VLC_USERDATA_DIR ) );
std::string mlDir = std::string{ userDir.get() } + "/ml/";
auto thumbnailsDir = mlDir + "thumbnails/";
auto initStatus = ml->initialize( mlDir + "ml.db", mlDir + "thumbnails/", this );
auto initStatus = ml->initialize( mlDir + "ml.db", thumbnailsDir, this );
switch ( initStatus )
{
case medialibrary::InitializeResult::AlreadyInitialized:
@ -310,6 +323,17 @@ bool MediaLibrary::Start()
}
ml->addParserService( std::make_shared<MetadataExtractor>( VLC_OBJECT( m_vlc_ml ) ) );
try
{
ml->addThumbnailer( std::make_shared<Thumbnailer>(
m_vlc_ml, std::move( thumbnailsDir ) ) );
}
catch ( const std::runtime_error& ex )
{
msg_Err( m_vlc_ml, "Failed to provide a thumbnailer module to the "
"medialib: %s", ex.what() );
return false;
}
if ( ml->start() == false )
{
msg_Err( m_vlc_ml, "Failed to start the MediaLibrary" );
@ -426,6 +450,7 @@ int MediaLibrary::Control( int query, va_list args )
case VLC_ML_MEDIA_GET_MEDIA_PLAYBACK_PREF:
case VLC_ML_MEDIA_SET_MEDIA_PLAYBACK_PREF:
case VLC_ML_MEDIA_SET_THUMBNAIL:
case VLC_ML_MEDIA_GENERATE_THUMBNAIL:
case VLC_ML_MEDIA_ADD_EXTERNAL_MRL:
return controlMedia( query, args );
default:
@ -873,6 +898,11 @@ int MediaLibrary::controlMedia( int query, va_list args )
m->setThumbnail( mrl );
return VLC_SUCCESS;
}
case VLC_ML_MEDIA_GENERATE_THUMBNAIL:
{
auto res = m_ml->requestThumbnail( m );
return res == true ? VLC_SUCCESS : VLC_EGENERIC;
}
case VLC_ML_MEDIA_ADD_EXTERNAL_MRL:
{
auto mrl = va_arg( args, const char* );

View File

@ -26,6 +26,7 @@
#include <medialibrary/parser/IItem.h>
#include <medialibrary/parser/Parser.h>
#include <medialibrary/IMedia.h>
#include <medialibrary/IThumbnailer.h>
#include <vlc_common.h>
#include <vlc_threads.h>
@ -38,6 +39,7 @@
struct vlc_event_t;
struct vlc_object_t;
struct vlc_thumbnailer_t;
class Logger;
@ -92,6 +94,18 @@ private:
vlc_object_t* m_obj;
};
class Thumbnailer : public medialibrary::IThumbnailer
{
public:
Thumbnailer( vlc_medialibrary_module_t* ml, std::string thumbnailsDir);
virtual bool generate( medialibrary::MediaPtr media, const std::string& mrl ) override;
private:
vlc_medialibrary_module_t* m_ml;
std::string m_thumbnailDir;
std::unique_ptr<vlc_thumbnailer_t, void(*)(vlc_thumbnailer_t*)> m_thumbnailer;
};
class MediaLibrary : public medialibrary::IMediaLibraryCb
{
public: