Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  thumbnail_to_files.c   Sprache: C

 

/*****************************************************************************
 * 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 <stdint.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.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(vlc_preparser_req *req, int status, void *userdata)
{
    input_item_t *item = vlc_preparser_req_GetItem(req);
    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);
    vlc_preparser_req_Release(req);
}

static void on_ended(vlc_preparser_req *thumbnailer_req, int status,
                     const bool *result_array, size_t result_count, void *data)
{
    (void) thumbnailer_req;
    struct context *context = data;
    assert(status == VLC_SUCCESS);
    assert(context->test_count == result_count);

    static const struct vlc_preparser_cbs 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 *parser_req =
                vlc_preparser_req_NewParse(context->preparser, thumb,
                                           VLC_PREPARSER_TYPE_PARSE,
                                           &parser_cbs, context);
            assert(parser_req != NULL);
            assert(vlc_preparser_Submit(context->preparser, parser_req)
                   == VLC_SUCCESS);
            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();
}

static int run_test(libvlc_instance_t *vlc, bool external)
{
    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")];

    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,
        .external_process = external,
    };
    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 *req =
        vlc_preparser_req_NewThumbnailToFiles(preparser, item, &arg,
                                              entries, test_count,
                                              &cbs, &context);
    assert(req != NULL);
    assert(vlc_preparser_Submit(preparser, req) == VLC_SUCCESS);

    /* 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);
    assert(count == 0); /* Should not be cancelled and already processed */
    vlc_preparser_req_Release(req);

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

skip:
    return 77;
}

struct alpha_context
{
    vlc_sem_t sem;
    bool success;
};

static void alpha_on_ended(vlc_preparser_req *req, int status,
                           const bool *result_array, size_t result_count,
                           void *data)
{
    struct alpha_context *ctx = data;
    (void) req;
    assert(status == VLC_SUCCESS);
    assert(result_count == 1);
    ctx->success = result_array[0];
    vlc_sem_post(&ctx->sem);
}

/* Regression test for:
 *  - test_align=false: "swscale: fix OOB read when scaling a cropped alpha plane"
 *  - test_align=true: "swscale: extract the alpha plane from the cropped origin"
 */

