というわけで、今回から実装に入ります。といっても今回の部分では簡単な部分を見ていきます。
なお、これ以降クラスメンバ変数として必要となる変数はすべてm_をつけていきなり出します。本来はクラスメンバとして必要なものなので
前回のprivateの宣言部に追加していってください。・・・これまでのDirectShowの説明でも似たような部分はありましたが・・・。
コンストラクタ
IDirect3D9 *m_lpDirect3D; IDirect3DDevice9 *m_lpDirect3DDevice; CVMRSurfaceAllocator::CVMRSurfaceAllocator(IDirect3DDevice9 *lpDirect3DDevice,HRESULT *lphr) : CUnknown(NAME("VMRSurfaceAllocator"),NULL), m_lpDirect3D(NULL), m_lpDirect3DDevice(lpDirect3DDevice), ... (a) //必要な初期化構文がこの場所に来る { HRESULT hr = S_OK; if(lpDirect3DDevice != NULL){ lpDirect3DDevice->GetDirect3D(&m_lpDirect3D); lpDirect3DDevice->AddRef(); } else{ hr = E_POINTER; } if(lphr != NULL) *lphr = hr; }
・・・わかっています。変数の初期化の部分を完全になくしていますの必要な変数はすべてちゃんとゼロ初期化(NULL初期化)を行ってください。
コンストラクタの動作は内部変数の初期化が主です。Direct3D9との関連性の関係上、IDirect3D9とIDirect3DDevice9をSurfaceAllocator側でも所有状態とします。
(GetDirect3DとAddRefがその動作)
デストラクタ
CVMRSurfaceAllocator::~CVMRSurfaceAllocator() { ... (b) //これ以外にも解放されるオブジェクトがあるがそれは後述する if(m_lpDirect3DDevice != NULL){ m_lpDirect3DDevice->Release(); m_lpDirect3DDevice = NULL; } if(m_lpDirect3D != NULL){ m_lpDirect3D->Release(); m_lpDirect3D = NULL; } }
内部オブジェクトのうち、Direct3Dに関わる要素をアロケータが解放される瞬間に解放して終了します。
DirectShowに関わる部分は主にTerminateDeviceなどで解放します。
QueryInterface(NonDelegatingQueryInterface)の実装
STDMETHODIMP CVMRSurfaceAllocator::NonDelegatingQueryInterface(REFIID refiid,void **lplpInterface) { if(IsEqualIID(refiid,IID_IVMRSurfaceAllocator9)) return GetInterface(static_cast<IVMRSurfaceAllocator9 *>(this),lplpInterface); else if(IsEqualIID(refiid,IID_IVMRImagePresenter9)) return GetInterface(static_cast<IVMRImagePresenter9 *>(this),lplpInterface); else return CUnknown::NonDelegatingQueryInterface(refiid,lplpInterface); }
見ればわかりますが、IStreamの実装でやった実装と類似すると思います。
今回は実装しているインターフェイスがIVMRSurfaceAllocator9とIVMRImagePresenter9の二つになっているのでそのいずれでも自分自身をキャストして返しています。
AdviseNotifyの実装
IVMRSurfaceAllocatorNotify9 *m_lpSurfaceAllocNotify; STDMETHODIMP CVMRSurfaceAllocator::AdviseNotify(IVMRSurfaceAllocatorNotify9 *lpVMRSurfaceAllocNotify) { HMONITOR hMonitor; D3DCAPS9 d3dCaps; m_lpSurfaceAllocNotify = lpVMRSurfaceAllocNotify; m_lpDirect3DDevice->GetDeviceCaps(&d3dCaps); hMonitor = m_lpDirect3D->GetAdapterMonitor(d3dCaps.AdapterOrdinal); return m_lpSurfaceAllocNotify->SetD3DDevice(m_lpDirect3DDevice,hMonitor); }
VMR9から渡されるSurfaceAllocatorNotifyと自分自身を関連づける動作で、IVMRSurfaceAllocator9で実装すべき処理です。
処理の要点は
- IVMRSurfaceAllocatorNotify9を自分自身に設定する(参照カウントをあげると自己参照に陥るのであげない方がいい)
- IVMRSurfaceAllocatorNotify9のSetD3DDeviceを呼び出して、描画に使用するDirect3DDevice(とモニタハンドル)を設定する
です。なお、モニタハンドルを取得するときのGetAdapterMonitorですが、Direct3DDeviceをD3DADAPTER_DEFAULTで初期化しているときはそちらを渡してください。
ちなみに、この処理があるため、コンストラクタでIDirect3DDevice9を渡して初期化してAdviceNotifyを設定するという順序をとっています。
また、受け取ったIVMRSurfaceAllocatorNotify9は常にVMR9のフィルタ内で有効となるので参照カウントはあげなくてもいいです。
というかあげると、処理の状態によっては自己参照(VMR9(IVMRSurfaceAllocatorNotify9)がCVMRSurfaceAllocatorの参照を持ち、
CVMRSurfaceAllocatorがIVMRSurfaceAllocatorNotify9の参照を持つという状態)になるので
それぞれのオブジェクトが解放できなくなってしまいます。注意しましょう。
StartPresenting,StopPresentingの実装
STDMETHODIMP CVMRSurfaceAllocator::StartPresenting(DWORD_PTR dwUserID) { if(m_lpDirect3DDevice != NULL) return E_FAIL; return S_OK; } STDMETHODIMP CVMRSurfaceAllocator::StopPresenting(DWORD_PTR dwUserID) { return S_OK; }
本来ならdwUserID(VMR9内部での識別子)を判別して・・・とかいるんですが、今回オブジェクトは一つだけという保証の中で行いますので無視します。
今回の処理ではこの2つの処理で行うことはありません。せいぜいオブジェクトがちゃんとあるかどうかを確認する程度です。
というわけで簡単なものを実装してみました。
これ以降が大変なんです・・・。
・・・で、本当は今日この記事書く気がなかったんですが・・・。