ref:c59469b77902077aa3f975f406ed9484e2c6c29b

fix(video): fall back to SDR when HDR isn't supported (#5429)

Co-authored-by: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com>
SHA: c59469b77902077aa3f975f406ed9484e2c6c29b
Author: matix753 <33520765+matix753@users.noreply.github.com>
Date: 2026-09-01 16:56
Parents: 6a1d9d4
3 files changed +100 -1
Type
src/video.cpp +29 −1
@@ -161,6 +161,32 @@
}
/**
* @brief Resolve a client-requested dynamic range against probed encoder capabilities.
*
* @param encoder Selected encoder and its probed codec capabilities.
* @param config Client-requested stream configuration.
* @return Effective stream configuration, downgraded to SDR when HDR is unsupported.
*/
config_t resolve_dynamic_range(const encoder_t &encoder, config_t config) {
if (!config.dynamicRange) {
return config;
}
const auto &video_format = encoder.codec_from_config(config);
const auto capability = config.chromaSamplingType == 1 ?
encoder_t::DYNAMIC_RANGE_YUV444 :
encoder_t::DYNAMIC_RANGE;
if (video_format[capability]) {
return config;
}
const auto mode = config.chromaSamplingType == 1 ? "YUV 4:4:4 dynamic range"sv : "dynamic range"sv;
BOOST_LOG(warning) << video_format.name << ": "sv << mode << " not supported, falling back to SDR"sv;
config.dynamicRange = 0;
return config;
}
/**
* @brief Create an FFmpeg hardware device buffer for D3D11VA input.
*
* @param encode_device Encode device.
@@ -2905,7 +2931,7 @@
* @brief Capture and encode video for a streaming session.
*
* @param mail Session mail bus.
* @param config Client-requested video configuration, normalized before capture begins.
* @param config Video configuration.
* @param channel_data Opaque channel data passed to packets.
*/
void capture(
@@ -2913,6 +2939,8 @@
config_t config,
void *channel_data
) {
config = resolve_dynamic_range(*chosen_encoder, config);
auto idr_events = mail->event<bool>(mail::idr);
idr_events->raise(true);
src/video.h +16 −0
@@ -683,6 +683,22 @@
extern bool last_encoder_probe_supported_ref_frames_invalidation;
extern std::array<bool, 3> last_encoder_probe_supported_yuv444_for_codec; // 0 - H.264, 1 - HEVC, 2 - AV1
/**
* @brief Resolve a client-requested dynamic range against probed encoder capabilities.
*
* @param encoder Selected encoder and its probed codec capabilities.
* @param config Client-requested stream configuration.
* @return Effective stream configuration, downgraded to SDR when HDR is unsupported.
*/
config_t resolve_dynamic_range(const encoder_t &encoder, config_t config);
/**
* @brief Capture and encode video for a streaming session.
*
* @param mail Session mail bus.
* @param config Client-requested video configuration, normalized before capture begins.
* @param channel_data Opaque channel data passed to packets.
*/
void capture(
safe::mail_t mail,
config_t config,
tests/unit/test_video.cpp +55 −0
@@ -94,6 +94,61 @@
)
);
/**
* @brief Parameterized coverage for resolving requested dynamic range against encoder capabilities.
*/
struct DynamicRangeTest: testing::TestWithParam<std::tuple<int, int, int, bool, bool, int>> {};
TEST_P(DynamicRangeTest, Resolve) {
const auto &[video_format, chroma_sampling_type, requested_dynamic_range, supports_hdr, supports_hdr_yuv444, expected_dynamic_range] = GetParam();
video::encoder_t encoder {
"test"sv,
{},
{},
{},
{},
0,
};
encoder.h264.name = "h264_test";
encoder.hevc.name = "hevc_test";
encoder.av1.name = "av1_test";
video::config_t config {};
config.videoFormat = video_format;
config.dynamicRange = requested_dynamic_range;
config.chromaSamplingType = chroma_sampling_type;
auto *codec = &encoder.h264;
if (video_format == 1) {
codec = &encoder.hevc;
} else if (video_format == 2) {
codec = &encoder.av1;
}
(*codec)[video::encoder_t::DYNAMIC_RANGE] = supports_hdr;
(*codec)[video::encoder_t::DYNAMIC_RANGE_YUV444] = supports_hdr_yuv444;
const auto effective_config = video::resolve_dynamic_range(encoder, config);
EXPECT_EQ(expected_dynamic_range, effective_config.dynamicRange);
EXPECT_EQ(requested_dynamic_range, config.dynamicRange);
EXPECT_EQ(video_format, effective_config.videoFormat);
EXPECT_EQ(chroma_sampling_type, effective_config.chromaSamplingType);
}
INSTANTIATE_TEST_SUITE_P(
DynamicRangeTests,
DynamicRangeTest,
testing::Values(
std::make_tuple(0, 0, 1, false, true, 0),
std::make_tuple(1, 0, 1, false, true, 0),
std::make_tuple(1, 0, 1, true, false, 1),
std::make_tuple(2, 1, 1, true, false, 0),
std::make_tuple(2, 1, 1, false, true, 1),
std::make_tuple(1, 0, 0, false, false, 0)
)
);
#ifdef _WIN32
TEST(AmfH264OptionsTest, CoderUsesConfiguredValue) {
const auto coder_option = std::ranges::find(video::amdvce.h264.common_options, "coder"sv, &video::encoder_t::option_t::name);