Browse Source

preparser: merge with the thumbnailer API

You should now use a preparser to generate thumbnails.

One single preparser is able to:
 - Parse
 - Fetch meta
 - Generate thumbnails

Using 3 different queues. It is also possible to create a preparser for
only one type of processing.
pull/172/head
Thomas Guillem 2 years ago
committed by Steve Lhomme
parent
commit
a4a0514ecc
  1. 1
      include/meson.build
  2. 88
      include/vlc_preparser.h
  3. 148
      include/vlc_thumbnailer.h
  4. 10
      lib/core.c
  5. 6
      lib/libvlc_internal.h
  6. 29
      lib/media.c
  7. 1
      modules/gui/qt/medialibrary/mlvideo.cpp
  8. 25
      modules/misc/medialibrary/Thumbnailer.cpp
  9. 4
      modules/misc/medialibrary/medialibrary.h
  10. 1
      src/Makefile.am
  11. 6
      src/libvlccore.sym
  12. 321
      src/preparser/preparser.c
  13. 40
      test/src/input/thumbnail.c

1
include/meson.build

@ -101,7 +101,6 @@ install_headers(
'vlc_subpicture.h',
'vlc_text_style.h',
'vlc_threads.h',
'vlc_thumbnailer.h',
'vlc_tick.h',
'vlc_timestamp_helper.h',
'vlc_tls.h',

88
include/vlc_preparser.h

@ -49,12 +49,77 @@ typedef size_t vlc_preparser_req_id;
#define VLC_PREPARSER_TYPE_PARSE 0x01
#define VLC_PREPARSER_TYPE_FETCHMETA_LOCAL 0x02
#define VLC_PREPARSER_TYPE_FETCHMETA_NET 0x04
#define VLC_PREPARSER_TYPE_THUMBNAIL 0x08
#define VLC_PREPARSER_TYPE_FETCHMETA_ALL \
(VLC_PREPARSER_TYPE_FETCHMETA_LOCAL|VLC_PREPARSER_TYPE_FETCHMETA_NET)
#define VLC_PREPARSER_OPTION_INTERACT 0x1000
#define VLC_PREPARSER_OPTION_SUBITEMS 0x2000
/** Preparser thumbnailer callbacks */
struct vlc_thumbnailer_cbs
{
/**
* Event received on thumbnailing completion or error
*
* This callback will always be called, provided
* vlc_preparser_GenerateThumbnail() returned a valid request, and provided
* the request is not cancelled before its completion.
*
* @note This callback is mandatory if calling
* vlc_preparser_GenerateThumbnail()
*
* In case of failure, timeout or cancellation, 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 item item used for the thumbnailer
*
* @param status VLC_SUCCESS in case of success, VLC_ETIMEOUT in case of
* timeout, -EINTR if cancelled, an error otherwise
*
* @param thumbnail The generated thumbnail, or NULL in case of failure or
* timeout
*
* @param data opaque pointer passed by
* vlc_preparser_GenerateThumbnail()
*
*/
void (*on_ended)(input_item_t *item, int status, picture_t* thumbnail,
void *data);
};
/**
* Preparser seek argument
*/
struct vlc_preparser_seek_arg
{
enum
{
/** Don't seek */
VLC_PREPARSER_SEEK_NONE,
/** Seek by time */
VLC_PREPARSER_SEEK_TIME,
/** Seek by position */
VLC_PREPARSER_SEEK_POS,
} type;
union
{
/** Seek time if type == VLC_PREPARSER_SEEK_TIME */
vlc_tick_t time;
/** Seek position if type == VLC_PREPARSER_SEEK_POS */
double pos;
};
enum
{
/** Precise, but potentially slow */
VLC_PREPARSER_SEEK_PRECISE,
/** Fast, but potentially imprecise */
VLC_PREPARSER_SEEK_FAST,
} speed;
};
/**
* This function creates the preparser object and thread.
*
@ -93,6 +158,29 @@ VLC_API vlc_preparser_req_id
vlc_preparser_Push( vlc_preparser_t *preparser, input_item_t *item, int type_option,
const input_item_parser_cbs_t *cbs, void *cbs_userdata );
/**
* This function enqueues the provided item for generating a thumbnail
*
* @param preparser the preparser object
* @param item a valid item to generate the thumbnail for
* @param seek_arg pointer to a seek struct, that tell at which time the
* thumbnail should be taken, NULL to disable seek
* @param timeout A timeout value, or VLC_TICK_INVALID to disable timeout
* @param cbs callback to listen to events (can't be NULL)
* @param cbs_userdata opaque pointer used by the callbacks
* @return VLC_PREPARSER_REQ_ID_INVALID in case of error, or a valid id if the
* item was scheduled for thumbnailing. If this returns an
* error, the thumbnailer.on_ended callback will *not* be invoked
*
* The provided input_item will be held by the thumbnailer and can safely be
* released safely after calling this function.
*/
VLC_API vlc_preparser_req_id
vlc_preparser_GenerateThumbnail( vlc_preparser_t *preparser, input_item_t *item,
const struct vlc_preparser_seek_arg *seek_arg,
const struct vlc_thumbnailer_cbs *cbs,
void *cbs_userdata );
/**
* This function cancel all preparsing requests for a given id
*

148
include/vlc_thumbnailer.h

@ -1,148 +0,0 @@
/*****************************************************************************
* vlc_thumbnailer.h: Thumbnailing API
*****************************************************************************
* Copyright (C) 2018 VLC authors and VideoLAN
*
* Authors: Hugo Beauzée-Luyssen <hugo@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>
#include <vlc_tick.h>
typedef struct vlc_thumbnailer_t vlc_thumbnailer_t;
typedef size_t vlc_thumbnailer_req_id;
#define VLC_THUMBNAILER_REQ_ID_INVALID 0
/**
* thumbnailer callbacks
*/
struct vlc_thumbnailer_cbs
{
/**
* Event received on thumbnailing completion or error
*
* This callback will always be called, provided vlc_thumbnailer_Request
* returned a valid request, and provided the request is not cancelled
* before its completion.
*
* @note This callback is mandatory.
*
* In case of failure, timeout or cancellation, 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 item item used for the thumbnailer
* \param status VLC_SUCCESS in case of success, VLC_ETIMEOUT in case of
* timeout, -EINTR if cancelled, an error otherwise
* \param thumbnail The generated thumbnail, or NULL in case of failure or
* timeout
* \param data Is the opaque pointer passed as vlc_thumbnailer_Request last
* parameter
*/
void (*on_ended)(input_item_t *item, int status, picture_t* thumbnail, void *data);
};
/**
* \brief vlc_thumbnailer_Create Creates a thumbnailer object
* \param parent A VLC object
* @param timeout timeout of the thumbnailer, 0 for no limits.
* \return A thumbnailer object, or NULL in case of failure
*/
VLC_API vlc_thumbnailer_t*
vlc_thumbnailer_Create(vlc_object_t* parent, vlc_tick_t timeout) VLC_USED;
/**
* Thumbnailer seek argument
*/
struct vlc_thumbnailer_seek_arg
{
enum
{
/** Don't seek */
VLC_THUMBNAILER_SEEK_NONE,
/** Seek by time */
VLC_THUMBNAILER_SEEK_TIME,
/** Seek by position */
VLC_THUMBNAILER_SEEK_POS,
} type;
union
{
/** Seek time if type == VLC_THUMBNAILER_SEEK_TIME */
vlc_tick_t time;
/** Seek position if type == VLC_THUMBNAILER_SEEK_POS */
double pos;
};
enum
{
/** Precise, but potentially slow */
VLC_THUMBNAILER_SEEK_PRECISE,
/** Fast, but potentially imprecise */
VLC_THUMBNAILER_SEEK_FAST,
} speed;
};
/**
* \brief vlc_thumbnailer_Request Requests a thumbnailer
* \param thumbnailer A thumbnailer object
* \param input_item The input item to generate the thumbnail for
* \param seek_arg pointer to a seek struct, that tell at which time the
* thumbnail should be taken, NULL to disable seek
* \param timeout A timeout value, or VLC_TICK_INVALID to disable timeout
* \param cbs callback to listen to events (can't be NULL)
* \param cbs_userdata opaque pointer used by the callbacks
* \return VLC_THUMBNAILER_REQ_ID_INVALID in case of error, or a valid id if the
* item was scheduled for thumbnailing. If this returns an
* error, the on_ended callback will *not* be invoked
*
* The provided input_item will be held by the thumbnailer and can safely be
* released safely after calling this function.
*/
VLC_API vlc_thumbnailer_req_id
vlc_thumbnailer_Request( vlc_thumbnailer_t *thumbnailer,
input_item_t *input_item,
const struct vlc_thumbnailer_seek_arg *seek_arg,
const struct vlc_thumbnailer_cbs *cbs,
void *cbs_userdata );
/**
* \brief vlc_thumbnailer_Camcel Cancel a thumbnail request
* \param thumbnailer A thumbnailer object
* \param id unique id returned by vlc_thumbnailer_Request*(),
* VLC_THUMBNAILER_REQ_ID_INVALID to cancels all tasks
* \return number of tasks cancelled
*/
VLC_API size_t
vlc_thumbnailer_Cancel( vlc_thumbnailer_t* thumbnailer, vlc_thumbnailer_req_id id );
/**
* \brief vlc_thumbnailer_Delete Deletes a thumbnailer and cancel all pending requests
* \param thumbnailer A thumbnailer object
*/
VLC_API void vlc_thumbnailer_Delete( vlc_thumbnailer_t* thumbnailer );
/**
* Do not use, libVLC only fonction, will be removed soon
*/
VLC_API void vlc_thumbnailer_SetTimeout( vlc_thumbnailer_t *thumbnailer,
vlc_tick_t timeout ) VLC_DEPRECATED;
#endif // VLC_THUMBNAILER_H

10
lib/core.c

@ -29,7 +29,6 @@
#include <vlc/vlc.h>
#include <vlc_preparser.h>
#include <vlc_thumbnailer.h>
#include <vlc_interface.h>
#include <stdarg.h>
@ -110,7 +109,7 @@ void libvlc_release( libvlc_instance_t *p_instance )
if (p_instance->parser != NULL)
vlc_preparser_Delete(p_instance->parser);
if (p_instance->thumbnailer != NULL)
vlc_thumbnailer_Delete(p_instance->thumbnailer);
vlc_preparser_Delete(p_instance->thumbnailer);
libvlc_InternalCleanup( p_instance->p_libvlc_int );
libvlc_InternalDestroy( p_instance->p_libvlc_int );
@ -277,15 +276,16 @@ vlc_preparser_t *libvlc_get_preparser(libvlc_instance_t *instance)
return parser;
}
vlc_thumbnailer_t *libvlc_get_thumbnailer(libvlc_instance_t *instance)
vlc_preparser_t *libvlc_get_thumbnailer(libvlc_instance_t *instance)
{
vlc_mutex_lock(&instance->lazy_init_lock);
vlc_thumbnailer_t *thumb = instance->thumbnailer;
vlc_preparser_t *thumb = instance->thumbnailer;
if (thumb == NULL)
{
thumb = instance->thumbnailer =
vlc_thumbnailer_Create(VLC_OBJECT(instance->p_libvlc_int), 0);
vlc_preparser_New(VLC_OBJECT(instance->p_libvlc_int), 1, 0,
VLC_PREPARSER_TYPE_THUMBNAIL);
}
vlc_mutex_unlock(&instance->lazy_init_lock);

6
lib/libvlc_internal.h

@ -36,7 +36,7 @@
#include <vlc_threads.h>
typedef struct vlc_preparser_t vlc_preparser_t;
typedef struct vlc_thumbnailer_t vlc_thumbnailer_t;
typedef struct vlc_preparser_t vlc_preparser_t;
/* Note well: this header is included from LibVLC core.
* Therefore, static inline functions MUST NOT call LibVLC functions here
@ -105,7 +105,7 @@ struct libvlc_instance_t
vlc_mutex_t lazy_init_lock;
vlc_preparser_t *parser;
vlc_thumbnailer_t *thumbnailer;
vlc_preparser_t *thumbnailer;
struct
{
@ -153,6 +153,6 @@ static inline vlc_tick_t vlc_tick_from_libvlc_time(libvlc_time_t time)
}
vlc_preparser_t *libvlc_get_preparser(libvlc_instance_t *instance);
vlc_thumbnailer_t *libvlc_get_thumbnailer(libvlc_instance_t *instance);
vlc_preparser_t *libvlc_get_thumbnailer(libvlc_instance_t *instance);
#endif

29
lib/media.c

@ -37,7 +37,6 @@
#include <vlc_common.h>
#include <vlc_meta.h>
#include <vlc_url.h>
#include <vlc_thumbnailer.h>
#include <vlc_atomic.h>
#include "../src/libvlc.h"
@ -924,7 +923,7 @@ struct libvlc_media_thumbnail_request_t
unsigned int height;
bool crop;
libvlc_picture_type_t type;
vlc_thumbnailer_req_id id;
vlc_preparser_req_id id;
};
static void media_on_thumbnail_ready( input_item_t *item, int status,
@ -952,18 +951,18 @@ static void media_on_thumbnail_ready( input_item_t *item, int status,
static libvlc_media_thumbnail_request_t*
libvlc_media_thumbnail_request( libvlc_instance_t *inst,
libvlc_media_t *md,
const struct vlc_thumbnailer_seek_arg *seek_arg,
const struct vlc_preparser_seek_arg *seek_arg,
unsigned int width, unsigned int height,
bool crop, libvlc_picture_type_t picture_type,
libvlc_time_t timeout )
{
assert( md );
vlc_thumbnailer_t *thumb = libvlc_get_thumbnailer(inst);
vlc_preparser_t *thumb = libvlc_get_thumbnailer(inst);
if (unlikely(thumb == NULL))
return NULL;
vlc_thumbnailer_SetTimeout( thumb, vlc_tick_from_libvlc_time( timeout ) );
vlc_preparser_SetTimeout( thumb, vlc_tick_from_libvlc_time( timeout ) );
libvlc_media_thumbnail_request_t *req = malloc( sizeof( *req ) );
if ( unlikely( req == NULL ) )
@ -979,8 +978,8 @@ libvlc_media_thumbnail_request( libvlc_instance_t *inst,
static const struct vlc_thumbnailer_cbs cbs = {
.on_ended = media_on_thumbnail_ready,
};
req->id = vlc_thumbnailer_Request( thumb, md->p_input_item, seek_arg,
&cbs, req );
req->id = vlc_preparser_GenerateThumbnail( thumb, md->p_input_item, seek_arg,
&cbs, req );
if ( req->id == VLC_PREPARSER_REQ_ID_INVALID )
{
free( req );
@ -999,11 +998,11 @@ libvlc_media_thumbnail_request_by_time( libvlc_instance_t *inst,
bool crop, libvlc_picture_type_t picture_type,
libvlc_time_t timeout )
{
const struct vlc_thumbnailer_seek_arg seek_arg = {
.type = VLC_THUMBNAILER_SEEK_TIME,
const struct vlc_preparser_seek_arg seek_arg = {
.type = VLC_PREPARSER_SEEK_TIME,
.time = vlc_tick_from_libvlc_time( time ),
.speed = speed == libvlc_media_thumbnail_seek_fast ?
VLC_THUMBNAILER_SEEK_FAST : VLC_THUMBNAILER_SEEK_PRECISE,
VLC_PREPARSER_SEEK_FAST : VLC_PREPARSER_SEEK_PRECISE,
};
return libvlc_media_thumbnail_request( inst, md, &seek_arg, width, height,
crop, picture_type, timeout );
@ -1018,11 +1017,11 @@ libvlc_media_thumbnail_request_by_pos( libvlc_instance_t *inst,
bool crop, libvlc_picture_type_t picture_type,
libvlc_time_t timeout )
{
const struct vlc_thumbnailer_seek_arg seek_arg = {
.type = VLC_THUMBNAILER_SEEK_POS,
const struct vlc_preparser_seek_arg seek_arg = {
.type = VLC_PREPARSER_SEEK_POS,
.pos = pos,
.speed = speed == libvlc_media_thumbnail_seek_fast ?
VLC_THUMBNAILER_SEEK_FAST : VLC_THUMBNAILER_SEEK_PRECISE,
VLC_PREPARSER_SEEK_FAST : VLC_PREPARSER_SEEK_PRECISE,
};
return libvlc_media_thumbnail_request( inst, md, &seek_arg, width, height,
crop, picture_type, timeout );
@ -1031,10 +1030,10 @@ libvlc_media_thumbnail_request_by_pos( libvlc_instance_t *inst,
// Destroy a thumbnail request
void libvlc_media_thumbnail_request_destroy( libvlc_media_thumbnail_request_t *req )
{
vlc_thumbnailer_t *thumb = libvlc_get_thumbnailer(req->instance);
vlc_preparser_t *thumb = libvlc_get_thumbnailer(req->instance);
assert(thumb != NULL);
vlc_thumbnailer_Cancel( thumb, req->id );
vlc_preparser_Cancel( thumb, req->id );
libvlc_media_release( req->md );
libvlc_release(req->instance);
free( req );

1
modules/gui/qt/medialibrary/mlvideo.cpp

@ -20,7 +20,6 @@
#include <cassert>
#include <vlc_thumbnailer.h>
#include "mlhelper.hpp"
#include "util/vlctick.hpp"

25
modules/misc/medialibrary/Thumbnailer.cpp

@ -24,23 +24,24 @@
#include "medialibrary.h"
#include <vlc_thumbnailer.h>
#include <vlc_fs.h>
#include <vlc_block.h>
#include <vlc_url.h>
#include <vlc_cxx_helpers.hpp>
#include <vlc_preparser.h>
#include <stdexcept>
Thumbnailer::Thumbnailer( vlc_medialibrary_module_t* ml )
: m_ml( ml )
, m_currentContext( nullptr )
, m_thumbnailer( nullptr, &vlc_thumbnailer_Delete )
, m_thumbnailer( nullptr, &vlc_preparser_Delete )
{
m_thumbnailer.reset( vlc_thumbnailer_Create( VLC_OBJECT( ml ),
VLC_TICK_FROM_SEC( 3 ) ) );
m_thumbnailer.reset( vlc_preparser_New( VLC_OBJECT( ml ), 1,
VLC_TICK_FROM_SEC( 3 ),
VLC_PREPARSER_TYPE_THUMBNAIL ) );
if ( unlikely( m_thumbnailer == nullptr ) )
throw std::runtime_error( "Failed to instantiate a vlc_thumbnailer_t" );
throw std::runtime_error( "Failed to instantiate a vlc_preparser_t" );
}
void Thumbnailer::onThumbnailComplete( input_item_t *, int, picture_t* thumbnail, void *data )
@ -71,20 +72,20 @@ bool Thumbnailer::generate( const medialibrary::IMedia&, const std::string& mrl,
{
vlc::threads::mutex_locker lock( m_mutex );
m_currentContext = &ctx;
struct vlc_thumbnailer_seek_arg seek_arg = {
.type = vlc_thumbnailer_seek_arg::VLC_THUMBNAILER_SEEK_POS,
struct vlc_preparser_seek_arg seek_arg = {
.type = vlc_preparser_seek_arg::VLC_PREPARSER_SEEK_POS,
.pos = position,
.speed = vlc_thumbnailer_seek_arg::VLC_THUMBNAILER_SEEK_FAST,
.speed = vlc_preparser_seek_arg::VLC_PREPARSER_SEEK_FAST,
};
static const struct vlc_thumbnailer_cbs cbs = {
.on_ended = onThumbnailComplete,
};
vlc_thumbnailer_req_id requestId =
vlc_thumbnailer_Request( m_thumbnailer.get(), item.get(), &seek_arg,
&cbs, &ctx );
vlc_preparser_req_id requestId =
vlc_preparser_GenerateThumbnail( m_thumbnailer.get(), item.get(),
&seek_arg, &cbs, &ctx );
if (requestId == VLC_THUMBNAILER_REQ_ID_INVALID)
if (requestId == VLC_PREPARSER_REQ_ID_INVALID)
{
m_currentContext = nullptr;
return false;

4
modules/misc/medialibrary/medialibrary.h

@ -40,7 +40,7 @@
struct vlc_event_t;
struct vlc_object_t;
struct vlc_thumbnailer_t;
struct vlc_preparser_t;
class Logger;
@ -144,7 +144,7 @@ private:
vlc::threads::mutex m_mutex;
vlc::threads::condition_variable m_cond;
ThumbnailerCtx* m_currentContext;
std::unique_ptr<vlc_thumbnailer_t, void(*)(vlc_thumbnailer_t*)> m_thumbnailer;
std::unique_ptr<vlc_preparser_t, void(*)(vlc_preparser_t*)> m_thumbnailer;
};
class MediaLibrary : public medialibrary::IMediaLibraryCb

1
src/Makefile.am

@ -107,7 +107,6 @@ pluginsinclude_HEADERS.h = \
../include/vlc_subpicture.h \
../include/vlc_text_style.h \
../include/vlc_threads.h \
../include/vlc_thumbnailer.h \
../include/vlc_tick.h \
../include/vlc_timestamp_helper.h \
../include/vlc_tls.h \

6
src/libvlccore.sym

@ -830,11 +830,6 @@ vlc_es_id_GetCat
vlc_es_id_GetStrId
vlc_es_id_IsStrIdStable
vlc_encoder_Destroy
vlc_thumbnailer_Create
vlc_thumbnailer_Request
vlc_thumbnailer_Cancel
vlc_thumbnailer_Delete
vlc_thumbnailer_SetTimeout
vlc_player_AddAssociatedMedia
vlc_player_AddListener
vlc_player_AddMetadataListener
@ -1042,6 +1037,7 @@ vlc_input_attachment_New
vlc_input_attachment_Hold
vlc_preparser_New
vlc_preparser_Push
vlc_preparser_GenerateThumbnail
vlc_preparser_Cancel
vlc_preparser_Delete
vlc_preparser_Deactivate

321
src/preparser/preparser.c

@ -26,17 +26,23 @@
#include <vlc_atomic.h>
#include <vlc_executor.h>
#include <vlc_preparser.h>
#include <vlc_thumbnailer.h>
#include "input/input_interface.h"
#include "input/input_internal.h"
#include "fetcher.h"
union vlc_preparser_cbs
{
const input_item_parser_cbs_t *parser;
const struct vlc_thumbnailer_cbs *thumbnailer;
};
struct vlc_preparser_t
{
vlc_object_t* owner;
input_fetcher_t* fetcher;
vlc_executor_t *parser;
vlc_executor_t *thumbnailer;
vlc_tick_t timeout;
atomic_bool deactivated;
@ -50,10 +56,13 @@ struct task
vlc_preparser_t *preparser;
input_item_t *item;
int options;
const input_item_parser_cbs_t *cbs;
struct vlc_preparser_seek_arg seek_arg;
union vlc_preparser_cbs cbs;
void *userdata;
vlc_preparser_req_id id;
picture_t *pic;
vlc_sem_t preparse_ended;
int preparse_status;
atomic_bool interrupted;
@ -63,42 +72,10 @@ struct task
struct vlc_list node; /**< node of vlc_preparser_t.submitted_tasks */
};
struct vlc_thumbnailer_t
{
vlc_object_t* parent;
vlc_executor_t *executor;
vlc_mutex_t lock;
vlc_thumbnailer_req_id current_id;
vlc_tick_t timeout;
struct vlc_list submitted_tasks; /**< list of struct th_task */
};
typedef struct th_task
{
vlc_thumbnailer_t *thumbnailer;
struct vlc_thumbnailer_seek_arg seek_arg;
input_item_t *item;
const struct vlc_thumbnailer_cbs *cbs;
void* userdata;
vlc_sem_t preparse_ended;
int preparse_status;
atomic_bool interrupted;
picture_t *pic;
vlc_thumbnailer_req_id id;
struct vlc_runnable runnable; /**< to be passed to the executor */
struct vlc_list node; /**< node of vlc_thumbnailer_t.submitted_tasks */
} th_task_t;
static struct task *
TaskNew(vlc_preparser_t *preparser, void (*run)(void *), input_item_t *item,
int options, const input_item_parser_cbs_t *cbs, void *userdata)
int options, const struct vlc_preparser_seek_arg *seek_arg,
union vlc_preparser_cbs cbs, void *userdata)
{
struct task *task = malloc(sizeof(*task));
if (!task)
@ -109,6 +86,14 @@ TaskNew(vlc_preparser_t *preparser, void (*run)(void *), input_item_t *item,
task->options = options;
task->cbs = cbs;
task->userdata = userdata;
task->pic = NULL;
if (seek_arg == NULL)
task->seek_arg = (struct vlc_preparser_seek_arg) {
.type = VLC_PREPARSER_SEEK_NONE,
};
else
task->seek_arg = *seek_arg;
input_item_Hold(item);
@ -165,7 +150,8 @@ NotifyPreparseEnded(struct task *task)
else if (task->preparse_status == VLC_SUCCESS)
input_item_SetPreparsed(task->item);
task->cbs->on_ended(task->item, task->preparse_status, task->userdata);
task->cbs.parser->on_ended(task->item, task->preparse_status,
task->userdata);
}
static void
@ -188,8 +174,8 @@ OnParserSubtreeAdded(input_item_t *item, input_item_node_t *subtree,
if (atomic_load(&task->interrupted))
return;
if (task->cbs->on_subtree_added)
task->cbs->on_subtree_added(task->item, subtree, task->userdata);
if (task->cbs.parser->on_subtree_added)
task->cbs.parser->on_subtree_added(task->item, subtree, task->userdata);
}
static void
@ -203,8 +189,9 @@ OnParserAttachmentsAdded(input_item_t *item,
if (atomic_load(&task->interrupted))
return;
if (task->cbs->on_attachments_added)
task->cbs->on_attachments_added(task->item, array, count, task->userdata);
if (task->cbs.parser->on_attachments_added)
task->cbs.parser->on_attachments_added(task->item, array, count,
task->userdata);
}
static void
@ -311,11 +298,6 @@ end:
TaskDelete(task);
}
static void NotifyThumbnail(th_task_t *task, picture_t *pic)
{
task->cbs->on_ended(task->item, task->preparse_status, pic, task->userdata);
}
static void
on_thumbnailer_input_event( input_thread_t *input,
const struct vlc_input_event *event, void *userdata )
@ -326,7 +308,7 @@ on_thumbnailer_input_event( input_thread_t *input,
event->state.value != END_S ) ) )
return;
th_task_t *task = userdata;
struct task *task = userdata;
if (event->type == INPUT_EVENT_THUMBNAIL_READY)
{
@ -336,20 +318,13 @@ on_thumbnailer_input_event( input_thread_t *input,
vlc_sem_post(&task->preparse_ended);
}
static void
ThTaskDestroy(th_task_t *task)
{
input_item_Release(task->item);
free(task);
}
static void
ThumbnailerRun(void *userdata)
{
vlc_thread_set_name("vlc-run-thumb");
th_task_t *task = userdata;
vlc_thumbnailer_t *thumbnailer = task->thumbnailer;
struct task *task = userdata;
vlc_preparser_t *preparser = task->preparser;
static const struct vlc_input_thread_callbacks cbs = {
.on_event = on_thumbnailer_input_event,
@ -361,27 +336,27 @@ ThumbnailerRun(void *userdata)
.cbs_data = task,
};
vlc_tick_t deadline = thumbnailer->timeout != VLC_TICK_INVALID ?
vlc_tick_now() + thumbnailer->timeout :
vlc_tick_t deadline = preparser->timeout != VLC_TICK_INVALID ?
vlc_tick_now() + preparser->timeout :
VLC_TICK_INVALID;
input_thread_t* input =
input_Create( thumbnailer->parent, task->item, &cfg );
input_Create( preparser->owner, task->item, &cfg );
if (!input)
goto error;
assert(task->seek_arg.speed == VLC_THUMBNAILER_SEEK_PRECISE
|| task->seek_arg.speed == VLC_THUMBNAILER_SEEK_FAST);
bool fast_seek = task->seek_arg.speed == VLC_THUMBNAILER_SEEK_FAST;
assert(task->seek_arg.speed == VLC_PREPARSER_SEEK_PRECISE
|| task->seek_arg.speed == VLC_PREPARSER_SEEK_FAST);
bool fast_seek = task->seek_arg.speed == VLC_PREPARSER_SEEK_FAST;
switch (task->seek_arg.type)
{
case VLC_THUMBNAILER_SEEK_NONE:
case VLC_PREPARSER_SEEK_NONE:
break;
case VLC_THUMBNAILER_SEEK_TIME:
case VLC_PREPARSER_SEEK_TIME:
input_SetTime(input, task->seek_arg.time, fast_seek);
break;
case VLC_THUMBNAILER_SEEK_POS:
case VLC_PREPARSER_SEEK_POS:
input_SetPosition(input, task->seek_arg.pos, fast_seek);
break;
default:
@ -409,11 +384,13 @@ ThumbnailerRun(void *userdata)
picture_t* pic = task->pic;
task->pic = NULL;
vlc_mutex_lock(&thumbnailer->lock);
vlc_mutex_lock(&preparser->lock);
vlc_list_remove(&task->node);
vlc_mutex_unlock(&thumbnailer->lock);
vlc_mutex_unlock(&preparser->lock);
NotifyThumbnail(task, task->preparse_status == VLC_SUCCESS ? pic : NULL);
task->cbs.thumbnailer->on_ended(task->item, task->preparse_status,
task->preparse_status == VLC_SUCCESS ?
pic : NULL, task->userdata);
if (pic)
picture_Release(pic);
@ -422,7 +399,7 @@ ThumbnailerRun(void *userdata)
input_Close(input);
error:
ThTaskDestroy(task);
TaskDelete(task);
}
static void
@ -438,7 +415,9 @@ vlc_preparser_t* vlc_preparser_New( vlc_object_t *parent, unsigned max_threads,
{
assert(max_threads >= 1);
assert(timeout >= 0);
assert(request_type & (VLC_PREPARSER_TYPE_FETCHMETA_ALL|VLC_PREPARSER_TYPE_PARSE));
assert(request_type & (VLC_PREPARSER_TYPE_FETCHMETA_ALL|
VLC_PREPARSER_TYPE_PARSE|
VLC_PREPARSER_TYPE_THUMBNAIL));
vlc_preparser_t* preparser = malloc( sizeof *preparser );
if (!preparser)
@ -465,6 +444,15 @@ vlc_preparser_t* vlc_preparser_New( vlc_object_t *parent, unsigned max_threads,
else
preparser->fetcher = NULL;
if (request_type & VLC_PREPARSER_TYPE_THUMBNAIL)
{
preparser->thumbnailer = vlc_executor_New(1);
if (!preparser->thumbnailer)
goto error_thumbnail;
}
else
preparser->thumbnailer = NULL;
atomic_init( &preparser->deactivated, false );
vlc_mutex_init(&preparser->lock);
@ -473,6 +461,9 @@ vlc_preparser_t* vlc_preparser_New( vlc_object_t *parent, unsigned max_threads,
return preparser;
error_thumbnail:
if (preparser->fetcher != NULL)
input_fetcher_Delete(preparser->fetcher);
error_fetcher:
if (preparser->parser != NULL)
vlc_executor_Delete(preparser->parser);
@ -489,6 +480,8 @@ vlc_preparser_req_id vlc_preparser_Push( vlc_preparser_t *preparser, input_item_
if( atomic_load( &preparser->deactivated ) )
return VLC_PREPARSER_REQ_ID_INVALID;
assert((type_options & VLC_PREPARSER_TYPE_THUMBNAIL) == 0);
assert(type_options & VLC_PREPARSER_TYPE_PARSE
|| type_options & VLC_PREPARSER_TYPE_FETCHMETA_ALL);
@ -499,8 +492,12 @@ vlc_preparser_req_id vlc_preparser_Push( vlc_preparser_t *preparser, input_item_
assert(cbs != NULL && cbs->on_ended != NULL);
struct task *task =
TaskNew(preparser, ParserRun, item, type_options, cbs, cbs_userdata);
union vlc_preparser_cbs task_cbs = {
.parser = cbs,
};
struct task *task = TaskNew(preparser, ParserRun, item, type_options, NULL,
task_cbs, cbs_userdata);
if( !task )
return VLC_PREPARSER_REQ_ID_INVALID;
@ -523,6 +520,35 @@ vlc_preparser_req_id vlc_preparser_Push( vlc_preparser_t *preparser, input_item_
return ret == VLC_SUCCESS ? id : 0;
}
vlc_preparser_req_id
vlc_preparser_GenerateThumbnail( vlc_preparser_t *preparser, input_item_t *item,
const struct vlc_preparser_seek_arg *seek_arg,
const struct vlc_thumbnailer_cbs *cbs,
void *cbs_userdata )
{
if( atomic_load( &preparser->deactivated ) )
return VLC_PREPARSER_REQ_ID_INVALID;
assert(preparser->thumbnailer != NULL);
assert(cbs != NULL && cbs->on_ended != NULL);
union vlc_preparser_cbs task_cbs = {
.thumbnailer = cbs,
};
struct task *task =
TaskNew(preparser, ThumbnailerRun, item, VLC_PREPARSER_TYPE_THUMBNAIL,
seek_arg, task_cbs, cbs_userdata);
if (task == NULL)
return VLC_PREPARSER_REQ_ID_INVALID;
vlc_preparser_req_id id = PreparserAddTask(preparser, task);
vlc_executor_Submit(preparser->thumbnailer, &task->runnable);
return id;
}
size_t vlc_preparser_Cancel( vlc_preparser_t *preparser, vlc_preparser_req_id id )
{
vlc_mutex_lock(&preparser->lock);
@ -542,8 +568,13 @@ size_t vlc_preparser_Cancel( vlc_preparser_t *preparser, vlc_preparser_req_id id
vlc_list_remove(&task->node);
vlc_mutex_unlock(&preparser->lock);
task->preparse_status = -EINTR;
task->cbs->parser.on_ended(task->item, task->preparse_status,
task->userdata);
if (task->options == VLC_PREPARSER_TYPE_THUMBNAIL)
task->cbs.thumbnailer->on_ended(task->item,
task->preparse_status, NULL,
task->userdata);
else
task->cbs.parser->on_ended(task->item, task->preparse_status,
task->userdata);
TaskDelete(task);
/* Small optimisation in the likely case where the user cancel
@ -586,144 +617,8 @@ void vlc_preparser_Delete( vlc_preparser_t *preparser )
if( preparser->fetcher )
input_fetcher_Delete( preparser->fetcher );
free( preparser );
}
if (preparser->thumbnailer != NULL)
vlc_executor_Delete(preparser->thumbnailer);
static void ThumbnailerRun(void *);
static th_task_t *
ThTaskNew(vlc_thumbnailer_t *thumbnailer, input_item_t *item,
const struct vlc_thumbnailer_seek_arg *seek_arg,
const struct vlc_thumbnailer_cbs *cbs, void *userdata )
{
th_task_t *task = malloc(sizeof(*task));
if (!task)
return NULL;
vlc_sem_init(&task->preparse_ended, 0);
atomic_init(&task->interrupted, false);
task->preparse_status = VLC_EGENERIC;
task->thumbnailer = thumbnailer;
task->item = item;
if (seek_arg == NULL)
task->seek_arg = (struct vlc_thumbnailer_seek_arg) {
.type = VLC_THUMBNAILER_SEEK_NONE,
};
else
task->seek_arg = *seek_arg;
task->cbs = cbs;
task->userdata = userdata;
task->pic = NULL;
task->runnable.run = ThumbnailerRun;
task->runnable.userdata = task;
input_item_Hold(item);
return task;
}
vlc_thumbnailer_req_id
vlc_thumbnailer_Request( vlc_thumbnailer_t *thumbnailer,
input_item_t *item,
const struct vlc_thumbnailer_seek_arg *seek_arg,
const struct vlc_thumbnailer_cbs *cbs, void *userdata )
{
assert( cbs != NULL && cbs->on_ended != NULL );
th_task_t *task = ThTaskNew(thumbnailer, item, seek_arg, cbs, userdata);
if (!task)
return 0;
vlc_mutex_lock(&thumbnailer->lock);
vlc_thumbnailer_req_id id = task->id = thumbnailer->current_id++;
static_assert(VLC_THUMBNAILER_REQ_ID_INVALID == 0, "Invalid id should be 0");
if (unlikely(thumbnailer->current_id == 0)) /* unsigned wrapping */
++thumbnailer->current_id;
vlc_list_append(&task->node, &thumbnailer->submitted_tasks);
vlc_mutex_unlock(&thumbnailer->lock);
vlc_executor_Submit(thumbnailer->executor, &task->runnable);
return id;
}
size_t vlc_thumbnailer_Cancel( vlc_thumbnailer_t* thumbnailer, vlc_thumbnailer_req_id id )
{
vlc_mutex_lock(&thumbnailer->lock);
th_task_t *task;
size_t count = 0;
vlc_list_foreach(task, &thumbnailer->submitted_tasks, node)
{
if (id == VLC_THUMBNAILER_REQ_ID_INVALID || task->id == id)
{
count++;
bool canceled =
vlc_executor_Cancel(thumbnailer->executor, &task->runnable);
if (canceled)
{
vlc_list_remove(&task->node);
vlc_mutex_unlock(&thumbnailer->lock);
task->preparse_status = -EINTR;
NotifyThumbnail(task, NULL);
ThTaskDestroy(task);
/* Small optimisation in the likely case where the user cancel
* only one task */
if (id != VLC_THUMBNAILER_REQ_ID_INVALID)
return count;
vlc_mutex_lock(&thumbnailer->lock);
}
else
{
/* The task will be finished and destroyed after run() */
atomic_store(&task->interrupted, true);
vlc_sem_post(&task->preparse_ended);
}
}
}
vlc_mutex_unlock(&thumbnailer->lock);
return count;
}
vlc_thumbnailer_t *vlc_thumbnailer_Create( vlc_object_t* parent, vlc_tick_t timeout )
{
assert(timeout >= 0);
vlc_thumbnailer_t *thumbnailer = malloc( sizeof( *thumbnailer ) );
if ( unlikely( thumbnailer == NULL ) )
return NULL;
thumbnailer->executor = vlc_executor_New(1);
if (!thumbnailer->executor)
{
free(thumbnailer);
return NULL;
}
thumbnailer->parent = parent;
thumbnailer->current_id = 1;
thumbnailer->timeout = timeout;
vlc_mutex_init(&thumbnailer->lock);
vlc_list_init(&thumbnailer->submitted_tasks);
return thumbnailer;
}
void vlc_thumbnailer_SetTimeout( vlc_thumbnailer_t *thumbnailer,
vlc_tick_t timeout )
{
thumbnailer->timeout = timeout;
}
void vlc_thumbnailer_Delete( vlc_thumbnailer_t *thumbnailer )
{
vlc_executor_Delete(thumbnailer->executor);
free( thumbnailer );
free( preparser );
}

40
test/src/input/thumbnail.c

@ -22,7 +22,7 @@
#include "../lib/libvlc_internal.h"
#include <vlc_common.h>
#include <vlc_thumbnailer.h>
#include <vlc_preparser.h>
#include <vlc_input_item.h>
#include <vlc_picture.h>
@ -128,8 +128,9 @@ static void test_thumbnails( libvlc_instance_t* p_vlc )
ctx.test_idx = i;
ctx.b_done = false;
vlc_thumbnailer_t* p_thumbnailer = vlc_thumbnailer_Create(
VLC_OBJECT( p_vlc->p_libvlc_int ), test_params[i].i_timeout );
vlc_preparser_t* p_thumbnailer = vlc_preparser_New(
VLC_OBJECT( p_vlc->p_libvlc_int ), 1, test_params[i].i_timeout,
VLC_PREPARSER_TYPE_THUMBNAIL );
assert( p_thumbnailer != NULL );
@ -145,28 +146,28 @@ static void test_thumbnails( libvlc_instance_t* p_vlc )
vlc_mutex_lock( &ctx.lock );
vlc_thumbnailer_req_id id;
struct vlc_thumbnailer_seek_arg seek_arg;
vlc_preparser_req_id id;
struct vlc_preparser_seek_arg seek_arg;
if ( test_params[i].b_use_pos )
{
seek_arg.type = VLC_THUMBNAILER_SEEK_POS;
seek_arg.type = VLC_PREPARSER_SEEK_POS;
seek_arg.pos = test_params[i].f_pos;
seek_arg.speed = test_params[i].b_fast_seek ?
VLC_THUMBNAILER_SEEK_FAST : VLC_THUMBNAILER_SEEK_PRECISE;
VLC_PREPARSER_SEEK_FAST : VLC_PREPARSER_SEEK_PRECISE;
}
else
{
seek_arg.type = VLC_THUMBNAILER_SEEK_TIME;
seek_arg.type = VLC_PREPARSER_SEEK_TIME;
seek_arg.time = test_params[i].i_time;
seek_arg.speed = test_params[i].b_fast_seek ?
VLC_THUMBNAILER_SEEK_FAST : VLC_THUMBNAILER_SEEK_PRECISE;
VLC_PREPARSER_SEEK_FAST : VLC_PREPARSER_SEEK_PRECISE;
}
static const struct vlc_thumbnailer_cbs cbs = {
.on_ended = thumbnailer_callback,
};
id = vlc_thumbnailer_Request( p_thumbnailer, p_item, &seek_arg,
&cbs, &ctx );
assert( id != VLC_THUMBNAILER_REQ_ID_INVALID );
id = vlc_preparser_GenerateThumbnail( p_thumbnailer, p_item, &seek_arg,
&cbs, &ctx );
assert( id != VLC_PREPARSER_REQ_ID_INVALID );
while ( ctx.b_done == false )
vlc_cond_wait( &ctx.cond, &ctx.lock );
@ -176,7 +177,7 @@ static void test_thumbnails( libvlc_instance_t* p_vlc )
input_item_Release( p_item );
free( psz_mrl );
vlc_thumbnailer_Delete( p_thumbnailer );
vlc_preparser_Delete( p_thumbnailer );
}
}
@ -193,8 +194,9 @@ static void thumbnailer_callback_cancel( input_item_t *item, int status,
static void test_cancel_thumbnail( libvlc_instance_t* p_vlc )
{
vlc_thumbnailer_t* p_thumbnailer = vlc_thumbnailer_Create(
VLC_OBJECT( p_vlc->p_libvlc_int ), VLC_TICK_INVALID );
vlc_preparser_t* p_thumbnailer = vlc_preparser_New(
VLC_OBJECT( p_vlc->p_libvlc_int ), 1, VLC_TICK_INVALID,
VLC_PREPARSER_TYPE_THUMBNAIL );
assert( p_thumbnailer != NULL );
const char* psz_mrl = "mock://video_track_count=0;audio_track_count=1;"
@ -210,16 +212,16 @@ static void test_cancel_thumbnail( libvlc_instance_t* p_vlc )
vlc_sem_t sem;
vlc_sem_init(&sem, 0);
vlc_thumbnailer_req_id id =
vlc_thumbnailer_Request( p_thumbnailer, p_item, NULL, &cbs, &sem );
vlc_preparser_req_id id =
vlc_preparser_GenerateThumbnail( p_thumbnailer, p_item, NULL, &cbs, &sem );
vlc_thumbnailer_Cancel( p_thumbnailer, id );
vlc_preparser_Cancel( p_thumbnailer, id );
vlc_sem_wait(&sem);
input_item_Release( p_item );
vlc_thumbnailer_Delete( p_thumbnailer );
vlc_preparser_Delete( p_thumbnailer );
}
int main( void )

Loading…
Cancel
Save