Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/VLC/src/preparser/   (VLC Media Plyer ©)  Datei vom 23.9.2026 mit Größe 32 kB image not shown  

Quelle  internal.c   Sprache: C

 

/*****************************************************************************
 * internal.c
 *****************************************************************************
 * Copyright © 2017-2025 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.
 *****************************************************************************/


#ifdef HAVE_CONFIG_H
# 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_fs.h>

#include "preparser.h"
#include "input/input_interface.h"
#include "input/input_internal.h"
#include "fetcher.h"

union vlc_preparser_cbs_internal
{
    const struct vlc_preparser_cbs *parser;
    const struct vlc_thumbnailer_cbs *thumbnailer;
    const struct vlc_thumbnailer_to_files_cbs *thumbnailer_to_files;
};

struct preparser_sys
{
    vlc_object_t* owner;
    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;
    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 vlc_preparser_req_owner
{
    struct preparser_sys *preparser;
    input_item_t *item;
    int options;
    struct vlc_thumbnailer_arg thumb_arg;
    union vlc_preparser_cbs_internal cbs;
    void *userdata;

    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;
    atomic_bool interrupted;

    struct vlc_runnable runnable; /**< to be passed to the executor */
    vlc_executor_t *executor; /**< executor the runnable was last submitted to */

    struct vlc_list node; /**< node of vlc_preparser_t.submitted_tasks */

    vlc_atomic_rc_t rc;

    struct vlc_preparser_req req;
};

static struct vlc_preparser_req_owner *
preparser_req_get_owner(struct vlc_preparser_req *req)
{
    return container_of(req, struct vlc_preparser_req_owner, req);
}

static input_item_t *
preparser_req_GetItem(struct vlc_preparser_req *req)
{
    assert(req != NULL);
    struct vlc_preparser_req_owner *req_owner = preparser_req_get_owner(req);
    return req_owner->item;
}

static void
PreparserRequestDelete(struct vlc_preparser_req *req)
{
    struct vlc_preparser_req_owner *req_owner = preparser_req_get_owner(req);
    input_item_Release(req_owner->item);
    for (size_t i = 0; i < req_owner->output_count; ++i)
        free(req_owner->outputs[i].file_path);
    free(req_owner->outputs);
    if (req_owner->pic != NULL)
        picture_Release(req_owner->pic);
    if (req_owner->i11e_ctx != NULL)
        vlc_interrupt_destroy(req_owner->i11e_ctx);
    free(req_owner);
}

static void
preparser_req_Release(struct vlc_preparser_req *req)
{
    assert(req != NULL);
    struct vlc_preparser_req_owner *req_owner = preparser_req_get_owner(req);
    if (!vlc_atomic_rc_dec(&req_owner->rc))
        return;

    PreparserRequestDelete(req);
}

static struct vlc_preparser_req *
PreparserRequestNew(struct preparser_sys *preparser, void (*run)(void *), input_item_t *item,
                    int options, const struct vlc_thumbnailer_arg *thumb_arg,
                    union vlc_preparser_cbs_internal cbs, void *userdata)
{
    struct vlc_preparser_req_owner *req_owner = malloc(sizeof(*req_owner));
    if (!req_owner)
        return NULL;

    req_owner->preparser = preparser;
    req_owner->item = item;
    req_owner->options = options;
    req_owner->cbs = cbs;
    req_owner->userdata = userdata;
    req_owner->pic = NULL;
    req_owner->outputs = NULL;
    req_owner->output_count = 0;
    vlc_atomic_rc_init(&req_owner->rc);

    static const struct vlc_preparser_req_operations ops = {
        .get_item = preparser_req_GetItem,
        .release = preparser_req_Release,
    };
    req_owner->req.ops = &ops;
    req_owner->req.submitted = false;

    if (thumb_arg == NULL)
        req_owner->thumb_arg = (struct vlc_thumbnailer_arg) {
            .seek.type = VLC_THUMBNAILER_SEEK_NONE,
            .hw_dec = false,
        };
    else
        req_owner->thumb_arg = *thumb_arg;

    input_item_Hold(item);

