mirror of
https://github.com/thinkonmay/sunshine-sdk.git
synced 2025-12-28 16:29:39 +00:00
Implement AV1 support
This commit is contained in:
parent
67c1fa6da7
commit
69e720b44b
@ -810,6 +810,37 @@ hevc_mode
|
||||
|
||||
hevc_mode = 2
|
||||
|
||||
av1_mode
|
||||
^^^^^^^^^
|
||||
|
||||
**Description**
|
||||
Allows the client to request AV1 Main 8-bit or 10-bit video streams.
|
||||
|
||||
.. Warning:: AV1 is more CPU-intensive to encode, so enabling this may reduce performance when using software
|
||||
encoding.
|
||||
|
||||
**Choices**
|
||||
|
||||
.. table::
|
||||
:widths: auto
|
||||
|
||||
===== ===========
|
||||
Value Description
|
||||
===== ===========
|
||||
0 advertise support for AV1 based on encoder
|
||||
1 do not advertise support for AV1
|
||||
2 advertise support for AV1 Main 8-bit profile
|
||||
3 advertise support for AV1 Main 8-bit and 10-bit (HDR) profiles
|
||||
===== ===========
|
||||
|
||||
**Default**
|
||||
``0``
|
||||
|
||||
**Example**
|
||||
.. code-block:: text
|
||||
|
||||
av1_mode = 2
|
||||
|
||||
capture
|
||||
^^^^^^^
|
||||
|
||||
|
||||
@ -130,12 +130,19 @@ namespace config {
|
||||
namespace amd {
|
||||
#ifdef __APPLE__
|
||||
// values accurate as of 27/12/2022, but aren't strictly necessary for MacOS build
|
||||
#define AMF_VIDEO_ENCODER_AV1_QUALITY_PRESET_SPEED 100
|
||||
#define AMF_VIDEO_ENCODER_AV1_QUALITY_PRESET_QUALITY 30
|
||||
#define AMF_VIDEO_ENCODER_AV1_QUALITY_PRESET_BALANCED 70
|
||||
#define AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET_SPEED 10
|
||||
#define AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET_QUALITY 0
|
||||
#define AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET_BALANCED 5
|
||||
#define AMF_VIDEO_ENCODER_QUALITY_PRESET_SPEED 1
|
||||
#define AMF_VIDEO_ENCODER_QUALITY_PRESET_QUALITY 2
|
||||
#define AMF_VIDEO_ENCODER_QUALITY_PRESET_BALANCED 0
|
||||
#define AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_CONSTANT_QP 0
|
||||
#define AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_CBR 3
|
||||
#define AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_PEAK_CONSTRAINED_VBR 2
|
||||
#define AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_LATENCY_CONSTRAINED_VBR 1
|
||||
#define AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_CONSTANT_QP 0
|
||||
#define AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_CBR 3
|
||||
#define AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_PEAK_CONSTRAINED_VBR 2
|
||||
@ -144,6 +151,10 @@ namespace config {
|
||||
#define AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_CBR 1
|
||||
#define AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_PEAK_CONSTRAINED_VBR 2
|
||||
#define AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_LATENCY_CONSTRAINED_VBR 3
|
||||
#define AMF_VIDEO_ENCODER_AV1_USAGE_TRANSCODING 0
|
||||
#define AMF_VIDEO_ENCODER_AV1_USAGE_LOW_LATENCY 1
|
||||
#define AMF_VIDEO_ENCODER_AV1_USAGE_ULTRA_LOW_LATENCY 2
|
||||
#define AMF_VIDEO_ENCODER_AV1_USAGE_WEBCAM 3
|
||||
#define AMF_VIDEO_ENCODER_HEVC_USAGE_TRANSCONDING 0
|
||||
#define AMF_VIDEO_ENCODER_HEVC_USAGE_ULTRA_LOW_LATENCY 1
|
||||
#define AMF_VIDEO_ENCODER_HEVC_USAGE_LOW_LATENCY 2
|
||||
@ -156,10 +167,17 @@ namespace config {
|
||||
#define AMF_VIDEO_ENCODER_CABAC 1
|
||||
#define AMF_VIDEO_ENCODER_CALV 2
|
||||
#else
|
||||
#include <AMF/components/VideoEncoderAV1.h>
|
||||
#include <AMF/components/VideoEncoderHEVC.h>
|
||||
#include <AMF/components/VideoEncoderVCE.h>
|
||||
#endif
|
||||
|
||||
enum class quality_av1_e : int {
|
||||
speed = AMF_VIDEO_ENCODER_AV1_QUALITY_PRESET_SPEED,
|
||||
quality = AMF_VIDEO_ENCODER_AV1_QUALITY_PRESET_QUALITY,
|
||||
balanced = AMF_VIDEO_ENCODER_AV1_QUALITY_PRESET_BALANCED
|
||||
};
|
||||
|
||||
enum class quality_hevc_e : int {
|
||||
speed = AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET_SPEED,
|
||||
quality = AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET_QUALITY,
|
||||
@ -172,6 +190,13 @@ namespace config {
|
||||
balanced = AMF_VIDEO_ENCODER_QUALITY_PRESET_BALANCED
|
||||
};
|
||||
|
||||
enum class rc_av1_e : int {
|
||||
cqp = AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_CONSTANT_QP,
|
||||
vbr_latency = AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_LATENCY_CONSTRAINED_VBR,
|
||||
vbr_peak = AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_PEAK_CONSTRAINED_VBR,
|
||||
cbr = AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_CBR
|
||||
};
|
||||
|
||||
enum class rc_hevc_e : int {
|
||||
cqp = AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_CONSTANT_QP,
|
||||
vbr_latency = AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_LATENCY_CONSTRAINED_VBR,
|
||||
@ -186,6 +211,13 @@ namespace config {
|
||||
cbr = AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_CBR
|
||||
};
|
||||
|
||||
enum class usage_av1_e : int {
|
||||
transcoding = AMF_VIDEO_ENCODER_AV1_USAGE_TRANSCODING,
|
||||
webcam = AMF_VIDEO_ENCODER_AV1_USAGE_WEBCAM,
|
||||
lowlatency = AMF_VIDEO_ENCODER_AV1_USAGE_LOW_LATENCY,
|
||||
ultralowlatency = AMF_VIDEO_ENCODER_AV1_USAGE_ULTRA_LOW_LATENCY
|
||||
};
|
||||
|
||||
enum class usage_hevc_e : int {
|
||||
transcoding = AMF_VIDEO_ENCODER_HEVC_USAGE_TRANSCONDING,
|
||||
webcam = AMF_VIDEO_ENCODER_HEVC_USAGE_WEBCAM,
|
||||
@ -206,10 +238,11 @@ namespace config {
|
||||
cavlc = AMF_VIDEO_ENCODER_CALV
|
||||
};
|
||||
|
||||
template <class T>
|
||||
std::optional<int>
|
||||
quality_from_view(const std::string_view &quality_type, int codec) {
|
||||
quality_from_view(const std::string_view &quality_type) {
|
||||
#define _CONVERT_(x) \
|
||||
if (quality_type == #x##sv) return codec == 0 ? (int) quality_hevc_e::x : (int) quality_h264_e::x
|
||||
if (quality_type == #x##sv) return (int) T::x
|
||||
_CONVERT_(quality);
|
||||
_CONVERT_(speed);
|
||||
_CONVERT_(balanced);
|
||||
@ -217,10 +250,11 @@ namespace config {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
std::optional<int>
|
||||
rc_from_view(const std::string_view &rc, int codec) {
|
||||
rc_from_view(const std::string_view &rc) {
|
||||
#define _CONVERT_(x) \
|
||||
if (rc == #x##sv) return codec == 0 ? (int) rc_hevc_e::x : (int) rc_h264_e::x
|
||||
if (rc == #x##sv) return (int) T::x
|
||||
_CONVERT_(cqp);
|
||||
_CONVERT_(vbr_latency);
|
||||
_CONVERT_(vbr_peak);
|
||||
@ -229,10 +263,11 @@ namespace config {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
std::optional<int>
|
||||
usage_from_view(const std::string_view &rc, int codec) {
|
||||
usage_from_view(const std::string_view &rc) {
|
||||
#define _CONVERT_(x) \
|
||||
if (rc == #x##sv) return codec == 0 ? (int) usage_hevc_e::x : (int) usage_h264_e::x
|
||||
if (rc == #x##sv) return (int) T::x
|
||||
_CONVERT_(transcoding);
|
||||
_CONVERT_(webcam);
|
||||
_CONVERT_(lowlatency);
|
||||
@ -333,15 +368,36 @@ namespace config {
|
||||
|
||||
} // namespace vt
|
||||
|
||||
namespace sw {
|
||||
int
|
||||
svtav1_preset_from_view(const std::string_view &preset) {
|
||||
#define _CONVERT_(x, y) \
|
||||
if (preset == #x##sv) return y
|
||||
_CONVERT_(veryslow, 1);
|
||||
_CONVERT_(slower, 2);
|
||||
_CONVERT_(slow, 4);
|
||||
_CONVERT_(medium, 5);
|
||||
_CONVERT_(fast, 7);
|
||||
_CONVERT_(faster, 9);
|
||||
_CONVERT_(veryfast, 10);
|
||||
_CONVERT_(superfast, 11);
|
||||
_CONVERT_(ultrafast, 12);
|
||||
#undef _CONVERT_
|
||||
return 11; // Default to superfast
|
||||
}
|
||||
} // namespace sw
|
||||
|
||||
video_t video {
|
||||
28, // qp
|
||||
|
||||
0, // hevc_mode
|
||||
0, // av1_mode
|
||||
|
||||
1, // min_threads
|
||||
{
|
||||
"superfast"s, // preset
|
||||
"zerolatency"s, // tune
|
||||
11, // superfast
|
||||
}, // software
|
||||
|
||||
{
|
||||
@ -359,10 +415,13 @@ namespace config {
|
||||
{
|
||||
(int) amd::quality_h264_e::balanced, // quality (h264)
|
||||
(int) amd::quality_hevc_e::balanced, // quality (hevc)
|
||||
(int) amd::quality_av1_e::balanced, // quality (av1)
|
||||
(int) amd::rc_h264_e::vbr_latency, // rate control (h264)
|
||||
(int) amd::rc_hevc_e::vbr_latency, // rate control (hevc)
|
||||
(int) amd::rc_av1_e::vbr_latency, // rate control (av1)
|
||||
(int) amd::usage_h264_e::ultralowlatency, // usage (h264)
|
||||
(int) amd::usage_hevc_e::ultralowlatency, // usage (hevc)
|
||||
(int) amd::usage_av1_e::ultralowlatency, // usage (av1)
|
||||
0, // preanalysis
|
||||
1, // vbaq
|
||||
(int) amd::coder_e::_auto, // coder
|
||||
@ -924,7 +983,11 @@ namespace config {
|
||||
int_f(vars, "qp", video.qp);
|
||||
int_f(vars, "min_threads", video.min_threads);
|
||||
int_between_f(vars, "hevc_mode", video.hevc_mode, { 0, 3 });
|
||||
int_between_f(vars, "av1_mode", video.av1_mode, { 0, 3 });
|
||||
string_f(vars, "sw_preset", video.sw.sw_preset);
|
||||
if (!video.sw.sw_preset.empty()) {
|
||||
video.sw.svtav1_preset = sw::svtav1_preset_from_view(video.sw.sw_preset);
|
||||
}
|
||||
string_f(vars, "sw_tune", video.sw.sw_tune);
|
||||
int_f(vars, "nv_preset", video.nv.nv_preset, nv::preset_from_view);
|
||||
int_f(vars, "nv_tune", video.nv.nv_tune, nv::tune_from_view);
|
||||
@ -937,23 +1000,26 @@ namespace config {
|
||||
std::string quality;
|
||||
string_f(vars, "amd_quality", quality);
|
||||
if (!quality.empty()) {
|
||||
video.amd.amd_quality_h264 = amd::quality_from_view(quality, 1);
|
||||
video.amd.amd_quality_hevc = amd::quality_from_view(quality, 0);
|
||||
video.amd.amd_quality_h264 = amd::quality_from_view<amd::quality_h264_e>(quality);
|
||||
video.amd.amd_quality_hevc = amd::quality_from_view<amd::quality_hevc_e>(quality);
|
||||
video.amd.amd_quality_av1 = amd::quality_from_view<amd::quality_av1_e>(quality);
|
||||
}
|
||||
|
||||
std::string rc;
|
||||
string_f(vars, "amd_rc", rc);
|
||||
int_f(vars, "amd_coder", video.amd.amd_coder, amd::coder_from_view);
|
||||
if (!rc.empty()) {
|
||||
video.amd.amd_rc_h264 = amd::rc_from_view(rc, 1);
|
||||
video.amd.amd_rc_hevc = amd::rc_from_view(rc, 0);
|
||||
video.amd.amd_rc_h264 = amd::rc_from_view<amd::rc_h264_e>(rc);
|
||||
video.amd.amd_rc_hevc = amd::rc_from_view<amd::rc_hevc_e>(rc);
|
||||
video.amd.amd_rc_av1 = amd::rc_from_view<amd::rc_av1_e>(rc);
|
||||
}
|
||||
|
||||
std::string usage;
|
||||
string_f(vars, "amd_usage", usage);
|
||||
if (!usage.empty()) {
|
||||
video.amd.amd_usage_h264 = amd::usage_from_view(rc, 1);
|
||||
video.amd.amd_usage_hevc = amd::usage_from_view(rc, 0);
|
||||
video.amd.amd_usage_h264 = amd::usage_from_view<amd::usage_h264_e>(rc);
|
||||
video.amd.amd_usage_hevc = amd::usage_from_view<amd::usage_hevc_e>(rc);
|
||||
video.amd.amd_usage_av1 = amd::usage_from_view<amd::usage_av1_e>(rc);
|
||||
}
|
||||
|
||||
bool_f(vars, "amd_preanalysis", (bool &) video.amd.amd_preanalysis);
|
||||
|
||||
@ -17,11 +17,13 @@ namespace config {
|
||||
int qp; // higher == more compression and less quality
|
||||
|
||||
int hevc_mode;
|
||||
int av1_mode;
|
||||
|
||||
int min_threads; // Minimum number of threads/slices for CPU encoding
|
||||
struct {
|
||||
std::string sw_preset;
|
||||
std::string sw_tune;
|
||||
std::optional<int> svtav1_preset;
|
||||
} sw;
|
||||
|
||||
struct {
|
||||
@ -39,10 +41,13 @@ namespace config {
|
||||
struct {
|
||||
std::optional<int> amd_quality_h264;
|
||||
std::optional<int> amd_quality_hevc;
|
||||
std::optional<int> amd_quality_av1;
|
||||
std::optional<int> amd_rc_h264;
|
||||
std::optional<int> amd_rc_hevc;
|
||||
std::optional<int> amd_rc_av1;
|
||||
std::optional<int> amd_usage_h264;
|
||||
std::optional<int> amd_usage_hevc;
|
||||
std::optional<int> amd_usage_av1;
|
||||
std::optional<int> amd_preanalysis;
|
||||
std::optional<int> amd_vbaq;
|
||||
int amd_coder;
|
||||
|
||||
@ -124,6 +124,11 @@ namespace nvenc {
|
||||
init_params.encodeGUID = NV_ENC_CODEC_HEVC_GUID;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
// AV1
|
||||
init_params.encodeGUID = NV_ENC_CODEC_AV1_GUID;
|
||||
break;
|
||||
|
||||
default:
|
||||
BOOST_LOG(error) << "NvEnc: unknown video format " << client_config.videoFormat;
|
||||
return false;
|
||||
@ -293,6 +298,33 @@ namespace nvenc {
|
||||
fill_vui(format_config.hevcVUIParameters);
|
||||
break;
|
||||
}
|
||||
|
||||
case 2: {
|
||||
// AV1
|
||||
auto &format_config = enc_config.encodeCodecConfig.av1Config;
|
||||
format_config.repeatSeqHdr = 1;
|
||||
format_config.idrPeriod = NVENC_INFINITE_GOPLENGTH;
|
||||
format_config.chromaFormatIDC = 1; // YUV444 not supported by NVENC yet
|
||||
format_config.enableBitstreamPadding = config.insert_filler_data;
|
||||
if (buffer_is_10bit()) {
|
||||
format_config.inputPixelBitDepthMinus8 = 2;
|
||||
format_config.pixelBitDepthMinus8 = 2;
|
||||
}
|
||||
format_config.colorPrimaries = colorspace.primaries;
|
||||
format_config.transferCharacteristics = colorspace.tranfer_function;
|
||||
format_config.matrixCoefficients = colorspace.matrix;
|
||||
format_config.colorRange = colorspace.full_range;
|
||||
set_ref_frames(format_config.maxNumRefFramesInDPB, format_config.numFwdRefs, 8);
|
||||
set_minqp_if_enabled(config.min_qp_av1);
|
||||
|
||||
if (client_config.slicesPerFrame > 1) {
|
||||
// NVENC only supports slice counts that are powers of two, so we'll pick powers of two
|
||||
// with bias to rows due to hopefully more similar macroblocks with a row vs a column.
|
||||
format_config.numTileRows = std::pow(2, std::ceil(std::log2(client_config.slicesPerFrame) / 2));
|
||||
format_config.numTileColumns = std::pow(2, std::floor(std::log2(client_config.slicesPerFrame) / 2));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
init_params.encodeConfig = &enc_config;
|
||||
|
||||
@ -35,6 +35,9 @@ namespace nvenc {
|
||||
// Min QP value for HEVC when enable_min_qp is selected
|
||||
unsigned min_qp_hevc = 23;
|
||||
|
||||
// Min QP value for AV1 when enable_min_qp is selected
|
||||
unsigned min_qp_av1 = 23;
|
||||
|
||||
// Use CAVLC entropy coding in H.264 instead of CABAC, not relevant and here for historical reasons
|
||||
bool h264_cavlc = false;
|
||||
|
||||
|
||||
@ -643,15 +643,20 @@ namespace nvhttp {
|
||||
tree.put("root.MaxLumaPixelsHEVC", video::active_hevc_mode > 1 ? "1869449984" : "0");
|
||||
tree.put("root.LocalIP", local_endpoint.address().to_string());
|
||||
|
||||
if (video::active_hevc_mode == 3) {
|
||||
tree.put("root.ServerCodecModeSupport", "3843");
|
||||
uint32_t codec_mode_flags = SCM_H264;
|
||||
if (video::active_hevc_mode >= 2) {
|
||||
codec_mode_flags |= SCM_HEVC;
|
||||
}
|
||||
else if (video::active_hevc_mode == 2) {
|
||||
tree.put("root.ServerCodecModeSupport", "259");
|
||||
if (video::active_hevc_mode >= 3) {
|
||||
codec_mode_flags |= SCM_HEVC_MAIN10;
|
||||
}
|
||||
else {
|
||||
tree.put("root.ServerCodecModeSupport", "3");
|
||||
if (video::active_av1_mode >= 2) {
|
||||
codec_mode_flags |= SCM_AV1_MAIN8;
|
||||
}
|
||||
if (video::active_av1_mode >= 3) {
|
||||
codec_mode_flags |= SCM_AV1_MAIN10;
|
||||
}
|
||||
tree.put("root.ServerCodecModeSupport", codec_mode_flags);
|
||||
|
||||
pt::ptree display_nodes;
|
||||
for (auto &resolution : config::nvhttp.resolutions) {
|
||||
|
||||
13
src/rtsp.cpp
13
src/rtsp.cpp
@ -504,6 +504,10 @@ namespace rtsp_stream {
|
||||
ss << "x-nv-video[0].refPicInvalidation=1"sv << std::endl;
|
||||
}
|
||||
|
||||
if (video::active_av1_mode != 1) {
|
||||
ss << "a=rtpmap:98 AV1/90000"sv << std::endl;
|
||||
}
|
||||
|
||||
for (int x = 0; x < audio::MAX_STREAM_CONFIG; ++x) {
|
||||
auto &stream_config = audio::stream_configs[x];
|
||||
std::uint8_t mapping[platf::speaker::MAX_SPEAKERS];
|
||||
@ -701,13 +705,20 @@ namespace rtsp_stream {
|
||||
}
|
||||
}
|
||||
|
||||
if (config.monitor.videoFormat != 0 && video::active_hevc_mode == 1) {
|
||||
if (config.monitor.videoFormat == 1 && video::active_hevc_mode == 1) {
|
||||
BOOST_LOG(warning) << "HEVC is disabled, yet the client requested HEVC"sv;
|
||||
|
||||
respond(sock, &option, 400, "BAD REQUEST", req->sequenceNumber, {});
|
||||
return;
|
||||
}
|
||||
|
||||
if (config.monitor.videoFormat == 2 && video::active_av1_mode == 1) {
|
||||
BOOST_LOG(warning) << "AV1 is disabled, yet the client requested AV1"sv;
|
||||
|
||||
respond(sock, &option, 400, "BAD REQUEST", req->sequenceNumber, {});
|
||||
return;
|
||||
}
|
||||
|
||||
auto session = stream::session::alloc(config, launch_session->gcm_key, launch_session->iv);
|
||||
|
||||
auto slot = server->accept(session);
|
||||
|
||||
216
src/video.cpp
216
src/video.cpp
@ -376,7 +376,7 @@ namespace video {
|
||||
operator[](flag_e flag) {
|
||||
return capabilities[(std::size_t) flag];
|
||||
}
|
||||
} hevc, h264;
|
||||
} av1, hevc, h264;
|
||||
|
||||
uint32_t flags;
|
||||
};
|
||||
@ -569,6 +569,16 @@ namespace video {
|
||||
std::make_unique<encoder_platform_formats_nvenc>(
|
||||
platf::mem_type_e::dxgi,
|
||||
platf::pix_fmt_e::nv12, platf::pix_fmt_e::p010),
|
||||
{
|
||||
// Common options
|
||||
{},
|
||||
// SDR-specific options
|
||||
{},
|
||||
// HDR-specific options
|
||||
{},
|
||||
std::nullopt, // QP
|
||||
"av1_nvenc"s,
|
||||
},
|
||||
{
|
||||
// Common options
|
||||
{},
|
||||
@ -609,6 +619,23 @@ namespace video {
|
||||
cuda_init_avcodec_hardware_input_buffer
|
||||
#endif
|
||||
),
|
||||
{
|
||||
// Common options
|
||||
{
|
||||
{ "delay"s, 0 },
|
||||
{ "forced-idr"s, 1 },
|
||||
{ "zerolatency"s, 1 },
|
||||
{ "preset"s, &config::video.nv.nv_preset },
|
||||
{ "tune"s, &config::video.nv.nv_tune },
|
||||
{ "rc"s, &config::video.nv.nv_rc },
|
||||
},
|
||||
// SDR-specific options
|
||||
{},
|
||||
// HDR-specific options
|
||||
{},
|
||||
std::nullopt,
|
||||
"av1_nvenc"s,
|
||||
},
|
||||
{
|
||||
// Common options
|
||||
{
|
||||
@ -660,6 +687,22 @@ namespace video {
|
||||
AV_PIX_FMT_QSV,
|
||||
AV_PIX_FMT_NV12, AV_PIX_FMT_P010,
|
||||
dxgi_init_avcodec_hardware_input_buffer),
|
||||
{
|
||||
// Common options
|
||||
{
|
||||
{ "preset"s, &config::video.qsv.qsv_preset },
|
||||
{ "forced_idr"s, 1 },
|
||||
{ "async_depth"s, 1 },
|
||||
{ "low_delay_brc"s, 1 },
|
||||
{ "low_power"s, 1 },
|
||||
},
|
||||
// SDR-specific options
|
||||
{},
|
||||
// HDR-specific options
|
||||
{},
|
||||
std::make_optional<encoder_t::option_t>({ "qp"s, &config::video.qp }),
|
||||
"av1_qsv"s,
|
||||
},
|
||||
{
|
||||
// Common options
|
||||
{
|
||||
@ -714,6 +757,20 @@ namespace video {
|
||||
AV_PIX_FMT_D3D11,
|
||||
AV_PIX_FMT_NV12, AV_PIX_FMT_P010,
|
||||
dxgi_init_avcodec_hardware_input_buffer),
|
||||
{
|
||||
// Common options
|
||||
{
|
||||
{ "filler_data"s, true },
|
||||
{ "preanalysis"s, &config::video.amd.amd_preanalysis },
|
||||
{ "quality"s, &config::video.amd.amd_quality_av1 },
|
||||
{ "rc"s, &config::video.amd.amd_rc_av1 },
|
||||
{ "usage"s, &config::video.amd.amd_usage_av1 },
|
||||
},
|
||||
{}, // SDR-specific options
|
||||
{}, // HDR-specific options
|
||||
std::make_optional<encoder_t::option_t>({ "qp_p"s, &config::video.qp }),
|
||||
"av1_amf"s,
|
||||
},
|
||||
{
|
||||
// Common options
|
||||
{
|
||||
@ -762,6 +819,20 @@ namespace video {
|
||||
AV_PIX_FMT_NONE,
|
||||
AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV420P10,
|
||||
nullptr),
|
||||
{
|
||||
// libsvtav1 takes different presets than libx264/libx265.
|
||||
// We set an infinite GOP length, use a low delay prediction structure,
|
||||
// force I frames to be key frames, and set max bitrate to default to work
|
||||
// around a FFmpeg bug with CBR mode.
|
||||
{
|
||||
{ "svtav1-params"s, "keyint=-1:pred-struct=1:force-key-frames=1:mbr=0"s },
|
||||
{ "preset"s, &config::video.sw.svtav1_preset },
|
||||
},
|
||||
{}, // SDR-specific options
|
||||
{}, // HDR-specific options
|
||||
std::make_optional<encoder_t::option_t>("qp"s, &config::video.qp),
|
||||
"libsvtav1"s,
|
||||
},
|
||||
{
|
||||
// x265's Info SEI is so long that it causes the IDR picture data to be
|
||||
// kicked to the 2nd packet in the frame, breaking Moonlight's parsing logic.
|
||||
@ -800,6 +871,17 @@ namespace video {
|
||||
AV_PIX_FMT_VAAPI,
|
||||
AV_PIX_FMT_NV12, AV_PIX_FMT_YUV420P10,
|
||||
vaapi_init_avcodec_hardware_input_buffer),
|
||||
{
|
||||
// Common options
|
||||
{
|
||||
{ "async_depth"s, 1 },
|
||||
{ "idr_interval"s, std::numeric_limits<int>::max() },
|
||||
},
|
||||
{}, // SDR-specific options
|
||||
{}, // HDR-specific options
|
||||
std::make_optional<encoder_t::option_t>("qp"s, &config::video.qp),
|
||||
"av1_vaapi"s,
|
||||
},
|
||||
{
|
||||
// Common options
|
||||
{
|
||||
@ -836,6 +918,18 @@ namespace video {
|
||||
AV_PIX_FMT_VIDEOTOOLBOX,
|
||||
AV_PIX_FMT_NV12, AV_PIX_FMT_NV12,
|
||||
nullptr),
|
||||
{
|
||||
// Common options
|
||||
{
|
||||
{ "allow_sw"s, &config::video.vt.vt_allow_sw },
|
||||
{ "require_sw"s, &config::video.vt.vt_require_sw },
|
||||
{ "realtime"s, &config::video.vt.vt_realtime },
|
||||
},
|
||||
{}, // SDR-specific options
|
||||
{}, // HDR-specific options
|
||||
std::nullopt,
|
||||
"av1_videotoolbox"s,
|
||||
},
|
||||
{
|
||||
// Common options
|
||||
{
|
||||
@ -883,6 +977,7 @@ namespace video {
|
||||
|
||||
static encoder_t *chosen_encoder;
|
||||
int active_hevc_mode;
|
||||
int active_av1_mode;
|
||||
bool last_encoder_probe_supported_ref_frames_invalidation = false;
|
||||
|
||||
void
|
||||
@ -1265,7 +1360,9 @@ namespace video {
|
||||
|
||||
bool hardware = platform_formats->avcodec_base_dev_type != AV_HWDEVICE_TYPE_NONE;
|
||||
|
||||
auto &video_format = config.videoFormat == 0 ? encoder.h264 : encoder.hevc;
|
||||
auto &video_format = config.videoFormat == 0 ? encoder.h264 :
|
||||
config.videoFormat == 1 ? encoder.hevc :
|
||||
encoder.av1;
|
||||
if (!video_format[encoder_t::PASSED]) {
|
||||
BOOST_LOG(error) << encoder.name << ": "sv << video_format.name << " mode not supported"sv;
|
||||
return nullptr;
|
||||
@ -1289,14 +1386,19 @@ namespace video {
|
||||
ctx->time_base = AVRational { 1, config.framerate };
|
||||
ctx->framerate = AVRational { config.framerate, 1 };
|
||||
|
||||
if (config.videoFormat == 0) {
|
||||
ctx->profile = FF_PROFILE_H264_HIGH;
|
||||
}
|
||||
else if (config.dynamicRange == 0) {
|
||||
ctx->profile = FF_PROFILE_HEVC_MAIN;
|
||||
}
|
||||
else {
|
||||
ctx->profile = FF_PROFILE_HEVC_MAIN_10;
|
||||
switch (config.videoFormat) {
|
||||
case 0:
|
||||
ctx->profile = FF_PROFILE_H264_HIGH;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
ctx->profile = config.dynamicRange ? FF_PROFILE_HEVC_MAIN_10 : FF_PROFILE_HEVC_MAIN;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
// AV1 supports both 8 and 10 bit encoding with the same Main profile
|
||||
ctx->profile = FF_PROFILE_AV1_MAIN;
|
||||
break;
|
||||
}
|
||||
|
||||
// B-frames delay decoder output, so never use them
|
||||
@ -1444,7 +1546,7 @@ namespace video {
|
||||
}
|
||||
|
||||
if (!(encoder.flags & NO_RC_BUF_LIMIT)) {
|
||||
if (!hardware && (ctx->slices > 1 || config.videoFormat != 0)) {
|
||||
if (!hardware && (ctx->slices > 1 || config.videoFormat == 1)) {
|
||||
// Use a larger rc_buffer_size for software encoding when slices are enabled,
|
||||
// because libx264 can severely degrade quality if the buffer is too small.
|
||||
// libx265 encounters this issue more frequently, so always scale the
|
||||
@ -1540,7 +1642,7 @@ namespace video {
|
||||
std::move(encode_device_final),
|
||||
|
||||
// 0 ==> don't inject, 1 ==> inject for h264, 2 ==> inject for hevc
|
||||
(1 - (int) video_format[encoder_t::VUI_PARAMETERS]) * (1 + config.videoFormat));
|
||||
config.videoFormat <= 1 ? (1 - (int) video_format[encoder_t::VUI_PARAMETERS]) * (1 + config.videoFormat) : 0);
|
||||
|
||||
return session;
|
||||
}
|
||||
@ -2099,15 +2201,19 @@ namespace video {
|
||||
}
|
||||
|
||||
int flag = 0;
|
||||
if (auto packet_avcodec = dynamic_cast<packet_raw_avcodec *>(packet.get())) {
|
||||
if (cbs::validate_sps(packet_avcodec->av_packet, config.videoFormat ? AV_CODEC_ID_H265 : AV_CODEC_ID_H264)) {
|
||||
|
||||
// This check only applies for H.264 and HEVC
|
||||
if (config.videoFormat <= 1) {
|
||||
if (auto packet_avcodec = dynamic_cast<packet_raw_avcodec *>(packet.get())) {
|
||||
if (cbs::validate_sps(packet_avcodec->av_packet, config.videoFormat ? AV_CODEC_ID_H265 : AV_CODEC_ID_H264)) {
|
||||
flag |= VUI_PARAMS;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Don't check it for non-avcodec encoders.
|
||||
flag |= VUI_PARAMS;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Don't check it for non-avcodec encoders.
|
||||
flag |= VUI_PARAMS;
|
||||
}
|
||||
|
||||
return flag;
|
||||
}
|
||||
@ -2124,8 +2230,12 @@ namespace video {
|
||||
auto force_hevc = active_hevc_mode >= 2;
|
||||
auto test_hevc = force_hevc || (active_hevc_mode == 0 && !(encoder.flags & H264_ONLY));
|
||||
|
||||
auto force_av1 = active_av1_mode >= 2;
|
||||
auto test_av1 = force_av1 || (active_av1_mode == 0 && !(encoder.flags & H264_ONLY));
|
||||
|
||||
encoder.h264.capabilities.set();
|
||||
encoder.hevc.capabilities.set();
|
||||
encoder.av1.capabilities.set();
|
||||
|
||||
// First, test encoder viability
|
||||
config_t config_max_ref_frames { 1920, 1080, 60, 1000, 1, 1, 1, 0, 0 };
|
||||
@ -2194,6 +2304,39 @@ namespace video {
|
||||
encoder.hevc.capabilities.reset();
|
||||
}
|
||||
|
||||
if (test_av1) {
|
||||
config_max_ref_frames.videoFormat = 2;
|
||||
config_autoselect.videoFormat = 2;
|
||||
|
||||
retry_av1:
|
||||
auto max_ref_frames_av1 = validate_config(disp, encoder, config_max_ref_frames);
|
||||
auto autoselect_av1 = max_ref_frames_av1 >= 0 ? max_ref_frames_av1 : validate_config(disp, encoder, config_autoselect);
|
||||
if (autoselect_av1 < 0) {
|
||||
if (encoder.av1.qp && encoder.av1[encoder_t::CBR]) {
|
||||
// It's possible the encoder isn't accepting Constant Bit Rate. Turn off CBR and make another attempt
|
||||
encoder.av1.capabilities.set();
|
||||
encoder.av1[encoder_t::CBR] = false;
|
||||
goto retry_av1;
|
||||
}
|
||||
|
||||
// If AV1 must be supported, but it is not supported
|
||||
if (force_av1) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto [validate_flag, encoder_flag] : packet_deficiencies) {
|
||||
encoder.av1[encoder_flag] = (max_ref_frames_av1 & validate_flag && autoselect_av1 & validate_flag);
|
||||
}
|
||||
|
||||
encoder.av1[encoder_t::REF_FRAMES_RESTRICT] = max_ref_frames_av1 >= 0;
|
||||
encoder.av1[encoder_t::PASSED] = max_ref_frames_av1 >= 0 || autoselect_av1 >= 0;
|
||||
}
|
||||
else {
|
||||
// Clear all cap bits for AV1 if we didn't probe it
|
||||
encoder.av1.capabilities.reset();
|
||||
}
|
||||
|
||||
std::vector<std::pair<encoder_t::flag_e, config_t>> configs {
|
||||
{ encoder_t::DYNAMIC_RANGE, { 1920, 1080, 60, 1000, 1, 0, 3, 1, 1 } },
|
||||
};
|
||||
@ -2201,9 +2344,11 @@ namespace video {
|
||||
for (auto &[flag, config] : configs) {
|
||||
auto h264 = config;
|
||||
auto hevc = config;
|
||||
auto av1 = config;
|
||||
|
||||
h264.videoFormat = 0;
|
||||
hevc.videoFormat = 1;
|
||||
av1.videoFormat = 2;
|
||||
|
||||
// HDR is not supported with H.264. Don't bother even trying it.
|
||||
encoder.h264[flag] = flag != encoder_t::DYNAMIC_RANGE && validate_config(disp, encoder, h264) >= 0;
|
||||
@ -2211,6 +2356,10 @@ namespace video {
|
||||
if (encoder.hevc[encoder_t::PASSED]) {
|
||||
encoder.hevc[flag] = validate_config(disp, encoder, hevc) >= 0;
|
||||
}
|
||||
|
||||
if (encoder.av1[encoder_t::PASSED]) {
|
||||
encoder.av1[flag] = validate_config(disp, encoder, av1) >= 0;
|
||||
}
|
||||
}
|
||||
|
||||
encoder.h264[encoder_t::VUI_PARAMETERS] = encoder.h264[encoder_t::VUI_PARAMETERS] && !config::sunshine.flags[config::flag::FORCE_VIDEO_HEADER_REPLACE];
|
||||
@ -2242,6 +2391,7 @@ namespace video {
|
||||
auto previous_encoder = chosen_encoder;
|
||||
chosen_encoder = nullptr;
|
||||
active_hevc_mode = config::video.hevc_mode;
|
||||
active_av1_mode = config::video.av1_mode;
|
||||
last_encoder_probe_supported_ref_frames_invalidation = false;
|
||||
|
||||
if (!config::video.encoder.empty()) {
|
||||
@ -2258,9 +2408,13 @@ namespace video {
|
||||
|
||||
// If we can't satisfy both the encoder and HDR requirement, prefer the encoder over HDR support
|
||||
if (active_hevc_mode == 3 && !encoder->hevc[encoder_t::DYNAMIC_RANGE]) {
|
||||
BOOST_LOG(warning) << "Encoder ["sv << config::video.encoder << "] does not support HDR on this system"sv;
|
||||
BOOST_LOG(warning) << "Encoder ["sv << config::video.encoder << "] does not support HEVC Main10 on this system"sv;
|
||||
active_hevc_mode = 0;
|
||||
}
|
||||
if (active_av1_mode == 3 && !encoder->av1[encoder_t::DYNAMIC_RANGE]) {
|
||||
BOOST_LOG(warning) << "Encoder ["sv << config::video.encoder << "] does not support AV1 Main10 on this system"sv;
|
||||
active_av1_mode = 0;
|
||||
}
|
||||
|
||||
chosen_encoder = encoder;
|
||||
break;
|
||||
@ -2277,7 +2431,7 @@ namespace video {
|
||||
BOOST_LOG(info) << "// Testing for available encoders, this may generate errors. You can safely ignore those errors. //"sv;
|
||||
|
||||
// If we haven't found an encoder yet, but we want one with HDR support, search for that now.
|
||||
if (chosen_encoder == nullptr && active_hevc_mode == 3) {
|
||||
if (chosen_encoder == nullptr && (active_hevc_mode == 3 || active_av1_mode == 3)) {
|
||||
KITTY_WHILE_LOOP(auto pos = std::begin(encoder_list), pos != std::end(encoder_list), {
|
||||
auto encoder = *pos;
|
||||
|
||||
@ -2288,7 +2442,8 @@ namespace video {
|
||||
}
|
||||
|
||||
// Skip it if it doesn't support HDR
|
||||
if (!encoder->hevc[encoder_t::DYNAMIC_RANGE]) {
|
||||
if ((active_hevc_mode == 3 && !encoder->hevc[encoder_t::DYNAMIC_RANGE]) ||
|
||||
(active_av1_mode == 3 && !encoder->av1[encoder_t::DYNAMIC_RANGE])) {
|
||||
pos++;
|
||||
continue;
|
||||
}
|
||||
@ -2340,6 +2495,7 @@ namespace video {
|
||||
BOOST_LOG(debug) << encoder_t::from_flag(flag) << (encoder.h264[flag] ? ": supported"sv : ": unsupported"sv);
|
||||
}
|
||||
BOOST_LOG(debug) << "-------------------"sv;
|
||||
BOOST_LOG(info) << "Found H.264 encoder: "sv << encoder.h264.name << " ["sv << encoder.name << ']';
|
||||
|
||||
if (encoder.hevc[encoder_t::PASSED]) {
|
||||
BOOST_LOG(debug) << "------ hevc ------"sv;
|
||||
@ -2349,16 +2505,28 @@ namespace video {
|
||||
}
|
||||
BOOST_LOG(debug) << "-------------------"sv;
|
||||
|
||||
BOOST_LOG(info) << "Found encoder "sv << encoder.name << ": ["sv << encoder.h264.name << ", "sv << encoder.hevc.name << ']';
|
||||
BOOST_LOG(info) << "Found HEVC encoder: "sv << encoder.hevc.name << " ["sv << encoder.name << ']';
|
||||
}
|
||||
else {
|
||||
BOOST_LOG(info) << "Found encoder "sv << encoder.name << ": ["sv << encoder.h264.name << ']';
|
||||
|
||||
if (encoder.av1[encoder_t::PASSED]) {
|
||||
BOOST_LOG(debug) << "------ av1 ------"sv;
|
||||
for (int x = 0; x < encoder_t::MAX_FLAGS; ++x) {
|
||||
auto flag = (encoder_t::flag_e) x;
|
||||
BOOST_LOG(debug) << encoder_t::from_flag(flag) << (encoder.av1[flag] ? ": supported"sv : ": unsupported"sv);
|
||||
}
|
||||
BOOST_LOG(debug) << "-------------------"sv;
|
||||
|
||||
BOOST_LOG(info) << "Found AV1 encoder: "sv << encoder.av1.name << " ["sv << encoder.name << ']';
|
||||
}
|
||||
|
||||
if (active_hevc_mode == 0) {
|
||||
active_hevc_mode = encoder.hevc[encoder_t::PASSED] ? (encoder.hevc[encoder_t::DYNAMIC_RANGE] ? 3 : 2) : 1;
|
||||
}
|
||||
|
||||
if (active_av1_mode == 0) {
|
||||
active_av1_mode = encoder.av1[encoder_t::PASSED] ? (encoder.av1[encoder_t::DYNAMIC_RANGE] ? 3 : 2) : 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@ -137,7 +137,7 @@ namespace video {
|
||||
SDR encoding colorspace (encoderCscMode >> 1) : 0 - BT.601, 1 - BT.709, 2 - BT.2020 */
|
||||
int encoderCscMode;
|
||||
|
||||
int videoFormat; // 0 - H.264, 1 - HEVC
|
||||
int videoFormat; // 0 - H.264, 1 - HEVC, 2 - AV1
|
||||
|
||||
/* Encoding color depth (bit depth): 0 - 8-bit, 1 - 10-bit
|
||||
HDR encoding activates when color depth is higher than 8-bit and the display which is being captured is operating in HDR mode */
|
||||
@ -145,6 +145,7 @@ namespace video {
|
||||
};
|
||||
|
||||
extern int active_hevc_mode;
|
||||
extern int active_av1_mode;
|
||||
extern bool last_encoder_probe_supported_ref_frames_invalidation;
|
||||
|
||||
void
|
||||
|
||||
@ -691,6 +691,28 @@
|
||||
HEVC is more CPU-intensive to encode, so enabling this may reduce performance when using software encoding.
|
||||
</div>
|
||||
</div>
|
||||
<!--AV1 Support -->
|
||||
<div class="mb-3">
|
||||
<label for="av1_mode" class="form-label">AV1 Support</label>
|
||||
<select id="av1_mode" class="form-select" v-model="config.av1_mode">
|
||||
<option value="0">
|
||||
Sunshine will specify support for AV1 based on encoder
|
||||
</option>
|
||||
<option value="1">
|
||||
Sunshine will not advertise support for AV1
|
||||
</option>
|
||||
<option value="2">
|
||||
Sunshine will advertise support for AV1 Main 8-bit profile
|
||||
</option>
|
||||
<option value="3">
|
||||
Sunshine will advertise support for AV1 Main 8-bit and 10-bit (HDR) profiles
|
||||
</option>
|
||||
</select>
|
||||
<div class="form-text">
|
||||
Allows the client to request AV1 Main 8-bit or 10-bit video streams.<br />
|
||||
AV1 is more CPU-intensive to encode, so enabling this may reduce performance when using software encoding.
|
||||
</div>
|
||||
</div>
|
||||
<!--Capture-->
|
||||
<div class="mb-3" v-if="platform === 'linux'">
|
||||
<label for="capture" class="form-label">Force a Specific Capture Method</label>
|
||||
@ -1032,6 +1054,7 @@
|
||||
"fps": "[10,30,60,90,120]",
|
||||
"gamepad": "auto",
|
||||
"hevc_mode": 0,
|
||||
"av1_mode": 0,
|
||||
"key_rightalt_to_key_win": "disabled",
|
||||
"keyboard": "enabled",
|
||||
"min_log_level": 2,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user