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.
 
 
 
 
 
 

631 lines
24 KiB

/*****************************************************************************
* placebo.c: Definition of various libplacebo helpers
*****************************************************************************
* Copyright (C) 2018 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.
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <assert.h>
#include <stdatomic.h>
#include <stdlib.h>
#include <vlc_common.h>
#include "utils.h"
static void Log(void *priv, enum pl_log_level level, const char *msg)
{
struct vlc_object_t *obj = priv;
switch (level) {
case PL_LOG_FATAL: // fall through
case PL_LOG_ERR: msg_Err(obj, "%s", msg); break;
case PL_LOG_WARN: msg_Warn(obj, "%s", msg); break;
case PL_LOG_INFO: msg_Info(obj, "%s", msg); break;
case PL_LOG_DEBUG: msg_Dbg(obj, "%s", msg); break;
default: break;
}
}
pl_log vlc_placebo_CreateLog(vlc_object_t *obj)
{
return pl_log_create(PL_API_VER, &(struct pl_log_params) {
.log_level = PL_LOG_DEBUG,
.log_cb = Log,
.log_priv = obj,
});
}
struct plane_desc {
int components;
size_t pixel_size;
int comp_bits[4];
int comp_map[4];
int w_denom;
int h_denom;
};
struct fmt_desc {
enum pl_fmt_type type;
struct plane_desc planes[4];
int num_planes;
int color_bits; // relevant bits, or 0 if the same as the texture depth
};
#define SIZE(n, bits, pad) (((n) * (bits) + (pad) + 7) / 8)
#define COMPS(...) {__VA_ARGS__}
#define PLANE(n, bits, map, wd, hd, pad) \
{ .components = n, \
.pixel_size = SIZE(n, bits, pad), \
.comp_bits = {bits, bits, bits, bits}, \
.comp_map = map, \
.w_denom = wd, \
.h_denom = hd, \
}
#define _PLANAR(n, bits, wd, hd) \
.type = PL_FMT_UNORM, \
.num_planes = n, \
.planes = { \
PLANE(1, bits, {0}, 1, 1, 0), \
PLANE(1, bits, {1}, wd, hd, 0), \
PLANE(1, bits, {2}, wd, hd, 0), \
PLANE(1, bits, {3}, 1, 1, 0), \
}
#define _SEMIPLANAR(n, bits, wd, hd) \
.type = PL_FMT_UNORM, \
.num_planes = n, \
.planes = { \
PLANE(1, bits, {0}, 1, 1, 0), \
PLANE(2, bits, COMPS(1, 2), wd, hd, 0), \
PLANE(1, bits, {3}, 1, 1, 0), \
}
#define PACKED(n, bits, pad) \
.type = PL_FMT_UNORM, \
.num_planes = 1, \
.planes = { \
PLANE(n, bits, COMPS(0, 1, 2, 3), 1, 1, pad), \
}
#define SINGLE(t, bits) \
.type = PL_FMT_##t, \
.num_planes = 1, \
.planes = { \
PLANE(1, bits, {0}, 1, 1, 0), \
}
#define PLANAR(...) _PLANAR(__VA_ARGS__)
#define SEMIPLANAR(...) _SEMIPLANAR(__VA_ARGS__)
#define _410 4, 2
#define _411 4, 1
#define _420 2, 2
#define _422 2, 1
#define _440 1, 2
#define _444 1, 1
// NOTE: This list contains some special formats that don't follow the normal
// rules, but which are included regardless. The corrections for these
// exceptions happen below, in the function vlc_placebo_PlaneFormat!
static const struct { vlc_fourcc_t fcc; struct fmt_desc desc; } formats[] = {
{ VLC_CODEC_YV9, {PLANAR(3, 8, _410)} },
{ VLC_CODEC_I410, {PLANAR(3, 8, _410)} },
{ VLC_CODEC_I411, {PLANAR(3, 8, _411)} },
{ VLC_CODEC_I440, {PLANAR(3, 8, _440)} },
{ VLC_CODEC_GREY, {PLANAR(1, 8, _444)} },
{ VLC_CODEC_I420, {PLANAR(3, 8, _420)} },
#ifdef WORDS_BIGENDIAN
{ VLC_CODEC_I420_9B, {PLANAR(3, 16, _420), .color_bits = 9} },
{ VLC_CODEC_I420_10B, {PLANAR(3, 16, _420), .color_bits = 10} },
{ VLC_CODEC_I420_12B, {PLANAR(3, 16, _420), .color_bits = 12} },
{ VLC_CODEC_I420_16B, {PLANAR(3, 16, _420), .color_bits = 16} },
#else
{ VLC_CODEC_I420_9L, {PLANAR(3, 16, _420), .color_bits = 9} },
{ VLC_CODEC_I420_10L, {PLANAR(3, 16, _420), .color_bits = 10} },
{ VLC_CODEC_I420_12L, {PLANAR(3, 16, _420), .color_bits = 12} },
{ VLC_CODEC_I420_16L, {PLANAR(3, 16, _420), .color_bits = 16} },
#endif
{ VLC_CODEC_I422, {PLANAR(3, 8, _422)} },
#ifdef WORDS_BIGENDIAN
{ VLC_CODEC_I422_9B, {PLANAR(3, 16, _422), .color_bits = 9} },
{ VLC_CODEC_I422_10B, {PLANAR(3, 16, _422), .color_bits = 10} },
{ VLC_CODEC_I422_12B, {PLANAR(3, 16, _422), .color_bits = 12} },
#else
{ VLC_CODEC_I422_9L, {PLANAR(3, 16, _422), .color_bits = 9} },
{ VLC_CODEC_I422_10L, {PLANAR(3, 16, _422), .color_bits = 10} },
{ VLC_CODEC_I422_12L, {PLANAR(3, 16, _422), .color_bits = 12} },
#endif
{ VLC_CODEC_I444, {PLANAR(3, 8, _444)} },
#ifdef WORDS_BIGENDIAN
{ VLC_CODEC_I444_9B, {PLANAR(3, 16, _444), .color_bits = 9} },
{ VLC_CODEC_I444_10B, {PLANAR(3, 16, _444), .color_bits = 10} },
{ VLC_CODEC_I444_12B, {PLANAR(3, 16, _444), .color_bits = 12} },
{ VLC_CODEC_I444_16B, {PLANAR(3, 16, _444), .color_bits = 16} },
#else
{ VLC_CODEC_I444_9L, {PLANAR(3, 16, _444), .color_bits = 9} },
{ VLC_CODEC_I444_10L, {PLANAR(3, 16, _444), .color_bits = 10} },
{ VLC_CODEC_I444_12L, {PLANAR(3, 16, _444), .color_bits = 12} },
{ VLC_CODEC_I444_16L, {PLANAR(3, 16, _444), .color_bits = 16} },
#endif
{ VLC_CODEC_YUVA, {PLANAR(4, 8, _444)} },
{ VLC_CODEC_YUV422A, {PLANAR(4, 8, _422)} },
#ifdef WORDS_BIGENDIAN
{ VLC_CODEC_YUVA_444_10B, {PLANAR(4, 16, _444), .color_bits = 10} },
#else
{ VLC_CODEC_YUVA_444_10L, {PLANAR(4, 16, _444), .color_bits = 10} },
#endif
{ VLC_CODEC_NV12, {SEMIPLANAR(2, 8, _420)} },
{ VLC_CODEC_NV21, {SEMIPLANAR(2, 8, _420)} },
{ VLC_CODEC_P010, {SEMIPLANAR(2, 16, _420)} },
{ VLC_CODEC_P016, {SEMIPLANAR(2, 16, _420)} },
{ VLC_CODEC_NV16, {SEMIPLANAR(2, 8, _422)} },
{ VLC_CODEC_NV61, {SEMIPLANAR(2, 8, _422)} },
{ VLC_CODEC_NV24, {SEMIPLANAR(2, 8, _444)} },
{ VLC_CODEC_NV42, {SEMIPLANAR(2, 8, _444)} },
{ VLC_CODEC_RGB233, {PACKED(3, 2, 2)} },
{ VLC_CODEC_BGR233, {PACKED(3, 2, 2)} },
{ VLC_CODEC_RGB332, {PACKED(3, 2, 2)} },
{ VLC_CODEC_RGB565LE, {PACKED(3, 5, 1)} },
{ VLC_CODEC_RGB555LE, {PACKED(3, 5, 1)} },
{ VLC_CODEC_RGB24, {PACKED(3, 8, 0)} },
{ VLC_CODEC_BGR24, {PACKED(3, 8, 0)} },
{ VLC_CODEC_RGBA, {PACKED(4, 8, 0)} },
{ VLC_CODEC_BGRX, {PACKED(3, 8, 8)} },
{ VLC_CODEC_BGRA, {PACKED(4, 8, 0)} },
{ VLC_CODEC_GBR_PLANAR, {PLANAR(3, 8, _444)} },
#ifdef WORDS_BIGENDIAN
{ VLC_CODEC_GBR_PLANAR_9B, {PLANAR(3, 16, _444), .color_bits = 9} },
{ VLC_CODEC_GBR_PLANAR_10B, {PLANAR(3, 16, _444), .color_bits = 10} },
{ VLC_CODEC_GBR_PLANAR_16B, {PLANAR(3, 16, _444), .color_bits = 16} },
#else
{ VLC_CODEC_GBR_PLANAR_9L, {PLANAR(3, 16, _444), .color_bits = 9} },
{ VLC_CODEC_GBR_PLANAR_10L, {PLANAR(3, 16, _444), .color_bits = 10} },
{ VLC_CODEC_GBR_PLANAR_16L, {PLANAR(3, 16, _444), .color_bits = 16} },
#endif
{ VLC_CODEC_U8, {SINGLE(UNORM, 8)} },
{ VLC_CODEC_S8, {SINGLE(SNORM, 8)} },
{ VLC_CODEC_U16N, {SINGLE(UNORM, 16)} },
{ VLC_CODEC_S16N, {SINGLE(SNORM, 16)} },
{ VLC_CODEC_U24N, {SINGLE(UNORM, 24)} },
{ VLC_CODEC_S24N, {SINGLE(SNORM, 24)} },
{ VLC_CODEC_U32N, {SINGLE(UNORM, 32)} },
{ VLC_CODEC_S32N, {SINGLE(SNORM, 32)} },
{ VLC_CODEC_FL32, {SINGLE(FLOAT, 32)} },
{ VLC_CODEC_FL64, {SINGLE(FLOAT, 64)} },
{ 0 },
};
static const struct fmt_desc *FindDesc(vlc_fourcc_t fcc)
{
for (int i = 0; formats[i].fcc; i++) {
if (formats[i].fcc == fcc) {
return &formats[i].desc;
}
}
return NULL;
}
// This fills everything except width/height, which are left as 1
static void FillDesc(vlc_fourcc_t fcc, const struct fmt_desc *desc,
struct pl_plane_data data[4])
{
assert(desc->num_planes <= 4);
for (int i = 0; i < desc->num_planes; i++) {
const struct plane_desc *p = &desc->planes[i];
data[i] = (struct pl_plane_data) {
.type = desc->type,
.width = 1,
.height = 1,
.pixel_stride = p->pixel_size,
};
for (int c = 0; c < p->components; c++) {
data[i].component_size[c] = p->comp_bits[c];
data[i].component_map[c] = p->comp_map[c];
}
}
// Exceptions to the rule
switch (fcc) {
case VLC_CODEC_YV9:
case VLC_CODEC_YV12:
// Planar Y:V:U
data[1].component_map[0] = 2;
data[2].component_map[0] = 1;
break;
case VLC_CODEC_BGRX:
case VLC_CODEC_BGRA:
// Packed BGR
data[0].component_map[0] = 2;
data[0].component_map[1] = 1;
data[0].component_map[2] = 0;
break;
case VLC_CODEC_GBR_PLANAR:
case VLC_CODEC_GBR_PLANAR_9L:
case VLC_CODEC_GBR_PLANAR_10L:
case VLC_CODEC_GBR_PLANAR_16L:
case VLC_CODEC_GBR_PLANAR_9B:
case VLC_CODEC_GBR_PLANAR_10B:
case VLC_CODEC_GBR_PLANAR_16B:
// Planar GBR
data[0].component_map[0] = 1;
data[1].component_map[0] = 2;
data[2].component_map[0] = 0;
break;
case VLC_CODEC_RGB565LE:
// 5:6:5 instead of 5:5:5
data[0].component_size[1] += 1;
break;
case VLC_CODEC_RGB233:
// 2:3:3 instead of 2:2:2
data[0].component_size[1] += 1;
data[0].component_size[2] += 1;
break;
case VLC_CODEC_BGR233:
// 2:3:3 instead of 2:2:2
data[0].component_size[1] += 1;
data[0].component_size[2] += 1;
// Packed BGR
data[0].component_map[0] = 2;
data[0].component_map[1] = 1;
data[0].component_map[2] = 0;
break;
case VLC_CODEC_RGB332:
// 3:3:2 instead of 2:2:2
data[0].component_size[0] += 1;
data[0].component_size[1] += 1;
break;
default: break;
}
}
int vlc_placebo_PlaneFormat(const video_format_t *fmt, struct pl_plane_data data[4])
{
const struct fmt_desc *desc = FindDesc(fmt->i_chroma);
if (!desc)
return 0;
FillDesc(fmt->i_chroma, desc, data);
for (int i = 0; i < desc->num_planes; i++) {
const struct plane_desc *p = &desc->planes[i];
data[i].width = (fmt->i_visible_width + p->w_denom - 1) / p->w_denom;
data[i].height = (fmt->i_visible_height + p->h_denom - 1) / p->h_denom;
}
return desc->num_planes;
}
int vlc_placebo_PlaneData(const picture_t *pic, struct pl_plane_data data[4],
pl_buf buf)
{
int planes = vlc_placebo_PlaneFormat(&pic->format, data);
if (!planes)
return 0;
assert(planes == pic->i_planes);
for (int i = 0; i < planes; i++) {
assert(data[i].height == pic->p[i].i_visible_lines);
data[i].row_stride = pic->p[i].i_pitch;
if (buf) {
assert(buf->data);
assert(pic->p[i].p_pixels <= buf->data + buf->params.size);
data[i].buf = buf;
data[i].buf_offset = (uintptr_t) pic->p[i].p_pixels - (ptrdiff_t) buf->data;
} else {
data[i].pixels = pic->p[i].p_pixels;
}
}
return planes;
}
bool vlc_placebo_FormatSupported(pl_gpu gpu, vlc_fourcc_t fcc)
{
const struct fmt_desc *desc = FindDesc(fcc);
if (!desc)
return false;
struct pl_plane_data data[4];
FillDesc(fcc, desc, data);
for (int i = 0; i < desc->num_planes; i++) {
if (!pl_plane_find_fmt(gpu, NULL, &data[i]))
return false;
}
return true;
}
struct pl_color_space vlc_placebo_ColorSpace(const video_format_t *fmt)
{
static const enum pl_color_primaries primaries[COLOR_PRIMARIES_MAX+1] = {
[COLOR_PRIMARIES_UNDEF] = PL_COLOR_PRIM_UNKNOWN,
[COLOR_PRIMARIES_BT601_525] = PL_COLOR_PRIM_BT_601_525,
[COLOR_PRIMARIES_BT601_625] = PL_COLOR_PRIM_BT_601_625,
[COLOR_PRIMARIES_BT709] = PL_COLOR_PRIM_BT_709,
[COLOR_PRIMARIES_BT2020] = PL_COLOR_PRIM_BT_2020,
[COLOR_PRIMARIES_DCI_P3] = PL_COLOR_PRIM_DCI_P3,
[COLOR_PRIMARIES_BT470_M] = PL_COLOR_PRIM_BT_470M,
};
static const enum pl_color_transfer transfers[TRANSFER_FUNC_MAX+1] = {
[TRANSFER_FUNC_UNDEF] = PL_COLOR_TRC_UNKNOWN,
[TRANSFER_FUNC_LINEAR] = PL_COLOR_TRC_LINEAR,
[TRANSFER_FUNC_SRGB] = PL_COLOR_TRC_SRGB,
[TRANSFER_FUNC_SMPTE_ST2084] = PL_COLOR_TRC_PQ,
[TRANSFER_FUNC_HLG] = PL_COLOR_TRC_HLG,
// these are all designed to be displayed on BT.1886 displays, so this
// is the correct way to handle them in libplacebo
[TRANSFER_FUNC_BT470_BG] = PL_COLOR_TRC_BT_1886,
[TRANSFER_FUNC_BT470_M] = PL_COLOR_TRC_BT_1886,
[TRANSFER_FUNC_BT709] = PL_COLOR_TRC_BT_1886,
[TRANSFER_FUNC_SMPTE_240] = PL_COLOR_TRC_BT_1886,
};
return (struct pl_color_space) {
.primaries = primaries[fmt->primaries],
.transfer = transfers[fmt->transfer],
.hdr = {
.prim = {
.green.x = fmt->mastering.primaries[0],
.green.y = fmt->mastering.primaries[1],
.blue.x = fmt->mastering.primaries[2],
.blue.y = fmt->mastering.primaries[3],
.red.x = fmt->mastering.primaries[4],
.red.y = fmt->mastering.primaries[5],
.white.x = fmt->mastering.white_point[0],
.white.y = fmt->mastering.white_point[1],
},
.min_luma = fmt->mastering.min_luminance,
.max_luma = fmt->mastering.max_luminance,
.max_cll = fmt->lighting.MaxCLL,
.max_fall = fmt->lighting.MaxFALL,
},
};
}
struct pl_color_repr vlc_placebo_ColorRepr(const video_format_t *fmt)
{
static const enum pl_color_system yuv_systems[COLOR_SPACE_MAX+1] = {
[COLOR_SPACE_UNDEF] = PL_COLOR_SYSTEM_BT_709, // _UNKNOWN is RGB
[COLOR_SPACE_BT601] = PL_COLOR_SYSTEM_BT_601,
[COLOR_SPACE_BT709] = PL_COLOR_SYSTEM_BT_709,
[COLOR_SPACE_BT2020] = PL_COLOR_SYSTEM_BT_2020_NC,
};
// fmt->space describes the YCbCr type only, it does not distinguish
// between YUV, XYZ, RGB and the likes!
enum pl_color_system sys;
if (likely(vlc_fourcc_IsYUV(fmt->i_chroma))) {
sys = yuv_systems[fmt->space];
} else if (unlikely(fmt->i_chroma == VLC_CODEC_XYZ12)) {
sys = PL_COLOR_SYSTEM_XYZ;
} else {
sys = PL_COLOR_SYSTEM_RGB;
}
const struct fmt_desc *desc = FindDesc(fmt->i_chroma);
int sample_depth = desc->planes[0].comp_bits[0]; // just use first component
return (struct pl_color_repr) {
.sys = sys,
.alpha = PL_ALPHA_PREMULTIPLIED,
.levels = unlikely(fmt->color_range == COLOR_RANGE_FULL)
? PL_COLOR_LEVELS_PC
: PL_COLOR_LEVELS_TV,
.bits = {
.sample_depth = sample_depth,
.color_depth = desc->color_bits ? desc->color_bits : sample_depth,
.bit_shift = 0,
},
};
}
void vlc_placebo_HdrMetadata(const vlc_video_hdr_dynamic_metadata_t *src,
struct pl_hdr_metadata *dst)
{
#if PL_API_VER >= 242
for (size_t i = 0; i < ARRAY_SIZE(dst->scene_max); i++)
dst->scene_max[i] = src->maxscl[i];
dst->scene_avg = src->average_maxrgb;
if (src->tone_mapping_flag) {
static_assert(sizeof(dst->ootf.anchors) == sizeof(src->bezier_curve_anchors), "array mismatch");
memcpy(dst->ootf.anchors, src->bezier_curve_anchors, sizeof(dst->ootf.anchors));
dst->ootf.num_anchors = src->num_bezier_anchors;
dst->ootf.target_luma = src->targeted_luminance;
dst->ootf.knee_x = src->knee_point_x;
dst->ootf.knee_y = src->knee_point_y;
}
#else
(void) src;
(void) dst;
#endif
}
void vlc_placebo_DoviMetadata(const vlc_video_dovi_metadata_t *src,
struct pl_dovi_metadata *dst)
{
static_assert(sizeof(dst->nonlinear_offset) == sizeof(src->nonlinear_offset), "array mismatch");
static_assert(sizeof(dst->nonlinear) == sizeof(src->nonlinear_matrix), "matrix mismatch");
static_assert(sizeof(dst->linear) == sizeof(src->linear_matrix), "matrix mismatch");
memcpy(dst->nonlinear_offset, src->nonlinear_offset,
sizeof(dst->nonlinear_offset));
memcpy(dst->nonlinear.m[0], src->nonlinear_matrix, sizeof(dst->nonlinear.m));
memcpy(dst->linear.m[0], src->linear_matrix, sizeof(dst->linear.m));
for (size_t c = 0; c < ARRAY_SIZE(dst->comp); c++) {
const struct vlc_dovi_reshape_t *csrc = &src->curves[c];
struct pl_reshape_data *cdst = &dst->comp[c];
cdst->num_pivots = csrc->num_pivots;
assert(csrc->num_pivots <= ARRAY_SIZE(csrc->pivots));
for (int i = 0; i < csrc->num_pivots; i++) {
const float scale = 1.0f / ((1 << src->bl_bit_depth) - 1);
cdst->pivots[i] = scale * csrc->pivots[i];
}
for (int i = 0; i < csrc->num_pivots - 1; i++) {
const float scale = 1.0f / (1 << src->coef_log2_denom);
cdst->method[i] = csrc->mapping_idc[i];
switch (csrc->mapping_idc[i]) {
case VLC_DOVI_RESHAPE_POLYNOMIAL:
for (size_t k = 0; k < ARRAY_SIZE(cdst->poly_coeffs[i]); k++) {
cdst->poly_coeffs[i][k] = (k <= csrc->poly_order[i])
? scale * csrc->poly_coef[i][k]
: 0.0f;
}
break;
case VLC_DOVI_RESHAPE_MMR:
cdst->mmr_order[i] = csrc->mmr_order[i];
cdst->mmr_constant[i] = scale * csrc->mmr_constant[i];
for (int j = 0; j < csrc->mmr_order[i]; j++) {
for (size_t k = 0; k < ARRAY_SIZE(cdst->mmr_coeffs[i][j]); k++)
cdst->mmr_coeffs[i][j][k] = scale * csrc->mmr_coef[i][j][k];
}
break;
}
}
}
}
void vlc_placebo_frame_DoviMetadata(struct pl_frame *frame, const picture_t *pic,
struct pl_dovi_metadata *dst)
{
struct vlc_ancillary *ancillary = picture_GetAncillary(pic, VLC_ANCILLARY_ID_DOVI);
if (!ancillary)
return;
const vlc_video_dovi_metadata_t *src = vlc_ancillary_GetData(ancillary);
vlc_placebo_DoviMetadata(src, dst);
// The output of the Dolby Vision reshaping process is always BT.2020/PQ,
// no matter the color space of the base layer, so override these fields
frame->color.primaries = PL_COLOR_PRIM_BT_2020;
frame->color.transfer = PL_COLOR_TRC_PQ;
frame->repr.sys = PL_COLOR_SYSTEM_DOLBYVISION;
frame->repr.dovi = dst;
// These fields are specified to always have 12-bit precision
const float scale = 1.0f / ((1 << 12) - 1);
frame->color.hdr.min_luma = pl_hdr_rescale(PL_HDR_PQ, PL_HDR_NITS,
scale * src->source_min_pq);
frame->color.hdr.max_luma = pl_hdr_rescale(PL_HDR_PQ, PL_HDR_NITS,
scale * src->source_max_pq);
}
enum pl_chroma_location vlc_placebo_ChromaLoc(const video_format_t *fmt)
{
static const enum pl_chroma_location locs[CHROMA_LOCATION_MAX+1] = {
[CHROMA_LOCATION_UNDEF] = PL_CHROMA_UNKNOWN,
[CHROMA_LOCATION_LEFT] = PL_CHROMA_LEFT,
[CHROMA_LOCATION_CENTER] = PL_CHROMA_CENTER,
[CHROMA_LOCATION_TOP_LEFT] = PL_CHROMA_TOP_LEFT,
[CHROMA_LOCATION_TOP_CENTER] = PL_CHROMA_TOP_CENTER,
[CHROMA_LOCATION_BOTTOM_LEFT] = PL_CHROMA_BOTTOM_LEFT,
[CHROMA_LOCATION_BOTTOM_CENTER] = PL_CHROMA_BOTTOM_CENTER,
};
return locs[fmt->chroma_location];
}
int vlc_placebo_PlaneComponents(const video_format_t *fmt,
struct pl_plane planes[4]) {
const struct fmt_desc *desc = FindDesc(fmt->i_chroma);
if (!desc)
return 0;
for (int i = 0; i < desc->num_planes; i++) {
const struct plane_desc *p = &desc->planes[i];
planes[i].components = p->components;
for (int c = 0; c < p->components; ++c)
planes[i].component_mapping[c] = p->comp_map[c];
}
return desc->num_planes;
}
void vlc_placebo_ColorMapParams(vlc_object_t *obj, const char *prefix,
struct pl_color_map_params *params)
{
#define PREFIX(str) (snprintf(opt, sizeof(opt), "%s-%s", prefix, str), opt)
char opt[64];
*params = pl_color_map_default_params;
switch (var_InheritInteger(obj, PREFIX("gamut-mapping"))) {
case GAMUT_AUTO: break;
#if PL_API_VER >= 269
case GAMUT_CLIP: params->gamut_mapping = &pl_gamut_map_clip; break;
case GAMUT_PERCEPTUAL: params->gamut_mapping = &pl_gamut_map_perceptual; break;
case GAMUT_RELATIVE: params->gamut_mapping = &pl_gamut_map_relative; break;
case GAMUT_SATURATION: params->gamut_mapping = &pl_gamut_map_saturation; break;
case GAMUT_ABSOLUTE: params->gamut_mapping = &pl_gamut_map_absolute; break;
case GAMUT_DESATURATE: params->gamut_mapping = &pl_gamut_map_desaturate; break;
case GAMUT_DARKEN: params->gamut_mapping = &pl_gamut_map_darken; break;
case GAMUT_WARN: params->gamut_mapping = &pl_gamut_map_highlight; break;
case GAMUT_LINEAR: params->gamut_mapping = &pl_gamut_map_linear; break;
#else
case GAMUT_CLIP: params->gamut_mode = PL_GAMUT_CLIP; break;
case GAMUT_PERCEPTUAL: break; // unsupported
case GAMUT_RELATIVE: params->intent = PL_INTENT_RELATIVE_COLORIMETRIC; break;
case GAMUT_SATURATION: params->intent = PL_INTENT_SATURATION; break;
case GAMUT_ABSOLUTE: params->intent = PL_INTENT_ABSOLUTE_COLORIMETRIC; break;
case GAMUT_DESATURATE: params->gamut_mode = PL_GAMUT_DESATURATE; break;
case GAMUT_DARKEN: params->gamut_mode = PL_GAMUT_DARKEN; break;
case GAMUT_WARN: params->gamut_mode = PL_GAMUT_WARN; break;
case GAMUT_LINEAR: break; // unsupported
#endif
}
switch (var_InheritInteger(obj, PREFIX("tone-mapping-function"))) {
case TONEMAP_AUTO: break;
case TONEMAP_CLIP: params->tone_mapping_function = &pl_tone_map_clip; break;
case TONEMAP_BT2390: params->tone_mapping_function = &pl_tone_map_bt2390; break;
case TONEMAP_REINHARD: params->tone_mapping_function = &pl_tone_map_reinhard; break;
case TONEMAP_MOBIUS: params->tone_mapping_function = &pl_tone_map_mobius; break;
case TONEMAP_HABLE: params->tone_mapping_function = &pl_tone_map_hable; break;
case TONEMAP_GAMMA: params->tone_mapping_function = &pl_tone_map_gamma; break;
case TONEMAP_LINEAR: params->tone_mapping_function = &pl_tone_map_linear; break;
case TONEMAP_BT2446A: params->tone_mapping_function = &pl_tone_map_bt2446a; break;
case TONEMAP_SPLINE: params->tone_mapping_function = &pl_tone_map_spline; break;
}
params->tone_mapping_param = var_InheritFloat(obj, PREFIX("tone-mapping-param"));
params->inverse_tone_mapping = var_InheritBool(obj, PREFIX("inverse-tone-mapping"));
}