    vlc_sem_init(&req_owner->preparse_ended, 0);
    req_owner->preparse_status = VLC_EGENERIC;
    atomic_init(&req_owner->interrupted, false);

    req_owner->runnable.run = run;
    req_owner->runnable.userdata = &req_owner->req;
    req_owner->executor = NULL;
    if (options & VLC_PREPARSER_TYPE_THUMBNAIL_TO_FILES)
        req_owner->i11e_ctx = vlc_interrupt_create();
    else
        req_owner->i11e_ctx = NULL;

    return &req_owner->req;
}

static void
PreparserQueueTaskLocked(struct vlc_preparser_req_owner *req_owner,
                         vlc_executor_t *executor)
{
    req_owner->executor = executor;
    vlc_executor_Submit(executor, &req_owner->runnable);
}

static void
PreparserSubmitTask(struct preparser_sys *preparser,
                    struct vlc_preparser_req *req,
                    vlc_executor_t *executor)
{
    struct vlc_preparser_req_owner *req_owner = preparser_req_get_owner(req);
    vlc_mutex_lock(&preparser->lock);
    vlc_list_append(&req_owner->node, &preparser->submitted_tasks);
    PreparserQueueTaskLocked(req_owner, executor);
    vlc_mutex_unlock(&preparser->lock);
}

static void
PreparserRemoveTask(struct preparser_sys *preparser, struct vlc_preparser_req *req)
{
    vlc_mutex_lock(&preparser->lock);
    struct vlc_preparser_req_owner *req_owner = preparser_req_get_owner(req);
    vlc_list_remove(&req_owner->node);
    vlc_mutex_unlock(&preparser->lock);
}

static void
NotifyPreparseEnded(struct vlc_preparser_req *req)
{
    struct vlc_preparser_req_owner *req_owner = preparser_req_get_owner(req);
    if (atomic_load(&req_owner->interrupted))
        req_owner->preparse_status = -EINTR;
    else if (req_owner->preparse_status == VLC_SUCCESS)
        input_item_SetPreparsed(req_owner->item);

    req_owner->cbs.parser->on_ended(req, req_owner->preparse_status,
                                    req_owner->userdata);
}

static void
OnParserEnded(input_item_t *item, int status, void *req_)
{
    VLC_UNUSED(item);
    struct vlc_preparser_req *req = req_;
    struct vlc_preparser_req_owner *req_owner = preparser_req_get_owner(req);

    req_owner->preparse_status = status;
    vlc_sem_post(&req_owner->preparse_ended);
}

static void
OnParserSubtreeAdded(input_item_t *item, input_item_node_t *subtree,
                     void *req_)
{
    VLC_UNUSED(item);
    struct vlc_preparser_req *req = req_;
    struct vlc_preparser_req_owner *req_owner = preparser_req_get_owner(req);

    if (atomic_load(&req_owner->interrupted))
        return;

    if (req_owner->cbs.parser->on_subtree_added)
        req_owner->cbs.parser->on_subtree_added(req, subtree,
                                                req_owner->userdata);
}

static void
OnParserAttachmentsAdded(input_item_t *item,
                         input_attachment_t *const *array,
                         size_t count, void *req_)
{
    VLC_UNUSED(item);
    struct vlc_preparser_req *req = req_;
    struct vlc_preparser_req_owner *req_owner = preparser_req_get_owner(req);

    if (atomic_load(&req_owner->interrupted))
        return;

    if (req_owner->cbs.parser->on_attachments_added)
        req_owner->cbs.parser->on_attachments_added(req, array, count,
                                                    req_owner->userdata);
}

static void
OnArtFetchEnded(input_item_t *item, bool fetched, void *userdata)
{
    VLC_UNUSED(item);
    VLC_UNUSED(fetched);

    struct vlc_preparser_req *req = userdata;

    NotifyPreparseEnded(req);
    vlc_preparser_req_Release(req);
}

static const input_fetcher_callbacks_t input_fetcher_callbacks = {
    .on_art_fetch_ended = OnArtFetchEnded,
};

static void
Parse(struct vlc_preparser_req *req, vlc_tick_t deadline)
{
    struct vlc_preparser_req_owner *req_owner = preparser_req_get_owner(req);

    static const input_item_parser_cbs_t cbs = {
        .on_ended = OnParserEnded,
        .on_subtree_added = OnParserSubtreeAdded,
        .on_attachments_added = OnParserAttachmentsAdded,
    };

    vlc_object_t *obj = req_owner->preparser->owner;
    const struct input_item_parser_cfg cfg = {
        .cbs = &cbs,
        .cbs_data = req,
        .subitems = req_owner->options & VLC_PREPARSER_OPTION_SUBITEMS,
        .interact = req_owner->options & VLC_PREPARSER_OPTION_INTERACT,
    };
    input_item_parser_id_t *parser =
        input_item_Parse(obj, req_owner->item, &cfg);
    if (parser == NULL)
    {
        req_owner->preparse_status = VLC_EGENERIC;
        return;
    }

    /* Wait until the end of parsing */
    if (deadline == VLC_TICK_INVALID)
        vlc_sem_wait(&req_owner->preparse_ended);
    else
        if (vlc_sem_timedwait(&req_owner->preparse_ended, deadline))
        {
            input_item_parser_id_Release(parser);
            req_owner->preparse_status = VLC_ETIMEOUT;
            return;
        }

    /* This call also interrupts the parsing if it is still running */
    input_item_parser_id_Release(parser);
}

static int
Fetch(struct vlc_preparser_req *req)
{
    struct vlc_preparser_req_owner *req_owner = preparser_req_get_owner(req);

    input_fetcher_t *fetcher = req_owner->preparser->fetcher;
    if (!fetcher || !(req_owner->options & VLC_PREPARSER_TYPE_FETCHMETA_ALL))
        return VLC_ENOENT;

    return input_fetcher_Push(fetcher, req_owner->item,
                              req_owner->options & VLC_PREPARSER_TYPE_FETCHMETA_ALL,
                              &input_fetcher_callbacks, req);
}

static void
ParserRun(void *userdata)
{
    vlc_thread_set_name("vlc-run-prepars");

    struct vlc_preparser_req *req = userdata;
    struct vlc_preparser_req_owner *req_owner = preparser_req_get_owner(req);
    struct preparser_sys *preparser = req_owner->preparser;

    vlc_tick_t deadline = preparser->timeout ? vlc_tick_now() + preparser->timeout
                                             : VLC_TICK_INVALID;

    if (req_owner->options & VLC_PREPARSER_TYPE_PARSE)
    {
        if (atomic_load(&req_owner->interrupted))
        {
            PreparserRemoveTask(preparser, req);
            goto end;
        }

        Parse(req, deadline);
    }

    PreparserRemoveTask(preparser, req);

    if (req_owner->preparse_status == VLC_ETIMEOUT || atomic_load(&req_owner->interrupted))
        goto end;

    int ret = Fetch(req);

    if (ret == VLC_SUCCESS)
        return; /* Remove the task and notify from the fetcher callback */

end:
    NotifyPreparseEnded(req);
    vlc_preparser_req_Release(req);
}

static bool
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.value != ERROR_S &&
                                                 event->state.value != END_S ) ) )
         return false;

    struct vlc_preparser_req *req = userdata;
    struct vlc_preparser_req_owner *req_owner = preparser_req_get_owner(req);

    if (event->type == INPUT_EVENT_THUMBNAIL_READY)
    {
        req_owner->pic = picture_Hold(event->thumbnail);
        req_owner->preparse_status = VLC_SUCCESS;
    }
    vlc_sem_post(&req_owner->preparse_ended);
    return true;
}

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 vlc_preparser_req *req = userdata;
    struct vlc_preparser_req_owner *req_owner = preparser_req_get_owner(req);
    struct preparser_sys *preparser = req_owner->preparser;
    picture_t* pic = req_owner->pic;

    vlc_interrupt_set(req_owner->i11e_ctx);

    bool *result_array = vlc_alloc(req_owner->output_count, sizeof(bool));
    if (result_array == NULL)
        goto error;

    for (size_t i = 0; i < req_owner->output_count; ++i)
    {
        struct task_thumbnail_output *output = &req_owner->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)
        {
            req_owner->preparse_status = -EINTR;
            goto error;
        }

        result_array[i] = ret == 0;
    }

    req_owner->preparse_status = VLC_SUCCESS;
