Microsoft Media Foundation

Last-modified: 2008-09-25 (木) 23:25:31

http://msdn.microsoft.com/en-us/library/ms694197(VS.85).aspx

「Media Foundation」とは?

Microsoft Windows Vista 以降でアプリケーションを開発する際に、お世話になるオーディオ・ビデオなどのコア技術、あるいはライブラリ。
今までの、DirectShow に取って代わる予定。
ハードウェア アクセラレーションを利用できたり、DirectShow から比べて設計が見直され スループットが向上している模様。
ただし、やっぱり COM の知識は多少必要。

Getting Started

同名のセクションが、MSDN 内にあるので、参考にしていただきたい。
http://msdn.microsoft.com/en-us/library/bb250385(VS.85).aspx

ここでは、補足で、SDKを入れるところと、MSDN の同セクションでの間違いを指摘しておく。

SDK

とりあえず、2008年9月現在で、Windows SDK に含まれるので、ダウンロードして、インストールしましょう。
Windows SDK の最新版は、Ver.6.1 です。
英語サイトでしか最新版は手に入らないことが多いので、Google の日本語設定ははずして検索しましょう。
とりあえず、今は、ここ↓
http://www.microsoft.com/downloads/details.aspx?FamilyID=e6e1c3df-a74f-4207-8586-711ebe331cdc&DisplayLang=en

補足

MSDN の「Getting Started」セクションで、必要なヘッダとしてあげられているものに間違いや追加で必要なヘッダが・・・。

  1. 「mferrors.h」ではなく、「mferror.h」です!
  2. 「windows.h」も入れておいてね。
  3. 「dxva2api.h」だけじゃなく、「d3d9.h」も必要よ!

初期化と終了(とヘッダとリンカ)

初期化と終了だけやるサンプルは以下。

#include <windows.h>
#include <d3d9.h>
#include <dxva2api.h>
#include <evr.h>
#include <evr9.h>
#include <mfapi.h>
#include <mferror.h>
#include <mfidl.h>
#include <mfobjects.h>
#include <mftransform.h>
#pragma comment(lib, "dxva2.lib")
#pragma comment(lib, "evr.lib")
#pragma comment(lib, "mf.lib")
#pragma comment(lib, "mfplat.lib")
#pragma comment(lib, "mfuuid.lib")
int main()
{
    HRESULT startupResult = MFStartup(MF_VERSION);
    if (startupResult == MF_E_BAD_STARTUP_VERSION) {
        OutputDebugString(TEXT("the Media Foundation headers does not match the Media Foundation DLLs."));
    }
    MFShutdown();
    return 0;
}

Media Foundation の基本構造

MSDN では、Media Foundation Architecture に解説がある。
Media Foundation には、大きく3つのレイヤーが存在している。

  1. コントロール レイヤー
  2. パイプライン レイヤー
  3. プラットフォーム レイヤー

コントロール レイヤー

こちらは、今のところあまり興味ないので割愛したいが、とりあえず簡単に説明。
コントロール レイヤーは、オーディオやビデオのようなメディアデータの流れを制御するレイヤーである。
「どこからメディアデータを入力させて、どうやって出力するか」をコントロールする。
このレイヤーは、次に紹介する「パイプライン レイヤー」を組み合わせて処理を行うような場合に使用するレイヤーになっている。

パイプライン レイヤー

お次は、パイプライン レイヤー。このレイヤーが、一番肝(個人的に)。
パイプライン レイヤーは、エンコーダやデコーダ、信号処理を行うレイヤーで、STS の概念を踏襲したレイヤーになっている。
STS は、情報処理の用語というか、基本設計で、プログラム中のデータは、
「Source(源)」→「Transform(変換)」「Sink(流し台、出力)」
という一連の流れを取るものだという考え。詳しくは違うかもしれないけど、STS に関してはこれくらい。
Media Foundation のパイプライン レイヤーでも、同じ考えに基づき、
「Media Sources」→「Media Foundation Transforms (MFTs)」→「Media Sinks」
の3つのモジュールに分かれている。
それぞれの役割は以下のとおり。

Media Sources
たとえば、ファイルやネットワーク上の場所からメディアデータを取ってくるモジュール。
MFTs
たとえば、エンコーダやデコーダのように、1つ以上のメディアデータを入力して、1つ以上の出力を出すような変換モジュール。
Media Sinks
MSDN内では「消費(consume)」と表現されているが、ようは、ディスプレイに映像を映したり、ファイルに書き出したり、音を鳴らしたり、などなどのように、何らかの出力を行うモジュール。

