Eine aufbereitete Darstellung der Quelle

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

Benutzer

Quelle  display.c   Sprache: C

 

/**
 * @file display.c
 * @brief libplacebo video output module
 */

/*****************************************************************************
 * Copyright © 2021 Niklas Haas
 *
 * 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 <stdlib.h>
#include <assert.h>

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#include <vlc_common.h>
#include <vlc_plugin.h>
#include <vlc_vout_display.h>
#include <vlc_fs.h>
#include <vlc_subpicture.h>

#include "utils.h"
#include "instance.h"

#include <libplacebo/renderer.h>
#include <libplacebo/utils/upload.h>
#include <libplacebo/swapchain.h>
#include <libplacebo/shaders/lut.h>

typedef struct vout_display_sys_t
{
    vlc_placebo_t *pl;
    pl_renderer renderer;
    pl_tex plane_tex[4];
    pl_tex fbo;

    // Pool of textures for the subpictures
    struct pl_overlay *overlays;
    struct pl_overlay_part *overlay_parts;
    pl_tex *overlay_tex;
    int num_overlays;

    // Storage for rendering parameters
    struct pl_filter_config upscaler;
    struct pl_filter_config downscaler;
    struct pl_deband_params deband;
    struct pl_sigmoid_params sigmoid;
    struct pl_color_map_params color_map;
    struct pl_dither_params dither;
    struct pl_render_params params;
    struct pl_color_space target;
    uint64_t target_icc_signature;
    struct pl_peak_detect_params peak_detect;
    enum pl_chroma_location yuv_chroma_loc;
    int dither_depth;

    struct pl_custom_lut *lut;
    char *lut_path;
    int lut_mode;

    const struct pl_hook *hook;
    char *hook_path;

    struct pl_dovi_metadata dovi_metadata;
} vout_display_sys_t;

// Display callbacks
static void PictureRender(vout_display_t *, picture_t *, const vlc_render_subpicture *, vlc_tick_t);
static void PictureDisplay(vout_display_t *, picture_t *);
static int SetDisplaySize(vout_display_t *, unsigned width, unsigned height);
static void Close(vout_display_t *);
static void UpdateParams(vout_display_t *);
static void UpdateColorspaceHint(vout_display_t *, const video_format_t *);
static void UpdateIccProfile(vout_display_t *, const vlc_icc_profile_t *);

static const struct vlc_display_operations ops = {
    .close = Close,
    .prepare = PictureRender,
    .display = PictureDisplay,
    .set_display_size = SetDisplaySize,
    .set_icc_profile = UpdateIccProfile,
};

static int Open(vout_display_t *vd,
                video_format_t *fmt, vlc_video_context *context)
{
    vout_display_sys_t *sys =
        vlc_obj_calloc(VLC_OBJECT(vd), 1, sizeof (*sys));
    if (unlikely(sys == NULL))
        return VLC_ENOMEM;

    char *name = var_InheritString(vd, "pl-gpu");
    sys->pl = vlc_placebo_Create(vd->cfg, name);
    free(name);
    if (sys->pl == NULL)
        return VLC_EGENERIC;

    /* From now on, the error label will reset this to NULL. */
    vd->sys = sys;

    if (vlc_placebo_MakeCurrent(sys->pl) != VLC_SUCCESS)
        goto error;

    // Set colorspace hint *before* first swapchain resize
    UpdateColorspaceHint(vd, fmt);

    // Set initial framebuffer size
    int width = (int) vd->cfg->display.width;
    int height = (int) vd->cfg->display.height;
    if (!pl_swapchain_resize(sys->pl->swapchain, &width, &height))
        goto error;

    pl_gpu gpu = sys->pl->gpu;
    sys->renderer = pl_renderer_create(sys->pl->log, gpu);
    if (!sys->renderer)
        goto error;

    vlc_placebo_ReleaseCurrent(sys->pl);

    // Attempt using the input format as the display format
    if (vlc_placebo_FormatSupported(gpu, vd->source->i_chroma)) {
        fmt->i_chroma = vd->source->i_chroma;
    } else {
        fmt->i_chroma = 0;
        vlc_fourcc_t *list = vlc_fourcc_GetFallback(vd->source->i_chroma);
        if (list != NULL)
        {
            for (const vlc_fourcc_t *fcc = list; *fcc; fcc++) {
                if (vlc_placebo_FormatSupported(gpu, *fcc)) {
                    fmt->i_chroma = *fcc;
                    break;
                }
            }
            free(list);
        }

        if (fmt->i_chroma == 0) {
            fmt->i_chroma = VLC_CODEC_RGBA;
            msg_Warn(vd, "Failed picking any suitable input format, falling "
                     "back to RGBA for sanity!");
        }
    }
    sys->yuv_chroma_loc = vlc_fourcc_IsYUV(fmt->i_chroma) ?
                          vlc_placebo_ChromaLoc(fmt) : PL_CHROMA_UNKNOWN;

    // Hard-coded list of supported subtitle chromas (non-planar only!)
    static const vlc_fourcc_t subfmts[] = {
        VLC_CODEC_RGBA,
        VLC_CODEC_BGRA,
        VLC_CODEC_RGB555LE,
        VLC_CODEC_RGB565LE,
        VLC_CODEC_RGB24,
        VLC_CODEC_XRGB,
        VLC_CODEC_RGB233,
        VLC_CODEC_GREY,
        0
    };

    vd->info.subpicture_chromas = subfmts;
    vd->ops = &ops;

    UpdateParams(vd);
    (void) context;
    return VLC_SUCCESS;

