[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