これらのモジュール(MSDNでは「コンポーネント」と表記)は、モジュール間で独立している。つまり、お互いに通信をおこなったりしない。これによって、個々のモジュールを再利用しやすくできている。
また「Media Sources」モジュールは、「Pull model」になっていて、「Media Sources」モジュールが能動的に次のモジュールにデータを引き渡したりすることはない。つまり、アプリケーションや先のコントロール レイヤーからの要求が来るまで待っている(「待つ」とは言え、何もしないという意味ではないと思われる。おそらく内部のバッファがいっぱいになるまで、ファイルやネットワークからデータを取得してためるくらいの処理は行うと思う)。

プラットフォーム レイヤー

ユーティリティな感じのレイヤー。決して、マルチプラットフォームのためのレイヤーというだけの意味ではないと思われる。
ここでは、動作スイッチの切り替えタイミングを知るための3つのモジュールが紹介されている。

  1. Asynchronous Model(非同期モデル)
  2. Event Model(イベントモデル)
  3. Wait Model(待ちモデル)

Asynchronous Model(非同期モデル)

これは、ある処理Xに関して、処理Xが完了するまでの間に、ほかのことができるモデル。
「処理Xを実行しろ」という要求を出す関数を呼び出したら、その関数は、処理の終了を待たずに即時復帰してくる。
で、後は「処理Xは完了した」的な関数を呼び出して、処理Xの完了を待つ。

Event Model(イベントモデル)

おおよそ、Asynchronous Model と同じだが、処理Xが実行完了になったことを知る術が違う。
Event Model では、要求時にあらかじめ「完了通知用の関数」を渡して処理Xの実行要求を出す。
そして、実行が完了したら、その「完了通知用の関数」が呼ばれるまで、ほかの処理をしながら待つ、ことになる。

Wait Model(待ちモデル)

これは、完全に処理が終わるまで待つモデル。
いたって、普通だが、たとえばネットワーク上のファイルを開く場合なんかには、不向き。
即座に処理が終わらないし、その処理に関して自分自身が行うべき作業も特にないので、CPU資源を無駄にしてしまう。

空定義

MFT

とりあえず空の定義を示すが、実装時には「Basic Practices for Writing an MFT」を見ること。