error:
    pl_renderer_destroy(&sys->renderer);
    vlc_placebo_Release(sys->pl);
    vd->sys = NULL;
    return VLC_EGENERIC;
}

static void Close(vout_display_t *vd)
{
    vout_display_sys_t *sys = vd->sys;
    pl_gpu gpu = sys->pl->gpu;

    if (vlc_placebo_MakeCurrent(sys->pl) == VLC_SUCCESS) {
        for (int i = 0; i < 4; i++)
            pl_tex_destroy(gpu, &sys->plane_tex[i]);
        for (int i = 0; i < sys->num_overlays; i++)
            pl_tex_destroy(gpu, &sys->overlay_tex[i]);
        pl_renderer_destroy(&sys->renderer);
        vlc_placebo_ReleaseCurrent(sys->pl);
    }

    if (sys->overlays) {
        free(sys->overlays);
        free(sys->overlay_parts);
        free(sys->overlay_tex);
    }

    pl_lut_free(&sys->lut);
    free(sys->lut_path);

    pl_mpv_user_shader_destroy(&sys->hook);
    free(sys->hook_path);

    vlc_placebo_Release(sys->pl);
}

static void PictureRender(vout_display_t *vd, picture_t *pic,
                          const vlc_render_subpicture *subpicture,
                          vlc_tick_t date)
{
    VLC_UNUSED(date);
    vout_display_sys_t *sys = vd->sys;
    pl_gpu gpu = sys->pl->gpu;
    bool failed = false;

    if (vlc_placebo_MakeCurrent(sys->pl) != VLC_SUCCESS)
        return;

    struct pl_swapchain_frame frame;
    if (!pl_swapchain_start_frame(sys->pl->swapchain, &frame)) {
        vlc_placebo_ReleaseCurrent(sys->pl);
        return; // Probably benign error, ignore it
    }
    sys->fbo = frame.fbo;

#if PL_API_VER >= 199
    bool need_vflip = false;
#else
    bool need_vflip = frame.flipped;
#endif

    struct pl_frame img = {
        .num_planes = pic->i_planes,
        .color      = vlc_placebo_ColorSpace(vd->fmt),
        .repr       = vlc_placebo_ColorRepr(vd->fmt),
        .crop = {
            .x0 = pic->format.i_x_offset,
            .y0 = pic->format.i_y_offset,
            .x1 = pic->format.i_x_offset + pic->format.i_visible_width,
            .y1 = pic->format.i_y_offset + pic->format.i_visible_height,
        },
    };

    vlc_placebo_frame_DoviMetadata(&img, pic, &sys->dovi_metadata);

    struct vlc_ancillary *iccp = picture_GetAncillary(pic, VLC_ANCILLARY_ID_ICC);
    if (iccp) {
        vlc_icc_profile_t *icc = vlc_ancillary_GetData(iccp);
        img.profile.data = icc->data;
        img.profile.len = icc->size;
        pl_icc_profile_compute_signature(&img.profile);
    }

    struct vlc_ancillary *hdrplus = picture_GetAncillary(pic, VLC_ANCILLARY_ID_HDR10PLUS);
    if (hdrplus) {
        vlc_video_hdr_dynamic_metadata_t *hdm = vlc_ancillary_GetData(hdrplus);
        vlc_placebo_HdrMetadata(hdm, &img.color.hdr);
    }

    // Upload the image data for each plane
    struct pl_plane_data data[4];
    if (!vlc_placebo_PlaneData(pic, data)) {
        // This should never happen, in theory
        assert(!"Failed processing the picture_t into pl_plane_data!?");
    }

    for (int i = 0; i < pic->i_planes; i++) {
        struct pl_plane *plane = &img.planes[i];
        if (!pl_upload_plane(gpu, plane, &sys->plane_tex[i], &data[i])) {
            msg_Err(vd, "Failed uploading image data!");
            failed = true;
            goto done;
        }

        // Matches only the chroma planes, never luma or alpha
        if (sys->yuv_chroma_loc != PL_CHROMA_UNKNOWN && i != 0 && i != 3)
            pl_chroma_location_offset(sys->yuv_chroma_loc, &plane->shift_x,
                                      &plane->shift_y);
    }

    struct pl_frame target;
    pl_frame_from_swapchain(&target, &frame);
    if (vd->cfg->icc_profile) {
        target.profile.data = vd->cfg->icc_profile->data;
        target.profile.len = vd->cfg->icc_profile->size;
        target.profile.signature = sys->target_icc_signature;
        if (!target.profile.signature) {
            pl_icc_profile_compute_signature(&target.profile);
            sys->target_icc_signature = target.profile.signature;
        }
    }

    // Set the target crop dynamically based on the swapchain flip state
    vout_display_place_t place = *vd->place;
    if (need_vflip)
    {
        place.y = place.height + place.y;
        place.height = -place.height;
    }

#define SWAP(a, b) { float _tmp = (a); (a) = (b); (b) = _tmp; }
    switch (vd->fmt->orientation) {
    case ORIENT_HFLIPPED:
        SWAP(img.crop.x0, img.crop.x1);
        break;
    case ORIENT_VFLIPPED:
        SWAP(img.crop.y0, img.crop.y1);
        break;
    case ORIENT_ROTATED_90:
        img.rotation = PL_ROTATION_90;
        break;
    case ORIENT_ROTATED_180:
        img.rotation = PL_ROTATION_180;
        break;
    case ORIENT_ROTATED_270:
        img.rotation = PL_ROTATION_270;
        break;
    case ORIENT_TRANSPOSED:
        img.rotation = PL_ROTATION_90;
        SWAP(img.crop.y0, img.crop.y1);
        break;
    case ORIENT_ANTI_TRANSPOSED:
        img.rotation = PL_ROTATION_90;
        SWAP(img.crop.x0, img.crop.x1);
    default:
        break;
    }

    target.crop = (struct pl_rect2df) {
        place.x, place.y, place.x + place.width, place.y + place.height,
    };

    // Override the target colorimetry only if the user requests it
    if (sys->target.primaries)
        target.color.primaries = sys->target.primaries;
    if (sys->target.transfer)
        target.color.transfer = sys->target.transfer;
    if (sys->dither_depth > 0) {
        // override the sample depth without affecting the color encoding
        struct pl_bit_encoding *bits = &target.repr.bits;
        float scale = bits->color_depth / bits->sample_depth;
        bits->sample_depth = sys->dither_depth;
        bits->color_depth = scale * sys->dither_depth;
    }

    if (subpicture) {
        int num_regions = subpicture->regions.size;
        const struct subpicture_region_rendered *r;

        // Grow the overlays array if needed
        if (num_regions > sys->num_overlays) {
            sys->overlays = realloc(sys->overlays, num_regions * sizeof(struct pl_overlay));
            sys->overlay_parts = realloc(sys->overlay_parts, num_regions * sizeof(struct pl_overlay_part));
            sys->overlay_tex = realloc(sys->overlay_tex, num_regions * sizeof(pl_tex));
            if (!sys->overlays || !sys->overlay_parts || !sys->overlay_tex) {
                // Unlikely OOM, just do whatever
                sys->num_overlays = 0;
                failed = true;
                goto done;
            }
            // Clear the newly added texture pointers for pl_upload_plane
            for (int i = sys->num_overlays; i < num_regions; i++)
                sys->overlay_tex[i] = NULL;
            sys->num_overlays = num_regions;
        }

        // Upload all of the regions
        int i = 0;
        vlc_vector_foreach(r, &subpicture->regions) {
            assert(r->p_picture->i_planes == 1);
            struct pl_plane_data subdata[4];
            if (!vlc_placebo_PlaneData(r->p_picture, subdata))
                assert(!"Failed processing the subpicture_t into pl_plane_data!?");

            if (!pl_upload_plane(gpu, NULL, &sys->overlay_tex[i], subdata)) {
                msg_Err(vd, "Failed uploading subpicture region!");
                num_regions = i; // stop here
                break;
            }

            int ysign = need_vflip ? (-1) : 1;
            sys->overlays[i] = (struct pl_overlay) {
                .tex   = sys->overlay_tex[i],
                .mode  = PL_OVERLAY_NORMAL,
                .color = vlc_placebo_ColorSpace(&r->p_picture->format),
                .repr  = vlc_placebo_ColorRepr(&r->p_picture->format),
                .parts = &sys->overlay_parts[i],
                .num_parts = 1,
            };

            sys->overlay_parts[i] = (struct pl_overlay_part) {
                .src = {
                    .x1 = r->p_picture->format.i_visible_width,
                    .y1 = r->p_picture->format.i_visible_height,
                },
                .dst = {
                    .x0 = r->place.x,
                    .y0 = r->place.y * ysign,
                    .x1 = (r->place.x + r->place.width ),
                    .y1 = (r->place.y + r->place.height) * ysign,
                },
            };
            i++;
        }

        // Update the target information to reference the subpictures
        target.overlays = sys->overlays;
        target.num_overlays = num_regions;
    }

    // If we don't cover the entire output, clear it first
    // struct pl_rect2d full = {0, 0, frame.fbo->params.w, frame.fbo->params.h };
    // struct pl_rect2d norm = {place.x, place.y, place.x + place.width, place.y + place.height };
    // pl_rect2d_normalize(&norm);
    // if (!pl_rect2d_eq(norm, full)) {
        // TODO: make background color configurable?
        pl_tex_clear(gpu, frame.fbo, (float[4]){ 0.0, 0.0, 0.0, 0.0 });
    // }

    switch (sys->lut_mode) {
    case LUT_DECODING:
        img.lut_type = PL_LUT_CONVERSION;
        img.lut = sys->lut;
        break;
    case LUT_ENCODING:
        target.lut_type = PL_LUT_CONVERSION;
        target.lut = sys->lut;
        break;
    }

    // Dispatch the actual image rendering with the pre-configured parameters
    if (!pl_render_image(sys->renderer, &img, &target, &sys->params)) {
        msg_Err(vd, "Failed rendering frame!");
        failed = true;
        goto done;
    }

done:

    if (failed)
        pl_tex_clear(gpu, frame.fbo, (float[4]){ 1.0, 0.0, 0.0, 1.0 });

    pl_gpu_flush(gpu);

    vlc_placebo_ReleaseCurrent(sys->pl);
}