error:
    PreparserRemoveTask(preparser, req);
    if (req_owner->preparse_status == VLC_SUCCESS)
        req_owner->cbs.thumbnailer_to_files->on_ended(req, req_owner->preparse_status,
                                                      result_array,
                                                      req_owner->output_count,
                                                      req_owner->userdata);
    else
        req_owner->cbs.thumbnailer_to_files->on_ended(req, req_owner->preparse_status,
                                                      NULL, 0, req_owner->userdata);
    req_owner->pic = NULL;
    picture_Release(pic);
    vlc_preparser_req_Release(req);
    free(result_array);
}

static void
ThumbnailerRun(void *userdata)
{
    vlc_thread_set_name("vlc-run-thumb");

    struct vlc_preparser_req *req = userdata;
    struct vlc_preparser_req_owner *req_owner = preparser_req_get_owner(req);
    struct preparser_sys *preparser = req_owner->preparser;

    static const struct vlc_input_thread_callbacks cbs = {
        .on_event = on_thumbnailer_input_event,
    };

    const struct vlc_input_thread_cfg cfg = {
        .type = INPUT_TYPE_THUMBNAILING,
        .hw_dec = req_owner->thumb_arg.hw_dec ? INPUT_CFG_HW_DEC_ENABLED
                                        : INPUT_CFG_HW_DEC_DISABLED,
        .cbs = &cbs,
        .cbs_data = req,
    };

    vlc_tick_t deadline = preparser->timeout != VLC_TICK_INVALID ?
                          vlc_tick_now() + preparser->timeout :
                          VLC_TICK_INVALID;

    input_thread_t* input =
            input_Create( preparser->owner, req_owner->item, &cfg );
    if (!input)
        goto error;

    assert(req_owner->thumb_arg.seek.speed == VLC_THUMBNAILER_SEEK_PRECISE
        || req_owner->thumb_arg.seek.speed == VLC_THUMBNAILER_SEEK_FAST);
    bool fast_seek = req_owner->thumb_arg.seek.speed == VLC_THUMBNAILER_SEEK_FAST;

    switch (req_owner->thumb_arg.seek.type)
    {
        case VLC_THUMBNAILER_SEEK_NONE:
            break;
        case VLC_THUMBNAILER_SEEK_TIME:
            if (req_owner->thumb_arg.seek.time >= VLC_TICK_0)
                input_SetTime(input, req_owner->thumb_arg.seek.time, fast_seek);
            break;
        case VLC_THUMBNAILER_SEEK_POS:
            if (req_owner->thumb_arg.seek.pos > 0)
            {
                float pos = req_owner->thumb_arg.seek.pos;
                if (pos > 1)
                    pos = 1;
                input_SetPosition(input, pos, fast_seek);
            }
            break;
        default:
            vlc_assert_unreachable();
    }

    int ret = input_Start(input);
    if (ret != VLC_SUCCESS)
    {
        input_Close(input);
        goto error;
    }

    bool timeout = false;
    if (deadline == VLC_TICK_INVALID)
        vlc_sem_wait(&req_owner->preparse_ended);
    else
        timeout = vlc_sem_timedwait(&req_owner->preparse_ended, deadline) != 0;

    /* tear the input thread down before reading what it produced to avoid races */
    input_Stop(input);
    input_Close(input);

    if (timeout)
        req_owner->preparse_status = VLC_ETIMEOUT;
    if (atomic_load(&req_owner->interrupted))
        req_owner->preparse_status = -EINTR;

    picture_t* pic = req_owner->pic;

    if (req_owner->options & VLC_PREPARSER_TYPE_THUMBNAIL)
    {
        assert((req_owner->options & VLC_PREPARSER_TYPE_THUMBNAIL_TO_FILES) == 0);

        PreparserRemoveTask(preparser, req);
        req_owner->cbs.thumbnailer->on_ended(req, req_owner->preparse_status,
                                             req_owner->preparse_status == VLC_SUCCESS ?
                                             pic : NULL, req_owner->userdata);
    }
    else
    {
        assert(req_owner->options & VLC_PREPARSER_TYPE_THUMBNAIL_TO_FILES);

        bool queued = false;

        if (req_owner->preparse_status == VLC_SUCCESS)
        {
            /* 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 */


            assert(pic != NULL);

            /* the request stays published while its runnable moves to
               another executor. Re-queue it under the lock so that a
               concurrent cancel never targets the executor it just left. */

            vlc_mutex_lock(&preparser->lock);
            if (atomic_load(&req_owner->interrupted))
                req_owner->preparse_status = -EINTR;
            else
            {
                req_owner->runnable.run = ThumbnailerToFilesRun;
                PreparserQueueTaskLocked(req_owner, preparser->thumbnailer_to_files);
                queued = true;
            }
            vlc_mutex_unlock(&preparser->lock);
        }

        if (queued)
        {
            pic = NULL;
            req_owner = NULL;
        }
        else
        {
            PreparserRemoveTask(preparser, req);
            req_owner->cbs.thumbnailer_to_files->on_ended(req, req_owner->preparse_status,
                                                          NULL, 0, req_owner->userdata);
        }
    }

    if (pic)
    {
        req_owner->pic = NULL;
        picture_Release(pic);
    }

error:
    if (req_owner != NULL)
        vlc_preparser_req_Release(req);
}

