我尝试播放"加密"快速实现的电影(在性能方面)我更喜欢使用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.
};
答案 0 :(得分:0)
所以你的电影是加密的。
所以你必须解密&#34; ByteStream&#34;在你的IMFByteStream实现中。
如果您返回加密的&#34; ByteStream&#34;就像你在这里做的那样,谁会解密它?
Mediafoundation不提供此类内容,也不提供IMFByteStream实现。并且SourceResolver无法处理加密的&#34; ByteStream&#34; (除了MediaFoundation Source能够这样做的事实,但默认情况下,它并不存在,据我所知)。
您只需要在IMFByteStream实现中添加解密,并返回解密的&#34; ByteStream&#34;。
那说,加密文件类型是什么? (DRM?整个文件(zip)?等......?),答案可能不同。