Windows Media Foundation使用IMFTransform将mp4电影帧解码为2D纹理



我正在尝试使用Windows Media Foundation类解码mp4视频,并将帧转换为2D纹理,DirectX着色器可以使用这些纹理进行渲染。我已经能够使用MFCreateSourceReaderfromURL读取源流,并且能够读取流的媒体类型,如预期的那样,该流的主要类型为MFMEdiaType_Video,次要类型为MFVideoFormat_H264

我现在需要将此格式转换为RGB格式,该格式可用于初始化D3D11_TEXTURE2D资源和资源视图,然后将其传递给HLSL像素着色器进行采样。我已经厌倦了使用IMFTransform类为我进行转换,但当我试图将转换的输出类型设置为任何MFVideoFormat_RGB变体时,我会遇到错误。我还尝试在源阅读器上设置一个新的输出类型,并只是采样,希望获得正确格式的样本,但我再次运气不佳。

所以我的问题是:

  • 这种类型的转换可能吗?

  • 这可以通过IMFTransform/SourceReader类完成吗,就像我在上面厌倦的那样,我只需要调整代码还是需要手动进行这种类型的转换?

  • 这是将视频纹理数据输入着色器进行采样的最佳方式吗?还是有一个我没有想过的更简单的替代方案。

使用的操作系统是Windows 7,所以我不能使用SourceReaderEx或ID3D11VideoDevice接口,因为据我所知,这些解决方案似乎只在Windows 8上可用。

任何正确方向的帮助/指针都将不胜感激,如果需要,我也可以提供一些源代码。

这种类型的转换可能吗?

是的,这是可能的。库存H.264视频解码器MFT是"Direct3D感知",这意味着它可以利用DXVA将视频解码为Direct3D 9表面/Direct3D 11纹理。或者,如果硬件能力不足,也有软件回退模式。出于性能原因,您有兴趣将输出直接传递到纹理中(否则您将不得不自己加载这些数据,并为此花费CPU和视频资源)。

这可以像我上面厌倦的那样通过IMFTransform/SourceReader类来完成吗?我只需要调整代码还是需要手动进行这种类型的转换?

IMFTransform是一个抽象接口。它由H.264解码器(以及其他MFT)实现,您可以直接使用它,也可以使用更高级别的Source Reader API让它管理从文件中读取视频和使用此MFT进行解码。

也就是说,MFT和Source Reader实际上并不是唯一的替代选项,而是更高级别和更低级别的API。MFT接口由解码器提供,您负责馈送H.264并排出解码输出。Source Reader管理相同的MFT并添加文件读取功能。

Source Reader本身在Windows 7、BTW中可用(即使在Vista上,与较新的操作系统相比,功能集也可能受到限制)。

我发现您对Media Foundation的理解有一些错误。您希望从MFVideoFormat_H264获得RGB格式的图像,但不使用解码器H264。你写道:"我已经厌倦了使用IMFTransform类"——IMFTransformat不是类。它是转换COM对象的接口。您必须创建COM对象Media Foundation H264解码器。Microsoft软件H264解码器的CLSID为CLSID_CMSH264解码器MFT。但是,从该解码器中,您可以获得以下格式的输出图像:输出类型

MFVideoFormat_I420

MFVideoFormat_IYUV

MFVideoFormat_NV12

MFVideoFormat_YUY2

MFVideoFormat_YV12

可以从其中一个创建D3D11_TEXTURE2D。或者你可以从我的项目CaptureManager SDK中做这样的事情:

CComPtrCustom<IMFTransform> lColorConvert;
if (!Result(lColorConvert.CoCreateInstance(__uuidof(CColorConvertDMO))))
{
lresult = MediaFoundationManager::setInputType(
lColorConvert,
0,
lVideoMediaType,
0);
if (lresult)
{
break;
}
DWORD lTypeIndex = 0;
while (!lresult)
{
CComPtrCustom<IMFMediaType> lOutputType;
lresult = lColorConvert->GetOutputAvailableType(0, lTypeIndex++, &lOutputType);
if (!lresult)
{

lresult = MediaFoundationManager::getGUID(
lOutputType,
MF_MT_SUBTYPE,
lSubType);
if (lresult)
{
break;
}
if (lSubType == MFVideoFormat_RGB32)
{
LONG lstride = 0;
MediaFoundationManager::getStrideForBitmapInfoHeader(
lSubType,
lWidth,
lstride);
if (lstride < 0)
lstride = -lstride;
lBitRate = (lHight * (UINT32)lstride * 8 * lNumerator) / lDenominator;
lresult = MediaFoundationManager::setUINT32(
lOutputType,
MF_MT_AVG_BITRATE,
lBitRate);
if (lresult)
{
break;
}

PROPVARIANT lVarItem;
lresult = MediaFoundationManager::getItem(
*aPtrPtrInputMediaType,
MF_MT_FRAME_RATE,
lVarItem);
if (lresult)
{
break;
}
lresult = MediaFoundationManager::setItem(
lOutputType,
MF_MT_FRAME_RATE,
lVarItem);
if (lresult)
{
break;
}
(*aPtrPtrInputMediaType)->Release();
*aPtrPtrInputMediaType = lOutputType.detach();
break;
}
}
}
}

