mirror of https://code.videolan.org/videolan/vlc
medialibrary: Add thumbnailing support
This commit is contained in:
parent
7f604351f6
commit
f1128968b3
|
@ -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 )
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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() );
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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* );
|
||||
|
|
|
@ -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:
|
||||
|
|
Loading…
Reference in New Issue