/* qsv_common.c
*
* Copyright (c) 2003-2025 HandBrake Team
* This file is part of the HandBrake source code.
* Homepage: .
* It may be used under the terms of the GNU General Public License v2.
* For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html
*/
#include "handbrake/handbrake.h"
#include "handbrake/project.h"
#if HB_PROJECT_FEATURE_QSV
#include
#include
#include "vpl/mfxvideo.h"
#include "vpl/mfxdispatcher.h"
#include "handbrake/ports.h"
#include "handbrake/common.h"
#include "handbrake/hwaccel.h"
#include "handbrake/hb_dict.h"
#include "handbrake/qsv_common.h"
#include "handbrake/h264_common.h"
#include "handbrake/h265_common.h"
#include "handbrake/av1_common.h"
#include "handbrake/hbffmpeg.h"
#ifndef HB_QSV_PRINT_RET_MSG
#define HB_QSV_PRINT_RET_MSG(ERR) { fprintf(stderr, "Error code %d,\t%s\t%d\n", ERR, __FUNCTION__, __LINE__); }
#endif
#ifndef HB_QSV_DEBUG_ASSERT
#define HB_QSV_DEBUG_ASSERT(x,y) { if ((x)) { fprintf(stderr, "\nASSERT: %s\n", y); } }
#endif
#define HB_QSV_CHECK_RET(P, X, ERR) {if ((X) > (P)) {HB_QSV_PRINT_RET_MSG(ERR); return;}}
#define HB_QSV_CHECK_RESULT(P, X, ERR) {if ((X) > (P)) {HB_QSV_PRINT_RET_MSG(ERR); return ERR;}}
#define HB_QSV_CHECK_POINTER(P, ERR) {if (!(P)) {HB_QSV_PRINT_RET_MSG(ERR); return ERR;}}
#define HB_QSV_IGNORE_MFX_STS(P, X) {if ((X) == (P)) {P = MFX_ERR_NONE;}}
#define HB_QSV_ASYNC_DEPTH_DEFAULT 4
#define HB_QSV_AVC_DECODER_WIDTH_MAX 4096
#define HB_QSV_AVC_DECODER_HEIGHT_MAX 4096
typedef struct hb_qsv_adapter_details
{
// DirectX index
int index;
mfxPlatform platform;
char *impl_name;
char *impl_path;
// QSV info for each codec
hb_qsv_info_t *hb_qsv_info_avc;
hb_qsv_info_t *hb_qsv_info_hevc;
hb_qsv_info_t *hb_qsv_info_av1;
// API versions
mfxVersion qsv_software_version;
mfxVersion qsv_hardware_version;
// AVC implementations
hb_qsv_info_t qsv_software_info_avc;
hb_qsv_info_t qsv_hardware_info_avc;
// HEVC implementations
hb_qsv_info_t qsv_software_info_hevc;
hb_qsv_info_t qsv_hardware_info_hevc;
// AV1 implementations
hb_qsv_info_t qsv_hardware_info_av1;
// Extended device information
mfxExtendedDeviceId extended_device_id;
} hb_qsv_adapter_details_t;
static hb_list_t *g_qsv_adapters_list = NULL;
static hb_list_t *g_qsv_adapters_details_list = NULL;
static int g_adapter_index = 0;
static int g_default_adapter_index = 0;
static int qsv_init_done = 0;
static int qsv_init_result = 0;
static void init_adapter_details(hb_qsv_adapter_details_t *adapter_details)
{
adapter_details->index = 0;
adapter_details->platform.CodeName = MFX_PLATFORM_UNKNOWN;
adapter_details->platform.MediaAdapterType = MFX_MEDIA_UNKNOWN;
// QSV info for each codec
adapter_details->hb_qsv_info_avc = NULL;
adapter_details->hb_qsv_info_hevc = NULL;
adapter_details->hb_qsv_info_av1 = NULL;
// API versions
adapter_details->qsv_software_version.Version = 0;
adapter_details->qsv_hardware_version.Version = 0;
// AVC implementations
adapter_details->qsv_software_info_avc.available = 0;
adapter_details->qsv_software_info_avc.codec_id = MFX_CODEC_AVC;
adapter_details->qsv_software_info_avc.implementation = MFX_IMPL_SOFTWARE;
adapter_details->qsv_hardware_info_avc.available = 0;
adapter_details->qsv_hardware_info_avc.codec_id = MFX_CODEC_AVC;
adapter_details->qsv_hardware_info_avc.implementation = MFX_IMPL_HARDWARE_ANY|MFX_IMPL_VIA_ANY;
// HEVC implementations
adapter_details->qsv_software_info_hevc.available = 0;
adapter_details->qsv_software_info_hevc.codec_id = MFX_CODEC_HEVC;
adapter_details->qsv_software_info_hevc.implementation = MFX_IMPL_SOFTWARE;
adapter_details->qsv_hardware_info_hevc.available = 0;
adapter_details->qsv_hardware_info_hevc.codec_id = MFX_CODEC_HEVC;
adapter_details->qsv_hardware_info_hevc.implementation = MFX_IMPL_HARDWARE_ANY|MFX_IMPL_VIA_ANY;
// AV1 implementations
adapter_details->qsv_hardware_info_av1.available = 0;
adapter_details->qsv_hardware_info_av1.codec_id = MFX_CODEC_AV1;
adapter_details->qsv_hardware_info_av1.implementation = MFX_IMPL_HARDWARE_ANY|MFX_IMPL_VIA_ANY;
}
// QSV info about adapters
static const char* hb_qsv_get_adapter_type(const hb_qsv_adapter_details_t *details);
static int hb_qsv_make_adapters_list(hb_list_t **qsv_adapters_list, hb_list_t **qsv_adapters_details_list);
static int hb_qsv_collect_adapters_details(hb_list_t *hb_qsv_adapter_details_list);
// QSV-supported profile and level lists (not all exposed to the user)
static hb_triplet_t hb_qsv_h264_profiles[] =
{
{ "Baseline", "baseline", MFX_PROFILE_AVC_BASELINE, },
{ "Main", "main", MFX_PROFILE_AVC_MAIN, },
{ "Extended", "extended", MFX_PROFILE_AVC_EXTENDED, },
{ "High", "high", MFX_PROFILE_AVC_HIGH, },
{ "High 4:2:2", "high422", MFX_PROFILE_AVC_HIGH_422, },
{ "Constrained Baseline", "baseline|set1", MFX_PROFILE_AVC_CONSTRAINED_BASELINE, },
{ "Constrained High", "high|set4|set5", MFX_PROFILE_AVC_CONSTRAINED_HIGH, },
{ "Progressive High", "high|set4", MFX_PROFILE_AVC_PROGRESSIVE_HIGH, },
{ NULL, },
};
static hb_triplet_t hb_qsv_h265_profiles[] =
{
{ "Main", "main", MFX_PROFILE_HEVC_MAIN, },
{ "Main 10", "main10", MFX_PROFILE_HEVC_MAIN10, },
{ "Main Still Picture", "mainstillpicture", MFX_PROFILE_HEVC_MAINSP, },
{ NULL, },
};
static const char * const hb_qsv_h265_profiles_names_10bit[] = {
"auto",
"main10",
NULL,
};
static hb_triplet_t hb_qsv_av1_profiles[] =
{
{ "Main", "main", MFX_PROFILE_AV1_MAIN, },
{ "High", "high", MFX_PROFILE_AV1_HIGH, },
{ "Professional", "professional", MFX_PROFILE_AV1_PRO, },
{ NULL, },
};
static const char * const hb_qsv_av1_profiles_names[] =
{
"auto",
"main",
NULL,
};
static hb_triplet_t hb_qsv_vpp_scale_modes[] =
{
{ "auto", "auto", MFX_SCALING_MODE_DEFAULT, },
{ "lowpower", "low_power", MFX_SCALING_MODE_LOWPOWER, },
{ "hq", "hq", MFX_SCALING_MODE_QUALITY, },
{ "compute", "compute", 3 },
{ "vd", "vd", 4 },
{ "ve", "ve", 5 },
{ NULL, },
};
static hb_triplet_t hb_qsv_memory_types[] =
{
{ "System memory", "system", MFX_IOPATTERN_OUT_SYSTEM_MEMORY, },
{ "Video memory", "video", MFX_IOPATTERN_OUT_VIDEO_MEMORY, },
{ NULL, },
};
static hb_triplet_t hb_qsv_hyper_encode_modes[] =
{
{ "Hyper Encode off", "off", MFX_HYPERMODE_OFF, },
{ "Hyper Encode on", "on", MFX_HYPERMODE_ON, },
{ "Hyper Encode adaptive", "adaptive", MFX_HYPERMODE_ADAPTIVE, },
{ NULL, },
};
static const enum AVPixelFormat hb_qsv_pix_fmts[] =
{
AV_PIX_FMT_NV12, AV_PIX_FMT_NONE
};
static const enum AVPixelFormat hb_qsv_10bit_pix_fmts[] =
{
AV_PIX_FMT_P010LE, AV_PIX_FMT_NONE
};
#define MFX_IMPL_VIA_MASK(impl) (0x0f00 & (impl))
// check available Intel Media SDK version against a minimum
#define HB_CHECK_MFX_VERSION(MFX_VERSION, MAJOR, MINOR) \
((MFX_VERSION.Major * 1000 + MFX_VERSION.Minor) >= (MAJOR * 1000 + MINOR))
int hb_qsv_get_adapter_index()
{
return g_adapter_index;
}
static int hb_qsv_set_default_adapter_index(int adapter_index)
{
g_default_adapter_index = adapter_index;
return 0;
}
static int hb_qsv_get_default_adapter_index()
{
return g_default_adapter_index;
}
hb_list_t* hb_qsv_adapters_list()
{
return g_qsv_adapters_list;
}
static hb_qsv_adapter_details_t* hb_qsv_get_adapters_details_by_index(int adapter_index)
{
for (int i = 0; i < hb_list_count(g_qsv_adapters_details_list); i++)
{
const hb_qsv_adapter_details_t *details = hb_list_item(g_qsv_adapters_details_list, i);
if (details->index == adapter_index || adapter_index == -1)
{
return details;
}
}
return NULL;
}
int hb_qsv_get_adapter_render_node(int adapter_index)
{
const hb_qsv_adapter_details_t *details = hb_qsv_get_adapters_details_by_index(adapter_index);
return details->extended_device_id.DRMRenderNodeNum;
}
int hb_qsv_set_adapter_index(int adapter_index)
{
if (g_adapter_index == adapter_index)
return 0;
for (int i = 0; i < hb_list_count(g_qsv_adapters_details_list); i++)
{
const hb_qsv_adapter_details_t *details = hb_list_item(g_qsv_adapters_details_list, i);
if (details && (details->index == adapter_index))
{
g_adapter_index = adapter_index;
return 0;
}
}
hb_error("hb_qsv_set_adapter_index: incorrect qsv device index %d", adapter_index);
return -1;
}
static int qsv_impl_set_preferred(hb_qsv_adapter_details_t *details, const char *name)
{
if (name == NULL || details == NULL)
{
return -1;
}
if (!strcasecmp(name, "software"))
{
if (details->qsv_software_info_avc.available)
{
details->hb_qsv_info_avc = &details->qsv_software_info_avc;
}
if (details->qsv_software_info_hevc.available)
{
details->hb_qsv_info_hevc = &details->qsv_software_info_hevc;
}
return 0;
}
if (!strcasecmp(name, "hardware"))
{
if (details->qsv_hardware_info_avc.available)
{
details->hb_qsv_info_avc = &details->qsv_hardware_info_avc;
}
if (details->qsv_hardware_info_hevc.available)
{
details->hb_qsv_info_hevc = &details->qsv_hardware_info_hevc;
}
if (details->qsv_hardware_info_av1.available)
{
details->hb_qsv_info_av1 = &details->qsv_hardware_info_av1;
}
return 0;
}
return -1;
}
int hb_qsv_impl_set_preferred(const char *name)
{
hb_qsv_adapter_details_t* details = hb_qsv_get_adapters_details_by_index(hb_qsv_get_adapter_index());
return qsv_impl_set_preferred(details, name);
}
int hb_qsv_hardware_generation(int cpu_platform)
{
switch (cpu_platform)
{
case HB_CPU_PLATFORM_INTEL_BNL:
return QSV_G0;
case HB_CPU_PLATFORM_INTEL_SNB:
return QSV_G1;
case HB_CPU_PLATFORM_INTEL_IVB:
case HB_CPU_PLATFORM_INTEL_SLM:
case HB_CPU_PLATFORM_INTEL_CHT:
return QSV_G2;
case HB_CPU_PLATFORM_INTEL_HSW:
return QSV_G3;
case HB_CPU_PLATFORM_INTEL_BDW:
return QSV_G4;
case HB_CPU_PLATFORM_INTEL_SKL:
return QSV_G5;
case HB_CPU_PLATFORM_INTEL_KBL:
case HB_CPU_PLATFORM_INTEL_CML:
return QSV_G6;
case HB_CPU_PLATFORM_INTEL_ICL:
return QSV_G7;
case HB_CPU_PLATFORM_INTEL_TGL:
case HB_CPU_PLATFORM_INTEL_ADL:
return QSV_G8;
case HB_CPU_PLATFORM_INTEL_DG2:
return QSV_G9;
case HB_CPU_PLATFORM_INTEL_LNL:
return QSV_G10;
default:
return QSV_FU;
}
}
int qsv_map_mfx_platform_codename(int mfx_platform_codename)
{
int platform = HB_CPU_PLATFORM_UNSPECIFIED;
switch (mfx_platform_codename)
{
case MFX_PLATFORM_SANDYBRIDGE:
platform = HB_CPU_PLATFORM_INTEL_SNB;
break;
case MFX_PLATFORM_IVYBRIDGE:
platform = HB_CPU_PLATFORM_INTEL_IVB;
break;
case MFX_PLATFORM_HASWELL:
platform = HB_CPU_PLATFORM_INTEL_HSW;
break;
case MFX_PLATFORM_BAYTRAIL:
case MFX_PLATFORM_BROADWELL:
platform = HB_CPU_PLATFORM_INTEL_BDW;
break;
case MFX_PLATFORM_CHERRYTRAIL:
platform = HB_CPU_PLATFORM_INTEL_CHT;
break;
case MFX_PLATFORM_SKYLAKE:
platform = HB_CPU_PLATFORM_INTEL_SKL;
break;
case MFX_PLATFORM_APOLLOLAKE:
case MFX_PLATFORM_KABYLAKE:
platform = HB_CPU_PLATFORM_INTEL_KBL;
break;
#if (MFX_VERSION >= 1025)
case MFX_PLATFORM_GEMINILAKE:
case MFX_PLATFORM_COFFEELAKE:
case MFX_PLATFORM_CANNONLAKE:
platform = HB_CPU_PLATFORM_INTEL_KBL;
break;
#endif
#if (MFX_VERSION >= 1027)
case MFX_PLATFORM_ICELAKE:
platform = HB_CPU_PLATFORM_INTEL_ICL;
break;
#endif
case MFX_PLATFORM_ELKHARTLAKE:
case MFX_PLATFORM_JASPERLAKE:
case MFX_PLATFORM_TIGERLAKE:
case MFX_PLATFORM_ROCKETLAKE:
platform = HB_CPU_PLATFORM_INTEL_TGL;
break;
case MFX_PLATFORM_ALDERLAKE_S:
case MFX_PLATFORM_ALDERLAKE_P:
platform = HB_CPU_PLATFORM_INTEL_ADL;
break;
case MFX_PLATFORM_ARCTICSOUND_P:
case MFX_PLATFORM_DG2:
case MFX_PLATFORM_ALDERLAKE_N:
case MFX_PLATFORM_KEEMBAY:
case MFX_PLATFORM_METEORLAKE:
case MFX_PLATFORM_BATTLEMAGE:
case MFX_PLATFORM_ARROWLAKE:
platform = HB_CPU_PLATFORM_INTEL_DG2;
break;
case MFX_PLATFORM_LUNARLAKE:
platform = HB_CPU_PLATFORM_INTEL_LNL;
break;
default:
platform = HB_CPU_PLATFORM_UNSPECIFIED;
}
return platform;
}
static const char* hb_qsv_get_adapter_type(const hb_qsv_adapter_details_t *details)
{
if (details)
{
return (details->platform.MediaAdapterType == MFX_MEDIA_INTEGRATED) ? "integrated" :
(details->platform.MediaAdapterType == MFX_MEDIA_DISCRETE) ? "discrete" : "unknown";
}
return NULL;
}
/*
* Determine whether a given mfxIMPL is hardware-accelerated.
*/
int hb_qsv_implementation_is_hardware(mfxIMPL implementation)
{
return MFX_IMPL_BASETYPE(implementation) != MFX_IMPL_SOFTWARE;
}
int hb_qsv_info_init()
{
int result;
result = hb_qsv_make_adapters_list(&g_qsv_adapters_list, &g_qsv_adapters_details_list);
if (result != 0)
{
hb_error("hb_qsv_info_init: hb_qsv_make_adapters_list failed");
return result;
}
result = hb_qsv_collect_adapters_details(g_qsv_adapters_details_list);
if (result != 0)
{
hb_error("hb_qsv_info_init: hb_qsv_collect_adapters_details failed");
return result;
}
if (hb_list_count(g_qsv_adapters_details_list) == 0)
{
hb_deep_log(1, "hb_qsv_info_init: g_qsv_adapters_details_list has no adapters");
return -1;
}
return 0;
}
#if defined(_WIN32) || defined(__MINGW32__)
static void hb_qsv_free_adapters_details(hb_list_t *qsv_adapters_details_list)
{
for (int i = 0; i < hb_list_count(qsv_adapters_details_list); i++)
{
hb_qsv_adapter_details_t *details = hb_list_item(qsv_adapters_details_list, i);
if (details)
{
av_free(details);
}
}
}
#endif
void hb_qsv_info_close()
{
if (g_qsv_adapters_details_list)
{
#if defined(_WIN32) || defined(__MINGW32__)
hb_qsv_free_adapters_details(g_qsv_adapters_details_list);
#endif
hb_list_close(&g_qsv_adapters_details_list);
g_qsv_adapters_details_list = NULL;
}
if (g_qsv_adapters_list)
{
hb_list_close(&g_qsv_adapters_list);
g_qsv_adapters_list = NULL;
}
}
static int hb_qsv_make_adapters_list(hb_list_t **qsv_adapters_list, hb_list_t **qsv_adapters_details_list)
{
if (!qsv_adapters_list)
{
hb_error("hb_qsv_make_adapters_list: qsv_adapters_list destination pointer is NULL");
return -1;
}
if (*qsv_adapters_list)
{
hb_error("hb_qsv_make_adapters_list: qsv_adapters_list is allocated already");
return -1;
}
if (!qsv_adapters_details_list)
{
hb_error("hb_qsv_make_adapters_list: qsv_adapters_details_list destination pointer is NULL");
return -1;
}
if (*qsv_adapters_details_list)
{
hb_error("hb_qsv_make_adapters_list: qsv_adapter_details_list is allocated already");
return -1;
}
hb_list_t *list = hb_list_init();
if (list == NULL)
{
hb_error("hb_qsv_make_adapters_list: hb_list_init() failed");
return -1;
}
hb_list_t *list2 = hb_list_init();
if (list == NULL)
{
hb_error("hb_qsv_make_adapters_list: hb_list_init() failed");
return -1;
}
mfxLoader loader = MFXLoad();
if (loader == NULL)
{
hb_error("hb_qsv_make_adapters_list: Error - MFXLoad() returned null - no libraries found\n");
return -1;
}
mfxConfig config = MFXCreateConfig(loader);
int i = 0;
mfxImplDescription *idesc = NULL;
int max_generation = QSV_G0;
int default_adapter = 0;
mfxVariant var = {};
var.Version.Version = MFX_VARIANT_VERSION;
mfxStatus err = MFX_ERR_NONE;
var.Type = MFX_VARIANT_TYPE_U32;
var.Data.U32 = MFX_IMPL_TYPE_HARDWARE;
err = MFXSetConfigFilterProperty(config, (const mfxU8 *)"mfxImplDescription.Impl", var);
if (err != MFX_ERR_NONE)
hb_error("hb_qsv_make_adapters_list: MFXSetConfigFilterProperty mfxImplDescription.Impl error=%d", err);
#if defined(_WIN32) || defined(__MINGW32__)
var.Type = MFX_VARIANT_TYPE_U32;
var.Data.U32 = MFX_ACCEL_MODE_VIA_D3D11;
err = MFXSetConfigFilterProperty(config, (const mfxU8 *)"mfxImplDescription.AccelerationMode", var);
if (err != MFX_ERR_NONE)
hb_error("hb_qsv_make_adapters_list: MFXSetConfigFilterProperty mfxImplDescription.AccelerationMode error=%d", err);
#endif
var.Type = MFX_VARIANT_TYPE_U32;
var.Data.U32 = 0x8086;
MFXSetConfigFilterProperty(config, (const mfxU8 *)"mfxImplDescription.VendorID", var);
if (err != MFX_ERR_NONE)
hb_error("hb_qsv_make_adapters_list: MFXSetConfigFilterProperty mfxImplDescription.VendorID error=%d", err);
while (1)
{
err = MFXEnumImplementations(loader,
i,
MFX_IMPLCAPS_IMPLDESCSTRUCTURE,
(mfxHDL *)(&idesc));
if (err != MFX_ERR_NONE)
{
if (err != MFX_ERR_NOT_FOUND)
{
hb_error("hb_qsv_make_adapters_list: MFXEnumImplementations returns %d", err);
}
break;
}
mfxSession session = NULL;
err = MFXCreateSession(loader, i, &session);
if (err == MFX_ERR_NONE)
{
hb_qsv_adapter_details_t *adapter_details = av_mallocz(sizeof(hb_qsv_adapter_details_t));
if (!adapter_details)
{
hb_error("hb_qsv_make_adapters_list: adapter_details allocation failed");
return -1;
}
int *adapter_index = av_mallocz(sizeof(int));
if (!adapter_index)
{
hb_error("hb_qsv_make_adapters_list: adapter_index allocation failed");
return -1;
}
init_adapter_details(adapter_details);
// On Linux VendorImplID number of the device starting from 0
adapter_details->index = idesc->VendorImplID;
adapter_details->impl_name = strdup( (const char *)idesc->ImplName);
*adapter_index = idesc->VendorImplID;
mfxHDL impl_path = NULL;
err = MFXEnumImplementations(loader, i, MFX_IMPLCAPS_IMPLPATH, &impl_path);
if (err == MFX_ERR_NONE)
{
if (impl_path)
{
adapter_details->impl_path = strdup( (const char *)impl_path);
MFXDispReleaseImplDescription(loader, impl_path);
}
}
else
{
hb_error("hb_qsv_make_adapters_list: MFXEnumImplementations MFX_IMPLCAPS_IMPLPATH failed impl=%d err=%d", i, err);
}
mfxExtendedDeviceId *idescDevice;
err = MFXEnumImplementations(loader,
i,
MFX_IMPLCAPS_DEVICE_ID_EXTENDED,
(mfxHDL *)(&idescDevice));
if (err == MFX_ERR_NONE) {
adapter_details->extended_device_id = *idescDevice;
MFXDispReleaseImplDescription(loader, idescDevice);
}
hb_list_add(list, (void*)adapter_index);
hb_list_add(list2, (void*)adapter_details);
// On Linux, the handle to the VA display must be set.
// This code is essentially a NOP other platforms.
hb_display_t *display = hb_qsv_display_init(adapter_details->extended_device_id.DRMRenderNodeNum);
if (display != NULL)
{
MFXVideoCORE_SetHandle(session, display->mfxType,
(mfxHDL)display->handle);
}
mfxPlatform platform = { 0 };
err = MFXVideoCORE_QueryPlatform(session, &platform);
if (MFX_ERR_NONE == err)
{
int generation = hb_qsv_hardware_generation(qsv_map_mfx_platform_codename(platform.CodeName));
// select default QSV adapter
if (generation > max_generation)
{
max_generation = generation;
default_adapter = idesc->VendorImplID;
}
adapter_details->platform = platform;
}
else
{
hb_error("hb_qsv_make_adapters_list: MFXVideoCORE_QueryPlatform failed impl=%d err=%d", i, err);
}
MFXClose(session);
// display must be closed after MFXClose
hb_display_close(&display);
}
else
{
hb_error("hb_qsv_make_adapters_list: MFXCreateSession failed impl=%d err=%d", i, err);
}
MFXDispReleaseImplDescription(loader, idesc);
i++;
}
MFXUnload(loader);
*qsv_adapters_list = list;
*qsv_adapters_details_list = list2;
hb_qsv_set_default_adapter_index(default_adapter);
hb_qsv_set_adapter_index(default_adapter);
return 0;
}
/**
* Check the actual availability of QSV implementations on the system
* and collect GPU adapters capabilities.
*
* @returns encoder codec mask supported by QSV implemenation,
* 0 if QSV is not available, -1 if HB_PROJECT_FEATURE_QSV is not enabled
*/
int hb_qsv_available()
{
if (hb_is_hardware_disabled())
{
return 0;
}
if (qsv_init_done != 0)
{
// This method gets called a lot. Don't probe hardware each time.
return qsv_init_result;
}
qsv_init_done = 1;
int result = hb_qsv_info_init();
if (result != 0)
{
hb_log("qsv: not available on this system");
qsv_init_result = 0;
return qsv_init_result;
}
hb_log("qsv: is available on this system");
// Return the codec capabilities for the highest platform generation
qsv_init_result = ((hb_qsv_video_encoder_is_available(HB_VCODEC_FFMPEG_QSV_H264) ? HB_VCODEC_FFMPEG_QSV_H264 : 0) |
(hb_qsv_video_encoder_is_available(HB_VCODEC_FFMPEG_QSV_H265) ? HB_VCODEC_FFMPEG_QSV_H265 : 0) |
(hb_qsv_video_encoder_is_available(HB_VCODEC_FFMPEG_QSV_H265_10BIT) ? HB_VCODEC_FFMPEG_QSV_H265_10BIT : 0) |
(hb_qsv_video_encoder_is_available(HB_VCODEC_FFMPEG_QSV_AV1) ? HB_VCODEC_FFMPEG_QSV_AV1 : 0) |
(hb_qsv_video_encoder_is_available(HB_VCODEC_FFMPEG_QSV_AV1_10BIT) ? HB_VCODEC_FFMPEG_QSV_AV1_10BIT : 0));
return qsv_init_result;
}
int hb_qsv_hyper_encode_available(int adapter_index)
{
const hb_qsv_adapter_details_t *details = hb_qsv_get_adapters_details_by_index(adapter_index);
if (details)
{
return (details->hb_qsv_info_avc != NULL && details->hb_qsv_info_avc->capabilities & HB_QSV_CAP_HYPERENCODE) ||
(details->hb_qsv_info_hevc != NULL && details->hb_qsv_info_hevc->capabilities & HB_QSV_CAP_HYPERENCODE) ||
(details->hb_qsv_info_av1 != NULL && details->hb_qsv_info_av1->capabilities & HB_QSV_CAP_HYPERENCODE);
}
else
{
return 0;
}
}
int hb_qsv_is_ffmpeg_supported_codec(int vcodec)
{
if (vcodec == HB_VCODEC_FFMPEG_QSV_H264 ||
vcodec == HB_VCODEC_FFMPEG_QSV_H265 ||
vcodec == HB_VCODEC_FFMPEG_QSV_H265_10BIT ||
vcodec == HB_VCODEC_FFMPEG_QSV_AV1 ||
vcodec == HB_VCODEC_FFMPEG_QSV_AV1_10BIT
)
{
return 1;
}
return 0;
}
int hb_qsv_video_encoder_is_available(int encoder)
{
for (int i = 0; i < hb_list_count(g_qsv_adapters_details_list); i++)
{
const hb_qsv_adapter_details_t *details = hb_list_item(g_qsv_adapters_details_list, i);
if (hb_qsv_adapter_video_encoder_is_available(details->index, encoder))
{
return 1;
}
}
return 0;
}
int hb_qsv_adapter_video_encoder_is_available(int adapter_index, int encoder)
{
const hb_qsv_adapter_details_t *details = hb_qsv_get_adapters_details_by_index(adapter_index);
if (hb_qsv_hardware_generation(hb_qsv_get_platform(adapter_index)) < QSV_G5)
{
return 0;
}
if (details)
{
switch (encoder)
{
case HB_VCODEC_FFMPEG_QSV_H264:
return details->hb_qsv_info_avc != NULL && details->hb_qsv_info_avc->available;
case HB_VCODEC_FFMPEG_QSV_H265_10BIT:
if (hb_qsv_hardware_generation(hb_qsv_get_platform(adapter_index)) < QSV_G6)
return 0;
case HB_VCODEC_FFMPEG_QSV_H265:
return details->hb_qsv_info_hevc != NULL && details->hb_qsv_info_hevc->available;
case HB_VCODEC_FFMPEG_QSV_AV1_10BIT:
case HB_VCODEC_FFMPEG_QSV_AV1:
return details->hb_qsv_info_av1 != NULL && details->hb_qsv_info_av1->available;
default:
return 0;
}
}
else
{
return 0;
}
}
static void init_video_param(mfxVideoParam *videoParam)
{
if (videoParam == NULL)
{
return;
}
memset(videoParam, 0, sizeof(mfxVideoParam));
videoParam->mfx.CodecId = MFX_CODEC_AVC;
videoParam->mfx.CodecLevel = MFX_LEVEL_UNKNOWN;
videoParam->mfx.CodecProfile = MFX_PROFILE_UNKNOWN;
videoParam->mfx.RateControlMethod = MFX_RATECONTROL_VBR;
videoParam->mfx.TargetUsage = MFX_TARGETUSAGE_BALANCED;
videoParam->mfx.TargetKbps = 5000;
videoParam->mfx.GopOptFlag = MFX_GOP_CLOSED;
videoParam->mfx.FrameInfo.FourCC = MFX_FOURCC_NV12;
videoParam->mfx.FrameInfo.ChromaFormat = MFX_CHROMAFORMAT_YUV420;
videoParam->mfx.FrameInfo.PicStruct = MFX_PICSTRUCT_PROGRESSIVE;
videoParam->mfx.FrameInfo.FrameRateExtN = 25;
videoParam->mfx.FrameInfo.FrameRateExtD = 1;
videoParam->mfx.FrameInfo.Width = 1920;
videoParam->mfx.FrameInfo.CropW = 1920;
videoParam->mfx.FrameInfo.AspectRatioW = 1;
videoParam->mfx.FrameInfo.Height = 1088;
videoParam->mfx.FrameInfo.CropH = 1080;
videoParam->mfx.FrameInfo.AspectRatioH = 1;
videoParam->AsyncDepth = HB_QSV_ASYNC_DEPTH_DEFAULT;
videoParam->IOPattern = MFX_IOPATTERN_IN_SYSTEM_MEMORY;
}
static void init_video_hyperencode_param(mfxVideoParam *videoParam, int codec_id)
{
if (videoParam == NULL)
{
return;
}
// Both GPUs must support of the same encoder parameters
if (codec_id == MFX_CODEC_HEVC)
{
videoParam->mfx.IdrInterval = 1;
}
else if (codec_id == MFX_CODEC_AVC)
{
videoParam->mfx.IdrInterval = 0;
// Relax ARC Gfx encoding settings to align ADL Gfx capabilities
videoParam->mfx.GopRefDist = 1;
}
videoParam->mfx.GopPicSize = 60;
videoParam->AsyncDepth = 60;
}
static void init_ext_video_signal_info(mfxExtVideoSignalInfo *extVideoSignalInfo)
{
if (extVideoSignalInfo == NULL)
{
return;
}
memset(extVideoSignalInfo, 0, sizeof(mfxExtVideoSignalInfo));
extVideoSignalInfo->Header.BufferId = MFX_EXTBUFF_VIDEO_SIGNAL_INFO;
extVideoSignalInfo->Header.BufferSz = sizeof(mfxExtVideoSignalInfo);
extVideoSignalInfo->VideoFormat = 5; // undefined
extVideoSignalInfo->VideoFullRange = 0; // TV range
extVideoSignalInfo->ColourDescriptionPresent = 0; // don't write to bitstream
extVideoSignalInfo->ColourPrimaries = 2; // undefined
extVideoSignalInfo->TransferCharacteristics = 2; // undefined
extVideoSignalInfo->MatrixCoefficients = 2; // undefined
}
static void init_ext_chroma_loc_info(mfxExtChromaLocInfo *extChromaLocInfo)
{
if (extChromaLocInfo == NULL)
{
return;
}
memset(extChromaLocInfo, 0, sizeof(mfxExtChromaLocInfo));
extChromaLocInfo->Header.BufferId = MFX_EXTBUFF_CHROMA_LOC_INFO;
extChromaLocInfo->Header.BufferSz = sizeof(mfxExtChromaLocInfo);
}
static void init_ext_mastering_display_colour_volume(mfxExtMasteringDisplayColourVolume *extMasteringDisplayColourVolume)
{
if (extMasteringDisplayColourVolume == NULL)
{
return;
}
memset(extMasteringDisplayColourVolume, 0, sizeof(mfxExtMasteringDisplayColourVolume));
extMasteringDisplayColourVolume->Header.BufferId = MFX_EXTBUFF_MASTERING_DISPLAY_COLOUR_VOLUME;
extMasteringDisplayColourVolume->Header.BufferSz = sizeof(mfxExtMasteringDisplayColourVolume);
extMasteringDisplayColourVolume->InsertPayloadToggle = MFX_PAYLOAD_OFF;
}
static void init_ext_content_light_level_info(mfxExtContentLightLevelInfo *extContentLightLevelInfo)
{
if (extContentLightLevelInfo == NULL)
{
return;
}
memset(extContentLightLevelInfo, 0, sizeof(mfxExtContentLightLevelInfo));
extContentLightLevelInfo->Header.BufferId = MFX_EXTBUFF_CONTENT_LIGHT_LEVEL_INFO;
extContentLightLevelInfo->Header.BufferSz = sizeof(mfxExtContentLightLevelInfo);
extContentLightLevelInfo->InsertPayloadToggle = MFX_PAYLOAD_OFF;
}
static void init_ext_hyperencode_option(mfxExtHyperModeParam *extHyperEncodemParam)
{
if (extHyperEncodemParam == NULL)
{
return;
}
memset(extHyperEncodemParam, 0, sizeof(mfxExtHyperModeParam));
extHyperEncodemParam->Header.BufferId = MFX_EXTBUFF_HYPER_MODE_PARAM;
extHyperEncodemParam->Header.BufferSz = sizeof(mfxExtHyperModeParam);
extHyperEncodemParam->Mode = MFX_HYPERMODE_OFF;
}
static void init_ext_coding_option(mfxExtCodingOption *extCodingOption)
{
if (extCodingOption == NULL)
{
return;
}
memset(extCodingOption, 0, sizeof(mfxExtCodingOption));
extCodingOption->Header.BufferId = MFX_EXTBUFF_CODING_OPTION;
extCodingOption->Header.BufferSz = sizeof(mfxExtCodingOption);
extCodingOption->AUDelimiter = MFX_CODINGOPTION_OFF;
extCodingOption->PicTimingSEI = MFX_CODINGOPTION_OFF;
extCodingOption->CAVLC = MFX_CODINGOPTION_OFF;
}
static void init_ext_coding_option2(mfxExtCodingOption2 *extCodingOption2)
{
if (extCodingOption2 == NULL)
{
return;
}
memset(extCodingOption2, 0, sizeof(mfxExtCodingOption2));
extCodingOption2->Header.BufferId = MFX_EXTBUFF_CODING_OPTION2;
extCodingOption2->Header.BufferSz = sizeof(mfxExtCodingOption2);
extCodingOption2->MBBRC = MFX_CODINGOPTION_ON;
extCodingOption2->ExtBRC = MFX_CODINGOPTION_ON;
extCodingOption2->Trellis = MFX_TRELLIS_I|MFX_TRELLIS_P|MFX_TRELLIS_B;
extCodingOption2->RepeatPPS = MFX_CODINGOPTION_ON;
extCodingOption2->BRefType = MFX_B_REF_PYRAMID;
extCodingOption2->AdaptiveI = MFX_CODINGOPTION_ON;
extCodingOption2->AdaptiveB = MFX_CODINGOPTION_ON;
extCodingOption2->LookAheadDS = MFX_LOOKAHEAD_DS_4x;
extCodingOption2->NumMbPerSlice = 2040; // 1920x1088/4
}
static void init_ext_av1bitstream_option(mfxExtAV1BitstreamParam *extAV1BitstreamParam)
{
if (extAV1BitstreamParam == NULL)
{
return;
}
memset(extAV1BitstreamParam, 0, sizeof(mfxExtAV1BitstreamParam));
extAV1BitstreamParam->Header.BufferId = MFX_EXTBUFF_AV1_BITSTREAM_PARAM;
extAV1BitstreamParam->Header.BufferSz = sizeof(mfxExtAV1BitstreamParam);
extAV1BitstreamParam->WriteIVFHeaders = MFX_CODINGOPTION_OFF;
}
static void init_ext_av1screencontent_tools(mfxExtAV1ScreenContentTools *extScreenContentTools)
{
if (extScreenContentTools == NULL)
{
return;
}
memset(extScreenContentTools, 0, sizeof(mfxExtAV1ScreenContentTools));
extScreenContentTools->Header.BufferId = MFX_EXTBUFF_AV1_SCREEN_CONTENT_TOOLS;
extScreenContentTools->Header.BufferSz = sizeof(mfxExtAV1ScreenContentTools);
extScreenContentTools->IntraBlockCopy = MFX_CODINGOPTION_OFF;
extScreenContentTools->Palette = MFX_CODINGOPTION_OFF;
}
static int query_capabilities(mfxSession session, int index, mfxVersion version, hb_qsv_info_t *info, int lowpower)
{
/*
* MFXVideoENCODE_Query(mfxSession, mfxVideoParam *in, mfxVideoParam *out);
*
* Mode 1:
* - in is NULL
* - out has the parameters we want to query set to 1
* - out->mfx.CodecId field has to be set (mandatory)
* - MFXVideoENCODE_Query should zero out all unsupported parameters
*
* Mode 2:
* - the parameters we want to query are set for in
* - in ->mfx.CodecId field has to be set (mandatory)
* - out->mfx.CodecId field has to be set (mandatory)
* - MFXVideoENCODE_Query should sanitize all unsupported parameters
*/
mfxStatus status;
mfxExtBuffer *videoExtParam[1];
mfxVideoParam videoParam, inputParam;
mfxExtCodingOption extCodingOption;
mfxExtCodingOption2 extCodingOption2;
mfxExtVideoSignalInfo extVideoSignalInfo;
mfxExtChromaLocInfo extChromaLocInfo;
mfxExtMasteringDisplayColourVolume extMasteringDisplayColourVolume;
mfxExtContentLightLevelInfo extContentLightLevelInfo;
mfxExtAV1BitstreamParam extAV1BitstreamParam;
mfxExtAV1ScreenContentTools extAV1ScreenContentToolsParam;
mfxExtHyperModeParam extHyperEncodeParam;
/* Reset capabilities before querying */
info->capabilities = 0;
/* Disable lowpower if the encoder is software */
if (hb_qsv_implementation_is_hardware(info->implementation) == 0)
{
lowpower = 0;
}
/*
* First of all, check availability of an encoder for
* this combination of a codec ID and implementation.
*
* Note: can error out rather than sanitizing
* unsupported codec IDs, so don't log errors.
*/
if (HB_CHECK_MFX_VERSION(version, HB_QSV_MINVERSION_MAJOR, HB_QSV_MINVERSION_MINOR))
{
{
mfxStatus mfxRes;
init_video_param(&inputParam);
inputParam.mfx.CodecId = info->codec_id;
inputParam.mfx.LowPower = lowpower;
memset(&videoParam, 0, sizeof(mfxVideoParam));
videoParam.mfx.CodecId = inputParam.mfx.CodecId;
mfxRes = MFXVideoENCODE_Query(session, &inputParam, &videoParam);
if (mfxRes >= MFX_ERR_NONE &&
videoParam.mfx.CodecId == info->codec_id)
{
/*
* MFXVideoENCODE_Query might tell you that an HEVC encoder is
* available on Haswell hardware, but it'll fail to initialize.
* So check encoder availability with MFXVideoENCODE_Init too.
*/
if ((status = MFXVideoENCODE_Init(session, &videoParam)) >= MFX_ERR_NONE)
{
info->available = 1;
}
else if (info->codec_id == MFX_CODEC_AVC)
{
/*
* This should not fail for AVC encoders, so we want to know
* about it - however, it may fail for other encoders (ignore)
*/
fprintf(stderr,
"query_capabilities: MFXVideoENCODE_Init failed"
" (0x%"PRIX32", 0x%"PRIX32", %d)\n",
info->codec_id, info->implementation, status);
}
MFXVideoENCODE_Close(session);
}
}
}
if (!info->available)
{
/* Don't check capabilities for unavailable encoders */
return 0;
}
{
/* Implementation-specific features that can't be queried */
if (info->codec_id == MFX_CODEC_AVC || info->codec_id == MFX_CODEC_HEVC || info->codec_id == MFX_CODEC_AV1)
{
if (hb_qsv_implementation_is_hardware(info->implementation))
{
if (hb_qsv_hardware_generation(hb_qsv_get_platform(index)) >= QSV_G3)
{
info->capabilities |= HB_QSV_CAP_B_REF_PYRAMID;
}
if (info->codec_id == MFX_CODEC_AVC &&
(hb_qsv_hardware_generation(hb_qsv_get_platform(index)) >= QSV_G7))
{
info->capabilities |= HB_QSV_CAP_LOWPOWER_ENCODE;
}
if (info->codec_id == MFX_CODEC_HEVC &&
(hb_qsv_hardware_generation(hb_qsv_get_platform(index)) >= QSV_G7))
{
info->capabilities |= HB_QSV_CAP_LOWPOWER_ENCODE;
}
if (info->codec_id == MFX_CODEC_AV1 &&
(hb_qsv_hardware_generation(hb_qsv_get_platform(index)) > QSV_G8))
{
info->capabilities |= HB_QSV_CAP_LOWPOWER_ENCODE;
}
}
else
{
if (HB_CHECK_MFX_VERSION(version, 1, 6))
{
info->capabilities |= HB_QSV_CAP_B_REF_PYRAMID;
}
}
}
/* API-specific features that can't be queried */
if (HB_CHECK_MFX_VERSION(version, 1, 6))
{
// API >= 1.6 (mfxBitstream::DecodeTimeStamp, mfxExtCodingOption2)
info->capabilities |= HB_QSV_CAP_MSDK_API_1_6;
}
/*
* Check availability of optional rate control methods.
*
* Mode 2 tends to error out, but mode 1 gives false negatives, which
* is worse. So use mode 2 and assume an error means it's unsupported.
*
* Also assume that LA and ICQ combined imply LA_ICQ is
* supported, so we don't need to check the latter too.
*/
if (HB_CHECK_MFX_VERSION(version, 1, 7))
{
init_video_param(&inputParam);
inputParam.mfx.CodecId = info->codec_id;
inputParam.mfx.LowPower = lowpower;
inputParam.mfx.RateControlMethod = MFX_RATECONTROL_LA;
inputParam.mfx.TargetKbps = 5000;
memset(&videoParam, 0, sizeof(mfxVideoParam));
videoParam.mfx.CodecId = inputParam.mfx.CodecId;
if (MFXVideoENCODE_Query(session, &inputParam, &videoParam) >= MFX_ERR_NONE &&
videoParam.mfx.RateControlMethod == MFX_RATECONTROL_LA)
{
info->capabilities |= HB_QSV_CAP_RATECONTROL_LA;
// also check for LA + interlaced support
init_video_param(&inputParam);
inputParam.mfx.CodecId = info->codec_id;
inputParam.mfx.LowPower = lowpower;
inputParam.mfx.RateControlMethod = MFX_RATECONTROL_LA;
inputParam.mfx.FrameInfo.PicStruct = MFX_PICSTRUCT_FIELD_TFF;
inputParam.mfx.TargetKbps = 5000;
memset(&videoParam, 0, sizeof(mfxVideoParam));
videoParam.mfx.CodecId = inputParam.mfx.CodecId;
if (MFXVideoENCODE_Query(session, &inputParam, &videoParam) >= MFX_ERR_NONE &&
videoParam.mfx.FrameInfo.PicStruct == MFX_PICSTRUCT_FIELD_TFF &&
videoParam.mfx.RateControlMethod == MFX_RATECONTROL_LA)
{
info->capabilities |= HB_QSV_CAP_RATECONTROL_LAi;
}
}
}
if (HB_CHECK_MFX_VERSION(version, 1, 8))
{
init_video_param(&inputParam);
inputParam.mfx.CodecId = info->codec_id;
inputParam.mfx.LowPower = lowpower;
inputParam.mfx.RateControlMethod = MFX_RATECONTROL_ICQ;
inputParam.mfx.ICQQuality = 20;
memset(&videoParam, 0, sizeof(mfxVideoParam));
videoParam.mfx.CodecId = inputParam.mfx.CodecId;
if (MFXVideoENCODE_Query(session, &inputParam, &videoParam) >= MFX_ERR_NONE &&
videoParam.mfx.RateControlMethod == MFX_RATECONTROL_ICQ)
{
info->capabilities |= HB_QSV_CAP_RATECONTROL_ICQ;
}
}
/*
* Determine whether mfxExtVideoSignalInfo is supported.
*/
if (HB_CHECK_MFX_VERSION(version, 1, 3))
{
init_video_param(&videoParam);
videoParam.mfx.CodecId = info->codec_id;
videoParam.mfx.LowPower = lowpower;
init_ext_video_signal_info(&extVideoSignalInfo);
videoParam.ExtParam = videoExtParam;
videoParam.ExtParam[0] = (mfxExtBuffer*)&extVideoSignalInfo;
videoParam.NumExtParam = 1;
status = MFXVideoENCODE_Query(session, NULL, &videoParam);
if (status >= MFX_ERR_NONE)
{
/* Encoder can be configured via mfxExtVideoSignalInfo */
info->capabilities |= HB_QSV_CAP_VUI_VSINFO;
}
else if (info->codec_id == MFX_CODEC_AVC)
{
/*
* This should not fail for AVC encoders, so we want to know
* about it - however, it may fail for other encoders (ignore)
*/
fprintf(stderr,
"query_capabilities: mfxExtVideoSignalInfo check"
" failed (0x%"PRIX32", 0x%"PRIX32", %d)\n",
info->codec_id, info->implementation, status);
}
}
/*
* Determine whether mfxExtCodingOption is supported.
*/
if (HB_CHECK_MFX_VERSION(version, 1, 0))
{
init_video_param(&videoParam);
videoParam.mfx.CodecId = info->codec_id;
videoParam.mfx.LowPower = lowpower;
init_ext_coding_option(&extCodingOption);
videoParam.ExtParam = videoExtParam;
videoParam.ExtParam[0] = (mfxExtBuffer*)&extCodingOption;
videoParam.NumExtParam = 1;
status = MFXVideoENCODE_Query(session, NULL, &videoParam);
if (status >= MFX_ERR_NONE)
{
/* Encoder can be configured via mfxExtCodingOption */
info->capabilities |= HB_QSV_CAP_OPTION1;
}
else if (info->codec_id == MFX_CODEC_AVC)
{
/*
* This should not fail for AVC encoders, so we want to know
* about it - however, it may fail for other encoders (ignore)
*/
fprintf(stderr,
"query_capabilities: mfxExtCodingOption check"
" failed (0x%"PRIX32", 0x%"PRIX32", %d)\n",
info->codec_id, info->implementation, status);
}
}
/*
* Determine whether mfxExtCodingOption2 and its fields are supported.
*
* Mode 2 suffers from false negatives with some drivers, whereas mode 1
* suffers from false positives instead. The latter is probably easier
* and/or safer to sanitize for us, so use mode 1.
*/
if (HB_CHECK_MFX_VERSION(version, 1, 6))
{
init_video_param(&videoParam);
videoParam.mfx.CodecId = info->codec_id;
videoParam.mfx.LowPower = lowpower;
init_ext_coding_option2(&extCodingOption2);
videoParam.ExtParam = videoExtParam;
videoParam.ExtParam[0] = (mfxExtBuffer*)&extCodingOption2;
videoParam.NumExtParam = 1;
status = MFXVideoENCODE_Query(session, NULL, &videoParam);
if (status >= MFX_ERR_NONE)
{
#if 0
// testing code that could come in handy
fprintf(stderr, "-------------------\n");
fprintf(stderr, "MBBRC: 0x%02X\n", extCodingOption2.MBBRC);
fprintf(stderr, "ExtBRC: 0x%02X\n", extCodingOption2.ExtBRC);
fprintf(stderr, "Trellis: 0x%02X\n", extCodingOption2.Trellis);
fprintf(stderr, "RepeatPPS: 0x%02X\n", extCodingOption2.RepeatPPS);
fprintf(stderr, "BRefType: %4"PRIu16"\n", extCodingOption2.BRefType);
fprintf(stderr, "AdaptiveI: 0x%02X\n", extCodingOption2.AdaptiveI);
fprintf(stderr, "AdaptiveB: 0x%02X\n", extCodingOption2.AdaptiveB);
fprintf(stderr, "LookAheadDS: %4"PRIu16"\n", extCodingOption2.LookAheadDS);
fprintf(stderr, "-------------------\n");
#endif
/* Encoder can be configured via mfxExtCodingOption2 */
info->capabilities |= HB_QSV_CAP_OPTION2;
/*
* Sanitize API 1.6 fields:
*
* - MBBRC requires G3 hardware (Haswell or equivalent)
* - ExtBRC requires G2 hardware (Ivy Bridge or equivalent)
*/
if (hb_qsv_implementation_is_hardware(info->implementation) &&
hb_qsv_hardware_generation(hb_qsv_get_platform(index)) >= QSV_G3)
{
if (extCodingOption2.MBBRC)
{
info->capabilities |= HB_QSV_CAP_OPTION2_MBBRC;
}
}
if (hb_qsv_implementation_is_hardware(info->implementation) &&
hb_qsv_hardware_generation(hb_qsv_get_platform(index)) >= QSV_G2)
{
if (extCodingOption2.ExtBRC)
{
info->capabilities |= HB_QSV_CAP_OPTION2_EXTBRC;
}
}
/*
* Sanitize API 1.7 fields:
*
* - Trellis requires G3 hardware (Haswell or equivalent)
*/
if (HB_CHECK_MFX_VERSION(version, 1, 7))
{
if (hb_qsv_implementation_is_hardware(info->implementation) &&
hb_qsv_hardware_generation(hb_qsv_get_platform(index)) >= QSV_G3)
{
if (extCodingOption2.Trellis)
{
info->capabilities |= HB_QSV_CAP_OPTION2_TRELLIS;
}
}
}
/*
* Sanitize API 1.8 fields:
*
* - BRefType requires B-pyramid support
* - LookAheadDS requires lookahead support
* - AdaptiveI, AdaptiveB, NumMbPerSlice unknown (trust Query)
*/
if (HB_CHECK_MFX_VERSION(version, 1, 8))
{
if (extCodingOption2.RepeatPPS)
{
info->capabilities |= HB_QSV_CAP_OPTION2_REPEATPPS;
}
if (info->capabilities & HB_QSV_CAP_B_REF_PYRAMID)
{
if (extCodingOption2.BRefType)
{
info->capabilities |= HB_QSV_CAP_OPTION2_BREFTYPE;
}
}
if (info->capabilities & HB_QSV_CAP_RATECONTROL_LA)
{
if (extCodingOption2.LookAheadDS)
{
info->capabilities |= HB_QSV_CAP_OPTION2_LA_DOWNS;
}
}
if (extCodingOption2.AdaptiveI && extCodingOption2.AdaptiveB)
{
info->capabilities |= HB_QSV_CAP_OPTION2_IB_ADAPT;
}
if (extCodingOption2.NumMbPerSlice)
{
info->capabilities |= HB_QSV_CAP_OPTION2_NMPSLICE;
}
}
}
else
{
fprintf(stderr,
"query_capabilities: mfxExtCodingOption2 check failed (0x%"PRIX32", 0x%"PRIX32", %d)\n",
info->codec_id, info->implementation, status);
}
}
if (HB_CHECK_MFX_VERSION(version, 1, 13) && info->codec_id == MFX_CODEC_AVC)
{
init_video_param(&videoParam);
videoParam.mfx.CodecId = info->codec_id;
videoParam.mfx.LowPower = lowpower;
init_ext_chroma_loc_info(&extChromaLocInfo);
videoParam.ExtParam = videoExtParam;
videoParam.ExtParam[0] = (mfxExtBuffer*)&extChromaLocInfo;
videoParam.NumExtParam = 1;
status = MFXVideoENCODE_Query(session, NULL, &videoParam);
if (status >= MFX_ERR_NONE)
{
/* Encoder can be configured via mfxExtChromaLocInfo */
info->capabilities |= HB_QSV_CAP_VUI_CHROMALOCINFO;
}
}
if (HB_CHECK_MFX_VERSION(version, 1, 19))
{
if (hb_qsv_hardware_generation(hb_qsv_get_platform(index)) >= QSV_G7)
{
info->capabilities |= HB_QSV_CAP_VPP_SCALING;
}
}
if (HB_CHECK_MFX_VERSION(version, 1, 25) && (info->codec_id == MFX_CODEC_HEVC || info->codec_id == MFX_CODEC_AV1))
{
init_video_param(&videoParam);
videoParam.mfx.CodecId = info->codec_id;
videoParam.mfx.LowPower = lowpower;
init_ext_mastering_display_colour_volume(&extMasteringDisplayColourVolume);
videoParam.ExtParam = videoExtParam;
videoParam.ExtParam[0] = (mfxExtBuffer*)&extMasteringDisplayColourVolume;
videoParam.NumExtParam = 1;
status = MFXVideoENCODE_Query(session, NULL, &videoParam);
if (status >= MFX_ERR_NONE)
{
/* Encoder can be configured via mfxExtMasteringDisplayColourVolume */
info->capabilities |= HB_QSV_CAP_VUI_MASTERINGINFO;
}
init_video_param(&videoParam);
videoParam.mfx.CodecId = info->codec_id;
videoParam.mfx.LowPower = lowpower;
init_ext_content_light_level_info(&extContentLightLevelInfo);
videoParam.ExtParam = videoExtParam;
videoParam.ExtParam[0] = (mfxExtBuffer*)&extContentLightLevelInfo;
videoParam.NumExtParam = 1;
status = MFXVideoENCODE_Query(session, NULL, &videoParam);
if (status >= MFX_ERR_NONE)
{
/* Encoder can be configured via mfxExtContentLightLevelInfo */
info->capabilities |= HB_QSV_CAP_VUI_CLLINFO;
}
}
if (HB_CHECK_MFX_VERSION(version, 1, 33))
{
if (hb_qsv_hardware_generation(hb_qsv_get_platform(index)) >= QSV_G7)
{
info->capabilities |= HB_QSV_CAP_VPP_INTERPOLATION;
}
}
if (lowpower == MFX_CODINGOPTION_ON)
{
init_video_param(&videoParam);
videoParam.mfx.CodecId = info->codec_id;
init_video_hyperencode_param(&videoParam, info->codec_id);
videoParam.mfx.LowPower = lowpower;
init_ext_hyperencode_option(&extHyperEncodeParam);
videoParam.ExtParam = videoExtParam;
videoParam.ExtParam[0] = (mfxExtBuffer*)&extHyperEncodeParam;
videoParam.NumExtParam = 1;
status = MFXVideoENCODE_Query(session, NULL, &videoParam);
if (status >= MFX_ERR_NONE && extHyperEncodeParam.Mode == MFX_HYPERMODE_ON)
{
info->capabilities |= HB_QSV_CAP_HYPERENCODE;
}
}
if ((lowpower == MFX_CODINGOPTION_ON) && (info->codec_id == MFX_CODEC_AV1))
{
init_video_param(&videoParam);
videoParam.mfx.CodecId = info->codec_id;
videoParam.mfx.LowPower = lowpower;
init_ext_av1bitstream_option(&extAV1BitstreamParam);
videoParam.ExtParam = videoExtParam;
videoParam.ExtParam[0] = (mfxExtBuffer*)&extAV1BitstreamParam;
videoParam.NumExtParam = 1;
status = MFXVideoENCODE_Query(session, NULL, &videoParam);
if (status >= MFX_ERR_NONE)
{
info->capabilities |= HB_QSV_CAP_AV1_BITSTREAM;
}
}
if ((lowpower == MFX_CODINGOPTION_ON) && (info->codec_id == MFX_CODEC_AV1))
{
init_video_param(&videoParam);
videoParam.mfx.CodecId = info->codec_id;
videoParam.mfx.LowPower = lowpower;
init_ext_av1screencontent_tools(&extAV1ScreenContentToolsParam);
videoParam.ExtParam = videoExtParam;
videoParam.ExtParam[0] = (mfxExtBuffer*)&extAV1ScreenContentToolsParam;
videoParam.NumExtParam = 1;
status = MFXVideoENCODE_Query(session, NULL, &videoParam);
if (status >= MFX_ERR_NONE && extAV1ScreenContentToolsParam.IntraBlockCopy != 0)
{
info->capabilities |= HB_QSV_CAP_AV1_SCREENCONTENT;
}
}
}
return 0;
}
const char * DRM_INTEL_DRIVER_NAME = "i915";
const char * VA_INTEL_DRIVER_NAMES[] = { "iHD", "i965", NULL};
hb_display_t * hb_qsv_display_init(const uint32_t dri_render_node)
{
return hb_display_init(DRM_INTEL_DRIVER_NAME, dri_render_node, VA_INTEL_DRIVER_NAMES);
}
#if defined(_WIN32) || defined(__MINGW32__)
mfxIMPL hb_qsv_dx_index_to_impl(int dx_index)
{
mfxIMPL impl;
switch (dx_index)
{
{
case 0:
impl = MFX_IMPL_HARDWARE;
break;
case 1:
impl = MFX_IMPL_HARDWARE2;
break;
case 2:
impl = MFX_IMPL_HARDWARE3;
break;
case 3:
impl = MFX_IMPL_HARDWARE4;
break;
default:
// try searching on all display adapters
impl = MFX_IMPL_HARDWARE_ANY;
break;
}
}
return impl;
}
#endif
// Adopted implementation of qsv_create_mfx_session() function for HandBrake
int hb_qsv_create_mfx_session(mfxIMPL implementation,
int drmRenderNodeNum,
mfxVersion *pver,
mfxSession *psession)
{
mfxStatus sts;
mfxLoader loader = NULL;
mfxSession session = NULL;
mfxConfig cfg;
mfxVersion ver;
mfxVariant impl_value;
uint32_t adapter_idx = 0;
uint32_t impl_idx = 0;
// Get adapter from MediaSDK implementation value
adapter_idx = hb_qsv_impl_get_num(implementation);
*psession = NULL;
loader = MFXLoad();
if (!loader) {
hb_error("hb_qsv_create_mfx_session: Error creating a MFX loader");
goto fail;
}
/* Create configurations for implementation */
cfg = MFXCreateConfig(loader);
if (!cfg) {
hb_error("hb_qsv_create_mfx_session: Error creating a MFX configuration");
goto fail;
}
impl_value.Type = MFX_VARIANT_TYPE_U32;
impl_value.Data.U32 = (implementation == MFX_IMPL_SOFTWARE) ?
MFX_IMPL_TYPE_SOFTWARE : MFX_IMPL_TYPE_HARDWARE;
sts = MFXSetConfigFilterProperty(cfg,
(const mfxU8 *)"mfxImplDescription.Impl", impl_value);
if (sts != MFX_ERR_NONE) {
hb_error("hb_qsv_create_mfx_session: Error adding a MFX configuration "
"property: %d.", sts);
goto fail;
}
if (MFX_IMPL_VIA_D3D11 == MFX_IMPL_VIA_MASK(implementation))
{
impl_value.Type = MFX_VARIANT_TYPE_U32;
impl_value.Data.U32 = MFX_ACCEL_MODE_VIA_D3D11;
sts = MFXSetConfigFilterProperty(cfg,
(const mfxU8 *)"mfxImplDescription.AccelerationMode", impl_value);
if (sts != MFX_ERR_NONE) {
hb_error("hb_qsv_create_mfx_session: Error adding a MFX configuration"
"MFX_ACCEL_MODE_VIA_D3D11 property: %d.", sts);
goto fail;
}
if (adapter_idx != -1) {
impl_value.Type = MFX_VARIANT_TYPE_U32;
impl_value.Data.U32 = adapter_idx;
sts = MFXSetConfigFilterProperty(cfg,
(const mfxU8 *)"mfxImplDescription.VendorImplID", impl_value);
if (sts != MFX_ERR_NONE) {
hb_error("hb_qsv_create_mfx_session: Error adding a MFX configuration"
"VendorImplID property: %d.", sts);
goto fail;
}
}
}
else
{
impl_value.Type = MFX_VARIANT_TYPE_U32;
impl_value.Data.U32 = drmRenderNodeNum;
sts = MFXSetConfigFilterProperty(cfg,
(const mfxU8 *)"mfxExtendedDeviceId.DRMRenderNodeNum", impl_value);
if (sts != MFX_ERR_NONE) {
hb_error("hb_qsv_create_mfx_session: Error adding a MFX configuration DRMRenderNodeNum property: %d.", sts);
goto fail;
}
}
impl_value.Type = MFX_VARIANT_TYPE_U32;
impl_value.Data.U32 = 0x8086;
sts = MFXSetConfigFilterProperty(cfg, (const mfxU8 *)"mfxImplDescription.VendorID", impl_value);
if (sts != MFX_ERR_NONE) {
hb_error("hb_qsv_create_mfx_session: MFXSetConfigFilterProperty mfxImplDescription.VendorID error=%d", sts);
goto fail;
}
impl_value.Type = MFX_VARIANT_TYPE_U32;
impl_value.Data.U32 = pver->Version;
sts = MFXSetConfigFilterProperty(cfg,
(const mfxU8 *)"mfxImplDescription.ApiVersion.Version",
impl_value);
if (sts != MFX_ERR_NONE) {
hb_error("hb_qsv_create_mfx_session: Error adding a MFX configuration "
"property: %d.", sts);
goto fail;
}
while (1) {
/* Enumerate all implementations */
mfxImplDescription *impl_desc;
sts = MFXEnumImplementations(loader, impl_idx,
MFX_IMPLCAPS_IMPLDESCSTRUCTURE,
(mfxHDL *)&impl_desc);
/* Failed to find an available implementation */
if (sts == MFX_ERR_NOT_FOUND)
break;
else if (sts != MFX_ERR_NONE) {
impl_idx++;
continue;
}
sts = MFXCreateSession(loader, impl_idx, &session);
MFXDispReleaseImplDescription(loader, impl_desc);
if (sts == MFX_ERR_NONE)
break;
impl_idx++;
}
if (sts != MFX_ERR_NONE) {
hb_error("hb_qsv_create_mfx_session: Error creating a MFX session: %d.", sts);
goto fail;
}
sts = MFXQueryVersion(session, &ver);
if (sts != MFX_ERR_NONE) {
hb_error("hb_qsv_create_mfx_session: Error querying a MFX session: %d.", sts);
goto fail;
}
*psession = session;
MFXUnload(loader);
return 0;
fail:
if (session)
MFXClose(session);
MFXUnload(loader);
return AVERROR_UNKNOWN;
}
static int hb_qsv_collect_adapters_details(hb_list_t *hb_qsv_adapter_details_list)
{
for (int i = 0; i < hb_list_count(hb_qsv_adapter_details_list); i++)
{
hb_qsv_adapter_details_t *details = hb_list_item(hb_qsv_adapter_details_list, i);
/*
* First, check for any MSDK version to determine whether one or
* more implementations are present; then check if we can use them.
*
* I've had issues using a NULL version with some combinations of
* hardware and driver, so use a low version number (1.0) instead.
*/
mfxSession session;
mfxVersion version = { .Major = 1, .Minor = 0, };
// check for software fallback
if (MFXInit(MFX_IMPL_SOFTWARE, &version, &session) == MFX_ERR_NONE)
{
// Media SDK software found, but check that our minimum is supported
MFXQueryVersion(session, &details->qsv_software_version);
if (HB_CHECK_MFX_VERSION(details->qsv_software_version,
HB_QSV_MINVERSION_MAJOR,
HB_QSV_MINVERSION_MINOR))
{
query_capabilities(session, details->index, details->qsv_software_version, &details->qsv_software_info_avc, MFX_CODINGOPTION_OFF);
query_capabilities(session, details->index, details->qsv_software_version, &details->qsv_software_info_hevc, MFX_CODINGOPTION_OFF);
// now that we know which hardware encoders are
// available, we can set the preferred implementation
qsv_impl_set_preferred(details, "software");
}
MFXClose(session);
}
// check for actual hardware support
#if defined(_WIN32) || defined(__MINGW32__)
mfxIMPL hw_preference = MFX_IMPL_VIA_D3D11;
#else
mfxIMPL hw_preference = MFX_IMPL_VIA_ANY;
#endif
do{
#if defined(_WIN32) || defined(__MINGW32__)
mfxIMPL hw_impl = hb_qsv_dx_index_to_impl(details->index);
#else
mfxIMPL hw_impl = MFX_IMPL_HARDWARE_ANY;
#endif
if (hb_qsv_create_mfx_session(hw_impl | hw_preference, details->extended_device_id.DRMRenderNodeNum, &version, &session) == MFX_ERR_NONE)
{
// On linux, the handle to the VA display must be set.
// This code is essentially a NOP other platforms.
hb_display_t * display = hb_qsv_display_init(details->extended_device_id.DRMRenderNodeNum);
if (display != NULL)
{
MFXVideoCORE_SetHandle(session, display->mfxType,
(mfxHDL)display->handle);
}
// Media SDK hardware found, but check that our minimum is supported
//
// Note: this-party hardware (QSV_G0) is unsupported for the time being
MFXQueryVersion(session, &details->qsv_hardware_version);
if (hb_qsv_hardware_generation(hb_qsv_get_platform(details->index)) >= QSV_G1 &&
HB_CHECK_MFX_VERSION(details->qsv_hardware_version,
HB_QSV_MINVERSION_MAJOR,
HB_QSV_MINVERSION_MINOR))
{
if (hb_qsv_hardware_generation(hb_qsv_get_platform(details->index)) >= QSV_G7)
{
query_capabilities(session, details->index, details->qsv_hardware_version, &details->qsv_hardware_info_avc, MFX_CODINGOPTION_ON);
}
if (details->qsv_hardware_info_avc.available == 0)
{
query_capabilities(session, details->index, details->qsv_hardware_version, &details->qsv_hardware_info_avc, MFX_CODINGOPTION_OFF);
}
details->qsv_hardware_info_avc.implementation = hw_impl | hw_preference;
if (hb_qsv_hardware_generation(hb_qsv_get_platform(details->index)) >= QSV_G7)
{
query_capabilities(session, details->index, details->qsv_hardware_version, &details->qsv_hardware_info_hevc, MFX_CODINGOPTION_ON);
}
if (details->qsv_hardware_info_hevc.available == 0)
{
query_capabilities(session, details->index, details->qsv_hardware_version, &details->qsv_hardware_info_hevc, MFX_CODINGOPTION_OFF);
}
details->qsv_hardware_info_hevc.implementation = hw_impl | hw_preference;
if (hb_qsv_hardware_generation(hb_qsv_get_platform(details->index)) > QSV_G8)
{
query_capabilities(session, details->index, details->qsv_hardware_version, &details->qsv_hardware_info_av1, MFX_CODINGOPTION_ON);
details->qsv_hardware_info_av1.implementation = hw_impl | hw_preference;
}
// now that we know which hardware encoders are
// available, we can set the preferred implementation
qsv_impl_set_preferred(details, "hardware");
}
MFXClose(session);
// display must be closed after MFXClose
hb_display_close(&display);
hw_preference = 0;
}
else
{
#if defined(_WIN32) || defined(__MINGW32__)
// Windows only: After D3D11 we will try D3D9
if (hw_preference == MFX_IMPL_VIA_D3D11)
hw_preference = MFX_IMPL_VIA_D3D9;
else
#endif
hw_preference = 0;
}
}
while(hw_preference != 0);
}
return 0;
}
static void log_decoder_capabilities(const int log_level, const hb_qsv_adapter_details_t *adapter_details, const char *prefix)
{
char buffer[128] = "";
if (hb_qsv_decode_h264_is_supported(adapter_details->index))
{
strcat(buffer, " h264");
}
if (hb_qsv_decode_h265_10_bit_is_supported(adapter_details->index))
{
strcat(buffer, " hevc (8bit: yes, 10bit: yes)");
}
else if (hb_qsv_decode_h265_is_supported(adapter_details->index))
{
strcat(buffer, " hevc (8bit: yes, 10bit: no)");
}
if (hb_qsv_decode_av1_is_supported(adapter_details->index))
{
strcat(buffer, " av1 (8bit: yes, 10bit: yes)");
}
if (hb_qsv_decode_vvc_is_supported(adapter_details->index))
{
strcat(buffer, " vvc (8bit: yes, 10bit: yes)");
}
hb_deep_log(log_level, "%s%s", prefix,
strnlen(buffer, 1) ? buffer : " no decode support");
}
static void log_encoder_capabilities(const int log_level, const uint64_t caps, const char *prefix)
{
/*
* Note: keep the string short, as it may be logged by default.
*/
char buffer[128] = "";
if (caps & HB_QSV_CAP_LOWPOWER_ENCODE)
{
strcat(buffer, " lowpower");
}
/* B-Pyramid, with or without direct control (BRefType) */
if (caps & HB_QSV_CAP_B_REF_PYRAMID)
{
if (caps & HB_QSV_CAP_OPTION2_BREFTYPE)
{
strcat(buffer, " breftype");
}
else
{
strcat(buffer, " bpyramid");
}
}
/* Rate control: ICQ, lookahead (options: interlaced, downsampling) */
if (caps & HB_QSV_CAP_RATECONTROL_LA)
{
if (caps & HB_QSV_CAP_RATECONTROL_ICQ)
{
strcat(buffer, " icq+la");
}
else
{
strcat(buffer, " la");
}
if (caps & HB_QSV_CAP_RATECONTROL_LAi)
{
strcat(buffer, "+i");
}
if (caps & HB_QSV_CAP_OPTION2_LA_DOWNS)
{
strcat(buffer, "+downs");
}
}
else if (caps & HB_QSV_CAP_RATECONTROL_ICQ)
{
strcat(buffer, " icq");
}
if (caps & HB_QSV_CAP_VUI_VSINFO)
{
strcat(buffer, " vsinfo");
}
if (caps & HB_QSV_CAP_VUI_CHROMALOCINFO)
{
strcat(buffer, " chromalocinfo");
}
if (caps & HB_QSV_CAP_VUI_MASTERINGINFO)
{
strcat(buffer, " masteringinfo");
}
if (caps & HB_QSV_CAP_VUI_CLLINFO)
{
strcat(buffer, " cllinfo");
}
if (caps & HB_QSV_CAP_OPTION1)
{
strcat(buffer, " opt1");
}
if (caps & HB_QSV_CAP_OPTION2)
{
{
strcat(buffer, " opt2");
}
if (caps & HB_QSV_CAP_OPTION2_MBBRC)
{
strcat(buffer, "+mbbrc");
}
if (caps & HB_QSV_CAP_OPTION2_EXTBRC)
{
strcat(buffer, "+extbrc");
}
if (caps & HB_QSV_CAP_OPTION2_TRELLIS)
{
strcat(buffer, "+trellis");
}
if (caps & HB_QSV_CAP_OPTION2_REPEATPPS)
{
strcat(buffer, "+repeatpps");
}
if (caps & HB_QSV_CAP_OPTION2_IB_ADAPT)
{
strcat(buffer, "+ib_adapt");
}
if (caps & HB_QSV_CAP_OPTION2_NMPSLICE)
{
strcat(buffer, "+nmpslice");
}
}
if (caps & HB_QSV_CAP_AV1_SCREENCONTENT)
{
strcat(buffer, " av1screencontent");
}
if (caps & HB_QSV_CAP_HYPERENCODE)
{
strcat(buffer, " hyperencode");
}
if (caps & HB_QSV_CAP_AV1_BITSTREAM)
{
strcat(buffer, " av1bitstream");
}
hb_deep_log(log_level, "%s%s", prefix,
strnlen(buffer, 1) ? buffer : " standard feature set");
}
static void hb_qsv_adapter_info_print(const hb_qsv_adapter_details_t *adapter_details)
{
if (adapter_details->qsv_hardware_version.Version)
{
hb_log(" - Intel Media SDK hardware: API %"PRIu16".%"PRIu16" (minimum: %"PRIu16".%"PRIu16")",
adapter_details->qsv_hardware_version.Major, adapter_details->qsv_hardware_version.Minor,
HB_QSV_MINVERSION_MAJOR, HB_QSV_MINVERSION_MINOR);
}
if (adapter_details->qsv_software_version.Version)
{
hb_deep_log(3, " - Intel Media SDK software: API %"PRIu16".%"PRIu16" (minimum: %"PRIu16".%"PRIu16")",
adapter_details->qsv_software_version.Major, adapter_details->qsv_software_version.Minor,
HB_QSV_MINVERSION_MAJOR, HB_QSV_MINVERSION_MINOR);
}
log_decoder_capabilities(1, adapter_details, " - Decode support: ");
if (adapter_details->hb_qsv_info_avc != NULL && adapter_details->hb_qsv_info_avc->available)
{
hb_log(" - H.264 encoder: yes");
hb_log(" - preferred implementation: %s %s",
hb_qsv_impl_get_name(adapter_details->hb_qsv_info_avc->implementation),
hb_qsv_impl_get_via_name(adapter_details->hb_qsv_info_avc->implementation));
if (adapter_details->qsv_hardware_info_avc.available)
{
log_encoder_capabilities(1, adapter_details->qsv_hardware_info_avc.capabilities,
" - capabilities (hardware): ");
}
if (adapter_details->qsv_software_info_avc.available)
{
log_encoder_capabilities(3, adapter_details->qsv_software_info_avc.capabilities,
" - capabilities (software): ");
}
}
else
{
hb_log(" - H.264 encoder: no");
}
if (adapter_details->hb_qsv_info_hevc != NULL && adapter_details->hb_qsv_info_hevc->available)
{
hb_log(" - H.265 encoder: yes (8bit: yes, 10bit: %s)", (hb_qsv_hardware_generation(hb_qsv_get_platform(adapter_details->index)) < QSV_G6) ? "no" : "yes" );
hb_log(" - preferred implementation: %s %s",
hb_qsv_impl_get_name(adapter_details->hb_qsv_info_hevc->implementation),
hb_qsv_impl_get_via_name(adapter_details->hb_qsv_info_hevc->implementation));
if (adapter_details->qsv_hardware_info_hevc.available)
{
log_encoder_capabilities(1, adapter_details->qsv_hardware_info_hevc.capabilities,
" - capabilities (hardware): ");
}
if (adapter_details->qsv_software_info_hevc.available)
{
log_encoder_capabilities(3, adapter_details->qsv_software_info_hevc.capabilities,
" - capabilities (software): ");
}
}
else
{
hb_log(" - H.265 encoder: no");
}
if (adapter_details->hb_qsv_info_av1 != NULL && adapter_details->hb_qsv_info_av1->available)
{
hb_log(" - AV1 encoder: yes (8bit: yes, 10bit: yes)");
hb_log(" - preferred implementation: %s %s",
hb_qsv_impl_get_name(adapter_details->hb_qsv_info_av1->implementation),
hb_qsv_impl_get_via_name(adapter_details->hb_qsv_info_av1->implementation));
if (adapter_details->qsv_hardware_info_av1.available)
{
log_encoder_capabilities(1, adapter_details->qsv_hardware_info_av1.capabilities,
" - capabilities (hardware): ");
}
}
else
{
hb_log(" - AV1 encoder: no");
}
}
void hb_qsv_info_print()
{
// is QSV available and usable?
if (hb_qsv_available())
{
#if defined(_WIN32) || defined(__MINGW32__)
if (hb_list_count(g_qsv_adapters_details_list))
{
char gpu_list_str[256] = "";
for (int i = 0; i < hb_list_count(g_qsv_adapters_details_list); i++)
{
const hb_qsv_adapter_details_t *details = hb_list_item(g_qsv_adapters_details_list, i);
char value_str[256];
snprintf(value_str, sizeof(value_str), "%d", details->index);
if (i > 0)
strcat(gpu_list_str, ", ");
strcat(gpu_list_str, value_str);
}
hb_log("Intel Quick Sync Video support: yes, gpu list: %s", gpu_list_str);
}
else
#endif
{
hb_log("Intel Quick Sync Video support: yes");
}
// also print the details about all QSV adapters
for (int i = 0; i < hb_list_count(g_qsv_adapters_details_list); i++)
{
const hb_qsv_adapter_details_t *details = hb_list_item(g_qsv_adapters_details_list, i);
#if defined(_WIN32) || defined(__MINGW32__)
hb_log("Intel Quick Sync Video %s adapter with index %d", hb_qsv_get_adapter_type(details), details->index);
#else
hb_log("Intel Quick Sync Video %s adapter with index %d and renderD%d",
hb_qsv_get_adapter_type(details), details->index, details->extended_device_id.DRMRenderNodeNum);
#endif
hb_log("Impl %s library path: %s", details->impl_name, details->impl_path);
hb_qsv_adapter_info_print(details);
}
}
else
{
hb_log("Intel Quick Sync Video support: no");
}
}
hb_qsv_info_t* hb_qsv_encoder_info_get(int adapter_index, int encoder)
{
const hb_qsv_adapter_details_t *details = hb_qsv_get_adapters_details_by_index(adapter_index);
if (details)
{
switch (encoder)
{
case HB_VCODEC_FFMPEG_QSV_H264:
return details->hb_qsv_info_avc;
case HB_VCODEC_FFMPEG_QSV_H265_10BIT:
case HB_VCODEC_FFMPEG_QSV_H265:
return details->hb_qsv_info_hevc;
case HB_VCODEC_FFMPEG_QSV_AV1_10BIT:
case HB_VCODEC_FFMPEG_QSV_AV1:
return details->hb_qsv_info_av1;
default:
return NULL;
}
}
return NULL;
}
const char* hb_qsv_decode_get_codec_name(enum AVCodecID codec_id)
{
switch (codec_id)
{
case AV_CODEC_ID_H264:
return "h264_qsv";
case AV_CODEC_ID_HEVC:
return "hevc_qsv";
case AV_CODEC_ID_MPEG2VIDEO:
return "mpeg2_qsv";
case AV_CODEC_ID_AV1:
return "av1_qsv";
case AV_CODEC_ID_VVC:
return "vvc_qsv";
default:
return NULL;
}
}
int hb_qsv_decode_h264_is_supported(int adapter_index)
{
return hb_qsv_hardware_generation(hb_qsv_get_platform(adapter_index)) >= QSV_G1;
}
int hb_qsv_decode_h265_is_supported(int adapter_index)
{
return hb_qsv_hardware_generation(hb_qsv_get_platform(adapter_index)) >= QSV_G5;
}
int hb_qsv_decode_h265_10_bit_is_supported(int adapter_index)
{
return hb_qsv_hardware_generation(hb_qsv_get_platform(adapter_index)) >= QSV_G6;
}
int hb_qsv_decode_av1_is_supported(int adapter_index)
{
return hb_qsv_hardware_generation(hb_qsv_get_platform(adapter_index)) >= QSV_G8;
}
int hb_qsv_decode_vvc_is_supported(int adapter_index)
{
return hb_qsv_hardware_generation(hb_qsv_get_platform(adapter_index)) > QSV_G9;
}
int hb_qsv_decode_is_codec_supported(int adapter_index, int video_codec_param, int pix_fmt, int width, int height)
{
switch (video_codec_param)
{
case AV_CODEC_ID_H264:
// QSV decode for AVC does not support higher video resolutions
if (width > HB_QSV_AVC_DECODER_WIDTH_MAX || height > HB_QSV_AVC_DECODER_HEIGHT_MAX)
return 0;
if (pix_fmt == AV_PIX_FMT_NV12 ||
pix_fmt == AV_PIX_FMT_YUV420P ||
pix_fmt == AV_PIX_FMT_YUVJ420P)
{
return hb_qsv_decode_h264_is_supported(adapter_index);
}
break;
case AV_CODEC_ID_HEVC:
if (pix_fmt == AV_PIX_FMT_NV12 ||
pix_fmt == AV_PIX_FMT_YUV420P ||
pix_fmt == AV_PIX_FMT_YUVJ420P)
{
return hb_qsv_decode_h265_is_supported(adapter_index);
}
else if (pix_fmt == AV_PIX_FMT_P010LE ||
pix_fmt == AV_PIX_FMT_YUV420P10)
{
return hb_qsv_decode_h265_10_bit_is_supported(adapter_index);
}
break;
case AV_CODEC_ID_AV1:
if (pix_fmt == AV_PIX_FMT_NV12 ||
pix_fmt == AV_PIX_FMT_P010LE ||
pix_fmt == AV_PIX_FMT_YUV420P ||
pix_fmt == AV_PIX_FMT_YUVJ420P ||
pix_fmt == AV_PIX_FMT_YUV420P10)
{
return hb_qsv_decode_av1_is_supported(adapter_index);
}
break;
case AV_CODEC_ID_VVC:
if (pix_fmt == AV_PIX_FMT_NV12 ||
pix_fmt == AV_PIX_FMT_P010LE ||
pix_fmt == AV_PIX_FMT_YUV420P ||
pix_fmt == AV_PIX_FMT_YUVJ420P ||
pix_fmt == AV_PIX_FMT_YUV420P10)
{
return hb_qsv_decode_vvc_is_supported(adapter_index);
}
break;
default:
return 0;
}
return 0;
}
static int hb_qsv_parse_options(hb_job_t *job)
{
int err = 0;
if (job->encoder_options != NULL && *job->encoder_options)
{
hb_dict_t *options_list;
options_list = hb_encopts_to_dict(job->encoder_options, job->vcodec);
hb_dict_iter_t iter;
for (iter = hb_dict_iter_init(options_list);
iter != HB_DICT_ITER_DONE;
iter = hb_dict_iter_next(options_list, iter))
{
const char *key = hb_dict_iter_key(iter);
hb_value_t *value = hb_dict_iter_value(iter);
if (!strcasecmp(key, "gpu"))
{
char *str = hb_value_get_string_xform(value);
int dx_index = hb_qsv_atoi(str, &err);
free(str);
if (!err)
{
hb_log("hb_qsv_parse_options: gpu=%d", dx_index);
hb_qsv_param_parse_dx_index(job, dx_index);
}
}
else if (!strcasecmp(key, "async-depth"))
{
char *str = hb_value_get_string_xform(value);
int async_depth = hb_qsv_atoi(str, &err);
free(str);
if (!err)
{
job->hw_device_async_depth = async_depth;
}
}
else if (!strcasecmp(key, "memory-type"))
{
hb_triplet_t *mode = NULL;
mode = hb_triplet4key(hb_qsv_memory_types, hb_value_get_string_xform(value));
if (!mode)
{
err = HB_QSV_PARAM_BAD_VALUE;
}
else
{
job->qsv_ctx->memory_type = mode->value;
}
}
else if (!strcasecmp(key, "scalingmode") ||
!strcasecmp(key, "vpp-sm"))
{
hb_triplet_t *mode = NULL;
mode = hb_triplet4key(hb_qsv_vpp_scale_modes, hb_value_get_string_xform(value));
if (!mode)
{
err = HB_QSV_PARAM_BAD_VALUE;
}
else
{
job->qsv_ctx->vpp_scale_mode = mode->name;
}
}
}
hb_dict_free(&options_list);
}
return 0;
}
int hb_qsv_setup_job(hb_job_t *job)
{
if (job->qsv_ctx == NULL)
{
return 1;
}
// Parse the json parameter
if (job->hw_device_index > -1)
{
hb_qsv_param_parse_dx_index(job, job->hw_device_index);
}
else
{
job->hw_device_index = hb_qsv_get_default_adapter_index();
}
// Parse the advanced options parameter
hb_qsv_parse_options(job);
int async_depth_default = hb_qsv_param_default_async_depth();
if (job->hw_device_async_depth <= 0 || job->hw_device_async_depth > async_depth_default)
{
job->hw_device_async_depth = async_depth_default;
}
// Make sure QSV Decode is only True if the selected QSV adapter supports decode
if (job->hw_decode & HB_DECODE_QSV)
{
int is_codec_supported = hb_qsv_decode_is_codec_supported(hb_qsv_get_adapter_index(),
job->title->video_codec_param, job->input_pix_fmt,
job->title->geometry.width, job->title->geometry.height);
if (is_codec_supported == 0)
{
job->hw_decode &= ~HB_DECODE_QSV;
}
}
return 0;
}
int hb_qsv_get_memory_type(hb_job_t *job)
{
int qsv_full_path_is_enabled = hb_qsv_full_path_is_enabled(job);
if (qsv_full_path_is_enabled)
{
if (job->qsv_ctx->memory_type == MFX_IOPATTERN_OUT_VIDEO_MEMORY)
return MFX_IOPATTERN_OUT_VIDEO_MEMORY;
else if (job->qsv_ctx->memory_type == MFX_IOPATTERN_OUT_SYSTEM_MEMORY)
return MFX_IOPATTERN_OUT_SYSTEM_MEMORY;
}
return qsv_full_path_is_enabled ? MFX_IOPATTERN_OUT_VIDEO_MEMORY : MFX_IOPATTERN_OUT_SYSTEM_MEMORY;
}
static int are_filters_supported(hb_list_t *filters)
{
#if defined(_WIN32) || defined(__MINGW32__)
int num_sw_filters = 0;
for (int i = 0; i < hb_list_count(filters); i++)
{
hb_filter_object_t *filter = hb_list_item(filters, i);
switch (filter->id)
{
// pixel format conversion is done via VPP filter
case HB_FILTER_FORMAT:
// cropping and scaling always done via VPP filter
case HB_FILTER_CROP_SCALE:
case HB_FILTER_ROTATE:
case HB_FILTER_AVFILTER:
break;
case HB_FILTER_VFR:
{
// Mode 0 doesn't require access to the frame data
int mode = hb_dict_get_int(filter->settings, "mode");
if (mode == 0)
{
break;
}
}
default:
// count only filters with access to frame data
num_sw_filters++;
break;
}
}
return num_sw_filters == 0;
#else // other OS
return 0;
#endif
}
int hb_qsv_full_path_is_enabled(hb_job_t *job)
{
int qsv_full_path_is_enabled = 0;
if (!job || !job->qsv_ctx)
{
return 0;
}
#if defined(_WIN32) || defined(__MINGW32__)
hb_qsv_info_t *info = hb_qsv_encoder_info_get(hb_qsv_get_adapter_index(), job->vcodec);
qsv_full_path_is_enabled = (job->hw_decode & HB_DECODE_QSV &&
info && hb_qsv_implementation_is_hardware(info->implementation) &&
job->qsv_ctx && are_filters_supported(job->list_filter));
#endif
return qsv_full_path_is_enabled;
}
int hb_qsv_atoindex(const char* const *arr, const char *str, int *err)
{
int i;
for (i = 0; arr[i] != NULL; i++)
{
if (!strcasecmp(arr[i], str))
{
break;
}
}
*err = (arr[i] == NULL);
return i;
}
// adapted from libx264
int hb_qsv_atobool(const char *str, int *err)
{
if (!strcasecmp(str, "1") ||
!strcasecmp(str, "yes") ||
!strcasecmp(str, "true"))
{
return 1;
}
if (!strcasecmp(str, "0") ||
!strcasecmp(str, "no") ||
!strcasecmp(str, "false"))
{
return 0;
}
*err = 1;
return 0;
}
// adapted from libx264
int hb_qsv_atoi(const char *str, int *err)
{
char *end;
int v = strtol(str, &end, 0);
if (end == str || end[0] != '\0')
{
*err = 1;
}
return v;
}
// adapted from libx264
float hb_qsv_atof(const char *str, int *err)
{
char *end;
float v = strtod(str, &end);
if (end == str || end[0] != '\0')
{
*err = 1;
}
return v;
}
static void add_qsv_param(AVDictionary** av_opts, const char *key, const char *value)
{
// qsv_params=KEY=VALUE:NEXTKEY=NEXTVALUE
char c[100] = {0};
if(av_dict_get(*av_opts, "qsv_params", NULL, 0) != NULL)
{
strcat(c, ":"); // add separator if already exists
}
strcat(c, key);
strcat(c, "=");
strcat(c, value);
av_dict_set(av_opts, "qsv_params", c, AV_DICT_APPEND);
}
static void add_qsv_param_u32(AVDictionary** av_opts, const char *key, mfxU32 value)
{
if(value == 0) return; // Default == 0; skip
int len = snprintf(NULL, 0, "%"PRIu32"", value); // u16 == u32
char c[100] = {0};
snprintf(c, len + 1, "%"PRIu32"", value);
add_qsv_param(av_opts, key, c);
}
int hb_qsv_select_ffmpeg_options(qsv_data_t * qsv_data, hb_job_t *job, AVDictionary **av_opts)
{
#define MFX_STRUCT_TO_AV_OPTS(MEMBER_NAME) add_qsv_param_u32(av_opts, #MEMBER_NAME, param->videoParam->mfx.MEMBER_NAME);
hb_qsv_info_t *qsv_info = qsv_data->qsv_info;
hb_qsv_param_t *param = &qsv_data->param;
int hw_generation = hb_qsv_hardware_generation(hb_qsv_get_platform(hb_qsv_get_adapter_index()));
// sanitize ICQ
// workaround for MediaSDK platforms below TGL to disable ICQ if incorrectly detected
if (!(qsv_info->capabilities & HB_QSV_CAP_RATECONTROL_ICQ) ||
((param->videoParam->mfx.LowPower == MFX_CODINGOPTION_ON) && (hw_generation < QSV_G8)))
{
// ICQ not supported
param->rc.icq = 0;
}
else
{
param->rc.icq = param->rc.icq && job->vquality > HB_INVALID_VIDEO_QUALITY;
}
// sanitize lookahead
if (!(qsv_info->capabilities & HB_QSV_CAP_RATECONTROL_LA))
{
// lookahead not supported
param->rc.lookahead = 0;
}
else if ((param->rc.lookahead) &&
(qsv_info->capabilities & HB_QSV_CAP_RATECONTROL_LAi) == 0 &&
(param->videoParam->mfx.FrameInfo.PicStruct != MFX_PICSTRUCT_PROGRESSIVE))
{
// lookahead enabled but we can't use it
hb_log("encqsvInit: LookAhead not used (LookAhead is progressive-only)");
param->rc.lookahead = 0;
}
else
{
param->rc.lookahead = param->rc.lookahead && (param->rc.icq || job->vquality <= HB_INVALID_VIDEO_QUALITY);
}
if (job->qsv_ctx != NULL)
{
job->qsv_ctx->la_is_enabled = param->rc.lookahead ? 1 : 0;
}
// libmfx BRC parameters are 16 bits thus maybe overflow, then BRCParamMultiplier is needed
// Comparison vbitrate in Kbps (kilobit) with vbv_max_bitrate, vbv_buffer_size, vbv_buffer_init in KB (kilobyte)
int brc_param_multiplier = (FFMAX(FFMAX3(job->vbitrate, param->rc.vbv_max_bitrate, param->rc.vbv_buffer_size),
param->rc.vbv_buffer_init) + 0x10000) / 0x10000;
// set VBV here (this will be overridden for CQP and ignored for LA)
// only set BufferSizeInKB, InitialDelayInKB and MaxKbps if we have
// them - otherwise Media SDK will pick values for us automatically
if (param->rc.vbv_buffer_size > 0)
{
if (param->rc.vbv_buffer_init > 1.0)
{
param->videoParam->mfx.InitialDelayInKB = (param->rc.vbv_buffer_init / 8) / brc_param_multiplier;
MFX_STRUCT_TO_AV_OPTS(InitialDelayInKB)
}
else if (param->rc.vbv_buffer_init > 0.0)
{
param->videoParam->mfx.InitialDelayInKB = (param->rc.vbv_buffer_size *
param->rc.vbv_buffer_init / 8) / brc_param_multiplier;
MFX_STRUCT_TO_AV_OPTS(InitialDelayInKB)
}
param->videoParam->mfx.BufferSizeInKB = (param->rc.vbv_buffer_size / 8) / brc_param_multiplier;
MFX_STRUCT_TO_AV_OPTS(BufferSizeInKB)
param->videoParam->mfx.BRCParamMultiplier = brc_param_multiplier;
}
if (param->rc.vbv_max_bitrate > 0)
{
param->videoParam->mfx.MaxKbps = param->rc.vbv_max_bitrate / brc_param_multiplier;
MFX_STRUCT_TO_AV_OPTS(MaxKbps)
param->videoParam->mfx.BRCParamMultiplier = brc_param_multiplier;
}
// set rate control parameters
if (job->vquality > HB_INVALID_VIDEO_QUALITY)
{
unsigned int upper_limit = 51;
if (param->rc.icq)
{
// introduced in API 1.8
if (param->rc.lookahead)
{
param->videoParam->mfx.RateControlMethod = MFX_RATECONTROL_LA_ICQ;
}
else
{
param->videoParam->mfx.RateControlMethod = MFX_RATECONTROL_ICQ;
}
param->videoParam->mfx.ICQQuality = HB_QSV_CLIP3(1, upper_limit, job->vquality);
MFX_STRUCT_TO_AV_OPTS(ICQQuality)
}
else
{
// introduced in API 1.1
// HEVC 10b has QP range as [-12;51]
// with shift +12 needed to be in QSV's U16 range
if (param->videoParam->mfx.CodecProfile == MFX_PROFILE_HEVC_MAIN10)
{
upper_limit = 63;
}
if (param->videoParam->mfx.CodecId == MFX_CODEC_AV1)
{
upper_limit = 255;
}
param->videoParam->mfx.RateControlMethod = MFX_RATECONTROL_CQP;
param->videoParam->mfx.QPI = HB_QSV_CLIP3(0, upper_limit, job->vquality + param->rc.cqp_offsets[0]);
param->videoParam->mfx.QPP = HB_QSV_CLIP3(0, upper_limit, job->vquality + param->rc.cqp_offsets[1]);
param->videoParam->mfx.QPB = HB_QSV_CLIP3(0, upper_limit, job->vquality + param->rc.cqp_offsets[2]);
MFX_STRUCT_TO_AV_OPTS(QPI)
MFX_STRUCT_TO_AV_OPTS(QPP)
MFX_STRUCT_TO_AV_OPTS(QPB)
// CQP + ExtBRC can cause bad output
param->codingOption2.ExtBRC = MFX_CODINGOPTION_OFF;
av_dict_set(av_opts, "extbrc", "0", 0); // MFX_CODINGOPTION_OFF
}
}
else if (job->vbitrate > 0)
{
if (param->rc.lookahead)
{
// introduced in API 1.7
param->videoParam->mfx.RateControlMethod = MFX_RATECONTROL_LA;
param->videoParam->mfx.TargetKbps = job->vbitrate / brc_param_multiplier;
MFX_STRUCT_TO_AV_OPTS(TargetKbps)
param->videoParam->mfx.BRCParamMultiplier = brc_param_multiplier;
// ignored, but some drivers will change AsyncDepth because of it
param->codingOption2.ExtBRC = MFX_CODINGOPTION_OFF;
av_dict_set(av_opts, "extbrc", "0", 0); // MFX_CODINGOPTION_OFF
}
else
{
// introduced in API 1.0
if (job->vbitrate == param->rc.vbv_max_bitrate)
{
param->videoParam->mfx.RateControlMethod = MFX_RATECONTROL_CBR;
}
else
{
param->videoParam->mfx.RateControlMethod = MFX_RATECONTROL_VBR;
}
param->videoParam->mfx.TargetKbps = job->vbitrate / brc_param_multiplier;
MFX_STRUCT_TO_AV_OPTS(TargetKbps)
param->videoParam->mfx.BRCParamMultiplier = brc_param_multiplier;
}
}
else
{
hb_error("encqsvInit: invalid rate control (%f, %d)",
job->vquality, job->vbitrate);
return -1;
}
MFX_STRUCT_TO_AV_OPTS(RateControlMethod);
// if VBV is enabled but ignored, log it
if (param->rc.vbv_max_bitrate > 0 || param->rc.vbv_buffer_size > 0)
{
switch (param->videoParam->mfx.RateControlMethod)
{
case MFX_RATECONTROL_LA:
case MFX_RATECONTROL_LA_ICQ:
hb_log("encqsvInit: LookAhead enabled, ignoring VBV");
break;
case MFX_RATECONTROL_ICQ:
hb_log("encqsvInit: ICQ rate control, ignoring VBV");
break;
default:
break;
}
}
// set the GOP structure
if (param->gop.gop_ref_dist < 0)
{
if ((hw_generation >= QSV_G8) &&
(param->videoParam->mfx.CodecId == MFX_CODEC_HEVC ||
param->videoParam->mfx.CodecId == MFX_CODEC_AV1))
{
param->gop.gop_ref_dist = 8;
}
else
{
param->gop.gop_ref_dist = 4;
}
}
param->videoParam->mfx.GopRefDist = param->gop.gop_ref_dist;
// set the keyframe interval
if (param->gop.gop_pic_size < 0)
{
double rate = (double)job->orig_vrate.num / job->orig_vrate.den + 0.5;
// set the keyframe interval based on the framerate
param->gop.gop_pic_size = (int)(FFMIN(rate * 2, 120));
}
param->videoParam->mfx.GopPicSize = param->gop.gop_pic_size;
// set the Hyper Encode structure
if (param->hyperEncodeParam->value != MFX_HYPERMODE_OFF)
{
if (param->videoParam->mfx.CodecId == MFX_CODEC_HEVC)
{
param->videoParam->mfx.IdrInterval = 1;
}
else if (param->videoParam->mfx.CodecId == MFX_CODEC_AVC)
{
param->videoParam->mfx.IdrInterval = 0;
}
MFX_STRUCT_TO_AV_OPTS(IdrInterval)
// sanitize some of the encoding parameters
param->videoParam->mfx.GopPicSize = (int)(FFMIN(param->gop.gop_pic_size, 60));
param->videoParam->AsyncDepth = (int)(FFMAX(param->videoParam->AsyncDepth, 60));
av_dict_set_int(av_opts, "async_depth", param->videoParam->AsyncDepth, 0);
char hyperencode[16];
snprintf(hyperencode, sizeof(hyperencode), "%s", qsv_data->param.hyperEncodeParam->key);
av_dict_set(av_opts, "dual_gfx", hyperencode, 0);
hb_log("encavcodec: Hyper Encoding mode: %s", hyperencode);
}
MFX_STRUCT_TO_AV_OPTS(GopPicSize)
// sanitize some settings that affect memory consumption
if (!qsv_data->is_sys_mem)
{
// limit these to avoid running out of resources (causes hang)
param->videoParam->mfx.GopRefDist = FFMIN(param->videoParam->mfx.GopRefDist,
param->rc.lookahead ? 8 : 16);
param->codingOption2.LookAheadDepth = FFMIN(param->codingOption2.LookAheadDepth,
param->rc.lookahead ? (48 - param->videoParam->mfx.GopRefDist -
3 * !param->videoParam->mfx.GopRefDist) : 0);
}
else
{
// encode-only is a bit less sensitive to memory issues
param->videoParam->mfx.GopRefDist = FFMIN(param->videoParam->mfx.GopRefDist, 16);
param->codingOption2.LookAheadDepth = FFMIN(param->codingOption2.LookAheadDepth,
param->rc.lookahead ? 100 : 0);
}
MFX_STRUCT_TO_AV_OPTS(GopRefDist)
if (param->rc.lookahead)
{
// LookAheadDepth 10 will cause a hang with some driver versions
param->codingOption2.LookAheadDepth = FFMAX(param->codingOption2.LookAheadDepth, 11);
}
av_dict_set_int(av_opts, "look_ahead_depth", param->codingOption2.LookAheadDepth, 0);
if(qsv_data->qsv_info->capabilities & HB_QSV_CAP_LOWPOWER_ENCODE)
{
char low_power[7];
snprintf(low_power, sizeof(low_power), "%d", qsv_data->param.low_power);
av_dict_set(av_opts, "low_power", low_power, 0);
if(qsv_data->param.low_power)
hb_log("encavcodec: using Low Power mode");
}
if((qsv_data->qsv_info->capabilities & HB_QSV_CAP_AV1_SCREENCONTENT) &&
qsv_data->param.av1ScreenContentToolsParam.IntraBlockCopy)
{
char intrabc[7];
snprintf(intrabc, sizeof(intrabc), "%d", qsv_data->param.av1ScreenContentToolsParam.IntraBlockCopy);
av_dict_set(av_opts, "intrabc", intrabc, 0);
hb_log("encavcodec: ScreenContentCoding is enabled IBC %s",
qsv_data->param.av1ScreenContentToolsParam.IntraBlockCopy ? "on" : "off");
}
if((qsv_data->qsv_info->capabilities & HB_QSV_CAP_AV1_SCREENCONTENT) &&
qsv_data->param.av1ScreenContentToolsParam.Palette)
{
char palette_mode[7];
snprintf(palette_mode, sizeof(palette_mode), "%d", qsv_data->param.av1ScreenContentToolsParam.Palette);
av_dict_set(av_opts, "palette_mode", palette_mode, 0);
hb_log("encavcodec: ScreenContentCoding is enabled Palette %s",
qsv_data->param.av1ScreenContentToolsParam.Palette ? "on" : "off");
}
// Transcoding Info
MFX_STRUCT_TO_AV_OPTS(BRCParamMultiplier)
// scenecut
MFX_STRUCT_TO_AV_OPTS(GopOptFlag)
#undef MFX_STRUCT_TO_AV_OPTS
return 0;
}
int hb_qsv_apply_encoder_options(qsv_data_t * qsv_data, hb_job_t *job, AVDictionary **av_opts)
{
if (qsv_data == NULL)
{
hb_error("hb_qsv_apply_encoder_options: invalid pointer qsv_data=%p", qsv_data);
return -1;
}
qsv_data->qsv_info = hb_qsv_encoder_info_get(hb_qsv_get_adapter_index(), job->vcodec);
if (qsv_data->qsv_info == NULL)
{
hb_error("hb_qsv_apply_encoder_options: invalid pointer qsv_data->qsv_info=%p", qsv_data->qsv_info);
return -1;
}
mfxVideoParam videoParam = {0};
qsv_data->param.videoParam = &videoParam;
qsv_data->is_sys_mem = (hb_qsv_get_memory_type(job) == MFX_IOPATTERN_OUT_SYSTEM_MEMORY);
int ret = hb_qsv_param_default(&qsv_data->param, qsv_data->qsv_info);
if (ret)
{
return ret;
}
if (job->encoder_options != NULL && *job->encoder_options)
{
hb_dict_t *options_list;
options_list = hb_encopts_to_dict(job->encoder_options, job->vcodec);
hb_dict_iter_t iter;
for (iter = hb_dict_iter_init(options_list);
iter != HB_DICT_ITER_DONE;
iter = hb_dict_iter_next(options_list, iter))
{
const char *key = hb_dict_iter_key(iter);
hb_value_t *value = hb_dict_iter_value(iter);
char *str = hb_value_get_string_xform(value);
switch (hb_qsv_param_parse(av_opts, &qsv_data->param, qsv_data->qsv_info, job, key, str))
{
case HB_QSV_PARAM_OK:
break;
case HB_QSV_PARAM_BAD_NAME:
hb_log("qsv_encavcodecInit: hb_qsv_param_parse: bad key %s", key);
break;
case HB_QSV_PARAM_BAD_VALUE:
hb_log("qsv_encavcodecInit: hb_qsv_param_parse: bad value %s for key %s",
str, key);
break;
case HB_QSV_PARAM_UNSUPPORTED:
hb_log("qsv_encavcodecInit: hb_qsv_param_parse: unsupported option %s",
key);
break;
case HB_QSV_PARAM_ERROR:
default:
hb_log("qsv_encavcodecInit: hb_qsv_param_parse: unknown error");
break;
}
free(str);
}
hb_dict_free(&options_list);
}
ret = hb_qsv_select_ffmpeg_options(qsv_data, job, av_opts);
if(ret)
{
hb_log("encavcodec: parsing ffmpeg options failed");
return ret;
}
hb_log("encavcodec: using%s%s path",
hb_qsv_full_path_is_enabled(job) ? " full QSV" : " encode-only",
hb_qsv_get_memory_type(job) == MFX_IOPATTERN_OUT_VIDEO_MEMORY ? " via video memory" : " via system memory");
return 0;
}
int hb_qsv_param_parse(AVDictionary** av_opts, hb_qsv_param_t *param, hb_qsv_info_t *info, hb_job_t *job,
const char *key, const char *value)
{
float fvalue;
int ivalue, error = 0;
if (param == NULL || info == NULL)
{
return HB_QSV_PARAM_ERROR;
}
if (value == NULL || value[0] == '\0')
{
value = "true";
}
else if (value[0] == '=')
{
value++;
}
if (key == NULL || key[0] == '\0')
{
return HB_QSV_PARAM_BAD_NAME;
}
else if (!strncasecmp(key, "no-", 3))
{
key += 3;
value = hb_qsv_atobool(value, &error) ? "false" : "true";
if (error)
{
return HB_QSV_PARAM_BAD_VALUE;
}
}
if (!strcasecmp(key, "target-usage") ||
!strcasecmp(key, "tu"))
{
ivalue = hb_qsv_atoi(value, &error);
if (!error)
{
int target_usage = HB_QSV_CLIP3(MFX_TARGETUSAGE_1, MFX_TARGETUSAGE_7, ivalue);
switch (target_usage)
{
case MFX_TARGETUSAGE_1:
av_dict_set(av_opts, "preset", "veryslow", 0);
break;
case MFX_TARGETUSAGE_2:
av_dict_set(av_opts, "preset", "slower", 0);
break;
case MFX_TARGETUSAGE_3:
av_dict_set(av_opts, "preset", "slow", 0);
break;
case MFX_TARGETUSAGE_4:
av_dict_set(av_opts, "preset", "medium", 0);
break;
case MFX_TARGETUSAGE_5:
av_dict_set(av_opts, "preset", "fast", 0);
break;
case MFX_TARGETUSAGE_6:
av_dict_set(av_opts, "preset", "faster", 0);
break;
case MFX_TARGETUSAGE_7:
av_dict_set(av_opts, "preset", "veryfast", 0);
break;
default:
break;
}
}
}
else if (!strcasecmp(key, "num-ref-frame") ||
!strcasecmp(key, "ref"))
{
ivalue = hb_qsv_atoi(value, &error);
if (!error)
{
av_dict_set_int(av_opts, "refs", HB_QSV_CLIP3(0, 16, ivalue), 0);
}
}
else if (!strcasecmp(key, "gop-ref-dist"))
{
ivalue = hb_qsv_atoi(value, &error);
if (!error)
{
av_dict_set_int(av_opts, "bf", HB_QSV_CLIP3(-1, 32, ivalue), 0);
}
}
else if (!strcasecmp(key, "gop-pic-size") ||
!strcasecmp(key, "keyint"))
{
ivalue = hb_qsv_atoi(value, &error);
if (!error)
{
av_dict_set_int(av_opts, "g", HB_QSV_CLIP3(-1, UINT16_MAX, ivalue), 0);
}
}
else if (!strcasecmp(key, "b-pyramid"))
{
if (info->capabilities & HB_QSV_CAP_B_REF_PYRAMID)
{
ivalue = hb_qsv_atoi(value, &error);
if (!error)
{
av_dict_set_int(av_opts, "b_strategy", HB_QSV_CLIP3(-1, 1, ivalue), 0);
}
}
else
{
return HB_QSV_PARAM_UNSUPPORTED;
}
}
else if (!strcasecmp(key, "scenecut"))
{
ivalue = hb_qsv_atobool(value, &error);
if (!error)
{
if (!ivalue)
{
param->videoParam->mfx.GopOptFlag |= MFX_GOP_STRICT;
}
else
{
param->videoParam->mfx.GopOptFlag &= ~MFX_GOP_STRICT;
}
}
}
else if (!strcasecmp(key, "adaptive-i") ||
!strcasecmp(key, "i-adapt"))
{
if (info->capabilities & HB_QSV_CAP_OPTION2_IB_ADAPT)
{
ivalue = hb_qsv_atobool(value, &error);
if (!error)
{
av_dict_set_int(av_opts, "adaptive_i", ivalue, 0);
}
}
else
{
return HB_QSV_PARAM_UNSUPPORTED;
}
}
else if (!strcasecmp(key, "adaptive-b") ||
!strcasecmp(key, "b-adapt"))
{
if (info->capabilities & HB_QSV_CAP_OPTION2_IB_ADAPT)
{
ivalue = hb_qsv_atobool(value, &error);
if (!error)
{
av_dict_set_int(av_opts, "adaptive_b", ivalue, 0);
}
}
else
{
return HB_QSV_PARAM_UNSUPPORTED;
}
}
else if (!strcasecmp(key, "force-cqp"))
{
ivalue = hb_qsv_atobool(value, &error);
if (!error)
{
param->rc.icq = !ivalue;
}
}
else if (!strcasecmp(key, "cqp-offset-i"))
{
ivalue = hb_qsv_atoi(value, &error);
if (!error)
{
param->rc.cqp_offsets[0] = HB_QSV_CLIP3(INT16_MIN, INT16_MAX, ivalue);
}
}
else if (!strcasecmp(key, "cqp-offset-p"))
{
ivalue = hb_qsv_atoi(value, &error);
if (!error)
{
param->rc.cqp_offsets[1] = HB_QSV_CLIP3(INT16_MIN, INT16_MAX, ivalue);
}
}
else if (!strcasecmp(key, "cqp-offset-b"))
{
ivalue = hb_qsv_atoi(value, &error);
if (!error)
{
param->rc.cqp_offsets[2] = HB_QSV_CLIP3(INT16_MIN, INT16_MAX, ivalue);
}
}
else if (!strcasecmp(key, "vbv-init"))
{
fvalue = hb_qsv_atof(value, &error);
if (!error)
{
param->rc.vbv_buffer_init = HB_QSV_CLIP3(0, INT32_MAX, fvalue);
}
}
else if (!strcasecmp(key, "vbv-bufsize"))
{
ivalue = hb_qsv_atoi(value, &error);
if (!error)
{
param->rc.vbv_buffer_size = HB_QSV_CLIP3(0, INT32_MAX, ivalue);
}
}
else if (!strcasecmp(key, "vbv-maxrate"))
{
ivalue = hb_qsv_atoi(value, &error);
if (!error)
{
param->rc.vbv_max_bitrate = HB_QSV_CLIP3(0, INT32_MAX, ivalue);
}
}
else if (!strcasecmp(key, "cavlc") || !strcasecmp(key, "cabac"))
{
if (info->capabilities & HB_QSV_CAP_OPTION1)
{
switch (info->codec_id)
{
case MFX_CODEC_AVC:
ivalue = hb_qsv_atobool(value, &error);
break;
default:
return HB_QSV_PARAM_UNSUPPORTED;
}
}
else
{
return HB_QSV_PARAM_UNSUPPORTED;
}
if (!error)
{
if (!strcasecmp(key, "cabac"))
{
ivalue = !ivalue;
}
av_dict_set_int(av_opts, "cavlc", ivalue, 0);
}
}
else if (!strcasecmp(key, "colorprim"))
{
if (info->capabilities & HB_QSV_CAP_VUI_VSINFO)
{
switch (info->codec_id)
{
case MFX_CODEC_AVC:
ivalue = hb_qsv_atoindex(hb_h264_colorprim_names, value, &error);
break;
case MFX_CODEC_HEVC:
ivalue = hb_qsv_atoindex(hb_h265_colorprim_names, value, &error);
break;
default:
return HB_QSV_PARAM_UNSUPPORTED;
}
}
else
{
return HB_QSV_PARAM_UNSUPPORTED;
}
if (!error)
{
av_dict_set_int(av_opts, "color_primaries", ivalue, 0);
}
}
else if (!strcasecmp(key, "transfer"))
{
if (info->capabilities & HB_QSV_CAP_VUI_VSINFO)
{
switch (info->codec_id)
{
case MFX_CODEC_AVC:
ivalue = hb_qsv_atoindex(hb_h264_transfer_names, value, &error);
break;
case MFX_CODEC_HEVC:
ivalue = hb_qsv_atoindex(hb_h265_transfer_names, value, &error);
break;
default:
return HB_QSV_PARAM_UNSUPPORTED;
}
}
else
{
return HB_QSV_PARAM_UNSUPPORTED;
}
if (!error)
{
av_dict_set_int(av_opts, "color_trc", ivalue, 0);
}
}
else if (!strcasecmp(key, "colormatrix"))
{
if (info->capabilities & HB_QSV_CAP_VUI_VSINFO)
{
switch (info->codec_id)
{
case MFX_CODEC_AVC:
ivalue = hb_qsv_atoindex(hb_h264_colmatrix_names, value, &error);
break;
case MFX_CODEC_HEVC:
ivalue = hb_qsv_atoindex(hb_h265_colmatrix_names, value, &error);
break;
default:
return HB_QSV_PARAM_UNSUPPORTED;
}
}
else
{
return HB_QSV_PARAM_UNSUPPORTED;
}
if (!error)
{
av_dict_set_int(av_opts, "colorspace", ivalue, 0);
}
}
else if (!strcasecmp(key, "tff") ||
!strcasecmp(key, "interlaced"))
{
switch (info->codec_id)
{
case MFX_CODEC_AVC:
ivalue = hb_qsv_atobool(value, &error);
break;
default:
return HB_QSV_PARAM_UNSUPPORTED;
}
if (!error)
{
ivalue = ivalue ? MFX_PICSTRUCT_FIELD_TFF : MFX_PICSTRUCT_PROGRESSIVE;
av_dict_set_int(av_opts, "flags", ivalue, AV_DICT_APPEND);
}
}
else if (!strcasecmp(key, "bff"))
{
switch (info->codec_id)
{
case MFX_CODEC_AVC:
ivalue = hb_qsv_atobool(value, &error);
break;
default:
return HB_QSV_PARAM_UNSUPPORTED;
}
if (!error)
{
ivalue = ivalue ? MFX_PICSTRUCT_FIELD_BFF : MFX_PICSTRUCT_PROGRESSIVE;
av_dict_set_int(av_opts, "flags", ivalue, AV_DICT_APPEND);
}
}
else if (!strcasecmp(key, "mbbrc"))
{
if (info->capabilities & HB_QSV_CAP_OPTION2_MBBRC)
{
ivalue = hb_qsv_atobool(value, &error);
if (!error)
{
av_dict_set_int(av_opts, "mbbrc", ivalue, 0);
}
}
else
{
return HB_QSV_PARAM_UNSUPPORTED;
}
}
else if (!strcasecmp(key, "extbrc"))
{
if (info->capabilities & HB_QSV_CAP_OPTION2_EXTBRC)
{
ivalue = hb_qsv_atobool(value, &error);
if (!error)
{
av_dict_set_int(av_opts, "extbrc", ivalue, 0);
}
}
else
{
return HB_QSV_PARAM_UNSUPPORTED;
}
}
else if (!strcasecmp(key, "lookahead") ||
!strcasecmp(key, "la"))
{
if (info->capabilities & HB_QSV_CAP_RATECONTROL_LA)
{
ivalue = hb_qsv_atobool(value, &error);
if (!error)
{
param->rc.lookahead = ivalue;
}
}
else
{
return HB_QSV_PARAM_UNSUPPORTED;
}
}
else if (!strcasecmp(key, "lookahead-depth") ||
!strcasecmp(key, "la-depth"))
{
if (info->capabilities & HB_QSV_CAP_RATECONTROL_LA)
{
ivalue = hb_qsv_atoi(value, &error);
if (!error)
{
av_dict_set_int(av_opts, "look_ahead_depth", HB_QSV_CLIP3(10, 100, ivalue), 0);
}
}
else
{
return HB_QSV_PARAM_UNSUPPORTED;
}
}
else if (!strcasecmp(key, "lookahead-ds") ||
!strcasecmp(key, "la-ds"))
{
if (info->capabilities & HB_QSV_CAP_OPTION2_LA_DOWNS)
{
ivalue = hb_qsv_atoi(value, &error);
if (!error)
{
ivalue = HB_QSV_CLIP3(MFX_LOOKAHEAD_DS_UNKNOWN, MFX_LOOKAHEAD_DS_4x, ivalue);
av_dict_set_int(av_opts, "look_ahead_downsampling", ivalue, 0);
}
}
else
{
return HB_QSV_PARAM_UNSUPPORTED;
}
}
else if (!strcasecmp(key, "trellis"))
{
if (info->capabilities & HB_QSV_CAP_OPTION2_TRELLIS)
{
ivalue = hb_qsv_atoi(value, &error);
if (!error)
{
av_dict_set_int(av_opts, "trellis", ivalue, 0);
}
}
else
{
return HB_QSV_PARAM_UNSUPPORTED;
}
}
else if (!strcasecmp(key, "repeatpps"))
{
if (info->capabilities & HB_QSV_CAP_OPTION2_REPEATPPS)
{
ivalue = hb_qsv_atobool(value, &error);
if (!error)
{
av_dict_set_int(av_opts, "repeat_pps", ivalue, 0);
}
}
else
{
return HB_QSV_PARAM_UNSUPPORTED;
}
}
else if (!strcasecmp(key, "lowpower"))
{
if (info->capabilities & HB_QSV_CAP_LOWPOWER_ENCODE)
{
ivalue = hb_qsv_atobool(value, &error);
if (!error)
{
param->low_power = ivalue;
}
}
else
{
return HB_QSV_PARAM_UNSUPPORTED;
}
}
else if (!strcasecmp(key, "gpu"))
{
// Already parsed in QSV initialization
}
else if (!strcasecmp(key, "memory-type"))
{
// Check if was parsed already in decoder initialization
if (job->qsv_ctx && !job->qsv_ctx->memory_type)
{
hb_triplet_t* mode = NULL;
mode = hb_triplet4key(hb_qsv_memory_types, value);
if (!mode)
error = HB_QSV_PARAM_BAD_VALUE;
else
job->qsv_ctx->memory_type = mode->value;
}
}
else if (!strcasecmp(key, "scalingmode") ||
!strcasecmp(key, "vpp-sm"))
{
// Already parsed it in decoder but need to check support
if (info->capabilities & HB_QSV_CAP_VPP_SCALING)
{
hb_triplet_t *mode = NULL;
mode = hb_triplet4key(hb_qsv_vpp_scale_modes, value);
if (!mode)
{
error = HB_QSV_PARAM_BAD_VALUE;
}
}
else
{
return HB_QSV_PARAM_UNSUPPORTED;
}
}
else if (!strcasecmp(key, "hyperencode"))
{
hb_triplet_t *mode = NULL;
if (info->capabilities & HB_QSV_CAP_HYPERENCODE)
{
mode = hb_triplet4key(hb_qsv_hyper_encode_modes, value);
if (!mode)
{
error = HB_QSV_PARAM_BAD_VALUE;
}
}
else
{
return HB_QSV_PARAM_UNSUPPORTED;
}
if (mode)
{
param->hyperEncodeParam = mode;
}
}
else if (!strcasecmp(key, "palette"))
{
if (info->capabilities & HB_QSV_CAP_AV1_SCREENCONTENT)
{
ivalue = hb_qsv_atobool(value, &error);
if (!error)
{
param->av1ScreenContentToolsParam.Palette = ivalue;
}
}
else
{
return HB_QSV_PARAM_UNSUPPORTED;
}
}
else if (!strcasecmp(key, "intrabc"))
{
if (info->capabilities & HB_QSV_CAP_AV1_SCREENCONTENT)
{
ivalue = hb_qsv_atobool(value, &error);
if (!error)
{
param->av1ScreenContentToolsParam.IntraBlockCopy = ivalue;
}
}
else
{
return HB_QSV_PARAM_UNSUPPORTED;
}
}
else if (!strcasecmp(key, "async-depth"))
{
int async_depth = hb_qsv_atoi(value, &error);
if (!error)
{
av_dict_set_int(av_opts, "async_depth", async_depth, 0);
param->videoParam->AsyncDepth = async_depth;
}
}
else
{
/*
* TODO:
* - slice count (num-slice/slices, num-mb-per-slice/slice-max-mbs)
* - open-gop
* - fake-interlaced (mfxExtCodingOption.FramePicture???)
* - intra-refresh
*/
return HB_QSV_PARAM_BAD_NAME;
}
return error ? HB_QSV_PARAM_BAD_VALUE : HB_QSV_PARAM_OK;
}
int hb_qsv_profile_parse(hb_qsv_param_t *param, hb_qsv_info_t *info, const char *profile_key, const int codec)
{
hb_triplet_t *profile = NULL;
if (profile_key != NULL && *profile_key && strcasecmp(profile_key, "auto"))
{
switch (param->videoParam->mfx.CodecId)
{
case MFX_CODEC_AVC:
profile = hb_triplet4key(hb_qsv_h264_profiles, profile_key);
break;
case MFX_CODEC_HEVC:
profile = hb_triplet4key(hb_qsv_h265_profiles, profile_key);
/* HEVC10 supported starting from KBL/G6 */
if (profile->value == MFX_PROFILE_HEVC_MAIN10 &&
hb_qsv_hardware_generation(hb_qsv_get_platform(hb_qsv_get_adapter_index())) < QSV_G6)
{
hb_log("qsv: HEVC Main10 is not supported on this platform");
profile = NULL;
}
break;
case MFX_CODEC_AV1:
profile = hb_triplet4key(hb_qsv_av1_profiles, profile_key);
if (hb_qsv_hardware_generation(hb_qsv_get_platform(hb_qsv_get_adapter_index())) <= QSV_G8)
{
hb_log("qsv: AV1 is not supported on this platform");
profile = NULL;
}
break;
default:
break;
}
if (profile == NULL)
{
return -1;
}
param->videoParam->mfx.CodecProfile = profile->value;
}
/* HEVC 10 bits defaults to Main 10 */
else if (((profile_key != NULL && !strcasecmp(profile_key, "auto")) || profile_key == NULL) &&
codec == HB_VCODEC_FFMPEG_QSV_H265_10BIT &&
param->videoParam->mfx.CodecId == MFX_CODEC_HEVC &&
hb_qsv_hardware_generation(hb_qsv_get_platform(hb_qsv_get_adapter_index())) >= QSV_G6)
{
profile = &hb_qsv_h265_profiles[1];
param->videoParam->mfx.CodecProfile = profile->value;
}
/* AV1 10 bits defaults to Main */
else if (((profile_key != NULL && !strcasecmp(profile_key, "auto")) || profile_key == NULL) &&
codec == HB_VCODEC_FFMPEG_QSV_AV1_10BIT &&
param->videoParam->mfx.CodecId == MFX_CODEC_AV1 &&
hb_qsv_hardware_generation(hb_qsv_get_platform(hb_qsv_get_adapter_index())) > QSV_G8)
{
profile = &hb_qsv_av1_profiles[0];
param->videoParam->mfx.CodecProfile = profile->value;
}
return 0;
}
const char* const* hb_qsv_preset_get_names()
{
if (hb_qsv_hardware_generation(hb_qsv_get_platform(hb_qsv_get_adapter_index())) >= QSV_G3)
{
return hb_qsv_preset_names2;
}
else
{
return hb_qsv_preset_names1;
}
}
const char* const* hb_qsv_profile_get_names(int encoder)
{
switch (encoder)
{
case HB_VCODEC_FFMPEG_QSV_H264:
return hb_h264_profile_names_8bit;
case HB_VCODEC_FFMPEG_QSV_H265_8BIT:
return hb_h265_profile_names_8bit;
case HB_VCODEC_FFMPEG_QSV_H265_10BIT:
return hb_qsv_h265_profiles_names_10bit;
case HB_VCODEC_FFMPEG_QSV_AV1_10BIT:
case HB_VCODEC_FFMPEG_QSV_AV1:
return hb_qsv_av1_profiles_names;
default:
return NULL;
}
}
const char* const* hb_qsv_level_get_names(int encoder)
{
switch (encoder)
{
case HB_VCODEC_FFMPEG_QSV_H264:
return hb_qsv_h264_level_names;
case HB_VCODEC_FFMPEG_QSV_H265_10BIT:
case HB_VCODEC_FFMPEG_QSV_H265:
return hb_qsv_h265_level_names;
case HB_VCODEC_FFMPEG_QSV_AV1_10BIT:
case HB_VCODEC_FFMPEG_QSV_AV1:
return hb_qsv_av1_level_names;
default:
return NULL;
}
}
const int* hb_qsv_get_pix_fmts(int encoder)
{
switch (encoder)
{
case HB_VCODEC_FFMPEG_QSV_H264:
case HB_VCODEC_FFMPEG_QSV_H265:
case HB_VCODEC_FFMPEG_QSV_AV1:
return hb_qsv_pix_fmts;
case HB_VCODEC_FFMPEG_QSV_H265_10BIT:
case HB_VCODEC_FFMPEG_QSV_AV1_10BIT:
return hb_qsv_10bit_pix_fmts;
default:
return hb_qsv_pix_fmts;
}
}
const char* hb_qsv_video_quality_get_name(uint32_t codec)
{
uint64_t caps = 0;
const hb_qsv_adapter_details_t *details = hb_qsv_get_adapters_details_by_index(hb_qsv_get_adapter_index());
if (details)
{
switch (codec)
{
case HB_VCODEC_FFMPEG_QSV_H264:
if (details->hb_qsv_info_avc != NULL) caps = details->hb_qsv_info_avc->capabilities;
break;
case HB_VCODEC_FFMPEG_QSV_H265_10BIT:
case HB_VCODEC_FFMPEG_QSV_H265:
if (details->hb_qsv_info_hevc != NULL) caps = details->hb_qsv_info_hevc->capabilities;
break;
case HB_VCODEC_FFMPEG_QSV_AV1_10BIT:
case HB_VCODEC_FFMPEG_QSV_AV1:
if (details->hb_qsv_info_av1 != NULL) caps = details->hb_qsv_info_av1->capabilities;
break;
default:
break;
}
}
return (caps & HB_QSV_CAP_RATECONTROL_ICQ) ? "ICQ" : "QP";
}
void hb_qsv_video_quality_get_limits(uint32_t codec, float *low, float *high,
float *granularity, int *direction)
{
uint64_t caps = 0;
const hb_qsv_adapter_details_t *details = hb_qsv_get_adapters_details_by_index(hb_qsv_get_adapter_index());
if (details)
{
switch (codec)
{
case HB_VCODEC_FFMPEG_QSV_H265_10BIT:
case HB_VCODEC_FFMPEG_QSV_H265:
if (details->hb_qsv_info_hevc != NULL) caps = details->hb_qsv_info_hevc->capabilities;
*direction = 1;
*granularity = 1.;
*low = (caps & HB_QSV_CAP_RATECONTROL_ICQ) ? 1. : 0.;
*high = 51.;
break;
case HB_VCODEC_FFMPEG_QSV_AV1_10BIT:
case HB_VCODEC_FFMPEG_QSV_AV1:
if (details->hb_qsv_info_av1 != NULL) caps = details->hb_qsv_info_av1->capabilities;
*direction = 1;
*granularity = 1.;
*low = (caps & HB_QSV_CAP_RATECONTROL_ICQ) ? 1. : 0.;
*high = 51.;
break;
case HB_VCODEC_FFMPEG_QSV_H264:
default:
if (details->hb_qsv_info_avc != NULL) caps = details->hb_qsv_info_avc->capabilities;
*direction = 1;
*granularity = 1.;
*low = (caps & HB_QSV_CAP_RATECONTROL_ICQ) ? 1. : 0.;
*high = 51.;
break;
}
}
}
const char * hb_map_qsv_preset_name(const char * preset)
{
if (preset == NULL)
{
return "medium";
}
if (strcmp(preset, "speed") == 0) {
return "veryfast";
} else if (strcmp(preset, "balanced") == 0) {
return "medium";
} else if (strcmp(preset, "quality") == 0) {
return "veryslow";
}
return "medium";
}
int hb_qsv_param_default_async_depth()
{
return hb_qsv_hardware_generation(hb_qsv_get_platform(hb_qsv_get_adapter_index())) >= QSV_G7 ? 6 : HB_QSV_ASYNC_DEPTH_DEFAULT;
}
int hb_qsv_param_default(hb_qsv_param_t *param, hb_qsv_info_t *info)
{
if (param != NULL && info != NULL)
{
// introduced in API 1.6
memset(¶m->codingOption2, 0, sizeof(mfxExtCodingOption2));
param->codingOption2.Header.BufferId = MFX_EXTBUFF_CODING_OPTION2;
param->codingOption2.Header.BufferSz = sizeof(mfxExtCodingOption2);
param->codingOption2.IntRefType = 0;
param->codingOption2.IntRefCycleSize = 2;
param->codingOption2.IntRefQPDelta = 0;
param->codingOption2.MaxFrameSize = 0;
param->codingOption2.BitrateLimit = MFX_CODINGOPTION_ON;
param->codingOption2.MBBRC = MFX_CODINGOPTION_OFF;
param->codingOption2.ExtBRC = MFX_CODINGOPTION_OFF;
// introduced in API 1.7
param->codingOption2.LookAheadDepth = 40;
param->codingOption2.Trellis = MFX_TRELLIS_OFF;
// introduced in API 1.8
param->codingOption2.RepeatPPS = MFX_CODINGOPTION_OFF;
param->codingOption2.BRefType = MFX_B_REF_UNKNOWN; // controlled via gop.b_pyramid
param->codingOption2.AdaptiveI = MFX_CODINGOPTION_OFF;
param->codingOption2.AdaptiveB = MFX_CODINGOPTION_OFF;
param->codingOption2.LookAheadDS = MFX_LOOKAHEAD_DS_OFF;
param->codingOption2.NumMbPerSlice = 0;
// introduced in API 2.5
param->hyperEncodeParam = hb_triplet4key(hb_qsv_hyper_encode_modes, "off");
// introduced in API 2.11
memset(¶m->av1ScreenContentToolsParam, 0, sizeof(mfxExtAV1ScreenContentTools));
param->av1ScreenContentToolsParam.Header.BufferId = MFX_EXTBUFF_AV1_SCREEN_CONTENT_TOOLS;
param->av1ScreenContentToolsParam.Header.BufferSz = sizeof(mfxExtAV1ScreenContentTools);
param->av1ScreenContentToolsParam.IntraBlockCopy = 0;
param->av1ScreenContentToolsParam.Palette = 0;
// GOP & rate control
param->gop.b_pyramid = 1; // enabled by default (if supported)
param->gop.gop_pic_size = -1; // set automatically
param->gop.gop_ref_dist = -1; // set automatically
param->gop.int_ref_cycle_size = -1; // set automatically
param->rc.icq = 1; // enabled by default (if supported)
param->rc.lookahead = 0; // disabled by default
param->rc.cqp_offsets[0] = 0;
param->rc.cqp_offsets[1] = 2;
param->rc.cqp_offsets[2] = 4;
param->rc.vbv_max_bitrate = 0; // set automatically
param->rc.vbv_buffer_size = 0; // set automatically
param->rc.vbv_buffer_init = .0; // set automatically
param->low_power = 0;
#if defined(_WIN32) || defined(__MINGW32__)
if (info->capabilities & HB_QSV_CAP_LOWPOWER_ENCODE)
{
param->low_power = 1;
}
#endif
}
else
{
hb_error("hb_qsv_param_default: invalid pointer(s)");
return -1;
}
return 0;
}
hb_triplet_t* hb_triplet4value(hb_triplet_t *triplets, const int value)
{
for (int i = 0; triplets[i].name != NULL; i++)
{
if (triplets[i].value == value)
{
return &triplets[i];
}
}
return NULL;
}
hb_triplet_t* hb_triplet4name(hb_triplet_t *triplets, const char *name)
{
for (int i = 0; triplets[i].name != NULL; i++)
{
if (!strcasecmp(triplets[i].name, name))
{
return &triplets[i];
}
}
return NULL;
}
hb_triplet_t* hb_triplet4key(hb_triplet_t *triplets, const char *key)
{
for (int i = 0; triplets[i].name != NULL; i++)
{
if (!strcasecmp(triplets[i].key, key))
{
return &triplets[i];
}
}
return NULL;
}
const char* hb_qsv_codec_name(uint32_t codec_id)
{
switch (codec_id)
{
case MFX_CODEC_AVC:
return "H.264/AVC";
case MFX_CODEC_HEVC:
return "H.265/HEVC";
case MFX_CODEC_AV1:
return "AV1";
default:
return NULL;
}
}
const char* hb_qsv_profile_name(uint32_t codec_id, uint16_t profile_id)
{
hb_triplet_t *profile = NULL;
switch (codec_id)
{
case MFX_CODEC_AVC:
profile = hb_triplet4value(hb_qsv_h264_profiles, profile_id);
break;
case MFX_CODEC_HEVC:
profile = hb_triplet4value(hb_qsv_h265_profiles, profile_id);
break;
case MFX_CODEC_AV1:
profile = hb_triplet4value(hb_qsv_av1_profiles, profile_id);
break;
default:
break;
}
return profile != NULL ? profile->name : NULL;
}
const char* hb_qsv_impl_get_name(int impl)
{
switch (MFX_IMPL_BASETYPE(impl))
{
case MFX_IMPL_SOFTWARE:
return "software";
case MFX_IMPL_HARDWARE:
return "hardware (1)";
case MFX_IMPL_HARDWARE2:
return "hardware (2)";
case MFX_IMPL_HARDWARE3:
return "hardware (3)";
case MFX_IMPL_HARDWARE4:
return "hardware (4)";
case MFX_IMPL_HARDWARE_ANY:
return "hardware (any)";
case MFX_IMPL_AUTO:
return "automatic";
case MFX_IMPL_AUTO_ANY:
return "automatic (any)";
default:
return NULL;
}
}
int hb_qsv_impl_get_num(int impl)
{
switch (MFX_IMPL_BASETYPE(impl))
{
case MFX_IMPL_HARDWARE:
return 0;
case MFX_IMPL_HARDWARE2:
return 1;
case MFX_IMPL_HARDWARE3:
return 2;
case MFX_IMPL_HARDWARE4:
return 3;
case MFX_IMPL_SOFTWARE:
case MFX_IMPL_AUTO:
case MFX_IMPL_AUTO_ANY:
case MFX_IMPL_HARDWARE_ANY:
default:
return -1;
}
}
const char* hb_qsv_impl_get_via_name(int impl)
{
if ((impl & 0xF00) == MFX_IMPL_VIA_VAAPI)
return "via VAAPI";
else if ((impl & 0xF00) == MFX_IMPL_VIA_D3D11)
return "via D3D11";
else if ((impl & 0xF00) == MFX_IMPL_VIA_D3D9)
return "via D3D9";
else if ((impl & 0xF00) == MFX_IMPL_VIA_ANY)
return "via ANY";
else return NULL;
}
int hb_qsv_get_platform(int adapter_index)
{
for (int i = 0; i < hb_list_count(g_qsv_adapters_details_list); i++)
{
const hb_qsv_adapter_details_t *details = hb_list_item(g_qsv_adapters_details_list, i);
// find DirectX adapter with given index in list of QSV adapters
if (details && (details->index == adapter_index))
{
return qsv_map_mfx_platform_codename(details->platform.CodeName);
}
}
return HB_CPU_PLATFORM_UNSPECIFIED;
}
int hb_qsv_param_parse_dx_index(hb_job_t *job, const int dx_index)
{
for (int i = 0; i < hb_list_count(g_qsv_adapters_details_list); i++)
{
const hb_qsv_adapter_details_t *details = hb_list_item(g_qsv_adapters_details_list, i);
// find DirectX adapter with given index in list of QSV adapters
if (details && (details->index == dx_index))
{
job->hw_device_index = details->index;
hb_log("qsv: %s qsv adapter with index %u has been selected", hb_qsv_get_adapter_type(details), details->index);
hb_qsv_set_adapter_index(details->index);
return 0;
}
}
job->hw_device_index = hb_qsv_get_adapter_index();
hb_log("qsv: default qsv adapter has been selected");
return -1;
}
hb_qsv_context_t * hb_qsv_context_init()
{
if (!hb_qsv_available())
{
return 0;
}
hb_qsv_context_t *ctx = av_mallocz(sizeof(hb_qsv_context_t));
if (!ctx)
{
hb_error("hb_qsv_context_init: qsv ctx alloc failed");
return NULL;
}
return ctx;
}
hb_qsv_context_t * hb_qsv_context_dup(const hb_qsv_context_t *src)
{
if (src == NULL)
{
return NULL;
}
hb_qsv_context_t *ctx = hb_qsv_context_init();
if (ctx)
{
memcpy(ctx, src, sizeof(hb_qsv_context_t));
}
return ctx;
}
void hb_qsv_context_close(hb_qsv_context_t **_ctx)
{
hb_qsv_context_t *ctx = *_ctx;
if (ctx == NULL)
{
return;
}
av_freep(_ctx);
// restore adapter index after user preferences
g_adapter_index = hb_qsv_get_default_adapter_index();
}
static void * find_decoder(int codec_param)
{
const char *codec_name = hb_qsv_decode_get_codec_name(codec_param);
return (void *)avcodec_find_decoder_by_name(codec_name);
}
static const int qsv_encoders[] =
{
HB_VCODEC_FFMPEG_QSV_H264,
HB_VCODEC_FFMPEG_QSV_H265,
HB_VCODEC_FFMPEG_QSV_H265_10BIT,
HB_VCODEC_FFMPEG_QSV_AV1,
HB_VCODEC_FFMPEG_QSV_AV1_10BIT,
HB_VCODEC_INVALID
};
hb_hwaccel_t hb_hwaccel_qsv =
{
.id = HB_DECODE_QSV,
.name = "qsv",
.encoders = qsv_encoders,
.type = AV_HWDEVICE_TYPE_QSV,
.hw_pix_fmt = AV_PIX_FMT_QSV,
.can_filter = are_filters_supported,
.find_decoder = find_decoder,
.caps = HB_HWACCEL_CAP_ROTATE | HB_HWACCEL_CAP_COLOR_RANGE
};
#else // HB_PROJECT_FEATURE_QSV
int hb_qsv_available()
{
return -1;
}
#endif // HB_PROJECT_FEATURE_QSV