将 IMFSample* 转换为 ID3D11ShaderResourceView*
Convert IMFSample* to ID3D11ShaderResourceView*
我是 DirectX 的新手,我正在尝试做一个简单的应用程序来读取视频并将其显示在 Quad 上。
我使用 Windows Media Foundation (IMFSourceReader) 阅读视频,它会在样本解码 (IMFSample) 时向我发送回调。
我想将此 IMFSample* 转换为 ID3D11ShaderResourceView*,以便将其用作纹理来绘制我的四边形,但转换失败。
这是我所做的(我删除了不相关的错误检查):
HRESULT SourceReaderCB::OnReadSample(HRESULT hrStatus, DWORD dwStreamIndex, DWORD dwStreamFlags, LONGLONG llTimestamp, IMFSample *pSample)
{
...
DWORD NumBuffers = 0;
hr = pSample->GetBufferCount(&NumBuffers);
if (FAILED(hr) || NumBuffers < 1)
{
...
}
IMFMediaBuffer* SourceMediaPtr = nullptr;
hr = pSample->GetBufferByIndex(0, &SourceMediaPtr);
if (FAILED(hr))
{
...
}
ComPtr<IMFMediaBuffer> _pInputBuffer = SourceMediaPtr;
ComPtr<IMF2DBuffer2> _pInputBuffer2D2;
bool isVideoFrame = (_pInputBuffer.As(&_pInputBuffer2D2) == S_OK);
if (isVideoFrame)
{
IMFDXGIBuffer* pDXGIBuffer = NULL;
ID3D11Texture2D* pSurface = NULL;
hr = _pInputBuffer->QueryInterface(__uuidof(IMFDXGIBuffer), (LPVOID*)&pDXGIBuffer);
if (FAILED(hr))
{
SafeRelease(&SourceMediaPtr);
goto done;
}
hr = pDXGIBuffer->GetResource(__uuidof(ID3D11Texture2D), (LPVOID*)&pSurface);
if (FAILED(hr))
{
...
}
ID3D11ShaderResourceView* resourceView;
if (pSurface)
{
D3D11_TEXTURE2D_DESC textureDesc;
pSurface->GetDesc(&textureDesc);
D3D11_SHADER_RESOURCE_VIEW_DESC shaderResourceViewDesc;
shaderResourceViewDesc.Format = DXGI_FORMAT_R8_UNORM;
shaderResourceViewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
shaderResourceViewDesc.Texture2D.MostDetailedMip = 0;
shaderResourceViewDesc.Texture2D.MipLevels = 1;
ID3D11ShaderResourceView* resourceView;
hr = d3d11device->CreateShaderResourceView(pSurface, &shaderResourceViewDesc, &resourceView);
if (FAILED(hr))
{
... // CODE FAILS HERE
}
...
}
}
}
我的第一个问题是我将 shaderResourceViewDesc.Format 设置为 DXGI_FORMAT_R8_UNORM,这可能只会给我红色图像(稍后我将不得不对此进行调查)。
我面临的第二个阻碍问题是 ID3D11Texture2D 到 ID3D11ShaderResourceView 的转换失败并显示以下错误消息:
ID3D11Device::CreateShaderResourceView: A ShaderResourceView cannot be created of a Resource that did not specify the D3D11_BIND_SHADER_RESOURCE BindFlag. [ STATE_CREATION ERROR #129: CREATESHADERRESOURCEVIEW_INVALIDRESOURCE]
我知道在创建纹理时缺少一个标志,阻止我做我想做的事,但由于数据缓冲区是由 WMF 创建的,我不确定我应该做什么解决这个问题。
感谢您的帮助
调试输出表明纹理不兼容,因为它是在没有 D3D11_BIND_SHADER_RESOURCE
标志的情况下创建的(在 D3D11_TEXTURE2D_DESC
structure 的 BindFlag
字段中指定)。
您阅读了由 Media Foundation 基元创建的纹理。在某些情况下,您可以更改创建标志,但一般情况是您需要自己创建一个兼容的纹理,在纹理之间复制数据,然后以您的纹理作为参数调用 CreateShaderResourceView
方法,而不是比原来的纹理。
我看到你的代码,我可以说你的方法是错误的 - 无意冒犯。首先,视频解码器创建简单的纹理 - 在你的情况下 DirectX11 纹理 - 它是一个常规纹理 - 它不是着色器资源,因此它不能在着色器代码中使用。在我看来,有两种方法可以解决您的任务:
研究 - Walkthrough: Using MF to render video in a Direct3D app - 这种 link 当前 "Walkthrough: Using Microsoft Media Foundation for Windows Phone 8" 的方法 - 从你的代码中我看到你尝试为 WindowsStore 编写解决方案- Windows Phone 的 UWP 和代码是可行的 - 此代码需要 MediaEnginePlayer
- MediaEnginePlayer class 用作包装 MF API 的助手 class;
在 GitHub Windows-classic-samples 上找到并在其中找到 DX11VideoRenderer
- 这是媒体基础渲染器的完整代码DirectX11 - 它包括使用 DirectX11 视频处理器的非常好的示例,该处理器将常规视频纹理从解码器传输到交换链的渲染视频纹理:
2.1.从交换链获取渲染纹理:
// Get Backbuffer
hr = m_pSwapChain1->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&pDXGIBackBuffer);
if (FAILED(hr))
{
break;
}
2.2。从视频处理器的渲染纹理输出视图创建:
//
// Create Output View of Output Surfaces.
//
D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC OutputViewDesc;
ZeroMemory( &OutputViewDesc, sizeof( OutputViewDesc ) );
if (m_b3DVideo && m_bStereoEnabled)
{
OutputViewDesc.ViewDimension = D3D11_VPOV_DIMENSION_TEXTURE2DARRAY;
}
else
{
OutputViewDesc.ViewDimension = D3D11_VPOV_DIMENSION_TEXTURE2D;
}
OutputViewDesc.Texture2D.MipSlice = 0;
OutputViewDesc.Texture2DArray.MipSlice = 0;
OutputViewDesc.Texture2DArray.FirstArraySlice = 0;
if (m_b3DVideo && 0 != m_vp3DOutput)
{
OutputViewDesc.Texture2DArray.ArraySize = 2; // STEREO
}
QueryPerformanceCounter(&lpcStart);
hr = m_pDX11VideoDevice->CreateVideoProcessorOutputView(pDXGIBackBuffer, m_pVideoProcessorEnum, &OutputViewDesc, &pOutputView);
2.3。从视频处理器的常规解码器视频纹理输入视图创建:
D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC InputLeftViewDesc;
ZeroMemory( &InputLeftViewDesc, sizeof( InputLeftViewDesc ) );
InputLeftViewDesc.FourCC = 0;
InputLeftViewDesc.ViewDimension = D3D11_VPIV_DIMENSION_TEXTURE2D;
InputLeftViewDesc.Texture2D.MipSlice = 0;
InputLeftViewDesc.Texture2D.ArraySlice = dwLeftViewIndex;
hr = m_pDX11VideoDevice->CreateVideoProcessorInputView(pLeftTexture2D, m_pVideoProcessorEnum, &InputLeftViewDesc, &pLeftInputView);
if (FAILED(hr))
{
break;
}
2.4。对来自交换链的渲染纹理进行常规解码器视频纹理的 blitting:
D3D11_VIDEO_PROCESSOR_STREAM StreamData;
ZeroMemory( &StreamData, sizeof( StreamData ) );
StreamData.Enable = TRUE;
StreamData.OutputIndex = 0;
StreamData.InputFrameOrField = 0;
StreamData.PastFrames = 0;
StreamData.FutureFrames = 0;
StreamData.ppPastSurfaces = NULL;
StreamData.ppFutureSurfaces = NULL;
StreamData.pInputSurface = pLeftInputView;
StreamData.ppPastSurfacesRight = NULL;
StreamData.ppFutureSurfacesRight = NULL;
if (m_b3DVideo && MFVideo3DSampleFormat_MultiView == m_vp3DOutput && pRightTexture2D)
{
StreamData.pInputSurfaceRight = pRightInputView;
}
hr = pVideoContext->VideoProcessorBlt(m_pVideoProcessor, pOutputView, 0, 1, &StreamData );
if (FAILED(hr))
{
break;
}
是的,它们是复杂代码的一部分,需要研究整个 DX11VideoRenderer
项目才能理解它 - 这将花费大量时间。
此致,
叶夫根尼·佩雷古达
我是 DirectX 的新手,我正在尝试做一个简单的应用程序来读取视频并将其显示在 Quad 上。
我使用 Windows Media Foundation (IMFSourceReader) 阅读视频,它会在样本解码 (IMFSample) 时向我发送回调。
我想将此 IMFSample* 转换为 ID3D11ShaderResourceView*,以便将其用作纹理来绘制我的四边形,但转换失败。
这是我所做的(我删除了不相关的错误检查):
HRESULT SourceReaderCB::OnReadSample(HRESULT hrStatus, DWORD dwStreamIndex, DWORD dwStreamFlags, LONGLONG llTimestamp, IMFSample *pSample)
{
...
DWORD NumBuffers = 0;
hr = pSample->GetBufferCount(&NumBuffers);
if (FAILED(hr) || NumBuffers < 1)
{
...
}
IMFMediaBuffer* SourceMediaPtr = nullptr;
hr = pSample->GetBufferByIndex(0, &SourceMediaPtr);
if (FAILED(hr))
{
...
}
ComPtr<IMFMediaBuffer> _pInputBuffer = SourceMediaPtr;
ComPtr<IMF2DBuffer2> _pInputBuffer2D2;
bool isVideoFrame = (_pInputBuffer.As(&_pInputBuffer2D2) == S_OK);
if (isVideoFrame)
{
IMFDXGIBuffer* pDXGIBuffer = NULL;
ID3D11Texture2D* pSurface = NULL;
hr = _pInputBuffer->QueryInterface(__uuidof(IMFDXGIBuffer), (LPVOID*)&pDXGIBuffer);
if (FAILED(hr))
{
SafeRelease(&SourceMediaPtr);
goto done;
}
hr = pDXGIBuffer->GetResource(__uuidof(ID3D11Texture2D), (LPVOID*)&pSurface);
if (FAILED(hr))
{
...
}
ID3D11ShaderResourceView* resourceView;
if (pSurface)
{
D3D11_TEXTURE2D_DESC textureDesc;
pSurface->GetDesc(&textureDesc);
D3D11_SHADER_RESOURCE_VIEW_DESC shaderResourceViewDesc;
shaderResourceViewDesc.Format = DXGI_FORMAT_R8_UNORM;
shaderResourceViewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
shaderResourceViewDesc.Texture2D.MostDetailedMip = 0;
shaderResourceViewDesc.Texture2D.MipLevels = 1;
ID3D11ShaderResourceView* resourceView;
hr = d3d11device->CreateShaderResourceView(pSurface, &shaderResourceViewDesc, &resourceView);
if (FAILED(hr))
{
... // CODE FAILS HERE
}
...
}
}
}
我的第一个问题是我将 shaderResourceViewDesc.Format 设置为 DXGI_FORMAT_R8_UNORM,这可能只会给我红色图像(稍后我将不得不对此进行调查)。
我面临的第二个阻碍问题是 ID3D11Texture2D 到 ID3D11ShaderResourceView 的转换失败并显示以下错误消息:
ID3D11Device::CreateShaderResourceView: A ShaderResourceView cannot be created of a Resource that did not specify the D3D11_BIND_SHADER_RESOURCE BindFlag. [ STATE_CREATION ERROR #129: CREATESHADERRESOURCEVIEW_INVALIDRESOURCE]
我知道在创建纹理时缺少一个标志,阻止我做我想做的事,但由于数据缓冲区是由 WMF 创建的,我不确定我应该做什么解决这个问题。
感谢您的帮助
调试输出表明纹理不兼容,因为它是在没有 D3D11_BIND_SHADER_RESOURCE
标志的情况下创建的(在 D3D11_TEXTURE2D_DESC
structure 的 BindFlag
字段中指定)。
您阅读了由 Media Foundation 基元创建的纹理。在某些情况下,您可以更改创建标志,但一般情况是您需要自己创建一个兼容的纹理,在纹理之间复制数据,然后以您的纹理作为参数调用 CreateShaderResourceView
方法,而不是比原来的纹理。
我看到你的代码,我可以说你的方法是错误的 - 无意冒犯。首先,视频解码器创建简单的纹理 - 在你的情况下 DirectX11 纹理 - 它是一个常规纹理 - 它不是着色器资源,因此它不能在着色器代码中使用。在我看来,有两种方法可以解决您的任务:
研究 - Walkthrough: Using MF to render video in a Direct3D app - 这种 link 当前 "Walkthrough: Using Microsoft Media Foundation for Windows Phone 8" 的方法 - 从你的代码中我看到你尝试为 WindowsStore 编写解决方案- Windows Phone 的 UWP 和代码是可行的 - 此代码需要
MediaEnginePlayer
- MediaEnginePlayer class 用作包装 MF API 的助手 class;在 GitHub Windows-classic-samples 上找到并在其中找到
DX11VideoRenderer
- 这是媒体基础渲染器的完整代码DirectX11 - 它包括使用 DirectX11 视频处理器的非常好的示例,该处理器将常规视频纹理从解码器传输到交换链的渲染视频纹理: 2.1.从交换链获取渲染纹理:// Get Backbuffer hr = m_pSwapChain1->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&pDXGIBackBuffer); if (FAILED(hr)) { break; }
2.2。从视频处理器的渲染纹理输出视图创建:
// // Create Output View of Output Surfaces. // D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC OutputViewDesc; ZeroMemory( &OutputViewDesc, sizeof( OutputViewDesc ) ); if (m_b3DVideo && m_bStereoEnabled) { OutputViewDesc.ViewDimension = D3D11_VPOV_DIMENSION_TEXTURE2DARRAY; } else { OutputViewDesc.ViewDimension = D3D11_VPOV_DIMENSION_TEXTURE2D; } OutputViewDesc.Texture2D.MipSlice = 0; OutputViewDesc.Texture2DArray.MipSlice = 0; OutputViewDesc.Texture2DArray.FirstArraySlice = 0; if (m_b3DVideo && 0 != m_vp3DOutput) { OutputViewDesc.Texture2DArray.ArraySize = 2; // STEREO } QueryPerformanceCounter(&lpcStart); hr = m_pDX11VideoDevice->CreateVideoProcessorOutputView(pDXGIBackBuffer, m_pVideoProcessorEnum, &OutputViewDesc, &pOutputView);
2.3。从视频处理器的常规解码器视频纹理输入视图创建:
D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC InputLeftViewDesc;
ZeroMemory( &InputLeftViewDesc, sizeof( InputLeftViewDesc ) );
InputLeftViewDesc.FourCC = 0;
InputLeftViewDesc.ViewDimension = D3D11_VPIV_DIMENSION_TEXTURE2D;
InputLeftViewDesc.Texture2D.MipSlice = 0;
InputLeftViewDesc.Texture2D.ArraySlice = dwLeftViewIndex;
hr = m_pDX11VideoDevice->CreateVideoProcessorInputView(pLeftTexture2D, m_pVideoProcessorEnum, &InputLeftViewDesc, &pLeftInputView);
if (FAILED(hr))
{
break;
}
2.4。对来自交换链的渲染纹理进行常规解码器视频纹理的 blitting:
D3D11_VIDEO_PROCESSOR_STREAM StreamData;
ZeroMemory( &StreamData, sizeof( StreamData ) );
StreamData.Enable = TRUE;
StreamData.OutputIndex = 0;
StreamData.InputFrameOrField = 0;
StreamData.PastFrames = 0;
StreamData.FutureFrames = 0;
StreamData.ppPastSurfaces = NULL;
StreamData.ppFutureSurfaces = NULL;
StreamData.pInputSurface = pLeftInputView;
StreamData.ppPastSurfacesRight = NULL;
StreamData.ppFutureSurfacesRight = NULL;
if (m_b3DVideo && MFVideo3DSampleFormat_MultiView == m_vp3DOutput && pRightTexture2D)
{
StreamData.pInputSurfaceRight = pRightInputView;
}
hr = pVideoContext->VideoProcessorBlt(m_pVideoProcessor, pOutputView, 0, 1, &StreamData );
if (FAILED(hr))
{
break;
}
是的,它们是复杂代码的一部分,需要研究整个 DX11VideoRenderer
项目才能理解它 - 这将花费大量时间。
此致,
叶夫根尼·佩雷古达