static void PictureDisplay(vout_display_t *vd, picture_t *pic)
{
    VLC_UNUSED(pic);
    vout_display_sys_t *sys = vd->sys;
    if (!sys->fbo)
        return;

    sys->fbo = NULL;
    if (vlc_placebo_MakeCurrent(sys->pl) == VLC_SUCCESS) {
        if (!pl_swapchain_submit_frame(sys->pl->swapchain))
            msg_Err(vd, "Failed rendering frame!");
        pl_swapchain_swap_buffers(sys->pl->swapchain);
        vlc_placebo_ReleaseCurrent(sys->pl);
    }
}

static void UpdateColorspaceHint(vout_display_t *vd, const video_format_t *fmt)
{
    vout_display_sys_t *sys = vd->sys;
    struct pl_swapchain_colors hint = {0};

    switch (var_InheritInteger(vd, "pl-output-hint")) {
    case OUTPUT_AUTO: ;
        const struct pl_color_space csp = vlc_placebo_ColorSpace(fmt);
        hint.primaries = csp.primaries;
        hint.transfer = csp.transfer;
        hint.hdr = (struct pl_hdr_metadata) {
            .prim.green.x   = fmt->mastering.primaries[0] / 50000.0f,
            .prim.green.y   = fmt->mastering.primaries[1] / 50000.0f,
            .prim.blue.x    = fmt->mastering.primaries[2] / 50000.0f,
            .prim.blue.y    = fmt->mastering.primaries[3] / 50000.0f,
            .prim.red.x     = fmt->mastering.primaries[4] / 50000.0f,
            .prim.red.y     = fmt->mastering.primaries[5] / 50000.0f,
            .prim.white.x   = fmt->mastering.white_point[0] / 50000.0f,
            .prim.white.y   = fmt->mastering.white_point[1] / 50000.0f,
            .max_luma       = fmt->mastering.max_luminance / 10000.0f,
            .min_luma       = fmt->mastering.min_luminance / 10000.0f,
            .max_cll        = fmt->lighting.MaxCLL,
            .max_fall       = fmt->lighting.MaxFALL,
        };
        break;
    case OUTPUT_SDR:
        break;
    case OUTPUT_HDR10:
        hint.primaries = PL_COLOR_PRIM_BT_2020;
        hint.transfer = PL_COLOR_TRC_PQ;
        break;
    case OUTPUT_HLG:
        hint.primaries = PL_COLOR_PRIM_BT_2020;
        hint.transfer = PL_COLOR_TRC_HLG;
        break;
    }

    pl_swapchain_colorspace_hint(sys->pl->swapchain, &hint);
}

