[vlc-commits] core: Add a thumbnailing API

Hugo Beauzée-Luyssen git at videolan.org
Mon Nov 12 17:12:19 CET 2018


vlc | branch: master | Hugo Beauzée-Luyssen <hugo at beauzee.fr> | Thu Oct  4 17:10:16 2018 +0200| [946b67f964396056caba8cb3b62bdf9a976abfae] | committer: Hugo Beauzée-Luyssen

core: Add a thumbnailing API

ref #17368

> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=946b67f964396056caba8cb3b62bdf9a976abfae
---

 include/vlc_input.h        |  11 ++
 include/vlc_thumbnailer.h  | 133 +++++++++++++++++++++
 src/Makefile.am            |   2 +
 src/input/decoder.c        |  45 +++++++-
 src/input/es_out.c         |   5 +-
 src/input/input.c          |  19 ++-
 src/input/input_internal.h |   1 +
 src/input/thumbnailer.c    | 281 +++++++++++++++++++++++++++++++++++++++++++++
 src/input/var.c            |   2 +
 src/libvlc.c               |   5 +
 src/libvlccore.sym         |   6 +
 11 files changed, 502 insertions(+), 8 deletions(-)

diff --git a/include/vlc_input.h b/include/vlc_input.h
index 5d9fd5fadf..713066d962 100644
--- a/include/vlc_input.h
+++ b/include/vlc_input.h
@@ -395,6 +395,9 @@ typedef enum input_event_type_e
 
     /* subs_fps has changed */
     INPUT_EVENT_SUBS_FPS,
+
+    /* Thumbnail generation */
+    INPUT_EVENT_THUMBNAIL_READY,
 } input_event_type_e;
 
 #define VLC_INPUT_CAPABILITIES_SEEKABLE (1<<0)
@@ -528,6 +531,8 @@ struct vlc_input_event
         bool vbi_transparent;
         /* INPUT_EVENT_SUBS_FPS */
         float subs_fps;
+        /* INPUT_EVENT_THUMBNAIL_READY */
+        picture_t *thumbnail;
     };
 };
 
@@ -621,6 +626,12 @@ VLC_API input_thread_t *input_CreatePreparser(vlc_object_t *obj,
                                               void *events_data, input_item_t *item)
 VLC_USED;
 
+VLC_API
+input_thread_t *input_CreateThumbnailer(vlc_object_t *obj,
+                                        input_thread_events_cb events_cb,
+                                        void *events_data, input_item_t *item)
+VLC_USED;
+
 VLC_API int input_Start( input_thread_t * );
 
 VLC_API void input_Stop( input_thread_t * );
