2010年7月29日木曜日

実行ファイルに内蔵したEVRカスタムプレゼンタを使うには

通常、EVR Custom Presenter (カスタムプレゼンタ)はcoclassとして実装し、DLL としてコンパイルする。この方法はバージョンアップが容易であったり、コンパイル無しに複数のアプリケーションで再利用できたりするメリットがある。しかし、配布ファイル数が多くなる、他のアプリケーションから参照されるといったデメリットもあり、プライベートで使用するのであれば DLL ではなく実行ファイル(.EXE)に内蔵することを検討したい。

Media Foundation ではActivation Object (アクティベーションオブジェクト) を実装することにより、実行ファイルに EVR Custom Presenter を内蔵することができる。

Activation Object を実装する

Activation ObjectとはCOMにおけるクラスファクトリに似ている。IMFActivateインターフェイスを実装したクラスであり、coclass のインスタンスを作成したり、シャットダウンする役割がある。

もっとも重要なのはIMFActivate::ActivateObjectで、CoCreateInstanceのような役割がある。インスタンスを作成し、そのインターフェイスのポインタを返すメソッドである。

EVR Custom Presenter のクラスをCEvrPresとすると、このインスタンスを作成するためのIMFActivate::ActivateObjectは以下のように実装できる。

IMFActivate::ActivateObject
CComPtr<CEvrPres> m_CustomPres; // メンバ変数
STDMETHODIMP ActivateObject(REFIID riid, void **ppv) {
    if (riid != __uuidof(IMFVideoPresenter)) {
        return MF_E_CANNOT_CREATE_SINK;
    }
    if (ppv == NULL) {
        return E_POINTER;
    }
    if (m_CustomPres != NULL) {
        CComQIPtr<IMFVideoPresenter> mfvp(m_CustomPres);
        *ppv = mfvp;
        mfvp.p->AddRef();
        return S_OK;
    }
    CComObject<CEvrPres> * object;
    CComObject<CEvrPres>::CreateInstance(&object);
    m_CustomPres.Attach(object);
    m_CustomPres.p->AddRef();
    CComQIPtr<IMFVideoPresenter> mfvp(object);
    if (mfvp == NULL) {
        return MF_E_CANNOT_CREATE_SINK;
    }
    *ppv = mfvp;
    mfvp.p->AddRef();
    return S_OK;
}

このコードの流れをざっと説明すると、はじめに、引数riid__uuidof(IMFVideoPresenter)かどうか確認する。EVR Custom Presenter の Activation Object に対してこのメソッドが呼ばれるとき IMFVideoPresenter インターフェイスが要求される。そして、CEvrPresクラスのインスタンスを作成し、IMFVideoPresenterインターフェイスを問い合わせる(QueryInterface)。最後に、戻り値ppvに代入して完了。

CoCreateInstanceIMFActivate::ActivateObjectの違いは、作成したインスタンスを Activation Object が記憶していることだ。これはIMFActivate::ActivateObjectを再度呼び出したとき、IMFActivate::ShutdownIMFActivate::DetachObjectのいずれかが呼ばれるまで同じインスタンスを示すポインタを返す必要があるからだ。

After the first call to ActivateObject, subsequent calls return a pointer to the same instance, until the client calls either ShutdownObject or IMFActivate::DetachObject.

IMFActivate::ActivateObject Method

EVR Custom Presenter の Activation Object を EVR に設定する

EVR の Activation Object を作成するAPIはMFCreateVideoRendererActivateである。これで作成した Activation Object の MF_ACTIVATE_CUSTOM_VIDEO_PRESENTER_ACTIVATE 属性に EVR Custom Presenter の Activation Object を設定する。

Activation Object を設定
CComPtr<IMFActivate> &activate;
hr = MFCreateVideoRendererActivate(m_hWnd, &activate);
CComObject<CMyActivateObj> * my_activate_obj;
CComObject<CMyActivateObj>::CreateInstance(&my_activate_obj);
CComPtr<IUnknown> unk(my_activate_obj);
hr = activate->SetUnknown(MF_ACTIVATE_CUSTOM_VIDEO_PRESENTER_ACTIVATE, unk);

あとはTopologyのノードに、この Activation Object を追加すれば、内蔵した EVR Custom Presenter が使われるようになる。