obs-qsv11: Add QSV AV1 encoder

This commit is contained in:
Gale
2022-11-21 19:08:02 -08:00
committed by Jim
parent 9dd70910cb
commit 319eac4bf5
8 changed files with 657 additions and 92 deletions
+8 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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;
}
@@ -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
View File
@@ -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
View File
@@ -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,
};