static void
Interrupt(struct vlc_preparser_req *req)
{
    struct vlc_preparser_req_owner *req_owner = preparser_req_get_owner(req);
    atomic_store(&req_owner->interrupted, true);
    if (req_owner->i11e_ctx != NULL)
        vlc_interrupt_kill(req_owner->i11e_ctx);

    vlc_sem_post(&req_owner->preparse_ended);
}

static struct vlc_preparser_req *
PreparserRequestRetain(struct vlc_preparser_req *req)
{
    struct vlc_preparser_req_owner *req_owner = preparser_req_get_owner(req);
    vlc_atomic_rc_inc(&req_owner->rc);
    return req;
}

static vlc_preparser_req *
preparser_req_NewParse( void *opaque, input_item_t *item,
                        int type_options,
                        const struct vlc_preparser_cbs *cbs,
                        void *cbs_userdata )
{
    assert(opaque != NULL);
    struct preparser_sys *preparser = opaque;

    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);

    assert(!(type_options & VLC_PREPARSER_TYPE_PARSE)
        || preparser->parser != NULL);
    assert(!(type_options & VLC_PREPARSER_TYPE_FETCHMETA_ALL)
        || preparser->fetcher != NULL);

    assert(cbs != NULL && cbs->on_ended != NULL);

    union vlc_preparser_cbs_internal req_cbs = {
        .parser = cbs,
    };

    return PreparserRequestNew(preparser, ParserRun, item, type_options,
                               NULL, req_cbs, cbs_userdata);
}

