Skip to content

Commit

Permalink
Initial implementation of yuv444in420 encoding
Browse files Browse the repository at this point in the history
  • Loading branch information
ns6089 committed Jul 30, 2024
1 parent db86523 commit 2cc6a6a
Show file tree
Hide file tree
Showing 29 changed files with 521 additions and 82 deletions.
17 changes: 13 additions & 4 deletions src/nvenc/nvenc_base.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,16 @@ namespace nvenc {
if (encoder) destroy_encoder();
auto fail_guard = util::fail_guard([this] { destroy_encoder(); });

encoder_params.width = client_config.width;
encoder_params.height = client_config.height;
if (client_config.chromaSamplingType == 2) {
// YUV 4:4:4 recombined into YUV 4:2:0
auto recombined_dimensions = video::get_yuv444in420_dimensions(client_config.width, client_config.height);
encoder_params.width = recombined_dimensions.width;
encoder_params.height = recombined_dimensions.height;
}
else {
encoder_params.width = client_config.width;
encoder_params.height = client_config.height;
}
encoder_params.buffer_format = buffer_format;
encoder_params.rfi = true;

Expand Down Expand Up @@ -288,7 +296,7 @@ namespace nvenc {
vui_config.colourPrimaries = colorspace.primaries;
vui_config.transferCharacteristics = colorspace.tranfer_function;
vui_config.colourMatrix = colorspace.matrix;
vui_config.chromaSampleLocationFlag = buffer_is_yuv444() ? 0 : 1;
vui_config.chromaSampleLocationFlag = (client_config.chromaSamplingType == 0) ? 1 : 0;
vui_config.chromaSampleLocationTop = 0;
vui_config.chromaSampleLocationBot = 0;
};
Expand Down Expand Up @@ -341,7 +349,7 @@ namespace nvenc {
format_config.transferCharacteristics = colorspace.tranfer_function;
format_config.matrixCoefficients = colorspace.matrix;
format_config.colorRange = colorspace.full_range;
format_config.chromaSamplePosition = buffer_is_yuv444() ? 0 : 1;
format_config.chromaSamplePosition = (client_config.chromaSamplingType == 0) ? 1 : 0;
set_ref_frames(format_config.maxNumRefFramesInDPB, format_config.numFwdRefs, 8);
set_minqp_if_enabled(config.min_qp_av1);

Expand Down Expand Up @@ -395,6 +403,7 @@ namespace nvenc {
std::string extra;
if (init_params.enableEncodeAsync) extra += " async";
if (buffer_is_yuv444()) extra += " yuv444";
if (client_config.chromaSamplingType == 2) extra += " yuv444in420";
if (buffer_is_10bit()) extra += " 10-bit";
if (enc_config.rcParams.multiPass != NV_ENC_MULTI_PASS_DISABLED) extra += " two-pass";
if (config.vbv_percentage_increase > 0 && get_encoder_cap(NV_ENC_CAPS_SUPPORT_CUSTOM_VBV_BUF_SIZE)) extra += " vbv+" + std::to_string(config.vbv_percentage_increase);
Expand Down
15 changes: 15 additions & 0 deletions src/nvhttp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -722,30 +722,45 @@ namespace nvhttp {
uint32_t codec_mode_flags = SCM_H264;
if (video::last_encoder_probe_supported_yuv444_for_codec[0]) {
codec_mode_flags |= SCM_H264_HIGH8_444;
if (video::last_encoder_probe_supported_yuv444in420) {
codec_mode_flags |= SCM_H264_HIGH8_444IN420;
}
}
if (video::active_hevc_mode >= 2) {
codec_mode_flags |= SCM_HEVC;
if (video::last_encoder_probe_supported_yuv444_for_codec[1]) {
codec_mode_flags |= SCM_HEVC_REXT8_444;
}
if (video::last_encoder_probe_supported_yuv444in420) {
codec_mode_flags |= SCM_HEVC_MAIN8_444IN420;
}
}
if (video::active_hevc_mode >= 3) {
codec_mode_flags |= SCM_HEVC_MAIN10;
if (video::last_encoder_probe_supported_yuv444_for_codec[1]) {
codec_mode_flags |= SCM_HEVC_REXT10_444;
}
if (video::last_encoder_probe_supported_yuv444in420) {
codec_mode_flags |= SCM_HEVC_MAIN10_444IN420;
}
}
if (video::active_av1_mode >= 2) {
codec_mode_flags |= SCM_AV1_MAIN8;
if (video::last_encoder_probe_supported_yuv444_for_codec[2]) {
codec_mode_flags |= SCM_AV1_HIGH8_444;
}
if (video::last_encoder_probe_supported_yuv444in420) {
codec_mode_flags |= SCM_AV1_MAIN8_444IN420;
}
}
if (video::active_av1_mode >= 3) {
codec_mode_flags |= SCM_AV1_MAIN10;
if (video::last_encoder_probe_supported_yuv444_for_codec[2]) {
codec_mode_flags |= SCM_AV1_HIGH10_444;
}
if (video::last_encoder_probe_supported_yuv444in420) {
codec_mode_flags |= SCM_AV1_MAIN10_444IN420;
}
}
tree.put("root.ServerCodecModeSupport", codec_mode_flags);

Expand Down
25 changes: 23 additions & 2 deletions src/platform/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -483,13 +483,25 @@ namespace platf {
virtual int
dummy_img(img_t *img) = 0;

/**
* @brief Create AVCodec encode device.
* @param pix_fmt_e Surface format of the encoder.
* @param yuv444in420 Whether YUV 4:4:4 recombination into YUV 4:2:0 must be performed.
* @return `unique_ptr` with `avcodec_encode_device_t` implementation on success, `nullptr` on failure.
*/
virtual std::unique_ptr<avcodec_encode_device_t>
make_avcodec_encode_device(pix_fmt_e pix_fmt) {
make_avcodec_encode_device(pix_fmt_e pix_fmt, bool yuv444in420) {
return nullptr;
}

/**
* @brief Create NVENC encode device.
* @param pix_fmt_e Surface format of the encoder.
* @param yuv444in420 Whether YUV 4:4:4 recombination into YUV 4:2:0 must be performed.
* @return `unique_ptr` with `nvenc_encode_device_t` implementation on success, `nullptr` on failure.
*/
virtual std::unique_ptr<nvenc_encode_device_t>
make_nvenc_encode_device(pix_fmt_e pix_fmt) {
make_nvenc_encode_device(pix_fmt_e pix_fmt, bool yuv444in420) {
return nullptr;
}

Expand All @@ -515,6 +527,15 @@ namespace platf {
return true;
}

/**
* @brief Check if YUV 4:4:4 recombination into YUV 4:2:0 is supported by the display device.
* @return `true` if supported, `false` otherwise.
*/
virtual bool
is_yuv444in420_supported() {
return false;
}

virtual ~display_t() = default;

// Offsets for when streaming a specific monitor. By default, they are 0.
Expand Down
7 changes: 6 additions & 1 deletion src/platform/linux/cuda.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -960,7 +960,12 @@ namespace cuda {
}

std::unique_ptr<platf::avcodec_encode_device_t>
make_avcodec_encode_device(platf::pix_fmt_e pix_fmt) {
make_avcodec_encode_device(platf::pix_fmt_e pix_fmt, bool yuv444in420) {
if (yuv444in420) {
BOOST_LOG(error) << "Recombined YUV 4:4:4 is not supported";
return nullptr;
}

return ::cuda::make_avcodec_encode_device(width, height, true);
}

Expand Down
14 changes: 12 additions & 2 deletions src/platform/linux/kmsgrab.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1236,7 +1236,12 @@ namespace platf {
}

std::unique_ptr<avcodec_encode_device_t>
make_avcodec_encode_device(pix_fmt_e pix_fmt) override {
make_avcodec_encode_device(pix_fmt_e pix_fmt, bool yuv444in420) override {
if (yuv444in420) {
BOOST_LOG(error) << "Recombined YUV 4:4:4 is not supported";
return nullptr;
}

#ifdef SUNSHINE_BUILD_VAAPI
if (mem_type == mem_type_e::vaapi) {
return va::make_avcodec_encode_device(width, height, false);
Expand Down Expand Up @@ -1373,7 +1378,12 @@ namespace platf {
display_t(mem_type) {}

std::unique_ptr<avcodec_encode_device_t>
make_avcodec_encode_device(pix_fmt_e pix_fmt) override {
make_avcodec_encode_device(pix_fmt_e pix_fmt, bool yuv444in420) override {
if (yuv444in420) {
BOOST_LOG(error) << "Recombined YUV 4:4:4 is not supported";
return nullptr;
}

#ifdef SUNSHINE_BUILD_VAAPI
if (mem_type == mem_type_e::vaapi) {
return va::make_avcodec_encode_device(width, height, dup(card.render_fd.el), img_offset_x, img_offset_y, true);
Expand Down
14 changes: 12 additions & 2 deletions src/platform/linux/wlgrab.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,12 @@ namespace wl {
}

std::unique_ptr<platf::avcodec_encode_device_t>
make_avcodec_encode_device(platf::pix_fmt_e pix_fmt) override {
make_avcodec_encode_device(platf::pix_fmt_e pix_fmt, bool yuv444in420) override {
if (yuv444in420) {
BOOST_LOG(error) << "Recombined YUV 4:4:4 is not supported";
return nullptr;
}

#ifdef SUNSHINE_BUILD_VAAPI
if (mem_type == platf::mem_type_e::vaapi) {
return va::make_avcodec_encode_device(width, height, false);
Expand Down Expand Up @@ -349,7 +354,12 @@ namespace wl {
}

std::unique_ptr<platf::avcodec_encode_device_t>
make_avcodec_encode_device(platf::pix_fmt_e pix_fmt) override {
make_avcodec_encode_device(platf::pix_fmt_e pix_fmt, bool yuv444in420) override {
if (yuv444in420) {
BOOST_LOG(error) << "Recombined YUV 4:4:4 is not supported";
return nullptr;
}

#ifdef SUNSHINE_BUILD_VAAPI
if (mem_type == platf::mem_type_e::vaapi) {
return va::make_avcodec_encode_device(width, height, 0, 0, true);
Expand Down
7 changes: 6 additions & 1 deletion src/platform/linux/x11grab.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -561,7 +561,12 @@ namespace platf {
}

std::unique_ptr<avcodec_encode_device_t>
make_avcodec_encode_device(pix_fmt_e pix_fmt) override {
make_avcodec_encode_device(pix_fmt_e pix_fmt, bool yuv444in420) override {
if (yuv444in420) {
BOOST_LOG(error) << "Recombined YUV 4:4:4 is not supported";
return nullptr;
}

#ifdef SUNSHINE_BUILD_VAAPI
if (mem_type == mem_type_e::vaapi) {
return va::make_avcodec_encode_device(width, height, false);
Expand Down
7 changes: 6 additions & 1 deletion src/platform/macos/display.mm
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,12 @@
}

std::unique_ptr<avcodec_encode_device_t>
make_avcodec_encode_device(pix_fmt_e pix_fmt) override {
make_avcodec_encode_device(pix_fmt_e pix_fmt, bool yuv444in420) {
if (yuv444in420) {
BOOST_LOG(error) << "Recombined YUV 4:4:4 is not supported";
return nullptr;
}

if (pix_fmt == pix_fmt_e::yuv420p) {
av_capture.pixelFormat = kCVPixelFormatType_32BGRA;

Expand Down
11 changes: 8 additions & 3 deletions src/platform/windows/display.h
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ namespace platf::dxgi {
get_supported_capture_formats() override;

std::unique_ptr<avcodec_encode_device_t>
make_avcodec_encode_device(pix_fmt_e pix_fmt) override;
make_avcodec_encode_device(pix_fmt_e pix_fmt, bool yuv444in420) override;

D3D11_MAPPED_SUBRESOURCE img_info;
texture2d_t texture;
Expand All @@ -295,11 +295,16 @@ namespace platf::dxgi {
bool
is_codec_supported(std::string_view name, const ::video::config_t &config) override;

bool
is_yuv444in420_supported() override {
return true;
}

std::unique_ptr<avcodec_encode_device_t>
make_avcodec_encode_device(pix_fmt_e pix_fmt) override;
make_avcodec_encode_device(pix_fmt_e pix_fmt, bool yuv444in420) override;

std::unique_ptr<nvenc_encode_device_t>
make_nvenc_encode_device(pix_fmt_e pix_fmt) override;
make_nvenc_encode_device(pix_fmt_e pix_fmt, bool yuv444in420) override;

std::atomic<uint32_t> next_image_id;
};
Expand Down
6 changes: 5 additions & 1 deletion src/platform/windows/display_ram.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,11 @@ namespace platf::dxgi {
}

std::unique_ptr<avcodec_encode_device_t>
display_ram_t::make_avcodec_encode_device(pix_fmt_e pix_fmt) {
display_ram_t::make_avcodec_encode_device(pix_fmt_e pix_fmt, bool yuv444in420) {
if (yuv444in420) {
BOOST_LOG(error) << "Recombined YUV 4:4:4 is not supported";
return nullptr;
}
return std::make_unique<avcodec_encode_device_t>();
}

Expand Down
Loading

0 comments on commit 2cc6a6a

Please sign in to comment.