static void UpdateIccProfile(vout_display_t *vd, const vlc_icc_profile_t *prof)
{
    vout_display_sys_t *sys = vd->sys;
    sys->target_icc_signature = 0; /* recompute signature on next PictureRender */
    (void) prof; /* we get the current value from vout_display_cfg_t */
}

static int SetDisplaySize(vout_display_t *vd, unsigned width_, unsigned height_)
{
    vout_display_sys_t *sys = vd->sys;

    /* The following resize should be automatic on most platforms but can
     * trigger bugs on some platform with some drivers, that have been seen
     * on Windows in particular. Doing it right now enforces the correct
     * behavior and prevents these bugs.
     * In addition, platforms like Wayland need the call as the size of the
     * window is defined by the size of the content, and not the opposite.
     * The swapchain creation won't be done twice with this call. */

    int width = (int) width_;
    int height = (int) height_;
    if (vlc_placebo_MakeCurrent(sys->pl) != VLC_SUCCESS)
        return VLC_SUCCESS; // ignore errors

    vlc_placebo_Resize(sys->pl, width_, height_);
    pl_swapchain_resize(sys->pl->swapchain, &width, &height);
    vlc_placebo_ReleaseCurrent(sys->pl);

    /* NOTE: We currently ignore resizing failures that are transient
     * on X11. Maybe improving resizing might fix that, but we don't
     * implement reset_pictures anyway.
    if (width != (int) width_ || height != (int) height_)
        return VLC_EGENERIC;
    */

    return VLC_SUCCESS;
}