static vlc_preparser_req *
preparser_req_NewThumbnail( void *opaque, input_item_t *item,
                            const struct vlc_thumbnailer_arg *thumb_arg,
                            const struct vlc_thumbnailer_cbs *cbs,
                            void *cbs_userdata )
{
    assert(opaque != NULL);
    struct preparser_sys *preparser = opaque;

    assert(preparser->thumbnailer != NULL);
    assert(cbs != NULL && cbs->on_ended != NULL);

    union vlc_preparser_cbs_internal req_cbs = {
        .thumbnailer = cbs,
    };

    return PreparserRequestNew(preparser, ThumbnailerRun, item, VLC_PREPARSER_TYPE_THUMBNAIL,
                               thumb_arg, req_cbs, cbs_userdata);
}

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",
        },
        {
            .format = VLC_THUMBNAILER_FORMAT_RGBA, .fourcc = VLC_CODEC_RGBA,
            .module = "avcodec", .ext = "rgba",
        },
        {
            .format = VLC_THUMBNAILER_FORMAT_ARGB, .fourcc = VLC_CODEC_ARGB,
            .module = "avcodec", .ext = "argb",
        },
    };

    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);
}

static vlc_preparser_req *
preparser_req_NewThumbnailToFiles( void *opaque, 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(opaque != NULL);
    struct preparser_sys *preparser = opaque;

    assert(preparser->thumbnailer != NULL);
    assert(preparser->thumbnailer_to_files != NULL);
    assert(cbs != NULL && cbs->on_ended != NULL);
    assert(outputs != NULL && output_count > 0);

    union vlc_preparser_cbs_internal req_cbs = {
        .thumbnailer_to_files = cbs,
    };

    struct vlc_preparser_req *req =
        PreparserRequestNew(preparser, ThumbnailerRun, item,
                            VLC_PREPARSER_TYPE_THUMBNAIL_TO_FILES, thumb_arg,
                            req_cbs, cbs_userdata);
    if (req == NULL)
        return NULL;

    struct vlc_preparser_req_owner *req_owner = preparser_req_get_owner(req);

    req_owner->outputs = vlc_alloc(output_count, sizeof(*outputs));
    if (unlikely(req_owner->outputs == NULL))
    {
        PreparserRequestDelete(req);
        return NULL;
    }

    size_t valid_output_count = 0;
    for (size_t i = 0; i < output_count; ++i)
    {
        struct task_thumbnail_output *dst = &req_owner->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))
        {
            PreparserRequestDelete(req);
            return NULL;
        }
        req_owner->output_count++;
    }

    if (valid_output_count == 0)
    {
        PreparserRequestDelete(req);
        msg_Err(preparser->owner, "thumbnailer: no valid \"image encoder\" found");
        return NULL;
    }

    return req;
}

