メディア ファンデーションでの Direct3D 11 ビデオ デコーディングのサポート

このトピックでは、Microsoft メディア ファンデーション デコーダーで Microsoft Direct3D 11 をサポートする方法について説明します。 具体的には、デコーダーとビデオ レンダラーの間の通信について説明します。 このトピックでは、デコーディング操作を実装する方法については説明しません。

概要

このトピックの残りの部分では、以下の用語が使用されます。

  • ソフトウェア デコーダー。 ソフトウェア デコーダーは、圧縮されたビデオ サンプルを受け取り、圧縮されていないビデオ フレームを出力するメディア ファンデーション変換 (MFT) です。
  • デコーダー デバイス。 デコーダー デバイスはビデオ アクセラレータであり、グラフィックス ドライバーによって実装されます。 デコーダー デバイスは、高速デコーディング操作を実行します。
  • パイプライン。 パイプラインはソフトウェア デコーダーをホストし、ソフトウェア デコーダーとの間のバッファーを提供します。 アプリケーションによって、パイプラインはメディア セッションソース リーダー、または MFT を直接呼び出すアプリケーション コードとなる可能性があります。

Direct3D 11 を使用してデコーディングを実行するには、ソフトウェア デコーダーが Direct3D 11 デバイスへのポインターを持っている必要があります。 Direct3D 11 デバイスは、ソフトウェア デコーダーの外部に作成されます。 メディア セッションのシナリオでは、ビデオ レンダラーが Direct3D 11 デバイスを作成します。 ソース リーダーのシナリオでは、通常、アプリケーションが Direct3D 11 デバイスを作成します。

DXGI デバイス マネージャーは、コンポーネント間で Direct3D 11 を共有するために使用されます。 DXGI デバイス マネージャーは IMFDXGIDeviceManager インターフェイスを公開します。 パイプラインは、MFT_MESSAGE_SET_D3D_MANAGER メッセージを送信することによって、ソフトウェア デコーダーに IMFDXGIDeviceManager ポインターを設定します。

次の図は、ソフトウェア デコーダー、Direct3D 11、パイプラインの関係を示しています。

a diagram that shows the software decoder and the dxgi device manager.

メディア ファンデーションで Direct3D 11 をサポートするためにソフトウェア デコーダーが実行する必要がある基本的な手順を以下に示します。

  1. Direct3D 11 デバイスへのハンドルを開きます。
  2. デコーダー構成を見つけます。
  3. 圧縮されていないバッファーを割り当てます。
  4. フレームをデコードします。

これらの手順については、このトピックの残りの部分で詳しく説明します。

デバイス ハンドルを開く

デコーダー MFT は、DXGI デバイス マネージャーを使用して Direct3D 11 デバイスへのハンドルを取得します。 デバイス ハンドルを開くには、以下の手順を実行します。

  1. デコーダー MFT は、値 TRUE を持つ MF_SA_D3D11_AWARE 属性を公開する必要があります。
  2. トポロジ ローダーは、IMFTransform::GetAttributes を呼び出してこの属性のクエリを実行します。 値 TRUE は、MFT が Direct3D 11 をサポートしていることを示します。
  3. トポロジ ローダーは、MFT_MESSAGE_SET_D3D_MANAGER メッセージを使用して IMFTransform::ProcessMessage を呼び出します。 ulParam パラメーターは、DXGI デバイス マネージャーへの IUnknown ポインターです。 IMFDXGIDeviceManager インターフェイスに対してこのポインターのクエリを実行します。
  4. IMFDXGIDeviceManager::OpenDeviceHandle を呼び出して、Direct3D 11 デバイスへのハンドルを取得します。
  5. Direct3D 11 デバイスへのポインターを取得するには、IMFDXGIDeviceManager::GetVideoService を呼び出します。 デバイス ハンドルと値 IID_ID3D11Device を渡します。 メソッドは、ID3D11Device インターフェイスへのポインターを返します。
  6. ビデオ アクセラレータへのポインターを取得するには、IMFDXGIDeviceManager::GetVideoService をもう一度呼び出します。 今回は、デバイス ハンドルと値 IID_ID3D11VideoDevice を渡します。 メソッドは、ID3D11VideoDevice インターフェイスへのポインターを返します。
  7. ID3D11Device::GetImmediateContext を呼び出して ID3D11DeviceContext ポインターを取得します。
  8. ID3D11DeviceContextQueryInterface を呼び出して、ID3D11VideoContext ポインターを取得します。
  9. ID3D11VideoContext::GetDecoderBuffer または ID3D11VideoContext::ReleaseDecoderBuffer を呼び出すときに時々発生する可能性があるデッドロックの問題を防ぐために、デバイス コンテキストでマルチスレッド保護を使用することをお勧めします。 マルチスレッド保護を設定するには、まず ID3D11DeviceQueryInterface を呼び出して ID3D10Multithread ポインターを取得します。 次に ID3D10Multithread::SetMultithreadProtected を呼び出し、bMTProtecttrue を渡します。

