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