手順 3: フィルター グラフを作成する
[このページに関連付けられている機能 DirectShow は、従来の機能です。 MediaPlayer、IMFMediaEngine、Media Foundation のオーディオ/ビデオ キャプチャに置き換わりました。 これらの機能は、Windows 10とWindows 11用に最適化されています。 新しいコードでは、可能であれば、DirectShow ではなく Media Foundation で MediaPlayer、IMFMediaEngine、Audio/Video Capture を使用することを強くお勧めします。 Microsoft は、レガシ API を使用する既存のコードを、可能であれば新しい API を使用するように書き換えるよう提案しています。]
このトピックは、 DirectShow でのオーディオ/ビデオ再生に関するチュートリアルの手順 3 です。 完全なコードについては、「 DirectShow 再生の例」を参照してください。
次の手順では、メディア ファイルを再生するフィルター グラフを作成します。
メディア ファイルを開く
メソッドは DShowPlayer::OpenFile
、再生用のメディア ファイルを開きます。 このメソッドは、次の処理を行います。
- 新しい (空の) フィルター グラフを作成します。
- IGraphBuilder::AddSourceFilter を呼び出して、指定したファイルのソース フィルターを追加します。
- ソース フィルターにストリームをレンダリングします。
HRESULT DShowPlayer::OpenFile(PCWSTR pszFileName)
{
IBaseFilter *pSource = NULL;
// Create a new filter graph. (This also closes the old one, if any.)
HRESULT hr = InitializeGraph();
if (FAILED(hr))
{
goto done;
}
// Add the source filter to the graph.
hr = m_pGraph->AddSourceFilter(pszFileName, NULL, &pSource);
if (FAILED(hr))
{
goto done;
}
// Try to render the streams.
hr = RenderStreams(pSource);
done:
if (FAILED(hr))
{
TearDownGraph();
}
SafeRelease(&pSource);
return hr;
}
フィルター グラフ マネージャーの作成
メソッドは DShowPlayer::InitializeGraph
、新しいフィルター グラフを作成します。 このメソッドは、次の処理を行います。
- CoCreateInstance を呼び出して、フィルター グラフ マネージャーの新しいインスタンスを作成します。
- フィルター グラフ マネージャーで IMediaControl インターフェイスと IMediaEventEx インターフェイスに 対してクエリを実行します。
- IMediaEventEx::SetNotifyWindow を呼び出してイベント通知を設定します。 詳細については、「 DirectShow でのイベント通知」を参照してください。
HRESULT DShowPlayer::InitializeGraph()
{
TearDownGraph();
// Create the Filter Graph Manager.
HRESULT hr = CoCreateInstance(CLSID_FilterGraph, NULL,
CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&m_pGraph));
if (FAILED(hr))
{
goto done;
}
hr = m_pGraph->QueryInterface(IID_PPV_ARGS(&m_pControl));
if (FAILED(hr))
{
goto done;
}
hr = m_pGraph->QueryInterface(IID_PPV_ARGS(&m_pEvent));
if (FAILED(hr))
{
goto done;
}
// Set up event notification.
hr = m_pEvent->SetNotifyWindow((OAHWND)m_hwnd, WM_GRAPH_EVENT, NULL);
if (FAILED(hr))
{
goto done;
}
m_state = STATE_STOPPED;
done:
return hr;
}
ストリームのレンダリング
次の手順では、ソース フィルターを 1 つ以上のレンダラー フィルターに接続します。
メソッドは DShowPlayer::RenderStreams
、次の手順を実行します。
- IFilterGraph2 インターフェイスのフィルター グラフ マネージャーに対してクエリを実行します。
- ビデオ レンダラー フィルターをフィルター グラフに追加します。
- オーディオ再生をサポートするために、 DirectSound レンダラー フィルター をフィルター グラフに追加します。 フィルター グラフへのフィルターの追加の詳細については、「 CLSID によるフィルターの追加」を参照してください。
- ソース フィルターの出力ピンを列挙します。 ピンの列挙の詳細については、「ピンの 列挙」を参照してください。
- ピンごとに、 IFilterGraph2::RenderEx メソッドを呼び出します。 このメソッドは、出力ピンをレンダラー フィルターに接続し、必要に応じて中間フィルター (デコーダーなど) を追加します。
- を呼び出
CVideoRenderer::FinalizeGraph
して、ビデオ レンダラーの初期化を完了します。 - そのフィルターが別のフィルターに接続されていない場合は、 DirectSound レンダラー フィルターを削除します。 これは、ソース ファイルにオーディオ ストリームが含まれていない場合に発生する可能性があります。
// Render the streams from a source filter.
HRESULT DShowPlayer::RenderStreams(IBaseFilter *pSource)
{
BOOL bRenderedAnyPin = FALSE;
IFilterGraph2 *pGraph2 = NULL;
IEnumPins *pEnum = NULL;
IBaseFilter *pAudioRenderer = NULL;
HRESULT hr = m_pGraph->QueryInterface(IID_PPV_ARGS(&pGraph2));
if (FAILED(hr))
{
goto done;
}
// Add the video renderer to the graph
hr = CreateVideoRenderer();
if (FAILED(hr))
{
goto done;
}
// Add the DSound Renderer to the graph.
hr = AddFilterByCLSID(m_pGraph, CLSID_DSoundRender,
&pAudioRenderer, L"Audio Renderer");
if (FAILED(hr))
{
goto done;
}
// Enumerate the pins on the source filter.
hr = pSource->EnumPins(&pEnum);
if (FAILED(hr))
{
goto done;
}
// Loop through all the pins
IPin *pPin;
while (S_OK == pEnum->Next(1, &pPin, NULL))
{
// Try to render this pin.
// It's OK if we fail some pins, if at least one pin renders.
HRESULT hr2 = pGraph2->RenderEx(pPin, AM_RENDEREX_RENDERTOEXISTINGRENDERERS, NULL);
pPin->Release();
if (SUCCEEDED(hr2))
{
bRenderedAnyPin = TRUE;
}
}
hr = m_pVideo->FinalizeGraph(m_pGraph);
if (FAILED(hr))
{
goto done;
}
// Remove the audio renderer, if not used.
BOOL bRemoved;
hr = RemoveUnconnectedRenderer(m_pGraph, pAudioRenderer, &bRemoved);
done:
SafeRelease(&pEnum);
SafeRelease(&pAudioRenderer);
SafeRelease(&pGraph2);
// If we succeeded to this point, make sure we rendered at least one
// stream.
if (SUCCEEDED(hr))
{
if (!bRenderedAnyPin)
{
hr = VFW_E_CANNOT_RENDER;
}
}
return hr;
}
前の例で使用した 関数のコード RemoveUnconnectedRenderer
を次に示します。
HRESULT RemoveUnconnectedRenderer(IGraphBuilder *pGraph, IBaseFilter *pRenderer, BOOL *pbRemoved)
{
IPin *pPin = NULL;
*pbRemoved = FALSE;
// Look for a connected input pin on the renderer.
HRESULT hr = FindConnectedPin(pRenderer, PINDIR_INPUT, &pPin);
SafeRelease(&pPin);
// If this function succeeds, the renderer is connected, so we don't remove it.
// If it fails, it means the renderer is not connected to anything, so
// we remove it.
if (FAILED(hr))
{
hr = pGraph->RemoveFilter(pRenderer);
*pbRemoved = TRUE;
}
return hr;
}
フィルター グラフの解放
アプリケーションが終了したら、次のコードに示すように、フィルター グラフを解放する必要があります。
void DShowPlayer::TearDownGraph()
{
// Stop sending event messages
if (m_pEvent)
{
m_pEvent->SetNotifyWindow((OAHWND)NULL, NULL, NULL);
}
SafeRelease(&m_pGraph);
SafeRelease(&m_pControl);
SafeRelease(&m_pEvent);
delete m_pVideo;
m_pVideo = NULL;
m_state = STATE_NO_GRAPH;
}
関連トピック