class __declspec(uuid("00000000-0000-0000-c000-000000000046"))
EmptyMFT : IMFTransform
{
#pragma region IUnknown Members
private:
    volatile long m_refCount;
    virtual ULONG STDMETHODCALLTYPE AddRef( void)
    {
        return InterlockedIncrement(&m_refCount);
    }
    virtual ULONG STDMETHODCALLTYPE Release( void)
    {
        ULONG uCount = InterlockedDecrement(&m_refCount);
        if (uCount == 0) {
            delete this;
        }
        return uCount;
    }
public:
    virtual HRESULT STDMETHODCALLTYPE QueryInterface(
        /* [in] */ REFIID riid,
        /* [iid_is][out] */ __RPC__deref_out void __RPC_FAR *__RPC_FAR *ppvObject)
    {
        if (ppvObject == NULL) {
            return E_POINTER;
        }
        else if (riid == __uuidof(IUnknown)) {
            *ppvObject = static_cast<IUnknown*>(this);
        }
        else if (riid == __uuidof(EmptyMFT)) {
            *ppvObject = static_cast<EmptyMFT*>(this);
        }
        else {
            *ppvObject = NULL;
            return E_NOINTERFACE;
        }
        AddRef();
        return S_OK;
    }
#pragma endregion
#pragma region IMFTransform
public:
    virtual HRESULT STDMETHODCALLTYPE GetStreamLimits(
        /* [out] */ __RPC__out DWORD *pdwInputMinimum,
        /* [out] */ __RPC__out DWORD *pdwInputMaximum,
        /* [out] */ __RPC__out DWORD *pdwOutputMinimum,
        /* [out] */ __RPC__out DWORD *pdwOutputMaximum)
    {
        return E_NOTIMPL;
    }
    virtual HRESULT STDMETHODCALLTYPE GetStreamCount(
        /* [out] */ __RPC__out DWORD *pcInputStreams,
        /* [out] */ __RPC__out DWORD *pcOutputStreams)
    {
        return E_NOTIMPL;
    }
    virtual HRESULT STDMETHODCALLTYPE GetStreamIDs(
        DWORD dwInputIDArraySize,
        /* [size_is][out] */ __RPC__out_ecount_full(dwInputIDArraySize) DWORD *pdwInputIDs,
        DWORD dwOutputIDArraySize,
        /* [size_is][out] */ __RPC__out_ecount_full(dwOutputIDArraySize) DWORD *pdwOutputIDs)
    {
        return E_NOTIMPL;
    }
    virtual HRESULT STDMETHODCALLTYPE GetInputStreamInfo(
        DWORD dwInputStreamID,
        /* [out] */ __RPC__out MFT_INPUT_STREAM_INFO *pStreamInfo)
    {
        return E_NOTIMPL;
    }
    virtual HRESULT STDMETHODCALLTYPE GetOutputStreamInfo(
        DWORD dwOutputStreamID,
        /* [out] */ __RPC__out MFT_OUTPUT_STREAM_INFO *pStreamInfo)
    {
        return E_NOTIMPL;
    }
    virtual HRESULT STDMETHODCALLTYPE GetAttributes(
        /* [out] */ __RPC__deref_out_opt IMFAttributes **pAttributes)
    {
        return E_NOTIMPL;
    }
    virtual HRESULT STDMETHODCALLTYPE GetInputStreamAttributes(
        DWORD dwInputStreamID,
        /* [out] */ __RPC__deref_out_opt IMFAttributes **pAttributes)
    {
        return E_NOTIMPL;
    }
    virtual HRESULT STDMETHODCALLTYPE GetOutputStreamAttributes(
        DWORD dwOutputStreamID,
        /* [out] */ __RPC__deref_out_opt IMFAttributes **pAttributes)
    {
        return E_NOTIMPL;
    }
    virtual HRESULT STDMETHODCALLTYPE DeleteInputStream(
        DWORD dwStreamID)
    {
        return E_NOTIMPL;
    }
    virtual HRESULT STDMETHODCALLTYPE AddInputStreams(
        DWORD cStreams,
        /* [in] */ __RPC__in DWORD *adwStreamIDs)
    {
        return E_NOTIMPL;
    }
    virtual HRESULT STDMETHODCALLTYPE GetInputAvailableType(
        DWORD dwInputStreamID,
        DWORD dwTypeIndex,
        /* [out] */ __RPC__deref_out_opt IMFMediaType **ppType)
    {
        return E_NOTIMPL;
    }
    virtual HRESULT STDMETHODCALLTYPE GetOutputAvailableType(
        DWORD dwOutputStreamID,
        DWORD dwTypeIndex,
        /* [out] */ __RPC__deref_out_opt IMFMediaType **ppType)
    {
        return E_NOTIMPL;
    }
    virtual HRESULT STDMETHODCALLTYPE SetInputType(
        DWORD dwInputStreamID,
        /* [in] */ __RPC__in_opt IMFMediaType *pType,
        DWORD dwFlags)
    {
        return E_NOTIMPL;
    }
    virtual HRESULT STDMETHODCALLTYPE SetOutputType(
        DWORD dwOutputStreamID,
        /* [in] */ __RPC__in_opt IMFMediaType *pType,
        DWORD dwFlags)
    {
        return E_NOTIMPL;
    }
    virtual HRESULT STDMETHODCALLTYPE GetInputCurrentType(
        DWORD dwInputStreamID,
        /* [out] */ __RPC__deref_out_opt IMFMediaType **ppType)
    {
        return E_NOTIMPL;
    }
    virtual HRESULT STDMETHODCALLTYPE GetOutputCurrentType(
        DWORD dwOutputStreamID,
        /* [out] */ __RPC__deref_out_opt IMFMediaType **ppType)
    {
        return E_NOTIMPL;
    }
    virtual HRESULT STDMETHODCALLTYPE GetInputStatus(
        DWORD dwInputStreamID,
        /* [out] */ __RPC__out DWORD *pdwFlags)
    {
        return E_NOTIMPL;
    }
    virtual HRESULT STDMETHODCALLTYPE GetOutputStatus(
        /* [out] */ __RPC__out DWORD *pdwFlags)
    {
        return E_NOTIMPL;
    }
    virtual HRESULT STDMETHODCALLTYPE SetOutputBounds(
        LONGLONG hnsLowerBound,
        LONGLONG hnsUpperBound)
    {
        return E_NOTIMPL;
    }
    virtual HRESULT STDMETHODCALLTYPE ProcessEvent(
        DWORD dwInputStreamID,
        /* [in] */ __RPC__in_opt IMFMediaEvent *pEvent)
    {
        return E_NOTIMPL;
    }
    virtual HRESULT STDMETHODCALLTYPE ProcessMessage(
        MFT_MESSAGE_TYPE eMessage,
        ULONG_PTR ulParam)
    {
        return E_NOTIMPL;
    }
    virtual /* [local] */ HRESULT STDMETHODCALLTYPE ProcessInput(
        DWORD dwInputStreamID,
        IMFSample *pSample,
        DWORD dwFlags)
    {
        return E_NOTIMPL;
    }
    virtual /* [local] */ HRESULT STDMETHODCALLTYPE ProcessOutput(
        DWORD dwFlags,
        DWORD cOutputBufferCount,
        /* [size_is][out][in] */ MFT_OUTPUT_DATA_BUFFER *pOutputSamples,
        /* [out] */ DWORD *pdwStatus)
    {
        return E_NOTIMPL;
    }
#pragma endregion
};