diff options
-rw-r--r-- | libhb/common.c | 3 | ||||
-rw-r--r-- | libhb/decavcodec.c | 31 | ||||
-rw-r--r-- | libhb/enc_qsv.c | 41 | ||||
-rw-r--r-- | libhb/handbrake/qsv_common.h | 21 | ||||
-rw-r--r-- | libhb/handbrake/qsv_libav.h | 1 | ||||
-rw-r--r-- | libhb/hb.c | 5 | ||||
-rw-r--r-- | libhb/hb_json.c | 31 | ||||
-rw-r--r-- | libhb/preset.c | 6 | ||||
-rw-r--r-- | libhb/qsv_common.c | 1200 | ||||
-rw-r--r-- | libhb/qsv_libav.c | 3 | ||||
-rw-r--r-- | libhb/sync.c | 2 | ||||
-rw-r--r-- | libhb/work.c | 6 | ||||
-rw-r--r-- | test/test.c | 77 | ||||
-rw-r--r-- | win/CS/HandBrake.Interop/Interop/HandBrakeHardwareEncoderHelper.cs | 5 | ||||
-rw-r--r-- | win/CS/HandBrake.Interop/Interop/HbLib/HbFunctions.cs | 10 |
15 files changed, 959 insertions, 483 deletions
diff --git a/libhb/common.c b/libhb/common.c index 0eb980468..637d14d0b 100644 --- a/libhb/common.c +++ b/libhb/common.c @@ -294,7 +294,7 @@ static int hb_video_encoder_is_enabled(int encoder, int disable_hardware) #if HB_PROJECT_FEATURE_QSV if (encoder & HB_VCODEC_QSV_MASK) { - return hb_qsv_video_encoder_is_enabled(encoder); + return hb_qsv_video_encoder_is_enabled(hb_qsv_get_adapter_index(), encoder); } #endif @@ -3897,6 +3897,7 @@ static void job_setup(hb_job_t * job, hb_title_t * title) job->metadata = hb_metadata_copy( title->metadata ); #if HB_PROJECT_FEATURE_QSV + job->qsv.ctx = hb_qsv_context_init(); job->qsv.enc_info.is_init_done = 0; job->qsv.async_depth = hb_qsv_param_default_async_depth(); job->qsv.decode = !!(title->video_decode_support & diff --git a/libhb/decavcodec.c b/libhb/decavcodec.c index 55c38a00b..08c82d5f5 100644 --- a/libhb/decavcodec.c +++ b/libhb/decavcodec.c @@ -1423,7 +1423,7 @@ static int decavcodecvInit( hb_work_object_t * w, hb_job_t * job ) pv->qsv.config.io_pattern = MFX_IOPATTERN_OUT_SYSTEM_MEMORY; if(hb_qsv_full_path_is_enabled(job)) { - hb_qsv_info_t *info = hb_qsv_info_get(job->vcodec); + hb_qsv_info_t *info = hb_qsv_encoder_info_get(hb_qsv_get_adapter_index(), job->vcodec); if (info != NULL) { // setup the QSV configuration @@ -1443,6 +1443,7 @@ static int decavcodecvInit( hb_work_object_t * w, hb_job_t * job ) hb_error( "decavcodecvInit: no context" ); return 1; } + pv->job->qsv.ctx->full_path_is_enabled = 1; if (!pv->job->qsv.ctx->hb_dec_qsv_frames_ctx) { pv->job->qsv.ctx->hb_dec_qsv_frames_ctx = av_mallocz(sizeof(HBQSVFramesContext)); @@ -2131,32 +2132,8 @@ static int decavcodecvInfo( hb_work_object_t *w, hb_work_info_t *info ) info->video_decode_support = HB_DECODE_SUPPORT_SW; #if HB_PROJECT_FEATURE_QSV - if (avcodec_find_decoder_by_name(hb_qsv_decode_get_codec_name(pv->context->codec_id))) - { - switch (pv->context->codec_id) - { - case AV_CODEC_ID_HEVC: - case AV_CODEC_ID_H264: - if (pv->context->pix_fmt == AV_PIX_FMT_YUV420P || - pv->context->pix_fmt == AV_PIX_FMT_YUVJ420P || - pv->context->pix_fmt == AV_PIX_FMT_YUV420P10LE) - { - info->video_decode_support |= HB_DECODE_SUPPORT_QSV; - } - break; - case AV_CODEC_ID_AV1: - if ((qsv_hardware_generation(hb_get_cpu_platform()) >= QSV_G8) && - (pv->context->pix_fmt == AV_PIX_FMT_YUV420P || - pv->context->pix_fmt == AV_PIX_FMT_YUVJ420P || - pv->context->pix_fmt == AV_PIX_FMT_YUV420P10LE)) - { - info->video_decode_support |= HB_DECODE_SUPPORT_QSV; - } - break; - default: - break; - } - } + if (hb_qsv_decode_codec_supported_codec(hb_qsv_get_adapter_index(), pv->context->codec_id, pv->context->pix_fmt)) + info->video_decode_support |= HB_DECODE_SUPPORT_QSV; #endif return 1; diff --git a/libhb/enc_qsv.c b/libhb/enc_qsv.c index 5f3d52fc7..e70ff3eb3 100644 --- a/libhb/enc_qsv.c +++ b/libhb/enc_qsv.c @@ -892,7 +892,7 @@ int qsv_enc_init(hb_work_private_t *pv) *job->die = 1; return -1; } - pv->loaded_plugins = hb_qsv_load_plugins(pv->qsv_info, qsv->mfx_session, version); + pv->loaded_plugins = hb_qsv_load_plugins(hb_qsv_get_adapter_index(), pv->qsv_info, qsv->mfx_session, version); if (pv->loaded_plugins == NULL) { hb_error("qsv_enc_init: hb_qsv_load_plugins failed"); @@ -998,35 +998,6 @@ int qsv_enc_init(hb_work_private_t *pv) return 0; } -static 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; -} - /*********************************************************************** * encqsvInit *********************************************************************** @@ -1039,7 +1010,7 @@ int encqsvInit(hb_work_object_t *w, hb_job_t *job) pv->is_sys_mem = hb_qsv_full_path_is_enabled(job) ? 0 : 1; // TODO: re-implement QSV VPP filtering support pv->job = job; - pv->qsv_info = hb_qsv_info_get(job->vcodec); + pv->qsv_info = hb_qsv_encoder_info_get(hb_qsv_get_adapter_index(), job->vcodec); pv->delayed_processing = hb_list_init(); pv->last_start = INT64_MIN; hb_buffer_list_clear(&pv->encoded_frames); @@ -1104,12 +1075,12 @@ int encqsvInit(hb_work_object_t *w, hb_job_t *job) } hb_dict_free(&options_list); } -#if !defined(SYS_LINUX) && !defined(SYS_FREEBSD) - if (pv->is_sys_mem) +#if defined(_WIN32) || defined(__MINGW32__) + if (pv->is_sys_mem && hb_qsv_implementation_is_hardware(pv->qsv_info->implementation)) { // select the right hardware implementation based on dx index if (!job->qsv.ctx->qsv_device) - hb_qsv_param_parse_dx_index(pv->job, -1); + hb_qsv_param_parse_dx_index(pv->job, hb_qsv_get_adapter_index()); mfxIMPL hw_preference = MFX_IMPL_VIA_D3D11; pv->qsv_info->implementation = hb_qsv_dx_index_to_impl(job->qsv.ctx->dx_index) | hw_preference; } @@ -1465,7 +1436,7 @@ int encqsvInit(hb_work_object_t *w, hb_job_t *job) } /* Load required MFX plug-ins */ - pv->loaded_plugins = hb_qsv_load_plugins(pv->qsv_info, session, version); + pv->loaded_plugins = hb_qsv_load_plugins(hb_qsv_get_adapter_index(), pv->qsv_info, session, version); if (pv->loaded_plugins == NULL) { hb_error("encqsvInit: hb_qsv_load_plugins failed"); diff --git a/libhb/handbrake/qsv_common.h b/libhb/handbrake/qsv_common.h index ef27464d9..2d4471bb1 100644 --- a/libhb/handbrake/qsv_common.h +++ b/libhb/handbrake/qsv_common.h @@ -44,7 +44,7 @@ void hb_qsv_force_workarounds(); // for developers only typedef struct hb_qsv_info_s { // each info struct only corresponds to one CodecId and implementation combo - const mfxU32 codec_id; + mfxU32 codec_id; mfxIMPL implementation; // whether the encoder is available for this implementation @@ -82,16 +82,19 @@ typedef struct hb_qsv_info_s /* Intel Quick Sync Video utilities */ hb_display_t * hb_qsv_display_init(void); -int hb_qsv_video_encoder_is_enabled(int encoder); +int hb_qsv_video_encoder_is_enabled(int adapter_index, int encoder); int hb_qsv_audio_encoder_is_enabled(int encoder); int hb_qsv_info_init(); void hb_qsv_info_print(); hb_list_t* hb_qsv_adapters_list(); -hb_qsv_info_t* hb_qsv_info_get(int encoder); -int qsv_hardware_generation(int cpu_platform); +hb_qsv_info_t* hb_qsv_encoder_info_get(int adapter_index, int encoder); +int hb_qsv_hardware_generation(int cpu_platform); +int hb_qsv_get_platform(int adapter_index); +int hb_qsv_get_adapter_index(); +int hb_qsv_implementation_is_hardware(mfxIMPL implementation); /* Automatically load and unload any required MFX plug-ins */ -hb_list_t* hb_qsv_load_plugins (hb_qsv_info_t *info, mfxSession session, mfxVersion version); +hb_list_t* hb_qsv_load_plugins (int adapter_index, hb_qsv_info_t *info, mfxSession session, mfxVersion version); void hb_qsv_unload_plugins(hb_list_t **_l, mfxSession session, mfxVersion version); /* Intel Quick Sync Video DECODE utilities */ @@ -247,6 +250,14 @@ enum AVPixelFormat hb_qsv_get_format(AVCodecContext *s, const enum AVPixelFormat int hb_qsv_preset_is_zero_copy_enabled(const hb_dict_t *job_dict); void hb_qsv_uninit_dec(AVCodecContext *s); void hb_qsv_uninit_enc(hb_job_t *job); +mfxIMPL hb_qsv_dx_index_to_impl(int dx_index); +int hb_qsv_parse_adapter_index(hb_job_t *job); +int hb_qsv_setup_job(hb_job_t *job); +int hb_qsv_decode_h264_is_supported(int adapter_index); +int hb_qsv_decode_h265_is_supported(int adapter_index); +int hb_qsv_decode_h265_10_bit_is_supported(int adapter_index); +int hb_qsv_decode_av1_is_supported(int adapter_index); +int hb_qsv_decode_codec_supported_codec(int adapter_index, int video_codec_param, int pix_fmt); #endif // __LIBHB__ #endif // HB_PROJECT_FEATURE_QSV diff --git a/libhb/handbrake/qsv_libav.h b/libhb/handbrake/qsv_libav.h index 23b4b96c6..f498f3107 100644 --- a/libhb/handbrake/qsv_libav.h +++ b/libhb/handbrake/qsv_libav.h @@ -333,6 +333,7 @@ typedef struct hb_qsv_context { int num_cpu_filters; int la_is_enabled; int qsv_filters_are_enabled; + int full_path_is_enabled; char *vpp_scale_mode; char *vpp_interpolation_method; char *qsv_device; diff --git a/libhb/hb.c b/libhb/hb.c index c85934b5b..ea554207a 100644 --- a/libhb/hb.c +++ b/libhb/hb.c @@ -1830,10 +1830,9 @@ int hb_global_init() #if HB_PROJECT_FEATURE_QSV if (!disable_hardware) { - result = hb_qsv_info_init(); - if (result < 0) + if (hb_qsv_available() < 0) { - hb_error("hb_qsv_info_init failed!"); + hb_error("hb_qsv_available failed!"); return -1; } hb_param_configure_qsv(); diff --git a/libhb/hb_json.c b/libhb/hb_json.c index f794c7ab7..1d83f5e3f 100644 --- a/libhb/hb_json.c +++ b/libhb/hb_json.c @@ -530,6 +530,13 @@ hb_dict_t* hb_job_to_dict( const hb_job_t * job ) json_error_t error; int subtitle_search_burn; int ii; + int adapter_index; + +#if HB_PROJECT_FEATURE_QSV + adapter_index = job->qsv.ctx->dx_index; +#else + adapter_index = 0; +#endif if (job == NULL || job->title == NULL) return NULL; @@ -538,7 +545,6 @@ hb_dict_t* hb_job_to_dict( const hb_job_t * job ) // necessary PAR value subtitle_search_burn = job->select_subtitle_config.dest == RENDERSUB; - dict = json_pack_ex(&error, 0, "{" // SequenceID @@ -550,8 +556,8 @@ hb_dict_t* hb_job_to_dict( const hb_job_t * job ) "s:{s:o, s:o, s:o,}," // PAR {Num, Den} "s:{s:o, s:o}," - // Video {Encoder, QSV {Decode, AsyncDepth}} - "s:{s:o, s:{s:o, s:o}}," + // Video {Encoder, QSV {Decode, AsyncDepth, AdapterIndex}} + "s:{s:o, s:{s:o, s:o, s:o}}," // Audio {CopyMask, FallbackEncoder, AudioList []} "s:{s:[], s:o, s:[]}," // Subtitles {Search {Enable, Forced, Default, Burn}, SubtitleList []} @@ -580,6 +586,7 @@ hb_dict_t* hb_job_to_dict( const hb_job_t * job ) "QSV", "Decode", hb_value_bool(job->qsv.decode), "AsyncDepth", hb_value_int(job->qsv.async_depth), + "AdapterIndex", hb_value_int(adapter_index), "Audio", "CopyMask", "FallbackEncoder", hb_value_int(job->acodec_fallback), @@ -1094,6 +1101,7 @@ hb_job_t* hb_dict_to_job( hb_handle_t * h, hb_dict_t *dict ) json_int_t range_start = -1, range_end = -1, range_seek_points = -1; int vbitrate = -1; double vquality = HB_INVALID_VIDEO_QUALITY; + int adapter_index = -1; result = json_unpack_ex(dict, &error, 0, "{" @@ -1114,7 +1122,7 @@ hb_job_t* hb_dict_to_job( hb_handle_t * h, hb_dict_t *dict ) // Mastering, // ContentLightLevel, // ColorPrimariesOverride, ColorTransferOverride, ColorMatrixOverride, - // QSV {Decode, AsyncDepth}} + // QSV {Decode, AsyncDepth, AdapterIndex}} "s:{s:o, s?F, s?i, s?s, s?s, s?s, s?s, s?s," " s?b, s?b," " s?i, s?i," @@ -1122,7 +1130,7 @@ hb_job_t* hb_dict_to_job( hb_handle_t * h, hb_dict_t *dict ) " s?o," " s?o," " s?i, s?i, s?i," - " s?{s?b, s?i}}," + " s?{s?b, s?i, s?i}}," // Audio {CopyMask, FallbackEncoder, AudioList} "s?{s?o, s?o, s?o}," // Subtitle {Search {Enable, Forced, Default, Burn}, SubtitleList} @@ -1177,6 +1185,7 @@ hb_job_t* hb_dict_to_job( hb_handle_t * h, hb_dict_t *dict ) "QSV", "Decode", unpack_b(&job->qsv.decode), "AsyncDepth", unpack_i(&job->qsv.async_depth), + "AdapterIndex", unpack_i(&adapter_index), "Audio", "CopyMask", unpack_o(&acodec_copy_mask), "FallbackEncoder", unpack_o(&acodec_fallback), @@ -1197,15 +1206,6 @@ hb_job_t* hb_dict_to_job( hb_handle_t * h, hb_dict_t *dict ) hb_error("hb_dict_to_job: failed to parse dict: %s", error.text); goto fail; } - // Make sure QSV Decode is only True if the hardware is available. - job->qsv.decode = job->qsv.decode && hb_qsv_available(); -#if HB_PROJECT_FEATURE_QSV - int async_depth_default = hb_qsv_param_default_async_depth(); - if(job->qsv.async_depth <= 0 || job->qsv.async_depth > async_depth_default) - { - job->qsv.async_depth = async_depth_default; - } -#endif // Lookup mux id if (hb_value_type(mux) == HB_VALUE_TYPE_STRING) { @@ -1275,6 +1275,9 @@ hb_job_t* hb_dict_to_job( hb_handle_t * h, hb_dict_t *dict ) hb_job_set_encoder_level(job, video_level); hb_job_set_encoder_options(job, video_options); +#if HB_PROJECT_FEATURE_QSV + job->qsv.ctx->dx_index = adapter_index; +#endif // If both vbitrate and vquality were specified, vbitrate is used; // we need to ensure the unused rate contro mode is always set to an // invalid value, as if both values are valid, behavior is undefined diff --git a/libhb/preset.c b/libhb/preset.c index 21305b2de..e853e183d 100644 --- a/libhb/preset.c +++ b/libhb/preset.c @@ -1825,7 +1825,11 @@ int hb_preset_apply_video(const hb_dict_t *preset, hb_dict_t *job_dict) hb_dict_set(qsv, "AsyncDepth", hb_value_xform(value, HB_VALUE_TYPE_INT)); } - + if ((value = hb_dict_get(preset, "VideoQSVAdapterIndex")) != NULL) + { + hb_dict_set(qsv, "AdapterIndex", + hb_value_xform(value, HB_VALUE_TYPE_INT)); + } return 0; } diff --git a/libhb/qsv_common.c b/libhb/qsv_common.c index 02c960087..52d50302d 100644 --- a/libhb/qsv_common.c +++ b/libhb/qsv_common.c @@ -30,21 +30,60 @@ #include "libavutil/hwcontext.h" #include "mfx/mfxadapter.h" +typedef struct hb_qsv_adapter_details +{ + // DirectX index + int index; + // QSV info for each codec + hb_qsv_info_t *hb_qsv_info_avc; + hb_qsv_info_t *hb_qsv_info_hevc; + // 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; +} hb_qsv_adapter_details_t; + // QSV info about adapters -static mfxAdaptersInfo qsv_adapters_info; -static hb_list_t *qsv_adapters_list = NULL; -// QSV info for each codec -static hb_qsv_info_t *hb_qsv_info_avc = NULL; -static hb_qsv_info_t *hb_qsv_info_hevc = NULL; -// API versions -static mfxVersion qsv_software_version = { .Version = 0, }; -static mfxVersion qsv_hardware_version = { .Version = 0, }; -// AVC implementations -static hb_qsv_info_t qsv_software_info_avc = { .available = 0, .codec_id = MFX_CODEC_AVC, .implementation = MFX_IMPL_SOFTWARE, }; -static hb_qsv_info_t qsv_hardware_info_avc = { .available = 0, .codec_id = MFX_CODEC_AVC, .implementation = MFX_IMPL_HARDWARE_ANY|MFX_IMPL_VIA_ANY, }; -// HEVC implementations -static hb_qsv_info_t qsv_software_info_hevc = { .available = 0, .codec_id = MFX_CODEC_HEVC, .implementation = MFX_IMPL_SOFTWARE, }; -static hb_qsv_info_t qsv_hardware_info_hevc = { .available = 0, .codec_id = MFX_CODEC_HEVC, .implementation = MFX_IMPL_HARDWARE_ANY|MFX_IMPL_VIA_ANY, }; +#if defined(_WIN32) || defined(__MINGW32__) +static mfxAdaptersInfo g_qsv_adapters_info; +static const char* hb_qsv_get_adapter_type(const mfxAdapterInfo* info); +#endif +int hb_qsv_get_platform(int adapter_index); +static hb_list_t *g_qsv_adapters_list = NULL; +static hb_list_t *g_qsv_adapters_details_list = NULL; +static int g_adapter_index = -1; + +static void init_adapter_details(hb_qsv_adapter_details_t *adapter_details) +{ + adapter_details->index = 0; + // QSV info for each codec + adapter_details->hb_qsv_info_avc = NULL; + adapter_details->hb_qsv_info_hevc = 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; +} // QSV-supported profile and level lists (not all exposed to the user) static hb_triplet_t hb_qsv_h264_profiles[] = @@ -124,7 +163,69 @@ static hb_triplet_t hb_qsv_h265_levels[] = #define HB_CHECK_MFX_VERSION(MFX_VERSION, MAJOR, MINOR) \ (MFX_VERSION.Major == MAJOR && MFX_VERSION.Minor >= MINOR) -int qsv_hardware_generation(int cpu_platform) +int hb_qsv_get_adapter_index() +{ + return g_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++) + { + 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; +} + +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; + } + 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) { @@ -157,7 +258,7 @@ int qsv_hardware_generation(int cpu_platform) /* * Determine whether a given mfxIMPL is hardware-accelerated. */ -static int qsv_implementation_is_hardware(mfxIMPL implementation) +int hb_qsv_implementation_is_hardware(mfxIMPL implementation) { return MFX_IMPL_BASETYPE(implementation) != MFX_IMPL_SOFTWARE; } @@ -169,24 +270,51 @@ int hb_qsv_available() return 0; } - return ((hb_qsv_video_encoder_is_enabled(HB_VCODEC_QSV_H264) ? HB_VCODEC_QSV_H264 : 0) | - (hb_qsv_video_encoder_is_enabled(HB_VCODEC_QSV_H265) ? HB_VCODEC_QSV_H265 : 0) | - (hb_qsv_video_encoder_is_enabled(HB_VCODEC_QSV_H265_10BIT) ? HB_VCODEC_QSV_H265_10BIT : 0)); + static int init_done = 0; + if (init_done == 0) + { + int result = hb_qsv_info_init(); + if (result != 0) + { + init_done = -1; + hb_log("hb_qsv_available: hb_qsv_info_init failed"); + return -1; + } + init_done = 1; + } + else if (init_done == -1) + { + hb_log("hb_qsv_available: hb_qsv_info_init failed"); + return -1; + } + + return ((hb_qsv_video_encoder_is_enabled(hb_qsv_get_adapter_index(), HB_VCODEC_QSV_H264) ? HB_VCODEC_QSV_H264 : 0) | + (hb_qsv_video_encoder_is_enabled(hb_qsv_get_adapter_index(), HB_VCODEC_QSV_H265) ? HB_VCODEC_QSV_H265 : 0) | + (hb_qsv_video_encoder_is_enabled(hb_qsv_get_adapter_index(), HB_VCODEC_QSV_H265_10BIT) ? HB_VCODEC_QSV_H265_10BIT : 0)); } -int hb_qsv_video_encoder_is_enabled(int encoder) +int hb_qsv_video_encoder_is_enabled(int adapter_index, int encoder) { - switch (encoder) + hb_qsv_adapter_details_t* details = hb_qsv_get_adapters_details_by_index(adapter_index); + + if (details) { - case HB_VCODEC_QSV_H264: - return hb_qsv_info_avc != NULL && hb_qsv_info_avc->available; - case HB_VCODEC_QSV_H265_10BIT: - if (qsv_hardware_generation(hb_get_cpu_platform()) < QSV_G6) + switch (encoder) + { + case HB_VCODEC_QSV_H264: + return details->hb_qsv_info_avc != NULL && details->hb_qsv_info_avc->available; + case HB_VCODEC_QSV_H265_10BIT: + if (hb_qsv_hardware_generation(hb_qsv_get_platform(adapter_index)) < QSV_G6) + return 0; + case HB_VCODEC_QSV_H265: + return details->hb_qsv_info_hevc != NULL && details->hb_qsv_info_hevc->available; + default: return 0; - case HB_VCODEC_QSV_H265: - return hb_qsv_info_hevc != NULL && hb_qsv_info_hevc->available; - default: - return 0; + } + } + else + { + return 0; } } @@ -283,7 +411,7 @@ static void init_ext_coding_option2(mfxExtCodingOption2 *extCodingOption2) extCodingOption2->NumMbPerSlice = 2040; // 1920x1088/4 } -static int query_capabilities(mfxSession session, mfxVersion version, hb_qsv_info_t *info, mfxAdaptersInfo* adapters_info) +static int query_capabilities(mfxSession session, int index, mfxVersion version, hb_qsv_info_t *info) { /* * MFXVideoENCODE_Query(mfxSession, mfxVideoParam *in, mfxVideoParam *out); @@ -312,7 +440,7 @@ static int query_capabilities(mfxSession session, mfxVersion version, hb_qsv_inf info->capabilities = 0; /* Load required MFX plug-ins */ - if ((mfxPluginList = hb_qsv_load_plugins(info, session, version)) == NULL) + if ((mfxPluginList = hb_qsv_load_plugins(index, info, session, version)) == NULL) { return 0; // the required plugin(s) couldn't be loaded } @@ -360,7 +488,7 @@ static int query_capabilities(mfxSession session, mfxVersion version, hb_qsv_inf * about it - however, it may fail for other encoders (ignore) */ fprintf(stderr, - "hb_qsv_info_init: MFXVideoENCODE_Init failed" + "query_capabilities: MFXVideoENCODE_Init failed" " (0x%"PRIX32", 0x%"PRIX32", %d)\n", info->codec_id, info->implementation, status); } @@ -384,27 +512,17 @@ static int query_capabilities(mfxSession session, mfxVersion version, hb_qsv_inf /* Implementation-specific features that can't be queried */ if (info->codec_id == MFX_CODEC_AVC || info->codec_id == MFX_CODEC_HEVC) { - if (qsv_implementation_is_hardware(info->implementation)) + if (hb_qsv_implementation_is_hardware(info->implementation)) { - if (qsv_hardware_generation(hb_get_cpu_platform()) >= QSV_G3) + 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_HEVC && - (qsv_hardware_generation(hb_get_cpu_platform()) >= QSV_G7)) + (hb_qsv_hardware_generation(hb_qsv_get_platform(index)) >= QSV_G7)) { info->capabilities |= HB_QSV_CAP_LOWPOWER_ENCODE; } -#if !defined(SYS_LINUX) && !defined(SYS_FREEBSD) - if (info->codec_id == MFX_CODEC_HEVC && - (adapters_info->NumActual > 0)) - { - if (adapters_info->Adapters[0].Platform.MediaAdapterType == MFX_MEDIA_DISCRETE) - { - info->capabilities |= HB_QSV_CAP_LOWPOWER_ENCODE; - } - } -#endif } else { @@ -507,7 +625,7 @@ static int query_capabilities(mfxSession session, mfxVersion version, hb_qsv_inf * about it - however, it may fail for other encoders (ignore) */ fprintf(stderr, - "hb_qsv_info_init: mfxExtVideoSignalInfo check" + "query_capabilities: mfxExtVideoSignalInfo check" " failed (0x%"PRIX32", 0x%"PRIX32", %d)\n", info->codec_id, info->implementation, status); } @@ -539,7 +657,7 @@ static int query_capabilities(mfxSession session, mfxVersion version, hb_qsv_inf * about it - however, it may fail for other encoders (ignore) */ fprintf(stderr, - "hb_qsv_info_init: mfxExtCodingOption check" + "query_capabilities: mfxExtCodingOption check" " failed (0x%"PRIX32", 0x%"PRIX32", %d)\n", info->codec_id, info->implementation, status); } @@ -588,16 +706,16 @@ static int query_capabilities(mfxSession session, mfxVersion version, hb_qsv_inf * - MBBRC requires G3 hardware (Haswell or equivalent) * - ExtBRC requires G2 hardware (Ivy Bridge or equivalent) */ - if (qsv_implementation_is_hardware(info->implementation) && - qsv_hardware_generation(hb_get_cpu_platform()) >= QSV_G3) + 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 (qsv_implementation_is_hardware(info->implementation) && - qsv_hardware_generation(hb_get_cpu_platform()) >= QSV_G2) + if (hb_qsv_implementation_is_hardware(info->implementation) && + hb_qsv_hardware_generation(hb_qsv_get_platform(index)) >= QSV_G2) { if (extCodingOption2.ExtBRC) { @@ -612,8 +730,8 @@ static int query_capabilities(mfxSession session, mfxVersion version, hb_qsv_inf */ if (HB_CHECK_MFX_VERSION(version, 1, 7)) { - if (qsv_implementation_is_hardware(info->implementation) && - qsv_hardware_generation(hb_get_cpu_platform()) >= QSV_G3) + if (hb_qsv_implementation_is_hardware(info->implementation) && + hb_qsv_hardware_generation(hb_qsv_get_platform(index)) >= QSV_G3) { if (extCodingOption2.Trellis) { @@ -658,20 +776,20 @@ static int query_capabilities(mfxSession session, mfxVersion version, hb_qsv_inf else { fprintf(stderr, - "hb_qsv_info_init: mfxExtCodingOption2 check failed (0x%"PRIX32", 0x%"PRIX32", %d)\n", + "query_capabilities: mfxExtCodingOption2 check failed (0x%"PRIX32", 0x%"PRIX32", %d)\n", info->codec_id, info->implementation, status); } } if (HB_CHECK_MFX_VERSION(version, 1, 19)) { - if (qsv_hardware_generation(hb_get_cpu_platform()) >= QSV_G7) + 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, 33)) { - if (qsv_hardware_generation(hb_get_cpu_platform()) >= QSV_G7) + if (hb_qsv_hardware_generation(hb_qsv_get_platform(index)) >= QSV_G7) { info->capabilities |= HB_QSV_CAP_VPP_INTERPOLATION; } @@ -692,124 +810,167 @@ hb_display_t * hb_qsv_display_init(void) return hb_display_init(DRM_INTEL_DRIVER_NAME, VA_INTEL_DRIVER_NAMES); } -#if !defined(SYS_LINUX) && !defined(SYS_FREEBSD) -static int hb_qsv_make_adapters_list(const mfxAdaptersInfo* adapters_info, hb_list_t **qsv_adapters_list); +#if defined(_WIN32) || defined(__MINGW32__) static int hb_qsv_query_adapters(mfxAdaptersInfo* adapters_info); +static int hb_qsv_make_adapters_list(const mfxAdaptersInfo* adapters_info, hb_list_t **qsv_adapters_list); +static int hb_qsv_make_adapters_details_list(const mfxAdaptersInfo* adapters_info, hb_list_t **hb_qsv_adapter_details_list); #endif -int hb_qsv_info_init() +mfxIMPL hb_qsv_dx_index_to_impl(int dx_index) { - static int init_done = 0; - if (init_done) - return 0; - init_done = 1; + mfxIMPL impl; -#if !defined(SYS_LINUX) && !defined(SYS_FREEBSD) - // Collect the information about qsv adapters - qsv_adapters_info.Adapters = NULL; - qsv_adapters_info.NumAlloc = 0; - qsv_adapters_info.NumActual = 0; - int err = hb_qsv_query_adapters(&qsv_adapters_info); - if (!err) + switch (dx_index) { - hb_qsv_make_adapters_list(&qsv_adapters_info, &qsv_adapters_list); - } - else - { - hb_error("hb_qsv_info_init: failed to query qsv adapters"); - } -#endif - /* - * 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, }; -#if defined(SYS_LINUX) || defined(SYS_FREEBSD) - mfxIMPL hw_preference = MFX_IMPL_VIA_ANY; -#else - mfxIMPL hw_preference = MFX_IMPL_VIA_D3D11; -#endif - - // 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, &qsv_software_version); - if (HB_CHECK_MFX_VERSION(qsv_software_version, - HB_QSV_MINVERSION_MAJOR, - HB_QSV_MINVERSION_MINOR)) { - query_capabilities(session, qsv_software_version, &qsv_software_info_avc, &qsv_adapters_info); - query_capabilities(session, qsv_software_version, &qsv_software_info_hevc, &qsv_adapters_info); - // now that we know which hardware encoders are - // available, we can set the preferred implementation - hb_qsv_impl_set_preferred("software"); + 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; } - MFXClose(session); } + return impl; +} - // check for actual hardware support - do{ - if (MFXInit(MFX_IMPL_HARDWARE_ANY | hw_preference, &version, &session) == MFX_ERR_NONE) - { - // On linux, the handle to the VA display must be set. - // This code is essentiall a NOP other platforms. - hb_display_t * display = hb_qsv_display_init(); - - 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, &qsv_hardware_version); - if (qsv_hardware_generation(hb_get_cpu_platform()) >= QSV_G1 && - HB_CHECK_MFX_VERSION(qsv_hardware_version, - HB_QSV_MINVERSION_MAJOR, - HB_QSV_MINVERSION_MINOR)) +static int hb_qsv_collect_adapters_details(hb_list_t *qsv_adapters_list, hb_list_t *hb_qsv_adapter_details_list) +{ + for (int i = 0; i < hb_list_count(hb_qsv_adapter_details_list); i++) + { + int *dx_index = (int *)hb_list_item(qsv_adapters_list, i); + hb_qsv_adapter_details_t *details = hb_list_item(hb_qsv_adapter_details_list, i); + details->index = *dx_index; + /* + * 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, }; +#if defined(_WIN32) || defined(__MINGW32__) + mfxIMPL hw_preference = MFX_IMPL_VIA_D3D11; +#else + mfxIMPL hw_preference = MFX_IMPL_VIA_ANY; +#endif + // 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, qsv_hardware_version, &qsv_hardware_info_avc, &qsv_adapters_info); - qsv_hardware_info_avc.implementation = MFX_IMPL_HARDWARE_ANY | hw_preference; - query_capabilities(session, qsv_hardware_version, &qsv_hardware_info_hevc, &qsv_adapters_info); - qsv_hardware_info_hevc.implementation = MFX_IMPL_HARDWARE_ANY | hw_preference; + query_capabilities(session, details->index, details->qsv_software_version, &details->qsv_software_info_avc); + query_capabilities(session, details->index, details->qsv_software_version, &details->qsv_software_info_hevc); // now that we know which hardware encoders are // available, we can set the preferred implementation - hb_qsv_impl_set_preferred("hardware"); + qsv_impl_set_preferred(details, "software"); } - hb_display_close(&display); MFXClose(session); - hw_preference = 0; } - else - { -#if !defined(SYS_LINUX) && !defined(SYS_FREEBSD) - // Windows only: After D3D11 we will try D3D9 - if (hw_preference == MFX_IMPL_VIA_D3D11) - hw_preference = MFX_IMPL_VIA_D3D9; + // check for actual hardware support + do{ + if (MFXInit(hb_qsv_dx_index_to_impl(*dx_index) | hw_preference, &version, &session) == MFX_ERR_NONE) + { + // On linux, the handle to the VA display must be set. + // This code is essentiall a NOP other platforms. + hb_display_t * display = hb_qsv_display_init(); + + 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(*dx_index)) >= QSV_G1 && + HB_CHECK_MFX_VERSION(details->qsv_hardware_version, + HB_QSV_MINVERSION_MAJOR, + HB_QSV_MINVERSION_MINOR)) + { + query_capabilities(session, details->index, details->qsv_hardware_version, &details->qsv_hardware_info_avc); + details->qsv_hardware_info_avc.implementation = hb_qsv_dx_index_to_impl(*dx_index) | hw_preference; + query_capabilities(session, details->index, details->qsv_hardware_version, &details->qsv_hardware_info_hevc); + details->qsv_hardware_info_hevc.implementation = hb_qsv_dx_index_to_impl(*dx_index) | hw_preference; + // now that we know which hardware encoders are + // available, we can set the preferred implementation + qsv_impl_set_preferred(details, "hardware"); + } + hb_display_close(&display); + MFXClose(session); + 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; + hw_preference = 0; + } } + while(hw_preference != 0); } - while(hw_preference != 0); - - // success return 0; } -static void log_capabilities(int log_level, uint64_t caps, const char *prefix) +static void log_decoder_capabilities(int log_level, 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)"); + } + + hb_deep_log(log_level, "%s%s", prefix, + strnlen(buffer, 1) ? buffer : " no decode support"); +} + +static void log_encoder_capabilities(int log_level, 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) { @@ -885,19 +1046,81 @@ static void log_capabilities(int log_level, uint64_t caps, const char *prefix) strnlen(buffer, 1) ? buffer : " standard feature set"); } +static void hb_qsv_adapter_info_print(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"); + } +} + void hb_qsv_info_print() { // is QSV available and usable? if (hb_qsv_available()) { -#if !defined(SYS_LINUX) && !defined(SYS_FREEBSD) - if (qsv_adapters_list && hb_list_count(qsv_adapters_list)) +#if defined(_WIN32) || defined(__MINGW32__) + if (g_qsv_adapters_list && hb_list_count(g_qsv_adapters_list)) { char gpu_list_str[256] = ""; - for (int i = 0; i < hb_list_count(qsv_adapters_list); i++) + for (int i = 0; i < hb_list_count(g_qsv_adapters_list); i++) { char value_str[256]; - int *value = hb_list_item(qsv_adapters_list, i); + int *value = hb_list_item(g_qsv_adapters_list, i); sprintf(value_str, "%d", *value); if (i > 0) strcat(gpu_list_str, ", "); @@ -910,62 +1133,15 @@ void hb_qsv_info_print() { hb_log("Intel Quick Sync Video support: yes"); } - // also print the details - if (qsv_hardware_version.Version) - { - hb_log(" - Intel Media SDK hardware: API %"PRIu16".%"PRIu16" (minimum: %"PRIu16".%"PRIu16")", - qsv_hardware_version.Major, qsv_hardware_version.Minor, - HB_QSV_MINVERSION_MAJOR, HB_QSV_MINVERSION_MINOR); - } - - if (qsv_software_version.Version) - { - hb_log(" - Intel Media SDK software: API %"PRIu16".%"PRIu16" (minimum: %"PRIu16".%"PRIu16")", - qsv_software_version.Major, qsv_software_version.Minor, - HB_QSV_MINVERSION_MAJOR, HB_QSV_MINVERSION_MINOR); - } - - if (hb_qsv_info_avc != NULL && hb_qsv_info_avc->available) - { - hb_log(" - H.264 encoder: yes"); - hb_log(" - preferred implementation: %s %s", - hb_qsv_impl_get_name(hb_qsv_info_avc->implementation), - hb_qsv_impl_get_via_name(hb_qsv_info_avc->implementation)); - if (qsv_hardware_info_avc.available) - { - log_capabilities(1, qsv_hardware_info_avc.capabilities, - " - capabilities (hardware): "); - } - if (qsv_software_info_avc.available) - { - log_capabilities(1, qsv_software_info_avc.capabilities, - " - capabilities (software): "); - } - } - else - { - hb_log(" - H.264 encoder: no"); - } - if (hb_qsv_info_hevc != NULL && hb_qsv_info_hevc->available) + // also print the details about all QSV adapters + for (int i = 0; i < hb_list_count(g_qsv_adapters_details_list); i++) { - hb_log(" - H.265 encoder: yes (8bit: yes, 10bit: %s)", (qsv_hardware_generation(hb_get_cpu_platform()) < QSV_G6) ? "no" : "yes" ); - hb_log(" - preferred implementation: %s %s", - hb_qsv_impl_get_name(hb_qsv_info_hevc->implementation), - hb_qsv_impl_get_via_name(hb_qsv_info_hevc->implementation)); - if (qsv_hardware_info_hevc.available) - { - log_capabilities(1, qsv_hardware_info_hevc.capabilities, - " - capabilities (hardware): "); - } - if (qsv_software_info_hevc.available) - { - log_capabilities(1, qsv_software_info_hevc.capabilities, - " - capabilities (software): "); - } - } - else - { - hb_log(" - H.265 encoder: no"); + hb_qsv_adapter_details_t *details = hb_list_item(g_qsv_adapters_details_list, i); +#if defined(_WIN32) || defined(__MINGW32__) + mfxAdapterInfo* info = &g_qsv_adapters_info.Adapters[i]; + hb_log("Intel Quick Sync Video %s adapter with index %d", hb_qsv_get_adapter_type(info), details->index); +#endif + hb_qsv_adapter_info_print(details); } } else @@ -974,21 +1150,27 @@ void hb_qsv_info_print() } } -hb_qsv_info_t* hb_qsv_info_get(int encoder) +hb_qsv_info_t* hb_qsv_encoder_info_get(int adapter_index, int encoder) { - switch (encoder) + hb_qsv_adapter_details_t* details = hb_qsv_get_adapters_details_by_index(adapter_index); + + if (details) { - case HB_VCODEC_QSV_H264: - return hb_qsv_info_avc; - case HB_VCODEC_QSV_H265_10BIT: - case HB_VCODEC_QSV_H265: - return hb_qsv_info_hevc; - default: - return NULL; + switch (encoder) + { + case HB_VCODEC_QSV_H264: + return details->hb_qsv_info_avc; + case HB_VCODEC_QSV_H265_10BIT: + case HB_VCODEC_QSV_H265: + return details->hb_qsv_info_hevc; + default: + return NULL; + } } + return NULL; } -hb_list_t* hb_qsv_load_plugins(hb_qsv_info_t *info, mfxSession session, mfxVersion version) +hb_list_t* hb_qsv_load_plugins(int index, hb_qsv_info_t *info, mfxSession session, mfxVersion version) { hb_list_t *mfxPluginList = hb_list_init(); if (mfxPluginList == NULL) @@ -999,10 +1181,10 @@ hb_list_t* hb_qsv_load_plugins(hb_qsv_info_t *info, mfxSession session, mfxVersi if (HB_CHECK_MFX_VERSION(version, 1, 8)) { - if (info->codec_id == MFX_CODEC_HEVC && !(qsv_hardware_generation(hb_get_cpu_platform()) < QSV_G5)) + if (info->codec_id == MFX_CODEC_HEVC && !(hb_qsv_hardware_generation(hb_qsv_get_platform(index)) < QSV_G5)) { if (HB_CHECK_MFX_VERSION(version, 1, 15) && - qsv_implementation_is_hardware(info->implementation)) + hb_qsv_implementation_is_hardware(info->implementation)) { if (MFXVideoUSER_Load(session, &MFX_PLUGINID_HEVCE_HW, 0) == MFX_ERR_NONE) { @@ -1065,12 +1247,91 @@ const char* hb_qsv_decode_get_codec_name(enum AVCodecID codec_id) } } +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_codec_supported_codec(int adapter_index, int video_codec_param, int pix_fmt) +{ + switch (video_codec_param) + { + case AV_CODEC_ID_H264: + if (pix_fmt == AV_PIX_FMT_NV12 || + pix_fmt == AV_PIX_FMT_YUV420P || + pix_fmt == AV_PIX_FMT_YUVJ420P || + pix_fmt == AV_PIX_FMT_YUV420P10LE) + { + 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_YUV420P10LE) + { + 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_YUV420P || + pix_fmt == AV_PIX_FMT_YUVJ420P || + pix_fmt == AV_PIX_FMT_YUV420P10LE) + { + return hb_qsv_decode_av1_is_supported(adapter_index); + } + break; + default: + return 0; + } + return 0; +} + +int hb_qsv_setup_job(hb_job_t *job) +{ +#if defined(_WIN32) || defined(__MINGW32__) + if (job->qsv.ctx->dx_index >= 0) + { + hb_qsv_param_parse_dx_index(job, job->qsv.ctx->dx_index); + } + hb_qsv_parse_adapter_index(job); +#endif + int async_depth_default = hb_qsv_param_default_async_depth(); + if (job->qsv.async_depth <= 0 || job->qsv.async_depth > async_depth_default) + { + job->qsv.async_depth = async_depth_default; + } + // Make sure QSV Decode is only True if the selected QSV adapter supports decode. + job->qsv.decode = job->qsv.decode && hb_qsv_available(); + return 0; +} + int hb_qsv_decode_is_enabled(hb_job_t *job) { return ((job != NULL && job->qsv.decode) && (job->title->video_decode_support & HB_DECODE_SUPPORT_QSV)) && - (job->vcodec == HB_VCODEC_QSV_H265 ? (qsv_hardware_generation(hb_get_cpu_platform()) >= QSV_G5) : 1) && - (job->vcodec == HB_VCODEC_QSV_H265_10BIT ? (qsv_hardware_generation(hb_get_cpu_platform()) >= QSV_G6) : 1); + hb_qsv_decode_codec_supported_codec(hb_qsv_get_adapter_index(), + job->title->video_codec_param, job->pix_fmt); } static int hb_dxva2_device_check(); @@ -1083,7 +1344,7 @@ int hb_qsv_hw_filters_are_enabled(hb_job_t *job) int hb_qsv_is_enabled(hb_job_t *job) { - return hb_qsv_decode_is_enabled(job) || hb_qsv_info_get(job->vcodec); + return hb_qsv_decode_is_enabled(job) || hb_qsv_encoder_info_get(hb_qsv_get_adapter_index(), job->vcodec); } int hb_qsv_full_path_is_enabled(hb_job_t *job) @@ -1092,6 +1353,7 @@ int hb_qsv_full_path_is_enabled(hb_job_t *job) static int device_check_succeded = 0; int codecs_exceptions = 0; int qsv_full_path_is_enabled = 0; + hb_qsv_info_t *info = hb_qsv_encoder_info_get(hb_qsv_get_adapter_index(), job->vcodec); if(!device_check_completed) { @@ -1102,19 +1364,24 @@ int hb_qsv_full_path_is_enabled(hb_job_t *job) codecs_exceptions = (job->title->pix_fmt == AV_PIX_FMT_YUV420P10 && job->vcodec == HB_VCODEC_QSV_H264); qsv_full_path_is_enabled = (hb_qsv_decode_is_enabled(job) && - hb_qsv_info_get(job->vcodec) && + info && hb_qsv_implementation_is_hardware(info->implementation) && device_check_succeded && !job->qsv.ctx->num_cpu_filters) && !codecs_exceptions; return qsv_full_path_is_enabled; } int hb_qsv_copyframe_is_slow(int encoder) { - hb_qsv_info_t *info = hb_qsv_info_get(encoder); - if (info != NULL && qsv_implementation_is_hardware(info->implementation)) + hb_qsv_info_t *info = hb_qsv_encoder_info_get(hb_qsv_get_adapter_index(), encoder); + if (info != NULL && hb_qsv_implementation_is_hardware(info->implementation)) { - // we should really check the driver version, but since it's not - // available, checking the API version is the best we can do :-( - return !HB_CHECK_MFX_VERSION(qsv_hardware_version, 1, 7); + hb_qsv_adapter_details_t* details = hb_qsv_get_adapters_details_by_index(hb_qsv_get_adapter_index()); + if (details) + { + // we should really check the driver version, but since it's not + // available, checking the API version is the best we can do :-( + return !HB_CHECK_MFX_VERSION(details->qsv_hardware_version, 1, 7); + } + return 0; } return 0; } @@ -1225,33 +1492,6 @@ float hb_qsv_atof(const char *str, int *err) return v; } -int hb_qsv_param_parse_dx_index(hb_job_t *job, const int dx_index) -{ - for (int i = 0; i < qsv_adapters_info.NumActual; i++) - { - mfxAdapterInfo* info = &qsv_adapters_info.Adapters[i]; - // find DirectX adapter with given index in list of QSV adapters - // if -1 use first adapter with highest priority - if (info && ((info->Number == dx_index) || (dx_index == -1))) - { - job->qsv.ctx->qsv_device = av_mallocz_array(32, sizeof(*job->qsv.ctx->qsv_device)); - if (!job->qsv.ctx->qsv_device) - { - hb_error("hb_qsv_param_parse_dx_index: failed to allocate memory for qsv device"); - return -1; - } - sprintf(job->qsv.ctx->qsv_device, "%u", info->Number); - job->qsv.ctx->dx_index = info->Number; - hb_log("qsv: %s qsv adapter with index %s has been selected", - (info->Platform.MediaAdapterType == MFX_MEDIA_INTEGRATED) ? "integrated" : - (info->Platform.MediaAdapterType == MFX_MEDIA_DISCRETE) ? "discrete" : "unknown", job->qsv.ctx->qsv_device); - return 0; - } - } - hb_error("qsv: incorrect qsv device index %d", dx_index); - return -1; -} - int hb_qsv_param_parse(hb_qsv_param_t *param, hb_qsv_info_t *info, hb_job_t *job, const char *key, const char *value) { @@ -1737,6 +1977,7 @@ int hb_qsv_param_parse(hb_qsv_param_t *param, hb_qsv_info_t *info, hb_job_t *job return HB_QSV_PARAM_UNSUPPORTED; } } +#if defined(_WIN32) || defined(__MINGW32__) else if (!strcasecmp(key, "gpu")) { // Check if was parsed already in decoder initialization @@ -1749,6 +1990,7 @@ int hb_qsv_param_parse(hb_qsv_param_t *param, hb_qsv_info_t *info, hb_job_t *job } } } +#endif else if (!strcasecmp(key, "scalingmode") || !strcasecmp(key, "vpp-sm")) { @@ -1815,7 +2057,7 @@ int hb_qsv_profile_parse(hb_qsv_param_t *param, hb_qsv_info_t *info, const char /* HEVC10 supported starting from KBL/G6 */ if (profile->value == MFX_PROFILE_HEVC_MAIN10 && - qsv_hardware_generation(hb_get_cpu_platform()) < QSV_G6) + 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; @@ -1836,7 +2078,7 @@ int hb_qsv_profile_parse(hb_qsv_param_t *param, hb_qsv_info_t *info, const char else if (((profile_key != NULL && !strcasecmp(profile_key, "auto")) || profile_key == NULL) && codec == HB_VCODEC_QSV_H265_10BIT && param->videoParam->mfx.CodecId == MFX_CODEC_HEVC && - qsv_hardware_generation(hb_get_cpu_platform()) >= QSV_G6) + 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; @@ -1888,7 +2130,7 @@ int hb_qsv_level_parse(hb_qsv_param_t *param, hb_qsv_info_t *info, const char *l const char* const* hb_qsv_preset_get_names() { - if (qsv_hardware_generation(hb_get_cpu_platform()) >= QSV_G3) + if (hb_qsv_hardware_generation(hb_qsv_get_platform(hb_qsv_get_adapter_index())) >= QSV_G3) { return hb_qsv_preset_names2; } @@ -1930,19 +2172,23 @@ const char* const* hb_qsv_level_get_names(int encoder) const char* hb_qsv_video_quality_get_name(uint32_t codec) { uint64_t caps = 0; - switch (codec) + hb_qsv_adapter_details_t* details = hb_qsv_get_adapters_details_by_index(hb_qsv_get_adapter_index()); + if (details) { - case HB_VCODEC_QSV_H264: - if (hb_qsv_info_avc != NULL) caps = hb_qsv_info_avc->capabilities; - break; + switch (codec) + { + case HB_VCODEC_QSV_H264: + if (details->hb_qsv_info_avc != NULL) caps = details->hb_qsv_info_avc->capabilities; + break; - case HB_VCODEC_QSV_H265_10BIT: - case HB_VCODEC_QSV_H265: - if (hb_qsv_info_hevc != NULL) caps = hb_qsv_info_hevc->capabilities; - break; + case HB_VCODEC_QSV_H265_10BIT: + case HB_VCODEC_QSV_H265: + if (details->hb_qsv_info_hevc != NULL) caps = details->hb_qsv_info_hevc->capabilities; + break; - default: - break; + default: + break; + } } return (caps & HB_QSV_CAP_RATECONTROL_ICQ) ? "ICQ" : "QP"; } @@ -1951,25 +2197,29 @@ void hb_qsv_video_quality_get_limits(uint32_t codec, float *low, float *high, float *granularity, int *direction) { uint64_t caps = 0; - switch (codec) - { - case HB_VCODEC_QSV_H265_10BIT: - case HB_VCODEC_QSV_H265: - if (hb_qsv_info_hevc != NULL) caps = hb_qsv_info_hevc->capabilities; - *direction = 1; - *granularity = 1.; - *low = (caps & HB_QSV_CAP_RATECONTROL_ICQ) ? 1. : 0.; - *high = 51.; - break; + 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_QSV_H265_10BIT: + case HB_VCODEC_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_QSV_H264: - default: - if (hb_qsv_info_avc != NULL) caps = hb_qsv_info_avc->capabilities; - *direction = 1; - *granularity = 1.; - *low = (caps & HB_QSV_CAP_RATECONTROL_ICQ) ? 1. : 0.; - *high = 51.; - break; + case HB_VCODEC_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; + } } } @@ -1987,7 +2237,7 @@ int hb_qsv_param_default_preset(hb_qsv_param_t *param, } else { - hb_error("hb_qsv_param_default_preset: invalid pointer(s)"); + hb_error("hb_qsv_param_default_preset: invalid pointer(s) param=%p videoParam=%p info=%p preset=%p", param, videoParam, info, preset); return -1; } if (preset != NULL && preset[0] != '\0') @@ -2021,7 +2271,7 @@ int hb_qsv_param_default_preset(hb_qsv_param_t *param, * LookAhead: 0 (off) * LookAheadDepth: Not Applicable */ - if (qsv_hardware_generation(hb_get_cpu_platform()) >= QSV_G3) + if (hb_qsv_hardware_generation(hb_qsv_get_platform(hb_qsv_get_adapter_index())) >= QSV_G3) { param->rc.lookahead = 0; param->videoParam->mfx.NumRefFrame = 1; @@ -2046,14 +2296,14 @@ int hb_qsv_param_default_preset(hb_qsv_param_t *param, } else if (!strcasecmp(preset, "speed")) { - if (qsv_hardware_generation(hb_get_cpu_platform()) >= QSV_G7) + if (hb_qsv_hardware_generation(hb_qsv_get_platform(hb_qsv_get_adapter_index())) >= QSV_G7) { // Since IceLake only param->rc.lookahead = 0; param->videoParam->mfx.NumRefFrame = 1; param->videoParam->mfx.TargetUsage = MFX_TARGETUSAGE_7; } - else if (qsv_hardware_generation(hb_get_cpu_platform()) >= QSV_G3) + else if (hb_qsv_hardware_generation(hb_qsv_get_platform(hb_qsv_get_adapter_index())) >= QSV_G3) { /* * HSW TargetUsage: 6 @@ -2101,7 +2351,7 @@ int hb_qsv_param_default_preset(hb_qsv_param_t *param, int hb_qsv_param_default_async_depth() { - return qsv_hardware_generation(hb_get_cpu_platform()) >= QSV_G7 ? 6 : HB_QSV_ASYNC_DEPTH_DEFAULT; + 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, mfxVideoParam *videoParam, @@ -2389,39 +2639,6 @@ uint8_t hb_qsv_frametype_xlat(uint16_t qsv_frametype, uint16_t *out_flags) return frametype; } -int hb_qsv_impl_set_preferred(const char *name) -{ - if (name == NULL) - { - return -1; - } - if (!strcasecmp(name, "software")) - { - if (qsv_software_info_avc.available) - { - hb_qsv_info_avc = &qsv_software_info_avc; - } - if (qsv_software_info_hevc.available) - { - hb_qsv_info_hevc = &qsv_software_info_hevc; - } - return 0; - } - if (!strcasecmp(name, "hardware")) - { - if (qsv_hardware_info_avc.available) - { - hb_qsv_info_avc = &qsv_hardware_info_avc; - } - if (qsv_hardware_info_hevc.available) - { - hb_qsv_info_hevc = &qsv_hardware_info_hevc; - } - return 0; - } - return -1; -} - const char* hb_qsv_impl_get_name(int impl) { switch (MFX_IMPL_BASETYPE(impl)) @@ -2466,10 +2683,14 @@ const char* hb_qsv_impl_get_via_name(int impl) void hb_qsv_force_workarounds() { #define FORCE_WORKAROUNDS ~(HB_QSV_CAP_OPTION2_BREFTYPE) - qsv_software_info_avc.capabilities &= FORCE_WORKAROUNDS; - qsv_hardware_info_avc.capabilities &= FORCE_WORKAROUNDS; - qsv_software_info_hevc.capabilities &= FORCE_WORKAROUNDS; - qsv_hardware_info_hevc.capabilities &= FORCE_WORKAROUNDS; + hb_qsv_adapter_details_t* details = hb_qsv_get_adapters_details_by_index(hb_qsv_get_adapter_index()); + if (details) + { + details->qsv_software_info_avc.capabilities &= FORCE_WORKAROUNDS; + details->qsv_hardware_info_avc.capabilities &= FORCE_WORKAROUNDS; + details->qsv_software_info_hevc.capabilities &= FORCE_WORKAROUNDS; + details->qsv_hardware_info_hevc.capabilities &= FORCE_WORKAROUNDS; + } #undef FORCE_WORKAROUNDS } @@ -2494,6 +2715,164 @@ typedef IDirect3D9* WINAPI pDirect3DCreate9(UINT); typedef HRESULT WINAPI pDirect3DCreate9Ex(UINT, IDirect3D9Ex **); typedef HRESULT(WINAPI *HB_PFN_CREATE_DXGI_FACTORY)(REFIID riid, void **ppFactory); +int hb_qsv_info_init() +{ + // Collect the information about qsv adapters + g_qsv_adapters_info.Adapters = NULL; + g_qsv_adapters_info.NumAlloc = 0; + g_qsv_adapters_info.NumActual = 0; + int err = hb_qsv_query_adapters(&g_qsv_adapters_info); + if (err) + { + hb_error("hb_qsv_info_init: failed to query qsv adapters"); + return -1; + } + hb_qsv_make_adapters_list(&g_qsv_adapters_info, &g_qsv_adapters_list); + hb_qsv_make_adapters_details_list(&g_qsv_adapters_info, &g_qsv_adapters_details_list); + hb_qsv_collect_adapters_details(g_qsv_adapters_list, g_qsv_adapters_details_list); + return 0; +} + +int hb_qsv_set_adapter_index(int adapter_index) +{ + for (int i = 0; i < g_qsv_adapters_info.NumActual; i++) + { + mfxAdapterInfo* info = &g_qsv_adapters_info.Adapters[i]; + if (info && (info->Number == 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; +} + +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 +#if (MFX_VERSION >= 1031) + case MFX_PLATFORM_ELKHARTLAKE: + case MFX_PLATFORM_JASPERLAKE: + case MFX_PLATFORM_TIGERLAKE: + platform = HB_CPU_PLATFORM_INTEL_TGL; + break; + // TODO: update mfx_dispatch to add MFX_PLATFORM_ALDERLAKE_S + // case MFX_PLATFORM_ALDERLAKE_S: + // platform = HB_CPU_PLATFORM_INTEL_TGL; + // break; +#endif + default: + platform = HB_CPU_PLATFORM_UNSPECIFIED; + } + return platform; +} + +static void hb_qsv_free_adapters_details() +{ + for (int i = 0; i < hb_list_count(g_qsv_adapters_details_list); i++) + { + hb_qsv_adapter_details_t *details = hb_list_item(g_qsv_adapters_details_list, i); + if (details->index) + { + av_free(details); + } + } +} + +static const char* hb_qsv_get_adapter_type(const mfxAdapterInfo* info) +{ + if (info) + { + return (info->Platform.MediaAdapterType == MFX_MEDIA_INTEGRATED) ? "integrated" : + (info->Platform.MediaAdapterType == MFX_MEDIA_DISCRETE) ? "discrete" : "unknown"; + } + return NULL; +} + +int hb_qsv_get_platform(int adapter_index) +{ + for (int i = 0; i < g_qsv_adapters_info.NumActual; i++) + { + mfxAdapterInfo* info = &g_qsv_adapters_info.Adapters[i]; + // find DirectX adapter with given index in list of QSV adapters + // if -1 use first adapter with highest priority + if (info && ((info->Number == adapter_index) || (adapter_index == -1))) + { + return qsv_map_mfx_platform_codename(info->Platform.CodeName); + } + } + hb_error("qsv: incorrect qsv device index %d", adapter_index); + 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 < g_qsv_adapters_info.NumActual; i++) + { + mfxAdapterInfo* info = &g_qsv_adapters_info.Adapters[i]; + // find DirectX adapter with given index in list of QSV adapters + // if -1 use first adapter with highest priority + if (info && ((info->Number == dx_index) || (dx_index == -1))) + { + if (!job->qsv.ctx->qsv_device) + { + job->qsv.ctx->qsv_device = av_mallocz_array(32, sizeof(*job->qsv.ctx->qsv_device)); + if (!job->qsv.ctx->qsv_device) + { + hb_error("hb_qsv_param_parse_dx_index: failed to allocate memory for qsv device"); + return -1; + } + } + sprintf(job->qsv.ctx->qsv_device, "%u", info->Number); + job->qsv.ctx->dx_index = info->Number; + hb_log("qsv: %s qsv adapter with index %s has been selected", hb_qsv_get_adapter_type(info), job->qsv.ctx->qsv_device); + hb_qsv_set_adapter_index(info->Number); + return 0; + } + } + hb_error("qsv: incorrect qsv device index %d", dx_index); + return -1; +} + static int hb_dxva2_device_create9(HMODULE d3dlib, UINT adapter, IDirect3D9 **d3d9_out) { pDirect3DCreate9 *createD3D = (pDirect3DCreate9 *)hb_dlsym(d3dlib, "Direct3DCreate9"); @@ -2938,6 +3317,8 @@ static int hb_qsv_get_dx_device(hb_job_t *job) static int hb_qsv_make_adapters_list(const mfxAdaptersInfo* adapters_info, hb_list_t **qsv_adapters_list) { + int max_generation = QSV_G0; + if (!qsv_adapters_list) { hb_error("hb_qsv_make_adapters_list: destination pointer is NULL"); @@ -2959,6 +3340,13 @@ static int hb_qsv_make_adapters_list(const mfxAdaptersInfo* adapters_info, hb_li mfxAdapterInfo* info = &adapters_info->Adapters[i]; if (info) { + int generation = hb_qsv_hardware_generation(qsv_map_mfx_platform_codename(info->Platform.CodeName)); + // select default QSV adapter + if (generation > max_generation || info->Platform.MediaAdapterType == MFX_MEDIA_DISCRETE) + { + max_generation = generation; + hb_qsv_set_adapter_index(info->Number); + } hb_list_add(list, (void*)&info->Number); } } @@ -2966,6 +3354,38 @@ static int hb_qsv_make_adapters_list(const mfxAdaptersInfo* adapters_info, hb_li return 0; } +static int hb_qsv_make_adapters_details_list(const mfxAdaptersInfo* adapters_info, hb_list_t **hb_qsv_adapters_details_list) +{ + if (*hb_qsv_adapters_details_list) + { + hb_error("hb_qsv_make_adapters_details_list: hb_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_details_list: hb_list_init() failed"); + return -1; + } + for (int i = 0; i < adapters_info->NumActual; i++) + { + mfxAdapterInfo* info = &adapters_info->Adapters[i]; + if (info) + { + hb_qsv_adapter_details_t* adapter_details = av_mallocz(sizeof(hb_qsv_adapter_details_t)); + if (!adapter_details) + { + hb_error("hb_qsv_make_adapters_details_list: adapter_details allocation failed"); + return -1; + } + init_adapter_details(adapter_details); + hb_list_add(list, (void*)adapter_details); + } + } + *hb_qsv_adapters_details_list = list; + return 0; +} + static int hb_qsv_query_adapters(mfxAdaptersInfo* adapters_info) { // Get number of Intel graphics adapters @@ -2991,17 +3411,6 @@ static int hb_qsv_query_adapters(mfxAdaptersInfo* adapters_info) hb_error("hb_qsv_query_adapters: failed to collect information about Intel graphics adapters %d", sts); return -1; } - // Reverse order to make dGPU higher priority - int start = 0; - int end = adapters_info->NumActual - 1; - while (start < end) - { - mfxAdapterInfo temp = adapters_info->Adapters[start]; - adapters_info->Adapters[start] = adapters_info->Adapters[end]; - adapters_info->Adapters[end] = temp; - start++; - end--; - } } } return 0; @@ -3247,6 +3656,37 @@ err_out: return err; } +int hb_qsv_parse_adapter_index(hb_job_t *job) +{ + int ret = 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); + if (!strcasecmp(key, "gpu")) + { + hb_value_t *value = hb_dict_iter_value(iter); + char *str = hb_value_get_string_xform(value); + int dx_index = hb_qsv_atoi(str, &ret); + free(str); + if (!ret) + { + hb_qsv_param_parse_dx_index(job, dx_index); + } + } + } + hb_dict_free(&options_list); + } + return 0; +} + int hb_create_ffmpeg_pool(hb_job_t *job, int coded_width, int coded_height, enum AVPixelFormat sw_pix_fmt, int pool_size, int extra_hw_frames, AVBufferRef **out_hw_frames_ctx) { AVHWFramesContext *frames_ctx; @@ -3269,20 +3709,9 @@ int hb_create_ffmpeg_pool(hb_job_t *job, int coded_width, int coded_height, enum iter = hb_dict_iter_next(options_list, iter)) { const char *key = hb_dict_iter_key(iter); - if (!strcasecmp(key, "gpu") && !job->qsv.ctx->qsv_device) - { - hb_value_t *value = hb_dict_iter_value(iter); - char *str = hb_value_get_string_xform(value); - int dx_index = hb_qsv_atoi(str, &ret); - free(str); - if (!ret) - { - hb_qsv_param_parse_dx_index(job, dx_index); - } - } if ((!strcasecmp(key, "scalingmode") || !strcasecmp(key, "vpp-sm")) && hb_qsv_hw_filters_are_enabled(job)) { - hb_qsv_info_t *info = hb_qsv_info_get(job->vcodec); + hb_qsv_info_t *info = hb_qsv_encoder_info_get(hb_qsv_get_adapter_index(), job->vcodec); if (info && (info->capabilities & HB_QSV_CAP_VPP_SCALING)) { hb_value_t *value = hb_dict_iter_value(iter); @@ -3300,7 +3729,7 @@ int hb_create_ffmpeg_pool(hb_job_t *job, int coded_width, int coded_height, enum } if ((!strcasecmp(key, "interpolationmethod") || !strcasecmp(key, "vpp-im")) && hb_qsv_hw_filters_are_enabled(job)) { - hb_qsv_info_t *info = hb_qsv_info_get(job->vcodec); + hb_qsv_info_t *info = hb_qsv_encoder_info_get(hb_qsv_get_adapter_index(), job->vcodec); if (info && (info->capabilities & HB_QSV_CAP_VPP_INTERPOLATION)) { hb_value_t *value = hb_dict_iter_value(iter); @@ -3321,7 +3750,7 @@ int hb_create_ffmpeg_pool(hb_job_t *job, int coded_width, int coded_height, enum } if (!job->qsv.ctx->qsv_device) - hb_qsv_param_parse_dx_index(job, -1); + hb_qsv_param_parse_dx_index(job, hb_qsv_get_adapter_index()); ret = qsv_device_init(job); if (ret < 0) @@ -3352,7 +3781,6 @@ int hb_create_ffmpeg_pool(hb_job_t *job, int coded_width, int coded_height, enum hb_error("hb_create_ffmpeg_pool: av_hwframe_ctx_init failed %d", ret); return ret; } - return 0; } @@ -3529,12 +3957,45 @@ int hb_qsv_sanitize_filter_list(hb_job_t *job) return 0; } -hb_list_t* hb_qsv_adapters_list() +#else // other OS + +int hb_qsv_get_platform(int adapter_index) { - return qsv_adapters_list; + return hb_get_cpu_platform(); } -#else // other OS +int hb_qsv_info_init() +{ + if (g_qsv_adapters_list) + { + hb_error("hb_qsv_info_init: qsv_adapters_list is allocated already"); + return -1; + } + g_qsv_adapters_list = hb_list_init(); + if (g_qsv_adapters_list == NULL) + { + hb_error("hb_qsv_info_init: g_qsv_adapters_list allocation failed"); + return -1; + } + if (g_qsv_adapters_details_list) + { + hb_error("hb_qsv_info_init: g_qsv_adapters_details_list is allocated already"); + return -1; + } + g_qsv_adapters_details_list = hb_list_init(); + if (g_qsv_adapters_details_list == NULL) + { + hb_error("hb_qsv_info_init: g_qsv_adapters_details_list allocation failed"); + return -1; + } + static int adapter_index = -1; + static hb_qsv_adapter_details_t adapter_details; + init_adapter_details(&adapter_details); + hb_list_add(g_qsv_adapters_list, (void*)&adapter_index); + hb_list_add(g_qsv_adapters_details_list, (void*)&adapter_details); + hb_qsv_collect_adapters_details(g_qsv_adapters_list, g_qsv_adapters_details_list); + return 0; +} int hb_create_ffmpeg_pool(hb_job_t *job, int coded_width, int coded_height, enum AVPixelFormat sw_pix_fmt, int pool_size, int extra_hw_frames, AVBufferRef **out_hw_frames_ctx) { @@ -3608,11 +4069,6 @@ int hb_qsv_release_surface_from_pool_by_surface_pointer(HBQSVFramesContext* hb_e return -1; } -hb_list_t* hb_qsv_adapters_list() -{ - return NULL; -} - #endif hb_qsv_context* hb_qsv_context_init() @@ -3640,9 +4096,33 @@ void hb_qsv_context_uninit(hb_job_t *job) hb_qsv_context_clean(ctx, hb_qsv_full_path_is_enabled(job)); av_free(ctx); job->qsv.ctx = NULL; + + 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; + } +#if defined(_WIN32) || defined(__MINGW32__) + if (g_qsv_adapters_info.Adapters) + { + av_free(g_qsv_adapters_info.Adapters); + } + g_qsv_adapters_info.Adapters = NULL; + g_qsv_adapters_info.NumAlloc = 0; + g_qsv_adapters_info.NumActual = 0; +#endif + g_adapter_index = -1; } -#else +#else // HB_PROJECT_FEATURE_QSV int hb_qsv_available() { diff --git a/libhb/qsv_libav.c b/libhb/qsv_libav.c index b03b9830c..7a0a8f8d0 100644 --- a/libhb/qsv_libav.c +++ b/libhb/qsv_libav.c @@ -236,8 +236,11 @@ int hb_qsv_context_clean(hb_qsv_context * qsv, int full_job) hb_qsv_pipe_list_clean(&qsv->pipes); if (qsv->mfx_session && !full_job) { + // MFXClose() fails in the media_driver under Linux when encoding interrupted +#if defined(_WIN32) || defined(__MINGW32__) sts = MFXClose(qsv->mfx_session); HB_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts); +#endif qsv->mfx_session = 0; } } diff --git a/libhb/sync.c b/libhb/sync.c index 736f271b2..bf342d36a 100644 --- a/libhb/sync.c +++ b/libhb/sync.c @@ -2915,7 +2915,7 @@ static int syncVideoWork( hb_work_object_t * w, hb_buffer_t ** buf_in, // as currently for such support we cannot allocate >64 slices per texture // due to MSFT limitation, not impacting other cases if (pv->common->job->qsv.ctx && (pv->common->job->qsv.ctx->la_is_enabled == 1) - && hb_qsv_full_path_is_enabled(pv->common->job)) + && pv->common->job->qsv.ctx->full_path_is_enabled) { pv->stream->max_len = SYNC_MIN_VIDEO_QUEUE_LEN; pv->common->job->qsv.ctx->la_is_enabled++; diff --git a/libhb/work.c b/libhb/work.c index 94ebe889b..65532361f 100644 --- a/libhb/work.c +++ b/libhb/work.c @@ -141,6 +141,9 @@ static void work_func( void * _work ) hb_job_close(&job); job = new_job; } +#if HB_PROJECT_FEATURE_QSV + hb_qsv_setup_job(job); +#endif hb_job_setup_passes(job->h, job, passes); hb_job_close(&job); @@ -871,7 +874,7 @@ static int get_best_pix_ftm(hb_job_t * job) { int bit_depth = hb_get_bit_depth(job->title->pix_fmt); #if HB_PROJECT_FEATURE_QSV && (defined( _WIN32 ) || defined( __MINGW32__ )) - if (hb_qsv_info_get(job->vcodec)) + if (hb_qsv_encoder_info_get(hb_qsv_get_adapter_index(), job->vcodec)) { if (hb_qsv_full_path_is_enabled(job)) { @@ -1461,7 +1464,6 @@ static void do_job(hb_job_t *job) #if HB_PROJECT_FEATURE_QSV if (hb_qsv_is_enabled(job)) { - job->qsv.ctx = hb_qsv_context_init(); #if HB_PROJECT_FEATURE_QSV && (defined( _WIN32 ) || defined( __MINGW32__ )) if (hb_qsv_full_path_is_enabled(job)) { diff --git a/test/test.c b/test/test.c index 9bf8e90bf..0aefedcc5 100644 --- a/test/test.c +++ b/test/test.c @@ -199,6 +199,7 @@ static int stop_at_frame = 0; static uint64_t min_title_duration = 10; #if HB_PROJECT_FEATURE_QSV static int qsv_async_depth = -1; +static int qsv_adapter = -1; static int qsv_decode = -1; #endif @@ -1938,8 +1939,15 @@ if (hb_qsv_available()) " explicitly synchronized.\n" " Omit 'number' for zero.\n" " (default: 4)\n" + ); +#if defined(_WIN32) || defined(__MINGW32__) + fprintf( out, +" --qsv-adapter[=index]\n" +" Set QSV hardware graphics adapter index\n" +" (default: QSV hardware graphics adapter with highest hardware generation)\n" "\n" ); +#endif } #endif } @@ -2146,36 +2154,36 @@ static int ParseOptions( int argc, char ** argv ) #define AUDIO_DITHER 294 #define QSV_BASELINE 295 #define QSV_ASYNC_DEPTH 296 - #define QSV_IMPLEMENTATION 297 - #define FILTER_NLMEANS 298 - #define FILTER_NLMEANS_TUNE 299 - #define AUDIO_LANG_LIST 300 - #define SUBTITLE_LANG_LIST 301 - #define PRESET_EXPORT 302 - #define PRESET_EXPORT_DESC 303 - #define PRESET_EXPORT_FILE 304 - #define PRESET_IMPORT 305 - #define PRESET_IMPORT_GUI 306 - #define VERSION 307 - #define DESCRIBE 308 - #define PAD 309 - #define FILTER_COMB_DETECT 310 - #define QUEUE_IMPORT 311 - #define FILTER_UNSHARP 312 - #define FILTER_UNSHARP_TUNE 313 - #define FILTER_LAPSHARP 314 - #define FILTER_LAPSHARP_TUNE 315 - #define JSON_LOGGING 316 - #define SSA_FILE 317 - #define SSA_OFFSET 318 - #define SSA_LANG 319 - #define SSA_DEFAULT 320 - #define SSA_BURN 321 - #define FILTER_CHROMA_SMOOTH 322 - #define FILTER_CHROMA_SMOOTH_TUNE 323 - #define FILTER_DEBLOCK_TUNE 324 - #define FILTER_COLORSPACE 325 - + #define QSV_ADAPTER 297 + #define QSV_IMPLEMENTATION 298 + #define FILTER_NLMEANS 299 + #define FILTER_NLMEANS_TUNE 300 + #define AUDIO_LANG_LIST 301 + #define SUBTITLE_LANG_LIST 302 + #define PRESET_EXPORT 303 + #define PRESET_EXPORT_DESC 304 + #define PRESET_EXPORT_FILE 305 + #define PRESET_IMPORT 306 + #define PRESET_IMPORT_GUI 307 + #define VERSION 308 + #define DESCRIBE 309 + #define PAD 310 + #define FILTER_COMB_DETECT 311 + #define QUEUE_IMPORT 312 + #define FILTER_UNSHARP 313 + #define FILTER_UNSHARP_TUNE 314 + #define FILTER_LAPSHARP 315 + #define FILTER_LAPSHARP_TUNE 316 + #define JSON_LOGGING 317 + #define SSA_FILE 318 + #define SSA_OFFSET 319 + #define SSA_LANG 320 + #define SSA_DEFAULT 321 + #define SSA_BURN 322 + #define FILTER_CHROMA_SMOOTH 323 + #define FILTER_CHROMA_SMOOTH_TUNE 324 + #define FILTER_DEBLOCK_TUNE 325 + #define FILTER_COLORSPACE 326 for( ;; ) { static struct option long_options[] = @@ -2189,6 +2197,7 @@ static int ParseOptions( int argc, char ** argv ) #if HB_PROJECT_FEATURE_QSV { "qsv-baseline", no_argument, NULL, QSV_BASELINE, }, { "qsv-async-depth", required_argument, NULL, QSV_ASYNC_DEPTH, }, + { "qsv-adapter", required_argument, NULL, QSV_ADAPTER }, { "qsv-implementation", required_argument, NULL, QSV_IMPLEMENTATION, }, { "disable-qsv-decoding", no_argument, &qsv_decode, 0, }, { "enable-qsv-decoding", no_argument, &qsv_decode, 1, }, @@ -3108,6 +3117,9 @@ static int ParseOptions( int argc, char ** argv ) case QSV_ASYNC_DEPTH: qsv_async_depth = atoi(optarg); break; + case QSV_ADAPTER: + qsv_adapter = atoi(optarg); + break; case QSV_IMPLEMENTATION: hb_qsv_impl_set_preferred(optarg); break; @@ -4212,6 +4224,11 @@ static hb_dict_t * PreparePreset(const char *preset_name) hb_dict_set(preset, "VideoQSVAsyncDepth", hb_value_int(qsv_async_depth)); } + if (qsv_adapter >= 0) + { + hb_dict_set(preset, "VideoQSVAdapterIndex", + hb_value_int(qsv_adapter)); + } if (qsv_decode != -1) { hb_dict_set(preset, "VideoQSVDecode", hb_value_int(qsv_decode)); diff --git a/win/CS/HandBrake.Interop/Interop/HandBrakeHardwareEncoderHelper.cs b/win/CS/HandBrake.Interop/Interop/HandBrakeHardwareEncoderHelper.cs index 168b7773d..8a36224c2 100644 --- a/win/CS/HandBrake.Interop/Interop/HandBrakeHardwareEncoderHelper.cs +++ b/win/CS/HandBrake.Interop/Interop/HandBrakeHardwareEncoderHelper.cs @@ -99,8 +99,9 @@ namespace HandBrake.Interop.Interop { try { - int cpu_platform = HBFunctions.hb_get_cpu_platform(); - int hardware = HBFunctions.qsv_hardware_generation(cpu_platform); + int adapter_index = HBFunctions.hb_qsv_get_adapter_index(); + int qsv_platform = HBFunctions.hb_qsv_get_platform(adapter_index); + int hardware = HBFunctions.hb_qsv_hardware_generation(qsv_platform); return hardware; } catch (Exception exc) diff --git a/win/CS/HandBrake.Interop/Interop/HbLib/HbFunctions.cs b/win/CS/HandBrake.Interop/Interop/HbLib/HbFunctions.cs index 6f55ff9d5..4fd7dcd4e 100644 --- a/win/CS/HandBrake.Interop/Interop/HbLib/HbFunctions.cs +++ b/win/CS/HandBrake.Interop/Interop/HbLib/HbFunctions.cs @@ -305,8 +305,14 @@ namespace HandBrake.Interop.Interop.HbLib [DllImport("hb", EntryPoint = "hb_get_cpu_platform", CallingConvention = CallingConvention.Cdecl)] public static extern int hb_get_cpu_platform(); - [DllImport("hb", EntryPoint = "qsv_hardware_generation", CallingConvention = CallingConvention.Cdecl)] - public static extern int qsv_hardware_generation(int cpu_platform); + [DllImport("hb", EntryPoint = "hb_qsv_get_platform", CallingConvention = CallingConvention.Cdecl)] + public static extern int hb_qsv_get_platform(int adapter_index); + + [DllImport("hb", EntryPoint = "hb_qsv_hardware_generation", CallingConvention = CallingConvention.Cdecl)] + public static extern int hb_qsv_hardware_generation(int cpu_platform); + + [DllImport("hb", EntryPoint = "hb_qsv_get_adapter_index", CallingConvention = CallingConvention.Cdecl)] + public static extern int hb_qsv_get_adapter_index(); [DllImport("hb", EntryPoint = "hb_qsv_adapters_list", CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr hb_qsv_adapters_list(); |