You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

928 lines
33 KiB

/**
* @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 Control(vout_display_t *, int);
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,
.control = Control,
.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;
const vlc_fourcc_t *fcc;
for (fcc = vlc_fourcc_GetFallback(vd->source->i_chroma); *fcc; fcc++) {
if (vlc_placebo_FormatSupported(gpu, *fcc)) {
fmt->i_chroma = *fcc;
break;
}
}
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, NULL)) {
// 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;
struct vout_display_placement dp = vd->cfg->display;
dp.width = frame.fbo->params.w;
dp.height = frame.fbo->params.h;
if (need_vflip) {
switch (dp.align.vertical) {
case VLC_VIDEO_ALIGN_TOP:
dp.align.vertical = VLC_VIDEO_ALIGN_BOTTOM;
break;
case VLC_VIDEO_ALIGN_BOTTOM:
dp.align.vertical = VLC_VIDEO_ALIGN_TOP;
break;
default:
break;
}
}
vout_display_PlacePicture(&place, vd->fmt, &dp);
if (need_vflip) {
place.y = frame.fbo->params.h - 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 = 0;
const subpicture_region_t *r;
vlc_spu_regions_foreach_const(r, &subpicture->regions)
num_regions++;
// 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_spu_regions_foreach_const(r, &subpicture->regions) {
assert(r->p_picture->i_planes == 1);
struct pl_plane_data subdata[4];
if (!vlc_placebo_PlaneData(r->p_picture, subdata, NULL))
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->fmt),
.repr = vlc_placebo_ColorRepr(&r->fmt),
.parts = &sys->overlay_parts[i],
.num_parts = 1,
};
sys->overlay_parts[i] = (struct pl_overlay_part) {
.src = {
.x1 = r->fmt.i_visible_width,
.y1 = r->fmt.i_visible_height,
},
.dst = {
.x0 = place.x + r->i_x,
.y0 = place.y + r->i_y * ysign,
.x1 = place.x + r->i_x + r->fmt.i_visible_width,
.y1 = place.y + (r->i_y + r->fmt.i_visible_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 Control(vout_display_t *vd, int query)
{
vout_display_sys_t *sys = vd->sys;
switch (query)
{
case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE:
case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED:
case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:
case VOUT_DISPLAY_CHANGE_SOURCE_CROP:
case VOUT_DISPLAY_CHANGE_ZOOM: {
/* 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. */
if (query == VOUT_DISPLAY_CHANGE_DISPLAY_SIZE)
{
int width = (int) vd->cfg->display.width;
int height = (int) vd->cfg->display.height;
if (vlc_placebo_MakeCurrent(sys->pl) != VLC_SUCCESS)
return VLC_SUCCESS; // ignore errors
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) vd->cfg->display.width
|| height != (int) vd->cfg->display.height)
return VLC_EGENERIC;
*/
}
return VLC_SUCCESS;
}
default:
msg_Err (vd, "Unknown request %d", query);
}
return VLC_EGENERIC;
}
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;
}
}