D3D11TexutreMediaSink は、Direct3D11テクスチャ(ID3D11Texture2D)の形で動画を出力する、MediaSession 用のカスタム MediaSink です。 Microsoft の DX11VideoRenderer サンプル を元にして作りました。
- MediaSession を構築する際に D3D11TextureMediaSink をビデオレンダラとして登録すると、動画の再生時に、フレーム画像を含んだ
IMFSample
COM オブジェクトを取得することができます。このIMFSample
のメディアバッファにはID3D11Texture2D
リソースが含まれています。 - テクスチャのフォーマットは
DXGI_FORMAT_B8G8R8A8_UNorm
で固定です。 - スケジューラでキャッシュすることができる出力テクスチャは、最大5枚です。
- デコード後のビデオ処理(インタレース解除や色変換)には D3D11/DXVA2.0 を使用します。XVP (Media Foundation Transcode Video Processor) には対応していません。
IMFActivate
には対応していません。- レジストリへの登録には対応していません。
-
D3D11TextureMediaSink プロジェクト(C++)
- D3D11TextureMediaSink.lib/dll を生成します。
-
D3D11TextureMediaSinkDemo プロジェクト(C++)
- D3D11TextureMediaSink.lib/dll を使って動画を再生するサンプルです。
-
D3D11TextureMediaSinkDemoCShart プロジェクト(C#)
- D3D11TextureMediaSink.dll を使って動画を再生するサンプルのC#版です。
- C# で DirectX と Media Foundation を扱うために、ビルド時に SharpDX と MediaFoundation を NuGet から取得します。
- Windows 10 October 2018 Update (1809)
- DirectX 11.0
- Visual Studio Community 2017 Version 15.9.7
- C++
- Windows 10 SDK for October 2018 Update (10.0.17763.0)
- Visual Studio 2017 (v141) Toolset
- C#
- Visual C# 7.0
- .NET Framework 4.7.1
- C++
- MIT Licence
-
初めに、アプリ側で、
ID3D11Device
とIMFDXGIDeviceManager
を作成しておきます。- D3D11TextureMediaSink は、
IMFDXGIDeviceManager
の lock 機能を使用せず、渡されたID3D11Device
をそのまま使います。
そのため、ID3D11Device
にはID3D10Multithread::SetMultithreadProtected(TRUE)
を設定しておく必要があります。
- D3D11TextureMediaSink は、
-
D3D11TextureMediaSink の
CreateD3D11TextureMediaSink
関数でIMFMediaSink
オブジェクトを生成します。
引数には、アプリ側で生成したIMFDXGIDeviceManager
オブジェクトとID3D11Device
オブジェクトを渡します。
// D3D11TextureMediaSink オブジェクトを生成し、IMFMediaSink インターフェースで受け取ります。
HRESULT hr;
hr = CreateD3D11TextureMediaSink(
IID_IMFMediaSink,
(void**)&m_pMediaSink, // IMFMediaSink* m_pMediaSink
m_pDXGIDeviceManager, // 作成済みの IMFDXGIDeviceManager
m_pD3DDevice); // 作成済みの ID3D11Device
- D3D11TextureMediaSink から、動画フレームの受け取りに必要となる
IMFAttributes
も取得しておきます。
hr = m_pMediaSink->QueryInterface(
IID_IMFAttributes,
(void**)&m_pMediaSinkAttributes); // IMFAttributes* m_pMediaSinkAttributes
- MediaSession を構築します。
- 部分トポロジーを作る際、ビデオの出力ノード(
IMFTopologyNode
)に、D3D11TextureMediaSink から取得できる最初のIMFStreamSink
オブジェクトを割り当てます。
// IMFMediaSink から、ID 0(固定値)の IMFStreamSink を取得します。
IMFStreamSink* pStreamSink;
hr = pMediaSink->GetStreamSinkById(0, &pStreamSink);
// IMFStreamSink を、出力ノードに割り当てます。
hr = pOutputNode->SetObject(pStreamSink); // IMFTopologyNode* pOutputNode;
- MediaSession の再生を開始します。
- 動画のフレーム(
IMFSample
)を、D3D11TextureMediaSink のTMS_SAMPLE
属性から取得します。
hr = m_pMediaSinkAttributes->GetUnknown(
TMS_SAMPLE, // D3D11TextureMediaSink.h で定義されているGUID
IID_IMFSample,
(void**)&pSample); // IMFSample* pSample
- 取得した
IMFSample
から、ID3D11Texture2D
を取得します。
// IMFSample からメディアバッファを取得します。
IMFMediaBuffer* pMediaBuffer;
hr = pSample->GetBufferByIndex(0, &pMediaBuffer);
// メディアバッファからDXGIバッファを取得します。
IMFDXGIBuffer* pDXGIBuffer;
hr = pMediaBuffer->QueryInterface(IID_IMFDXGIBuffer, (void**)&pDXGIBuffer);
// DXGIバッファから Texture2D を取得します。
ID3D11Texture2D* pTexture2D;
hr = pDXGIBuffer->GetResource(IID_ID3D11Texture2D, (void**)&pTexture2D);
// Texture2D のサブリソースインデックスを取得します。
UINT subIndex;
hr = pDXGIBuffer->GetSubresourceIndex(&subIndex);
- 取得した
ID3D11Texture2D
リソースを、アプリ側の好みの方法で描画します。
(略)
- 描画が終わったら、
IMFSample
を解放します。
pTexture2D->Release();
pDXGIBuffer->Release();
pMediaBufer->Release();
pSample->Release();
hr = m_pMediaSinkAttributes->SetUnknown(TMS_SAMPLE, NULL);
-
注意:
D3D11TextureMediaSink は、内部のスケジューラに従い、常に、TMS_SAMPLE 属性で提供するIMFSample
を、現在表示すべき最新のものに入れ替えていきます。
アプリが TMS_SAMPLE 属性に対してIMFAttributes::GetUnknown()
を呼び出すと、D3D11TextureMediaSink は、このIMFSample
の入れ替え作業をいったん保留(ロック)します。
アプリは、IMFSample
の描画を終えてこれが不要になったら、IMFAttributes::SetUnknown()
を呼び出して、解放を宣言してください。これが呼び出されると、D3D11TexutreMediaSink は、スケジュールによる最新のIMFSample
の入れ替え作業を再開します。 -
さらに注意:
IMFSample
の入れ替え作業は、長時間保留されるべきではありません。
例えば、IMFSample
から取得したID3D11Texture2D
リソースをピクセルシェーダに設定する場合には、シェーダーが実行されるまでそれを維持する必要があります。
このような場合には、別のリソースに画像をコピーして利用することをお勧めします。