繁体   English   中英

英特尔 H264 硬件 MFT 不支持 GOP 设置

[英]GOP setting is not honored by Intel H264 hardware MFT

问题陈述:

Intel 硬件 MFT 不遵守 GOP 设置,导致实时应用程序中的带宽消耗更多。 相同的代码在 Nvidia 硬件 MFT 上运行良好。

背景:

我正在尝试使用 Windows10 机器上的 MediaFoundation H264 硬件编码器将通过 DesktopDuplication API 捕获的 NV12 样本编码为视频流,通过 LAN 实时流式传输和渲染相同内容。

最初,我在编码器处面临过多的缓冲,因为编码器在提供输出样本之前缓冲了多达 25 帧(GOP 大小)。 经过一些研究,我发现设置 CODECAPI_AVLowLatencyMode 会以牺牲一点质量和带宽为代价来减少延迟。

设置 CODECAPI_AVLowLatencyMode 属性稍微提高了性能,但达不到实时要求。 看起来现在编码器至少在生成样本之前仍会缓冲多达 15 帧(在输出中引入大约 2 秒的延迟)。 并且只有在配置低帧率时才会注意到此行为。 在 60FPS 下,输出几乎是实时的,没有视觉上明显的延迟。

事实上,只有当帧速率设置为低于 30FPS 时,人眼才会注意到缓冲。 并且,延迟增加与 FPS 配置成反比,在 25FPS 时延迟为几百毫秒,当 FPS 配置为 10(恒定速率)时延迟增加到 3 秒。 我想,将 FPS 设置为超过 30(比如 60FPS)实际上会导致编码器缓冲区溢出速度快到足以产生延迟不明显的样本。

最近,我尝试了 CODECAPI_AVEncCommonRealTime 属性 ( https://learn.microsoft.com/en-us/windows/win32/directshow/avenccommonrealtime-property ) 并检查它在降低输入帧速率以避免带宽消耗时是否提高了性能,但该调用因“参数不正确”错误而失败。

我的实验:

为了保持恒定的帧速率,并强制编码器产生实时输出,我以 30FPS/60FPS 的恒定速率将相同的样本(之前保存的样本)提供给编码器。 我通过仅捕获最多 10FPS(或任何所需的 FPS)并伪造 30/60FPS 通过三次输入相同的样本或完全以基于 EMULATED_FRAME_RATE/ACTUAL_FRAME_RATE 比率(例如:30/10、60/15)的速率来伪造 30/60FPS , 60/20) 以固定的间隔精确地填充间隙。 例如,当 10 秒内没有变化时,我会向编码器输入相同的样本 30*10 次 (30FPS)。 我从一些开源 Github 项目中了解到这种方法,也从 Chromium 的实验代码示例中了解到,我还被告知( 主要是在 SO 上,也在其他论坛上)这是推送编码器进行实时输出的唯一方法,并且没有其他办法了。

上述方法产生近乎实时的输出,但消耗的数据比我预期的要多,即使我只将之前保存的样本提供给编码器。

输出比特率在 Intel MFT 上似乎一直保持在 350KBps 到 500KBps 之间,在 NVidia MFT 上(比特率配置为 30FPS 和 500KB)在 80KBps 到 400KBps 之间变化,无论屏幕内容是在 30FPS 还是 0FPS(空闲)变化。 在这种情况下,NVidia 硬件编码器似乎要好一些。

事实上,在屏幕空闲时间,编码器每秒产生的数据比上述速率多得多。 通过设置更大的 GOP 大小(当前配置的 GOP 大小为 16K),我已经能够减少 NVidia 设备上的数据消耗。 但是,屏幕空闲时间数据消耗在 Intel Graphics 620 硬件上保持在 300KBps 左右,在 NVidia GTX 1070(配置:500KB 比特率和 30FPS)上保持在 50KBps 到 80KBps,这是不可接受的。 我猜,英特尔硬件 MFT 根本不遵守 GOP 设置,或者改进不明显。

通过设置非常低的比特率,我还能够将 Intel 和 Nvidia 硬件上的空闲时间数据消耗分别降低到 ~130KBps 和 ~40KBps,但这仍然是不可接受的,这也会降低视频质量。

当输入样本之间没有发生变化时,有没有办法将编码器配置为产生小于 ~10KBps 的输出? 实际上,我的目标是在没有发生变化时输出 ~0KB,但 ~10KBps 在某种程度上是可以接受的。

更新:

