diff --git a/plugins/obs-qsv11/QSV_Encoder.cpp b/plugins/obs-qsv11/QSV_Encoder.cpp index 95f84e21c..bbaf70954 100644 --- a/plugins/obs-qsv11/QSV_Encoder.cpp +++ b/plugins/obs-qsv11/QSV_Encoder.cpp @@ -90,7 +90,14 @@ qsv_t *qsv_encoder_open(qsv_param_t *pParams, enum qsv_codec codec) size_t adapter_idx = ovi.adapter; // Select current adapter - will be iGPU if exists due to adapter reordering - if (!adapters[adapter_idx].is_intel) { + if (codec == QSV_CODEC_AV1 && !adapters[adapter_idx].supports_av1) { + for (size_t i = 0; i < 4; i++) { + if (adapters[i].supports_av1) { + adapter_idx = i; + break; + } + } + } else if (!adapters[adapter_idx].is_intel) { for (size_t i = 0; i < 4; i++) { if (adapters[i].is_intel) { adapter_idx = i; diff --git a/plugins/obs-qsv11/QSV_Encoder.h b/plugins/obs-qsv11/QSV_Encoder.h index 319eea502..97394db82 100644 --- a/plugins/obs-qsv11/QSV_Encoder.h +++ b/plugins/obs-qsv11/QSV_Encoder.h @@ -58,6 +58,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include "mfxstructures.h" +#include "mfxadapter.h" #include #ifdef __cplusplus @@ -73,7 +74,12 @@ static const struct qsv_rate_control_info qsv_ratecontrols[] = { {"CBR", false}, {"VBR", false}, {"VCM", true}, {"CQP", false}, {"AVBR", false}, {"ICQ", true}, {"LA_ICQ", true}, {"LA_CBR", true}, {"LA_VBR", true}, {0, false}}; + +static const struct qsv_rate_control_info qsv_av1_ratecontrols[] = + {{"CBR", false}, {"VBR", false}, {"CQP", false}, {0, false}}; + static const char *const qsv_profile_names[] = {"high", "main", "baseline", 0}; +static const char *const qsv_profile_names_av1[] = {"main", 0}; static const char *const qsv_usage_names[] = {"quality", "balanced", "speed", "veryslow", "slower", "slow", "medium", "fast", "faster", @@ -85,10 +91,12 @@ typedef struct qsv_t qsv_t; struct adapter_info { bool is_intel; bool is_dgpu; + bool supports_av1; }; enum qsv_codec { QSV_CODEC_AVC, + QSV_CODEC_AV1, }; #define MAX_ADAPTERS 10 @@ -116,8 +124,24 @@ typedef struct { mfxU16 nKeyIntSec; mfxU16 nbFrames; mfxU16 nICQQuality; + mfxU16 VideoFormat; + mfxU16 VideoFullRange; + mfxU16 ColourPrimaries; + mfxU16 TransferCharacteristics; + mfxU16 MatrixCoefficients; + mfxU16 ChromaSampleLocTypeTopField; + mfxU16 ChromaSampleLocTypeBottomField; + mfxU16 DisplayPrimariesX[3]; + mfxU16 DisplayPrimariesY[3]; + mfxU16 WhitePointX; + mfxU16 WhitePointY; + mfxU32 MaxDisplayMasteringLuminance; + mfxU32 MinDisplayMasteringLuminance; + mfxU16 MaxContentLightLevel; + mfxU16 MaxPicAverageLightLevel; bool bMBBRC; bool bCQM; + bool video_fmt_10bit; } qsv_param_t; enum qsv_cpu_platform { @@ -154,6 +178,7 @@ int qsv_encoder_headers(qsv_t *, uint8_t **pSPS, uint8_t **pPPS, uint16_t *pnSPS, uint16_t *pnPPS); enum qsv_cpu_platform qsv_get_cpu_platform(); bool prefer_igpu_enc(int *iGPUIndex); +bool qsv_query_encode_device(struct adapter_info *adapter_info); #ifdef __cplusplus } diff --git a/plugins/obs-qsv11/QSV_Encoder_Internal.cpp b/plugins/obs-qsv11/QSV_Encoder_Internal.cpp index b436d9fba..c65990fe3 100644 --- a/plugins/obs-qsv11/QSV_Encoder_Internal.cpp +++ b/plugins/obs-qsv11/QSV_Encoder_Internal.cpp @@ -167,7 +167,8 @@ mfxStatus QSV_Encoder_Internal::Open(qsv_param_t *pParams, enum qsv_codec codec) m_pmfxENC = new MFXVideoENCODE(m_session); - InitParams(pParams, codec); + sts = InitParams(pParams, codec); + MSDK_CHECK_RESULT(sts, MFX_ERR_NONE, sts); sts = m_pmfxENC->Query(&m_mfxEncParams, &m_mfxEncParams); MSDK_IGNORE_MFX_STS(sts, MFX_WRN_INCOMPATIBLE_VIDEO_PARAM); @@ -191,13 +192,15 @@ mfxStatus QSV_Encoder_Internal::Open(qsv_param_t *pParams, enum qsv_codec codec) return sts; } -bool QSV_Encoder_Internal::InitParams(qsv_param_t *pParams, - enum qsv_codec codec) +mfxStatus QSV_Encoder_Internal::InitParams(qsv_param_t *pParams, + enum qsv_codec codec) { memset(&m_mfxEncParams, 0, sizeof(m_mfxEncParams)); if (codec == QSV_CODEC_AVC) m_mfxEncParams.mfx.CodecId = MFX_CODEC_AVC; + else if (codec == QSV_CODEC_AV1) + m_mfxEncParams.mfx.CodecId = MFX_CODEC_AV1; m_mfxEncParams.mfx.GopOptFlag = MFX_GOP_STRICT; m_mfxEncParams.mfx.NumSlice = 1; @@ -205,14 +208,24 @@ bool QSV_Encoder_Internal::InitParams(qsv_param_t *pParams, m_mfxEncParams.mfx.CodecProfile = pParams->nCodecProfile; m_mfxEncParams.mfx.FrameInfo.FrameRateExtN = pParams->nFpsNum; m_mfxEncParams.mfx.FrameInfo.FrameRateExtD = pParams->nFpsDen; - m_mfxEncParams.mfx.FrameInfo.FourCC = MFX_FOURCC_NV12; + if (pParams->video_fmt_10bit) { + m_mfxEncParams.mfx.FrameInfo.FourCC = MFX_FOURCC_P010; + m_mfxEncParams.mfx.FrameInfo.BitDepthChroma = 10; + m_mfxEncParams.mfx.FrameInfo.BitDepthLuma = 10; + m_mfxEncParams.mfx.FrameInfo.Shift = 1; + } else { + m_mfxEncParams.mfx.FrameInfo.FourCC = MFX_FOURCC_NV12; + } m_mfxEncParams.mfx.FrameInfo.ChromaFormat = MFX_CHROMAFORMAT_YUV420; m_mfxEncParams.mfx.FrameInfo.PicStruct = MFX_PICSTRUCT_PROGRESSIVE; m_mfxEncParams.mfx.FrameInfo.CropX = 0; m_mfxEncParams.mfx.FrameInfo.CropY = 0; m_mfxEncParams.mfx.FrameInfo.CropW = pParams->nWidth; m_mfxEncParams.mfx.FrameInfo.CropH = pParams->nHeight; - m_mfxEncParams.mfx.GopRefDist = pParams->nbFrames + 1; + if (codec == QSV_CODEC_AV1) + m_mfxEncParams.mfx.GopRefDist = 1; + else + m_mfxEncParams.mfx.GopRefDist = pParams->nbFrames + 1; enum qsv_cpu_platform qsv_platform = qsv_get_cpu_platform(); if ((qsv_platform >= QSV_CPU_PLATFORM_ICL || @@ -231,11 +244,17 @@ bool QSV_Encoder_Internal::InitParams(qsv_param_t *pParams, switch (pParams->nRateControl) { case MFX_RATECONTROL_CBR: m_mfxEncParams.mfx.TargetKbps = pParams->nTargetBitRate; + m_mfxEncParams.mfx.BufferSizeInKB = pParams->nTargetBitRate * 2; + m_mfxEncParams.mfx.InitialDelayInKB = + pParams->nTargetBitRate * 1; break; case MFX_RATECONTROL_VBR: case MFX_RATECONTROL_VCM: m_mfxEncParams.mfx.TargetKbps = pParams->nTargetBitRate; m_mfxEncParams.mfx.MaxKbps = pParams->nMaxBitRate; + m_mfxEncParams.mfx.BufferSizeInKB = pParams->nTargetBitRate * 2; + m_mfxEncParams.mfx.InitialDelayInKB = + pParams->nTargetBitRate * 1; break; case MFX_RATECONTROL_CQP: m_mfxEncParams.mfx.QPI = pParams->nQPI; @@ -269,13 +288,15 @@ bool QSV_Encoder_Internal::InitParams(qsv_param_t *pParams, (mfxU16)(pParams->nKeyIntSec * pParams->nFpsNum / (float)pParams->nFpsDen); - static mfxExtBuffer *extendedBuffers[3]; + static mfxExtBuffer *extendedBuffers[7]; int iBuffers = 0; if (m_ver.Major == 1 && m_ver.Minor >= 8) { memset(&m_co2, 0, sizeof(mfxExtCodingOption2)); m_co2.Header.BufferId = MFX_EXTBUFF_CODING_OPTION2; m_co2.Header.BufferSz = sizeof(m_co2); + if (codec != QSV_CODEC_AVC) + m_co2.RepeatPPS = MFX_CODINGOPTION_ON; if (pParams->nRateControl == MFX_RATECONTROL_LA_ICQ || pParams->nRateControl == MFX_RATECONTROL_LA) m_co2.LookAheadDepth = pParams->nLADEPTH; @@ -284,7 +305,8 @@ bool QSV_Encoder_Internal::InitParams(qsv_param_t *pParams, if (pParams->nbFrames > 1) m_co2.BRefType = MFX_B_REF_PYRAMID; if (m_mfxEncParams.mfx.LowPower == MFX_CODINGOPTION_ON) { - m_co2.RepeatPPS = MFX_CODINGOPTION_OFF; + if (codec == QSV_CODEC_AVC) + m_co2.RepeatPPS = MFX_CODINGOPTION_OFF; if (pParams->nRateControl == MFX_RATECONTROL_CBR || pParams->nRateControl == MFX_RATECONTROL_VBR) { m_co2.LookAheadDepth = pParams->nLADEPTH; @@ -293,20 +315,13 @@ bool QSV_Encoder_Internal::InitParams(qsv_param_t *pParams, extendedBuffers[iBuffers++] = (mfxExtBuffer *)&m_co2; } - if (m_mfxEncParams.mfx.LowPower == MFX_CODINGOPTION_ON) { + if ((m_mfxEncParams.mfx.LowPower == MFX_CODINGOPTION_ON) || + (pParams->bCQM && m_ver.Major == 1 && m_ver.Minor >= 16)) { memset(&m_co3, 0, sizeof(mfxExtCodingOption3)); m_co3.Header.BufferId = MFX_EXTBUFF_CODING_OPTION3; m_co3.Header.BufferSz = sizeof(m_co3); m_co3.ScenarioInfo = MFX_SCENARIO_GAME_STREAMING; extendedBuffers[iBuffers++] = (mfxExtBuffer *)&m_co3; - } else if (pParams->bCQM) { - if (m_ver.Major == 1 && m_ver.Minor >= 16) { - memset(&m_co3, 0, sizeof(mfxExtCodingOption3)); - m_co3.Header.BufferId = MFX_EXTBUFF_CODING_OPTION3; - m_co3.Header.BufferSz = sizeof(m_co3); - m_co3.ScenarioInfo = 7; // MFX_SCENARIO_GAME_STREAMING - extendedBuffers[iBuffers++] = (mfxExtBuffer *)&m_co3; - } } if (iBuffers > 0) { @@ -314,6 +329,79 @@ bool QSV_Encoder_Internal::InitParams(qsv_param_t *pParams, m_mfxEncParams.NumExtParam = (mfxU16)iBuffers; } + memset(&m_ExtVideoSignalInfo, 0, sizeof(m_ExtVideoSignalInfo)); + m_ExtVideoSignalInfo.Header.BufferId = MFX_EXTBUFF_VIDEO_SIGNAL_INFO; + m_ExtVideoSignalInfo.Header.BufferSz = sizeof(m_ExtVideoSignalInfo); + m_ExtVideoSignalInfo.VideoFormat = pParams->VideoFormat; + m_ExtVideoSignalInfo.VideoFullRange = pParams->VideoFullRange; + m_ExtVideoSignalInfo.ColourDescriptionPresent = 1; + m_ExtVideoSignalInfo.ColourPrimaries = pParams->ColourPrimaries; + m_ExtVideoSignalInfo.TransferCharacteristics = + pParams->TransferCharacteristics; + m_ExtVideoSignalInfo.MatrixCoefficients = pParams->MatrixCoefficients; + extendedBuffers[iBuffers++] = (mfxExtBuffer *)&m_ExtVideoSignalInfo; + +/* TODO: Ask Intel why this is MFX_ERR_UNSUPPORTED */ +#if 0 + memset(&m_ExtChromaLocInfo, 0, sizeof(m_ExtChromaLocInfo)); + m_ExtChromaLocInfo.Header.BufferId = MFX_EXTBUFF_CHROMA_LOC_INFO; + m_ExtChromaLocInfo.Header.BufferSz = sizeof(m_ExtChromaLocInfo); + m_ExtChromaLocInfo.ChromaLocInfoPresentFlag = 1; + m_ExtChromaLocInfo.ChromaSampleLocTypeTopField = + pParams->ChromaSampleLocTypeTopField; + m_ExtChromaLocInfo.ChromaSampleLocTypeBottomField = + pParams->ChromaSampleLocTypeBottomField; + extendedBuffers[iBuffers++] = (mfxExtBuffer *)&m_ExtChromaLocInfo; +#endif + + if (pParams->MaxContentLightLevel > 0) { + memset(&m_ExtMasteringDisplayColourVolume, 0, + sizeof(m_ExtMasteringDisplayColourVolume)); + m_ExtMasteringDisplayColourVolume.Header.BufferId = + MFX_EXTBUFF_MASTERING_DISPLAY_COLOUR_VOLUME; + m_ExtMasteringDisplayColourVolume.Header.BufferSz = + sizeof(m_ExtMasteringDisplayColourVolume); + m_ExtMasteringDisplayColourVolume.InsertPayloadToggle = + MFX_PAYLOAD_IDR; + m_ExtMasteringDisplayColourVolume.DisplayPrimariesX[0] = + pParams->DisplayPrimariesX[0]; + m_ExtMasteringDisplayColourVolume.DisplayPrimariesX[1] = + pParams->DisplayPrimariesX[1]; + m_ExtMasteringDisplayColourVolume.DisplayPrimariesX[2] = + pParams->DisplayPrimariesX[2]; + m_ExtMasteringDisplayColourVolume.DisplayPrimariesY[0] = + pParams->DisplayPrimariesY[0]; + m_ExtMasteringDisplayColourVolume.DisplayPrimariesY[1] = + pParams->DisplayPrimariesY[1]; + m_ExtMasteringDisplayColourVolume.DisplayPrimariesY[2] = + pParams->DisplayPrimariesY[2]; + m_ExtMasteringDisplayColourVolume.WhitePointX = + pParams->WhitePointX; + m_ExtMasteringDisplayColourVolume.WhitePointY = + pParams->WhitePointY; + m_ExtMasteringDisplayColourVolume.MaxDisplayMasteringLuminance = + pParams->MaxDisplayMasteringLuminance; + m_ExtMasteringDisplayColourVolume.MinDisplayMasteringLuminance = + pParams->MinDisplayMasteringLuminance; + extendedBuffers[iBuffers++] = + (mfxExtBuffer *)&m_ExtMasteringDisplayColourVolume; + + memset(&m_ExtContentLightLevelInfo, 0, + sizeof(m_ExtContentLightLevelInfo)); + m_ExtContentLightLevelInfo.Header.BufferId = + MFX_EXTBUFF_CONTENT_LIGHT_LEVEL_INFO; + m_ExtContentLightLevelInfo.Header.BufferSz = + sizeof(m_ExtContentLightLevelInfo); + m_ExtContentLightLevelInfo.InsertPayloadToggle = + MFX_PAYLOAD_IDR; + m_ExtContentLightLevelInfo.MaxContentLightLevel = + pParams->MaxContentLightLevel; + m_ExtContentLightLevelInfo.MaxPicAverageLightLevel = + pParams->MaxPicAverageLightLevel; + extendedBuffers[iBuffers++] = + (mfxExtBuffer *)&m_ExtContentLightLevelInfo; + } + // Width must be a multiple of 16 // Height must be a multiple of 16 in case of frame picture and a // multiple of 32 in case of field picture @@ -333,7 +421,7 @@ bool QSV_Encoder_Internal::InitParams(qsv_param_t *pParams, } } - return true; + return sts; } bool QSV_Encoder_Internal::UpdateParams(qsv_param_t *pParams) @@ -481,6 +569,41 @@ mfxStatus QSV_Encoder_Internal::InitBitstream() return MFX_ERR_NONE; } +mfxStatus QSV_Encoder_Internal::LoadP010(mfxFrameSurface1 *pSurface, + uint8_t *pDataY, uint8_t *pDataUV, + uint32_t strideY, uint32_t strideUV) +{ + mfxU16 w, h, i, pitch; + mfxU8 *ptr; + mfxFrameInfo *pInfo = &pSurface->Info; + mfxFrameData *pData = &pSurface->Data; + + if (pInfo->CropH > 0 && pInfo->CropW > 0) { + w = pInfo->CropW; + h = pInfo->CropH; + } else { + w = pInfo->Width; + h = pInfo->Height; + } + + pitch = pData->Pitch; + ptr = pData->Y + pInfo->CropX + pInfo->CropY * pData->Pitch; + const size_t line_size = w * 2; + + // load Y plane + for (i = 0; i < h; i++) + memcpy(ptr + i * pitch, pDataY + i * strideY, line_size); + + // load UV plane + h /= 2; + ptr = pData->UV + pInfo->CropX + (pInfo->CropY / 2) * pitch; + + for (i = 0; i < h; i++) + memcpy(ptr + i * pitch, pDataUV + i * strideUV, line_size); + + return MFX_ERR_NONE; +} + mfxStatus QSV_Encoder_Internal::LoadNV12(mfxFrameSurface1 *pSurface, uint8_t *pDataY, uint8_t *pDataUV, uint32_t strideY, uint32_t strideUV) @@ -587,7 +710,10 @@ mfxStatus QSV_Encoder_Internal::Encode(uint64_t ts, uint8_t *pDataY, MSDK_CHECK_RESULT(sts, MFX_ERR_NONE, sts); } - sts = LoadNV12(pSurface, pDataY, pDataUV, strideY, strideUV); + sts = (pSurface->Info.FourCC == MFX_FOURCC_P010) + ? LoadP010(pSurface, pDataY, pDataUV, strideY, strideUV) + : LoadNV12(pSurface, pDataY, pDataUV, strideY, strideUV); + MSDK_CHECK_RESULT(sts, MFX_ERR_NONE, sts); pSurface->Data.TimeStamp = ts; diff --git a/plugins/obs-qsv11/QSV_Encoder_Internal.h b/plugins/obs-qsv11/QSV_Encoder_Internal.h index 00c5ce365..9f98493d5 100644 --- a/plugins/obs-qsv11/QSV_Encoder_Internal.h +++ b/plugins/obs-qsv11/QSV_Encoder_Internal.h @@ -79,13 +79,16 @@ public: bool UpdateParams(qsv_param_t *pParams); protected: - bool InitParams(qsv_param_t *pParams, enum qsv_codec codec); + mfxStatus InitParams(qsv_param_t *pParams, enum qsv_codec codec); mfxStatus AllocateSurfaces(); mfxStatus GetVideoParam(); mfxStatus InitBitstream(); mfxStatus LoadNV12(mfxFrameSurface1 *pSurface, uint8_t *pDataY, uint8_t *pDataUV, uint32_t strideY, uint32_t strideUV); + mfxStatus LoadP010(mfxFrameSurface1 *pSurface, uint8_t *pDataY, + uint8_t *pDataUV, uint32_t strideY, + uint32_t strideUV); mfxStatus Drain(); int GetFreeTaskIndex(Task *pTaskPool, mfxU16 nPoolSize); @@ -107,6 +110,10 @@ private: mfxExtCodingOption3 m_co3; mfxExtCodingOption2 m_co2; mfxExtCodingOption m_co; + mfxExtVideoSignalInfo m_ExtVideoSignalInfo{}; + mfxExtChromaLocInfo m_ExtChromaLocInfo{}; + mfxExtMasteringDisplayColourVolume m_ExtMasteringDisplayColourVolume{}; + mfxExtContentLightLevelInfo m_ExtContentLightLevelInfo{}; mfxU16 m_nTaskPool; Task *m_pTaskPool; int m_nTaskIdx; diff --git a/plugins/obs-qsv11/common_directx11.cpp b/plugins/obs-qsv11/common_directx11.cpp index 89df82622..046583e75 100644 --- a/plugins/obs-qsv11/common_directx11.cpp +++ b/plugins/obs-qsv11/common_directx11.cpp @@ -169,6 +169,8 @@ mfxStatus _simple_alloc(mfxFrameAllocRequest *request, request->Info .FourCC) //|| MFX_FOURCC_P8_TEXTURE == request->Info.FourCC format = DXGI_FORMAT_P8; + else if (MFX_FOURCC_P010 == request->Info.FourCC) + format = DXGI_FORMAT_P010; else format = DXGI_FORMAT_UNKNOWN; @@ -389,6 +391,14 @@ mfxStatus simple_lock(mfxHDL pthis, mfxMemId mid, mfxFrameData *ptr) ptr->U = 0; ptr->V = 0; break; + case DXGI_FORMAT_P010: + ptr->Pitch = (mfxU16)lockedRect.RowPitch; + ptr->PitchHigh = 0; + ptr->Y = (mfxU8 *)lockedRect.pData; + ptr->U = (mfxU8 *)lockedRect.pData + + desc.Height * lockedRect.RowPitch; + ptr->V = ptr->U + 2; + break; default: return MFX_ERR_LOCK_MEMORY; } diff --git a/plugins/obs-qsv11/obs-qsv-test/obs-qsv-test.cpp b/plugins/obs-qsv11/obs-qsv-test/obs-qsv-test.cpp index 856aa6c03..83f2b4a82 100644 --- a/plugins/obs-qsv11/obs-qsv-test/obs-qsv-test.cpp +++ b/plugins/obs-qsv11/obs-qsv-test/obs-qsv-test.cpp @@ -22,6 +22,7 @@ extern "C" __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; struct adapter_caps { bool is_intel = false; bool is_dgpu = false; + bool supports_av1 = false; }; static std::map adapter_info; @@ -70,6 +71,7 @@ static bool get_adapter_caps(IDXGIFactory *factory, uint32_t adapter_idx) caps.is_intel = true; caps.is_dgpu = dgpu; + caps.supports_av1 = has_encoder(impl, MFX_CODEC_AV1); return true; } @@ -117,6 +119,8 @@ try { printf("[%u]\n", idx); printf("is_intel=%s\n", caps.is_intel ? "true" : "false"); printf("is_dgpu=%s\n", caps.is_dgpu ? "true" : "false"); + printf("supports_av1=%s\n", + caps.supports_av1 ? "true" : "false"); } return 0; diff --git a/plugins/obs-qsv11/obs-qsv11-plugin-main.c b/plugins/obs-qsv11/obs-qsv11-plugin-main.c index f0962853a..410dc2b1a 100644 --- a/plugins/obs-qsv11/obs-qsv11-plugin-main.c +++ b/plugins/obs-qsv11/obs-qsv11-plugin-main.c @@ -69,6 +69,10 @@ MODULE_EXPORT const char *obs_module_description(void) extern struct obs_encoder_info obs_qsv_encoder; extern struct obs_encoder_info obs_qsv_encoder_tex; +extern struct obs_encoder_info obs_qsv_av1_encoder_tex; +extern struct obs_encoder_info obs_qsv_av1_encoder; + +extern bool av1_supported(mfxIMPL impl); struct adapter_info adapters[MAX_ADAPTERS] = {0}; size_t adapter_count = 0; @@ -116,6 +120,7 @@ bool obs_module_load(void) adapter_count = config_num_sections(config); bool avc_supported = false; + bool av1_supported = false; if (adapter_count > MAX_ADAPTERS) adapter_count = MAX_ADAPTERS; @@ -125,16 +130,24 @@ bool obs_module_load(void) snprintf(section, sizeof(section), "%d", (int)i); struct adapter_info *adapter = &adapters[i]; - adapter->is_intel = config_get_bool(config, section, "is_intel"); + adapter->is_intel = + config_get_bool(config, section, "is_intel"); adapter->is_dgpu = config_get_bool(config, section, "is_dgpu"); + adapter->supports_av1 = + config_get_bool(config, section, "supports_av1"); avc_supported |= adapter->is_intel; + av1_supported |= adapter->supports_av1; } if (avc_supported) { obs_register_encoder(&obs_qsv_encoder_tex); obs_register_encoder(&obs_qsv_encoder); } + if (av1_supported) { + obs_register_encoder(&obs_qsv_av1_encoder_tex); + obs_register_encoder(&obs_qsv_av1_encoder); + } fail: config_close(config); diff --git a/plugins/obs-qsv11/obs-qsv11.c b/plugins/obs-qsv11/obs-qsv11.c index 7b26273a6..293eb1a93 100644 --- a/plugins/obs-qsv11/obs-qsv11.c +++ b/plugins/obs-qsv11/obs-qsv11.c @@ -115,6 +115,12 @@ static const char *obs_qsv_getname(void *type_data) return "QuickSync H.264"; } +static const char *obs_qsv_getname_av1(void *type_data) +{ + UNUSED_PARAMETER(type_data); + return "QuickSync AV1"; +} + static void obs_qsv_stop(void *data); static void clear_data(struct obs_qsv *obsqsv) @@ -145,7 +151,7 @@ static void obs_qsv_destroy(void *data) } } -static void obs_qsv_defaults_h264(obs_data_t *settings) +static void obs_qsv_defaults(obs_data_t *settings, int ver) { obs_data_set_default_string(settings, "target_usage", "balanced"); obs_data_set_default_int(settings, "bitrate", 2500); @@ -153,8 +159,11 @@ static void obs_qsv_defaults_h264(obs_data_t *settings) obs_data_set_default_string(settings, "profile", "high"); obs_data_set_default_string(settings, "rate_control", "CBR"); + obs_data_set_default_int(settings, "__ver", ver); + obs_data_set_default_int(settings, "accuracy", 1000); obs_data_set_default_int(settings, "convergence", 1); + obs_data_set_default_int(settings, "cqp", 23); obs_data_set_default_int(settings, "qpi", 23); obs_data_set_default_int(settings, "qpp", 23); obs_data_set_default_int(settings, "qpb", 23); @@ -166,6 +175,16 @@ static void obs_qsv_defaults_h264(obs_data_t *settings) obs_data_set_default_bool(settings, "enhancements", false); } +static void obs_qsv_defaults_v1(obs_data_t *settings) +{ + obs_qsv_defaults(settings, 1); +} + +static void obs_qsv_defaults_v2(obs_data_t *settings) +{ + obs_qsv_defaults(settings, 2); +} + static inline void add_strings(obs_property_t *list, const char *const *strings) { while (*strings) { @@ -289,11 +308,17 @@ static bool rate_control_modified(obs_properties_t *ppts, obs_property_t *p, bVisible = astrcmpi(rate_control, "CQP") == 0; p = obs_properties_get(ppts, "qpi"); - obs_property_set_visible(p, bVisible); + if (p) + obs_property_set_visible(p, bVisible); p = obs_properties_get(ppts, "qpb"); - obs_property_set_visible(p, bVisible); + if (p) + obs_property_set_visible(p, bVisible); p = obs_properties_get(ppts, "qpp"); - obs_property_set_visible(p, bVisible); + if (p) + obs_property_set_visible(p, bVisible); + p = obs_properties_get(ppts, "cqp"); + if (p) + obs_property_set_visible(p, bVisible); bVisible = astrcmpi(rate_control, "ICQ") == 0 || astrcmpi(rate_control, "LA_ICQ") == 0; @@ -337,13 +362,44 @@ static inline void add_rate_controls(obs_property_t *list, } } -static obs_properties_t *obs_qsv_props(enum qsv_codec codec, void *unused) +static obs_properties_t *obs_qsv_props(enum qsv_codec codec, void *unused, + int ver) { UNUSED_PARAMETER(unused); obs_properties_t *props = obs_properties_create(); obs_property_t *prop; + prop = obs_properties_add_list(props, "rate_control", TEXT_RATE_CONTROL, + OBS_COMBO_TYPE_LIST, + OBS_COMBO_FORMAT_STRING); + if (codec == QSV_CODEC_AVC) + add_rate_controls(prop, qsv_ratecontrols); + else if (codec == QSV_CODEC_AV1) + add_rate_controls(prop, qsv_av1_ratecontrols); + + obs_property_set_modified_callback(prop, rate_control_modified); + + prop = obs_properties_add_int(props, "bitrate", TEXT_TARGET_BITRATE, 50, + 10000000, 50); + obs_property_int_set_suffix(prop, " Kbps"); + + prop = obs_properties_add_int(props, "max_bitrate", TEXT_MAX_BITRATE, + 50, 10000000, 50); + obs_property_int_set_suffix(prop, " Kbps"); + + if (ver >= 2) { + obs_properties_add_int(props, "cqp", "CQP", 1, + codec == QSV_CODEC_AV1 ? 63 : 51, 1); + } else { + obs_properties_add_int(props, "qpi", "QPI", 1, 51, 1); + obs_properties_add_int(props, "qpp", "QPP", 1, 51, 1); + obs_properties_add_int(props, "qpb", "QPB", 1, 51, 1); + } + + obs_properties_add_int(props, "icq_quality", TEXT_ICQ_QUALITY, 1, 51, + 1); + prop = obs_properties_add_list(props, "target_usage", TEXT_SPEED, OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING); @@ -355,6 +411,8 @@ static obs_properties_t *obs_qsv_props(enum qsv_codec codec, void *unused) if (codec == QSV_CODEC_AVC) add_strings(prop, qsv_profile_names); + else if (codec == QSV_CODEC_AV1) + add_strings(prop, qsv_profile_names_av1); obs_property_set_modified_callback(prop, profile_modified); @@ -362,52 +420,35 @@ static obs_properties_t *obs_qsv_props(enum qsv_codec codec, void *unused) 20, 1); obs_property_int_set_suffix(prop, " s"); - prop = obs_properties_add_list(props, "rate_control", TEXT_RATE_CONTROL, + obs_properties_add_int(props, "accuracy", TEXT_ACCURACY, 0, 10000, 1); + obs_properties_add_int(props, "convergence", TEXT_CONVERGENCE, 0, 10, + 1); + prop = obs_properties_add_list(props, "latency", TEXT_LATENCY, OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING); - if (codec == QSV_CODEC_AVC) - add_rate_controls(prop, qsv_ratecontrols); - - obs_property_set_modified_callback(prop, rate_control_modified); - - prop = obs_properties_add_int(props, "bitrate", TEXT_TARGET_BITRATE, 50, - 10000000, 50); - obs_property_int_set_suffix(prop, " Kbps"); - - prop = obs_properties_add_int(props, "max_bitrate", TEXT_MAX_BITRATE, - 50, 10000000, 50); - obs_property_int_set_suffix(prop, " Kbps"); - - if (codec == QSV_CODEC_AVC) { - obs_properties_add_int(props, "accuracy", TEXT_ACCURACY, 0, - 10000, 1); - obs_properties_add_int(props, "convergence", TEXT_CONVERGENCE, - 0, 10, 1); - obs_properties_add_int(props, "qpi", "QPI", 1, 51, 1); - obs_properties_add_int(props, "qpp", "QPP", 1, 51, 1); - obs_properties_add_int(props, "qpb", "QPB", 1, 51, 1); - obs_properties_add_int(props, "icq_quality", TEXT_ICQ_QUALITY, - 1, 51, 1); - prop = obs_properties_add_list(props, "latency", TEXT_LATENCY, - OBS_COMBO_TYPE_LIST, - OBS_COMBO_FORMAT_STRING); - add_strings(prop, qsv_latency_names); - obs_property_set_long_description( - prop, obs_module_text("Latency.ToolTip")); + add_strings(prop, qsv_latency_names); + obs_property_set_long_description(prop, + obs_module_text("Latency.ToolTip")); + if (codec != QSV_CODEC_AV1) obs_properties_add_int(props, "bframes", TEXT_BFRAMES, 0, 3, 1); - if (is_skl_or_greater_platform()) - obs_properties_add_bool(props, "enhancements", - TEXT_PERCEPTUAL_ENHANCEMENTS); - } + if (is_skl_or_greater_platform()) + obs_properties_add_bool(props, "enhancements", + TEXT_PERCEPTUAL_ENHANCEMENTS); return props; } static obs_properties_t *obs_qsv_props_h264(void *unused) { UNUSED_PARAMETER(unused); - return obs_qsv_props(QSV_CODEC_AVC, unused); + return obs_qsv_props(QSV_CODEC_AVC, unused, 1); +} + +static obs_properties_t *obs_qsv_props_av1(void *unused) +{ + UNUSED_PARAMETER(unused); + return obs_qsv_props(QSV_CODEC_AV1, unused, 2); } static void update_params(struct obs_qsv *obsqsv, obs_data_t *settings) @@ -430,6 +471,8 @@ static void update_params(struct obs_qsv *obsqsv, obs_data_t *settings) int qpi = (int)obs_data_get_int(settings, "qpi"); int qpp = (int)obs_data_get_int(settings, "qpp"); int qpb = (int)obs_data_get_int(settings, "qpb"); + int cqp = (int)obs_data_get_int(settings, "cqp"); + int ver = (int)obs_data_get_int(settings, "__ver"); int icq_quality = (int)obs_data_get_int(settings, "icq_quality"); int keyint_sec = (int)obs_data_get_int(settings, "keyint_sec"); bool cbr_override = obs_data_get_bool(settings, "cbr"); @@ -440,7 +483,8 @@ static void update_params(struct obs_qsv *obsqsv, obs_data_t *settings) bFrames = (int)obs_data_get_int(settings, "bf"); enum qsv_cpu_platform plat = qsv_get_cpu_platform(); - if (plat == QSV_CPU_PLATFORM_IVB || plat == QSV_CPU_PLATFORM_SNB) + if (obsqsv->codec == QSV_CODEC_AV1 || plat == QSV_CPU_PLATFORM_IVB || + plat == QSV_CPU_PLATFORM_SNB) bFrames = 0; int width = (int)obs_encoder_get_width(obsqsv->encoder); @@ -466,12 +510,81 @@ static void update_params(struct obs_qsv *obsqsv, obs_data_t *settings) else if (astrcmpi(target_usage, "veryfast") == 0) obsqsv->params.nTargetUsage = MFX_TARGETUSAGE_7; - if (astrcmpi(profile, "baseline") == 0) - obsqsv->params.nCodecProfile = MFX_PROFILE_AVC_BASELINE; - else if (astrcmpi(profile, "main") == 0) - obsqsv->params.nCodecProfile = MFX_PROFILE_AVC_MAIN; - else if (astrcmpi(profile, "high") == 0) - obsqsv->params.nCodecProfile = MFX_PROFILE_AVC_HIGH; + if (obsqsv->codec == QSV_CODEC_AVC) { + if (astrcmpi(profile, "baseline") == 0) + obsqsv->params.nCodecProfile = MFX_PROFILE_AVC_BASELINE; + else if (astrcmpi(profile, "main") == 0) + obsqsv->params.nCodecProfile = MFX_PROFILE_AVC_MAIN; + else if (astrcmpi(profile, "high") == 0) + obsqsv->params.nCodecProfile = MFX_PROFILE_AVC_HIGH; + + } else if (obsqsv->codec == QSV_CODEC_AV1) { + obsqsv->params.nCodecProfile = MFX_PROFILE_AV1_MAIN; + } + + obsqsv->params.VideoFormat = 5; + obsqsv->params.VideoFullRange = voi->range == VIDEO_RANGE_FULL; + + switch (voi->colorspace) { + case VIDEO_CS_601: + obsqsv->params.ColourPrimaries = 6; + obsqsv->params.TransferCharacteristics = 6; + obsqsv->params.MatrixCoefficients = 6; + obsqsv->params.ChromaSampleLocTypeTopField = 0; + obsqsv->params.ChromaSampleLocTypeBottomField = 0; + break; + case VIDEO_CS_DEFAULT: + case VIDEO_CS_709: + obsqsv->params.ColourPrimaries = 1; + obsqsv->params.TransferCharacteristics = 1; + obsqsv->params.MatrixCoefficients = 1; + obsqsv->params.ChromaSampleLocTypeTopField = 0; + obsqsv->params.ChromaSampleLocTypeBottomField = 0; + break; + case VIDEO_CS_SRGB: + obsqsv->params.ColourPrimaries = 1; + obsqsv->params.TransferCharacteristics = 13; + obsqsv->params.MatrixCoefficients = 1; + obsqsv->params.ChromaSampleLocTypeTopField = 0; + obsqsv->params.ChromaSampleLocTypeBottomField = 0; + break; + case VIDEO_CS_2100_PQ: + obsqsv->params.ColourPrimaries = 9; + obsqsv->params.TransferCharacteristics = 16; + obsqsv->params.MatrixCoefficients = 9; + obsqsv->params.ChromaSampleLocTypeTopField = 2; + obsqsv->params.ChromaSampleLocTypeBottomField = 2; + break; + case VIDEO_CS_2100_HLG: + obsqsv->params.ColourPrimaries = 9; + obsqsv->params.TransferCharacteristics = 18; + obsqsv->params.MatrixCoefficients = 9; + obsqsv->params.ChromaSampleLocTypeTopField = 2; + obsqsv->params.ChromaSampleLocTypeBottomField = 2; + } + + const bool pq = voi->colorspace == VIDEO_CS_2100_PQ; + const bool hlg = voi->colorspace == VIDEO_CS_2100_HLG; + if (pq || hlg) { + const int hdr_nominal_peak_level = + pq ? (int)obs_get_video_hdr_nominal_peak_level() + : (hlg ? 1000 : 0); + + obsqsv->params.DisplayPrimariesX[0] = 34000; + obsqsv->params.DisplayPrimariesX[1] = 13250; + obsqsv->params.DisplayPrimariesX[2] = 7500; + obsqsv->params.DisplayPrimariesY[0] = 16000; + obsqsv->params.DisplayPrimariesY[1] = 34500; + obsqsv->params.DisplayPrimariesY[2] = 3000; + obsqsv->params.WhitePointX = 15635; + obsqsv->params.WhitePointY = 16450; + obsqsv->params.MaxDisplayMasteringLuminance = + hdr_nominal_peak_level * 10000; + obsqsv->params.MinDisplayMasteringLuminance = 0; + + obsqsv->params.MaxContentLightLevel = hdr_nominal_peak_level; + obsqsv->params.MaxPicAverageLightLevel = hdr_nominal_peak_level; + } /* internal convenience parameter, overrides rate control param * XXX: Deprecated */ @@ -503,16 +616,22 @@ static void update_params(struct obs_qsv *obsqsv, obs_data_t *settings) else if (astrcmpi(rate_control, "LA_CBR") == 0) obsqsv->params.nRateControl = MFX_RATECONTROL_LA_HRD; - if (astrcmpi(latency, "ultra-low") == 0) { - obsqsv->params.nAsyncDepth = 1; - obsqsv->params.nLADEPTH = (mfxU16)0; - } else if (astrcmpi(latency, "low") == 0) { + if (obsqsv->codec == QSV_CODEC_AV1) { obsqsv->params.nAsyncDepth = 4; - obsqsv->params.nLADEPTH = - (mfxU16)(voi->fps_num / voi->fps_den / 2); - } else if (astrcmpi(latency, "normal") == 0) { - obsqsv->params.nAsyncDepth = 4; - obsqsv->params.nLADEPTH = (mfxU16)(voi->fps_num / voi->fps_den); + obsqsv->params.nLADEPTH = 0; + } else { + if (astrcmpi(latency, "ultra-low") == 0) { + obsqsv->params.nAsyncDepth = 1; + obsqsv->params.nLADEPTH = (mfxU16)0; + } else if (astrcmpi(latency, "low") == 0) { + obsqsv->params.nAsyncDepth = 4; + obsqsv->params.nLADEPTH = + (mfxU16)(voi->fps_num / voi->fps_den / 2); + } else if (astrcmpi(latency, "normal") == 0) { + obsqsv->params.nAsyncDepth = 4; + obsqsv->params.nLADEPTH = + (mfxU16)(voi->fps_num / voi->fps_den); + } } if (obsqsv->params.nLADEPTH > 0) { @@ -524,9 +643,18 @@ static void update_params(struct obs_qsv *obsqsv, obs_data_t *settings) obsqsv->params.nAccuracy = (mfxU16)accuracy; obsqsv->params.nConvergence = (mfxU16)convergence; - obsqsv->params.nQPI = (mfxU16)qpi; - obsqsv->params.nQPP = (mfxU16)qpp; - obsqsv->params.nQPB = (mfxU16)qpb; + if (ver == 1) { + obsqsv->params.nQPI = (mfxU16)qpi; + obsqsv->params.nQPP = (mfxU16)qpp; + obsqsv->params.nQPB = (mfxU16)qpb; + } else { + int actual_cqp = cqp; + if (obsqsv->codec == QSV_CODEC_AV1) + actual_cqp *= 4; + obsqsv->params.nQPI = actual_cqp; + obsqsv->params.nQPP = actual_cqp; + obsqsv->params.nQPB = actual_cqp; + } obsqsv->params.nTargetBitRate = (mfxU16)target_bitrate; obsqsv->params.nMaxBitRate = (mfxU16)max_bitrate; obsqsv->params.nWidth = (mfxU16)width; @@ -580,6 +708,118 @@ static void update_params(struct obs_qsv *obsqsv, obs_data_t *settings) info("debug info:"); } +static void update_params_av1(struct obs_qsv *obsqsv, obs_data_t *settings) +{ + video_t *video = obs_encoder_video(obsqsv->encoder); + const struct video_output_info *voi = video_output_get_info(video); + + const char *target_usage = + obs_data_get_string(settings, "target_usage"); + const char *profile = obs_data_get_string(settings, "profile"); + const char *rate_control = + obs_data_get_string(settings, "rate_control"); + int target_bitrate = (int)obs_data_get_int(settings, "bitrate"); + int max_bitrate = (int)obs_data_get_int(settings, "max_bitrate"); + int keyint_sec = (int)obs_data_get_int(settings, "keyint_sec"); + + int width = (int)obs_encoder_get_width(obsqsv->encoder); + int height = (int)obs_encoder_get_height(obsqsv->encoder); + if (astrcmpi(target_usage, "quality") == 0) + obsqsv->params.nTargetUsage = MFX_TARGETUSAGE_BEST_QUALITY; + else if (astrcmpi(target_usage, "balanced") == 0) + obsqsv->params.nTargetUsage = MFX_TARGETUSAGE_BALANCED; + else if (astrcmpi(target_usage, "speed") == 0) + obsqsv->params.nTargetUsage = MFX_TARGETUSAGE_BEST_SPEED; + else if (astrcmpi(target_usage, "veryslow") == 0) + obsqsv->params.nTargetUsage = MFX_TARGETUSAGE_1; + else if (astrcmpi(target_usage, "slower") == 0) + obsqsv->params.nTargetUsage = MFX_TARGETUSAGE_2; + else if (astrcmpi(target_usage, "slow") == 0) + obsqsv->params.nTargetUsage = MFX_TARGETUSAGE_3; + else if (astrcmpi(target_usage, "medium") == 0) + obsqsv->params.nTargetUsage = MFX_TARGETUSAGE_4; + else if (astrcmpi(target_usage, "fast") == 0) + obsqsv->params.nTargetUsage = MFX_TARGETUSAGE_5; + else if (astrcmpi(target_usage, "faster") == 0) + obsqsv->params.nTargetUsage = MFX_TARGETUSAGE_6; + else if (astrcmpi(target_usage, "veryfast") == 0) + obsqsv->params.nTargetUsage = MFX_TARGETUSAGE_7; + + if (astrcmpi(profile, "main") == 0) + obsqsv->params.nCodecProfile = MFX_PROFILE_AV1_MAIN; + + if (astrcmpi(rate_control, "CBR") == 0) + obsqsv->params.nRateControl = MFX_RATECONTROL_CBR; + else if (astrcmpi(rate_control, "VBR") == 0) + obsqsv->params.nRateControl = MFX_RATECONTROL_VBR; + + obsqsv->params.nAsyncDepth = 4; + + obsqsv->params.nTargetBitRate = (mfxU16)target_bitrate; + obsqsv->params.nMaxBitRate = (mfxU16)max_bitrate; + obsqsv->params.nWidth = (mfxU16)width; + obsqsv->params.nHeight = (mfxU16)height; + obsqsv->params.nFpsNum = (mfxU16)voi->fps_num; + obsqsv->params.nFpsDen = (mfxU16)voi->fps_den; + obsqsv->params.nKeyIntSec = (mfxU16)keyint_sec; + + obsqsv->params.VideoFormat = 5; + obsqsv->params.VideoFullRange = voi->range == VIDEO_RANGE_FULL; + + switch (voi->colorspace) { + case VIDEO_CS_601: + obsqsv->params.ColourPrimaries = 6; + obsqsv->params.TransferCharacteristics = 6; + obsqsv->params.MatrixCoefficients = 6; + obsqsv->params.ChromaSampleLocTypeTopField = 0; + obsqsv->params.ChromaSampleLocTypeBottomField = 0; + break; + case VIDEO_CS_DEFAULT: + case VIDEO_CS_709: + obsqsv->params.ColourPrimaries = 1; + obsqsv->params.TransferCharacteristics = 1; + obsqsv->params.MatrixCoefficients = 1; + obsqsv->params.ChromaSampleLocTypeTopField = 0; + obsqsv->params.ChromaSampleLocTypeBottomField = 0; + break; + case VIDEO_CS_SRGB: + obsqsv->params.ColourPrimaries = 1; + obsqsv->params.TransferCharacteristics = 13; + obsqsv->params.MatrixCoefficients = 1; + obsqsv->params.ChromaSampleLocTypeTopField = 0; + obsqsv->params.ChromaSampleLocTypeBottomField = 0; + break; + case VIDEO_CS_2100_PQ: + obsqsv->params.ColourPrimaries = 9; + obsqsv->params.TransferCharacteristics = 16; + obsqsv->params.MatrixCoefficients = 9; + obsqsv->params.ChromaSampleLocTypeTopField = 2; + obsqsv->params.ChromaSampleLocTypeBottomField = 2; + break; + case VIDEO_CS_2100_HLG: + obsqsv->params.ColourPrimaries = 9; + obsqsv->params.TransferCharacteristics = 18; + obsqsv->params.MatrixCoefficients = 9; + obsqsv->params.ChromaSampleLocTypeTopField = 2; + obsqsv->params.ChromaSampleLocTypeBottomField = 2; + } + + info("settings:\n\trate_control: %s", rate_control); + + if (obsqsv->params.nRateControl == MFX_RATECONTROL_VBR) + blog(LOG_INFO, "\tmax_bitrate: %d", + (int)obsqsv->params.nMaxBitRate); + + blog(LOG_INFO, + "\tfps_num: %d\n" + "\tfps_den: %d\n" + "\twidth: %d\n" + "\theight: %d", + voi->fps_num, voi->fps_den, width, height); + + info("debug info:"); +} + static bool update_settings(struct obs_qsv *obsqsv, obs_data_t *settings) { update_params(obsqsv, settings); @@ -601,9 +841,13 @@ static void load_headers(struct obs_qsv *obsqsv) uint8_t *pSPS, *pPPS; uint16_t nSPS, nPPS; + qsv_encoder_headers(obsqsv->context, &pSPS, &pPPS, &nSPS, &nPPS); da_push_back_array(header, pSPS, nSPS); - da_push_back_array(header, pPPS, nPPS); + + // AV1 does not need PPS + if (obsqsv->codec != QSV_CODEC_AV1) + da_push_back_array(header, pPPS, nPPS); obsqsv->extra_data = header.array; obsqsv->extra_data_size = header.num; @@ -645,6 +889,7 @@ static void *obs_qsv_create(enum qsv_codec codec, obs_data_t *settings, bfree(obsqsv); return NULL; } + obsqsv->params.video_fmt_10bit = true; break; default: switch (voi->colorspace) { @@ -715,6 +960,11 @@ static void *obs_qsv_create_h264(obs_data_t *settings, obs_encoder_t *encoder) return obs_qsv_create(QSV_CODEC_AVC, settings, encoder); } +static void *obs_qsv_create_av1(obs_data_t *settings, obs_encoder_t *encoder) +{ + return obs_qsv_create(QSV_CODEC_AV1, settings, encoder); +} + static HANDLE get_lib(const char *lib) { HMODULE mod = GetModuleHandleA(lib); @@ -729,30 +979,38 @@ static HANDLE get_lib(const char *lib) typedef HRESULT(WINAPI *CREATEDXGIFACTORY1PROC)(REFIID, void **); -static bool is_intel_gpu_primary(void) -{ - struct obs_video_info ovi; - obs_get_video_info(&ovi); - - return adapters[ovi.adapter].is_intel; -} - static void *obs_qsv_create_tex(enum qsv_codec codec, obs_data_t *settings, obs_encoder_t *encoder) { char *encoder_id; if (codec == QSV_CODEC_AVC) encoder_id = "obs_qsv11_soft"; + else if (codec == QSV_CODEC_AV1) + encoder_id = "obs_qsv11_av1_soft"; - if (!is_intel_gpu_primary()) { + struct obs_video_info ovi; + obs_get_video_info(&ovi); + + if (!adapters[ovi.adapter].is_intel) { blog(LOG_INFO, ">>> app not on intel GPU, fall back to old qsv encoder"); return obs_encoder_create_rerouted(encoder, (const char *)encoder_id); } + if (codec == QSV_CODEC_AV1 && !adapters[ovi.adapter].supports_av1) { + blog(LOG_INFO, + ">>> cap on different device, fall back to non-texture sharing AV1 qsv encoder"); + return obs_encoder_create_rerouted(encoder, + (const char *)encoder_id); + } + bool gpu_texture_active = obs_nv12_tex_active(); + if (codec == QSV_CODEC_AV1) + gpu_texture_active = gpu_texture_active || + obs_p010_tex_active(); + if (!gpu_texture_active) { blog(LOG_INFO, ">>> gpu tex not active, fall back to old qsv encoder"); @@ -777,6 +1035,12 @@ static void *obs_qsv_create_tex_h264(obs_data_t *settings, return obs_qsv_create_tex(QSV_CODEC_AVC, settings, encoder); } +static void *obs_qsv_create_tex_av1(obs_data_t *settings, + obs_encoder_t *encoder) +{ + return obs_qsv_create_tex(QSV_CODEC_AV1, settings, encoder); +} + static bool obs_qsv_extra_data(void *data, uint8_t **extra_data, size_t *size) { struct obs_qsv *obsqsv = data; @@ -806,6 +1070,11 @@ static inline bool valid_format(enum video_format format) return format == VIDEO_FORMAT_NV12; } +static inline bool valid_av1_format(enum video_format format) +{ + return format == VIDEO_FORMAT_NV12 || format == VIDEO_FORMAT_P010; +} + static inline void cap_resolution(obs_encoder_t *encoder, struct video_scale_info *info) { @@ -844,6 +1113,23 @@ static void obs_qsv_video_info(void *data, struct video_scale_info *info) cap_resolution(obsqsv->encoder, info); } +static void obs_qsv_av1_video_info(void *data, struct video_scale_info *info) +{ + struct obs_qsv *obsqsv = data; + enum video_format pref_format; + + pref_format = obs_encoder_get_preferred_video_format(obsqsv->encoder); + + if (!valid_av1_format(pref_format)) { + pref_format = valid_av1_format(info->format) + ? info->format + : VIDEO_FORMAT_NV12; + } + + info->format = pref_format; + cap_resolution(obsqsv->encoder, info); +} + static mfxU64 ts_obs_to_mfx(int64_t ts, const struct video_output_info *voi) { return ts * 90000 / voi->fps_num; @@ -956,6 +1242,57 @@ static void parse_packet(struct obs_qsv *obsqsv, struct encoder_packet *packet, g_bFirst = false; } +static void parse_packet_av1(struct obs_qsv *obsqsv, + struct encoder_packet *packet, mfxBitstream *pBS, + const struct video_output_info *voi, + bool *received_packet) +{ + if (pBS == NULL || pBS->DataLength == 0) { + *received_packet = false; + return; + } + + da_resize(obsqsv->packet_data, 0); + da_push_back_array(obsqsv->packet_data, &pBS->Data[pBS->DataOffset], + pBS->DataLength); + + packet->data = obsqsv->packet_data.array; + packet->size = obsqsv->packet_data.num; + packet->type = OBS_ENCODER_VIDEO; + packet->pts = ts_mfx_to_obs((mfxI64)pBS->TimeStamp, voi); + packet->keyframe = (pBS->FrameType & MFX_FRAMETYPE_IDR); + + uint16_t frameType = pBS->FrameType; + uint8_t priority; + + if (frameType & MFX_FRAMETYPE_I) + priority = OBS_NAL_PRIORITY_HIGHEST; + else if ((frameType & MFX_FRAMETYPE_P) || + (frameType & MFX_FRAMETYPE_REF)) + priority = OBS_NAL_PRIORITY_HIGH; + else + priority = OBS_NAL_PRIORITY_DISPOSABLE; + + packet->priority = priority; + + bool pFrame = pBS->FrameType & MFX_FRAMETYPE_P; + + packet->dts = ts_mfx_to_obs(pBS->DecodeTimeStamp, voi); + +#if 0 + info("parse packet:\n" + "\tFrameType: %d\n" + "\tpts: %d\n" + "\tdts: %d", + iType, packet->pts, packet->dts); +#endif + + *received_packet = true; + pBS->DataLength = 0; + + g_bFirst = false; +} + static bool obs_qsv_encode(void *data, struct encoder_frame *frame, struct encoder_packet *packet, bool *received_packet) { @@ -994,6 +1331,8 @@ static bool obs_qsv_encode(void *data, struct encoder_frame *frame, if (obsqsv->codec == QSV_CODEC_AVC) parse_packet(obsqsv, packet, pBS, voi, received_packet); + else if (obsqsv->codec == QSV_CODEC_AV1) + parse_packet_av1(obsqsv, packet, pBS, voi, received_packet); ReleaseSRWLockExclusive(&g_QsvLock); @@ -1038,6 +1377,8 @@ static bool obs_qsv_encode_tex(void *data, uint32_t handle, int64_t pts, if (obsqsv->codec == QSV_CODEC_AVC) parse_packet(obsqsv, packet, pBS, voi, received_packet); + else if (obsqsv->codec == QSV_CODEC_AV1) + parse_packet_av1(obsqsv, packet, pBS, voi, received_packet); ReleaseSRWLockExclusive(&g_QsvLock); @@ -1054,7 +1395,7 @@ struct obs_encoder_info obs_qsv_encoder = { .encode = obs_qsv_encode, .update = obs_qsv_update, .get_properties = obs_qsv_props_h264, - .get_defaults = obs_qsv_defaults_h264, + .get_defaults = obs_qsv_defaults_v1, .get_extra_data = obs_qsv_extra_data, .get_sei_data = obs_qsv_sei, .get_video_info = obs_qsv_video_info, @@ -1072,8 +1413,40 @@ struct obs_encoder_info obs_qsv_encoder_tex = { .encode_texture = obs_qsv_encode_tex, .update = obs_qsv_update, .get_properties = obs_qsv_props_h264, - .get_defaults = obs_qsv_defaults_h264, + .get_defaults = obs_qsv_defaults_v1, .get_extra_data = obs_qsv_extra_data, .get_sei_data = obs_qsv_sei, .get_video_info = obs_qsv_video_info, }; + +struct obs_encoder_info obs_qsv_av1_encoder_tex = { + .id = "obs_qsv11_av1", + .type = OBS_ENCODER_VIDEO, + .codec = "av1", + .get_name = obs_qsv_getname_av1, + .create = obs_qsv_create_tex_av1, + .destroy = obs_qsv_destroy, + .caps = OBS_ENCODER_CAP_DYN_BITRATE | OBS_ENCODER_CAP_PASS_TEXTURE, + .encode_texture = obs_qsv_encode_tex, + .update = obs_qsv_update, + .get_properties = obs_qsv_props_av1, + .get_defaults = obs_qsv_defaults_v2, + .get_extra_data = obs_qsv_extra_data, + .get_video_info = obs_qsv_av1_video_info, +}; + +struct obs_encoder_info obs_qsv_av1_encoder = { + .id = "obs_qsv11_av1_soft", + .type = OBS_ENCODER_VIDEO, + .codec = "av1", + .get_name = obs_qsv_getname_av1, + .create = obs_qsv_create_av1, + .destroy = obs_qsv_destroy, + .encode = obs_qsv_encode, + .update = obs_qsv_update, + .get_properties = obs_qsv_props_av1, + .get_defaults = obs_qsv_defaults_v2, + .get_extra_data = obs_qsv_extra_data, + .get_video_info = obs_qsv_av1_video_info, + .caps = OBS_ENCODER_CAP_DYN_BITRATE | OBS_ENCODER_CAP_INTERNAL, +};