RenderFile
やRenderStream
を使ってフィルタグラフを構築するとき、必要に応じて中間フィルタやレンダラーフィルタが自動的に挿入される(インテリジェント接続)。これによりフィルタグラフを構築するコードが少なく済む。
この記事では、インテリジェント接続に頼らずに一つ一つフィルタを接続しWMVファイルの再生を行う。少し低レベルの処理を覗いてみればDirectShowに対する理解が深まるだろう。 ## お手本となるフィルタグラフを確認する
まずはお手本となるフィルタグラフをGraphEditで確認してみる。
GraphEditでメニュー [File]-[Render Media File...] を選択「C:\Users\Public\Videos\Sample Videos\Bear.wmv」を開いてみよう。
名前 | DMO or DirectShowフィルタ |
---|---|
WM ASF リーダー | DirectShowフィルタ |
WMAudio Decoder DMO | DMO |
WMVideo Decoder DMO | DMO |
DirectSound レンダラ | DirectShowフィルタ |
Video Mixing Renderer(VMR) | DirectShowフィルタ |
水色のボックスはDirectShowフィルタ、緑のボックスはDMO(DirectX Media Object)と呼ばれるオブジェクトである。OSで提供されている一部のコーデックはDirectShowフィルタではなくDMOとして提供されている。DMOはDirectShowフィルタとはアーキテクチャが異なるものですが、ラッパーが用意されていてDirectShowフィルタとして扱うことができる。
GraphEditに表示されたWM ASF リーダーフィルタの形状を見るとビデオとオーディオの2つの出力があることが分かる。そこからそれぞれデコーダのDMOへ接続している。最後はレンダラフィルタでレンダリングが行われる。
フィルタグラフの構築を実装する
ソースフィルタを追加する
ソースフィルタはIGraphBuilder::AddSourceFilter
というメソッドが特別に用意されているので、それを使う。AddSourceFilter
を使うと、インスタンスの生成、フィルタグラフへの追加、ファイル名の指定まで一通りやってくれるので便利である。
1 2 3 4 5 6 7 8 | CComPtr<IGraphBuilder> graph;
CComPtr<IMediaControl> control;
CComPtr<IMediaEvent> media_event;
CComPtr<IBaseFilter> source_filter;
graph.CoCreateInstance(CLSID_FilterGraph);
graph.QueryInterface<IMediaControl>(&control);
graph.QueryInterface<IMediaEvent>(&media_event);
graph->AddSourceFilter(pFilename, pFilename, &source_filter);
|
DMOを追加する
DMOは直接フィルタグラフへ追加できない。DMOラッパーフィルタを使う。CLSID_DMOWrapperFilter
のインスタンスを作成しIDMOWrapperFilter
インターフェイスを取得する。IDMOWrapperFilter::Init
で、どのDMOを使うのか指定する。最後にIFilterGraph::AddFilter
でフィルタを追加する。(IGraphBuilder
はIFilterGraph
を継承している。)
1 2 3 4 5 6 7 | CComPtr<IBaseFilter> wm_audio;
wm_audio.CoCreateInstance(CLSID_DMOWrapperFilter);
CComPtr<IDMOWrapperFilter> dmo_wrapper;
wm_audio.QueryInterface<IDMOWrapperFilter>(&dmo_wrapper);
dmo_wrapper->Init(CLSID_CWMADecMediaObject
, DMOCATEGORY_AUDIO_DECODER);
graph->AddFilter(wm_audio, L"WMA decoder");
|
DirectShowフィルタを追加する
DirectShowフィルタを追加するにはフィルタのクラスIDを指定してインスタンスを作成しIFilterGraph::AddFilter
でフィルタを追加する。(フィルタを追加する前に、必要に応じて設定を行うことができる。)
1 2 | ds_renderer.CoCreateInstance(CLSID_DSoundRender);
graph->AddFilter(ds_renderer,TEXT("DirectSound Renderer"));
|
フィルタ上のピン同士を直接接続する
フィルタグラフへフィルタを追加したら、フィルタ上のピン同士を接続する。
- 接続元フィルタの出力ピン、接続先フィルタの入力ピンを取得する。ピンは
IPin
インターフェイスで表される。 - 出力ピンから入力ピンに向かって接続する。
まず、フィルタに備わっているピンを取得する。IBaseFilter::EnumPins
を使ってフィルタの持つピンの列挙子IEnumPins
を取得する。IEnumPins::Next
でフィルタを1つずつ取得できる。
1 2 3 4 5 6 7 8 9 10 11 12 | // ピンを取得する
HRESULT
GetPin
(IBaseFilter *pFilter, PIN_DIRECTION dir, CComPtr<IPin> &pin, GUID majorType)
{
HRESULT retCode=E_FAIL;
HRESULT hr=NOERROR;
CComPtr<IEnumPins> e;
hr=pFilter->EnumPins(&e);
if(FAILED(hr))
return hr;
while(e->Next(1, &pin.p, NULL) == S_OK) {
|
次に、取得したピンが出力ピンなのか入力ピンなのか判定する。IPin::QueryDirection
を使う。
1 2 | PIN_DIRECTION this_direction;
hr=pin->QueryDirection(&this_direction);
|
this_direction
には、入力ピンならPINDIR_INPUT
、出力ピンならPINDIR_OUTPUT
が代入されている。
WM ASF リーダーフィルタを見ると出力ピンが2つあるはず。取得した出力ピンが映像/音声のどちらを出力するのか判定する必要がある。それにはIPin::EnumMediaTypes
を使う。
IPin::EnumMediaTypes
で、メディアタイプの列挙子IEnumMediaTypes
を取得しIEnumMediaTypes::Next
でメディアタイプを1つずつ取得できる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | CComPtr<IEnumMediaTypes> em;
AM_MEDIA_TYPE *amt;
hr=pin->EnumMediaTypes(&em);
if(SUCCEEDED(hr)) {
while(em->Next(1,&amt,NULL)==S_OK) {
GUID mj=amt->majortype;
// amt を解放
if (amt->cbFormat!=NULL) {
CoTaskMemFree((PVOID)amt->pbFormat);
}
if(amt->pUnk)
amt->pUnk->Release();
CoTaskMemFree(amt);
if(mj==majorType) {
retCode=S_OK;
break;
}
}
}
|
接続する入力ピンと出力ピンを特定できたらIFilterGraph::ConnectDirect
を呼び出して接続する。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | #include "stdafx.h"
LPCWSTR MovieFile=L"C:\\Users\\Public\\Videos\\Sample Videos\\Bear.wmv";
// ピンを取得する
HRESULT
GetPin
(IBaseFilter *pFilter, PIN_DIRECTION dir, CComPtr<IPin> &pin, GUID majorType)
{
HRESULT retCode=E_FAIL;
HRESULT hr=NOERROR;
CComPtr<IEnumPins> e;
hr=pFilter->EnumPins(&e);
if(FAILED(hr))
return hr;
while(e->Next(1, &pin.p, NULL) == S_OK) {
if(!pin) {
continue;
}
PIN_DIRECTION this_direction;
hr=pin->QueryDirection(&this_direction);
if(FAILED(hr)) {
continue;
}
if(this_direction!=dir) {
continue;
}
if(majorType == GUID_NULL) {
retCode=S_OK;
break;
}
CComPtr<IEnumMediaTypes> em;
AM_MEDIA_TYPE *amt;
hr=pin->EnumMediaTypes(&em);
if(SUCCEEDED(hr)) {
while(em->Next(1,&amt,NULL)==S_OK) {
GUID mj=amt->majortype;
// amt を解放
if (amt->cbFormat!=NULL) {
CoTaskMemFree((PVOID)amt->pbFormat);
}
if(amt->pUnk)
amt->pUnk->Release();
CoTaskMemFree(amt);
if(mj==majorType) {
retCode=S_OK;
return retCode;
}
}
}
}
return retCode;
}
// フィルタの同士を接続する
HRESULT
ConnectFilter
(IGraphBuilder *pBuilder, IBaseFilter *pSrc, IBaseFilter *pDest, GUID majorType)
{
HRESULT hr=NOERROR;
CComPtr<IPin> in;
CComPtr<IPin> out;
hr=GetPin(pSrc, PINDIR_OUTPUT, out, majorType);
if(FAILED(hr)) {
return hr;
}
hr=GetPin(pDest, PINDIR_INPUT, in, GUID_NULL);
if(SUCCEEDED(hr)) {
hr=pBuilder->ConnectDirect(out, in, NULL);
}
return hr;
}
void Play()
{
HRESULT hr;
CComPtr<IGraphBuilder> graph;
CComPtr<IMediaControl> control;
CComPtr<IMediaEvent> media_event;
CComPtr<IBaseFilter> source_filter;
graph.CoCreateInstance(CLSID_FilterGraph);
graph.QueryInterface<IMediaControl>(&control);
graph.QueryInterface<IMediaEvent>(&media_event);
graph->AddSourceFilter(MovieFile, pFilename, &source_filter);
CComPtr<IBaseFilter> wm_audio;
CComPtr<IBaseFilter> wm_video;
CComPtr<IBaseFilter> video_renderer;
CComPtr<IBaseFilter> ds_renderer;
// WMA
wm_audio.CoCreateInstance(CLSID_DMOWrapperFilter);
CComPtr<IDMOWrapperFilter> dmo_wrapper;
wm_audio.QueryInterface<IDMOWrapperFilter>(&dmo_wrapper);
dmo_wrapper->Init(CLSID_CWMADecMediaObject
, DMOCATEGORY_AUDIO_DECODER);
graph->AddFilter(wm_audio, L"WMA decoder");
dmo_wrapper.Release();
// WMV
wm_video.CoCreateInstance(CLSID_DMOWrapperFilter);
wm_video.QueryInterface<IDMOWrapperFilter>(&dmo_wrapper);
dmo_wrapper->Init(CLSID_CWMVDecMediaObject
, DMOCATEGORY_VIDEO_DECODER);
graph->AddFilter(wm_video, L"WMV decoder");
// Video Renderer
video_renderer.CoCreateInstance(CLSID_VideoRenderer);
graph->AddFilter(video_renderer,TEXT("Video Renderer"));
// DirectSound Renderer
ds_renderer.CoCreateInstance(CLSID_DSoundRender);
graph->AddFilter(ds_renderer,TEXT("DirectSound Renderer"));
// connect
hr=ConnectFilter(graph, source_filter, wm_video, MEDIATYPE_Video);
hr=ConnectFilter(graph, wm_video, video_renderer, MEDIATYPE_Video);
hr=ConnectFilter(graph, source_filter, wm_audio, MEDIATYPE_Audio);
hr=ConnectFilter(graph, wm_audio, ds_renderer, MEDIATYPE_Audio);
hr = control->Run();
long evCode;
media_event->WaitForCompletion(INFINITE, &evCode);
}
int _tmain(int argc, _TCHAR* argv[])
{
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
Play();
CoUninitialize();
return 0;
}
|
Comments
comments powered by Disqus