static int
preparser_Submit( void *opaque, struct vlc_preparser_req *req )
{
    assert(opaque != NULL);
    struct preparser_sys *preparser = opaque;

    struct vlc_preparser_req_owner *req_owner = preparser_req_get_owner(req);
    assert(req_owner->preparser == preparser);

    /* the task's reference */
    PreparserRequestRetain(req);

    if (req_owner->options & (VLC_PREPARSER_TYPE_THUMBNAIL |
                              VLC_PREPARSER_TYPE_THUMBNAIL_TO_FILES))
    {
        assert(preparser->thumbnailer != NULL);
        PreparserSubmitTask(preparser, req, preparser->thumbnailer);
        return VLC_SUCCESS;
    }

    if (preparser->parser != NULL)
    {
        PreparserSubmitTask(preparser, req, preparser->parser);
        return VLC_SUCCESS;
    }

    if (Fetch(req) != VLC_SUCCESS)
    {
        vlc_preparser_req_Release(req); /* task ref only; caller keeps theirs */
        return VLC_EGENERIC;
    }

    return VLC_SUCCESS;
}

static size_t preparser_Cancel( void *opaque, vlc_preparser_req *req )
{
    assert(opaque != NULL);
    struct preparser_sys *preparser = opaque;

    vlc_mutex_lock(&preparser->lock);

    struct vlc_preparser_req_owner *req_itr;
    size_t count = 0;
    vlc_list_foreach(req_itr, &preparser->submitted_tasks, node)
    {
        if (req == NULL || req_itr == preparser_req_get_owner(req))
        {
            count++;

            /* TODO: the fetcher should be cancellable too */
            bool canceled = req_itr->executor != NULL &&
                            vlc_executor_Cancel(req_itr->executor,
                                                &req_itr->runnable);

            if (canceled)
            {
                vlc_list_remove(&req_itr->node);
                vlc_mutex_unlock(&preparser->lock);
                req_itr->preparse_status = -EINTR;
                if (req_itr->options & (VLC_PREPARSER_TYPE_PARSE |
                                        VLC_PREPARSER_TYPE_FETCHMETA_ALL))
                {
                    assert((req_itr->options & VLC_PREPARSER_TYPE_THUMBNAIL) == 0);
                    req_itr->cbs.parser->on_ended(&req_itr->req, req_itr->preparse_status,
                                                  req_itr->userdata);
                }
                else if (req_itr->options & VLC_PREPARSER_TYPE_THUMBNAIL)
                {
                    assert((req_itr->options & VLC_PREPARSER_TYPE_THUMBNAIL_TO_FILES) == 0);
                    req_itr->cbs.thumbnailer->on_ended(&req_itr->req,
                                                       req_itr->preparse_status, NULL,
                                                       req_itr->userdata);
                }
                else
                {
                    assert(req_itr->options & VLC_PREPARSER_TYPE_THUMBNAIL_TO_FILES);
                    req_itr->cbs.thumbnailer_to_files->on_ended(&req_itr->req,
                                                                req_itr->preparse_status,
                                                                NULL, 0,
                                                                req_itr->userdata);
                }
                vlc_preparser_req_Release(&req_itr->req);

                /* Small optimisation in the likely case where the user cancel
                 * only one task */

                if (req != NULL)
                    return count;
                vlc_mutex_lock(&preparser->lock);
            }
            else
                /* The task will be finished and destroyed after run() */
                Interrupt(&req_itr->req);
        }
    }

    vlc_mutex_unlock(&preparser->lock);

    return count;
}

