使用 Media Foundation 时如何读取 - "unsigned char const *"?

How to read from - "unsigned char const *" when use Media Foundation?

我有这样一个实现

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*。问题是 - 如何正确转换?

你不能这样偷工减料:

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

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

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

您可以快速完成的操作:CreateStreamOnHGlobal to create IStream implementation and copy your bytes into underlying buffer (see docs). Then MFCreateMFByteStreamOnStream 会在其上创建一个 IMFByteStream 包装器,您可以将此包装器用作 MFCreateSourceReaderFromByteStream 参数。

我曾经使用 VectorStream class 处理此类内容。部分功能没有实现,但你应该了解了基本思路。

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中。