更新成功!!见底部编辑
一些部分成功,但也许足以回答您的问题。请继续阅读。
在我的系统上,调试异常显示OnTimer() 函数在尝试调用TransferVideoFrame() 时失败。它给出的错误是InvalidArgumentException。
所以,在谷歌上搜索了一下,我第一次发现 - 显然有 a bug in NVIDIA drivers - 这意味着视频播放似乎在 11 和 10 功能级别时失败。
所以我的第一个更改是函数CreateDX11Device() 如下:
static const D3D_FEATURE_LEVEL levels[] = {
/*
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
};
现在TransferVideoFrame() 仍然失败,但给出E_FAIL(作为 HRESULT)而不是无效参数。
更多谷歌搜索导致我的second discovery -
这是一个使用TransferVideoFrame() 而不使用CreateTexture2D() 来预先创建纹理的示例。我看到您在 OnTimer() 中已经有一些与此类似但未使用的代码,所以我猜您找到了相同的链接。
不管怎样,我现在用这段代码来获取视频帧:
ComPtr <ID3D11Texture2D> spTextureDst;
m_spDX11SwapChain->GetBuffer (0, IID_PPV_ARGS (&spTextureDst));
m_spMediaEngine->TransferVideoFrame (spTextureDst.Get (), nullptr, &m_rcTarget, &m_bkgColor);
执行此操作后,我看到 TransferVideoFrame() 成功(好!)但在您复制的纹理 - m_spCopyTexture 上调用 Map() - 失败,因为该纹理不是使用 CPU 读取访问权限创建的。
所以,我只是使用您的读/写 m_spRenderTexture 作为副本的目标,因为它具有正确的标志,并且由于之前的更改,我不再使用它。
//copy the render target texture to the readable texture.
m_spDX11DeviceContext->CopySubresourceRegion(m_spRenderTexture.Get(),0,0,0,0,spTextureDst.Get(),0,NULL);
m_spDX11DeviceContext->Flush();
//Map the readable texture;
D3D11_MAPPED_SUBRESOURCE mapped = {0};
HRESULT hr = m_spDX11DeviceContext->Map(m_spRenderTexture.Get(),0,D3D11_MAP_READ,0,&mapped);
void* buffer = ::CoTaskMemAlloc(176 * 144 * 3);
memcpy(buffer, mapped.pData,176 * 144 * 3);
//unmap so we can copy during next update.
m_spDX11DeviceContext->Unmap(m_spRenderTexture.Get(),0);
现在,在我的系统上,OnTimer() 函数不会失败。视频帧渲染到纹理,像素数据成功复制到内存缓冲区。
在查看是否有进一步的问题之前,也许这是一个很好的时机,看看你是否能取得和我目前一样的进步。如果您对此答案发表评论并提供更多信息,我将编辑答案以尽可能添加更多帮助。
编辑
FramePlayer::CreateBackBuffers() 中对纹理描述的更改
//make first texture cpu readable
D3D11_TEXTURE2D_DESC texDesc = {0};
texDesc.Width = 176;
texDesc.Height = 144;
texDesc.MipLevels = 1;
texDesc.ArraySize = 1;
texDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
texDesc.SampleDesc.Count = 1;
texDesc.SampleDesc.Quality = 0;
texDesc.Usage = D3D11_USAGE_STAGING;
texDesc.BindFlags = 0;
texDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE;
texDesc.MiscFlags = 0;
MEDIA::ThrowIfFailed(m_spDX11Device->CreateTexture2D(&texDesc,NULL,&m_spRenderTexture));
另请注意,有时需要清除内存泄漏(我相信您知道) - 在下一行中分配的内存永远不会被释放:
void* buffer = ::CoTaskMemAlloc(176 * 144 * 3); // sizes changed for my test
成功
我现在已经成功保存了一个单独的帧,但现在没有使用复制纹理。
首先,我下载了最新版本的the DirectXTex Library,它提供了DX11纹理辅助功能,例如从纹理中提取图像并保存到文件。 The instructions 将 DirectXTex 库添加到您的解决方案中作为现有项目需要仔细遵循,并注意 Windows 8 应用商店应用所需的更改。
一旦包含、引用并构建了上述库,将以下#include添加到FramePlayer.cpp
#include "..\DirectXTex\DirectXTex.h" // nb - use the relative path you copied to
#include <wincodec.h>
最后,FramePlayer::OnTimer() 中的中心代码部分需要类似于以下内容。你会看到我每次都保存到相同的文件名,所以这需要修改以添加例如名称的帧号
// new frame available at the media engine so get it
ComPtr<ID3D11Texture2D> spTextureDst;
MEDIA::ThrowIfFailed(m_spDX11SwapChain->GetBuffer(0, IID_PPV_ARGS(&spTextureDst)));
auto rcNormalized = MFVideoNormalizedRect();
rcNormalized.left = 0;
rcNormalized.right = 1;
rcNormalized.top = 0;
rcNormalized.bottom = 1;
MEDIA::ThrowIfFailed(m_spMediaEngine->TransferVideoFrame(spTextureDst.Get(), &rcNormalized, &m_rcTarget, &m_bkgColor));
// capture an image from the DX11 texture
DirectX::ScratchImage pImage;
HRESULT hr = DirectX::CaptureTexture(m_spDX11Device.Get(), m_spDX11DeviceContext.Get(), spTextureDst.Get(), pImage);
if (SUCCEEDED(hr))
{
// get the image object from the wrapper
const DirectX::Image *pRealImage = pImage.GetImage(0, 0, 0);
// set some place to save the image frame
StorageFolder ^dataFolder = ApplicationData::Current->LocalFolder;
Platform::String ^szPath = dataFolder->Path + "\\frame.png";
// save the image to file
hr = DirectX::SaveToWICFile(*pRealImage, DirectX::WIC_FLAGS_NONE, GUID_ContainerFormatPng, szPath->Data());
}
// and the present it to the screen
MEDIA::ThrowIfFailed(m_spDX11SwapChain->Present(1, 0));
我现在没有时间再进一步,但我对我目前所取得的成就感到非常满意 :-))
您能否重新审视并更新您在 cmets 中的结果?