static void LoadCustomLUT(vout_display_sys_t *sys, const char *filepath)
{
    if (!filepath || !*filepath) {
        pl_lut_free(&sys->lut);
        return;
    }

    if (sys->lut_path && strcmp(filepath, sys->lut_path) == 0)
        return; // same LUT

    char *lut_file = NULL;
    FILE *fs = NULL;

    free(sys->lut_path);
    sys->lut_path = strdup(filepath);

    fs = vlc_fopen(filepath, "rb");
    if (!fs)
        goto error;
    int ret = fseek(fs, 0, SEEK_END);
    if (ret == -1)
        goto error;
    long length = ftell(fs);
    if (length < 0)
        goto error;
    rewind(fs);

    lut_file = vlc_alloc(length, sizeof(*lut_file));
    if (!lut_file)
        goto error;
    ret = fread(lut_file, length, 1, fs);
    if (ret != 1)
        goto error;
    sys->lut = pl_lut_parse_cube(sys->pl->log, lut_file, length);
    if (!sys->lut)
        goto error;

    fclose(fs);
    free(lut_file);
    return;

error:
    free(lut_file);
    if (fs)
        fclose(fs);
    return;
}

static void LoadUserShader(vout_display_sys_t *sys, const char *filepath)
{
    if (!filepath || !*filepath) {
        pl_mpv_user_shader_destroy(&sys->hook);
        return;
    }

    if (sys->hook_path && strcmp(filepath, sys->hook_path) == 0)
        return; // same shader

    char *shader_str = NULL;
    FILE *fs = NULL;

    free(sys->hook_path);
    sys->hook_path = strdup(filepath);

    fs = vlc_fopen(filepath, "rb");
    int ret = fseek(fs, 0, SEEK_END);
    if (ret == -1)
        goto error;
    long length = ftell(fs);
    if (length < 0)
        goto error;
    rewind(fs);

    shader_str = vlc_alloc(length, sizeof(*shader_str));
    if (!shader_str)
        goto error;
    ret = fread(shader_str, length, 1, fs);
    if (ret != 1)
        goto error;
    sys->hook = pl_mpv_user_shader_parse(sys->pl->gpu, shader_str, length);
    if (!sys->hook)
        goto error;

    fclose(fs);
    free(shader_str);
    return;

error:
    free(shader_str);
    if (fs)
        fclose(fs);
    return;
}

// Options

#define PROVIDER_TEXT N_("GPU instance provider")
#define PROVIDER_LONGTEXT N_( \
    "Extension which provides the GPU instance to use.")

