mirror of
https://github.com/obsproject/obs-studio.git
synced 2026-09-21 12:49:40 +08:00
obs-qsv11: Add QSV AV1 encoder
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user