[vlc-commits] [Git][videolan/vlc][master] 4 commits: preparser: fix/improve documentation
Steve Lhomme (@robUx4)
gitlab at videolan.org
Fri Nov 29 13:29:45 UTC 2024
Steve Lhomme pushed to branch master at VideoLAN / VLC
Commits:
87d0c201 by Thomas Guillem at 2024-11-29T13:14:48+00:00
preparser: fix/improve documentation
- - - - -
24a54a13 by Thomas Guillem at 2024-11-29T13:14:48+00:00
preparser: use PreparserRemoveTask()
- - - - -
8ce087f0 by Thomas Guillem at 2024-11-29T13:14:48+00:00
preparser: add GenerateThumbnailToFiles API
This will avoid to parse/decode the same video more than one times when
generating more than one thumbnail from one video (when generating
thumbnails with different sizes, like it is already done by the
medialibrary)
- - - - -
6f04965a by Thomas Guillem at 2024-11-29T13:14:48+00:00
test: preparser: test GenerateThumbnailToFiles()
Generate more than one thumbnail from a single video and parse them to
make sure it was well generated.
- - - - -
6 changed files:
- include/vlc_preparser.h
- src/libvlccore.sym
- src/preparser/preparser.c
- test/Makefile.am
- test/src/meson.build
- + test/src/preparser/thumbnail_to_files.c
Changes:
=====================================
include/vlc_preparser.h
=====================================
@@ -50,13 +50,18 @@ typedef size_t vlc_preparser_req_id;
#define VLC_PREPARSER_TYPE_FETCHMETA_LOCAL 0x02
#define VLC_PREPARSER_TYPE_FETCHMETA_NET 0x04
#define VLC_PREPARSER_TYPE_THUMBNAIL 0x08
+#define VLC_PREPARSER_TYPE_THUMBNAIL_TO_FILES 0x10
#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 */
+/**
+ * Preparser thumbnailer callbacks
+ *
+ * Used by vlc_preparser_GenerateThumbnail()
+ */
struct vlc_thumbnailer_cbs
{
/**
@@ -87,8 +92,45 @@ struct vlc_thumbnailer_cbs
void *data);
};
+/**
+ * Preparser thumbnailer to file callbacks
+ *
+ * Used by vlc_preparser_GenerateThumbnailToFiles()
+ */
+struct vlc_thumbnailer_to_files_cbs
+{
+ /**
+ * Event received on thumbnailing completion or error
+ *
+ * This callback will always be called, provided
+ *
+ * vlc_preparser_GenerateThumbnailToFiles() returned a valid request, and
+ * provided the request is not cancelled before its completion.
+ *
+ * @note This callback is mandatory if calling
+ * vlc_preparser_GenerateThumbnailToFiles()
+ *
+ * @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. A success mean that an
+ * image was generated but it is still possible that the export failed,
+ * check result_array to assure export were successful.
+ * @param array of results, if result_array[i] is true, the outputs[i] from
+ * vlc_preparser_GenerateThumbnailToFiles() succeeded.
+ * @param result_count size of the array, same than the output_count arg
+ * from vlc_preparser_GenerateThumbnailToFiles()
+ * @param data opaque pointer passed by
+ * vlc_preparser_GenerateThumbnailToFiles()
+ */
+ void (*on_ended)(input_item_t *item, int status,
+ const bool *result_array, size_t result_count, void *data);
+};
+
/**
* Thumbnailer argument
+ *
+ * Used by vlc_preparser_GenerateThumbnail() and
+ * vlc_preparser_GenerateThumbnailToFiles()
*/
struct vlc_thumbnailer_arg
{
@@ -97,7 +139,7 @@ struct vlc_thumbnailer_arg
{
enum
{
- /** Don't seek */
+ /** Don't seek (default) */
VLC_THUMBNAILER_SEEK_NONE,
/** Seek by time */
VLC_THUMBNAILER_SEEK_TIME,
@@ -120,10 +162,59 @@ struct vlc_thumbnailer_arg
} speed;
} seek;
- /** True to enable hardware decoder */
+ /** True to enable hardware decoder (false by default) */
bool hw_dec;
};
+/**
+ * Thumbnailer output format
+ */
+enum vlc_thumbnailer_format
+{
+ VLC_THUMBNAILER_FORMAT_PNG,
+ VLC_THUMBNAILER_FORMAT_WEBP,
+ VLC_THUMBNAILER_FORMAT_JPEG,
+};
+
+/**
+ * Thumbnailer output argument
+ *
+ * Used by vlc_preparser_GenerateThumbnailToFiles()
+ */
+struct vlc_thumbnailer_output
+{
+ /**
+ * Thumbnailer output format
+ */
+ enum vlc_thumbnailer_format format;
+
+ /**
+ * Requested width of the thumbnail
+ *
+ * cf. picture_Export() documentation.
+ */
+ int width;
+
+ /**
+ * Requested Height of the thumbnail
+ *
+ * cf. picture_Export() documentation.
+ */
+ int height;
+
+ /**
+ * True if the thumbnail should be cropped
+ *
+ * cf. picture_Export() documentation.
+ */
+ bool crop;
+
+ /** File output path of the thumbnail */
+ const char *file_path;
+ /** File mode bits (cf. "mode_t mode" in `man 2 open`) */
+ unsigned int creat_mode;
+};
+
/**
* Preparser creation configuration
*/
@@ -192,9 +283,7 @@ vlc_preparser_Push( vlc_preparser_t *preparser, input_item_t *item, int type_opt
*
* @param preparser the preparser object
* @param item a valid item to generate the thumbnail for
- * @param 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 arg pointer to the arg struct, NULL for default options
* @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
@@ -210,6 +299,54 @@ vlc_preparser_GenerateThumbnail( vlc_preparser_t *preparser, input_item_t *item,
const struct vlc_thumbnailer_cbs *cbs,
void *cbs_userdata );
+/**
+ * Get the best possible format
+ *
+ * @param[out] format pointer to the best format
+ * @param[out] out_ext pointer to the extension of the format
+ * @return 0 if a format was found, VLC_ENOENT otherwise (in case there are no
+ * "image encoder" modules)
+ */
+VLC_API int
+vlc_preparser_GetBestThumbnailerFormat(enum vlc_thumbnailer_format *format,
+ const char **out_ext);
+
+/**
+ * Check if the format is handled by VLC
+ *
+ * @param format format to check
+ * @return 0 if the format was found, VLC_ENOENT otherwise (in case there are
+ * no "image encoder" modules)
+ */
+VLC_API int
+vlc_preparser_CheckThumbnailerFormat(enum vlc_thumbnailer_format format);
+
+/**
+ * This function generates a thumbnail to one or several files
+ *
+ * @param preparser the preparser object
+ * @param item a valid item to generate the thumbnail for
+ * @param arg pointer to the arg struct, NULL for default options
+ * @param outputs array of outputs, one file will be generated per output for a
+ * single thumbnail
+ * @param output_count outputs array size, must be > 1
+ * @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_GenerateThumbnailToFiles( vlc_preparser_t *preparser, input_item_t *item,
+ const struct vlc_thumbnailer_arg *arg,
+ const struct vlc_thumbnailer_output *outputs,
+ size_t output_count,
+ const struct vlc_thumbnailer_to_files_cbs *cbs,
+ void *cbs_userdata );
+
/**
* This function cancel all preparsing requests for a given id
*
=====================================
src/libvlccore.sym
=====================================
@@ -1037,7 +1037,10 @@ vlc_input_attachment_New
vlc_input_attachment_Hold
vlc_preparser_New
vlc_preparser_Push
+vlc_preparser_CheckThumbnailerFormat
+vlc_preparser_GetBestThumbnailerFormat
vlc_preparser_GenerateThumbnail
+vlc_preparser_GenerateThumbnailToFiles
vlc_preparser_Cancel
vlc_preparser_Delete
vlc_preparser_SetTimeout
=====================================
src/preparser/preparser.c
=====================================
@@ -22,10 +22,15 @@
# include "config.h"
#endif
+#include <fcntl.h>
+
#include <vlc_common.h>
#include <vlc_atomic.h>
#include <vlc_executor.h>
#include <vlc_preparser.h>
+#include <vlc_interrupt.h>
+#include <vlc_modules.h>
+#include <vlc_fs.h>
#include "input/input_interface.h"
#include "input/input_internal.h"
@@ -35,6 +40,7 @@ union vlc_preparser_cbs
{
const input_item_parser_cbs_t *parser;
const struct vlc_thumbnailer_cbs *thumbnailer;
+ const struct vlc_thumbnailer_to_files_cbs *thumbnailer_to_files;
};
struct vlc_preparser_t
@@ -43,6 +49,7 @@ struct vlc_preparser_t
input_fetcher_t* fetcher;
vlc_executor_t *parser;
vlc_executor_t *thumbnailer;
+ vlc_executor_t *thumbnailer_to_files;
vlc_tick_t timeout;
vlc_mutex_t lock;
@@ -50,6 +57,16 @@ struct vlc_preparser_t
struct vlc_list submitted_tasks; /**< list of struct task */
};
+struct task_thumbnail_output
+{
+ vlc_fourcc_t fourcc;
+ int width;
+ int height;
+ bool crop;
+ char *file_path;
+ unsigned int creat_mode;
+};
+
struct task
{
vlc_preparser_t *preparser;
@@ -60,7 +77,10 @@ struct task
void *userdata;
vlc_preparser_req_id id;
+ vlc_interrupt_t *i11e_ctx;
picture_t *pic;
+ struct task_thumbnail_output *outputs;
+ size_t output_count;
vlc_sem_t preparse_ended;
int preparse_status;
@@ -86,6 +106,8 @@ TaskNew(vlc_preparser_t *preparser, void (*run)(void *), input_item_t *item,
task->cbs = cbs;
task->userdata = userdata;
task->pic = NULL;
+ task->outputs = NULL;
+ task->output_count = 0;
if (thumb_arg == NULL)
task->thumb_arg = (struct vlc_thumbnailer_arg) {
@@ -103,6 +125,10 @@ TaskNew(vlc_preparser_t *preparser, void (*run)(void *), input_item_t *item,
task->runnable.run = run;
task->runnable.userdata = task;
+ if (options & VLC_PREPARSER_TYPE_THUMBNAIL_TO_FILES)
+ task->i11e_ctx = vlc_interrupt_create();
+ else
+ task->i11e_ctx = NULL;
return task;
}
@@ -111,6 +137,11 @@ static void
TaskDelete(struct task *task)
{
input_item_Release(task->item);
+ for (size_t i = 0; i < task->output_count; ++i)
+ free(task->outputs[i].file_path);
+ free(task->outputs);
+ if (task->i11e_ctx != NULL)
+ vlc_interrupt_destroy(task->i11e_ctx);
free(task);
}
@@ -318,6 +349,103 @@ on_thumbnailer_input_event( input_thread_t *input,
vlc_sem_post(&task->preparse_ended);
}
+static int
+WriteToFile(const block_t *block, const char *path, unsigned mode)
+{
+ int bflags =
+#ifdef O_BINARY
+ O_BINARY;
+#else
+ 0;
+#endif
+
+ int fd = vlc_open(path, O_WRONLY|O_CREAT|O_TRUNC|O_NONBLOCK|bflags, mode);
+ if (fd == -1)
+ return -errno;
+
+ uint8_t *data = block->p_buffer;
+ size_t size = block->i_buffer;
+
+ while (size > 0)
+ {
+ ssize_t len = vlc_write_i11e(fd, data, size);
+ if (len == -1)
+ {
+ if (errno == EAGAIN)
+ continue;
+ break;
+ }
+
+ size -= len;
+ data += len;
+ }
+
+ close(fd);
+ return size == 0 ? 0 : -errno;
+}
+
+static void
+ThumbnailerToFilesRun(void *userdata)
+{
+ vlc_thread_set_name("vlc-run-thfil");
+
+ struct task *task = userdata;
+ vlc_preparser_t *preparser = task->preparser;
+ picture_t* pic = task->pic;
+
+ vlc_interrupt_set(task->i11e_ctx);
+
+ bool *result_array = vlc_alloc(task->output_count, sizeof(bool));
+ if (result_array == NULL)
+ goto error;
+
+ for (size_t i = 0; i < task->output_count; ++i)
+ {
+ struct task_thumbnail_output *output = &task->outputs[i];
+
+ if (output->fourcc == VLC_CODEC_UNKNOWN)
+ {
+ result_array[i] = false;
+ continue;
+ }
+
+ block_t* block;
+ int ret = picture_Export(preparser->owner, &block, NULL, pic,
+ output->fourcc, output->width, output->height,
+ output->crop);
+
+ if (ret != VLC_SUCCESS)
+ {
+ result_array[i] = false;
+ continue;
+ }
+
+ ret = WriteToFile(block, output->file_path, output->creat_mode);
+ block_Release(block);
+ if (ret == -EINTR)
+ {
+ task->preparse_status = -EINTR;
+ goto error;
+ }
+
+ result_array[i] = ret == 0;
+ }
+
+ task->preparse_status = VLC_SUCCESS;
+error:
+ PreparserRemoveTask(preparser, task);
+ if (task->preparse_status == VLC_SUCCESS)
+ task->cbs.thumbnailer_to_files->on_ended(task->item, task->preparse_status,
+ result_array, task->output_count,
+ task->userdata);
+ else
+ task->cbs.thumbnailer_to_files->on_ended(task->item, task->preparse_status,
+ NULL, 0, task->userdata);
+ picture_Release(pic);
+ TaskDelete(task);
+ free(result_array);
+}
+
static void
ThumbnailerRun(void *userdata)
{
@@ -384,15 +512,40 @@ ThumbnailerRun(void *userdata)
task->preparse_status = -EINTR;
picture_t* pic = task->pic;
- task->pic = NULL;
- vlc_mutex_lock(&preparser->lock);
- vlc_list_remove(&task->node);
- vlc_mutex_unlock(&preparser->lock);
+ if (task->options & VLC_PREPARSER_TYPE_THUMBNAIL)
+ {
+ assert((task->options & VLC_PREPARSER_TYPE_THUMBNAIL_TO_FILES) == 0);
+
+ PreparserRemoveTask(preparser, task);
+ task->cbs.thumbnailer->on_ended(task->item, task->preparse_status,
+ task->preparse_status == VLC_SUCCESS ?
+ pic : NULL, task->userdata);
+ }
+ else
+ {
+ assert(task->options & VLC_PREPARSER_TYPE_THUMBNAIL_TO_FILES);
+
+ if (task->preparse_status != VLC_SUCCESS)
+ {
+ PreparserRemoveTask(preparser, task);
+ task->cbs.thumbnailer_to_files->on_ended(task->item, task->preparse_status,
+ NULL, 0, task->userdata);
+ }
+ else
+ {
+ /* Export the thumbnail to several files via a new executor in
+ * order to not slow down the current thread doing picture
+ * conversion and I/O */
- task->cbs.thumbnailer->on_ended(task->item, task->preparse_status,
- task->preparse_status == VLC_SUCCESS ?
- pic : NULL, task->userdata);
+ assert(pic != NULL);
+
+ task->runnable.run = ThumbnailerToFilesRun;
+ vlc_executor_Submit(preparser->thumbnailer_to_files, &task->runnable);
+ pic = NULL;
+ task = NULL;
+ }
+ }
if (pic)
picture_Release(pic);
@@ -401,13 +554,16 @@ ThumbnailerRun(void *userdata)
input_Close(input);
error:
- TaskDelete(task);
+ if (task != NULL)
+ TaskDelete(task);
}
static void
Interrupt(struct task *task)
{
atomic_store(&task->interrupted, true);
+ if (task->i11e_ctx != NULL)
+ vlc_interrupt_kill(task->i11e_ctx);
vlc_sem_post(&task->preparse_ended);
}
@@ -454,7 +610,8 @@ vlc_preparser_t* vlc_preparser_New( vlc_object_t *parent,
else
preparser->fetcher = NULL;
- if (request_type & VLC_PREPARSER_TYPE_THUMBNAIL)
+ if (request_type & (VLC_PREPARSER_TYPE_THUMBNAIL |
+ VLC_PREPARSER_TYPE_THUMBNAIL_TO_FILES))
{
preparser->thumbnailer = vlc_executor_New(thumbnailer_threads);
if (!preparser->thumbnailer)
@@ -463,12 +620,24 @@ vlc_preparser_t* vlc_preparser_New( vlc_object_t *parent,
else
preparser->thumbnailer = NULL;
+ if (request_type & VLC_PREPARSER_TYPE_THUMBNAIL_TO_FILES)
+ {
+ preparser->thumbnailer_to_files = vlc_executor_New(1);
+ if (preparser->thumbnailer_to_files == NULL)
+ goto error_thumbnail_to_files;
+ }
+ else
+ preparser->thumbnailer_to_files = NULL;
+
vlc_mutex_init(&preparser->lock);
vlc_list_init(&preparser->submitted_tasks);
preparser->current_id = 1;
return preparser;
+error_thumbnail_to_files:
+ if (preparser->thumbnailer != NULL)
+ vlc_executor_Delete(preparser->thumbnailer);
error_thumbnail:
if (preparser->fetcher != NULL)
input_fetcher_Delete(preparser->fetcher);
@@ -486,6 +655,7 @@ vlc_preparser_req_id vlc_preparser_Push( vlc_preparser_t *preparser, input_item_
void *cbs_userdata )
{
assert((type_options & VLC_PREPARSER_TYPE_THUMBNAIL) == 0);
+ assert((type_options & VLC_PREPARSER_TYPE_THUMBNAIL_TO_FILES) == 0);
assert(type_options & VLC_PREPARSER_TYPE_PARSE
|| type_options & VLC_PREPARSER_TYPE_FETCHMETA_ALL);
@@ -551,6 +721,140 @@ vlc_preparser_GenerateThumbnail( vlc_preparser_t *preparser, input_item_t *item,
return id;
}
+static int
+CheckThumbnailerFormat(enum vlc_thumbnailer_format format,
+ enum vlc_thumbnailer_format *out_format,
+ const char **out_ext, vlc_fourcc_t *out_fourcc)
+{
+ static const struct format
+ {
+ enum vlc_thumbnailer_format format;
+ const char *module;
+ vlc_fourcc_t fourcc;
+ const char *ext;
+ } formats[] = {
+ {
+ .format = VLC_THUMBNAILER_FORMAT_PNG, .fourcc = VLC_CODEC_PNG,
+ .module = "png", .ext = "png",
+ },
+ {
+ .format = VLC_THUMBNAILER_FORMAT_WEBP, .fourcc = VLC_CODEC_WEBP,
+ .module = "vpx", .ext = "webp",
+ },
+ {
+ .format = VLC_THUMBNAILER_FORMAT_JPEG, .fourcc = VLC_CODEC_JPEG,
+ .module = "jpeg", .ext = "jpg",
+ },
+ };
+
+ for (size_t i = 0; i < ARRAY_SIZE(formats); ++i)
+ {
+ /* if out_format is valid, don't check format, and get the first
+ * possible format */
+ if (out_format == NULL && format != formats[i].format)
+ continue;
+
+ /* Check if the image encoder is present */
+ if (!module_exists(formats[i].module))
+ continue;
+
+ if (out_format != NULL)
+ *out_format = formats[i].format;
+ if (out_fourcc != NULL)
+ *out_fourcc = formats[i].fourcc;
+ if (out_ext != NULL)
+ *out_ext = formats[i].ext;
+ return 0;
+ }
+
+ return VLC_ENOENT;
+}
+
+int
+vlc_preparser_GetBestThumbnailerFormat(enum vlc_thumbnailer_format *format,
+ const char **out_ext)
+{
+ return CheckThumbnailerFormat(0, format, out_ext, NULL);
+}
+
+int
+vlc_preparser_CheckThumbnailerFormat(enum vlc_thumbnailer_format format)
+{
+ return CheckThumbnailerFormat(format, NULL, NULL, NULL);
+}
+
+vlc_preparser_req_id
+vlc_preparser_GenerateThumbnailToFiles( vlc_preparser_t *preparser, input_item_t *item,
+ const struct vlc_thumbnailer_arg *thumb_arg,
+ const struct vlc_thumbnailer_output *outputs,
+ size_t output_count,
+ const struct vlc_thumbnailer_to_files_cbs *cbs,
+ void *cbs_userdata )
+{
+ assert(preparser->thumbnailer != NULL);
+ assert(cbs != NULL && cbs->on_ended != NULL);
+ assert(outputs != NULL && output_count > 0);
+
+ union vlc_preparser_cbs task_cbs = {
+ .thumbnailer_to_files = cbs,
+ };
+
+ struct task *task =
+ TaskNew(preparser, ThumbnailerRun, item,
+ VLC_PREPARSER_TYPE_THUMBNAIL_TO_FILES, thumb_arg,
+ task_cbs, cbs_userdata);
+ if (task == NULL)
+ return VLC_PREPARSER_REQ_ID_INVALID;
+
+ task->outputs = vlc_alloc(output_count, sizeof(*outputs));
+ if (unlikely(task->outputs == NULL))
+ {
+ TaskDelete(task);
+ return VLC_PREPARSER_REQ_ID_INVALID;
+ }
+
+ size_t valid_output_count = 0;
+ for (size_t i = 0; i < output_count; ++i)
+ {
+ struct task_thumbnail_output *dst = &task->outputs[i];
+ const struct vlc_thumbnailer_output *src = &outputs[i];
+ assert(src->file_path != NULL);
+
+ enum vlc_thumbnailer_format format = src->format;
+ int ret = CheckThumbnailerFormat(format, NULL, NULL, &dst->fourcc);
+ if (ret != 0)
+ dst->fourcc = VLC_CODEC_UNKNOWN;
+ else
+ valid_output_count++;
+
+ dst->width = src->width;
+ dst->height = src->height;
+ dst->crop = src->crop;
+ dst->creat_mode = src->creat_mode;
+ dst->file_path = strdup(src->file_path);
+
+ if (unlikely(dst->file_path == NULL))
+ {
+ TaskDelete(task);
+ return VLC_PREPARSER_REQ_ID_INVALID;
+ }
+ task->output_count++;
+ }
+
+ if (valid_output_count == 0)
+ {
+ TaskDelete(task);
+ msg_Err(preparser->owner, "thumbnailer: no valid \"image encoder\" found");
+ 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);
@@ -570,11 +874,19 @@ size_t vlc_preparser_Cancel( vlc_preparser_t *preparser, vlc_preparser_req_id id
canceled = vlc_executor_Cancel(preparser->parser,
&task->runnable);
}
- else if (task->options & VLC_PREPARSER_TYPE_THUMBNAIL)
+ else if (task->options & (VLC_PREPARSER_TYPE_THUMBNAIL |
+ VLC_PREPARSER_TYPE_THUMBNAIL_TO_FILES))
{
assert(preparser->thumbnailer != NULL);
canceled = vlc_executor_Cancel(preparser->thumbnailer,
&task->runnable);
+ if (!canceled &&
+ task->options & VLC_PREPARSER_TYPE_THUMBNAIL_TO_FILES)
+ {
+ assert(preparser->thumbnailer_to_files != NULL);
+ canceled = vlc_executor_Cancel(preparser->thumbnailer_to_files,
+ &task->runnable);
+ }
}
else /* TODO: the fetcher should be cancellable too */
canceled = false;
@@ -591,13 +903,21 @@ size_t vlc_preparser_Cancel( vlc_preparser_t *preparser, vlc_preparser_req_id id
task->cbs.parser->on_ended(task->item, task->preparse_status,
task->userdata);
}
- else
+ else if (task->options & VLC_PREPARSER_TYPE_THUMBNAIL)
{
- assert(task->options & VLC_PREPARSER_TYPE_THUMBNAIL);
+ assert((task->options & VLC_PREPARSER_TYPE_THUMBNAIL_TO_FILES) == 0);
task->cbs.thumbnailer->on_ended(task->item,
task->preparse_status, NULL,
task->userdata);
}
+ else
+ {
+ assert(task->options & VLC_PREPARSER_TYPE_THUMBNAIL_TO_FILES);
+ task->cbs.thumbnailer_to_files->on_ended(task->item,
+ task->preparse_status,
+ NULL, 0,
+ task->userdata);
+ }
TaskDelete(task);
/* Small optimisation in the likely case where the user cancel
@@ -637,5 +957,8 @@ void vlc_preparser_Delete( vlc_preparser_t *preparser )
if (preparser->thumbnailer != NULL)
vlc_executor_Delete(preparser->thumbnailer);
+ if (preparser->thumbnailer_to_files != NULL)
+ vlc_executor_Delete(preparser->thumbnailer_to_files);
+
free( preparser );
}
=====================================
test/Makefile.am
=====================================
@@ -30,6 +30,7 @@ check_PROGRAMS = \
test_src_input_stream \
test_src_input_stream_fifo \
test_src_preparser_thumbnail \
+ test_src_preparser_thumbnail_to_files \
test_src_input_decoder \
test_src_player \
test_src_player_monotonic_clock \
@@ -180,6 +181,8 @@ test_src_input_stream_fifo_SOURCES = src/input/stream_fifo.c
test_src_input_stream_fifo_LDADD = $(LIBVLCCORE) $(LIBVLC)
test_src_preparser_thumbnail_SOURCES = src/preparser/thumbnail.c
test_src_preparser_thumbnail_LDADD = $(LIBVLCCORE) $(LIBVLC)
+test_src_preparser_thumbnail_to_files_SOURCES = src/preparser/thumbnail_to_files.c
+test_src_preparser_thumbnail_to_files_LDADD = $(LIBVLCCORE) $(LIBVLC)
test_src_player_SOURCES = src/player/player.c
test_src_player_LDADD = $(LIBVLCCORE) $(LIBVLC) $(LIBM)
test_src_player_monotonic_clock_SOURCES = src/player/player.c
=====================================
test/src/meson.build
=====================================
@@ -80,6 +80,14 @@ vlc_tests += {
'module_depends' : ['demux_mock', 'rawvideo']
}
+vlc_tests += {
+ 'name' : 'test_src_preparser_thumbnail_to_files',
+ 'sources' : files('preparser/thumbnail_to_files.c'),
+ 'suite' : ['src', 'test_src'],
+ 'link_with' : [libvlc, libvlccore],
+ 'module_depends' : ['demux_mock', 'rawvideo']
+}
+
vlc_tests += {
'name' : 'test_src_player',
'sources' : files('player/player.c'),
=====================================
test/src/preparser/thumbnail_to_files.c
=====================================
@@ -0,0 +1,362 @@
+/*****************************************************************************
+ * thumbnail_to_files.c: test thumbnailing to files API
+ *****************************************************************************
+ * Copyright (C) 2024 VLC authors and VideoLAN
+ *
+ * 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.
+ *****************************************************************************/
+
+#include "../../libvlc/test.h"
+#include "../lib/libvlc_internal.h"
+
+#include <vlc_common.h>
+#include <vlc_preparser.h>
+#include <vlc_input_item.h>
+#include <vlc_picture.h>
+#include <vlc_modules.h>
+#include <vlc_fs.h>
+
+#include <errno.h>
+
+#define MOCK_WIDTH 640
+#define MOCK_HEIGHT 480
+
+#define STRINGIFY_(x) #x
+#define STRINGIFY(x) STRINGIFY_(x)
+#define MOCK_URL "mock://video_track_count=1;" \
+ "video_width="STRINGIFY(MOCK_WIDTH)";" \
+ "video_height="STRINGIFY(MOCK_HEIGHT)
+
+struct test_entry
+{
+ int in_width;
+ int in_height;
+ bool in_crop;
+
+ bool should_succeed;
+ unsigned out_width;
+ unsigned out_height;
+};
+
+static const struct test_entry test_entries[] =
+{
+ {
+ .in_width = 0, .in_height = 0, .in_crop = false,
+ .should_succeed = true,
+ .out_width = MOCK_WIDTH, .out_height = MOCK_HEIGHT
+ },
+
+ {
+ .in_width = 0, .in_height = 0, .in_crop = false,
+ .should_succeed = false,
+ },
+ {
+ .in_width = 320, .in_height = 0, .in_crop = false,
+ .should_succeed = true,
+ .out_width = 320, .out_height = 240,
+ },
+};
+
+struct context
+{
+ size_t test_count;
+ const struct vlc_thumbnailer_output *entries;
+ const struct test_entry *test_entries;
+ vlc_preparser_t *preparser;
+
+ vlc_fourcc_t parsed_fourcc;
+ const char *forced_demux;
+
+ vlc_sem_t sem;
+};
+
+static void parser_on_ended(input_item_t *item, int status, void *userdata)
+{
+ struct context *context = userdata;
+ assert(status == VLC_SUCCESS);
+
+ /* Retrieve the test_entry from the uri */
+ const struct test_entry *test_entry = NULL;
+ for (size_t i = 0; i < context->test_count; ++i)
+ {
+ if (strcmp(item->psz_uri + strlen("file://"),
+ context->entries[i].file_path) == 0)
+ {
+ test_entry = &context->test_entries[i];
+ break;
+ }
+ }
+ assert(test_entry != NULL);
+
+ assert(item->es_vec.size == 1);
+ const struct input_item_es *item_es = &item->es_vec.data[0];
+ const es_format_t *es_fmt = &item_es->es;
+
+ /* Check if the video format of the thumbnail is expected */
+ assert(es_fmt->i_cat == VIDEO_ES);
+ assert(es_fmt->i_codec == context->parsed_fourcc);
+ assert(es_fmt->video.i_width == test_entry->out_width);
+ assert(es_fmt->video.i_height == test_entry->out_height);
+
+ vlc_sem_post(&context->sem);
+}
+
+static void on_ended(input_item_t *item, int status,
+ const bool *result_array, size_t result_count, void *data)
+{
+ struct context *context = data;
+ (void) item;
+ assert(status == VLC_SUCCESS);
+ assert(context->test_count == result_count);
+
+ static const struct input_item_parser_cbs_t parser_cbs = {
+ .on_ended = parser_on_ended,
+ };
+
+ for (size_t i = 0; i < result_count; ++i)
+ {
+ const struct vlc_thumbnailer_output *entry = &context->entries[i];
+ const struct test_entry *test_entry = &context->test_entries[i];
+
+ assert(test_entry->should_succeed == result_array[i]);
+
+ if (!result_array[i])
+ vlc_sem_post(&context->sem);
+ else
+ {
+ /* Parse the thumbnail to check its validity */
+ char *uri;
+ int ret = asprintf(&uri, "file://%s", entry->file_path);
+ assert(ret > 0);
+ input_item_t *thumb = input_item_New(uri, "thumb");
+ free(uri);
+ assert(thumb != NULL);
+
+ if (context->forced_demux != NULL)
+ {
+ /* Setup the forced demux */
+ char *option;
+ ret = asprintf(&option, ":demux=%s", context->forced_demux);
+ assert(ret > 0);
+ input_item_AddOption(thumb, option, VLC_INPUT_OPTION_TRUSTED);
+ free(option);
+ }
+
+ vlc_preparser_req_id req_id =
+ vlc_preparser_Push(context->preparser, thumb,
+ VLC_PREPARSER_TYPE_PARSE,
+ &parser_cbs, context);
+ assert(req_id != VLC_PREPARSER_REQ_ID_INVALID);
+ input_item_Release(thumb);
+ }
+ }
+}
+
+static int get_formats(enum vlc_thumbnailer_format *out_format,
+ vlc_fourcc_t *parsed_fourcc, const char **forced_demux,
+ const char **ext)
+{
+ /* This test require any of these images encoders */
+ static const struct format
+ {
+ enum vlc_thumbnailer_format format;
+ vlc_fourcc_t parsed_fourcc;
+ const char *ext;
+ const char *forced_demux;
+ } formats[] = {
+ {
+ .format = VLC_THUMBNAILER_FORMAT_PNG,
+ .parsed_fourcc = VLC_CODEC_RGB24,
+ .ext = "png"
+ },
+ {
+ .format = VLC_THUMBNAILER_FORMAT_WEBP,
+ .parsed_fourcc = VLC_CODEC_I420,
+ .ext = "webp"
+ },
+ {
+ .format = VLC_THUMBNAILER_FORMAT_JPEG,
+ .parsed_fourcc = VLC_CODEC_MJPG,
+ .ext = "jpg",
+ /* XXX: The mjpeg demux won't parse width/height */
+ .forced_demux = "avformat"
+ },
+ };
+
+ /* Retrieve the best format */
+ enum vlc_thumbnailer_format format;
+ int ret = vlc_preparser_GetBestThumbnailerFormat(&format, ext);
+ if (ret != 0)
+ return ret;
+
+ /* Check that the best format is checked and succeed */
+ ret = vlc_preparser_CheckThumbnailerFormat(format);
+ assert(ret == 0);
+
+ for (size_t i = 0; i < ARRAY_SIZE(formats); ++i)
+ {
+ if (formats[i].format != format)
+ continue;
+
+ /* Check if the forced demux module (for preparsing) is present */
+ if (formats[i].forced_demux != NULL
+ && !module_exists(formats[i].forced_demux))
+ return VLC_ENOENT;
+
+ assert(strcmp(*ext, formats[i].ext) == 0);
+
+ *out_format = format;
+ *parsed_fourcc = formats[i].parsed_fourcc;
+ *forced_demux = formats[i].forced_demux;
+ return 0;
+ }
+
+ vlc_assert_unreachable();
+}
+
+int main(int argc, const char *argv[])
+{
+ test_init();
+ argc--;
+ argv++;
+
+ size_t test_count = ARRAY_SIZE(test_entries);
+ struct vlc_thumbnailer_output entries[test_count];
+ int fd_array[test_count];
+ char path_array[test_count][sizeof("/tmp/libvlc_XXXXXX")];
+
+ libvlc_instance_t *vlc = libvlc_new(argc, argv);
+ assert(vlc);
+
+ /* This test require swscale */
+ if (!module_exists("swscale"))
+ {
+ fprintf(stderr, "skip: no \"swscale\" module\n");
+ goto skip;
+ }
+
+ enum vlc_thumbnailer_format format;
+ const char *forced_demux, *ext;
+ vlc_fourcc_t parsed_fourcc;
+ int ret = get_formats(&format, &parsed_fourcc, &forced_demux, &ext);
+ if (ret == VLC_ENOENT)
+ {
+ fprintf(stderr, "skip: no \"image encoder\" modules\n");
+ goto skip;
+ }
+ assert(ret == 0);
+
+ fprintf(stderr, "thumbnail_to_files: using format: %s\n", ext);
+
+ /* Fill output entries */
+ for (size_t i = 0; i < test_count; ++i)
+ {
+ const struct test_entry *test_entry = &test_entries[i];
+ struct vlc_thumbnailer_output *entry = &entries[i];
+
+ if (test_entry->should_succeed)
+ {
+ /* Fill the output entry with a valid tmp path */
+ strcpy(path_array[i], "/tmp/libvlc_XXXXXX");
+ fd_array[i] = vlc_mkstemp(path_array[i]);
+ if (i == 0 && fd_array[i] == -1)
+ {
+ fprintf(stderr, "skip: vlc_mkstemp failed\n");
+ goto skip;
+ }
+ assert(fd_array[i] != -1);
+ entry->file_path = path_array[i];
+ }
+ else
+ {
+ fd_array[i] = -1;
+ entry->file_path = "/this/path/does/not/exist/and/should/fail";
+ }
+ entry->format = format;
+ entry->width = test_entry->in_width;
+ entry->height = test_entry->in_height;
+ entry->crop = test_entry->in_crop;
+ entry->creat_mode = 0666;
+ }
+
+ /* Preparser configuration: thumbnailer + parser */
+ const struct vlc_preparser_cfg cfg = {
+ .types = VLC_PREPARSER_TYPE_THUMBNAIL_TO_FILES|VLC_PREPARSER_TYPE_PARSE,
+ .max_parser_threads = 1,
+ .max_thumbnailer_threads = 1,
+ .timeout = 0,
+ };
+ vlc_preparser_t *preparser = vlc_preparser_New(VLC_OBJECT(vlc->p_libvlc_int),
+ &cfg);
+ assert(preparser != NULL);
+
+ struct context context = {
+ .test_count = test_count,
+ .entries = entries,
+ .test_entries = test_entries,
+ .preparser = preparser,
+ .parsed_fourcc = parsed_fourcc,
+ .forced_demux = forced_demux,
+ };
+ vlc_sem_init(&context.sem, 0);
+
+ /* Common arguments */
+ const struct vlc_thumbnailer_arg arg =
+ {
+ .seek = {
+ .type = VLC_THUMBNAILER_SEEK_POS,
+ .pos = 0.2,
+ .speed = VLC_THUMBNAILER_SEEK_FAST,
+ },
+ .hw_dec = false,
+ };
+ static const struct vlc_thumbnailer_to_files_cbs cbs = {
+ .on_ended = on_ended,
+ };
+
+ input_item_t *item = input_item_New(MOCK_URL, "mock");
+ assert(item != NULL);
+
+ vlc_preparser_req_id req_id =
+ vlc_preparser_GenerateThumbnailToFiles(preparser, item, &arg,
+ entries, test_count,
+ &cbs, &context);
+
+ assert(req_id != VLC_PREPARSER_REQ_ID_INVALID);
+
+ /* Wait for all tests */
+ for (size_t i = 0; i < test_count; ++i)
+ vlc_sem_wait(&context.sem);
+
+ size_t count = vlc_preparser_Cancel(preparser, req_id);
+ assert(count == 0); /* Should not be cancelled and already processed */
+
+ for (size_t i = 0; i < test_count; ++i)
+ if (fd_array[i] != -1)
+ {
+ unlink(path_array[i]);
+ close(fd_array[i]);
+ }
+
+ input_item_Release(item);
+
+ vlc_preparser_Delete(preparser);
+ libvlc_release(vlc);
+ return 0;
+
+skip:
+ libvlc_release(vlc);
+ return 77;
+}
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/45ec033f434169e854dc3ece05fbbcf3c9009156...6f04965a23b8b0abe9e4ca6c9365cd4a5657fc7a
--
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/45ec033f434169e854dc3ece05fbbcf3c9009156...6f04965a23b8b0abe9e4ca6c9365cd4a5657fc7a
You're receiving this email because of your account on code.videolan.org.
VideoLAN code repository instance
More information about the vlc-commits
mailing list