video - 有没有一种简单有效的方法来读取文件并将它们转换为指定的 DXGI_Format 并解码输出大小?
问题描述
我异步读取了一个mp4文件(H264 encodec),并且我之前已经配置了相关参数,但是当我最终通过CopyResource将样本传递给目标纹理共享时,它总是失败如下。
D3D11 ERROR: ID3D11DeviceContext::CopyResource: Cannot invoke CopyResource when the Formats of each Resource are not the same or at least castable to each other, unless one format is compressed (DXGI_FORMAT_R9G9B9E5_SHAREDEXP, or DXGI_FORMAT_BC[1,2,3,4,5]_* ) and the source format is similar to the dest according to: BC[1|4] ~= R16G16B16A16|R32G32, BC[2|3|5] ~= R32G32B32A32, R9G9B9E5_SHAREDEXP ~= R32. [ RESOURCE_MANIPULATION ERROR #284: COPYRESOURCE_INVALIDSOURCE]
如果我指定MF_MT_SUBTYPE/MF_MT_FRAME_SIZE/MF_MT_FRAME_RATE等等,这些真的能生效吗?还是必须一步一步实现CResizerDMO、CColorConvertDMO、CFrameRateConvertDMO等?我看到 IMFSinkWriter 似乎只要配置好这些参数就可以达到目的。
HRESULT CAsyncFileReader::StartAsyncRead(PCWSTR pszURL)
{
CComPtr<IMFAttributes> pAttributes = NULL;
HRESULT hr = MFCreateAttributes(&pAttributes, 5);
RETURN_ON_FAIL(hr);
hr = pAttributes->SetUnknown(MF_SOURCE_READER_ASYNC_CALLBACK, this);
RETURN_ON_FAIL(hr);
hr = pAttributes->SetUnknown(MF_SOURCE_READER_D3D_MANAGER, _dxgi_device_manager);
RETURN_ON_FAIL(hr);
hr = pAttributes->SetUINT32(MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS, TRUE);
RETURN_ON_FAIL(hr);
hr = pAttributes->SetUINT32(MF_LOW_LATENCY, 1);
RETURN_ON_FAIL(hr);
//MF_SOURCE_READER_ENABLE_VIDEO_PROCESSING (version < win8)
hr = pAttributes->SetUINT32(MF_SOURCE_READER_ENABLE_ADVANCED_VIDEO_PROCESSING, TRUE);
RETURN_ON_FAIL(hr);
hr = MFCreateSourceReaderFromURL(pszURL, pAttributes, &m_pReader);
RETURN_ON_FAIL(hr);
CComPtr<IMFMediaType> pType = NULL;
hr = m_pReader->GetNativeMediaType((DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, 0, &pType);
RETURN_ON_FAIL(hr);
hr = pType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32);
MFSetAttributeSize(pType, MF_MT_FRAME_SIZE, Capture_Width, Capture_Height);
hr = m_pReader->SetCurrentMediaType((DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, NULL, pType);
RETURN_ON_FAIL(hr);
...
}
//called by OnReadSample callback
HRESULT CAsyncFileReader::Copy2Texture(IMFSample* sample)
{
CComPtr<IMFMediaBuffer> buffer;
HRESULT hr = sample->GetBufferByIndex(0, &buffer);
if (SUCCEEDED(hr))
{
CComPtr<IMFDXGIBuffer> dxgiBuffer;
hr = buffer->QueryInterface(IID_PPV_ARGS(&dxgiBuffer));
if (SUCCEEDED(hr))
{
CComPtr<ID3D11Texture2D> texture;
hr = dxgiBuffer->GetResource(IID_PPV_ARGS(&texture));
if (SUCCEEDED(hr))
{
// unsigned int subresource = 0;
// hr = dxgiBuffer->GetSubresourceIndex(&subresource);
if (SUCCEEDED(hr))
{
CComPtr<ID3D11DeviceContext> immediate_context;
_d3d_device->GetImmediateContext(&immediate_context);
//tbd, AcquireSync need restore if failed under certain conditions
UINT64 key = AllocKey();
hr = _dxgi_keyed_mutex->AcquireSync(key, 10);
RETURN_ON_FAIL(hr);
immediate_context->CopyResource(_shared_texture2d, texture); <-------mftrace as above
//immediate_context->CopySubresourceRegion(_shared_texture2d, 0, 0, 0, 0, texture, subresource, nullptr);
hr = _dxgi_keyed_mutex->ReleaseSync(key + 1);
RETURN_ON_FAIL(hr);
}
}
}
}
return hr;
}
HRESULT CreateSharedD3D11Texture2D(ID3D11Device* d3d11_device, const UINT32& w, const UINT32& h, ID3D11Texture2D** d3d11_texture_2d)
{
D3D11_TEXTURE2D_DESC texDesc;
texDesc.ArraySize = 1;
texDesc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
texDesc.CPUAccessFlags = 0;
texDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
texDesc.Width = w;
texDesc.Height = h;
texDesc.MipLevels = 1;
texDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX;
texDesc.Usage = D3D11_USAGE_DEFAULT;
texDesc.SampleDesc.Count = 1;
texDesc.SampleDesc.Quality = 0;
texDesc.Usage = D3D11_USAGE_DEFAULT;
return d3d11_device->CreateTexture2D(&texDesc, nullptr, d3d11_texture_2d);
}
另外,纹理的DXGI_FORMAT_B8G8R8A8_UNORM相当于MFVideoFormat_RGB32吧?
解决方案
对于与主题一样广泛的问题,答案是“否”。
如果您可以将其缩小到DXGI_FORMAT_B8G8R8A8_UNORM
使用 Media Foundation Source Reader API 进行格式转换和拟合的结果,那么存在这样的解决方案。
各自的媒体基础格式是MFVideoFormat_RGB32
和MFVideoFormat_ARGB32
.
在您的代码片段中,您GetNativeMediaType
和您SetCurrentMediaType
的格式完全相同。相反,您需要在这些调用之间构建新的 RGB32/ARGB32 媒体类型,并改为设置这种新格式。您可能需要更新Source Reader 属性以启用转换。Source Reader API 将在内部管理必要的原语(转换),以便为您提供所请求格式的数据。
或者,您可以以“本机”格式提取数据并自己管理解码器/转换器,这几乎是相同的,只是在您弄清楚如何通过转换链实现数据流之前需要相当长的时间。
推荐阅读
- javascript - 如何在类组件中使用前向引用?
- java - 如何访问Java jar 文件夹中的多个资源图像?
- go - 带有一个 go-exiftool 实例的循环挂在大量文件上
- javascript - appendBuffer() 不追加到 sourceBuffer
- javascript - 嵌套动态表单 Antd
- c++ - 无法在 Windows 上使用 mingw 将 sqlite3 与 c 连接
- spring-webclient - io.netty.channel.unix.Errors$NativeEceptionIoException: readAddress failed: Connection reset by peer 请求将被重试
- docker - 将 Docker 镜像推送到 Container Registry
- c - 使用套接字在客户端/服务器之间传输数据
- r - 数据框长到宽格式