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. 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 -
-
Quantitization Parameter
+ Some devices may not 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
@@ -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) {