如何正确使用IMFByteStream的实现

时间:2018-03-30 13:21:12

标签: c++ video ms-media-foundation

我尝试播放"加密"快速实现的电影(在性能方面)我更喜欢使用Microsoft Media Foundation。

关键在于,为了播放加密的电影,我需要在读取或写入电影之前对电影执行一些操作,而Windows专门为此函数创建了BeginCreateObjectFromByteStream,旨在从中生成媒体源字节流。

要使用此功能,我必须实现自己的IMFByteStream,它可以读写加密文件。

这就是我试图做的事情:

以下是我的IMFByteStream对象的代码:

#pragma once
#include <Mfidl.h>

#include <Shlwapi.h>
#include <chrono>
#include <fstream>
#include <vector>
#include <thread>
#include <mutex>          // std::mutex

#define FILE_NAME ("C:\\try_file.mp4")



class my_IMFByteStream : public IMFByteStream
{


public:
    my_IMFByteStream()
    {
        file = fopen(FILE_NAME, "r+");

        // init our end position
        end_position = -1;

        // init referance counter
        reference_counter = 1;
    };


    virtual ~my_IMFByteStream() {

    };

    HRESULT STDMETHODCALLTYPE Close()
    {
        fclose(file);
        return 0;
    };


    HRESULT STDMETHODCALLTYPE GetCapabilities(DWORD *pdwCapabilities)
    {
        *pdwCapabilities = MFBYTESTREAM_IS_READABLE | MFBYTESTREAM_IS_WRITABLE | MFBYTESTREAM_IS_SEEKABLE;
        return S_OK;
    }

    HRESULT STDMETHODCALLTYPE Read(BYTE  *pb, ULONG cb, ULONG *pcbRead)
    {
        *pcbRead = fread(pb, 1, cb, file);

        /* copy and decrypt */
        for (ULONG i = 0; i < *pcbRead; i++)
        {
            pb[i] ^= 0x55;
        }


        return S_OK;
    }


    HRESULT STDMETHODCALLTYPE Write(const BYTE  *pb, ULONG cb, ULONG *pcbWritten)
    {
        BYTE * n_pb = new BYTE[cb];
        for (ULONG i = 0; i < cb; i++)
        {
            n_pb[i] = pb[i] ^ 0x55;
        }

        *pcbWritten = fwrite(n_pb, 1, cb, file);


        delete[] n_pb;


        return S_OK;
    }


    HRESULT STDMETHODCALLTYPE Flush()
    {
        return fflush(file);
    }

    HRESULT STDMETHODCALLTYPE SetCurrentPosition(QWORD qwPosition)
    {
        //qwPosition[in] == New position in the stream, as a byte offset from the start of the stream.
        return fseek(file, qwPosition, SEEK_SET);
    }

    HRESULT STDMETHODCALLTYPE GetCurrentPosition(QWORD *pqwPosition)
    {
        *pqwPosition = ftell(file);
        return S_OK;
    }

    HRESULT STDMETHODCALLTYPE BeginRead( BYTE     *pb,   ULONG     cb,   IMFAsyncCallback *pCallback,    IUnknown     *punkState)
    {
        // must be implemented with a thread 
        std::thread([=] { inner_async_read(pb, cb, pCallback, punkState); });       
        return S_OK;
    }

    HRESULT STDMETHODCALLTYPE BeginWrite(const BYTE  *pb,     ULONG     cb,     IMFAsyncCallback *pCallback,    IUnknown    *punkState)
    {
        // must be implemented with a thread 
        std::thread([=] { inner_async_write(pb, cb, pCallback, punkState); });
        return S_OK;
    }

    HRESULT STDMETHODCALLTYPE GetLength( QWORD *pqwLength)
    {

        // save current position
        long int curr_loc = ftell(file);

        // return file length
        fseek(file, 0, SEEK_END);
        *pqwLength = ftell(file);

        // restore to curr location
        fseek(file, curr_loc, SEEK_SET);

        return S_OK;
    }

    HRESULT STDMETHODCALLTYPE Seek(  MFBYTESTREAM_SEEK_ORIGIN SeekOrigin,     LONGLONG    qwSeekOffset,  DWORD    dwSeekFlags,   QWORD   *pqwCurrentPosition    )
    {
        // not sure what to do with seek flags ....
        switch (SeekOrigin)
        {
        case msoBegin:
        {
            //The seek position is specified relative to the start of the stream.
            fseek(file, qwSeekOffset, SEEK_SET);
            break;
        }
        case msoCurrent:
        {
            //The seek position is specified relative to the current read/write position in the stream.
            fseek(file, qwSeekOffset, SEEK_CUR);
            break;
        }
        }

        *pqwCurrentPosition = ftell(file);

        return S_OK;
    }