diff --git a/include/vlc_thumbnailer.h b/include/vlc_thumbnailer.h
new file mode 100644
index 0000000000..39c23cd727
--- /dev/null
+++ b/include/vlc_thumbnailer.h
@@ -0,0 +1,133 @@
+/*****************************************************************************
+ * vlc_thumbnailer.h: Thumbnailing API
+ *****************************************************************************
+ * Copyright (C) 2018 VLC authors and VideoLAN
+ *
+ * Authors: Hugo Beauzée-Luyssen <hugo at beauzee.fr>
+ *
+ * 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.
+ *****************************************************************************/
+
+#ifndef VLC_THUMBNAILER_H
+#define VLC_THUMBNAILER_H
+
+#include <vlc_common.h>
+
+typedef struct vlc_thumbnailer_t vlc_thumbnailer_t;
+typedef struct vlc_thumbnailer_request_t vlc_thumbnailer_request_t;
+
+/**
+ * \brief vlc_thumbnailer_cb defines a callback invoked on thumbnailing completion or error
+ *
+ * This callback will always be called, provided vlc_thumbnailer_Request returned
+ * a non NULL request, and provided the request is not cancelled before its
+ * completion.
+ * In case of failure, p_thumbnail will be NULL.
+ * The picture, if any, is owned by the thumbnailer, and must be acquired by using
+ * \link picture_Hold \endlink to use it pass the callback's scope.
+ *
+ * \param data Is the opaque pointer passed as vlc_thumbnailer_Request last parameter
+ * \param thumbnail The generated thumbnail, or NULL in case of failure or timeout
+ */
+typedef void(*vlc_thumbnailer_cb)( void* data, picture_t* thumbnail );
+
+
+/**
+ * \brief vlc_thumbnailer_Create Creates a thumbnailer object
+ * \param parent A VLC object
+ * \return A thumbnailer object, or NULL in case of failure
+ */
+VLC_API vlc_thumbnailer_t*
+vlc_thumbnailer_Create( vlc_object_t* p_parent )
+VLC_USED;
+
+enum vlc_thumbnailer_seek_speed
+{
+    /** Precise, but potentially slow */
+    VLC_THUMBNAILER_SEEK_PRECISE,
+    /** Fast, but potentially imprecise */
+    VLC_THUMBNAILER_SEEK_FAST,
+};
+
+/**
+ * \brief vlc_thumbnailer_RequestByTime Requests a thumbnailer at a given time
+ * \param thumbnailer A thumbnailer object
+ * \param time The time at which the thumbnail should be taken
+ * \param speed The seeking speed \sa{enum vlc_thumbnailer_seek_speed}
+ * \param input_item The input item to generate the thumbnail for
+ * \param timeout A timeout value, or VLC_TICK_INVALID to disable timeout
+ * \param cb A user callback to be called on completion (success & error)
+ * \param user_data An opaque value, provided as pf_cb's first parameter
+ * \return An opaque request object, or NULL in case of failure
+ *
+ * If this function returns a valid request object, the callback is guaranteed
+ * to be called, even in case of later failure.
+ * The returned request object must not be used after the callback has been
+ * invoked. That request object is owned by the thumbnailer, and must not be
+ * released.
+ * The provided input_item will be held by the thumbnailer and can safely be
+ * released safely after calling this function.
+ */
+VLC_API vlc_thumbnailer_request_t*
+vlc_thumbnailer_RequestByTime( vlc_thumbnailer_t *thumbnailer,
+                               vlc_tick_t time,
+                               enum vlc_thumbnailer_seek_speed speed,
+                               input_item_t *input_item, vlc_tick_t timeout,
+                               vlc_thumbnailer_cb cb, void* user_data );
+/**
+ * \brief vlc_thumbnailer_RequestByTime Requests a thumbnailer at a given time
+ * \param thumbnailer A thumbnailer object
+ * \param pos The position at which the thumbnail should be taken
+ * \param speed The seeking speed \sa{enum vlc_thumbnailer_seek_speed}
+ * \param input_item The input item to generate the thumbnail for
+ * \param timeout A timeout value, or VLC_TICK_INVALID to disable timeout
+ * \param cb A user callback to be called on completion (success & error)
+ * \param user_data An opaque value, provided as pf_cb's first parameter
+ * \return An opaque request object, or NULL in case of failure
+ *
+ * If this function returns a valid request object, the callback is guaranteed
+ * to be called, even in case of later failure.
+ * The returned request object must not be used after the callback has been
+ * invoked. That request object is owned by the thumbnailer, and must not be
+ * released.
+ * The provided input_item will be held by the thumbnailer and can safely be
+ * released after calling this function.
+ */
+VLC_API vlc_thumbnailer_request_t*
+vlc_thumbnailer_RequestByPos( vlc_thumbnailer_t *thumbnailer,
+                              float pos,
+                              enum vlc_thumbnailer_seek_speed speed,
+                              input_item_t *input_item, vlc_tick_t timeout,
+                              vlc_thumbnailer_cb cb, void* user_data );
+
+/**
+ * \brief vlc_thumbnailer_Cancel Cancel a thumbnail request
+ * \param thumbnailer A thumbnailer object
+ * \param request An opaque thumbnail request object
+ *
+ * Cancelling a request will *not* invoke the completion callback.
+ * The behavior is undefined if the request is cancelled after its completion.
+ */
+VLC_API void
+vlc_thumbnailer_Cancel( vlc_thumbnailer_t* thumbnailer,
+                        vlc_thumbnailer_request_t* request );
+
+/**
+ * \brief vlc_thumbnailer_Release releases a thumbnailer and cancel all pending requests
+ * \param thumbnailer A thumbnailer object
+ */
+VLC_API void vlc_thumbnailer_Release( vlc_thumbnailer_t* thumbnailer );
+
+#endif // VLC_THUMBNAILER_H
diff --git a/src/Makefile.am b/src/Makefile.am
index c64592a11b..4c7f7a6f98 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -94,6 +94,7 @@ pluginsinclude_HEADERS = \
 	../include/vlc_threads.h \
 	../include/vlc_tick.h \
 	../include/vlc_timestamp_helper.h \
