Sunshine/src/video_colorspace.cpp

255 lines
8.5 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* @file src/video_colorspace.cpp
* @brief Definitions for colorspace functions.
*/
// this include
#include "video_colorspace.h"
// local includes
#include "logging.h"
#include "video.h"
extern "C" {
#include <libswscale/swscale.h>
}
namespace video {
bool colorspace_is_hdr(const sunshine_colorspace_t &colorspace) {
return colorspace.colorspace == colorspace_e::bt2020;
}
sunshine_colorspace_t colorspace_from_client_config(const config_t &config, bool hdr_display) {
sunshine_colorspace_t colorspace;
/* See video::config_t declaration for details */
if (config.dynamicRange > 0 && hdr_display) {
// Rec. 2020 with ST 2084 perceptual quantizer
colorspace.colorspace = colorspace_e::bt2020;
} else {
switch (config.encoderCscMode >> 1) {
case 0:
// Rec. 601
colorspace.colorspace = colorspace_e::rec601;
break;
case 1:
// Rec. 709
colorspace.colorspace = colorspace_e::rec709;
break;
case 2:
// Rec. 2020
colorspace.colorspace = colorspace_e::bt2020sdr;
break;
default:
BOOST_LOG(error) << "Unknown video colorspace in csc, falling back to Rec. 709";
colorspace.colorspace = colorspace_e::rec709;
break;
}
}
colorspace.full_range = (config.encoderCscMode & 0x1);
switch (config.dynamicRange) {
case 0:
colorspace.bit_depth = 8;
break;
case 1:
colorspace.bit_depth = 10;
break;
default:
BOOST_LOG(error) << "Unknown dynamicRange value, falling back to 10-bit color depth";
colorspace.bit_depth = 10;
break;
}
if (colorspace.colorspace == colorspace_e::bt2020sdr && colorspace.bit_depth != 10) {
BOOST_LOG(error) << "BT.2020 SDR colorspace expects 10-bit color depth, falling back to Rec. 709";
colorspace.colorspace = colorspace_e::rec709;
}
return colorspace;
}
avcodec_colorspace_t avcodec_colorspace_from_sunshine_colorspace(const sunshine_colorspace_t &sunshine_colorspace) {
avcodec_colorspace_t avcodec_colorspace;
switch (sunshine_colorspace.colorspace) {
case colorspace_e::rec601:
// Rec. 601
avcodec_colorspace.primaries = AVCOL_PRI_SMPTE170M;
avcodec_colorspace.transfer_function = AVCOL_TRC_SMPTE170M;
avcodec_colorspace.matrix = AVCOL_SPC_SMPTE170M;
avcodec_colorspace.software_format = SWS_CS_SMPTE170M;
break;
case colorspace_e::rec709:
// Rec. 709
avcodec_colorspace.primaries = AVCOL_PRI_BT709;
avcodec_colorspace.transfer_function = AVCOL_TRC_BT709;
avcodec_colorspace.matrix = AVCOL_SPC_BT709;
avcodec_colorspace.software_format = SWS_CS_ITU709;
break;
case colorspace_e::bt2020sdr:
// Rec. 2020
avcodec_colorspace.primaries = AVCOL_PRI_BT2020;
assert(sunshine_colorspace.bit_depth == 10);
avcodec_colorspace.transfer_function = AVCOL_TRC_BT2020_10;
avcodec_colorspace.matrix = AVCOL_SPC_BT2020_NCL;
avcodec_colorspace.software_format = SWS_CS_BT2020;
break;
case colorspace_e::bt2020:
// Rec. 2020 with ST 2084 perceptual quantizer
avcodec_colorspace.primaries = AVCOL_PRI_BT2020;
assert(sunshine_colorspace.bit_depth == 10);
avcodec_colorspace.transfer_function = AVCOL_TRC_SMPTE2084;
avcodec_colorspace.matrix = AVCOL_SPC_BT2020_NCL;
avcodec_colorspace.software_format = SWS_CS_BT2020;
break;
}
avcodec_colorspace.range = sunshine_colorspace.full_range ? AVCOL_RANGE_JPEG : AVCOL_RANGE_MPEG;
return avcodec_colorspace;
}
const color_t *color_vectors_from_colorspace(const sunshine_colorspace_t &colorspace, bool unorm_output) {
constexpr auto generate_color_vectors = [](const sunshine_colorspace_t &colorspace, bool unorm_output) -> color_t {
// "Table 4 Interpretation of matrix coefficients (MatrixCoefficients) value" section of ITU-T H.273
double Kr, Kb;
switch (colorspace.colorspace) {
case colorspace_e::rec601:
Kr = 0.299;
Kb = 0.114;
break;
case colorspace_e::rec709:
default:
Kr = 0.2126;
Kb = 0.0722;
break;
case colorspace_e::bt2020:
case colorspace_e::bt2020sdr:
Kr = 0.2627;
Kb = 0.0593;
break;
}
double Kg = 1.0 - Kr - Kb;
double y_mult, y_add;
double uv_mult, uv_add;
// "8.3 Matrix coefficients" section of ITU-T H.273
if (colorspace.full_range) {
y_mult = (1 << colorspace.bit_depth) - 1;
y_add = 0;
uv_mult = (1 << colorspace.bit_depth) - 1;
uv_add = (1 << (colorspace.bit_depth - 1));
} else {
y_mult = (1 << (colorspace.bit_depth - 8)) * 219;
y_add = (1 << (colorspace.bit_depth - 8)) * 16;
uv_mult = (1 << (colorspace.bit_depth - 8)) * 224;
uv_add = (1 << (colorspace.bit_depth - 8)) * 128;
}
if (unorm_output) {
const double unorm_range = (1 << colorspace.bit_depth) - 1;
y_mult /= unorm_range;
y_add /= unorm_range;
uv_mult /= unorm_range;
uv_add /= unorm_range;
} else {
// For rounding
y_add += 0.5f;
uv_add += 0.5f;
}
color_t color_vectors;
color_vectors.color_vec_y[0] = Kr * y_mult;
color_vectors.color_vec_y[1] = Kg * y_mult;
color_vectors.color_vec_y[2] = Kb * y_mult;
color_vectors.color_vec_y[3] = y_add;
color_vectors.color_vec_u[0] = -0.5 * Kr / (1.0 - Kb) * uv_mult;
color_vectors.color_vec_u[1] = -0.5 * Kg / (1.0 - Kb) * uv_mult;
color_vectors.color_vec_u[2] = 0.5 * uv_mult;
color_vectors.color_vec_u[3] = uv_add;
color_vectors.color_vec_v[0] = 0.5 * uv_mult;
color_vectors.color_vec_v[1] = -0.5 * Kg / (1.0 - Kr) * uv_mult;
color_vectors.color_vec_v[2] = -0.5 * Kb / (1.0 - Kr) * uv_mult;
color_vectors.color_vec_v[3] = uv_add;
// Unused
color_vectors.range_y[0] = 1;
color_vectors.range_y[1] = 0;
color_vectors.range_uv[0] = 1;
color_vectors.range_uv[1] = 0;
return color_vectors;
};
static constexpr color_t colors[] = {
generate_color_vectors({colorspace_e::rec601, false, 8}, false),
generate_color_vectors({colorspace_e::rec601, true, 8}, false),
generate_color_vectors({colorspace_e::rec601, false, 10}, false),
generate_color_vectors({colorspace_e::rec601, true, 10}, false),
generate_color_vectors({colorspace_e::rec709, false, 8}, false),
generate_color_vectors({colorspace_e::rec709, true, 8}, false),
generate_color_vectors({colorspace_e::rec709, false, 10}, false),
generate_color_vectors({colorspace_e::rec709, true, 10}, false),
generate_color_vectors({colorspace_e::bt2020, false, 8}, false),
generate_color_vectors({colorspace_e::bt2020, true, 8}, false),
generate_color_vectors({colorspace_e::bt2020, false, 10}, false),
generate_color_vectors({colorspace_e::bt2020, true, 10}, false),
generate_color_vectors({colorspace_e::rec601, false, 8}, true),
generate_color_vectors({colorspace_e::rec601, true, 8}, true),
generate_color_vectors({colorspace_e::rec601, false, 10}, true),
generate_color_vectors({colorspace_e::rec601, true, 10}, true),
generate_color_vectors({colorspace_e::rec709, false, 8}, true),
generate_color_vectors({colorspace_e::rec709, true, 8}, true),
generate_color_vectors({colorspace_e::rec709, false, 10}, true),
generate_color_vectors({colorspace_e::rec709, true, 10}, true),
generate_color_vectors({colorspace_e::bt2020, false, 8}, true),
generate_color_vectors({colorspace_e::bt2020, true, 8}, true),
generate_color_vectors({colorspace_e::bt2020, false, 10}, true),
generate_color_vectors({colorspace_e::bt2020, true, 10}, true),
};
const color_t *result = nullptr;
switch (colorspace.colorspace) {
case colorspace_e::rec601:
result = &colors[0];
break;
case colorspace_e::rec709:
default:
result = &colors[4];
break;
case colorspace_e::bt2020:
case colorspace_e::bt2020sdr:
result = &colors[8];
break;
}
if (colorspace.bit_depth == 10) {
result += 2;
}
if (colorspace.full_range) {
result += 1;
}
if (unorm_output) {
result += 12;
}
return result;
}
} // namespace video