diff --git a/QSVHelper/Encoder.h b/QSVHelper/Encoder.h index aa4ad7e0..820523c3 100644 --- a/QSVHelper/Encoder.h +++ b/QSVHelper/Encoder.h @@ -102,7 +102,7 @@ struct Encoder , using_d3d11(false), session(), encoder(session), event_prefix(event_prefix), encoder_flushed(event_prefix + ENCODER_FLUSHED), flushed(false), log_file(log_file) { params.Init(init_req->target_usage, init_req->profile, init_req->fps, init_req->keyint, init_req->bframes, init_req->width, init_req->height, init_req->max_bitrate, - init_req->buffer_size, init_req->use_cbr); + init_req->buffer_size, init_req->use_cbr, init_req->use_custom_parameters, init_req->custom_parameters, init_req->la_depth); params.SetVideoSignalInfo(init_req->full_range, init_req->primaries, init_req->transfer, init_req->matrix); } @@ -172,6 +172,8 @@ struct Encoder Parameters query = params; encoder.GetVideoParam(query); + init_res->rate_control = query->mfx.RateControlMethod; + switch (query->mfx.CodecProfile) { case MFX_PROFILE_AVC_BASELINE: diff --git a/QSVHelper/IPCStructs.h b/QSVHelper/IPCStructs.h index 32acb5ea..f88947e1 100644 --- a/QSVHelper/IPCStructs.h +++ b/QSVHelper/IPCStructs.h @@ -31,7 +31,9 @@ struct init_request uint32_t obs_process_id; uint16_t target_usage, profile; int32_t fps, keyint, bframes, width, height, max_bitrate, buffer_size; - bool use_cbr; + bool use_cbr, use_custom_parameters; + mfxInfoMFX custom_parameters; + decltype(mfxExtCodingOption2::LookAheadDepth) la_depth; int32_t full_range, matrix, primaries, transfer; bool use_custom_impl; mfxVersion custom_version; @@ -44,6 +46,7 @@ struct init_response mfxVersion version; mfxIMPL requested_impl, actual_impl; + decltype(mfxInfoMFX::RateControlMethod) rate_control; bool using_custom_impl; diff --git a/QSVHelper/QSVStuff.cpp b/QSVHelper/QSVStuff.cpp index e1e3c50e..9d099af4 100644 --- a/QSVHelper/QSVStuff.cpp +++ b/QSVHelper/QSVStuff.cpp @@ -80,20 +80,48 @@ Parameters &Parameters::operator =(const Parameters& o) return *this; } -void Parameters::Init(mfxU16 target_usage, mfxU16 profile, int fps, int keyframe_interval_frames, int bframes, int width, int height, int max_bitrate, int buffer_size, bool use_cbr) +void Parameters::Init(mfxU16 target_usage, mfxU16 profile, int fps, int keyframe_interval_frames, int bframes, int width, int height, int max_bitrate, + int buffer_size, bool use_cbr, bool use_custom_params, mfxInfoMFX custom_params, decltype(mfxExtCodingOption2::LookAheadDepth) la_depth) { params.mfx.CodecId = MFX_CODEC_AVC; params.mfx.TargetUsage = target_usage; - params.mfx.TargetKbps = saturate(max_bitrate); - params.mfx.MaxKbps = saturate(max_bitrate); - params.mfx.BufferSizeInKB = buffer_size/8; params.mfx.GopOptFlag = MFX_GOP_CLOSED; params.mfx.GopPicSize = keyframe_interval_frames; params.mfx.GopRefDist = bframes+1; params.mfx.NumSlice = 1; params.mfx.CodecProfile = profile; - params.mfx.RateControlMethod = use_cbr ? MFX_RATECONTROL_CBR : MFX_RATECONTROL_VBR; + params.mfx.TargetKbps = use_custom_params ? custom_params.TargetKbps : saturate(max_bitrate); + params.mfx.BufferSizeInKB = use_custom_params ? custom_params.BufferSizeInKB : buffer_size / 8; + + params.mfx.RateControlMethod = use_cbr ? MFX_RATECONTROL_CBR : use_custom_params ? custom_params.RateControlMethod : MFX_RATECONTROL_VBR; + switch (params.mfx.RateControlMethod) + { + case MFX_RATECONTROL_VBR: + case MFX_RATECONTROL_VCM: + params.mfx.MaxKbps = use_custom_params ? custom_params.MaxKbps : 0; + break; + + case MFX_RATECONTROL_AVBR: + params.mfx.Accuracy = custom_params.Accuracy; + params.mfx.Convergence = custom_params.Convergence; + break; + + case MFX_RATECONTROL_CQP: + params.mfx.QPI = custom_params.QPI; + params.mfx.QPP = custom_params.QPP; + params.mfx.QPB = custom_params.QPB; + break; + + case MFX_RATECONTROL_ICQ: + case MFX_RATECONTROL_LA_ICQ: + params.mfx.ICQQuality = custom_params.ICQQuality; + case MFX_RATECONTROL_LA: + AddCodingOption2(); + co2.LookAheadDepth = la_depth; + break; + } + params.IOPattern = MFX_IOPATTERN_IN_SYSTEM_MEMORY; auto& fi = params.mfx.FrameInfo; diff --git a/QSVHelper/QSVStuff.h b/QSVHelper/QSVStuff.h index a3777f42..f714ec3b 100644 --- a/QSVHelper/QSVStuff.h +++ b/QSVHelper/QSVStuff.h @@ -51,7 +51,8 @@ public: mfxVideoParam* operator->() { return ¶ms; } const mfxVideoParam* operator->() const { return ¶ms; } - void Init(mfxU16 preset, mfxU16 profile, int fps, int keyframe_interval_frames, int bframes, int width, int height, int max_bitrate, int buffer_size, bool use_cbr); + void Init(mfxU16 preset, mfxU16 profile, int fps, int keyframe_interval_frames, int bframes, int width, int height, int max_bitrate, + int buffer_size, bool use_cbr, bool use_custom_params, mfxInfoMFX custom_params, decltype(mfxExtCodingOption2::LookAheadDepth) la_depth); void SetCodingOptionSPSPPS(mfxU8 *sps_buff, mfxU16 sps_size, mfxU8 *pps_buff, mfxU16 pps_size); void SetVideoSignalInfo(int full_range, int primaries, int transfer, int matrix); void AddCodingOption(); diff --git a/QSVHelper/SupportStuff.h b/QSVHelper/SupportStuff.h index ca3d2314..5114d5e6 100644 --- a/QSVHelper/SupportStuff.h +++ b/QSVHelper/SupportStuff.h @@ -52,7 +52,7 @@ void InitFrame(mfxFrameData &frame, mfxU8 *Y, mfxU8 *UV, mfxU8 *V, mfxU16 pitch) frame.Pitch = pitch; } -std::vector InitSEIUserData(bool use_cbr, const mfxVideoParam& params, const mfxVersion& ver) +std::vector InitSEIUserData(bool use_cbr, const Parameters& params, const mfxVersion& ver) { #define TO_STR(x) #x static const char *usage_str[] = { @@ -66,6 +66,24 @@ std::vector InitSEIUserData(bool use_cbr, const mfxVideoParam& params, co TO_STR(MFX_TARGETUSAGE_BEST_SPEED) }; #undef TO_STR + +#define RATE_CONTROL(x) {MFX_RATECONTROL_##x, #x} + struct + { + decltype(mfxInfoMFX::RateControlMethod) method; + const char* name; + } rate_control_str[] = { + RATE_CONTROL(CBR), + RATE_CONTROL(VBR), + RATE_CONTROL(CQP), + RATE_CONTROL(AVBR), + RATE_CONTROL(LA), + RATE_CONTROL(ICQ), + RATE_CONTROL(VCM), + RATE_CONTROL(LA_ICQ), + }; +#undef RATE_CONTROL + using namespace std; vector data; @@ -73,14 +91,59 @@ std::vector InitSEIUserData(bool use_cbr, const mfxVideoParam& params, co 0x90, 0x24, 0x00, 0x50, 0xc2, 0x49, 0x00, 0x48 }; //6d1a26a0-bddc-11e2-9024-0050c2490048 data.insert(end(data), begin(UUID), end(UUID)); + auto method = params->mfx.RateControlMethod; + + const char *name = "UKNOWN"; + for (const auto &names : rate_control_str) + { + if (names.method != method) + continue; + + name = names.name; + break; + } + ostringstream str; str << "QSV hardware encoder options:" - << " rate control: " << (use_cbr ? "cbr" : "vbr") - << "; target bitrate: " << params.mfx.TargetKbps - << "; max bitrate: " << params.mfx.MaxKbps - << "; buffersize: " << params.mfx.BufferSizeInKB * 8 - << "; API level: " << ver.Major << "." << ver.Minor - << "; Target Usage: " << usage_str[params.mfx.TargetUsage]; + << " rate control: " << name; + switch (method) + { + case MFX_RATECONTROL_CBR: + case MFX_RATECONTROL_VBR: + case MFX_RATECONTROL_VCM: + str << "; target bitrate: " << params->mfx.TargetKbps; + if (method != MFX_RATECONTROL_CBR) + str << "; max bitrate: " << params->mfx.MaxKbps; + str << "; buffersize: " << params->mfx.BufferSizeInKB * 8; + break; + + case MFX_RATECONTROL_AVBR: + str << "; target bitrate: " << params->mfx.TargetKbps + << "; accuracy: " << params->mfx.Accuracy + << "; convergence: " << params->mfx.Convergence; + break; + + case MFX_RATECONTROL_CQP: + str << "; QPI: " << params->mfx.QPI + << "; QPP: " << params->mfx.QPP + << "; QPB: " << params->mfx.QPB; + break; + + case MFX_RATECONTROL_LA: + str << "; target bitrate: " << params->mfx.TargetKbps + << "; look ahead: " << params.co2.LookAheadDepth; + break; + + case MFX_RATECONTROL_ICQ: + case MFX_RATECONTROL_LA_ICQ: + str << "; ICQQuality: " << params->mfx.ICQQuality; + if (method == MFX_RATECONTROL_LA_ICQ) + str << "; look ahead: " << params.co2.LookAheadDepth; + break; + } + str << "; API level: " << ver.Major << "." << ver.Minor + << "; Target Usage: " << usage_str[params->mfx.TargetUsage]; + string str_(str.str()); data.insert(end(data), begin(str_), end(str_)); diff --git a/QSVHelper/Utilities.h b/QSVHelper/Utilities.h index 3aeeab9d..9d7d35a5 100644 --- a/QSVHelper/Utilities.h +++ b/QSVHelper/Utilities.h @@ -42,3 +42,39 @@ static inline T saturate(U val) return std::numeric_limits::min(); return val; } + +template +static inline void saturate(T &t, U val) +{ + if (val > std::numeric_limits::max()) + t = std::numeric_limits::max(); + else if (val < std::numeric_limits::min()) + t = std::numeric_limits::min(); + else + t = static_cast(val); +} + +template +static inline T clamp(T t, U u, V v) +{ + if (t < u) + return u; + return t > v ? v : t; +} + +static bool valid_method(decltype(mfxInfoMFX::RateControlMethod) method) +{ + switch (method) + { + case MFX_RATECONTROL_CBR: + case MFX_RATECONTROL_VBR: + case MFX_RATECONTROL_CQP: + case MFX_RATECONTROL_AVBR: + case MFX_RATECONTROL_LA: + case MFX_RATECONTROL_ICQ: + case MFX_RATECONTROL_VCM: + case MFX_RATECONTROL_LA_ICQ: + return true; + } + return false; +} diff --git a/Source/Encoder_QSV.cpp b/Source/Encoder_QSV.cpp index ff385863..95d2b669 100644 --- a/Source/Encoder_QSV.cpp +++ b/Source/Encoder_QSV.cpp @@ -74,6 +74,24 @@ namespace TO_STR(MFX_TARGETUSAGE_7_BEST_SPEED) }; +#define RATE_CONTROL(x, cq) {MFX_RATECONTROL_##x, #x, cq} + struct + { + decltype(mfxInfoMFX::RateControlMethod) method; + const char* name; + bool cq; + } rate_control_str[] = { + RATE_CONTROL(CBR, false), + RATE_CONTROL(VBR, false), + RATE_CONTROL(CQP, true), + RATE_CONTROL(AVBR, false), + RATE_CONTROL(LA, false), + RATE_CONTROL(ICQ, true), + RATE_CONTROL(VCM, false), + RATE_CONTROL(LA_ICQ, true), + }; +#undef RATE_CONTROL + CTSTR qsv_intf_str(const mfxU32 impl) { switch(impl & (-MFX_IMPL_VIA_ANY)) @@ -325,6 +343,26 @@ bool CheckQSVHardwareSupport(bool log=true, bool *configurationWarning = nullptr return false; } +bool QSVMethodAvailable(decltype(mfxInfoMFX::RateControlMethod) method) +{ + static qsv_cpu_platform plat = qsv_get_cpu_platform(); + switch (method) + { + case MFX_RATECONTROL_CBR: + case MFX_RATECONTROL_VBR: + case MFX_RATECONTROL_AVBR: + case MFX_RATECONTROL_CQP: + return plat != QSV_CPU_PLATFORM_UNKNOWN; + + case MFX_RATECONTROL_VCM: + case MFX_RATECONTROL_LA: + case MFX_RATECONTROL_ICQ: + case MFX_RATECONTROL_LA_ICQ: + return plat >= QSV_CPU_PLATFORM_HSW; + } + return false; +} + struct VideoPacket { List Packet; @@ -372,6 +410,7 @@ class QSVEncoder : public VideoEncoder mfxU16 target_usage, profile, max_bitrate; + decltype(mfxInfoMFX::RateControlMethod) rate_control; String event_prefix; @@ -505,6 +544,51 @@ public: ipc_init_request request((event_prefix + INIT_REQUEST).Array()); + if (AppConfig->GetInt(L"QSV (Advanced)", L"UseCustomParams")) + { + Log(L"QSV: Using custom parameters"); + request->use_custom_parameters = true; + auto &mfx = request->custom_parameters; + mfx.RateControlMethod = AppConfig->GetInt(L"QSV (Advanced)", L"RateControlMethod", MFX_RATECONTROL_VBR); + if (!valid_method(mfx.RateControlMethod)) + mfx.RateControlMethod = MFX_RATECONTROL_VBR; + + auto load_int = [&](CTSTR name, int def) { return AppConfig->GetInt(L"QSV (Advanced)", name, def); }; + + bool use_global_bitrate = !!load_int(L"UseGlobalBitrate", true); + bool use_global_buffer = !!load_int(L"UseGlobalBufferSize", true); + + mfx.TargetKbps = use_global_bitrate ? maxBitrate : load_int(L"TargetKbps", 1000); + mfx.BufferSizeInKB = use_global_buffer ? (bufferSize / 8) : load_int(L"BufferSizeInKB", 0); + + switch (mfx.RateControlMethod) + { + case MFX_RATECONTROL_VBR: + case MFX_RATECONTROL_VCM: + mfx.MaxKbps = load_int(L"MaxKbps", 0); + break; + + case MFX_RATECONTROL_AVBR: + mfx.Accuracy = clamp(load_int(L"Accuracy", 1000), 0, 1000); + mfx.Convergence = load_int(L"Convergence", 1); + break; + + case MFX_RATECONTROL_CQP: + mfx.QPI = clamp(load_int(L"QPI", 23), 1, 51); + mfx.QPP = clamp(load_int(L"QPP", 23), 1, 51); + mfx.QPB = clamp(load_int(L"QPB", 23), 1, 51); + break; + + case MFX_RATECONTROL_ICQ: + case MFX_RATECONTROL_LA_ICQ: + mfx.ICQQuality = clamp(load_int(L"ICQQuality", 23), 1, 51); + case MFX_RATECONTROL_LA: + request->la_depth = load_int(L"LADepth", 40); + if (request->la_depth != 0) + request->la_depth = clamp(request->la_depth, 10, 100); + } + } + request->mode = request->MODE_ENCODE; request->obs_process_id = GetCurrentProcessId(); @@ -549,6 +633,11 @@ public: CrashError(TEXT("QSVHelper.exe could not find a valid configuration. Make sure you have a (virtual) display connected to your iGPU")); //FIXME: convert to localized error CrashError(TEXT("QSVHelper.exe could not find a valid configuration")); default: + if (code == EXIT_ENCODER_INIT_FAILED && request->use_custom_parameters) + { + Log(L"Encoder initialization failed with code %i while using custom parameters", code); + throw Str("Encoder.QSV.InitCustomParamsFailed"); + } CrashError(TEXT("QSVHelper.exe has exited with code %i (before response)"), code); //FIXME: convert to localized error } } @@ -569,6 +658,7 @@ public: target_usage = response->target_usage; profile = response->profile; + rate_control = response->rate_control; encode_tasks.SetSize(response->bitstream_num); @@ -1088,6 +1178,16 @@ public: { String strInfo; + const char* name = "UNKNOWN"; + for (auto &names : rate_control_str) + { + if (names.method != rate_control) + continue; + + name = names.name; + break; + } + strInfo << TEXT("Video Encoding: QSV") << TEXT("\r\n fps: ") << IntString(fps) << TEXT("\r\n width: ") << IntString(width) << TEXT(", height: ") << IntString(height) << @@ -1095,12 +1195,9 @@ public: TEXT("\r\n profile: ") << qsv_profile_str(profile) << TEXT("\r\n CBR: ") << CTSTR((bUseCBR) ? TEXT("yes") : TEXT("no")) << TEXT("\r\n CFR: ") << CTSTR((bUseCFR) ? TEXT("yes") : TEXT("no")) << - TEXT("\r\n max bitrate: ") << IntString(max_bitrate); - - if(!bUseCBR) - { - strInfo << TEXT("\r\n buffer size: ") << IntString(encode_tasks[0].bs.MaxLength*8/1000); - } + TEXT("\r\n max bitrate: ") << IntString(max_bitrate) << + TEXT("\r\n buffer size: ") << IntString(encode_tasks[0].bs.MaxLength * 8 / 1000) << + TEXT("\r\n rate control: ") << name; return strInfo; } @@ -1163,5 +1260,17 @@ VideoEncoder* CreateQSVEncoder(int fps, int width, int height, int quality, CTST } } - return new QSVEncoder(fps, width, height, quality, preset, bUse444, colorDesc, maxBitRate, bufferSize, bUseCFR); + try + { + return new QSVEncoder(fps, width, height, quality, preset, bUse444, colorDesc, maxBitRate, bufferSize, bUseCFR); + } + catch (CTSTR str) + { + errors << str; + return nullptr; + } + catch (...) + { + CrashError(L"Caught unhandled exception"); + } }