我可以通过调整一些参数来降低 NVidia MFT 上的空闲时间数据消耗,在400KB 比特率配置下低于~20KBps,在 100KB 比特率配置下低于 ~10KBps。 这是有说服力的。 但具有相同编码器配置的相同代码在英特尔机器上产生的数据要多 20 到 40 倍。 Intel(Intel 显卡 620)肯定不支持 GOP 设置。 我什至尝试将 GOP 在 256 到 INT_MAX 之间变化,英特尔硬件 MFT 的输出似乎没有任何变化。

更新 2:

在玩弄编码器属性后(我只使用 eAVEncCommonRateControlMode_UnconstrainedVBR 而不是 eAVEncCommonRateControlMode_CBR 配置了 CODECAPI_AVEncCommonRateControlMode),现在我可以看到英特尔 MFT 在屏幕空闲时间产生 3KBps 的数据,但仅在前几秒(可能大约 3 到 8 秒) ,然后又回到同一个故事。 我猜想几秒钟后,编码器将失去对其与样本进行比较的关键帧的引用,并且似乎在那之后没有恢复。 无论 GOP 是 16/128/256/512/1024 还是 INT_MAX,行为都是相同的。

编码器配置:

参考: http ://alax.info/blog/1586

const int EMULATED_FRAME_RATE = 30;//
const int TARGET_FPS = 10;
const int FPS_DENOMINATOR = 1;
const unsigned long long time_between_capture = 1000 / TARGET_FPS;
const unsigned long long nEmulatedWaitTime = 1000 / EMULATED_FRAME_RATE;
const unsigned long long TARGET_AVERAGE_BIT_RATE = 4000000; // Adjusting this affects the quality of the H264 bit stream.
const LONGLONG VIDEO_FRAME_DURATION = 10ll * 1000ll * 1000ll / ((long long)EMULATED_FRAME_RATE); // frame duration in 100ns units
const UINT32 KEY_FRAME_SPACING = 16384;
const UINT32 GOP_SIZE = 16384;
const UINT32 BPICTURECOUNT = 2;

VARIANT var = { 0 };

//no failure on both Nvidia & Intel, but Intel seems to be not behaving as expected
var.vt = VT_UI4;
var.lVal = GOP_SIZE;
CHECK_HR(mpCodecAPI->SetValue(&CODECAPI_AVEncMPVGOPSize, &var), "Failed to set GOP size");

var.vt = VT_BOOL;
var.ulVal = VARIANT_TRUE;
// fails with "parameter incorrect" error.
CHECK_HR(mpCodecAPI->SetValue(&CODECAPI_AVEncCommonRealTime, &var), "Failed to set realtime mode");

var = { 0 };
var.vt = VT_BOOL;
var.ulVal = VARIANT_TRUE;
CHECK_HR(mpCodecAPI->SetValue(&CODECAPI_AVLowLatencyMode, &var), "Failed to set low latency mode");

var = { 0 };
var.vt = VT_BOOL;
var.ulVal = VARIANT_TRUE;
CHECK_HR(mpCodecAPI->SetValue(&CODECAPI_AVEncCommonLowLatency, &var), "Failed to set low latency mode");

var = { 0 };
var.vt = VT_UI4;
var.lVal = 2; // setting B-picture count to 0 to avoid latency and buffering at both encoder and decoder
CHECK_HR(mpCodecAPI->SetValue(&CODECAPI_AVEncMPVDefaultBPictureCount, &var), "Failed to set B-Picture count");

var = { 0 };
var.vt = VT_UI4;
var.lVal = 100; //0 - 100 (100 for best quality, 0 for low delay)
CHECK_HR(mpCodecAPI->SetValue(&CODECAPI_AVEncCommonQualityVsSpeed, &var), "Failed to set Quality-speed ratio");

var = { 0 };
var.vt = VT_UI4;
var.lVal = 20;
CHECK_HR(mpCodecAPI->SetValue(&CODECAPI_AVEncCommonQuality, &var), "Failed to set picture quality");

var = { 0 };
var.vt = VT_UI4;
var.lVal = eAVEncCommonRateControlMode_CBR; // This too fails on some hardware
CHECK_HR(mpCodecAPI->SetValue(&CODECAPI_AVEncCommonRateControlMode, &var), "Failed to set rate control");

var = { 0 };
var.vt = VT_UI4;
var.lVal = 4000000;
CHECK_HR(mpCodecAPI->SetValue(&CODECAPI_AVEncCommonMeanBitRate, &var), "Failed to set Adaptive mode");

