none
[DirectShow]2回目以降に構成したフィルタグラフでのキャプチャが失敗する RRS feed

  • 質問

  • こんばんわ。DirectShowを使ったプログラミングについて困難な問題に当たってしまったため、質問を投稿させていただきました。

    現在、DirectShowと.NET(C++/CLI)を使ってWebカメラの映像をやりとりするアプリケーションを作っているのですが、これに受信側PCの処理能力を考慮して圧縮した画像を送信するような機能を付加しようと思っています。
    そこで色々種類のある圧縮フィルタを動作中に切り替えられるようにしたいのですが、新しい圧縮フィルタを使用して構成したフィルタグラフがうまく動作せず困っています。具体的には、サンプルグラバのGetCurrentBufferメソッドが失敗してしまいます。(参照違反などは起きていません)

    動作に関してですが、DirectShowに関する処理は自作のクラス(DShowCustomUtil)で行っており、フィルタグラフを構成するメソッド(RefreshGraph)もそのメンバメソッドに含まれています。
    グラフの構成の際には1回目も2回目も同じメソッドを使用しているのですが、新しいフィルタグラフを作成した際にメンバとして定義されているISampleGrabber(this->pGrab)を更新することで、常に同じメンバのISampleGrabber::GetCurrentBufferメソッドを使用によって正しいキャプチャ画像が得られるような構造になっています。

    以下がRefreshGraphメソッドの記述です。(少々汚いですが…)
    キャプチャに関連しているコードはもっとあるのですが、膨大なため主要な部分だけをペーストさせていただきます。

     

    void DShowCustomUtil::RefreshGraph()
    {
    	this->CaptureEnable = false;
    
    	HRESULT hr;
    
    	//フィルタグラフ作成
    	IGraphBuilder *pGraph = NULL;
    	CoCreateInstance( CLSID_FilterGraph, NULL, CLSCTX_INPROC, IID_IGraphBuilder, (void **)&pGraph);
    
    	// キャプチャフィルタの取得
    	IBaseFilter *pbf = this->GetCaptureDevice();
    	pGraph->AddFilter(pbf, this->APINameCap); // キャプチャフィルタをフィルタグラフに追加
    
    	// グラバフィルタの作成
    	IBaseFilter *pF = NULL;
    	CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (LPVOID *)&pF); // グラバフィルタの作成
    
    	ISampleGrabber *pGrab;
    	pF->QueryInterface(IID_ISampleGrabber, (void **)&pGrab); // サンプルグラバの取得
    	if(FAILED(hr)){ Debug::WriteLine("Failed:Query"); }
    	else{ Debug::WriteLine("Succeeded:Query"); }
    
    	// グラバフィルタをフィルタグラフに追加
    	pGraph->AddFilter(pF, this->APINameGrab);
    
    	// 圧縮フィルタの追加
    	IBaseFilter *pFCmp = NULL;
    	pFCmp = GetCompressorFromFName(this->SelectedCmpFilterFname);
    
    	// IAMVideoCompressionのサポート
    	IPin *pb = this->GetPin( pFCmp, PINDIR_OUTPUT );
    	IAMVideoCompression *pMje = NULL;
    	pb->QueryInterface(IID_IAMVideoCompression, (void **)&pMje);
    	
    	pb->Release();
    	pMje->Release();
    
    	if(FAILED(hr)){ Debug::WriteLine("Failed:Query"); }
    	else{ Debug::WriteLine("Succeeded:Query"); }
    
    	hr = pGraph->AddFilter(pFCmp, this->APINameCmp);
    	if(FAILED(hr)){ Debug::WriteLine("Failed:AddFilter"); }
    	else{ Debug::WriteLine("Succeeded:AddFilter"); }
    
    	// 取得データのメディアタイプを設定
    	AM_MEDIA_TYPE amt = AMMTypetoCaptype(this->Captype);
    	this->MediaType = &amt;
    
    
    	pGrab->SetMediaType(&amt); // サンプルグラバの入力ピン上の接続に使うメディアタイプを指定
    
    	// キャプチャグラフの作成
    	ICaptureGraphBuilder2 *pCapturebuf = NULL; // ●キャプチャグラフ用
    	CoCreateInstance(CLSID_CaptureGraphBuilder2,
    	  NULL, CLSCTX_INPROC, IID_ICaptureGraphBuilder2, (void **)&pCapturebuf);
    	this->pCapture = pCapturebuf;
    	pCapture->SetFiltergraph(pGraph); // フィルタグラフをキャプチャグラフに組み込む
    
    	// キャプチャフィルタ"pbf"と グラバフィルタ"pF"をチェーン接続
    	hr = pCapture->RenderStream(&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video, pbf, pFCmp, pF);
    	if( FAILED(hr) ){ Debug::WriteLine("Failed:RenderStream"); }
    	else { Debug::WriteLine("Succeeded:RenderStream"); }
    
    	pFCmp->Release();
    	pF->Release();
    
    	// ビデオ出力フォーマットの設定
    	IAMStreamConfig *pConfig = NULL;
    	hr = pCapture->FindInterface(&PIN_CATEGORY_CAPTURE, 0, pbf, IID_IAMStreamConfig, (void**)&pConfig); // IAMStreamConfigインターフェイスを検索
    
    	pbf->Release();      // キャプチャフィルタ用
    	pCapture->Release();  // キャプチャグラフ用
    
    	pConfig->Release();
    
    	// キャプチャ開始
    	IMediaControl *pMC = NULL;
    	pGraph->QueryInterface(IID_IMediaControl, (LPVOID *)&pMC);
    
    	pMC->Run(); // レンダリング開始
    	pGrab->SetBufferSamples(TRUE); // グラブ開始
    
    
    	// クラスのメンバ更新
    	this->pGraph = pGraph;
    	this->pMC = pMC;
    	this->pGrab = pGrab;
    	this->pMje = pMje;
    	this->pConfig = pConfig;
    
    	pGraph->Release();    // フィルタグラフ用
    
    	::Debug::WriteLine("DirectShow Initialized.");
    	Debug::WriteLine(this->Captype);
    	this->CaptureEnable = true;
    }
    
    
    ちなみに2回目の実行時も、一応チェーン接続まで成功した旨のメッセージが出力されていました。
    どうかご回答よろしくお願いいたします。

    ---

    VisualStudio2008のプロジェクトファイルをアップロードしましたので、よろしければご覧ください。DShowCustomUtilクラスの記述は/ExternalDesign/DShowCustomUtil.h(.cpp)にあります。
    またネットワーク機能は実装されていないため、キャプチャした画像をそのままフォームに出力するようになっています。
    http://www1.axfc.net/uploader/Si/so/131366.zip


    2012年2月1日 17:57