    HRESULT STDMETHODCALLTYPE IsEndOfStream(BOOL *pfEndOfStream)
    {
        if (-1 == end_position)
        {
            // init to the real end position

            // save current position
            long int curr_loc = ftell(file);

            // return file length
            fseek(file, 0, SEEK_END);
            end_position = ftell(file);

            // restore to curr location
            fseek(file, curr_loc, SEEK_SET);
        }

        if (ftell(file) == end_position)
        {
            *pfEndOfStream = true;
        }
        else
        {
            *pfEndOfStream = false;
        }

        return S_OK;
    }

    HRESULT STDMETHODCALLTYPE EndRead( IMFAsyncResult *pResult,  ULONG  *pcbRead)
    {
        *pcbRead = actual_read;
        pResult->SetStatus(S_OK);

        return S_OK;
    }

    HRESULT STDMETHODCALLTYPE EndWrite( IMFAsyncResult *pResult, ULONG    *pcbWritten)
    {
        *pcbWritten = actual_read;
        pResult->SetStatus(S_OK);

        return S_OK;
    }


    HRESULT STDMETHODCALLTYPE SetLength( QWORD qwLength)
    {
        QWORD curr_length = 0;
        GetLength(&curr_length);

        if (curr_length > qwLength)
        {
            // can't shrink a file
            return E_FAIL;
        }
        if (curr_length == qwLength)
        {
            // there is nothing to do 
            return S_OK;
        }

        // use lock 
        mtx.lock();

        fseek(file, qwLength, SEEK_SET);
        ULONG garbage = 0;
        const BYTE nada[1] = { "" };
        Write(nada, 1, &garbage);

        // free lock
        mtx.unlock();
        return S_OK;
    }



    ULONG STDMETHODCALLTYPE Release()
    {
        if (reference_counter == 0)
        {
            delete this;
            return 0;
        }

        reference_counter--;
        return reference_counter;
    }

    HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppv)
    {
        // no idea what this code douse ...
        static const QITAB qit[] =
        {
            QITABENT(my_IMFByteStream, IMFByteStream),
            { 0 }
        };
        return QISearch(this, qit, riid, ppv);
    }

    ULONG STDMETHODCALLTYPE AddRef()
    {
        reference_counter++;
        return reference_counter;
    }

protected:
    void inner_async_write( const BYTE  *pb,     ULONG     cb,      IMFAsyncCallback *pCallback,   IUnknown    *punkState)
    {
        // use lock 
        mtx.lock();

        // make the actual write
        actual_write = 0;
        Write(pb, cb, &actual_write);

        // set the results
        ULONG pcbUnclear = 0;
        if (punkState != NULL)
        {
            EndWrite((IMFAsyncResult *)punkState, &pcbUnclear);
        }


        // free lock
        mtx.unlock();

        // call callback "we done"
        pCallback->Invoke((IMFAsyncResult *)punkState);
    }

    void inner_async_read( BYTE  *pb, ULONG  cb, IMFAsyncCallback *pCallback,    IUnknown         *punkState)
    {
        // use lock 
        mtx.lock();

        // make the actual read
        actual_read = 0;
        Read(pb, cb, &actual_read);

        // set the results
        ULONG pcbUnclear = 0;
        if (punkState != NULL)
        {
            EndRead((IMFAsyncResult *)punkState, &pcbUnclear);
        }


        // free lock
        mtx.unlock();

        // call callback "we done"
        pCallback->Invoke((IMFAsyncResult *)punkState);
    }


private:
    FILE* file;
    std::mutex mtx;           // mutex for critical section

    long int end_position;
    ULONG actual_read;
    ULONG actual_write;
    ULONG reference_counter;
};

以下是我尝试使用该对象的方法:

HRESULT CreateMediaSource(PCWSTR sURL, IMFMediaSource **ppSource, CPlayer * our_impl)
{
    MF_OBJECT_TYPE ObjectType = MF_OBJECT_INVALID;

    IMFSourceResolver* pSourceResolver = NULL;
    IUnknown* pSource = NULL;

    // Create the source resolver.
    HRESULT hr = MFCreateSourceResolver(&pSourceResolver);
    if (FAILED(hr))
    {
        goto done;
    }

    // Use the source resolver to create the media source.

    // -> Create my object
    my_IMFByteStream * bytesrc = new my_IMFByteStream();

    // -> Try to use it as my new source instead of the default msdn implementation that commented bellow
    hr = pSourceResolver->BeginCreateObjectFromByteStream(bytesrc, NULL, MF_RESOLUTION_READ, NULL, NULL, our_impl, pSource); // in this line i get exception "Unhandled exception at 0x013C1169"

    //hr = pSourceResolver->CreateObjectFromURL(
    //  sURL,                       // URL of the source.
    //  MF_RESOLUTION_MEDIASOURCE,  // Create a source object.
    //  NULL,                       // Optional property store.
    //  &ObjectType,        // Receives the created object type. 
    //  &pSource            // Receives a pointer to the media source.
    //);
    if (FAILED(hr))
    {
        goto done;
    }

    // Get the IMFMediaSource interface from the media source.
    hr = pSource->QueryInterface(IID_PPV_ARGS(ppSource));

done:
    SafeRelease(&pSourceResolver);
    SafeRelease(&pSource);
    return hr;
}