var = { 0 };
var.vt = VT_UI4;
var.lVal = eAVEncAdaptiveMode_FrameRate;
CHECK_HR(mpCodecAPI->SetValue(&CODECAPI_AVEncAdaptiveMode, &var), "Failed to set Adaptive mode");

我尝试使用以下代码检索 GOP 大小的支持参数范围,但它只返回 E_NOTIMPL 错误。

VARIANT ValueMin = { 0 };
VARIANT ValueMax = { 0 };
VARIANT SteppingDelt = { 0 };
HRESULT hr = S_OK;

if (!mpCodecAPI) {
    CHECK_HR(_pTransform->QueryInterface(IID_PPV_ARGS(&mpCodecAPI)), "Failed to get codec api");
}

hr = mpCodecAPI->GetParameterRange(&CODECAPI_AVEncMPVGOPSize, &ValueMin, &ValueMax, &SteppingDelt);
CHECK_HR(hr, "Failed to get GOP range");

VariantClear(&ValueMin);
VariantClear(&ValueMax);
VariantClear(&SteppingDelt);

我错过了什么吗? 在没有屏幕内容变化的情况下,我是否可以尝试其他任何属性来获得实时性能,同时消耗尽可能少的带宽?

奇迹发生了。 在尝试编码器配置的同时,我不小心将我的主监视器更改为我机器上的另一个监视器,现在问题消失了。 切换回先前选择的主监视器会导致同样的问题。 我怀疑 d3ddevice 是麻烦制造者。 我不确定为什么这种情况只发生在该设备/显示器上,必须进行更多试验。

注意:我没有将此标记为答案,因为我还没有找出问题仅发生在该显示器/d3d 设备上的原因。 仅将此发布作为其他可能遇到类似情况的人的参考。 一旦我能够找到该特定 d3d11device 实例上出现奇怪行为的原因,我将更新答案。

这就是我创建 d3ddevice 的方式,并将其重复用于桌面复制图像捕获器、用于颜色转换的视频处理器以及通过MFT_MESSAGE_SET_D3D_MANAGER属性进行的硬件转换。

选项:

const D3D_DRIVER_TYPE m_DriverTypes[] = {

    //Hardware based Rasterizer
    D3D_DRIVER_TYPE_HARDWARE,

    //High performance Software Rasterizer
    D3D_DRIVER_TYPE_WARP,

    //Software Rasterizer (Low performance but more accurate)
    D3D_DRIVER_TYPE_REFERENCE,

    //TODO: Explore other driver types
};

const D3D_FEATURE_LEVEL m_FeatureLevel[] = {

    D3D_FEATURE_LEVEL_11_1,
    D3D_FEATURE_LEVEL_11_0,
    D3D_FEATURE_LEVEL_10_1,
    D3D_FEATURE_LEVEL_10_0,
    D3D_FEATURE_LEVEL_9_3,
    D3D_FEATURE_LEVEL_9_2,
    D3D_FEATURE_LEVEL_9_1

    //TODO: Explore other features levels as well
};

int m_DriversCount = ARRAYSIZE(m_DriverTypes);
int m_FeatureLevelsCount = ARRAYSIZE(m_FeatureLevel);

创建 d3d 设备:

DWORD errorCode = ERROR_SUCCESS;

if (m_FnD3D11CreateDevice == NULL)
{
    errorCode = loadD3D11FunctionsFromDll();
}

if (m_Id3d11Device)
{
    m_Id3d11Device = NULL;
    m_Id3d11DeviceContext = NULL;
}

UINT uiD3D11CreateFlag = (0 * D3D11_CREATE_DEVICE_SINGLETHREADED) | D3D11_CREATE_DEVICE_VIDEO_SUPPORT;

if (errorCode == ERROR_SUCCESS)
{
    if (m_FnD3D11CreateDevice) {

        for (UINT driverTypeIndex = 0; driverTypeIndex < m_DriversCount; ++driverTypeIndex)
        {
            m_LastErrorCode = D3D11CreateDevice(nullptr, m_DriverTypes[driverTypeIndex], nullptr, uiD3D11CreateFlag,
                m_FeatureLevel, m_FeatureLevelsCount, D3D11_SDK_VERSION, &m_Id3d11Device, &m_SelectedFeatureLevel, &m_Id3d11DeviceContext);

            if (SUCCEEDED(m_LastErrorCode))
            {
                break;
            }
        }
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM