mirror of https://github.com/HandBrake/HandBrake
3911 lines
132 KiB
C
3911 lines
132 KiB
C
/* qsv_common.c
|
|
*
|
|
* Copyright (c) 2003-2025 HandBrake Team
|
|
* This file is part of the HandBrake source code.
|
|
* Homepage: <http://handbrake.fr/>.
|
|
* 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 <stdio.h>
|
|
#include <string.h>
|
|
|
|
#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
|