static void preparser_Delete(void *opaque)
{
    assert(opaque != NULL);
    struct preparser_sys *preparser = opaque;

    /* In case vlc_preparser_Deactivate() has not been called */
    preparser_Cancel(preparser, NULL);

    if (preparser->parser != NULL)
        vlc_executor_Delete(preparser->parser);

    if( preparser->fetcher )
        input_fetcher_Delete( preparser->fetcher );

    if (preparser->thumbnailer != NULL)
        vlc_executor_Delete(preparser->thumbnailer);

    if (preparser->thumbnailer_to_files != NULL)
        vlc_executor_Delete(preparser->thumbnailer_to_files);

    free( preparser );
}

void*
vlc_preparser_internal_New(vlc_preparser_t *owner, vlc_object_t *parent,
                           const struct vlc_preparser_cfg *cfg)
{
    assert(cfg->timeout >= 0);

    int request_type = cfg->types;
    assert(request_type & (VLC_PREPARSER_TYPE_FETCHMETA_ALL|
                           VLC_PREPARSER_TYPE_PARSE|
                           VLC_PREPARSER_TYPE_THUMBNAIL|
                           VLC_PREPARSER_TYPE_THUMBNAIL_TO_FILES));

    unsigned parser_threads = cfg->max_parser_threads == 0 ? 1 :
                              cfg->max_parser_threads;
    unsigned thumbnailer_threads = cfg->max_thumbnailer_threads == 0 ? 1 :
                                   cfg->max_thumbnailer_threads;

    struct preparser_sys* preparser = malloc( sizeof *preparser );
    if (!preparser)
        return NULL;

    preparser->timeout = cfg->timeout;
    preparser->owner = parent;

    if (request_type & VLC_PREPARSER_TYPE_PARSE)
    {
        preparser->parser = vlc_executor_New(parser_threads);
        if (!preparser->parser)
            goto error_parser;
    }
    else
        preparser->parser = NULL;

    if (request_type & VLC_PREPARSER_TYPE_FETCHMETA_ALL)
    {
        preparser->fetcher = input_fetcher_New(parent, request_type);
        if (unlikely(preparser->fetcher == NULL))
            goto error_fetcher;
    }
    else
        preparser->fetcher = NULL;

    if (request_type & (VLC_PREPARSER_TYPE_THUMBNAIL |
                        VLC_PREPARSER_TYPE_THUMBNAIL_TO_FILES))
    {
        preparser->thumbnailer = vlc_executor_New(thumbnailer_threads);
        if (!preparser->thumbnailer)
            goto error_thumbnail;
    }
    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);

    static const struct vlc_preparser_operations ops = {
        .req_new_parse = preparser_req_NewParse,
        .req_new_thumbnail = preparser_req_NewThumbnail,
        .req_new_thumbnail_to_files = preparser_req_NewThumbnailToFiles,
        .submit = preparser_Submit,
        .cancel = preparser_Cancel,
        .delete = preparser_Delete,
    };
    assert(owner != NULL);
    owner->ops = &ops;

    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);
error_fetcher:
    if (preparser->parser != NULL)
        vlc_executor_Delete(preparser->parser);
error_parser:
    free(preparser);
    return NULL;
}

Messung
CHZG
849610093

¤ Dauer der Verarbeitung: 0.7 Sekunden  (vorverarbeitet am  2026-11-04) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover


Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.