+	../include/vlc_thumbnailer.h \
 	../include/vlc_tls.h \
 	../include/vlc_url.h \
 	../include/vlc_variables.h \
@@ -267,6 +268,7 @@ libvlccore_la_SOURCES = \
 	input/stream_filter.c \
 	input/stream_memory.c \
 	input/subtitles.c \
+	input/thumbnailer.c \
 	input/var.c \
 	audio_output/aout_internal.h \
 	audio_output/common.c \
diff --git a/src/input/decoder.c b/src/input/decoder.c
index 7ca4c87fcf..5ce154678e 100644
--- a/src/input/decoder.c
+++ b/src/input/decoder.c
@@ -1210,6 +1210,37 @@ static void DecoderQueueVideo( decoder_t *p_dec, picture_t *p_pic )
     p_owner->pf_update_stat( p_owner, 1, i_lost );
 }
 
+static int thumbnailer_update_format( decoder_t *p_dec )
+{
+    VLC_UNUSED(p_dec);
+    return 0;
+}
+
+static picture_t *thumbnailer_buffer_new( decoder_t *p_dec )
+{
+    struct decoder_owner *p_owner = dec_get_owner( p_dec );
+    /* Avoid decoding more than one frame when a thumbnail was
+     * already generated */
+    if( !p_owner->b_first )
+        return NULL;
+    return picture_NewFromFormat( &p_dec->fmt_out.video );
+}
+
+static void DecoderQueueThumbnail( decoder_t *p_dec, picture_t *p_pic )
+{
+    struct decoder_owner *p_owner = dec_get_owner( p_dec );
+    if( p_owner->b_first )
+    {
+        input_SendEvent(p_owner->p_input, &(struct vlc_input_event) {
+            .type = INPUT_EVENT_THUMBNAIL_READY,
+            .thumbnail = p_pic
+        });
+        p_owner->b_first = false;
+    }
+    picture_Release( p_pic );
+
+}
+
 static void DecoderPlayAudio( decoder_t *p_dec, block_t *p_audio,
                              unsigned *restrict pi_lost_sum )
 {
@@ -1779,6 +1810,15 @@ static const struct decoder_owner_callbacks dec_video_cbs =
     },
     .get_attachments = DecoderGetInputAttachments,
 };