回答

  • 外しているかもしれませんが、前の Graph が残っているからうまく動かないと言うことはありませんか?
    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    2012年2月1日 22:17
    モデレータ
  • こんにちは。回答ありがとうございます!

    おっしゃる通り、1回目で構成したグラフのフィルタをすべて削除するようにしたらうまくいきました!ありがとうございます!
    この問題で1ヶ月ぐらい悩んでいたのですが…早く投稿すればよかったです^^;

    ---

    誰か見るかもしれないので具体的な方法を書いておきます。

    今回の問題を解決するためにフィルタグラフ内のフィルタを全て削除するメソッドを新たに作成しました。
    グラフの再構成時にはそのメソッドを事前に呼ぶようにするため、再構成を実行するイベント関数は別にしておく必要があります。自分の場合はフォームのコンストラクタとボタンクリック。
    以下がフィルタを全て削除するメソッドの記述です。

    void DShowCustomUtil::ClearGraph()
    {
    	this->CaptureEnable = false;
    
    	this->pMC->Stop();
    
    	// グラフのフィルタを列挙
    	IEnumFilters *pEnum = NULL;
    	HRESULT hr = pGraph->EnumFilters(&pEnum);
    	if (SUCCEEDED(hr))
    	{
    		IBaseFilter *pFilter = NULL;
    		while (S_OK == pEnum->Next(1, &pFilter, NULL))
    		{
    			 // フィルタを削除する
    			 pGraph->RemoveFilter(pFilter);
    			 // 列挙子をリセットする
    			 pEnum->Reset();
    			 pFilter->Release();
    		}
    		pEnum->Release();
    	}
    	
    }
    


    2012年2月2日 7:50

すべての返信

  • 外しているかもしれませんが、前の Graph が残っているからうまく動かないと言うことはありませんか?
    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    2012年2月1日 22:17
    モデレータ
  • こんにちは。回答ありがとうございます!

    おっしゃる通り、1回目で構成したグラフのフィルタをすべて削除するようにしたらうまくいきました!ありがとうございます!
    この問題で1ヶ月ぐらい悩んでいたのですが…早く投稿すればよかったです^^;

    ---

    誰か見るかもしれないので具体的な方法を書いておきます。

    今回の問題を解決するためにフィルタグラフ内のフィルタを全て削除するメソッドを新たに作成しました。
    グラフの再構成時にはそのメソッドを事前に呼ぶようにするため、再構成を実行するイベント関数は別にしておく必要があります。自分の場合はフォームのコンストラクタとボタンクリック。
    以下がフィルタを全て削除するメソッドの記述です。

    void DShowCustomUtil::ClearGraph()
    {
    	this->CaptureEnable = false;
    
    	this->pMC->Stop();
    
    	// グラフのフィルタを列挙
    	IEnumFilters *pEnum = NULL;
    	HRESULT hr = pGraph->EnumFilters(&pEnum);
    	if (SUCCEEDED(hr))
    	{
    		IBaseFilter *pFilter = NULL;
    		while (S_OK == pEnum->Next(1, &pFilter, NULL))
    		{
    			 // フィルタを削除する
    			 pGraph->RemoveFilter(pFilter);
    			 // 列挙子をリセットする
    			 pEnum->Reset();
    			 pFilter->Release();
    		}
    		pEnum->Release();
    	}
    	
    }
    


    2012年2月2日 7:50