使用媒体基金会时如何读取-“unsigned char const *”?

Posted

技术标签:

【中文标题】使用媒体基金会时如何读取-“unsigned char const *”?【英文标题】:How to read from - "unsigned char const *" when use Media Foundation? 【发布时间】:2020-11-23 15:58:24 【问题描述】:

我有这样的实现

void coAudioPlayerSampleGrabber::test(SoundDataType dataType,
    unsigned char const * pData,
    int64_t dataLen)

    HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);

    IMFSourceReader *pReader = NULL;
    IMFByteStream * spByteStream = NULL;

    HRESULT hr = S_OK;
    // Initialize the COM library.
    hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);

    // Initialize the Media Foundation platform.
    if (SUCCEEDED(hr))
    
        hr = MFStartup(MF_VERSION);
    

    hr = MFCreateMFByteStreamOnStreamEx((IUnknown*)pData, &spByteStream);

    if (FAILED(hr))
    
        printf("Error MFCreateMFByteStreamOnStreamEx");
    

    IMFAttributes * Atrr = NULL;
    hr = MFCreateAttributes(&Atrr, 10);

    if (FAILED(hr))
    
        printf("Error MFCreateAttributes");
    

    hr = Atrr->SetUINT32(MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS, true);

    if (FAILED(hr))
    
        printf("Error Atrr->SetUINT32(MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS, true)");
    

    hr = MFCreateSourceReaderFromByteStream(spByteStream, Atrr, &pReader);

    if (FAILED(hr))
    
        printf("Error MFCreateSourceReaderFromByteStream");
    

    if (FAILED(hr))
    
        printf("Error opening input file");
    

    IMFMediaType *pAudioType = NULL;    // Represents the PCM audio format.
    hr = ConfigureAudiostream(dataType, pReader, &pAudioType);

    if (FAILED(hr))
    
        printf("Error ConfigureAudioStream");
    

    IMFSample *pSample = NULL;
    IMFMediaBuffer *pBuffer = NULL;
    BYTE *pAudioData = NULL;
    DWORD cbBuffer = 0;

    std::vector<SampleData> samples_vec;

    while (true) 
    
        DWORD dwFlags = 0;
        hr = pReader->ReadSample((DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, NULL, &dwFlags, NULL, &pSample);
        
        if (FAILED(hr))  break; 
        if (dwFlags & MF_SOURCE_READERF_CURRENTMEDIATYPECHANGED)
        
            printf("Type change - not supported by WAVE file format.\n");
            break;
        
        if (dwFlags & MF_SOURCE_READERF_ENDOFSTREAM)
        
            printf("End of input file.\n");
            break;
        

        hr = pSample->ConvertToContiguousBuffer(&pBuffer);

        if (FAILED(hr))  break; 

        hr = pBuffer->Lock(&pAudioData, NULL, &cbBuffer);
        if (FAILED(hr))  break; 
        //Do something with the pAudioData which is an array of unsigned chars of lenth cbBuffer

        SampleData tmp;

        tmp.pAudioData = new byte[cbBuffer];
        memcpy(tmp.pAudioData, pAudioData, cbBuffer);
        tmp.cbBuffer = cbBuffer;
        samples_vec.push_back(tmp);

        // Unlock the buffer.
        hr = pBuffer->Unlock();
        pAudioData = NULL;
        if (FAILED(hr))  break; 
    

    SafeRelease(&pReader);
    SafeRelease(&pSample);
    SafeRelease(&pBuffer);
    SafeRelease(&spByteStream);
    SafeRelease(&Atrr);

    // Shut down Media Foundation.
    MFShutdown();
    CoUninitialize();

如您所见,我有指向数据和大小的指针,这实际上是我需要解码的数据。问题是这里

hr = MFCreateMFByteStreamOnStreamEx((IUnknown*)pData, &spByteStream);

我收到一个错误access violation,据我所知,这是因为我尝试将pData 转换为IUnknown*。问题是 - 如何正确转换它?

【问题讨论】:

【参考方案1】:

你不能这样偷工减料:

unsigned char const * pData;
...
hr = MFCreateMFByteStreamOnStreamEx((IUnknown*)pData, &spByteStream);

IUnknown 不是字节的另一个花哨的别名。如文档所述,您应该按字面意思提供表示流的接口指针。

Media Foundation 确实为您提供了从内存字节中读取的方法。您需要为每个文档创建一个真实的流,IStreamIRandomAccessStreamIMFByteStream。还提供 IMFAttributes 您创建的具有适当属性以指定数据类型(否则在文件的情况下是从扩展名或 MIME 类型派生的),然后 Source Reader API 将能够处理内存字节作为媒体文件数据的源,合适的解码器会将音频解码为 PCM 数据(类似于this)。

您可以快速做的事情:CreateStreamOnHGlobal 创建IStream 实现并将您的字节复制到底层缓冲区(请参阅文档)。然后MFCreateMFByteStreamOnStream 将在其上创建一个IMFByteStream wrappr,您可以将此包装器用作MFCreateSourceReaderFromByteStream 参数。

【讨论】:

【参考方案2】:

我曾经使用 VectorStream 类来处理这些东西。有的功能没有实现,但基本思路你应该明白了。

class VectorStream : public IStream

public:

    bool ReadOnly = false;
    ULONG r = 1;
    std::vector<char> d;
    size_t p = 0;
    VectorStream()
    
    


    void Clear()
    
        d.clear();
        p = 0;

    

    // IUnknown
    virtual HRESULT STDMETHODCALLTYPE QueryInterface(
        /* [in] */ REFIID riid,
        /* [iid_is][out] */ _COM_Outptr_ void __RPC_FAR* __RPC_FAR* ppvObject)
    
        if (riid == __uuidof(IUnknown) || riid == __uuidof(IStream))
        
            *ppvObject = (IStream*)this;
            r++;
            return S_OK;
        
        return E_NOINTERFACE;
    

    virtual ULONG STDMETHODCALLTYPE AddRef(void)
    
        return ++r;
    

    virtual ULONG STDMETHODCALLTYPE Release(void)
    
        return --r;
    

    HRESULT __stdcall Clone(
        IStream** ppstm
    )
    
        return E_NOTIMPL;
    

    HRESULT __stdcall Commit(
        DWORD grfCommitFlags
    )
    
        return S_OK;
    

    HRESULT __stdcall CopyTo(
        IStream* pstm,
        ULARGE_INTEGER cb,
        ULARGE_INTEGER* pcbRead,
        ULARGE_INTEGER* pcbWritten
    )
    
        return E_NOINTERFACE;
    

    HRESULT __stdcall LockRegion(
        ULARGE_INTEGER libOffset,
        ULARGE_INTEGER cb,
        DWORD          dwLockType
    )
    
        return S_OK;
    

    HRESULT __stdcall UnlockRegion(
        ULARGE_INTEGER libOffset,
        ULARGE_INTEGER cb,
        DWORD          dwLockType
    )
    
        return S_OK;

    

    HRESULT __stdcall Revert()
    
        return E_NOTIMPL;
    

    HRESULT __stdcall Seek(
        LARGE_INTEGER  dlibMove,
        DWORD          dwOrigin,
        ULARGE_INTEGER* plibNewPosition
    )
    
        LARGE_INTEGER lo =  0 ;
        if (dwOrigin == STREAM_SEEK_SET)
        
            p = dlibMove.QuadPart;
        
        if (dwOrigin == STREAM_SEEK_CUR)
        
            p += dlibMove.QuadPart;
        
        if (dwOrigin == STREAM_SEEK_END)
        
            p = d.size() - dlibMove.QuadPart;
        
        if (p >= d.size())
            p = d.size();
        if (plibNewPosition)
            plibNewPosition->QuadPart = p;
        return S_OK;
    


    HRESULT __stdcall SetSize(
        ULARGE_INTEGER libNewSize
    )
    
        d.resize(libNewSize.QuadPart);
        return S_OK;
    

    int eb = 0;
    HRESULT __stdcall Stat(
        STATSTG* pstatstg,
        DWORD   grfStatFlag
    )
    
        pstatstg->type = STGTY_STREAM;
        pstatstg->cbSize.QuadPart = d.size();
        pstatstg->grfLocksSupported = true;
        return S_OK;
    


    unsigned long long readbytes = 0;
    HRESULT __stdcall Read(
        void* pv,
        ULONG cb,
        ULONG* pcbRead
    )
    
        auto av = d.size() - p;
        if (cb < av)
            av = cb;
        memcpy(pv, d.data() + p, av);
        p += av;
        if (pcbRead)
            *pcbRead = (ULONG)av;
//      if (av < cb)
    //      return S_FALSE;
        return S_OK;

    


    HRESULT __stdcall Write(
        const void* pv,
        ULONG      cb,
        ULONG* pcbWritten
    )
    
        if (ReadOnly)
            return STG_E_ACCESSDENIED;
        if (d.size() < (p + cb))
        
            auto exc = (p + cb) - d.size();
            d.resize(d.size() + exc);
        
        memcpy(d.data() + p, pv, cb);
        p += cb;
        if (pcbWritten)
            *pcbWritten = cb;
        return S_OK;
    
;

它将 std::vector 封装在 IStream 中。

【讨论】:

以上是关于使用媒体基金会时如何读取-“unsigned char const *”?的主要内容,如果未能解决你的问题,请参考以下文章

媒体基金会:第 6 次循环视频不稳定

UWP,媒体基金会,选择特定编码器

媒体基金会 AMR 解码

媒体基金会音频/视频捕获到 MPEG4FileSink 产生不正确的持续时间

如何使用媒体键读取 USB 键盘的键码

媒体基金会截图挂钩