Windows Media Foundation 使用 IMFTransform 将 mp4 电影帧解码为 2D 纹理
Windows Media Foundation using IMFTransform to decode mp4 movie frames to 2D textures
我正在尝试使用 Windows Media Foundation classes 解码 mp4 视频,并将帧转换为可由 DirectX 着色器用于渲染的 2D 纹理。我已经能够使用 MFCreateSourceReaderfromURL 读取源流,并且能够读取其主要类型为 MFMEdiaType_Video[= 的流的媒体类型35=] 和次要类型如预期的那样 MFVideoFormat_H264。
我现在需要将此格式转换为 RGB 格式,可用于初始化 D3D11_TEXTURE2D 资源和资源视图,然后将其传递给 HLSL 像素着色器进行采样。我已经厌倦了使用 IMFTransform class 为我进行转换,但是当我尝试将转换的输出类型设置为任何 MFVideoFormat_RGB 变体时,我得到一个错误。我还尝试在源 reader 上设置新的输出类型,然后只是希望获得正确格式的样本的采样,但我又一次没有运气。
所以我的问题是:
这种类型的转换是否可行?
这可以通过 IMFTransform/SourceReader classes 来完成吗,就像我上面已经厌倦的那样,我只需要调整代码还是我需要做这种类型手动转换?
这是将视频纹理数据馈送到着色器进行采样的最佳方式吗?还是有我没有想到的更简单的替代方法。
正在使用的 OS 是 Windows 7,所以我不能使用 SourceReaderEx 或 ID3D11VideoDevice 接口,因为据我所知,这些解决方案似乎只在 Windows 上可用8.
任何方向正确的 help/pointers 将不胜感激,如有必要,我也可以提供一些源代码。
我看出你对Media Foundation的理解有误。你想从 MFVideoFormat_H264 获取 RGB 格式的图像,但你不使用解码器 H264。您写了 "I've tired using the IMFTransform class" - IMFTransform 不是 class。它是 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解码器的输出格式转换为您需要的格式。
另外,您可以通过link查看代码:videoInput。此代码从网络摄像头获取实时视频并将其解码为 RGB。如果您将网络摄像头源替换为 mp4 视频文件源,您将获得接近您需要的解决方案。
此致
Is this type of conversion possible?
是的,这是可能的。 Stock H.264 Video Decoder MFT 是 "Direct3D aware",这意味着它可以利用 DXVA 将视频解码为 Direct3D 9 surfaces/Direct3D 11 种纹理。或者,如果硬件功能不足,也可以使用软件回退模式。出于性能原因,您有兴趣将输出直接传送到纹理中(否则您将不得不自己加载此数据,花费 CPU 和视频资源)。
Can this be done through the IMFTransform/SourceReader classes like I've tired above and do I just need to tweak the code or do I need to do this type of conversion manually?
IMFTransform
是抽象接口。它由 H.264 解码器(以及其他 MFT)实现,您可以直接使用它,或者您可以使用更高级别的 Source Reader API 来管理从文件中读取视频并使用解码这个 MFT。
也就是说,MFT和Source Reader其实并不是排他性的alternate option,而是更高和更低级别的API。 MFT 接口由解码器提供,您负责输入 H.264 并排出解码输出。来源 Reader 管理相同的 MFT 并添加文件读取功能。
Source Reader 本身在 Windows 7 中可用,顺便说一句(即使在 Vista 上,与较新的操作系统相比,功能集也可能受到限制)。
解码可以通过下一段代码执行:
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 中,媒体基础会话用于为网络摄像头创建的媒体源找到合适的解码器,并以未压缩格式抓取帧。它已经完成了所需的处理。您只需将来自视频文件的媒体源上的来自网络摄像头的媒体源替换掉。它可以比直接调用 IMFTransform 编写代码更容易解码,并允许简化许多问题(例如 - 稳定帧速率。如果代码将在解码后立即渲染图像然后解码新帧,那么它可以渲染 1 分钟的视频在几秒钟内剪辑,或者如果视频和其他内容的呈现可能需要超过一帧持续时间,视频可以以 "Slow motion" 样式呈现,而 1 分钟视频剪辑的呈现可能需要 2、3 或 5 分钟。
我不知道你需要解码视频的项目是什么,但你应该有充分的理由使用直接调用媒体基础函数和接口的代码。
此致。
我正在尝试使用 Windows Media Foundation classes 解码 mp4 视频,并将帧转换为可由 DirectX 着色器用于渲染的 2D 纹理。我已经能够使用 MFCreateSourceReaderfromURL 读取源流,并且能够读取其主要类型为 MFMEdiaType_Video[= 的流的媒体类型35=] 和次要类型如预期的那样 MFVideoFormat_H264。
我现在需要将此格式转换为 RGB 格式,可用于初始化 D3D11_TEXTURE2D 资源和资源视图,然后将其传递给 HLSL 像素着色器进行采样。我已经厌倦了使用 IMFTransform class 为我进行转换,但是当我尝试将转换的输出类型设置为任何 MFVideoFormat_RGB 变体时,我得到一个错误。我还尝试在源 reader 上设置新的输出类型,然后只是希望获得正确格式的样本的采样,但我又一次没有运气。
所以我的问题是:
这种类型的转换是否可行?
这可以通过 IMFTransform/SourceReader classes 来完成吗,就像我上面已经厌倦的那样,我只需要调整代码还是我需要做这种类型手动转换?
这是将视频纹理数据馈送到着色器进行采样的最佳方式吗?还是有我没有想到的更简单的替代方法。
正在使用的 OS 是 Windows 7,所以我不能使用 SourceReaderEx 或 ID3D11VideoDevice 接口,因为据我所知,这些解决方案似乎只在 Windows 上可用8.
任何方向正确的 help/pointers 将不胜感激,如有必要,我也可以提供一些源代码。
我看出你对Media Foundation的理解有误。你想从 MFVideoFormat_H264 获取 RGB 格式的图像,但你不使用解码器 H264。您写了 "I've tired using the IMFTransform class" - IMFTransform 不是 class。它是 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解码器的输出格式转换为您需要的格式。
另外,您可以通过link查看代码:videoInput。此代码从网络摄像头获取实时视频并将其解码为 RGB。如果您将网络摄像头源替换为 mp4 视频文件源,您将获得接近您需要的解决方案。
此致
Is this type of conversion possible?
是的,这是可能的。 Stock H.264 Video Decoder MFT 是 "Direct3D aware",这意味着它可以利用 DXVA 将视频解码为 Direct3D 9 surfaces/Direct3D 11 种纹理。或者,如果硬件功能不足,也可以使用软件回退模式。出于性能原因,您有兴趣将输出直接传送到纹理中(否则您将不得不自己加载此数据,花费 CPU 和视频资源)。
Can this be done through the IMFTransform/SourceReader classes like I've tired above and do I just need to tweak the code or do I need to do this type of conversion manually?
IMFTransform
是抽象接口。它由 H.264 解码器(以及其他 MFT)实现,您可以直接使用它,或者您可以使用更高级别的 Source Reader API 来管理从文件中读取视频并使用解码这个 MFT。
也就是说,MFT和Source Reader其实并不是排他性的alternate option,而是更高和更低级别的API。 MFT 接口由解码器提供,您负责输入 H.264 并排出解码输出。来源 Reader 管理相同的 MFT 并添加文件读取功能。
Source Reader 本身在 Windows 7 中可用,顺便说一句(即使在 Vista 上,与较新的操作系统相比,功能集也可能受到限制)。
解码可以通过下一段代码执行:
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 中,媒体基础会话用于为网络摄像头创建的媒体源找到合适的解码器,并以未压缩格式抓取帧。它已经完成了所需的处理。您只需将来自视频文件的媒体源上的来自网络摄像头的媒体源替换掉。它可以比直接调用 IMFTransform 编写代码更容易解码,并允许简化许多问题(例如 - 稳定帧速率。如果代码将在解码后立即渲染图像然后解码新帧,那么它可以渲染 1 分钟的视频在几秒钟内剪辑,或者如果视频和其他内容的呈现可能需要超过一帧持续时间,视频可以以 "Slow motion" 样式呈现,而 1 分钟视频剪辑的呈现可能需要 2、3 或 5 分钟。 我不知道你需要解码视频的项目是什么,但你应该有充分的理由使用直接调用媒体基础函数和接口的代码。
此致。