vlc_module_begin ()
    set_shortname ("libplacebo")
    set_description (N_("libplacebo video output"))
    set_subcategory (SUBCAT_VIDEO_VOUT)
    set_callback_display(Open, 0)
    add_shortcut ("libplacebo", "pl")
    add_module ("pl-gpu", "libplacebo gpu", "any", PROVIDER_TEXT, PROVIDER_LONGTEXT)

    set_section("Custom shaders", NULL)
    add_loadfile("pl-user-shader", NULL, USER_SHADER_FILE_TEXT, USER_SHADER_FILE_LONGTEXT)

    set_section("Scaling", NULL)
    add_integer("pl-upscaler-preset", SCALE_BUILTIN,
            UPSCALER_PRESET_TEXT, SCALER_PRESET_LONGTEXT)
            change_integer_list(scale_values, scale_text)
    add_integer("pl-downscaler-preset", SCALE_BUILTIN,
            DOWNSCALER_PRESET_TEXT, SCALER_PRESET_LONGTEXT)
            change_integer_list(scale_values, scale_text)
    add_integer_with_range("pl-lut-entries", 64, 16, 256,
            LUT_ENTRIES_TEXT, LUT_ENTRIES_LONGTEXT)
    add_float_with_range("pl-antiringing", 0.0,
            0.0, 1.0, ANTIRING_TEXT, ANTIRING_LONGTEXT)
    add_bool("pl-sigmoid", !!pl_render_default_params.sigmoid_params,
            SIGMOID_TEXT, SIGMOID_LONGTEXT)
    add_float_with_range("pl-sigmoid-center", pl_sigmoid_default_params.center,
            0., 1., SIGMOID_CENTER_TEXT, SIGMOID_CENTER_LONGTEXT)
    add_float_with_range("pl-sigmoid-slope", pl_sigmoid_default_params.slope,
            1., 20., SIGMOID_SLOPE_TEXT, SIGMOID_SLOPE_LONGTEXT)

    set_section("Debanding", NULL)
    add_bool("pl-debanding", false, DEBAND_TEXT, DEBAND_LONGTEXT)
    add_integer("pl-iterations", pl_deband_default_params.iterations,
            DEBAND_ITER_TEXT, DEBAND_ITER_LONGTEXT)
    add_float("pl-threshold", pl_deband_default_params.threshold,
            DEBAND_THRESH_TEXT, DEBAND_THRESH_LONGTEXT)
    add_float("pl-radius", pl_deband_default_params.radius,
            DEBAND_RADIUS_TEXT, DEBAND_RADIUS_LONGTEXT)
    add_float("pl-grain", pl_deband_default_params.grain,
            DEBAND_GRAIN_TEXT, DEBAND_GRAIN_LONGTEXT)

    set_section("Colorspace conversion", NULL)
    add_integer("pl-output-hint", true, OUTPUT_HINT_TEXT, OUTPUT_HINT_LONGTEXT)
            change_integer_list(output_values, output_text)
    add_placebo_color_map_opts("pl")
    add_integer("pl-target-prim", PL_COLOR_PRIM_UNKNOWN, PRIM_TEXT, PRIM_LONGTEXT)
            change_integer_list(prim_values, prim_text)
    add_integer("pl-target-trc", PL_COLOR_TRC_UNKNOWN, TRC_TEXT, TRC_LONGTEXT)
            change_integer_list(trc_values, trc_text)

    add_loadfile("pl-lut-file", NULL, LUT_FILE_TEXT, LUT_FILE_LONGTEXT)
    add_integer("pl-lut-mode", LUT_DISABLED, LUT_MODE_TEXT, LUT_MODE_LONGTEXT)
            change_integer_list(lut_mode_values, lut_mode_text)

    // TODO: support for ICC profiles

    add_float_with_range("pl-peak-period", pl_peak_detect_default_params.smoothing_period,
            0., 1000., PEAK_PERIOD_TEXT, PEAK_PERIOD_LONGTEXT)
    add_float("pl-scene-threshold-low", pl_peak_detect_default_params.scene_threshold_low,
            SCENE_THRESHOLD_LOW_TEXT, SCENE_THRESHOLD_LOW_LONGTEXT)
    add_float("pl-scene-threshold-high", pl_peak_detect_default_params.scene_threshold_high,
            SCENE_THRESHOLD_HIGH_TEXT, SCENE_THRESHOLD_HIGH_LONGTEXT)

#if PL_API_VER >= 285
    add_float_with_range("pl-contrast-recovery", pl_color_map_default_params.contrast_recovery,
            0., 3., CONTRAST_RECOVERY_TEXT, CONTRAST_RECOVERY_LONGTEXT)
    add_float_with_range("pl-contrast-smoothness", pl_color_map_default_params.contrast_smoothness,
            0., 10., CONTRAST_SMOOTHNESS_TEXT, CONTRAST_SMOOTHNESS_LONGTEXT)
