Browse Source

obs-qsv11: Add QSV AV1 encoder

Gale 3 years ago
parent
commit
319eac4bf5

+ 8 - 1
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;

+ 25 - 0
plugins/obs-qsv11/QSV_Encoder.h

@@ -58,6 +58,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include <Windows.h>
 #include "mfxstructures.h"
+#include "mfxadapter.h"
 #include <stdint.h>
 
 #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
 }

+ 144 - 18
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;
 

+ 8 - 1
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;

+ 10 - 0
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;
 	}

+ 4 - 0
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<uint32_t, adapter_caps> 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;

+ 14 - 1
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);

+ 444 - 71
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,
+};