您可以设置ColorConvertDMO,将H264解码器的输出格式转换为您需要的格式。

此外,您还可以通过链接查看代码:videoInput。该代码从网络摄像头中获取实时视频,并将其解码为RGB。如果你在mp4视频文件源上替换网络摄像头源,你会得到接近你需要的解决方案。

问候

解码可以通过下一个代码执行:

MFT_OUTPUT_DATA_BUFFER loutputDataBuffer;
initOutputDataBuffer(
lTransform,
loutputDataBuffer);
DWORD lprocessOutputStatus = 0;
lresult = lTransform->ProcessOutput(
0,
1,
&loutputDataBuffer,
&lprocessOutputStatus);
if ((HRESULT)lresult == E_FAIL)
{
break;
}

函数initOutputDataBuffer分配所需的内存。该功能的示例如下:

Result initOutputDataBuffer(IMFTransform* aPtrTransform,
MFT_OUTPUT_DATA_BUFFER& aRefOutputBuffer)
{
Result lresult;
MFT_OUTPUT_STREAM_INFO loutputStreamInfo;
DWORD loutputStreamId = 0;
CComPtrCustom<IMFSample> lOutputSample;
CComPtrCustom<IMFMediaBuffer> lMediaBuffer;
do
{
if (aPtrTransform == nullptr)
{
lresult = E_POINTER;
break;
}
ZeroMemory(&loutputStreamInfo, sizeof(loutputStreamInfo));
ZeroMemory(&aRefOutputBuffer, sizeof(aRefOutputBuffer));
lresult = aPtrTransform->GetOutputStreamInfo(loutputStreamId, &loutputStreamInfo);
if (lresult)
{
break;
}
if ((loutputStreamInfo.dwFlags & MFT_OUTPUT_STREAM_PROVIDES_SAMPLES) == 0 &&
(loutputStreamInfo.dwFlags & MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES) == 0)
{
lresult = MFCreateSample(&lOutputSample);
if (lresult)
{
break;
}
lresult = MFCreateMemoryBuffer(loutputStreamInfo.cbSize, &lMediaBuffer);
if (lresult)
{
break;
}
lresult = lOutputSample->AddBuffer(lMediaBuffer);
if (lresult)
{
break;
}
aRefOutputBuffer.pSample = lOutputSample.Detach();
}
else
{
lresult = S_OK;
}
aRefOutputBuffer.dwStreamID = loutputStreamId;
} while (false);
return lresult;
}

它需要通过IMFTransform的GetOutputStreamInfo方法获取输出样本的信息。MFT_OUTPUT_STREAM_INFO包含有关输出媒体样本所需内存大小的信息-cbSize。它需要分配具有该大小的内存,将其添加到MediaSample中,并将其附加到MFT_OUTPUT_DATA_BUFFER。

因此,您可以看到,通过直接调用MediaFoundation函数编写视频编码和解码代码可能很困难,需要大量的相关知识。从您的任务描述中,我看到您只需要解码视频并呈现它。我可以建议您尝试使用Media Foundation Session功能。它是由微软的工程师开发的,已经包括了使用所需编码器的算法并进行了优化。在项目中,videoInput Media Foundation Session用于为网络摄像机创建的Media Source找到合适的解码器,并抓取未压缩格式的帧。它已经在做必要的处理了。您只需要在视频文件的媒体源上替换网络摄像机的媒体源。与直接调用IMFTransform进行解码相比,它可以更容易地编写代码,并可以简化许多问题(例如,稳定帧速率。如果代码在解码后立即渲染图像,然后解码新帧,则它可以在几秒钟内渲染1分钟的视频片段,或者如果视频和其他内容的渲染可能需要一帧以上的持续时间,则视频可以以"慢动作"风格呈现,渲染1分钟视频片段可能需要2、3或5分钟。我不知道你需要解码视频的项目是什么,但你应该有充分的理由使用直接调用Media Foundation函数和接口的代码。

谨致问候。

最新更新