+static const struct decoder_owner_callbacks dec_thumbnailer_cbs =
+{
+    .video = {
+        .format_update = thumbnailer_update_format,
+        .buffer_new = thumbnailer_buffer_new,
+        .queue = DecoderQueueThumbnail,
+    },
+    .get_attachments = DecoderGetInputAttachments,
+};
 static const struct decoder_owner_callbacks dec_audio_cbs =
 {
     .audio = {
@@ -1894,7 +1934,10 @@ static decoder_t * CreateDecoder( vlc_object_t *p_parent,
     switch( fmt->i_cat )
     {
         case VIDEO_ES:
-            p_dec->cbs = &dec_video_cbs;
+            if( !input_priv( p_input )->b_thumbnailing )
+                p_dec->cbs = &dec_video_cbs;
+            else
+                p_dec->cbs = &dec_thumbnailer_cbs;
             p_owner->pf_update_stat = DecoderUpdateStatVideo;
             break;
         case AUDIO_ES:
diff --git a/src/input/es_out.c b/src/input/es_out.c
index 21ba6137eb..0cdd08cfe2 100644
--- a/src/input/es_out.c
+++ b/src/input/es_out.c
@@ -1821,6 +1821,7 @@ static void EsOutSelectEs( es_out_t *out, es_out_id_t *es )
 {
     es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
     input_thread_t *p_input = p_sys->p_input;
+    bool b_thumbnailing = input_priv(p_input)->b_thumbnailing;
 
     if( EsIsSelected( es ) )
     {
@@ -1859,7 +1860,7 @@ static void EsOutSelectEs( es_out_t *out, es_out_id_t *es )
             }
             else if( es->fmt.i_cat == AUDIO_ES )
             {
-                if( !var_GetBool( p_input, b_sout ? "sout-audio" : "audio" ) )
+                if( !var_GetBool( p_input, b_sout ? "sout-audio" : "audio" ) || b_thumbnailing )
                 {
                     msg_Dbg( p_input, "audio is disabled, not selecting ES 0x%x",
                              es->fmt.i_id );
@@ -1868,7 +1869,7 @@ static void EsOutSelectEs( es_out_t *out, es_out_id_t *es )
             }
             if( es->fmt.i_cat == SPU_ES )
             {
-                if( !var_GetBool( p_input, b_sout ? "sout-spu" : "spu" ) )
+                if( !var_GetBool( p_input, b_sout ? "sout-spu" : "spu" ) || b_thumbnailing )
                 {
                     msg_Dbg( p_input, "spu is disabled, not selecting ES 0x%x",
                              es->fmt.i_id );
diff --git a/src/input/input.c b/src/input/input.c
index 7265ac6cd6..6999d75eb5 100644
--- a/src/input/input.c
+++ b/src/input/input.c
@@ -63,7 +63,7 @@ static  void *Run( void * );
 static  void *Preparse( void * );
 
 static input_thread_t * Create  ( vlc_object_t *, input_thread_events_cb, void *,
-                                  input_item_t *, const char *, bool,
+                                  input_item_t *, const char *, bool, bool,
                                   input_resource_t *, vlc_renderer_item_t * );
 static  int             Init    ( input_thread_t *p_input );
 static void             End     ( input_thread_t *p_input );
@@ -132,7 +132,7 @@ input_thread_t *input_Create( vlc_object_t *p_parent,
                               vlc_renderer_item_t *p_renderer )
 {
     return Create( p_parent, events_cb, events_data, p_item, psz_log, false,
-                   p_resource, p_renderer );
+                   false, p_resource, p_renderer );
 }
 
 #undef input_Read
@@ -147,7 +147,7 @@ int input_Read( vlc_object_t *p_parent, input_item_t *p_item,
                 input_thread_events_cb events_cb, void *events_data )
 {
     input_thread_t *p_input = Create( p_parent, events_cb, events_data, p_item,
-                                      NULL, false, NULL, NULL );
+                                      NULL, false, false, NULL, NULL );
     if( !p_input )
         return VLC_EGENERIC;
 
@@ -165,7 +165,14 @@ input_thread_t *input_CreatePreparser( vlc_object_t *parent,
                                        input_thread_events_cb events_cb,
                                        void *events_data, input_item_t *item )
 {
-    return Create( parent, events_cb, events_data, item, NULL, true, NULL, NULL );
+    return Create( parent, events_cb, events_data, item, NULL, true, false, NULL, NULL );
+}
+
+input_thread_t *input_CreateThumbnailer(vlc_object_t *obj,
+                                        input_thread_events_cb events_cb,
+                                        void *events_data, input_item_t *item)
+{
+    return Create( obj, events_cb, events_data, item, NULL, false, true, NULL, NULL );
 }
 
 /**
@@ -308,7 +315,8 @@ input_item_t *input_GetItem( input_thread_t *p_input )
 static input_thread_t *Create( vlc_object_t *p_parent,
                                input_thread_events_cb events_cb, void *events_data,
                                input_item_t *p_item, const char *psz_header,
-                               bool b_preparsing, input_resource_t *p_resource,
+                               bool b_preparsing, bool b_thumbnailing,
+                               input_resource_t *p_resource,
                                vlc_renderer_item_t *p_renderer )
 {
     /* Allocate descriptor */
@@ -345,6 +353,7 @@ static input_thread_t *Create( vlc_object_t *p_parent,
     priv->is_running = false;
     priv->is_stopped = false;
     priv->b_recording = false;
+    priv->b_thumbnailing = b_thumbnailing;
     priv->i_rate = INPUT_RATE_DEFAULT;
     memset( &priv->bookmark, 0, sizeof(priv->bookmark) );
     TAB_INIT( priv->i_bookmark, priv->pp_bookmark );
diff --git a/src/input/input_internal.h b/src/input/input_internal.h
index 95d09528cf..82eb5f64bd 100644
--- a/src/input/input_internal.h
+++ b/src/input/input_internal.h
@@ -132,6 +132,7 @@ typedef struct input_thread_private_t
     bool        is_running;
     bool        is_stopped;
     bool        b_recording;
+    bool        b_thumbnailing;
     int         i_rate;
 
     /* Playtime configuration and state */
diff --git a/src/input/thumbnailer.c b/src/input/thumbnailer.c
new file mode 100644
index 0000000000..bd10032d53
--- /dev/null
+++ b/src/input/thumbnailer.c
@@ -0,0 +1,281 @@
+/*****************************************************************************
+ * thumbnailer.c: Thumbnailing API
+ *****************************************************************************
+ * Copyright (C) 2018 VLC authors and VideoLAN
+ *
+ * Authors: Hugo Beauzée-Luyssen <hugo at beauzee.fr>
+ *
+ * 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 <vlc_thumbnailer.h>
+#include <vlc_input.h>
+#include "misc/background_worker.h"
+
+struct vlc_thumbnailer_t
+{
+    vlc_object_t* parent;
+    struct background_worker* worker;
+};
+
+typedef struct vlc_thumbnailer_params_t
+{
+    union
+    {
+        vlc_tick_t time;
+        float pos;
+    };
+    enum
+    {
+        VLC_THUMBNAILER_SEEK_TIME,
+        VLC_THUMBNAILER_SEEK_POS,
+    } type;
+    bool fast_seek;
+    input_item_t* input_item;
+    /**
+     * A positive value will be used as the timeout duration
+     * VLC_TICK_INVALID means no timeout
+     */
+    vlc_tick_t timeout;
+    vlc_thumbnailer_cb cb;
+    void* user_data;
+} vlc_thumbnailer_params_t;
+
+struct vlc_thumbnailer_request_t
+{
+    vlc_thumbnailer_t *thumbnailer;
+    input_thread_t *input_thread;
+
+    vlc_thumbnailer_params_t params;
+
+    vlc_mutex_t lock;
+    bool done;
+};
+
+static void
+on_thumbnailer_input_event( input_thread_t *input,
+                            const struct vlc_input_event *event, void *userdata )
+{
+    VLC_UNUSED(input);
+    if ( event->type != INPUT_EVENT_THUMBNAIL_READY &&
+         ( event->type != INPUT_EVENT_STATE || ( event->state != ERROR_S &&
+                                                 event->state != END_S ) ) )
+         return;
+
+    vlc_thumbnailer_request_t* request = userdata;
+    picture_t *pic = NULL;
+
+    if ( event->type == INPUT_EVENT_THUMBNAIL_READY )
+    {
+        /*
+         * Stop the input thread ASAP, delegate its release to
+         * thumbnailer_request_Release
+         */
+        input_Stop( request->input_thread );
+        pic = event->thumbnail;
+    }
+    vlc_mutex_lock( &request->lock );
+    request->done = true;
+    /*
+     * If the request has not been cancelled, we can invoke the completion
+     * callback.
+     */
+    if ( request->params.cb )
+    {
+        request->params.cb( request->params.user_data, pic );
+        request->params.cb = NULL;
+    }
+    vlc_mutex_unlock( &request->lock );
+    background_worker_RequestProbe( request->thumbnailer->worker );
+}
+
+static void thumbnailer_request_Hold( void* data )
+{
+    VLC_UNUSED(data);
+}
+
+static void thumbnailer_request_Release( void* data )
+{
+    vlc_thumbnailer_request_t* request = data;
+    if ( request->input_thread )
+        input_Close( request->input_thread );
+
+    input_item_Release( request->params.input_item );
+    vlc_mutex_destroy( &request->lock );
+    free( request );
+}
+
+static int thumbnailer_request_Start( void* owner, void* entity, void** out )
+{
+    vlc_thumbnailer_t* thumbnailer = owner;
+    vlc_thumbnailer_request_t* request = entity;
+    input_thread_t* input = request->input_thread =
+            input_CreateThumbnailer( thumbnailer->parent,
+                                     on_thumbnailer_input_event, request,
+                                     request->params.input_item );
+    if ( unlikely( input == NULL ) )
+        return VLC_EGENERIC;
+    if ( request->params.type == VLC_THUMBNAILER_SEEK_TIME )
+    {
+        input_SetTime( input, request->params.time,
+                       request->params.fast_seek );
+    }
+    else
+    {
+        assert( request->params.type == VLC_THUMBNAILER_SEEK_POS );
+        input_SetPosition( input, request->params.pos,
+                       request->params.fast_seek );
+    }
+    if ( input_Start( input ) != VLC_SUCCESS )
+        return VLC_EGENERIC;
+    *out = request;
+    return VLC_SUCCESS;
+}
+
+static void thumbnailer_request_Stop( void* owner, void* handle )
+{
+    VLC_UNUSED(owner);
+
+    vlc_thumbnailer_request_t *request = handle;
+    vlc_mutex_lock( &request->lock );
+    /*
+     * If the callback hasn't been invoked yet, we assume a timeout and
+     * signal it back to the user
+     */
+    if ( request->params.cb != NULL )
+    {
+        request->params.cb( request->params.user_data, NULL );
+        request->params.cb = NULL;
+    }
+    vlc_mutex_unlock( &request->lock );
+    assert( request->input_thread != NULL );
+    input_Stop( request->input_thread );
+}
+
+static int thumbnailer_request_Probe( void* owner, void* handle )
+{
+    VLC_UNUSED(owner);
+    vlc_thumbnailer_request_t *request = handle;
+    vlc_mutex_lock( &request->lock );
+    int res = request->done;
+    vlc_mutex_unlock( &request->lock );
+    return res;
+}
+
+static vlc_thumbnailer_request_t*
+thumbnailer_RequestCommon( vlc_thumbnailer_t* thumbnailer,
+                           const vlc_thumbnailer_params_t* params )
+{
+    vlc_thumbnailer_request_t *request = malloc( sizeof( *request ) );
+    if ( unlikely( request == NULL ) )
+        return NULL;
+    request->thumbnailer = thumbnailer;
+    request->input_thread = NULL;
+    request->params = *(vlc_thumbnailer_params_t*)params;
+    request->done = false;
+    input_item_Hold( request->params.input_item );
+    vlc_mutex_init( &request->lock );
+
+    int timeout = params->timeout == VLC_TICK_INVALID ?
+                0 : MS_FROM_VLC_TICK( params->timeout );
+    if ( background_worker_Push( thumbnailer->worker, request, request,
+                                  timeout ) != VLC_SUCCESS )
+    {
+        thumbnailer_request_Release( request );
+        return NULL;
+    }
+    return request;
+}
+
+vlc_thumbnailer_request_t*
+vlc_thumbnailer_RequestByTime( vlc_thumbnailer_t *thumbnailer,
+                               vlc_tick_t time,
+                               enum vlc_thumbnailer_seek_speed speed,
+                               input_item_t *input_item, vlc_tick_t timeout,
+                               vlc_thumbnailer_cb cb, void* user_data )
+{
+    return thumbnailer_RequestCommon( thumbnailer,
+            &(const vlc_thumbnailer_params_t){
+                .time = time,
+                .type = VLC_THUMBNAILER_SEEK_TIME,
+                .fast_seek = speed == VLC_THUMBNAILER_SEEK_FAST,
+                .input_item = input_item,
+                .timeout = timeout,
+                .cb = cb,
+                .user_data = user_data,
+        });
+}
+
+vlc_thumbnailer_request_t*
+vlc_thumbnailer_RequestByPos( vlc_thumbnailer_t *thumbnailer,
+                              float pos, enum vlc_thumbnailer_seek_speed speed,
+                              input_item_t *input_item, vlc_tick_t timeout,
+                              vlc_thumbnailer_cb cb, void* user_data )
+{
+    return thumbnailer_RequestCommon( thumbnailer,
+            &(const vlc_thumbnailer_params_t){
+                .pos = pos,
+                .type = VLC_THUMBNAILER_SEEK_POS,
+                .fast_seek = speed == VLC_THUMBNAILER_SEEK_FAST,
+                .input_item = input_item,
+                .timeout = timeout,
+                .cb = cb,
+                .user_data = user_data,
+        });
+}
+
+void vlc_thumbnailer_Cancel( vlc_thumbnailer_t* thumbnailer,
+                             vlc_thumbnailer_request_t* req )
+{
+    vlc_mutex_lock( &req->lock );
+    /* Ensure we won't invoke the callback if the input was running. */
+    req->params.cb = NULL;
+    vlc_mutex_unlock( &req->lock );
+    background_worker_Cancel( thumbnailer->worker, req );
+}
+
+vlc_thumbnailer_t *vlc_thumbnailer_Create( vlc_object_t* parent)
+{
+    vlc_thumbnailer_t *thumbnailer = malloc( sizeof( *thumbnailer ) );
+    if ( unlikely( thumbnailer == NULL ) )
+        return NULL;
+    thumbnailer->parent = parent;
+    struct background_worker_config cfg = {
+        .default_timeout = -1,
+        .max_threads = 1,
+        .pf_release = thumbnailer_request_Release,
+        .pf_hold = thumbnailer_request_Hold,
+        .pf_start = thumbnailer_request_Start,
+        .pf_probe = thumbnailer_request_Probe,
+        .pf_stop = thumbnailer_request_Stop,
+    };
+    thumbnailer->worker = background_worker_New( thumbnailer, &cfg );
+    if ( unlikely( thumbnailer->worker == NULL ) )
+    {
+        free( thumbnailer );
+        return NULL;
+    }
+    return thumbnailer;
+}
+
+void vlc_thumbnailer_Release( vlc_thumbnailer_t *thumbnailer )
+{
+    background_worker_Delete( thumbnailer->worker );
+    free( thumbnailer );
+}
diff --git a/src/input/var.c b/src/input/var.c
index 895e373b23..6038c883c1 100644
--- a/src/input/var.c
+++ b/src/input/var.c
@@ -425,6 +425,8 @@ void input_LegacyEvents( input_thread_t *p_input,
             break;
         case INPUT_EVENT_SUBITEMS:
             break;
+        case INPUT_EVENT_THUMBNAIL_READY:
+            break;
     }
     Trigger( p_input, event->type );
 }
diff --git a/src/libvlc.c b/src/libvlc.c
index f12f980936..18d55004d1 100644
--- a/src/libvlc.c
+++ b/src/libvlc.c
@@ -63,6 +63,7 @@
 #include <vlc_url.h>
 #include <vlc_modules.h>
 #include <vlc_media_library.h>
+#include <vlc_thumbnailer.h>
 
 #include "libvlc.h"
 #include "playlist/playlist_internal.h"
@@ -225,6 +226,10 @@ int libvlc_InternalInit( libvlc_int_t *p_libvlc, int i_argc,
             msg_Warn( p_libvlc, "Media library initialization failed" );
     }
 
+    priv->p_thumbnailer = vlc_thumbnailer_Create( VLC_OBJECT( p_libvlc ) );
+    if ( priv->p_thumbnailer == NULL )
+        msg_Warn( p_libvlc, "Failed to instantiate VLC thumbnailer" );
+
     /*
      * Initialize hotkey handling
      */
diff --git a/src/libvlccore.sym b/src/libvlccore.sym
index c1b2b1ea7b..c87f11c69f 100644
--- a/src/libvlccore.sym
+++ b/src/libvlccore.sym
@@ -155,6 +155,7 @@ input_Control
 input_Create
 input_CreateFilename
 input_CreatePreparser
+input_CreateThumbnailer
 input_DecoderCreate
 input_DecoderDelete
 input_DecoderDecode
@@ -786,3 +787,8 @@ vlc_es_id_Hold
 vlc_es_id_Release
 vlc_es_id_GetInputId
 vlc_es_id_GetCat
+vlc_thumbnailer_Create
+vlc_thumbnailer_RequestByTime
+vlc_thumbnailer_RequestByPos
+vlc_thumbnailer_Cancel
+vlc_thumbnailer_Release



More information about the vlc-commits mailing list