D3D11TexutreMediaSink は、Direct3D11テクスチャ(ID3D11Texture2D)の形で動画を出力する、MediaSession 用のカスタム MediaSink です。 Microsoft の DX11VideoRenderer サンプル を元にして作りました。
- MediaSession を構築する際に D3D11TextureMediaSink をビデオレンダラとして登録すると、動画の再生時に、フレーム画像を含んだ
IMFSampleCOM オブジェクトを取得することができます。この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リソースをピクセルシェーダに設定する場合には、シェーダーが実行されるまでそれを維持する必要があります。
このような場合には、別のリソースに画像をコピーして利用することをお勧めします。