首页 > 解决方案 > 有没有一种简单有效的方法来读取文件并将它们转换为指定的 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吧?

标签: videodirect3dms-media-foundationdxgi

解决方案


对于与主题一样广泛的问题,答案是“否”。

如果您可以将其缩小到DXGI_FORMAT_B8G8R8A8_UNORM使用 Media Foundation Source Reader API 进行格式转换和拟合的结果,那么存在这样的解决方案。

各自的媒体基础格式是MFVideoFormat_RGB32MFVideoFormat_ARGB32.

在您的代码片段中,您GetNativeMediaType和您SetCurrentMediaType的格式完全相同。相反,您需要在这些调用之间构建新的 RGB32/ARGB32 媒体类型,并改为设置这种新格式。您可能需要更新Source Reader 属性以启用转换。Source Reader API 将在内部管理必要的原语(转换),以便为您提供所请求格式的数据。

或者,您可以以“本机”格式提取数据并自己管理解码器/转换器,这几乎是相同的,只是在您弄清楚如何通过转换链实现数据流之前需要相当长的时间。


推荐阅读