Add custom parameters for QSV rate control modes

Custom parameters can be set via profile settings. This also allows
selecting newer modes such as look ahead modes on Haswell+.
master
palana 2014-08-12 23:57:53 +02:00
parent 2697fa9893
commit 0b0b5d28e6
7 changed files with 264 additions and 22 deletions

View File

@ -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:

View File

@ -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;

View File

@ -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<mfxU16>(max_bitrate);
params.mfx.MaxKbps = saturate<mfxU16>(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<mfxU16>(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;

View File

@ -51,7 +51,8 @@ public:
mfxVideoParam* operator->() { return &params; }
const mfxVideoParam* operator->() const { return &params; }
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();

View File

@ -52,7 +52,7 @@ void InitFrame(mfxFrameData &frame, mfxU8 *Y, mfxU8 *UV, mfxU8 *V, mfxU16 pitch)
frame.Pitch = pitch;
}
std::vector<mfxU8> InitSEIUserData(bool use_cbr, const mfxVideoParam& params, const mfxVersion& ver)
std::vector<mfxU8> 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<mfxU8> 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<mfxU8> data;
@ -73,14 +91,59 @@ std::vector<mfxU8> 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_));

View File

@ -42,3 +42,39 @@ static inline T saturate(U val)
return std::numeric_limits<T>::min();
return val;
}
template <class T, class U>
static inline void saturate(T &t, U val)
{
if (val > std::numeric_limits<T>::max())
t = std::numeric_limits<T>::max();
else if (val < std::numeric_limits<T>::min())
t = std::numeric_limits<T>::min();
else
t = static_cast<T>(val);
}
template <typename T, typename U, typename V>
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;
}

View File

@ -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<BYTE> 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");
}
}