Microsoft DirectX 8.0

C/C++ による変換フィルタの開発

変換フィルタは、メディア入力を受け取り、それに何らかの変更を加える。変換フィルタを設計する場合、フィルタ クラスはいずれかの変換基底クラス (CTransformFilterCTransInPlaceFilter、または CVideoTransformFilter) から派生するか、より一般的な CBaseFilter クラスから派生する。どの基底クラスを選択するかは、フィルタがメディア サンプルをコピーする必要があるか、あるいはそのまま変換できるかによって決まる。詳細については、「使用する基底クラスを決める」を参照すること。

フィルタ グラフ マネージャは、フィルタの派生元の基底クラスの関数を使用して、フィルタ グラフにフィルタを挿入し、フィルタ間の接続を自動的に作成する。フィルタ マッパーは、フィルタのレジストリ情報を使用してフィルタ グラフを設定する。

最も単純な変換フィルタ (たとえば、入力ピン出力ピンが 1 つずつしかないフィルタ) の場合、フィルタ クラスを CTransformFilter から派生して、Transform 関数と CheckInputType 関数だけをオーバーライドすればよい。カスタム機能が必要であれば、他の関数をオーバーライドして独自の接続、ピン、および他のフィルタ機能を作成できる。詳細については、「基底クラス メンバ関数をオーバーライドする」を参照すること。また、フィルタ クラスを CBaseFilter から派生して、そのメソッドをオーバーライドすることもできる。

このセクションでは、以下の方法について説明する。

すべての変換フィルタは、追加インターフェイスへのアクセスを除き、上記のステップをすべて実行するコードを実装しなければならない。

変換フィルタの背景情報については、以下のトピックを参照すること。

フィルタの登録と自己登録化については、「DirectShow オブジェクトの登録方法」を参照すること。

フィルタ クラスの定義とインスタンス化