#endif

    set_section("Dithering", NULL)
    add_integer("pl-dither", -1,
            DITHER_TEXT, DITHER_LONGTEXT)
            change_integer_list(dither_values, dither_text)
    add_integer_with_range("pl-dither-size", pl_dither_default_params.lut_size,
            1, 8, DITHER_SIZE_TEXT, DITHER_SIZE_LONGTEXT)
    add_bool("pl-temporal-dither", pl_dither_default_params.temporal,
            TEMPORAL_DITHER_TEXT, TEMPORAL_DITHER_LONGTEXT)
    add_integer_with_range("pl-dither-depth", 0,
            0, 16, DITHER_DEPTH_TEXT, DITHER_DEPTH_LONGTEXT)

    set_section("Custom upscaler (when preset = custom)", NULL)
    add_integer("pl-upscaler-kernel", FILTER_BOX,
            KERNEL_TEXT, KERNEL_LONGTEXT)
            change_integer_list(filter_values, filter_text)
    add_integer("pl-upscaler-window", FILTER_NONE,
            WINDOW_TEXT, WINDOW_LONGTEXT)
            change_integer_list(filter_values, filter_text)
    add_bool("pl-upscaler-polar", false, POLAR_TEXT, POLAR_LONGTEXT)
    add_float_with_range("pl-upscaler-clamp", 0.0,
            0.0, 1.0, CLAMP_TEXT, CLAMP_LONGTEXT)
    add_float_with_range("pl-upscaler-blur", 1.0,
            0.0, 100.0, BLUR_TEXT, BLUR_LONGTEXT)
    add_float_with_range("pl-upscaler-taper", 0.0,
            0.0, 10.0, TAPER_TEXT, TAPER_LONGTEXT)

    set_section("Custom downscaler (when preset = custom)", NULL)
    add_integer("pl-downscaler-kernel", FILTER_BOX,
            KERNEL_TEXT, KERNEL_LONGTEXT)
            change_integer_list(filter_values, filter_text)
    add_integer("pl-downscaler-window", FILTER_NONE,
            WINDOW_TEXT, WINDOW_LONGTEXT)
            change_integer_list(filter_values, filter_text)
    add_bool("pl-downscaler-polar", false, POLAR_TEXT, POLAR_LONGTEXT)
    add_float_with_range("pl-downscaler-clamp", 0.0,
            0.0, 1.0, CLAMP_TEXT, CLAMP_LONGTEXT)
    add_float_with_range("pl-downscaler-blur", 1.0,
            0.0, 100.0, BLUR_TEXT, BLUR_LONGTEXT)
    add_float_with_range("pl-downscaler-taper", 0.0,
            0.0, 10.0, TAPER_TEXT, TAPER_LONGTEXT)

    set_section("Performance tweaks / debugging", NULL)
    add_bool("pl-skip-aa", false, SKIP_AA_TEXT, SKIP_AA_LONGTEXT)
    add_float_with_range("pl-polar-cutoff", 0.001,
            0., 1., POLAR_CUTOFF_TEXT, POLAR_CUTOFF_LONGTEXT)
    add_bool("pl-overlay-direct", false, OVERLAY_DIRECT_TEXT, OVERLAY_DIRECT_LONGTEXT)
    add_bool("pl-disable-linear", false, DISABLE_LINEAR_TEXT, DISABLE_LINEAR_LONGTEXT)
    add_bool("pl-force-general", false, FORCE_GENERAL_TEXT, FORCE_GENERAL_LONGTEXT)
    add_bool("pl-delayed-peak", false, DELAYED_PEAK_TEXT, DELAYED_PEAK_LONGTEXT)

vlc_module_end ()

