[vlc-commits] medialibrary: Add thumbnailing support
Hugo Beauzée-Luyssen
git at videolan.org
Mon Nov 12 17:12:26 CET 2018
vlc | branch: master | Hugo Beauzée-Luyssen <hugo at beauzee.fr> | Fri Oct 26 17:31:55 2018 +0200| [f1128968b3789307e3008438eaca13825443c215] | committer: Hugo Beauzée-Luyssen
medialibrary: Add thumbnailing support
> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=f1128968b3789307e3008438eaca13825443c215
---
include/vlc_media_library.h | 21 ++++++
modules/misc/Makefile.am | 1 +
modules/misc/medialibrary/Thumbnailer.cpp | 109 ++++++++++++++++++++++++++++++
modules/misc/medialibrary/entities.cpp | 16 ++++-
modules/misc/medialibrary/medialib.cpp | 34 +++++++++-
modules/misc/medialibrary/medialibrary.h | 14 ++++
6 files changed, 190 insertions(+), 5 deletions(-)
diff --git a/include/vlc_media_library.h b/include/vlc_media_library.h
index 06ad9ce4c8..30079b0844 100644
--- a/include/vlc_media_library.h
+++ b/include/vlc_media_library.h
@@ -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 )
{
diff --git a/modules/misc/Makefile.am b/modules/misc/Makefile.am
index 89946bbe96..7848080109 100644
--- a/modules/misc/Makefile.am
+++ b/modules/misc/Makefile.am
@@ -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)
diff --git a/modules/misc/medialibrary/Thumbnailer.cpp b/modules/misc/medialibrary/Thumbnailer.cpp
new file mode 100644
index 0000000000..604f9932eb
--- /dev/null
+++ b/modules/misc/medialibrary/Thumbnailer.cpp
@@ -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() );
+}
diff --git a/modules/misc/medialibrary/entities.cpp b/modules/misc/medialibrary/entities.cpp
index 0579e38475..f6476c76c7 100644
--- a/modules/misc/medialibrary/entities.cpp
+++ b/modules/misc/medialibrary/entities.cpp
@@ -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;
}
diff --git a/modules/misc/medialibrary/medialib.cpp b/modules/misc/medialibrary/medialib.cpp
index bef9bf92cd..85689e26d5 100644
--- a/modules/misc/medialibrary/medialib.cpp
+++ b/modules/misc/medialibrary/medialib.cpp
@@ -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* );
diff --git a/modules/misc/medialibrary/medialibrary.h b/modules/misc/medialibrary/medialibrary.h
index 1949682fda..47aac1f1f6 100644
--- a/modules/misc/medialibrary/medialibrary.h
+++ b/modules/misc/medialibrary/medialibrary.h
@@ -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:
More information about the vlc-commits
mailing list