フィルタ クラスを定義してインスタンス化するための手順を以下に示す。

  1. フィルタ クラス (および必要に応じてピン クラス) を派生する基底クラスを決定する。通常、変換フィルタ クラスは変換基底クラスである CTransformFilterCTransInPlaceFilter、または CVideoTransformFilter から派生するか、または、より一般的な CBaseFilter クラスから派生する。ビデオ メディア (特に AVI データ) を変換したい場合は、CVideoTransformFilter から派生する。フィルタが入力メディア サンプルをコピーする必要がある場合は、CTransformFilter から派生する。フィルタがメディア サンプルをそのまま変換できる場合は、CTransInPlaceFilter から派生する。変換基底クラスで提供される単純な変換フィルタ サポートの代わりに、独自のメンバ関数を実装したい場合は、CBaseFilter から派生する。詳細については、「使用する基底クラスを決める」を参照すること。

    次の例では、フィルタ クラスを CTransInPlaceFilter から派生している。

        class CMyFilter  :public CTransInPlaceFilter
    
  2. オブジェクトに対して IUnknown インターフェイスを実装する。

    フィルタ クラス定義のパブリック セクションで CUnknown のインスタンスを作成してから、DECLARE_IUNKNOWN マクロを呼び出す。

    public:
            static CUnknown *WINAPI CreateInstance(LPUNKNOWN punk, HRESULT 
        *phr);
    DECLARE_IUNKNOWN;
    
  3. コンストラクタを定義する。また、Transform 関数と CheckInputType 関数を定義する (フィルタ クラスが CBaseFilter から派生している場合、これらの関数は定義しない)。

    フィルタ クラス定義のプライベート セクションで、コンストラクタを定義する。これは、派生元の変換フィルタ クラスのコンストラクタを呼び出すことで行う。次に、変換を実行して入力タイプをチェックするコードを追加する。次に例を示す。

        // CTransInPlaceFilter のコンストラクタを呼び出すことにより、
        // コンストラクタを定義する。
        CMyFilter(TCHAR *tszName, LPUNKNOWN punk, HRESULT *phr)
        : CTransInPlaceFilter (tszName, punk, CLSID_MyFilter, phr)
        { }
    
        // 変換コードを追加する。
        HRESULT Transform(IMediaSample *pSample){ 
        // ここに変換コードが入る。
        }
    
        // 入力タイプをチェックするコードを追加する。
        HRESULT CheckInputType(const CMediaType* mtIn) { 
        // ここに入力チェック コードが入る。
        }
    
  4. フィルタ オブジェクトに対して CreateInstance を実装する。通常、CreateInstance はフィルタ クラスのコンストラクタを呼び出す。次に例を示す。
        CUnknown * WINAPI CMyFilter::CreateInstance(LPUNKNOWN punk, HRESULT *phr) {
        CMyFilter *pNewObject = new CMyFilter(NAME("Description of My Filter"), 
            punk, phr );
    if (pNewObject == NULL) {
    *phr = E_OUTOFMEMORY;
        }
    return pNewObject;
        } 
    
  5. CFactoryTemplate オブジェクトのグローバル配列 g_Templates を宣言する。これは、CreateInstance 関数へのアクセス方法をデフォルトのクラス ファクトリ コードに通知するために使用される。
        CFactoryTemplate g_Templates[]=
            {   { L"My Filter"          
                , &CLSID_MyFilter
                , CMyFilter::CreateInstance  // クラス ファクトリによって呼び出される関数
                , NULL
                , &sudMyFilter }     // AMOVIESETUP_FILTER 構造体のアドレス。
                                     //   構造体が存在しない場合は NULL。
        };
    int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]);
    

    g_cTemplates 変数は、フィルタに対するクラス ファクトリ テンプレート (CFactoryTemplate) の数を定義する。各テンプレートは COM とフィルタの間のリンクを提供し、フィルタのベース オブジェクトを作成するために使用される。フィルタには最低でも 1 つのテンプレートがある。これは、フィルタ独自の CreateInstance 関数のアドレスを提供する。この関数は、呼び出されるとベース オブジェクトを作成する。

    CFactoryTemplate テンプレートにパラメータを追加することで、プロパティ ページを追加できる。登録時の CFactoryTemplate の使用法については、「DirectShow オブジェクトの登録方法」を参照すること。

  6. フィルタ オブジェクトの GUID を生成する。

    GUID の生成に関する一般的な情報については、Platform SDK の "GUID の作成と最適化" および "uuidgen ユーティリティ" を参照すること。

    Microsoft® Visual C++® 5.x で GUID を生成するには、[ツール] メニューから [Create GUID] を選択する。GUID のデフォルトのフォーマットは DEFINE_GUID であり、これをそのまま使用する。[コピー] ボタンをクリックする。ソース ファイルでインクルード文の下にカーソルを置き、[編集] メニューの [貼り付け] を選択する。次の例に示すようなコードが挿入される。ただし、番号と CLSID は固有のものが入る。ヘッダー ファイルまたはメイン ファイルのクラス定義の前にコードを挿入する。

        // {3FA5D260-AF2F-11d0-AE9C-00A0C91F0841}
        DEFINE_GUID(CLSID_MyFilter, 
        0x3fa5d260, 0xaf2f, 0x11d0, 0xae, 0x9c, 0x0, 0xa0, 0xc9, 0x1f, 0x8, 0x41);
    

CheckInputType のオーバーライド

フィルタに対して指定された入力が正しいかどうか確認するために、CheckInputType 関数をオーバーライドする (これは CBaseFilter から派生したフィルタ クラスには適用されない)。実装では、サポートできないメディア タイプに対してエラーを返す必要がある。フィルタがサポートするメディア タイプは、AMOVIESETUP_MEDIATYPE 構造体にリストされる。次に例を示す。

    HRESULT CMyFilter::CheckInputType(const CMediaType *pmt)
    {
       if (pmt->majortype != MEDIATYPE_Video) {
           return S_FALSE;
       } 
           else return S_OK;
       }

Transform 関数のオーバーライド

入力メディアに対して必要な変換を実行するために、変換基底クラスの Transform 関数をオーバーライドするか、または独自の変換関数を実装する必要がある (これは CBaseFilter から派生したフィルタ クラスには適用されない)。

たとえば、コントラスト サンプルの以下のコードについて考える。CContrast::Transform 関数を次のようにオーバーライドする。

    HRESULT CContrast::Transform(IMediaSample *pIn, IMediaSample *pOut)
    {
            HRESULT hr = Copy(pIn, pOut);
        if (FAILED(hr)) {
        return hr;
        }
            return Transform(pOut);

    }