// Update the renderer settings based on the current configuration.
//
// XXX: This could be called every time the parameters change, but currently
// VLC does not allow that - so we're stuck with doing it once on Open().
// Should be changed as soon as it's possible!
static void UpdateParams(vout_display_t *vd)
{
    vout_display_sys_t *sys = vd->sys;
    vlc_placebo_ColorMapParams(VLC_OBJECT(vd), "pl", &sys->color_map);

    sys->deband = pl_deband_default_params;
    sys->deband.iterations = var_InheritInteger(vd, "pl-iterations");
    sys->deband.threshold = var_InheritFloat(vd, "pl-threshold");
    sys->deband.radius = var_InheritFloat(vd, "pl-radius");
    sys->deband.grain = var_InheritFloat(vd, "pl-grain");
    bool use_deband = sys->deband.iterations > 0 || sys->deband.grain > 0;
    use_deband &= var_InheritBool(vd, "pl-debanding");

    sys->sigmoid = pl_sigmoid_default_params;
    sys->sigmoid.center = var_InheritFloat(vd, "pl-sigmoid-center");
    sys->sigmoid.slope = var_InheritFloat(vd, "pl-sigmoid-slope");
    bool use_sigmoid = var_InheritBool(vd, "pl-sigmoid");

    sys->dither = pl_dither_default_params;
    int method = var_InheritInteger(vd, "pl-dither");
    bool use_dither = method >= 0;
    sys->dither.method = use_dither ? method : 0;
    sys->dither.lut_size = var_InheritInteger(vd, "pl-dither-size");
    sys->dither.temporal = var_InheritBool(vd, "pl-temporal-dither");

    sys->params = pl_render_default_params;
    sys->params.deband_params = use_deband ? &sys->deband : NULL;
    sys->params.sigmoid_params = use_sigmoid ? &sys->sigmoid : NULL;
    sys->params.color_map_params = &sys->color_map;
    sys->params.dither_params = use_dither ? &sys->dither : NULL;
    sys->params.lut_entries = var_InheritInteger(vd, "pl-lut-entries");
    sys->params.antiringing_strength = var_InheritFloat(vd, "pl-antiringing");
    sys->params.skip_anti_aliasing = var_InheritBool(vd, "pl-skip-aa");
    sys->params.polar_cutoff = var_InheritFloat(vd, "pl-polar-cutoff");
    sys->params.disable_linear_scaling = var_InheritBool(vd, "pl-disable-linear");
    sys->params.disable_builtin_scalers = var_InheritBool(vd, "pl-force-general");

    sys->peak_detect.smoothing_period = var_InheritFloat(vd, "pl-peak-period");
    sys->peak_detect.scene_threshold_low = var_InheritFloat(vd, "pl-scene-threshold-low");
    sys->peak_detect.scene_threshold_high = var_InheritFloat(vd, "pl-scene-threshold-high");
#if PL_API_VER >= 254
    sys->peak_detect.allow_delayed = var_InheritBool(vd, "pl-delayed-peak");
#else
    sys->params.allow_delayed_peak_detect = var_InheritBool(vd, "pl-delayed-peak");
#endif
    if (sys->peak_detect.smoothing_period > 0.0)
        sys->params.peak_detect_params = &sys->peak_detect;

#if PL_API_VER >= 285
    sys->color_map.contrast_recovery = var_InheritFloat(vd, "pl-contrast-recovery");
    sys->color_map.contrast_smoothness = var_InheritFloat(vd, "pl-contrast-smoothness");
#endif

    int preset = var_InheritInteger(vd, "pl-upscaler-preset");
    sys->params.upscaler = scale_config[preset];
    if (preset == SCALE_CUSTOM) {
        sys->params.upscaler = &sys->upscaler;
        sys->upscaler = (struct pl_filter_config) {
            .kernel = filter_fun[var_InheritInteger(vd, "pl-upscaler-kernel")],
            .window = filter_fun[var_InheritInteger(vd, "pl-upscaler-window")],
            .clamp  = var_InheritFloat(vd, "pl-upscaler-clamp"),
            .blur   = var_InheritFloat(vd, "pl-upscaler-blur"),
            .taper  = var_InheritFloat(vd, "pl-upscaler-taper"),
            .polar  = var_InheritBool(vd, "pl-upscaler-polar"),
        };

        if (!sys->upscaler.kernel) {
            msg_Err(vd, "Tried specifying a custom upscaler with no kernel!");
            sys->params.upscaler = NULL;
        }
    };

    preset = var_InheritInteger(vd, "pl-downscaler-preset");
    sys->params.downscaler = scale_config[preset];
    if (preset == SCALE_CUSTOM) {
        sys->params.downscaler = &sys->downscaler;
        sys->downscaler = (struct pl_filter_config) {
            .kernel = filter_fun[var_InheritInteger(vd, "pl-downscaler-kernel")],
            .window = filter_fun[var_InheritInteger(vd, "pl-downscaler-window")],
            .clamp  = var_InheritFloat(vd, "pl-downscaler-clamp"),
            .blur   = var_InheritFloat(vd, "pl-downscaler-blur"),
            .taper  = var_InheritFloat(vd, "pl-downscaler-taper"),
            .polar  = var_InheritBool(vd, "pl-downscaler-polar"),
        };

        if (!sys->downscaler.kernel) {
            msg_Err(vd, "Tried specifying a custom downscaler with no kernel!");
            sys->params.downscaler = NULL;
        }
    };

    sys->dither_depth = var_InheritInteger(vd, "pl-dither-depth");
    sys->target = (struct pl_color_space) {
        .primaries = var_InheritInteger(vd, "pl-target-prim"),
        .transfer = var_InheritInteger(vd, "pl-target-trc"),
    };

    sys->lut_mode = var_InheritInteger(vd, "pl-lut-mode");
    char *lut_file = var_InheritString(vd, "pl-lut-file");
    LoadCustomLUT(sys, lut_file);
    free(lut_file);
    if (sys->lut) {
        sys->params.lut = sys->lut;
        switch (sys->lut_mode) {
        case LUT_NATIVE:     sys->params.lut_type = PL_LUT_NATIVE; break;
        case LUT_LINEAR:     sys->params.lut_type = PL_LUT_NORMALIZED; break;
        case LUT_CONVERSION: sys->params.lut_type = PL_LUT_CONVERSION; break;
        default:
            sys->params.lut = NULL; // the others need to be applied elsewhere
            break;
        }
    }

    char *shader_file = var_InheritString(vd, "pl-user-shader");
    LoadUserShader(sys, shader_file);
    free(shader_file);
    if (sys->hook) {
        sys->params.hooks = &sys->hook;
        sys->params.num_hooks = 1;
    } else {
        sys->params.num_hooks = 0;
    }
}

Messung
CHZG
83949490

¤ Dauer der Verarbeitung: 0.5 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.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=1104856
#Domains=417809