当我尝试运行此代码时,我获得了经验并且我不太清楚为什么,这里有没有人从这种风格中获得更多经验并可以指导我错误的地方?

P.S。这是MSDN对CPlayer的实现(我现阶段试图在此基础上做一些小改动,将错误隔离到我添加的部分 - 我依靠微软编写的其余代码是准确的)

template <class T> void SafeRelease(T **ppT)
{
    if (*ppT)
    {
        (*ppT)->Release();
        *ppT = NULL;
    }
}

const UINT WM_APP_PLAYER_EVENT = WM_APP + 1;

// WPARAM = IMFMediaEvent*, WPARAM = MediaEventType

enum PlayerState
{
    Closed = 0,     // No session.
    Ready,          // Session was created, ready to open a file. 
    OpenPending,    // Session is opening a file.
    Started,        // Session is playing a file.
    Paused,         // Session is paused.
    Stopped,        // Session is stopped (ready to play). 
    Closing         // Application has closed the session, but is waiting for MESessionClosed.
};

class CPlayer : public IMFAsyncCallback
{
public:
    static HRESULT CreateInstance(HWND hVideo, HWND hEvent, CPlayer **ppPlayer);

    // IUnknown methods
    STDMETHODIMP QueryInterface(REFIID iid, void** ppv);
    STDMETHODIMP_(ULONG) AddRef();
    STDMETHODIMP_(ULONG) Release();

    // IMFAsyncCallback methods
    STDMETHODIMP  GetParameters(DWORD*, DWORD*)
    {
        // Implementation of this method is optional.
        return E_NOTIMPL;
    }
    STDMETHODIMP  Invoke(IMFAsyncResult* pAsyncResult);

    // Playback
    HRESULT       OpenURL(const WCHAR *sURL);
    HRESULT       Play();
    HRESULT       Pause();
    HRESULT       Stop();
    HRESULT       Shutdown();
    HRESULT       HandleEvent(UINT_PTR pUnkPtr);
    PlayerState   GetState() const { return m_state; }

    // Video functionality
    HRESULT       Repaint();
    HRESULT       ResizeVideo(WORD width, WORD height);

    BOOL          HasVideo() const { return (m_pVideoDisplay != NULL); }

protected:

    // Constructor is private. Use static CreateInstance method to instantiate.
    CPlayer(HWND hVideo, HWND hEvent);

    // Destructor is private. Caller should call Release.
    virtual ~CPlayer();

    HRESULT Initialize();
    HRESULT CreateSession();
    HRESULT CloseSession();
    HRESULT StartPlayback();

    // Media event handlers
    virtual HRESULT OnTopologyStatus(IMFMediaEvent *pEvent);
    virtual HRESULT OnPresentationEnded(IMFMediaEvent *pEvent);
    virtual HRESULT OnNewPresentation(IMFMediaEvent *pEvent);

    // Override to handle additional session events.
    virtual HRESULT OnSessionEvent(IMFMediaEvent*, MediaEventType)
    {
        return S_OK;
    }

protected:
    long                    m_nRefCount;        // Reference count.

    IMFMediaSession         *m_pSession;
    IMFMediaSource          *m_pSource;
    IMFVideoDisplayControl  *m_pVideoDisplay;

    HWND                    m_hwndVideo;        // Video window.
    HWND                    m_hwndEvent;        // App window to receive events.
    PlayerState             m_state;            // Current state of the media session.
    HANDLE                  m_hCloseEvent;      // Event to wait on while closing.
};

1 个答案:

答案 0 :(得分:0)

所以你的电影是加密的。

所以你必须解密&#34; ByteStream&#34;在你的IMFByteStream实现中。

如果您返回加密的&#34; ByteStream&#34;就像你在这里做的那样,谁会解密它?

Mediafoundation不提供此类内容,也不提供IMFByteStream实现。并且SourceResolver无法处理加密的&#34; ByteStream&#34; (除了MediaFoundation Source能够这样做的事实,但默认情况下,它并不存在,据我所知)。

您只需要在IMFByteStream实现中添加解密,并返回解密的&#34; ByteStream&#34;。

那说,加密文件类型是什么? (DRM?整个文件(zip)?等......?),答案可能不同。