最初の CContrast::Transform 関数はメディア データをコピーして、(pOut パラメータが指す) そのコピーを次の Transform 関数に渡す。コントラスト サンプルの最初の Transform 関数はオーバーロードされた関数であり、2 番目の形の Transform 関数は入力メディアのコピーに対してインプレイス変換を行う。これを次のコードに示す。

    HRESULT CContrast::Transform(IMediaSample *pMediaSample)
    {
        signed char ContrastLevel;
        ContrastLevel = m_ContrastLevel;    
        AM_MEDIA_TYPE *pAdjustedType = NULL;

        pMediaSample->GetMediaType(&pAdjustedType);
        HRESULT hr = Transform(&AdjustedType, ContrastLevel);
        pMediaSample->SetMediaType(&AdjustedType);
return NOERROR;
    }

2 番目の形のオーバーロードされた Transform 関数は、さらに 3 番目の形のオーバーロードされた Transform 関数を呼び出すことに注意。

追加インターフェイスへのアクセス

フィルタが基底クラスに実装されていないインターフェイスを実装する場合は、NonDelegatingQueryInterface 関数をオーバーライドして、実装されたインターフェイスへのポインタを返す必要がある。

  1. フィルタ クラス定義のパブリック セクションで、NonDelegatingQueryInterface を宣言する。
    STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void **ppv)
    
  2. クラスの実装セクションで、NonDelegatingQueryInterface 関数を実装する。次に例を示す。
        // 永続ストリームとプロパティ ページを公開する。
        STDMETHODIMP CMyFilter::NonDelegatingQueryInterface(REFIID riid, void **ppv)
        {
            if (riid == IID_IPersistStream) {
              AddRef( );   // 参照カウントを追加する。終了後に解放すること。
              *ppv = (void *)(IPersistStream *)this;
              return NOERROR;
           }
           } else if (riid == IID_ISpecifyPropertyPages) {
    return GetInterface((ISpecifyPropertyPages *) this, ppv);
           }  
           else {
    return CTransInPlaceFilter::NonDelegatingQueryInterface(riid, ppv);
           }
        }
    

レジストリ情報の作成

フィルタ グラフ マネージャは、フィルタのレジストリ エントリを使用して、フィルタとその接続を設定する。フィルタのレジストリ情報は、AMOVIESETUP_MEDIATYPEAMOVIESETUP_PIN、および AMOVIESETUP_FILTER 構造体に提供する。通常、これらの構造体はフィルタ実装コードの先頭に位置する。これらの構造体の使用法の詳細については、「DirectShow オブジェクトの登録方法」を参照すること。

フィルタの登録に必要な 3 つの構造体を提供するための手順を以下に示す。

  1. AMOVIESETUP_MEDIATYPE 構造体を提供する。この構造体は、フィルタがサポートするメディア タイプに関するレジストリ情報を保持する。次に例を示す。
        const AMOVIESETUP_MEDIATYPE sudPinTypes = 
                        { &MEDIATYPE_Video             // メジャー タイプ
                        , &MEDIASUBTYPE_NULL}  ;       // マイナー タイプ
    

    メジャー タイプは、MEDIATYPE_Stream、MEDIATYPE_Video、MEDIATYPE_Audio のいずれかである。

  2. Provide the AMOVIESETUP_PIN 構造体を提供する。この構造体は、フィルタがサポートするピンに関するレジストリ情報を保持する。
  3. AMOVIESETUP_FILTER 構造体を提供する。この構造体は、フィルタ オブジェクトに関するレジストリ情報 (CLSID、説明、ピン数、ピン構造体の名前、フィルタのメリット) を保持する。メリット は、フィルタ グラフ マネージャがフィルタにアクセスする順序を制御する。メリットの値は MERIT_PREFERRED、MERIT_NORMAL、MERIT_UNLIKELY、MERIT_DO_NOT_USE のいずれかである。メリット値の詳細については、「IFilterMapper::RegisterFilter」を参照すること。次のコードは、AMOVIESETUP_FILTER 構造体の例を示す。
        const AMOVIESETUP_FILTER
        sudMyFilter = { &CLSID_MyFilter         // clsID
                    , L"My Filter Description"  // strName
                    , MERIT_UNLIKELY            // dwMerit
                    , 2                         // nPins
                    , sudpPins };               // lpPin