diff --git a/assets/sunshine.conf b/assets/sunshine.conf
index d78d5e14..bb846029 100644
--- a/assets/sunshine.conf
+++ b/assets/sunshine.conf
@@ -157,14 +157,9 @@
# Honestly, I have no idea what the optimal values would be.
# Play around with this :)
-# Constant Rate Factor. Between 1 and 52. It allows QP to go up during motion and down with still image, resulting in constant perceived quality
-# Higher value means more compression, but less quality
-# If crf == 0, then use QP directly instead
-# crf = 0
-
# Quantitization Parameter
+# Some devices don't support Constant Bit Rate. For those devices, QP is used instead
# Higher value means more compression, but less quality
-# If crf != 0, then this parameter is ignored
# qp = 28
# Minimum number of threads used by ffmpeg to encode the video.
diff --git a/assets/web/config.html b/assets/web/config.html
index 0ed0c736..d7b67b98 100644
--- a/assets/web/config.html
+++ b/assets/web/config.html
@@ -256,27 +256,15 @@
Set the familly of ports used by Sunshine
-
-
-
Constant Rate Factor
-
-
- Constant Rate Factor. Between 1 and 52. It allows QP to go up during motion and down with still
- image,
- resulting in constant perceived quality
- Higher value means more compression, but less quality
- If crf == 0, then use QP directly instead
-
-
@@ -600,4 +588,4 @@
font-size: 12px;
font-weight: bold;
}
-
\ No newline at end of file
+
diff --git a/sunshine/config.cpp b/sunshine/config.cpp
index 2fcca9ba..a71d98ad 100644
--- a/sunshine/config.cpp
+++ b/sunshine/config.cpp
@@ -147,7 +147,6 @@ int coder_from_view(const std::string_view &coder) {
} // namespace amd
video_t video {
- 0, // crf
28, // qp
0, // hevc_mode
@@ -582,7 +581,6 @@ void apply_config(std::unordered_map &&vars) {
std::cout << "["sv << name << "] -- ["sv << val << ']' << std::endl;
}
- int_f(vars, "crf", video.crf);
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 });
diff --git a/sunshine/config.h b/sunshine/config.h
index ba914819..3c0537dd 100644
--- a/sunshine/config.h
+++ b/sunshine/config.h
@@ -11,8 +11,7 @@
namespace config {
struct video_t {
// ffmpeg params
- int crf; // higher == more compression and less quality
- int qp; // higher == more compression and less quality, ignored if crf != 0
+ int qp; // higher == more compression and less quality
int hevc_mode;
diff --git a/sunshine/video.cpp b/sunshine/video.cpp
index a1ff920e..028c5a9e 100644
--- a/sunshine/video.cpp
+++ b/sunshine/video.cpp
@@ -250,6 +250,7 @@ struct encoder_t {
REF_FRAMES_RESTRICT, // Set maximum reference frames
REF_FRAMES_AUTOSELECT, // Allow encoder to select maximum reference frames (If !REF_FRAMES_RESTRICT --> REF_FRAMES_AUTOSELECT)
SLICE, // Allow frame to be partitioned into multiple slices
+ CBR, // Some encoders don't support CBR, if not supported --> attempt constant quantatication parameter instead
DYNAMIC_RANGE, // hdr
VUI_PARAMETERS, // AMD encoder with VAAPI doesn't add VUI parameters to SPS
NALU_PREFIX_5b, // libx264/libx265 have a 3-byte nalu prefix instead of 4-byte nalu prefix
@@ -265,6 +266,7 @@ struct encoder_t {
_CONVERT(REF_FRAMES_RESTRICT);
_CONVERT(REF_FRAMES_AUTOSELECT);
_CONVERT(SLICE);
+ _CONVERT(CBR);
_CONVERT(DYNAMIC_RANGE);
_CONVERT(VUI_PARAMETERS);
_CONVERT(NALU_PREFIX_5b);
@@ -299,7 +301,7 @@ struct encoder_t {
struct {
std::vector options;
- std::optional crf, qp;
+ std::optional qp;
std::string name;
std::bitset capabilities;
@@ -424,7 +426,6 @@ static encoder_t nvenc {
{ "rc"s, &config::video.nv.rc },
},
std::nullopt,
- std::nullopt,
"hevc_nvenc"s,
},
{
@@ -435,7 +436,6 @@ static encoder_t nvenc {
{ "rc"s, &config::video.nv.rc },
{ "coder"s, &config::video.nv.coder },
},
- std::nullopt,
std::make_optional({ "qp"s, &config::video.qp }),
"h264_nvenc"s,
},
@@ -463,7 +463,6 @@ static encoder_t amdvce {
{ "quality"s, &config::video.amd.quality },
{ "rc"s, &config::video.amd.rc },
},
- std::nullopt,
std::make_optional({ "qp"s, &config::video.qp }),
"hevc_amf"s,
},
@@ -474,7 +473,6 @@ static encoder_t amdvce {
{ "rc"s, &config::video.amd.rc },
{ "log_to_dbg"s, "1"s },
},
- std::nullopt,
std::make_optional({ "qp"s, &config::video.qp }),
"h264_amf"s,
},
@@ -500,7 +498,6 @@ static encoder_t software {
{ "preset"s, &config::video.sw.preset },
{ "tune"s, &config::video.sw.tune },
},
- std::make_optional("crf"s, &config::video.crf),
std::make_optional("qp"s, &config::video.qp),
"libx265"s,
},
@@ -509,7 +506,6 @@ static encoder_t software {
{ "preset"s, &config::video.sw.preset },
{ "tune"s, &config::video.sw.tune },
},
- std::make_optional("crf"s, &config::video.crf),
std::make_optional("qp"s, &config::video.qp),
"libx264"s,
},
@@ -530,8 +526,7 @@ static encoder_t vaapi {
{ "sei"s, 0 },
{ "idr_interval"s, std::numeric_limits::max() },
},
- std::nullopt,
- std::nullopt,
+ std::make_optional("qp"s, &config::video.qp),
"hevc_vaapi"s,
},
{
@@ -539,8 +534,7 @@ static encoder_t vaapi {
{ "sei"s, 0 },
{ "idr_interval"s, std::numeric_limits::max() },
},
- std::nullopt,
- std::nullopt,
+ std::make_optional("qp"s, &config::video.qp),
"h264_vaapi"s,
},
LIMITED_GOP_SIZE | SYSTEM_MEMORY,
@@ -921,21 +915,18 @@ std::optional make_session(const encoder_t &encoder, const config_t &
handle_option(option);
}
- if(config.bitrate > 500) {
+ if(video_format[encoder_t::CBR]) {
auto bitrate = config.bitrate * 1000;
ctx->rc_max_rate = bitrate;
ctx->rc_buffer_size = bitrate / config.framerate;
ctx->bit_rate = bitrate;
ctx->rc_min_rate = bitrate;
}
- else if(video_format.crf && config::video.crf != 0) {
- handle_option(*video_format.crf);
- }
else if(video_format.qp) {
handle_option(*video_format.qp);
}
else {
- BOOST_LOG(error) << "Couldn't set video quality: encoder "sv << encoder.name << " doesn't support either crf or qp"sv;
+ BOOST_LOG(error) << "Couldn't set video quality: encoder "sv << encoder.name << " doesn't support qp"sv;
return std::nullopt;
}
@@ -1469,10 +1460,17 @@ bool validate_encoder(encoder_t &encoder) {
config_t config_max_ref_frames { 1920, 1080, 60, 1000, 1, 1, 1, 0, 0 };
config_t config_autoselect { 1920, 1080, 60, 1000, 1, 0, 1, 0, 0 };
+retry:
auto max_ref_frames_h264 = validate_config(disp, encoder, config_max_ref_frames);
auto autoselect_h264 = validate_config(disp, encoder, config_autoselect);
if(max_ref_frames_h264 < 0 && autoselect_h264 < 0) {
+ if(encoder.h264.qp && encoder.h264[encoder_t::CBR]) {
+ // It's possible the encoder isn't accepting Constant Bit Rate. Turn off CBR and make another attempt
+ encoder.h264.capabilities.set();
+ encoder.h264[encoder_t::CBR] = false;
+ goto retry;
+ }
return false;
}
@@ -1494,12 +1492,22 @@ bool validate_encoder(encoder_t &encoder) {
config_max_ref_frames.videoFormat = 1;
config_autoselect.videoFormat = 1;
+retry_hevc:
auto max_ref_frames_hevc = validate_config(disp, encoder, config_max_ref_frames);
auto autoselect_hevc = validate_config(disp, encoder, config_autoselect);
// If HEVC must be supported, but it is not supported
- if(force_hevc && max_ref_frames_hevc < 0 && autoselect_hevc < 0) {
- return false;
+ if(max_ref_frames_hevc < 0 && autoselect_hevc < 0) {
+ if(encoder.hevc.qp && encoder.hevc[encoder_t::CBR]) {
+ // It's possible the encoder isn't accepting Constant Bit Rate. Turn off CBR and make another attempt
+ encoder.hevc.capabilities.set();
+ encoder.hevc[encoder_t::CBR] = false;
+ goto retry_hevc;
+ }
+
+ if(force_hevc) {
+ return false;
+ }
}
for(auto [validate_flag, encoder_flag] : packet_deficiencies) {