デコーダー構成を見つける

デコーディングを実行するために、ソフトウェア デコーダーは、レンダーターゲットの形式を含め、デコーダー デバイスでサポートされている互換性のある構成を見つける必要があります。 この手順は、以下のように IMFTransform::SetInputType メソッド内で行われます。

  1. 入力メディアの種類を検証します。 種類が拒否された場合は、残りの手順をスキップし、エラー コードを返します。
  2. ID3D11VideoDevice::GetVideoDecoderProfileCount を呼び出して、サポートされているプロファイルの数を取得します。
  3. ID3D11VideoDevice::GetVideoDecoderProfile を呼び出して、プロファイルを列挙し、プロファイル GUID を取得します。
  4. ビデオ形式とソフトウェア デコーダーの機能に一致するプロファイル GUID を探します。 たとえば、MPEG-2 デコーダーは、D3D11_DECODER_PROFILE_MPEG2_MOCOMPD3D11_DECODER_PROFILE_MPEG2_IDCT、および D3D11_DECODER_PROFILE_MPEG2_VLD を検索します。
  5. 適切なデコーダー GUID が見つかった場合は、ID3D11VideoDevice::CheckVideoDecoderFormat メソッドを呼び出して出力形式を確認します。 デコーダー GUID とレンダーターゲット形式を指定する DXGI_FORMAT 値を渡します。
  6. 次に、デコーダーに適した構成を見つけます。
    1. ID3D11VideoDevice::GetVideoDecoderConfigCount を呼び出して、デコーダー構成の数を取得します。 提案されたレンダーターゲット形式を記述する D3D11_VIDEO_DECODER_DESC 構造体と共に、同じデコーダー デバイス GUID を渡します。
    2. ID3D11VideoDevice::GetVideoDecoderConfig を呼び出して、デコーダー構成を列挙します。

IMFTransform::GetOutputAvailableType メソッドで、提案されたレンダーターゲット形式に基づいて、圧縮されていないビデオ形式を返します。

IMFTransform::SetOutputType メソッドで、レンダー ターゲット形式に対してメディアの種類を確認します。

ソフトウェア デコーディングへフォールバックする

MFT が構成を見つけられない場合があります。 たとえば、グラフィックス ドライバーが適切な機能をサポートしていない場合があります。 その場合、MFT は以下のようにソフトウェア デコーディングにフォールバックする必要があります。

  1. SetInputType メソッドと SetOutputType メソッドは、両方とも MF_E_UNSUPPORTED_D3D_TYPE を返す必要があります。
  2. 応答として、トポロジ ローダーは ulParam パラメーターに値 NULL を持つ MFT_MESSAGE_SET_D3D_MANAGER メッセージを送信します。
  3. MFT は IMFDXGIDeviceManager インターフェイスへのポインターを解放します。
  4. トポロジ ローダーは、メディアの種類を再ネゴシエートします。

この時点で、MFT はソフトウェア デコーディングを使用できます。

圧縮されていないバッファーの割り当て

デコーダーは、圧縮されていないビデオ バッファーとして使用する Direct3D 11 テクスチャを割り当てる役割を担います。 出力ストリーム属性内の MF_SA_MINIMUM_OUTPUT_SAMPLE_COUNT 属性 (IMFTransform::GetOutputStreamAttributes を参照してください) は、ビデオ レンダラーがデインターレースに使用するためのサーフェスをデコーダーが何個割り当てる必要があるかを決定するために使用されます。 デコーダーは、この値を使用してその数を何らかの合理的な上限と下限 (たとえば、3 から 32 など) 内に制限する必要があります。 プログレッシブ コンテンツについては、MF_SA_MINIMUM_OUTPUT_SAMPLE_COUNT_PROGRESSIVE を参照してください。

