我正在尝试使用Windows Media Foundation类解码mp4视频,并将帧转换为可由DirectX着色器用于渲染的2D纹理。我已经能够使用 MFCreateSourceReaderfromURL 阅读源流,并且能够读取其主要类型 MFMEdiaType_Video 的流媒体类型和次要类型 MFVideoFormat_H264 符合预期。
我现在需要将此格式转换为RGB格式,该格式可用于初始化D3D11_TEXTURE2D资源和资源视图,然后可以将其传递给HLSL像素着色器进行采样。我已经厌倦了使用 IMFTransform 类为我做转换,但是当我尝试将转换的输出类型设置为任何MFVideoFormat_RGB变体时,我收到错误。我也尝试在源阅读器上设置一个新的输出类型,然后只是采样,希望以正确的格式获取样本,但我又没有运气。
所以我的问题是:
这种转换是否可行?
这可以通过IMFTransform / SourceReader类完成,就像我上面已经累了一样,我只需要调整代码,还是需要手动进行这种类型的转换?
这是将视频纹理数据输入到着色器进行采样的最佳方式,还是有一个我没想过的更容易的替代方法。
正在使用的操作系统是Windows 7,因此我无法使用SourceReaderEx或ID3D11VideoDevice接口,因为据我所知,这些解决方案似乎只在Windows 8上可用。
我们非常感谢任何正确方向的帮助/指针,如有必要,我还可以提供一些源代码。
答案 0 :(得分:4)
这种转换是否可行?
是的,这是可能的。股票H.264 Video Decoder MFT是“Direct3D识别”,这意味着它可以利用DXVA将视频解码为Direct3D 9曲面/ Direct3D 11纹理。或者,如果硬件功能不足,则还有软件回退模式。出于性能原因,您有兴趣将输出正确地输出到纹理中(否则您将不得不自己加载这些数据并花费CPU和视频资源)。
这可以通过IMFTransform / SourceReader类完成,就像我上面已经累了一样,我只需要调整代码,还是需要手动进行这种类型的转换?
IMFTransform
是抽象接口。它由H.264解码器(以及其他MFT)实现,您可以直接使用它,或者您可以使用更高级别的源读取器API来管理视频读取和使用此MFT解码。
也就是说,MFT和Source Reader实际上不是独占备用选项,而是更高和更低级别的API。 MFT接口由解码器提供,您负责提供H.264并排除解码输出。 Source Reader管理相同的MFT并添加文件读取功能。
Source Reader本身可用于Windows 7,BTW(即使在Vista上,与新版操作系统相比,功能设置可能有限)。
答案 1 :(得分:2)
我发现你对Media Foundation的理解有些错误。您希望从MFVideoFormat_H264获取RGB格式的图像,但不使用解码器H264。您写道“我已经厌倦了使用IMFTransform类” - IMFTransform不是类。它是Transform COM对象的接口。您必须创建COM对象Media Foundation H264解码器。 Microsoft软件H264解码器的CLSID是CLSID_CMSH264DecoderMFT。但是,从该解码器,您可以获得以下格式的输出图像: 输出类型
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视频文件源上的网络摄像头源,您将获得接近您需要的解决方案。
此致
答案 2 :(得分:1)
解码可以通过下一个代码执行:
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功能。它由Microsoft的工程师开发,并已包含使用所需编码器和优化的算法。在项目videoInput中,Media Foundation Session用于为媒体源找到合适的解码器,该解码器是为网络摄像头创建的,并以非压缩格式抓取帧。它已经完成了所需的处理。您只需从视频文件中的Media Source上的Web摄像头替换Media Source。通过直接调用IMFTransform进行解码可以更轻松地编写代码,并允许简化许多问题(例如 - 稳定帧速率。如果代码在解码后立即渲染图像然后解码新帧,那么它可以渲染1分钟视频在几秒钟内剪辑,或者如果视频和其他内容的渲染可能需要多个帧持续时间,则视频可以在&#34;慢动作&#34;样式和1分钟视频剪辑的渲染中呈现2,3或者5分钟。 我不知道您需要解码视频的项目,但是您应该有充分的理由使用直接调用Media Foundation功能和接口的代码。
问候。