浏览代码

obs-qsv11: Add HEVC

Co-authored-by: jpark37 <[email protected]>
Co-authored-by: Jim <[email protected]>
Yuriy Chumak 2 年之前
父节点
当前提交
fceee90aa9

+ 10 - 0
plugins/obs-qsv11/QSV_Encoder.cpp

@@ -363,3 +363,13 @@ enum qsv_cpu_platform qsv_get_cpu_platform()
 	//assume newer revisions are at least as capable as Haswell
 	return QSV_CPU_PLATFORM_INTEL;
 }
+
+int qsv_hevc_encoder_headers(qsv_t *pContext, uint8_t **pVPS, uint8_t **pSPS,
+			     uint8_t **pPPS, uint16_t *pnVPS, uint16_t *pnSPS,
+			     uint16_t *pnPPS)
+{
+	QSV_Encoder_Internal *pEncoder = (QSV_Encoder_Internal *)pContext;
+	pEncoder->GetVpsSpsPps(pVPS, pSPS, pPPS, pnVPS, pnSPS, pnPPS);
+
+	return 0;
+}

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

@@ -80,6 +80,7 @@ static const struct qsv_rate_control_info qsv_av1_ratecontrols[] =
 
 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_profile_names_hevc[] = {"main", "main10", 0};
 static const char *const qsv_usage_names[] = {"quality",  "balanced", "speed",
 					      "veryslow", "slower",   "slow",
 					      "medium",   "fast",     "faster",
@@ -92,11 +93,13 @@ struct adapter_info {
 	bool is_intel;
 	bool is_dgpu;
 	bool supports_av1;
+	bool supports_hevc;
 };
 
 enum qsv_codec {
 	QSV_CODEC_AVC,
 	QSV_CODEC_AV1,
+	QSV_CODEC_HEVC,
 };
 
 #define MAX_ADAPTERS 10
@@ -179,6 +182,10 @@ int qsv_encoder_headers(qsv_t *, uint8_t **pSPS, uint8_t **pPPS,
 enum qsv_cpu_platform qsv_get_cpu_platform();
 bool prefer_igpu_enc(int *iGPUIndex);
 
+int qsv_hevc_encoder_headers(qsv_t *pContext, uint8_t **vVPS, uint8_t **pSPS,
+			     uint8_t **pPPS, uint16_t *pnVPS, uint16_t *pnSPS,
+			     uint16_t *pnPPS);
+
 #ifdef __cplusplus
 }
 #endif

+ 57 - 5
plugins/obs-qsv11/QSV_Encoder_Internal.cpp

@@ -180,7 +180,7 @@ mfxStatus QSV_Encoder_Internal::Open(qsv_param_t *pParams, enum qsv_codec codec)
 	sts = m_pmfxENC->Init(&m_mfxEncParams);
 	MSDK_CHECK_RESULT(sts, MFX_ERR_NONE, sts);
 
-	sts = GetVideoParam();
+	sts = GetVideoParam(codec);
 	MSDK_CHECK_RESULT(sts, MFX_ERR_NONE, sts);
 
 	sts = InitBitstream();
@@ -201,9 +201,16 @@ mfxStatus QSV_Encoder_Internal::InitParams(qsv_param_t *pParams,
 		m_mfxEncParams.mfx.CodecId = MFX_CODEC_AVC;
 	else if (codec == QSV_CODEC_AV1)
 		m_mfxEncParams.mfx.CodecId = MFX_CODEC_AV1;
+	else if (codec == QSV_CODEC_HEVC)
+		m_mfxEncParams.mfx.CodecId = MFX_CODEC_HEVC;
 
 	m_mfxEncParams.mfx.GopOptFlag = MFX_GOP_STRICT;
-	m_mfxEncParams.mfx.NumSlice = 1;
+	if (codec == QSV_CODEC_HEVC) {
+		m_mfxEncParams.mfx.NumSlice = 0;
+		m_mfxEncParams.mfx.IdrInterval = 1;
+	} else {
+		m_mfxEncParams.mfx.NumSlice = 1;
+	}
 	m_mfxEncParams.mfx.TargetUsage = pParams->nTargetUsage;
 	m_mfxEncParams.mfx.CodecProfile = pParams->nCodecProfile;
 	m_mfxEncParams.mfx.FrameInfo.FrameRateExtN = pParams->nFpsNum;
@@ -227,6 +234,9 @@ mfxStatus QSV_Encoder_Internal::InitParams(qsv_param_t *pParams,
 	else
 		m_mfxEncParams.mfx.GopRefDist = pParams->nbFrames + 1;
 
+	if (codec == QSV_CODEC_HEVC)
+		m_mfxEncParams.mfx.LowPower = MFX_CODINGOPTION_OFF;
+
 	enum qsv_cpu_platform qsv_platform = qsv_get_cpu_platform();
 	if ((qsv_platform >= QSV_CPU_PLATFORM_ICL ||
 	     qsv_platform == QSV_CPU_PLATFORM_UNKNOWN) &&
@@ -329,6 +339,19 @@ mfxStatus QSV_Encoder_Internal::InitParams(qsv_param_t *pParams,
 		m_mfxEncParams.NumExtParam = (mfxU16)iBuffers;
 	}
 
+	if (codec == QSV_CODEC_HEVC) {
+		if ((pParams->nWidth & 15) || (pParams->nHeight & 15)) {
+			memset(&m_ExtHEVCParam, 0, sizeof(m_ExtHEVCParam));
+			m_ExtHEVCParam.Header.BufferId = MFX_EXTBUFF_HEVC_PARAM;
+			m_ExtHEVCParam.Header.BufferSz = sizeof(m_ExtHEVCParam);
+			m_ExtHEVCParam.PicWidthInLumaSamples = pParams->nWidth;
+			m_ExtHEVCParam.PicHeightInLumaSamples =
+				pParams->nHeight;
+			extendedBuffers[iBuffers++] =
+				(mfxExtBuffer *)&m_ExtHEVCParam;
+		}
+	}
+
 	memset(&m_ExtVideoSignalInfo, 0, sizeof(m_ExtVideoSignalInfo));
 	m_ExtVideoSignalInfo.Header.BufferId = MFX_EXTBUFF_VIDEO_SIGNAL_INFO;
 	m_ExtVideoSignalInfo.Header.BufferSz = sizeof(m_ExtVideoSignalInfo);
@@ -502,7 +525,7 @@ mfxStatus QSV_Encoder_Internal::AllocateSurfaces()
 	return sts;
 }
 
-mfxStatus QSV_Encoder_Internal::GetVideoParam()
+mfxStatus QSV_Encoder_Internal::GetVideoParam(enum qsv_codec codec)
 {
 	memset(&m_parameter, 0, sizeof(m_parameter));
 	mfxExtCodingOptionSPSPPS opt;
@@ -511,18 +534,33 @@ mfxStatus QSV_Encoder_Internal::GetVideoParam()
 	opt.Header.BufferSz = sizeof(mfxExtCodingOptionSPSPPS);
 
 	static mfxExtBuffer *extendedBuffers[1];
-	extendedBuffers[0] = (mfxExtBuffer *)&opt;
 	m_parameter.ExtParam = extendedBuffers;
-	m_parameter.NumExtParam = 1;
 
 	opt.SPSBuffer = m_SPSBuffer;
 	opt.PPSBuffer = m_PPSBuffer;
 	opt.SPSBufSize = 1024; //  m_nSPSBufferSize;
 	opt.PPSBufSize = 1024; //  m_nPPSBufferSize;
 
+	mfxExtCodingOptionVPS opt_vps{};
+	if (codec == QSV_CODEC_HEVC) {
+		opt_vps.Header.BufferId = MFX_EXTBUFF_CODING_OPTION_VPS;
+		opt_vps.Header.BufferSz = sizeof(mfxExtCodingOptionVPS);
+		opt_vps.VPSBuffer = m_VPSBuffer;
+		opt_vps.VPSBufSize = 1024;
+
+		extendedBuffers[0] = (mfxExtBuffer *)&opt_vps;
+		extendedBuffers[1] = (mfxExtBuffer *)&opt;
+		m_parameter.NumExtParam = 2;
+	} else {
+		extendedBuffers[0] = (mfxExtBuffer *)&opt;
+		m_parameter.NumExtParam = 1;
+	}
+
 	mfxStatus sts = m_pmfxENC->GetVideoParam(&m_parameter);
 	MSDK_CHECK_RESULT(sts, MFX_ERR_NONE, sts);
 
+	if (codec == QSV_CODEC_HEVC)
+		m_nVPSBufferSize = opt_vps.VPSBufSize;
 	m_nSPSBufferSize = opt.SPSBufSize;
 	m_nPPSBufferSize = opt.PPSBufSize;
 
@@ -538,6 +576,20 @@ void QSV_Encoder_Internal::GetSPSPPS(mfxU8 **pSPSBuf, mfxU8 **pPPSBuf,
 	*pnPPSBuf = m_nPPSBufferSize;
 }
 
+void QSV_Encoder_Internal::GetVpsSpsPps(mfxU8 **pVPSBuf, mfxU8 **pSPSBuf,
+					mfxU8 **pPPSBuf, mfxU16 *pnVPSBuf,
+					mfxU16 *pnSPSBuf, mfxU16 *pnPPSBuf)
+{
+	*pVPSBuf = m_VPSBuffer;
+	*pnVPSBuf = m_nVPSBufferSize;
+
+	*pSPSBuf = m_SPSBuffer;
+	*pnSPSBuf = m_nSPSBufferSize;
+
+	*pPPSBuf = m_PPSBuffer;
+	*pnPPSBuf = m_nPPSBufferSize;
+}
+
 mfxStatus QSV_Encoder_Internal::InitBitstream()
 {
 	m_nTaskPool = m_parameter.AsyncDepth;

+ 6 - 1
plugins/obs-qsv11/QSV_Encoder_Internal.h

@@ -67,6 +67,8 @@ public:
 	mfxStatus Open(qsv_param_t *pParams, enum qsv_codec codec);
 	void GetSPSPPS(mfxU8 **pSPSBuf, mfxU8 **pPPSBuf, mfxU16 *pnSPSBuf,
 		       mfxU16 *pnPPSBuf);
+	void GetVpsSpsPps(mfxU8 **pVPSBuf, mfxU8 **pSPSBuf, mfxU8 **pPPSBuf,
+			  mfxU16 *pnVPSBuf, mfxU16 *pnSPSBuf, mfxU16 *pnPPSBuf);
 	mfxStatus Encode(uint64_t ts, uint8_t *pDataY, uint8_t *pDataUV,
 			 uint32_t strideY, uint32_t strideUV,
 			 mfxBitstream **pBS);
@@ -81,7 +83,7 @@ public:
 protected:
 	mfxStatus InitParams(qsv_param_t *pParams, enum qsv_codec codec);
 	mfxStatus AllocateSurfaces();
-	mfxStatus GetVideoParam();
+	mfxStatus GetVideoParam(enum qsv_codec codec);
 	mfxStatus InitBitstream();
 	mfxStatus LoadNV12(mfxFrameSurface1 *pSurface, uint8_t *pDataY,
 			   uint8_t *pDataUV, uint32_t strideY,
@@ -102,14 +104,17 @@ private:
 	mfxFrameSurface1 **m_pmfxSurfaces;
 	mfxU16 m_nSurfNum;
 	MFXVideoENCODE *m_pmfxENC;
+	mfxU8 m_VPSBuffer[1024];
 	mfxU8 m_SPSBuffer[1024];
 	mfxU8 m_PPSBuffer[1024];
+	mfxU16 m_nVPSBufferSize;
 	mfxU16 m_nSPSBufferSize;
 	mfxU16 m_nPPSBufferSize;
 	mfxVideoParam m_parameter;
 	mfxExtCodingOption3 m_co3;
 	mfxExtCodingOption2 m_co2;
 	mfxExtCodingOption m_co;
+	mfxExtHEVCParam m_ExtHEVCParam{};
 	mfxExtVideoSignalInfo m_ExtVideoSignalInfo{};
 	mfxExtChromaLocInfo m_ExtChromaLocInfo{};
 	mfxExtMasteringDisplayColourVolume m_ExtMasteringDisplayColourVolume{};

+ 7 - 1
plugins/obs-qsv11/obs-qsv-test/obs-qsv-test.cpp

@@ -23,6 +23,7 @@ struct adapter_caps {
 	bool is_intel = false;
 	bool is_dgpu = false;
 	bool supports_av1 = false;
+	bool supports_hevc = false;
 };
 
 static std::map<uint32_t, adapter_caps> adapter_info;
@@ -72,6 +73,9 @@ 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);
+#if ENABLE_HEVC
+	caps.supports_hevc = has_encoder(impl, MFX_CODEC_HEVC);
+#endif
 
 	return true;
 }
@@ -80,7 +84,7 @@ DWORD WINAPI TimeoutThread(LPVOID param)
 {
 	HANDLE hMainThread = (HANDLE)param;
 
-	DWORD ret = WaitForSingleObject(hMainThread, 5000);
+	DWORD ret = WaitForSingleObject(hMainThread, 8000);
 	if (ret == WAIT_TIMEOUT)
 		TerminateProcess(GetCurrentProcess(), STATUS_TIMEOUT);
 
@@ -121,6 +125,8 @@ try {
 		printf("is_dgpu=%s\n", caps.is_dgpu ? "true" : "false");
 		printf("supports_av1=%s\n",
 		       caps.supports_av1 ? "true" : "false");
+		printf("supports_hevc=%s\n",
+		       caps.supports_hevc ? "true" : "false");
 	}
 
 	return 0;

+ 12 - 0
plugins/obs-qsv11/obs-qsv11-plugin-main.c

@@ -73,6 +73,8 @@ extern struct obs_encoder_info obs_qsv_encoder_tex;
 extern struct obs_encoder_info obs_qsv_encoder_tex_v2;
 extern struct obs_encoder_info obs_qsv_av1_encoder_tex;
 extern struct obs_encoder_info obs_qsv_av1_encoder;
+extern struct obs_encoder_info obs_qsv_hevc_encoder_tex;
+extern struct obs_encoder_info obs_qsv_hevc_encoder;
 
 extern bool av1_supported(mfxIMPL impl);
 
@@ -123,6 +125,7 @@ bool obs_module_load(void)
 	adapter_count = config_num_sections(config);
 	bool avc_supported = false;
 	bool av1_supported = false;
+	bool hevc_supported = false;
 
 	if (adapter_count > MAX_ADAPTERS)
 		adapter_count = MAX_ADAPTERS;
@@ -137,9 +140,12 @@ bool obs_module_load(void)
 		adapter->is_dgpu = config_get_bool(config, section, "is_dgpu");
 		adapter->supports_av1 =
 			config_get_bool(config, section, "supports_av1");
+		adapter->supports_hevc =
+			config_get_bool(config, section, "supports_hevc");
 
 		avc_supported |= adapter->is_intel;
 		av1_supported |= adapter->supports_av1;
+		hevc_supported |= adapter->supports_hevc;
 	}
 
 	if (avc_supported) {
@@ -152,6 +158,12 @@ bool obs_module_load(void)
 		obs_register_encoder(&obs_qsv_av1_encoder_tex);
 		obs_register_encoder(&obs_qsv_av1_encoder);
 	}
+#if ENABLE_HEVC
+	if (hevc_supported) {
+		obs_register_encoder(&obs_qsv_hevc_encoder_tex);
+		obs_register_encoder(&obs_qsv_hevc_encoder);
+	}
+#endif
 
 fail:
 	config_close(config);

+ 184 - 5
plugins/obs-qsv11/obs-qsv11.c

@@ -59,6 +59,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <util/darray.h>
 #include <util/platform.h>
 #include <obs-module.h>
+#include <obs-hevc.h>
 #include <obs-avc.h>
 #include <d3d11.h>
 #include <dxgi1_2.h>
@@ -127,6 +128,12 @@ static const char *obs_qsv_getname_av1(void *type_data)
 	return "QuickSync AV1";
 }
 
+static const char *obs_qsv_getname_hevc(void *type_data)
+{
+	UNUSED_PARAMETER(type_data);
+	return "QuickSync HEVC";
+}
+
 static void obs_qsv_stop(void *data);
 
 static void clear_data(struct obs_qsv *obsqsv)
@@ -198,6 +205,11 @@ static void obs_qsv_defaults_av1(obs_data_t *settings)
 	obs_qsv_defaults(settings, 2, QSV_CODEC_AV1);
 }
 
+static void obs_qsv_defaults_hevc(obs_data_t *settings)
+{
+	obs_qsv_defaults(settings, 2, QSV_CODEC_HEVC);
+}
+
 static inline void add_strings(obs_property_t *list, const char *const *strings)
 {
 	while (*strings) {
@@ -386,7 +398,8 @@ static obs_properties_t *obs_qsv_props(enum qsv_codec codec, void *unused,
 	prop = obs_properties_add_list(props, "rate_control", TEXT_RATE_CONTROL,
 				       OBS_COMBO_TYPE_LIST,
 				       OBS_COMBO_FORMAT_STRING);
-	if (codec == QSV_CODEC_AVC)
+
+	if (codec == QSV_CODEC_AVC || codec == QSV_CODEC_HEVC)
 		add_rate_controls(prop, qsv_ratecontrols);
 	else if (codec == QSV_CODEC_AV1)
 		add_rate_controls(prop, qsv_av1_ratecontrols);
@@ -426,6 +439,8 @@ static obs_properties_t *obs_qsv_props(enum qsv_codec codec, void *unused,
 		add_strings(prop, qsv_profile_names);
 	else if (codec == QSV_CODEC_AV1)
 		add_strings(prop, qsv_profile_names_av1);
+	else if (codec == QSV_CODEC_HEVC)
+		add_strings(prop, qsv_profile_names_hevc);
 
 	obs_property_set_modified_callback(prop, profile_modified);
 
@@ -470,6 +485,12 @@ static obs_properties_t *obs_qsv_props_av1(void *unused)
 	return obs_qsv_props(QSV_CODEC_AV1, unused, 2);
 }
 
+static obs_properties_t *obs_qsv_props_hevc(void *unused)
+{
+	UNUSED_PARAMETER(unused);
+	return obs_qsv_props(QSV_CODEC_HEVC, unused, 2);
+}
+
 static void update_params(struct obs_qsv *obsqsv, obs_data_t *settings)
 {
 	video_t *video = obs_encoder_video(obsqsv->encoder);
@@ -537,6 +558,20 @@ static void update_params(struct obs_qsv *obsqsv, obs_data_t *settings)
 		else if (astrcmpi(profile, "high") == 0)
 			obsqsv->params.nCodecProfile = MFX_PROFILE_AVC_HIGH;
 
+	} else if (obsqsv->codec == QSV_CODEC_HEVC) {
+		if (astrcmpi(profile, "main") == 0) {
+			obsqsv->params.nCodecProfile = MFX_PROFILE_HEVC_MAIN;
+			if (obs_p010_tex_active()) {
+				blog(LOG_WARNING,
+				     "[qsv encoder] Forcing main10 for P010");
+				obsqsv->params.nCodecProfile =
+					MFX_PROFILE_HEVC_MAIN10;
+			}
+
+		} else if (astrcmpi(profile, "main10") == 0) {
+			obsqsv->params.nCodecProfile = MFX_PROFILE_HEVC_MAIN10;
+		}
+
 	} else if (obsqsv->codec == QSV_CODEC_AV1) {
 		obsqsv->params.nCodecProfile = MFX_PROFILE_AV1_MAIN;
 	}
@@ -733,6 +768,28 @@ static bool update_settings(struct obs_qsv *obsqsv, obs_data_t *settings)
 	return true;
 }
 
+static void load_hevc_headers(struct obs_qsv *obsqsv)
+{
+	DARRAY(uint8_t) header;
+	DARRAY(uint8_t) sei;
+
+	da_init(header);
+	da_init(sei);
+
+	uint8_t *pVPS, *pSPS, *pPPS;
+	uint16_t nVPS, nSPS, nPPS;
+	qsv_hevc_encoder_headers(obsqsv->context, &pVPS, &pSPS, &pPPS, &nVPS,
+				 &nSPS, &nPPS);
+	da_push_back_array(header, pVPS, nVPS);
+	da_push_back_array(header, pSPS, nSPS);
+	da_push_back_array(header, pPPS, nPPS);
+
+	obsqsv->extra_data = header.array;
+	obsqsv->extra_data_size = header.num;
+	obsqsv->sei = sei.array;
+	obsqsv->sei_size = sei.num;
+}
+
 static void load_headers(struct obs_qsv *obsqsv)
 {
 	DARRAY(uint8_t) header;
@@ -819,6 +876,8 @@ static void *obs_qsv_create(enum qsv_codec codec, obs_data_t *settings,
 
 		if (obsqsv->context == NULL)
 			warn("qsv failed to load");
+		else if (obsqsv->codec == QSV_CODEC_HEVC)
+			load_hevc_headers(obsqsv);
 		else
 			load_headers(obsqsv);
 	} else {
@@ -873,6 +932,11 @@ static void *obs_qsv_create_av1(obs_data_t *settings, obs_encoder_t *encoder)
 	return obs_qsv_create(QSV_CODEC_AV1, settings, encoder);
 }
 
+static void *obs_qsv_create_hevc(obs_data_t *settings, obs_encoder_t *encoder)
+{
+	return obs_qsv_create(QSV_CODEC_HEVC, settings, encoder);
+}
+
 static HANDLE get_lib(const char *lib)
 {
 	HMODULE mod = GetModuleHandleA(lib);
@@ -909,7 +973,7 @@ static void *obs_qsv_create_tex(enum qsv_codec codec, obs_data_t *settings,
 
 	bool gpu_texture_active = obs_nv12_tex_active();
 
-	if (codec == QSV_CODEC_AV1)
+	if (codec != QSV_CODEC_AVC)
 		gpu_texture_active = gpu_texture_active ||
 				     obs_p010_tex_active();
 
@@ -952,6 +1016,13 @@ static void *obs_qsv_create_tex_av1(obs_data_t *settings,
 				  "obs_qsv11_av1_soft");
 }
 
+static void *obs_qsv_create_tex_hevc(obs_data_t *settings,
+				     obs_encoder_t *encoder)
+{
+	return obs_qsv_create_tex(QSV_CODEC_HEVC, settings, encoder,
+				  "obs_qsv11_hevc_soft");
+}
+
 static bool obs_qsv_extra_data(void *data, uint8_t **extra_data, size_t *size)
 {
 	struct obs_qsv *obsqsv = data;
@@ -1024,7 +1095,8 @@ 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)
+static void obs_qsv_video_plus_hdr_info(void *data,
+					struct video_scale_info *info)
 {
 	struct obs_qsv *obsqsv = data;
 	enum video_format pref_format;
@@ -1204,6 +1276,77 @@ static void parse_packet_av1(struct obs_qsv *obsqsv,
 	g_bFirst = false;
 }
 
+static void parse_packet_hevc(struct obs_qsv *obsqsv,
+			      struct encoder_packet *packet, mfxBitstream *pBS,
+			      const struct video_output_info *voi,
+			      bool *received_packet)
+{
+	bool is_vcl_packet = false;
+
+	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 = OBS_NAL_PRIORITY_DISPOSABLE;
+
+	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;
+
+	packet->priority = priority;
+
+	/* ------------------------------------ */
+
+	//bool iFrame = pBS->FrameType & MFX_FRAMETYPE_I;
+	//bool bFrame = pBS->FrameType & MFX_FRAMETYPE_B;
+	bool pFrame = pBS->FrameType & MFX_FRAMETYPE_P;
+
+	// In case MSDK doesn't support automatic DecodeTimeStamp, do manual
+	// calculation
+	if (g_pts2dtsShift >= 0) {
+		if (g_bFirst) {
+			packet->dts = packet->pts - 3 * obsqsv->params.nFpsDen;
+		} else if (pFrame) {
+			packet->dts = packet->pts - 10 * obsqsv->params.nFpsDen;
+			g_prevDts = packet->dts;
+		} else {
+			packet->dts = g_prevDts + obsqsv->params.nFpsDen;
+			g_prevDts = packet->dts;
+		}
+	} else {
+		packet->dts = ts_mfx_to_obs(pBS->DecodeTimeStamp, voi);
+	}
+
+#if 0
+	int iType = iFrame ? 0 : (bFrame ? 1 : (pFrame ? 2 : -1));
+	int64_t interval = obsqsv->params.nbFrames + 1;
+
+	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)
 {
@@ -1244,6 +1387,8 @@ static bool obs_qsv_encode(void *data, struct encoder_frame *frame,
 		parse_packet(obsqsv, packet, pBS, voi, received_packet);
 	else if (obsqsv->codec == QSV_CODEC_AV1)
 		parse_packet_av1(obsqsv, packet, pBS, voi, received_packet);
+	else if (obsqsv->codec == QSV_CODEC_HEVC)
+		parse_packet_hevc(obsqsv, packet, pBS, voi, received_packet);
 
 	ReleaseSRWLockExclusive(&g_QsvLock);
 
@@ -1290,6 +1435,8 @@ static bool obs_qsv_encode_tex(void *data, uint32_t handle, int64_t pts,
 		parse_packet(obsqsv, packet, pBS, voi, received_packet);
 	else if (obsqsv->codec == QSV_CODEC_AV1)
 		parse_packet_av1(obsqsv, packet, pBS, voi, received_packet);
+	else if (obsqsv->codec == QSV_CODEC_HEVC)
+		parse_packet_hevc(obsqsv, packet, pBS, voi, received_packet);
 
 	ReleaseSRWLockExclusive(&g_QsvLock);
 
@@ -1379,7 +1526,7 @@ struct obs_encoder_info obs_qsv_av1_encoder_tex = {
 	.get_properties = obs_qsv_props_av1,
 	.get_defaults = obs_qsv_defaults_av1,
 	.get_extra_data = obs_qsv_extra_data,
-	.get_video_info = obs_qsv_av1_video_info,
+	.get_video_info = obs_qsv_video_plus_hdr_info,
 };
 
 struct obs_encoder_info obs_qsv_av1_encoder = {
@@ -1394,6 +1541,38 @@ struct obs_encoder_info obs_qsv_av1_encoder = {
 	.get_properties = obs_qsv_props_av1,
 	.get_defaults = obs_qsv_defaults_av1,
 	.get_extra_data = obs_qsv_extra_data,
-	.get_video_info = obs_qsv_av1_video_info,
+	.get_video_info = obs_qsv_video_plus_hdr_info,
+	.caps = OBS_ENCODER_CAP_DYN_BITRATE | OBS_ENCODER_CAP_INTERNAL,
+};
+
+struct obs_encoder_info obs_qsv_hevc_encoder_tex = {
+	.id = "obs_qsv11_hevc",
+	.type = OBS_ENCODER_VIDEO,
+	.codec = "hevc",
+	.get_name = obs_qsv_getname_hevc,
+	.create = obs_qsv_create_tex_hevc,
+	.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_hevc,
+	.get_defaults = obs_qsv_defaults_hevc,
+	.get_extra_data = obs_qsv_extra_data,
+	.get_video_info = obs_qsv_video_plus_hdr_info,
+};
+
+struct obs_encoder_info obs_qsv_hevc_encoder = {
+	.id = "obs_qsv11_hevc_soft",
+	.type = OBS_ENCODER_VIDEO,
+	.codec = "hevc",
+	.get_name = obs_qsv_getname_hevc,
+	.create = obs_qsv_create_hevc,
+	.destroy = obs_qsv_destroy,
+	.encode = obs_qsv_encode,
+	.update = obs_qsv_update,
+	.get_properties = obs_qsv_props_hevc,
+	.get_defaults = obs_qsv_defaults_hevc,
+	.get_extra_data = obs_qsv_extra_data,
+	.get_video_info = obs_qsv_video_plus_hdr_info,
 	.caps = OBS_ENCODER_CAP_DYN_BITRATE | OBS_ENCODER_CAP_INTERNAL,
 };