static int run_alpha_crop_test(libvlc_instance_t *vlc, bool test_align)
{
    /* Needs an RGBA image encoder (avcodec); skip otherwise. */
    if (vlc_preparser_CheckThumbnailerFormat(VLC_THUMBNAILER_FORMAT_RGBA) != 0)
    {
        fprintf(stderr, "skip: no RGBA thumbnailer format\n");
        return 0;
    }

    char path[] = "/tmp/libvlc_XXXXXX";
    int fd = vlc_mkstemp(path);
    if (fd == -1)
    {
        fprintf(stderr, "skip: vlc_mkstemp failed\n");
        return 0;
    }

    /* 256x128 (2:1) from a 640x480 (4:3) source forces a non-zero centered crop
     * (640x320, y_offset 80), which the colorbar alignment check below relies
     * on to keep the bottom opaque band. */

    const unsigned ow = 256, oh = 128;
    const struct vlc_thumbnailer_output output = {
        .format = VLC_THUMBNAILER_FORMAT_RGBA,
        .width = ow, .height = oh, .crop = true,
        .file_path = path, .creat_mode = 0666,
    };

    const struct vlc_preparser_cfg cfg = {
        .types = VLC_PREPARSER_TYPE_THUMBNAIL_TO_FILES,
        .max_parser_threads = 1,
        .max_thumbnailer_threads = 1,
        .timeout = 0,
        .external_process = false,
    };
    vlc_preparser_t *preparser = vlc_preparser_New(VLC_OBJECT(vlc->p_libvlc_int),
                                                   &cfg);
    assert(preparser != NULL);

    struct alpha_context ctx = { .success = false };
    vlc_sem_init(&ctx.sem, 0);

    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 = alpha_on_ended,
    };

    /* RGBA source so the conversion exercises swscale's alpha path. */
    input_item_t *item = input_item_New(
        test_align ? MOCK_URL ";video_chroma=RGBA;video_colorbar=true"
                   : MOCK_URL ";video_chroma=RGBA", "mock");
    assert(item != NULL);

    vlc_preparser_req *req =
        vlc_preparser_req_NewThumbnailToFiles(preparser, item, &arg, &output, 1,
                                              &cbs, &ctx);
    assert(req != NULL);
    assert(vlc_preparser_Submit(preparser, req) == VLC_SUCCESS);
    vlc_sem_wait(&ctx.sem);

    vlc_preparser_req_Release(req);
    input_item_Release(item);
    vlc_preparser_Delete(preparser);

    assert(ctx.success);


    const size_t size = (size_t)ow * oh * 4;
    uint8_t *buf = malloc(size);
    assert(buf != NULL);
    assert(lseek(fd, 0, SEEK_SET) == 0);
    for (size_t off = 0; off < size; )
    {
        ssize_t r = read(fd, buf + off, size - off);
        assert(r > 0);
        off += r;
    }
    if (test_align)
    {
        /* With the mock colorbar, a 640x480 RGBA frame has 4 horizontal bands
         * of 120 lines; only the bottom band (rows 360..479) is opaque (alpha
         * 0xFF), the rest is transparent. A 2:1 output crops to 640x320
         * centered (rows 80..399), so the cropped region includes the bottom
         * opaque band. If the alpha is (wrongly) extracted from (0,0), it
         * covers rows 0..319 which are fully transparent, and the output alpha
         * is 0 everywhere. */

        uint8_t max_alpha = 0;
        for (size_t i = 0; i < (size_t)ow * oh; i++)
            if (buf[i * 4 + 3] > max_alpha)
                max_alpha = buf[i * 4 + 3];
        /* Loose midpoint: the band is kept (alpha ~0xFF) or dropped (alpha 0).
         * The exact scaled value is implementation-dependent. */

        assert(max_alpha > 200
               && "swscale alpha-crop regression: alpha extracted from wrong origin");
    }
    else
    {
        /* Read back the raw RGBA. The mock fills every byte with the same
         * value, so R == G == B == A on input. The colour planes go through
         * the in-bounds main rescale, the alpha through the cropped alpha
         * path, so on a correct conversion the output alpha still equals the
         * red channel everywhere. The out-of-bounds bug leaves the alpha
         * garbage (it differs from red). */

        for (size_t i = 0; i < (size_t)ow * oh; i++)
        assert(buf[i * 4 + 3] == buf[i * 4 + 0]
               && "swscale alpha-crop regression: alpha does not match colour");
    }
    free(buf);

    close(fd);
    unlink(path);
    return 0;
}

int main(int argc, const char *argv[])
{
    test_init();
    argc--;
    argv++;

    libvlc_instance_t *vlc = libvlc_new(argc, argv);
    assert(vlc);

    int ret = 0;

    /* This test require swscale */
    if (!module_exists("swscale"))
    {
        fprintf(stderr, "skip: no \"swscale\" module\n");
        goto end;
    }

    fprintf(stderr, "Run with internal preparser...\n");
    ret = run_test(vlc, false);
    if (ret != 0) {
        goto end;
    }
    fprintf(stderr, "Run alpha crop test with internal preparser...\n");
    ret = run_alpha_crop_test(vlc, false);
    if (ret != 0) {
        goto end;
    }
    fprintf(stderr, "Run alpha align test with internal preparser...\n");
    ret = run_alpha_crop_test(vlc, true);
    if (ret != 0) {
        goto end;
    }
#if !defined(HAVE_VLC_PROCESS_SPAWN)
    fprintf(stderr, "Run with external preparser...\n");
    ret = run_test(vlc, true);
    if (ret != 0) {
        goto end;
    }
#endif

end:
    libvlc_release(vlc);
    return ret;
}

Messung
CHZG
849710093

¤ Dauer der Verarbeitung: 0.7 Sekunden  ¤

*© 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.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=1104845
#Domains=499261