IMFTransform::GetOutputStreamInfo メソッドで、MFT_OUTPUT_STREAM_INFO 構造体に MFT_OUTPUT_STREAM_PROVIDES_SAMPLES フラグを設定します。 このフラグは、MFT が独自の出力サンプルを割り当てることをメディア セッションに通知します。 出力サンプルを割り当てるために、MFT は以下の手順を実行します。

  1. ID3D11Device::CreateTexture2D を呼び出して 2D テクスチャ配列を作成します。 D3D11_TEXTURE2D_DESC 構造体で、デコーダーが必要とするサーフェスの数と等しい ArraySize を設定します。 これには、次のものが含まれます。

    • 参照フレームのサーフェス。
    • インターレース解除用のサーフェス (3 つのサーフェス)。
    • デコーダーがバッファリングのために必要とするサーフェス。

    バインド フラグ (BindFlags) には、D3D11_BIND_DECODER フラグと、出力ストリーム属性の MF_SA_D3D11_BINDFLAGS 属性を通して設定されるすべてのバインド フラグを含める必要があります。

  2. テクスチャ配列内の各サーフェスに対して、ID3D11VideoDevice::CreateVideoDecoderOutputView を呼び出して、ビデオ デコーダー出力ビューを作成します。 デコーディング中に、これらの出力ビューは ID3D11VideoContext::DecoderBeginFrame メソッドに渡されます。

  3. テクスチャ配列内の各サーフェスに対して、以下のようにメディア サンプルを作成します。

    1. MFCreateDXGISurfaceBuffer 関数を呼び出して、DXGI メディア バッファーを作成します。 ID3D11Texture2D ポインターと、テクスチャ配列内の各要素のオフセットを渡します。 関数は IMFMediaBuffer ポインターを返します。
    2. MFCreateVideoSampleFromSurface 関数を呼び出して、空のメディア サンプルを作成します。 pUnkSurface パラメーターを NULL に設定します。 関数は IMFSample ポインターを返します。
    3. IMFSample::AddBuffer を呼び出して、メディア バッファーをサンプルに追加します。

一部のテクスチャを破棄して残りを使用し続けるのではなく、作成したすべてのテクスチャを同時に破棄する必要があります。

デコーディング

デコーダー デバイスを作成するには、ID3D11VideoDevice::CreateVideoDecoder を呼び出します。 メソッドは、ID3D11VideoDecoder インターフェイスへのポインターを返します。 デコーディングは、IMFTransform::ProcessOutput メソッド内で行う必要があります。 各フレームで、IMFDXGIDeviceManager::TestDevice を呼び出して、DXGI の可用性をテストします。 デバイスが変更された場合、ソフトウェア デコーダーは、以下のようにデコーダー デバイスを再作成する必要があります。

  1. IMFDXGIDeviceManager::CloseDeviceHandle を呼び出して、デバイス ハンドルを閉じます。
  2. ID3D11VideoDecoderID3D11VideoContextID3D11Texture2DID3D11VideoDecoderOutputView インターフェイスを含め、以前の Direct3D 11 デバイスに関連付けられているすべてのリソースを解放します。
  3. 新しいデバイス ハンドルを開きます。
  4. デコーダー構成を見つける」で前述したように、新しいデコーダー構成をネゴシエートします。 デバイスの機能が変化した可能性があるため、この手順が必要です。
  5. 新しいデコーダー デバイスを作成します。

デバイス ハンドルが有効であると仮定すると、デコーディング プロセスは以下のように機能します。

  1. 現在使用されていない利用可能なサーフェスを取得します。 最初は、すべてのサーフェスが利用可能です。
  2. IMFTrackedSample インターフェイスに対してメディア サンプルのクエリを実行します。
  3. IMFTrackedSample::SetAllocator を呼び出し、IMFAsyncCallback インターフェイスへのポインターを指定します。 (ソフトウェア デコーダーは、このインターフェイスを実装する必要があります。)ビデオ レンダラーがサンプルをリリースすると、コールバックが呼び出されます。 このコールバックを使用して、どのサンプルが現在利用可能で、どのサンプルが使用されているかを追跡します。
  4. ID3D11VideoContext::DecoderBeginFrame を呼び出します。 デコーダー デバイスの ID3D11VideoDecoder インターフェイスと出力ビューの ID3D11VideoDecoderOutputView インターフェイスへのポインターを渡します。
  5. 以下の操作を 1 回以上行います。
    1. ID3D11VideoContext::GetDecoderBuffer を呼び出してバッファーを取得します。
    2. バッファーを埋めます。
    3. ID3D11VideoContext::ReleaseDecoderBuffer を呼び出します。
    4. ID3D11VideoContext::SubmitDecoderBuffer を呼び出します。 このメソッドは、フレーム上のデコーディング操作を実行するようにデコーダー デバイスに指示します。
  6. ID3D11VideoContext::DecoderEndFrame を呼び出して、現在のフレームのデコーディングの終了を通知します。

Direct3D 11 は、デコーディング操作に DXVA 2.0 と同じデータ構造を使用します。 DXVA プロファイルの元のセット (H.261、H.263、MPEG-2 用) については、「DXVA 1.0 仕様」の中でこれらのデータ構造について説明されています。

DecoderBeginFrame 呼び出しと SubmitDecoderBuffer 呼び出しの各ペア内では、GetDecoderBuffer を複数回呼び出すことができますが、各種類のバッファーに対して呼び出せるのは 1 回のみです。 SubmitDecoderBuffer を呼び出さずに同じバッファーの種類を 2 回使用すると、バッファー内のデータを上書きすることになります。

Direct3D 11 ビデオ API

DirectX ビデオ アクセラレータ 2.0