/* qsv_common.c * * Copyright (c) 2003-2020 HandBrake Team * This file is part of the HandBrake source code. * Homepage: . * It may be used under the terms of the GNU General Public License v2. * For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #include "handbrake/handbrake.h" #include "handbrake/project.h" #if HB_PROJECT_FEATURE_QSV #include #include #include "handbrake/handbrake.h" #include "handbrake/ports.h" #include "handbrake/common.h" #include "handbrake/hb_dict.h" #include "handbrake/qsv_common.h" #include "handbrake/h264_common.h" #include "handbrake/h265_common.h" #include "handbrake/hbffmpeg.h" #include "libavfilter/avfilter.h" #include "libavfilter/buffersrc.h" #include "libavfilter/buffersink.h" #include "libavutil/hwcontext_qsv.h" #include "libavutil/hwcontext.h" #include "mfx/mfxadapter.h" // 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, }; // 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 hb_triplet_t hb_qsv_h264_levels[] = { { "1.0", "1.0", MFX_LEVEL_AVC_1, }, { "1b", "1b", MFX_LEVEL_AVC_1b, }, { "1.1", "1.1", MFX_LEVEL_AVC_11, }, { "1.2", "1.2", MFX_LEVEL_AVC_12, }, { "1.3", "1.3", MFX_LEVEL_AVC_13, }, { "2.0", "2.0", MFX_LEVEL_AVC_2, }, { "2.1", "2.1", MFX_LEVEL_AVC_21, }, { "2.2", "2.2", MFX_LEVEL_AVC_22, }, { "3.0", "3.0", MFX_LEVEL_AVC_3, }, { "3.1", "3.1", MFX_LEVEL_AVC_31, }, { "3.2", "3.2", MFX_LEVEL_AVC_32, }, { "4.0", "4.0", MFX_LEVEL_AVC_4, }, { "4.1", "4.1", MFX_LEVEL_AVC_41, }, { "4.2", "4.2", MFX_LEVEL_AVC_42, }, { "5.0", "5.0", MFX_LEVEL_AVC_5, }, { "5.1", "5.1", MFX_LEVEL_AVC_51, }, { "5.2", "5.2", MFX_LEVEL_AVC_52, }, { NULL, }, }; static hb_triplet_t hb_qsv_h265_levels[] = { { "1.0", "1.0", MFX_LEVEL_HEVC_1, }, { "2.0", "2.0", MFX_LEVEL_HEVC_2, }, { "2.1", "2.1", MFX_LEVEL_HEVC_21, }, { "3.0", "3.0", MFX_LEVEL_HEVC_3, }, { "3.1", "3.1", MFX_LEVEL_HEVC_31, }, { "4.0", "4.0", MFX_LEVEL_HEVC_4, }, { "4.1", "4.1", MFX_LEVEL_HEVC_41, }, { "5.0", "5.0", MFX_LEVEL_HEVC_5, }, { "5.1", "5.1", MFX_LEVEL_HEVC_51, }, { "5.2", "5.2", MFX_LEVEL_HEVC_52, }, { "6.0", "6.0", MFX_LEVEL_HEVC_6, }, { "6.1", "6.1", MFX_LEVEL_HEVC_61, }, { "6.2", "6.2", MFX_LEVEL_HEVC_62, }, { NULL, }, }; #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 == MAJOR && MFX_VERSION.Minor >= MINOR) int 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: return QSV_G6; case HB_CPU_PLATFORM_INTEL_ICL: return QSV_G7; default: return QSV_FU; } } /* * Determine whether a given mfxIMPL is hardware-accelerated. */ static int qsv_implementation_is_hardware(mfxIMPL implementation) { return MFX_IMPL_BASETYPE(implementation) != MFX_IMPL_SOFTWARE; } int hb_qsv_available() { if (is_hardware_disabled()) { 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)); } int hb_qsv_video_encoder_is_enabled(int encoder) { switch (encoder) { 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) return 0; case HB_VCODEC_QSV_H265: return hb_qsv_info_hevc != NULL && hb_qsv_info_hevc->available; default: return 0; } } int hb_qsv_audio_encoder_is_enabled(int encoder) { switch (encoder) { default: 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_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_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 int query_capabilities(mfxSession session, mfxVersion version, hb_qsv_info_t *info) { /* * 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; hb_list_t *mfxPluginList; mfxExtBuffer *videoExtParam[1]; mfxVideoParam videoParam, inputParam; mfxExtCodingOption extCodingOption; mfxExtCodingOption2 extCodingOption2; mfxExtVideoSignalInfo extVideoSignalInfo; /* Reset capabilities before querying */ info->capabilities = 0; /* Load required MFX plug-ins */ if ((mfxPluginList = hb_qsv_load_plugins(info, session, version)) == NULL) { return 0; // the required plugin(s) couldn't be loaded } /* * 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)) { if (info->implementation & MFX_IMPL_AUDIO) { /* Not yet supported */ return 0; } else { mfxStatus mfxRes; init_video_param(&inputParam); inputParam.mfx.CodecId = info->codec_id; 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, "hb_qsv_info_init: 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; } if (info->implementation & MFX_IMPL_AUDIO) { /* We don't have any audio capability checks yet */ return 0; } else { /* 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 (qsv_hardware_generation(hb_get_cpu_platform()) >= 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) { 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.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.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.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; 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, "hb_qsv_info_init: 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; 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, "hb_qsv_info_init: 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) && info->codec_id == MFX_CODEC_AVC) { init_video_param(&videoParam); videoParam.mfx.CodecId = info->codec_id; 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 (qsv_implementation_is_hardware(info->implementation) && qsv_hardware_generation(hb_get_cpu_platform()) >= 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 (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 (qsv_implementation_is_hardware(info->implementation) && qsv_hardware_generation(hb_get_cpu_platform()) >= 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 (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, "hb_qsv_info_init: mfxExtCodingOption2 check failed (0x%"PRIX32", 0x%"PRIX32", %d)\n", info->codec_id, info->implementation, status); } } } /* Unload MFX plug-ins */ hb_qsv_unload_plugins(&mfxPluginList, session, version); 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(void) { return hb_display_init(DRM_INTEL_DRIVER_NAME, VA_INTEL_DRIVER_NAMES); } int hb_qsv_info_init() { static int init_done = 0; if (init_done) return 0; init_done = 1; /* * 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); query_capabilities(session, qsv_software_version, &qsv_software_info_hevc); // now that we know which hardware encoders are // available, we can set the preferred implementation hb_qsv_impl_set_preferred("software"); } MFXClose(session); } // 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)) { query_capabilities(session, qsv_hardware_version, &qsv_hardware_info_avc); qsv_hardware_info_avc.implementation = MFX_IMPL_HARDWARE_ANY | hw_preference; query_capabilities(session, qsv_hardware_version, &qsv_hardware_info_hevc); qsv_hardware_info_hevc.implementation = MFX_IMPL_HARDWARE_ANY | hw_preference; // now that we know which hardware encoders are // available, we can set the preferred implementation hb_qsv_impl_set_preferred("hardware"); } 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; else #endif hw_preference = 0; } } while(hw_preference != 0); // success return 0; } static void log_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] = ""; /* 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_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_IB_ADAPT) { strcat(buffer, "+ib_adapt"); } if (caps & HB_QSV_CAP_OPTION2_NMPSLICE) { strcat(buffer, "+nmpslice"); } } hb_deep_log(log_level, "%s%s", prefix, strnlen(buffer, 1) ? buffer : " standard feature set"); } void hb_qsv_info_print() { // is QSV available and usable? hb_log("Intel Quick Sync Video support: %s", hb_qsv_available() ? "yes": "no"); if (hb_qsv_available()) { // 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) { 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_info_t* hb_qsv_info_get(int encoder) { switch (encoder) { 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; } } hb_list_t* hb_qsv_load_plugins(hb_qsv_info_t *info, mfxSession session, mfxVersion version) { hb_list_t *mfxPluginList = hb_list_init(); if (mfxPluginList == NULL) { hb_log("hb_qsv_load_plugins: hb_list_init() failed"); goto fail; } 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 (HB_CHECK_MFX_VERSION(version, 1, 15) && qsv_implementation_is_hardware(info->implementation)) { if (MFXVideoUSER_Load(session, &MFX_PLUGINID_HEVCE_HW, 0) == MFX_ERR_NONE) { hb_list_add(mfxPluginList, (void*)&MFX_PLUGINID_HEVCE_HW); } } else if (HB_CHECK_MFX_VERSION(version, 1, 15)) { if (MFXVideoUSER_Load(session, &MFX_PLUGINID_HEVCE_SW, 0) == MFX_ERR_NONE) { hb_list_add(mfxPluginList, (void*)&MFX_PLUGINID_HEVCE_SW); } } } } return mfxPluginList; fail: hb_list_close(&mfxPluginList); return NULL; } void hb_qsv_unload_plugins(hb_list_t **_l, mfxSession session, mfxVersion version) { mfxPluginUID *pluginUID; hb_list_t *mfxPluginList = *_l; if (mfxPluginList != NULL && HB_CHECK_MFX_VERSION(version, 1, 8)) { for (int i = 0; i < hb_list_count(mfxPluginList); i++) { if ((pluginUID = hb_list_item(mfxPluginList, i)) != NULL) { MFXVideoUSER_UnLoad(session, pluginUID); } } } hb_list_close(_l); } 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"; default: return NULL; } } 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); } static int hb_dxva2_device_check(); static int hb_d3d11va_device_check(); int hb_qsv_hw_filters_are_enabled(hb_job_t *job) { return job && job->qsv.ctx && job->qsv.ctx->qsv_filters_are_enabled; } int hb_qsv_is_enabled(hb_job_t *job) { return hb_qsv_decode_is_enabled(job) || hb_qsv_info_get(job->vcodec); } int hb_qsv_full_path_is_enabled(hb_job_t *job) { static int device_check_completed = 0; static int device_check_succeded = 0; int qsv_full_path_is_enabled = 0; if(!device_check_completed) { device_check_succeded = ((hb_d3d11va_device_check() >= 0) || (hb_dxva2_device_check() == 0)) ? 1 : 0; device_check_completed = 1; } qsv_full_path_is_enabled = (hb_qsv_decode_is_enabled(job) && hb_qsv_info_get(job->vcodec) && device_check_succeded && !job->qsv.ctx->num_cpu_filters); 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)) { // 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); } return 0; } int hb_qsv_codingoption_xlat(int val) { switch (HB_QSV_CLIP3(-1, 2, val)) { case 0: return MFX_CODINGOPTION_OFF; case 1: case 2: // MFX_CODINGOPTION_ADAPTIVE, reserved return MFX_CODINGOPTION_ON; case -1: default: return MFX_CODINGOPTION_UNKNOWN; } } int hb_qsv_trellisvalue_xlat(int val) { switch (HB_QSV_CLIP3(0, 3, val)) { case 0: return MFX_TRELLIS_OFF; case 1: // I-frames only return MFX_TRELLIS_I; case 2: // I- and P-frames return MFX_TRELLIS_I|MFX_TRELLIS_P; case 3: // all frames return MFX_TRELLIS_I|MFX_TRELLIS_P|MFX_TRELLIS_B; default: return MFX_TRELLIS_UNKNOWN; } } const char* hb_qsv_codingoption_get_name(int val) { switch (val) { case MFX_CODINGOPTION_ON: return "on"; case MFX_CODINGOPTION_OFF: return "off"; case MFX_CODINGOPTION_ADAPTIVE: return "adaptive"; case MFX_CODINGOPTION_UNKNOWN: return "unknown (auto)"; default: return NULL; } } 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; } int hb_qsv_param_parse_decoder(hb_job_t *job, const char *key, const char *value) { int ivalue, error = 0; if (!strcasecmp(key, "gpu")) { ivalue = hb_qsv_atoi(value, &error); if (!error) { if (ivalue < job->qsv.ctx->adapters_info.NumActual) { for (int i = 0; i < job->qsv.ctx->adapters_info.NumActual; i++) { // convert qsv adapter index to DirectX 11 adapter index if (i == ivalue) { 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: failed to allocate memory for qsv device"); return HB_QSV_PARAM_ERROR; } mfxAdapterInfo* info = &job->qsv.ctx->adapters_info.Adapters[i]; sprintf(job->qsv.ctx->qsv_device, "%u", info->Number); hb_log("hb_qsv_param_parse: %s qsv adapter with index %s has been selected %d", (info->Platform.MediaAdapterType == MFX_MEDIA_INTEGRATED) ? "integrated" : (info->Platform.MediaAdapterType == MFX_MEDIA_DISCRETE) ? "discrete" : "unknown", job->qsv.ctx->qsv_device, strlen(job->qsv.ctx->qsv_device)); return HB_QSV_PARAM_OK; } } } else { hb_error("hb_qsv_param_parse: incorrect qsv device index"); return HB_QSV_PARAM_BAD_VALUE; } } } return HB_QSV_PARAM_OK; } 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) { 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) { param->videoParam->mfx.TargetUsage = HB_QSV_CLIP3(MFX_TARGETUSAGE_1, MFX_TARGETUSAGE_7, ivalue); } } else if (!strcasecmp(key, "num-ref-frame") || !strcasecmp(key, "ref")) { ivalue = hb_qsv_atoi(value, &error); if (!error) { param->videoParam->mfx.NumRefFrame = HB_QSV_CLIP3(0, 16, ivalue); } } else if (!strcasecmp(key, "gop-ref-dist")) { ivalue = hb_qsv_atoi(value, &error); if (!error) { param->gop.gop_ref_dist = HB_QSV_CLIP3(-1, 32, ivalue); } } else if (!strcasecmp(key, "gop-pic-size") || !strcasecmp(key, "keyint")) { ivalue = hb_qsv_atoi(value, &error); if (!error) { param->gop.gop_pic_size = HB_QSV_CLIP3(-1, UINT16_MAX, ivalue); } } else if (!strcasecmp(key, "b-pyramid")) { if (info->capabilities & HB_QSV_CAP_B_REF_PYRAMID) { ivalue = hb_qsv_atoi(value, &error); if (!error) { param->gop.b_pyramid = HB_QSV_CLIP3(-1, 1, ivalue); } } 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) { param->codingOption2.AdaptiveI = hb_qsv_codingoption_xlat(ivalue); } } 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) { param->codingOption2.AdaptiveB = hb_qsv_codingoption_xlat(ivalue); } } 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, UINT16_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, UINT16_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, UINT16_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; } param->codingOption.CAVLC = hb_qsv_codingoption_xlat(ivalue); } } else if (!strcasecmp(key, "videoformat")) { if (info->capabilities & HB_QSV_CAP_VUI_VSINFO) { switch (info->codec_id) { case MFX_CODEC_AVC: ivalue = hb_qsv_atoindex(hb_h264_vidformat_names, value, &error); break; case MFX_CODEC_HEVC: ivalue = hb_qsv_atoindex(hb_h265_vidformat_names, value, &error); break; default: return HB_QSV_PARAM_UNSUPPORTED; } } else { return HB_QSV_PARAM_UNSUPPORTED; } if (!error) { param->videoSignalInfo.VideoFormat = ivalue; } } else if (!strcasecmp(key, "fullrange")) { if (info->capabilities & HB_QSV_CAP_VUI_VSINFO) { switch (info->codec_id) { case MFX_CODEC_AVC: ivalue = hb_qsv_atoindex(hb_h264_fullrange_names, value, &error); break; case MFX_CODEC_HEVC: ivalue = hb_qsv_atoindex(hb_h265_fullrange_names, value, &error); break; default: return HB_QSV_PARAM_UNSUPPORTED; } } else { return HB_QSV_PARAM_UNSUPPORTED; } if (!error) { param->videoSignalInfo.VideoFullRange = ivalue; } } 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) { param->videoSignalInfo.ColourDescriptionPresent = 1; param->videoSignalInfo.ColourPrimaries = ivalue; } } 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) { param->videoSignalInfo.ColourDescriptionPresent = 1; param->videoSignalInfo.TransferCharacteristics = ivalue; } } 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) { param->videoSignalInfo.ColourDescriptionPresent = 1; param->videoSignalInfo.MatrixCoefficients = ivalue; } } 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) { param->videoParam->mfx.FrameInfo.PicStruct = (ivalue ? MFX_PICSTRUCT_FIELD_TFF : MFX_PICSTRUCT_PROGRESSIVE); } } 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) { param->videoParam->mfx.FrameInfo.PicStruct = (ivalue ? MFX_PICSTRUCT_FIELD_BFF : MFX_PICSTRUCT_PROGRESSIVE); } } else if (!strcasecmp(key, "mbbrc")) { if (info->capabilities & HB_QSV_CAP_OPTION2_MBBRC) { ivalue = hb_qsv_atobool(value, &error); if (!error) { param->codingOption2.MBBRC = hb_qsv_codingoption_xlat(ivalue); } } 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) { param->codingOption2.ExtBRC = hb_qsv_codingoption_xlat(ivalue); } } 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) { param->codingOption2.LookAheadDepth = HB_QSV_CLIP3(10, 100, ivalue); } } 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) { param->codingOption2.LookAheadDS = HB_QSV_CLIP3(MFX_LOOKAHEAD_DS_UNKNOWN, MFX_LOOKAHEAD_DS_4x, ivalue); } } 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) { param->codingOption2.Trellis = hb_qsv_trellisvalue_xlat(ivalue); } } 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->videoParam->mfx.LowPower = ivalue ? MFX_CODINGOPTION_ON : MFX_CODINGOPTION_OFF; } } else { return HB_QSV_PARAM_UNSUPPORTED; } } else if (!strcasecmp(key, "gpu")) { // We have already parsed it and used when decoder initialization. return HB_QSV_PARAM_OK; } 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 && qsv_hardware_generation(hb_get_cpu_platform()) < QSV_G6) { hb_log("qsv: HEVC Main10 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 defautls to Main 10 */ 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) { profile = &hb_qsv_h265_profiles[1]; param->videoParam->mfx.CodecProfile = profile->value; } return 0; } int hb_qsv_level_parse(hb_qsv_param_t *param, hb_qsv_info_t *info, const char *level_key) { hb_triplet_t *level = NULL; if (level_key != NULL && *level_key && strcasecmp(level_key, "auto")) { switch (param->videoParam->mfx.CodecId) { case MFX_CODEC_AVC: level = hb_triplet4key(hb_qsv_h264_levels, level_key); break; case MFX_CODEC_HEVC: level = hb_triplet4key(hb_qsv_h265_levels, level_key); break; default: break; } if (level == NULL) { return -1; } if (param->videoParam->mfx.CodecId == MFX_CODEC_AVC) { if (info->capabilities & HB_QSV_CAP_MSDK_API_1_6) { param->videoParam->mfx.CodecLevel = FFMIN(MFX_LEVEL_AVC_52, level->value); } else { // Media SDK API < 1.6, MFX_LEVEL_AVC_52 unsupported param->videoParam->mfx.CodecLevel = FFMIN(MFX_LEVEL_AVC_51, level->value); } } else { param->videoParam->mfx.CodecLevel = level->value; } } return 0; } const char* const* hb_qsv_preset_get_names() { if (qsv_hardware_generation(hb_get_cpu_platform()) >= 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_QSV_H264: return hb_h264_profile_names_8bit; case HB_VCODEC_QSV_H265_8BIT: return hb_h265_profile_names_8bit; case HB_VCODEC_QSV_H265_10BIT: return hb_h265_qsv_profile_names_10bit; default: return NULL; } } const char* const* hb_qsv_level_get_names(int encoder) { switch (encoder) { case HB_VCODEC_QSV_H264: return hb_h264_level_names; case HB_VCODEC_QSV_H265_10BIT: case HB_VCODEC_QSV_H265: return hb_h265_level_names; default: return NULL; } } const char* hb_qsv_video_quality_get_name(uint32_t codec) { uint64_t caps = 0; switch (codec) { case HB_VCODEC_QSV_H264: if (hb_qsv_info_avc != NULL) caps = 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; 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; 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; 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; } } int hb_qsv_param_default_preset(hb_qsv_param_t *param, mfxVideoParam *videoParam, hb_qsv_info_t *info, const char *preset) { if (param != NULL && videoParam != NULL && info != NULL) { int ret = hb_qsv_param_default(param, videoParam, info); if (ret) { return ret; } } else { hb_error("hb_qsv_param_default_preset: invalid pointer(s)"); return -1; } if (preset != NULL && preset[0] != '\0') { if (!strcasecmp(preset, "quality")) { /* * HSW TargetUsage: 2 * NumRefFrame: 0 * GopRefDist: 4 (CQP), 3 (VBR) -> -1 (set by encoder) * GopPicSize: 32 (CQP), 1 second (VBR) -> -1 (set by encoder) * BPyramid: 1 (CQP), 0 (VBR) -> -1 (set by encoder) * LookAhead: 1 (on) * LookAheadDepth: 40 * * * SNB * IVB Preset Not Available * * Note: this preset is the libhb default (like x264's "medium"). */ } else if (!strcasecmp(preset, "balanced")) { /* * HSW TargetUsage: 4 * NumRefFrame: 1 * GopRefDist: 4 (CQP), 3 (VBR) -> -1 (set by encoder) * GopPicSize: 32 (CQP), 1 second (VBR) -> -1 (set by encoder) * BPyramid: 1 (CQP), 0 (VBR) -> -1 (set by encoder) * LookAhead: 0 (off) * LookAheadDepth: Not Applicable */ if (qsv_hardware_generation(hb_get_cpu_platform()) >= QSV_G3) { param->rc.lookahead = 0; param->videoParam->mfx.NumRefFrame = 1; param->videoParam->mfx.TargetUsage = MFX_TARGETUSAGE_4; } else { /* * SNB * IVB TargetUsage: 2 * NumRefFrame: 0 * GopRefDist: 4 (CQP), 3 (VBR) -> -1 (set by encoder) * GopPicSize: 32 (CQP), 1 second (VBR) -> -1 (set by encoder) * BPyramid: Not Applicable * LookAhead: Not Applicable * LookAheadDepth: Not Applicable * * Note: this preset is not the libhb default, * but the settings are the same so do nothing. */ } } else if (!strcasecmp(preset, "speed")) { if (qsv_hardware_generation(hb_get_cpu_platform()) >= 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) { /* * HSW TargetUsage: 6 * NumRefFrame: 0 (CQP), 1 (VBR) -> see note * GopRefDist: 4 (CQP), 3 (VBR) -> -1 (set by encoder) * GopPicSize: 32 (CQP), 1 second (VBR) -> -1 (set by encoder) * BPyramid: 1 (CQP), 0 (VBR) -> -1 (set by encoder) * LookAhead: 0 (off) * LookAheadDepth: Not Applicable * * Note: NumRefFrame depends on the RC method, which we don't * know here. Rather than have an additional variable and * having the encoder set it, we set it to 1 and let the * B-pyramid code sanitize it. Since BPyramid is 1 w/CQP, * the result (3) is the same as what MSDK would pick for * NumRefFrame 0 GopRefDist 4 GopPicSize 32. */ param->rc.lookahead = 0; param->videoParam->mfx.NumRefFrame = 1; param->videoParam->mfx.TargetUsage = MFX_TARGETUSAGE_6; } else { /* * SNB * IVB TargetUsage: 4 * NumRefFrame: 0 * GopRefDist: 4 (CQP), 3 (VBR) -> -1 (set by encoder) * GopPicSize: 32 (CQP), 1 second (VBR) -> -1 (set by encoder) * BPyramid: Not Applicable * LookAhead: Not Applicable * LookAheadDepth: Not Applicable */ param->videoParam->mfx.TargetUsage = MFX_TARGETUSAGE_4; } } else { hb_error("hb_qsv_param_default_preset: invalid preset '%s'", preset); return -1; } } return 0; } int hb_qsv_param_default_async_depth() { return qsv_hardware_generation(hb_get_cpu_platform()) >= QSV_G7 ? 6 : HB_QSV_ASYNC_DEPTH_DEFAULT; } int hb_qsv_param_default(hb_qsv_param_t *param, mfxVideoParam *videoParam, hb_qsv_info_t *info) { if (param != NULL && videoParam != NULL && info != NULL) { // introduced in API 1.0 memset(¶m->codingOption, 0, sizeof(mfxExtCodingOption)); param->codingOption.Header.BufferId = MFX_EXTBUFF_CODING_OPTION; param->codingOption.Header.BufferSz = sizeof(mfxExtCodingOption); param->codingOption.MECostType = 0; // reserved, must be 0 param->codingOption.MESearchType = 0; // reserved, must be 0 param->codingOption.MVSearchWindow.x = 0; // reserved, must be 0 param->codingOption.MVSearchWindow.y = 0; // reserved, must be 0 param->codingOption.RefPicListReordering = 0; // reserved, must be 0 param->codingOption.IntraPredBlockSize = 0; // reserved, must be 0 param->codingOption.InterPredBlockSize = 0; // reserved, must be 0 param->codingOption.MVPrecision = 0; // reserved, must be 0 param->codingOption.EndOfSequence = MFX_CODINGOPTION_UNKNOWN; param->codingOption.RateDistortionOpt = MFX_CODINGOPTION_UNKNOWN; param->codingOption.ResetRefList = MFX_CODINGOPTION_UNKNOWN; param->codingOption.MaxDecFrameBuffering = 0; // unspecified param->codingOption.AUDelimiter = MFX_CODINGOPTION_OFF; param->codingOption.SingleSeiNalUnit = MFX_CODINGOPTION_UNKNOWN; param->codingOption.PicTimingSEI = MFX_CODINGOPTION_OFF; param->codingOption.VuiNalHrdParameters = MFX_CODINGOPTION_UNKNOWN; param->codingOption.FramePicture = MFX_CODINGOPTION_UNKNOWN; param->codingOption.CAVLC = MFX_CODINGOPTION_OFF; // introduced in API 1.3 param->codingOption.RefPicMarkRep = MFX_CODINGOPTION_UNKNOWN; param->codingOption.FieldOutput = MFX_CODINGOPTION_UNKNOWN; param->codingOption.NalHrdConformance = MFX_CODINGOPTION_UNKNOWN; param->codingOption.SingleSeiNalUnit = MFX_CODINGOPTION_UNKNOWN; param->codingOption.VuiVclHrdParameters = MFX_CODINGOPTION_UNKNOWN; // introduced in API 1.4 param->codingOption.ViewOutput = MFX_CODINGOPTION_UNKNOWN; // introduced in API 1.6 param->codingOption.RecoveryPointSEI = MFX_CODINGOPTION_UNKNOWN; // introduced in API 1.3 memset(¶m->videoSignalInfo, 0, sizeof(mfxExtVideoSignalInfo)); param->videoSignalInfo.Header.BufferId = MFX_EXTBUFF_VIDEO_SIGNAL_INFO; param->videoSignalInfo.Header.BufferSz = sizeof(mfxExtVideoSignalInfo); param->videoSignalInfo.VideoFormat = 5; // undefined param->videoSignalInfo.VideoFullRange = 0; // TV range param->videoSignalInfo.ColourDescriptionPresent = 0; // don't write to bitstream param->videoSignalInfo.ColourPrimaries = 2; // undefined param->videoSignalInfo.TransferCharacteristics = 2; // undefined param->videoSignalInfo.MatrixCoefficients = 2; // undefined // 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_ON; 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_ON; 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; // GOP & rate control param->gop.b_pyramid = -1; // set automatically 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 = 1; // enabled by default (if supported) 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 // introduced in API 1.0 memset(videoParam, 0, sizeof(mfxVideoParam)); param->videoParam = videoParam; param->videoParam->Protected = 0; // reserved, must be 0 param->videoParam->NumExtParam = 0; param->videoParam->IOPattern = MFX_IOPATTERN_IN_SYSTEM_MEMORY; param->videoParam->mfx.TargetUsage = MFX_TARGETUSAGE_BALANCED; param->videoParam->mfx.GopOptFlag = MFX_GOP_CLOSED; param->videoParam->mfx.NumThread = 0; // deprecated, must be 0 param->videoParam->mfx.EncodedOrder = 0; // input is in display order param->videoParam->mfx.IdrInterval = 0; // all I-frames are IDR param->videoParam->mfx.NumSlice = 0; // use Media SDK default param->videoParam->mfx.NumRefFrame = 0; // use Media SDK default param->videoParam->mfx.GopPicSize = 0; // use Media SDK default param->videoParam->mfx.GopRefDist = 0; // use Media SDK default param->videoParam->mfx.LowPower = MFX_CODINGOPTION_OFF; // use Media SDK default // introduced in API 1.1 param->videoParam->AsyncDepth = hb_qsv_param_default_async_depth(); // introduced in API 1.3 param->videoParam->mfx.BRCParamMultiplier = 0; // no multiplier // FrameInfo: set by video encoder, except PicStruct param->videoParam->mfx.FrameInfo.PicStruct = MFX_PICSTRUCT_PROGRESSIVE; // attach supported mfxExtBuffer structures to the mfxVideoParam param->videoParam->NumExtParam = 0; param->videoParam->ExtParam = param->ExtParamArray; if (info->capabilities & HB_QSV_CAP_VUI_VSINFO) { param->videoParam->ExtParam[param->videoParam->NumExtParam++] = (mfxExtBuffer*)¶m->videoSignalInfo; } if (info->capabilities & HB_QSV_CAP_OPTION1) { param->videoParam->ExtParam[param->videoParam->NumExtParam++] = (mfxExtBuffer*)¶m->codingOption; } if (info->capabilities & HB_QSV_CAP_OPTION2) { param->videoParam->ExtParam[param->videoParam->NumExtParam++] = (mfxExtBuffer*)¶m->codingOption2; } if (info->capabilities & HB_QSV_CAP_LOWPOWER_ENCODE) { param->videoParam->mfx.LowPower = MFX_CODINGOPTION_ON; } } 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"; 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; default: break; } return profile != NULL ? profile->name : NULL; } const char* hb_qsv_level_name(uint32_t codec_id, uint16_t level_id) { hb_triplet_t *level = NULL; switch (codec_id) { case MFX_CODEC_AVC: level = hb_triplet4value(hb_qsv_h264_levels, level_id); break; case MFX_CODEC_HEVC: level = hb_triplet4value(hb_qsv_h265_levels, level_id); break; default: break; } return level != NULL ? level->name : NULL; } const char* hb_qsv_frametype_name(uint16_t qsv_frametype) { if (qsv_frametype & MFX_FRAMETYPE_IDR) { return qsv_frametype & MFX_FRAMETYPE_REF ? "IDR (ref)" : "IDR"; } else if (qsv_frametype & MFX_FRAMETYPE_I) { return qsv_frametype & MFX_FRAMETYPE_REF ? "I (ref)" : "I"; } else if (qsv_frametype & MFX_FRAMETYPE_P) { return qsv_frametype & MFX_FRAMETYPE_REF ? "P (ref)" : "P"; } else if (qsv_frametype & MFX_FRAMETYPE_B) { return qsv_frametype & MFX_FRAMETYPE_REF ? "B (ref)" : "B"; } else { return "unknown"; } } uint8_t hb_qsv_frametype_xlat(uint16_t qsv_frametype, uint16_t *out_flags) { uint16_t flags = 0; uint8_t frametype = 0; if (qsv_frametype & MFX_FRAMETYPE_IDR) { flags |= HB_FLAG_FRAMETYPE_KEY; frametype = HB_FRAME_IDR; } else if (qsv_frametype & MFX_FRAMETYPE_I) { frametype = HB_FRAME_I; } else if (qsv_frametype & MFX_FRAMETYPE_P) { frametype = HB_FRAME_P; } else if (qsv_frametype & MFX_FRAMETYPE_B) { frametype = HB_FRAME_B; } if (qsv_frametype & MFX_FRAMETYPE_REF) { flags |= HB_FLAG_FRAMETYPE_REF; } if (out_flags != NULL) { *out_flags = 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)) { 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; } } 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; } 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; #undef FORCE_WORKAROUNDS } #if defined(_WIN32) || defined(__MINGW32__) // Direct X #define COBJMACROS #include #include #include #include #if HAVE_DXGIDEBUG_H #include #endif static mfxHDL device_manager_handle = NULL; static mfxHandleType device_manager_handle_type; static ID3D11DeviceContext *device_context = NULL; typedef IDirect3D9* WINAPI pDirect3DCreate9(UINT); typedef HRESULT WINAPI pDirect3DCreate9Ex(UINT, IDirect3D9Ex **); typedef HRESULT(WINAPI *HB_PFN_CREATE_DXGI_FACTORY)(REFIID riid, void **ppFactory); static int hb_dxva2_device_create9(HMODULE d3dlib, UINT adapter, IDirect3D9 **d3d9_out) { pDirect3DCreate9 *createD3D = (pDirect3DCreate9 *)hb_dlsym(d3dlib, "Direct3DCreate9"); if (!createD3D) { hb_error("hb_dxva2_device_create9: failed to locate Direct3DCreate9"); return -1; } IDirect3D9 *d3d9 = createD3D(D3D_SDK_VERSION); if (!d3d9) { hb_error("hb_dxva2_device_create9: createD3D failed"); return -1; } *d3d9_out = d3d9; return 0; } static int hb_dxva2_device_create9ex(HMODULE d3dlib, UINT adapter, IDirect3D9 **d3d9_out) { IDirect3D9Ex *d3d9ex = NULL; HRESULT hr; pDirect3DCreate9Ex *createD3DEx = (pDirect3DCreate9Ex *)hb_dlsym(d3dlib, "Direct3DCreate9Ex"); if (!createD3DEx) { hb_error("hb_dxva2_device_create9ex: failed to locate Direct3DCreate9Ex"); return -1; } hr = createD3DEx(D3D_SDK_VERSION, &d3d9ex); if (FAILED(hr)) { hb_error("hb_dxva2_device_create9ex: createD3DEx failed %d", hr); return -1; } *d3d9_out = (IDirect3D9 *)d3d9ex; return 0; } static int hb_d3d11va_device_check() { HANDLE d3dlib, dxgilib; d3dlib = hb_dlopen("d3d11.dll"); dxgilib = hb_dlopen("dxgi.dll"); if (!d3dlib || !dxgilib) { hb_error("hb_d3d11va_device_check: failed to load d3d11.dll and dxgi.dll"); return -1; } PFN_D3D11_CREATE_DEVICE mD3D11CreateDevice; HB_PFN_CREATE_DXGI_FACTORY mCreateDXGIFactory; mD3D11CreateDevice = (PFN_D3D11_CREATE_DEVICE)hb_dlsym(d3dlib, "D3D11CreateDevice"); mCreateDXGIFactory = (HB_PFN_CREATE_DXGI_FACTORY)hb_dlsym(dxgilib, "CreateDXGIFactory1"); if (!mD3D11CreateDevice || !mCreateDXGIFactory) { hb_error("hb_d3d11va_device_check: failed to locate D3D11CreateDevice and CreateDXGIFactory1 functions"); return -1; } HRESULT hr; IDXGIAdapter *pAdapter = NULL; int adapter_id = 0; IDXGIFactory2 *pDXGIFactory; hr = mCreateDXGIFactory(&IID_IDXGIFactory2, (void **)&pDXGIFactory); while (IDXGIFactory2_EnumAdapters(pDXGIFactory, adapter_id++, &pAdapter) != DXGI_ERROR_NOT_FOUND) { ID3D11Device* g_pd3dDevice = NULL; DXGI_ADAPTER_DESC adapterDesc; hr = mD3D11CreateDevice(pAdapter, D3D_DRIVER_TYPE_UNKNOWN, NULL, D3D11_CREATE_DEVICE_VIDEO_SUPPORT, NULL, 0, D3D11_SDK_VERSION, &g_pd3dDevice, NULL, NULL); if (FAILED(hr)) { hb_error("hb_d3d11va_device_check: D3D11CreateDevice returned %d", hr); continue; } hr = IDXGIAdapter2_GetDesc(pAdapter, &adapterDesc); if (FAILED(hr)) { hb_error("hb_d3d11va_device_check: IDXGIAdapter2_GetDesc returned %d", hr); continue; } if (pAdapter) IDXGIAdapter_Release(pAdapter); if (adapterDesc.VendorId == 0x8086) { IDXGIFactory2_Release(pDXGIFactory); return adapter_id - 1; } } IDXGIFactory2_Release(pDXGIFactory); return -1; } static int hb_dxva2_device_check() { HRESULT hr; HMODULE d3dlib = NULL; IDirect3D9 *d3d9 = NULL; D3DADAPTER_IDENTIFIER9 identifier; D3DADAPTER_IDENTIFIER9 *d3dai = &identifier; UINT adapter = D3DADAPTER_DEFAULT; d3dlib = hb_dlopen("d3d9.dll"); if (!d3dlib) { hb_error("hb_dxva2_device_check: failed to load d3d9 library"); return -1; } if (hb_dxva2_device_create9ex(d3dlib, adapter, &d3d9) < 0) { // Retry with "classic" d3d9 hr = hb_dxva2_device_create9(d3dlib, adapter, &d3d9); if (hr < 0) { hr = -1; goto clean_up; } } hr = IDirect3D9_GetAdapterIdentifier(d3d9, D3DADAPTER_DEFAULT, 0, d3dai); if (FAILED(hr)) { hb_error("hb_dxva2_device_check: IDirect3D9_GetAdapterIdentifier failed"); hr = -1; goto clean_up; } unsigned intel_id = 0x8086; if(d3dai) { if(d3dai->VendorId != intel_id) { hb_error("hb_dxva2_device_check: adapter that was found does not support QSV. It is required for zero-copy QSV path"); hr = -1; goto clean_up; } } hr = 0; clean_up: if (d3d9) IDirect3D9_Release(d3d9); if (d3dlib) hb_dlclose(d3dlib); return hr; } static HRESULT lock_device( IDirect3DDeviceManager9 *pDeviceManager, BOOL fBlock, IDirect3DDevice9 **ppDevice, // Receives a pointer to the device. HANDLE *pHandle // Receives a device handle. ) { *pHandle = NULL; *ppDevice = NULL; HANDLE hDevice = 0; HRESULT hr = pDeviceManager->lpVtbl->OpenDeviceHandle(pDeviceManager, &hDevice); if (SUCCEEDED(hr)) { hr = pDeviceManager->lpVtbl->LockDevice(pDeviceManager, hDevice, ppDevice, fBlock); } if (hr == DXVA2_E_NEW_VIDEO_DEVICE) { // Invalid device handle. Try to open a new device handle. hr = pDeviceManager->lpVtbl->CloseDeviceHandle(pDeviceManager, hDevice); if (SUCCEEDED(hr)) { hr = pDeviceManager->lpVtbl->OpenDeviceHandle(pDeviceManager, &hDevice); } // Try to lock the device again. if (SUCCEEDED(hr)) { hr = pDeviceManager->lpVtbl->LockDevice(pDeviceManager, hDevice, ppDevice, TRUE); } } if (SUCCEEDED(hr)) { *pHandle = hDevice; } return hr; } static HRESULT unlock_device( IDirect3DDeviceManager9 *pDeviceManager, HANDLE handle // Receives a device handle. ) { HRESULT hr = pDeviceManager->lpVtbl->UnlockDevice(pDeviceManager, handle, 0); if (SUCCEEDED(hr)) { hr = pDeviceManager->lpVtbl->CloseDeviceHandle(pDeviceManager, handle); } return hr; } static int hb_qsv_find_surface_idx(const QSVMid *mids, const int nb_mids, const QSVMid *mid) { if (mids) { int i; for (i = 0; i < nb_mids; i++) { const QSVMid *m = &mids[i]; if ((m->handle_pair->first == mid->handle_pair->first) && (m->handle_pair->second == mid->handle_pair->second)) return i; } } return -1; } int hb_qsv_replace_surface_mid(HBQSVFramesContext* hb_enc_qsv_frames_ctx, const QSVMid *mid, mfxFrameSurface1 *surface) { if (!hb_enc_qsv_frames_ctx || !surface) return -1; int ret = hb_qsv_find_surface_idx(hb_enc_qsv_frames_ctx->mids, hb_enc_qsv_frames_ctx->nb_mids, mid); if (ret < 0) { hb_error("hb_qsv_replace_surface_mid: Surface with MemId=%p has not been found in the pool", mid); return -1; } else { surface->Data.MemId = &hb_enc_qsv_frames_ctx->mids[ret]; } return 0; } int hb_qsv_release_surface_from_pool_by_surface_pointer(HBQSVFramesContext* hb_enc_qsv_frames_ctx, const mfxFrameSurface1 *surface) { if (!hb_enc_qsv_frames_ctx || !surface) return -1; AVHWFramesContext *frames_ctx = (AVHWFramesContext*)hb_enc_qsv_frames_ctx->hw_frames_ctx->data; AVQSVFramesContext *frames_hwctx = frames_ctx->hwctx; for(int i = 0; i < hb_enc_qsv_frames_ctx->nb_mids; i++) { mfxFrameSurface1 *pool_surface = &frames_hwctx->surfaces[i]; if(surface == pool_surface) { ff_qsv_atomic_dec(&hb_enc_qsv_frames_ctx->pool[i]); return 0; } } return -1; } int hb_qsv_get_mid_by_surface_from_pool(HBQSVFramesContext* hb_enc_qsv_frames_ctx, mfxFrameSurface1 *surface, QSVMid **out_mid) { if (!hb_enc_qsv_frames_ctx || !surface) return -1; QSVMid *mid = NULL; AVHWFramesContext *frames_ctx = (AVHWFramesContext*)hb_enc_qsv_frames_ctx->hw_frames_ctx->data; AVQSVFramesContext *frames_hwctx = frames_ctx->hwctx; // find the first available surface in the pool int count = 0; while(1) { if(count > 30) { hb_error("hb_qsv_get_mid_by_surface_from_pool has not been found or busy", mid); hb_qsv_sleep(10); // prevent hang when all surfaces all used count = 0; } for(int i = 0; i < hb_enc_qsv_frames_ctx->nb_mids; i++) { mid = &hb_enc_qsv_frames_ctx->mids[i]; mfxFrameSurface1 *pool_surface = &frames_hwctx->surfaces[i]; if( (pool_surface->Data.Locked == 0) && (surface == pool_surface)) { *out_mid = mid; return 0; } } count++; } } int hb_qsv_get_free_surface_from_pool(HBQSVFramesContext* hb_enc_qsv_frames_ctx, AVFrame* frame, QSVMid** out_mid) { if (!hb_enc_qsv_frames_ctx || !frame) return -1; AVHWFramesContext *frames_ctx = (AVHWFramesContext*)hb_enc_qsv_frames_ctx->hw_frames_ctx->data; AVQSVFramesContext *frames_hwctx = frames_ctx->hwctx; // find the first available surface in the pool int count = 0; while(1) { if(count > 30) { hb_qsv_sleep(10); // prevent hang when all surfaces all used count = 0; } int ret = av_hwframe_get_buffer(hb_enc_qsv_frames_ctx->hw_frames_ctx, frame, 0); if (ret) { return -1; } else { mfxFrameSurface1 *output_surface = (mfxFrameSurface1 *)frame->data[3]; for(int i = 0; i < hb_enc_qsv_frames_ctx->nb_mids; i++) { QSVMid* mid = &hb_enc_qsv_frames_ctx->mids[i]; mfxFrameSurface1* cur_surface = &frames_hwctx->surfaces[i]; if(cur_surface == output_surface) { if((hb_enc_qsv_frames_ctx->pool[i] == 0) && (output_surface->Data.Locked == 0)) { *out_mid = mid; ff_qsv_atomic_inc(&hb_enc_qsv_frames_ctx->pool[i]); return 0; } else { // we need to do unref if surface is not taken to be used, otherwise -12. av_frame_unref(frame); break; } } } } count++; } } static int hb_qsv_allocate_dx11_encoder_pool(HBQSVFramesContext* frames_ctx, ID3D11Device *device, ID3D11Texture2D* input_texture) { D3D11_TEXTURE2D_DESC desc = { 0 }; ID3D11Texture2D_GetDesc(input_texture, &desc); desc.ArraySize = 1; desc.BindFlags = D3D11_BIND_RENDER_TARGET; for (size_t i = 0; i < frames_ctx->nb_mids; i++) { ID3D11Texture2D* texture; HRESULT hr = ID3D11Device_CreateTexture2D(device, &desc, NULL, &texture); if (hr != S_OK) { hb_error("hb_qsv_allocate_dx11_encoder_pool: ID3D11Device_CreateTexture2D error"); return -1; } QSVMid *mid = &frames_ctx->mids[i]; mid->handle_pair->first = texture; mid->handle_pair->second = 0; } return 0; } static int hb_qsv_get_dx_device(hb_job_t *job) { AVHWDeviceContext *device_ctx = (AVHWDeviceContext*)job->qsv.ctx->hb_hw_device_ctx->data; AVQSVDeviceContext *device_hwctx = device_ctx->hwctx; mfxSession parent_session = device_hwctx->session; if (device_manager_handle == NULL) { mfxIMPL device_impl; int err = MFXQueryIMPL(parent_session, &device_impl); if (err != MFX_ERR_NONE) { hb_error("hb_qsv_get_dx_device: no impl could be retrieved"); return -1; } if (MFX_IMPL_VIA_D3D11 == MFX_IMPL_VIA_MASK(device_impl)) { device_manager_handle_type = MFX_HANDLE_D3D11_DEVICE; } else if (MFX_IMPL_VIA_D3D9 == MFX_IMPL_VIA_MASK(device_impl)) { device_manager_handle_type = MFX_HANDLE_D3D9_DEVICE_MANAGER; } else { hb_error("hb_qsv_get_dx_device: unsupported impl"); return -1; } err = MFXVideoCORE_GetHandle(parent_session, device_manager_handle_type, &device_manager_handle); if (err != MFX_ERR_NONE) { hb_error("hb_qsv_get_dx_device: no supported hw handle could be retrieved " "from the session\n"); return -1; } if (device_manager_handle_type == MFX_HANDLE_D3D11_DEVICE) { ID3D11Device *device = (ID3D11Device *)device_manager_handle; ID3D11Texture2D* input_texture = job->qsv.ctx->hb_dec_qsv_frames_ctx->input_texture; err = hb_qsv_allocate_dx11_encoder_pool(job->qsv.ctx->hb_dec_qsv_frames_ctx, device, input_texture); if (err < 0) { hb_error("hb_qsv_get_dx_device: hb_qsv_allocate_dx11_encoder_pool failed"); return -1; } if (device_context == NULL) { ID3D11Device_GetImmediateContext(device, &device_context); if (!device_context) return -1; } } } return 0; } void hb_qsv_get_free_surface_from_pool_with_range(HBQSVFramesContext* hb_enc_qsv_frames_ctx, const int start_index, const int end_index, QSVMid** out_mid, mfxFrameSurface1** out_surface) { AVHWFramesContext *frames_ctx = (AVHWFramesContext*)hb_enc_qsv_frames_ctx->hw_frames_ctx->data; AVQSVFramesContext *frames_hwctx = frames_ctx->hwctx; // find the first available surface in the pool int count = 0; while(1) { if (count > 30) { hb_qsv_sleep(10); // prevent hang when all surfaces all used count = 0; } for (int i = start_index; i < end_index; i++) { if ((hb_enc_qsv_frames_ctx->pool[i] == 0) && (frames_hwctx->surfaces[i].Data.Locked == 0)) { *out_mid = &hb_enc_qsv_frames_ctx->mids[i]; *out_surface = &frames_hwctx->surfaces[i]; ff_qsv_atomic_inc(&hb_enc_qsv_frames_ctx->pool[i]); return; } } count++; } } hb_buffer_t* hb_qsv_copy_frame(hb_job_t *job, AVFrame *frame, int is_vpp) { hb_buffer_t *out; out = hb_frame_buffer_init(frame->format, frame->width, frame->height); hb_avframe_set_video_buffer_flags(out, frame, (AVRational){1,1}); // alloc new frame out->qsv_details.frame = av_frame_alloc(); if (!out->qsv_details.frame) { return out; } out->qsv_details.frame->format = frame->format; out->qsv_details.frame->width = frame->width; out->qsv_details.frame->height = frame->height; out->qsv_details.frame->channels = frame->channels; out->qsv_details.frame->channel_layout = frame->channel_layout; out->qsv_details.frame->nb_samples = frame->nb_samples; int ret = av_frame_copy_props(out->qsv_details.frame, frame); if (ret < 0) { hb_error("hb_qsv_copy_frame: av_frame_copy_props error %d", ret); } QSVMid *mid = NULL; mfxFrameSurface1* output_surface = NULL; HBQSVFramesContext* hb_qsv_frames_ctx = NULL; if (is_vpp) { hb_qsv_frames_ctx = job->qsv.ctx->hb_vpp_qsv_frames_ctx; } else { hb_qsv_frames_ctx = job->qsv.ctx->hb_dec_qsv_frames_ctx; } if (!is_vpp && hb_qsv_hw_filters_are_enabled(job)) { ret = hb_qsv_get_free_surface_from_pool(hb_qsv_frames_ctx, out->qsv_details.frame, &mid); if (ret < 0) return out; output_surface = (mfxFrameSurface1*)out->qsv_details.frame->data[3]; } else { hb_qsv_get_free_surface_from_pool_with_range(hb_qsv_frames_ctx, 0, HB_QSV_POOL_SURFACE_SIZE - HB_QSV_POOL_ENCODER_SIZE, &mid, &output_surface); } if (device_manager_handle_type == MFX_HANDLE_D3D9_DEVICE_MANAGER) { mfxFrameSurface1* input_surface = (mfxFrameSurface1*)frame->data[3]; mfxHDLPair* input_pair = (mfxHDLPair*)input_surface->Data.MemId; // copy all surface fields *output_surface = *input_surface; if (hb_qsv_hw_filters_are_enabled(job)) { output_surface->Data.MemId = mid->handle_pair; } else { // replace the mem id to mem id from the pool output_surface->Data.MemId = mid; } // copy input sufrace to sufrace from the pool IDirect3DDevice9 *pDevice = NULL; HANDLE handle; HRESULT result = lock_device((IDirect3DDeviceManager9 *)device_manager_handle, 1, &pDevice, &handle); if (FAILED(result)) { hb_error("hb_qsv_copy_frame: lock_device failed %d", result); return out; } result = IDirect3DDevice9_StretchRect(pDevice, input_pair->first, 0, mid->handle_pair->first, 0, D3DTEXF_LINEAR); if (FAILED(result)) { hb_error("hb_qsv_copy_frame: IDirect3DDevice9_StretchRect failed %d", result); return out; } result = unlock_device((IDirect3DDeviceManager9 *)device_manager_handle, handle); if (FAILED(result)) { hb_error("hb_qsv_copy_frame: unlock_device failed %d", result); return out; } } else if (device_manager_handle_type == MFX_HANDLE_D3D11_DEVICE) { mfxFrameSurface1* input_surface = (mfxFrameSurface1*)frame->data[3]; mfxHDLPair* input_pair = (mfxHDLPair*)input_surface->Data.MemId; // Need to pass 0 instead of MFX_INFINITE to DirectX as index of surface int input_index = (int)(intptr_t)input_pair->second == MFX_INFINITE ? 0 : (int)(intptr_t)input_pair->second; int output_index = (int)(intptr_t)mid->handle_pair->second == MFX_INFINITE ? 0 : (int)(intptr_t)mid->handle_pair->second; // copy all surface fields *output_surface = *input_surface; if (hb_qsv_hw_filters_are_enabled(job)) { // Make sure that we pass handle_pair to scale_qsv output_surface->Data.MemId = mid->handle_pair; } else { // Make sure that we pass QSVMid to QSV encoder output_surface->Data.MemId = mid; } // copy input sufrace to sufrace from the pool ID3D11DeviceContext_CopySubresourceRegion(device_context, mid->handle_pair->first, output_index, 0, 0, 0, input_pair->first, input_index, NULL); ID3D11DeviceContext_Flush(device_context); } else { hb_error("hb_qsv_copy_frame: incorrect mfx impl"); return out; } out->qsv_details.frame->data[3] = (uint8_t*)output_surface; out->qsv_details.qsv_frames_ctx = hb_qsv_frames_ctx; out->qsv_details.qsv_atom = 0; out->qsv_details.ctx = job->qsv.ctx; return out; } static int qsv_get_buffer(AVCodecContext *s, AVFrame *frame, int flags) { int ret = -1; if(s->hw_frames_ctx) { ret = av_hwframe_get_buffer(s->hw_frames_ctx, frame, 0); } return ret; } void hb_qsv_uninit_dec(AVCodecContext *s) { if(s && s->hw_frames_ctx) av_buffer_unref(&s->hw_frames_ctx); } void hb_qsv_uninit_enc(hb_job_t *job) { if(job->qsv.ctx->hb_dec_qsv_frames_ctx) { av_buffer_unref(&job->qsv.ctx->hb_dec_qsv_frames_ctx->hw_frames_ctx); job->qsv.ctx->hb_dec_qsv_frames_ctx->hw_frames_ctx = NULL; av_free(job->qsv.ctx->hb_dec_qsv_frames_ctx); job->qsv.ctx->hb_dec_qsv_frames_ctx = NULL; } if(job->qsv.ctx->hb_vpp_qsv_frames_ctx) { av_buffer_unref(&job->qsv.ctx->hb_vpp_qsv_frames_ctx->hw_frames_ctx); job->qsv.ctx->hb_vpp_qsv_frames_ctx->hw_frames_ctx = NULL; av_free(job->qsv.ctx->hb_vpp_qsv_frames_ctx); job->qsv.ctx->hb_vpp_qsv_frames_ctx = NULL; } if (device_context) { ID3D11DeviceContext_Release(device_context); device_context = NULL; } job->qsv.ctx->hb_hw_device_ctx = NULL; if (job->qsv.ctx->qsv_device) { av_free(job->qsv.ctx->qsv_device); job->qsv.ctx->qsv_device = NULL; } job->qsv.ctx->num_adapters_available = 0; if (job->qsv.ctx->adapters_info.Adapters) { av_free(job->qsv.ctx->adapters_info.Adapters); job->qsv.ctx->adapters_info.Adapters = NULL; job->qsv.ctx->adapters_info.NumActual = 0; job->qsv.ctx->adapters_info.NumAlloc = 0; } device_manager_handle = NULL; } static int qsv_device_init(hb_job_t *job) { int err; AVDictionary *dict = NULL; if (job->qsv.ctx->qsv_device) { err = av_dict_set(&dict, "child_device", job->qsv.ctx->qsv_device, 0); if (err < 0) return err; } else { av_dict_set(&dict, "vendor", "0x8086", 0); } av_dict_set(&dict, "child_device_type", "d3d11va", 0); err = av_hwdevice_ctx_create(&job->qsv.ctx->hb_hw_device_ctx, AV_HWDEVICE_TYPE_QSV, 0, dict, 0); if (err < 0) { hb_error("qsv_device_init: error creating a QSV device %d", err); goto err_out; } err_out: if (dict) av_dict_free(&dict); return err; } 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; AVQSVFramesContext *frames_hwctx; AVBufferRef *hw_frames_ctx = *out_hw_frames_ctx; int ret; if (!job->qsv.ctx->hb_hw_device_ctx) { // parse and use user-specified encoder options for decoder, if present 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_decoder(job, key, str)) { case HB_QSV_PARAM_OK: break; case HB_QSV_PARAM_BAD_NAME: hb_log("hb_create_ffmpeg_pool: hb_qsv_param_parse: bad key %s", key); break; case HB_QSV_PARAM_BAD_VALUE: hb_log("hb_create_ffmpeg_pool: hb_qsv_param_parse: bad value %s for key %s", str, key); break; case HB_QSV_PARAM_UNSUPPORTED: hb_log("hb_create_ffmpeg_pool: hb_qsv_param_parse: unsupported option %s", key); break; case HB_QSV_PARAM_ERROR: default: hb_log("hb_create_ffmpeg_pool: hb_qsv_param_parse: unknown error"); break; } free(str); } hb_dict_free(&options_list); } ret = qsv_device_init(job); if (ret < 0) return ret; } av_buffer_unref(&hw_frames_ctx); hw_frames_ctx = av_hwframe_ctx_alloc(job->qsv.ctx->hb_hw_device_ctx); if (!hw_frames_ctx) return AVERROR(ENOMEM); *out_hw_frames_ctx = hw_frames_ctx; frames_ctx = (AVHWFramesContext*)hw_frames_ctx->data; frames_hwctx = frames_ctx->hwctx; frames_ctx->width = FFALIGN(coded_width, 32); frames_ctx->height = FFALIGN(coded_height, 32); frames_ctx->format = AV_PIX_FMT_QSV; frames_ctx->sw_format = sw_pix_fmt; frames_ctx->initial_pool_size = pool_size + extra_hw_frames; frames_hwctx->frame_type = MFX_MEMTYPE_VIDEO_MEMORY_DECODER_TARGET; ret = av_hwframe_ctx_init(hw_frames_ctx); if (ret < 0) { hb_error("hb_create_ffmpeg_pool: av_hwframe_ctx_init failed %d", ret); return ret; } return 0; } int hb_qsv_hw_frames_init(AVCodecContext *s) { AVHWFramesContext *frames_ctx; AVQSVFramesContext *frames_hwctx; AVBufferRef *hw_frames_ctx; int ret; hb_job_t *job = s->opaque; if (!job) { hb_error("hb_qsv_hw_frames_init: job is NULL"); return -1; } HBQSVFramesContext *hb_dec_qsv_frames_ctx = job->qsv.ctx->hb_dec_qsv_frames_ctx; int coded_width = s->coded_width; int coded_height = s->coded_height; enum AVPixelFormat sw_pix_fmt = s->sw_pix_fmt; int extra_hw_frames = s->extra_hw_frames; AVBufferRef **out_hw_frames_ctx = &s->hw_frames_ctx; ret = hb_create_ffmpeg_pool(job, coded_width, coded_height, sw_pix_fmt, HB_QSV_POOL_FFMPEG_SURFACE_SIZE, extra_hw_frames, out_hw_frames_ctx); if (ret < 0) { hb_error("hb_qsv_hw_frames_init: hb_create_ffmpeg_pool decoder failed %d", ret); return ret; } hw_frames_ctx = *out_hw_frames_ctx; frames_ctx = (AVHWFramesContext*)hw_frames_ctx->data; frames_hwctx = frames_ctx->hwctx; mfxHDLPair* handle_pair = (mfxHDLPair*)frames_hwctx->surfaces[0].Data.MemId; hb_dec_qsv_frames_ctx->input_texture = ((size_t)handle_pair->second != MFX_INFINITE) ? handle_pair->first : NULL; ret = hb_create_ffmpeg_pool(job, coded_width, coded_height, sw_pix_fmt, HB_QSV_POOL_SURFACE_SIZE, extra_hw_frames, &hb_dec_qsv_frames_ctx->hw_frames_ctx); if (ret < 0) { hb_error("hb_qsv_hw_frames_init: hb_create_ffmpeg_pool qsv surface allocation failed %d", ret); return ret; } /* allocate the memory ids for the external frames */ av_buffer_unref(&hb_dec_qsv_frames_ctx->mids_buf); hb_dec_qsv_frames_ctx->mids_buf = hb_qsv_create_mids(hb_dec_qsv_frames_ctx->hw_frames_ctx); if (!hb_dec_qsv_frames_ctx->mids_buf) return AVERROR(ENOMEM); hb_dec_qsv_frames_ctx->mids = (QSVMid*)hb_dec_qsv_frames_ctx->mids_buf->data; hb_dec_qsv_frames_ctx->nb_mids = frames_hwctx->nb_surfaces; memset(hb_dec_qsv_frames_ctx->pool, 0, hb_dec_qsv_frames_ctx->nb_mids * sizeof(hb_dec_qsv_frames_ctx->pool[0])); ret = hb_qsv_get_dx_device(job); if (ret < 0) { hb_error("qsv_init: hb_qsv_get_dx_device failed %d", ret); return ret; } return 0; } int hb_qsv_get_buffer(AVCodecContext *s, AVFrame *frame, int flags) { if (frame->format == AV_PIX_FMT_QSV) return qsv_get_buffer(s, frame, flags); return avcodec_default_get_buffer2(s, frame, flags); } enum AVPixelFormat hb_qsv_get_format(AVCodecContext *s, const enum AVPixelFormat *pix_fmts) { while (*pix_fmts != AV_PIX_FMT_NONE) { if (*pix_fmts == AV_PIX_FMT_QSV) { int ret = hb_qsv_hw_frames_init(s); if (ret < 0) { hb_error("hb_qsv_get_format: QSV hwaccel initialization failed"); return AV_PIX_FMT_NONE; } if (s->hw_frames_ctx) { s->hw_frames_ctx = av_buffer_ref(s->hw_frames_ctx); if (!s->hw_frames_ctx) return AV_PIX_FMT_NONE; } return AV_PIX_FMT_QSV; } pix_fmts++; } hb_error("hb_qsv_get_format: the QSV pixel format not offered in get_format()"); return AV_PIX_FMT_NONE; } int hb_qsv_preset_is_zero_copy_enabled(const hb_dict_t *job_dict) { hb_dict_t *video_dict, *qsv, *encoder; int qsv_encoder_enabled = 0; int qsv_decoder_enabled = 0; video_dict = hb_dict_get(job_dict, "Video"); if(video_dict) { encoder = hb_dict_get(video_dict, "Encoder"); if(encoder) { if (hb_value_type(encoder) == HB_VALUE_TYPE_STRING) { if(!strcasecmp(hb_value_get_string(encoder), "qsv_h264") || !strcasecmp(hb_value_get_string(encoder), "qsv_h265")) { qsv_encoder_enabled = 1; } } } qsv = hb_dict_get(video_dict, "QSV"); if (qsv != NULL) { hb_dict_t *decode; decode = hb_dict_get(qsv, "Decode"); if(decode) { if (hb_value_type(decode) == HB_VALUE_TYPE_BOOL) { qsv_decoder_enabled = hb_value_get_bool(decode); } } } } return (qsv_encoder_enabled && qsv_decoder_enabled); } int hb_qsv_sanitize_filter_list(hb_job_t *job) { /* * When QSV's VPP is used for filtering, not all CPU filters * are supported, so we need to do a little extra setup here. */ if (job->vcodec & HB_VCODEC_QSV_MASK) { int i = 0; int num_cpu_filters = 0; if (job->list_filter != NULL && hb_list_count(job->list_filter) > 0) { for (i = 0; i < hb_list_count(job->list_filter); i++) { hb_filter_object_t *filter = hb_list_item(job->list_filter, i); switch (filter->id) { // cropping and scaling always done via VPP filter case HB_FILTER_CROP_SCALE: break; case HB_FILTER_DEINTERLACE: case HB_FILTER_ROTATE: case HB_FILTER_RENDER_SUB: case HB_FILTER_AVFILTER: num_cpu_filters++; break; default: num_cpu_filters++; break; } } } job->qsv.ctx->num_cpu_filters = num_cpu_filters; job->qsv.ctx->qsv_filters_are_enabled = ((hb_list_count(job->list_filter) == 1) && hb_qsv_full_path_is_enabled(job)) ? 1 : 0; if (job->qsv.ctx->qsv_filters_are_enabled) { job->qsv.ctx->hb_vpp_qsv_frames_ctx = av_mallocz(sizeof(HBQSVFramesContext)); if (!job->qsv.ctx->hb_vpp_qsv_frames_ctx) { hb_error( "sanitize_qsv: HBQSVFramesContext vpp alloc failed" ); return 1; } } } return 0; } int hb_qsv_query_adapters(hb_job_t *job) { if (!job->qsv.ctx->adapters_info.Adapters) { // Get number of Intel graphics adapters mfxStatus sts = MFXQueryAdaptersNumber(&job->qsv.ctx->num_adapters_available); if (sts != MFX_ERR_NONE) { hb_error("hb_qsv_query_adapters: failed to get number of Intel graphics adapters"); return -1; } if (job->qsv.ctx->num_adapters_available > 0) { job->qsv.ctx->adapters_info.Adapters = av_mallocz_array(job->qsv.ctx->num_adapters_available, sizeof(*job->qsv.ctx->adapters_info.Adapters)); if (job->qsv.ctx->adapters_info.Adapters) { job->qsv.ctx->adapters_info.NumActual = 0; job->qsv.ctx->adapters_info.NumAlloc = job->qsv.ctx->num_adapters_available; // Collect information about Intel graphics adapters sts = MFXQueryAdapters(NULL, &job->qsv.ctx->adapters_info); if (sts != MFX_ERR_NONE) { hb_error("hb_qsv_query_adapters: failed to collect information about Intel graphics adapters"); return -1; } } } } return 0; } #else // other OS 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) { return -1; } int hb_qsv_hw_frames_init(AVCodecContext *s) { return -1; } hb_buffer_t* hb_qsv_copy_frame(hb_job_t *job, AVFrame *frame, int is_vpp) { return NULL; } int hb_qsv_get_free_surface_from_pool(HBQSVFramesContext* hb_enc_qsv_frames_ctx, AVFrame* frame, QSVMid** out_mid) { return -1; } void hb_qsv_get_free_surface_from_pool_with_range(HBQSVFramesContext* hb_enc_qsv_frames_ctx, const int start_index, const int end_index, QSVMid** out_mid, mfxFrameSurface1** out_surface) { return; } int hb_qsv_replace_surface_mid(HBQSVFramesContext* hb_qsv_frames_ctx, const QSVMid *mid, mfxFrameSurface1 *surface) { return -1; } enum AVPixelFormat hb_qsv_get_format(AVCodecContext *s, const enum AVPixelFormat *pix_fmts) { return AV_PIX_FMT_NONE; } int hb_qsv_get_buffer(AVCodecContext *s, AVFrame *frame, int flags) { return -1; } void hb_qsv_uninit_dec(AVCodecContext *s) { } void hb_qsv_uninit_enc(hb_job_t *job) { } int hb_qsv_preset_is_zero_copy_enabled(const hb_dict_t *job_dict) { return 0; } static int hb_dxva2_device_check() { return -1; } static int hb_d3d11va_device_check() { return -1; } int hb_qsv_get_mid_by_surface_from_pool(HBQSVFramesContext* hb_enc_qsv_frames_ctx, mfxFrameSurface1 *surface, QSVMid **out_mid) { } int hb_qsv_release_surface_from_pool_by_surface_pointer(HBQSVFramesContext* hb_enc_qsv_frames_ctx, const mfxFrameSurface1 *surface) { return -1; } int hb_qsv_query_adapters(hb_job_t *job) { return -1; } #endif hb_qsv_context* hb_qsv_context_init() { hb_qsv_context *ctx; ctx = av_mallocz(sizeof(hb_qsv_context)); if (!ctx) { hb_error( "hb_qsv_context_init: qsv ctx alloc failed" ); return NULL; } hb_qsv_add_context_usage(ctx, 0); return ctx; } void hb_qsv_context_uninit(hb_job_t *job) { hb_qsv_context *ctx = job->qsv.ctx; if ( ctx == NULL ) { hb_error( "hb_qsv_context_uninit: ctx is NULL" ); return; } /* QSV context cleanup and MFXClose */ hb_qsv_context_clean(ctx, hb_qsv_full_path_is_enabled(job)); av_free(ctx); job->qsv.ctx = NULL; } #else int hb_qsv_available() { return 0; } #endif // HB_PROJECT_FEATURE_QSV