DirectShow。ISampleGrabberでのイメージ取得

解決


t.aoki  2005-11-15 19:15:09  No: 59580

DirectShowのSampleGrabber、NullRendererを用いて動画の1フレーム目のイメージを
取得したいと思っています。
そこで以下のような処理を組んだのですが、ソースのメディアタイプがRGB24以外の場合
ピンの接続で失敗します。(指定したメディアタイプと異なるので当然なのでしょうが)
そこで、仮として AM_MEDIA_TYPE の設定値をGraphEditerで該当ファイルを読み込み
調べた値に変更したりしてみたのですが、それでもピンの接続に失敗します。

下記の処理をDirectShowで読み込み可能な全ての動画に対応させるにはどのような修正
を加えればよろしいでしょうか。
現状はAVI、MPG形式のファイルはイメージを取得できたのですが、ASF、WMVファイルは
ピンの接続で失敗しています。

よろしくお願いします。

WinXP VC++6.0 sp6 MFC

-------------------------------
  {
      CComPtr<IGraphBuilder> pGraph;
      pGraph.CoCreateInstance(CLSID_FilterGraph);
      // ソースフィルタを追加
      CComPtr<IBaseFilter> pSourceF;
      // std::wstring stFilepath_ にファイルパスが入っている
      pGraph->AddSourceFilter(stFilepath_.c_str(), L"Source", &pSourceF);
      // サンプルグラバフィルタを作成
      CComPtr<IBaseFilter> pGrabberF;
      pGrabberF.CoCreateInstance(CLSID_SampleGrabber);
      // 取得イメージの形式を指定
      CComQIPtr<ISampleGrabber, &IID_ISampleGrabber> qipGrabber(pGrabberF);
      AM_MEDIA_TYPE obmt;
      memset(&obmt, 0x00, sizeof(obmt));
      obmt.majortype = MEDIATYPE_Video;
      obmt.subtype = MEDIASUBTYPE_RGB24;
      obmt.formattype = FORMAT_VideoInfo;
      qipGrabber->SetMediaType(&obmt);
      // サンプルグラバフィルタを追加
      pGraph->AddFilter(pGrabberF, L"Sample Grabber");
      // ソースフィルタからサンプルグラバフィルタへピン接続
      CComPtr<IPin> pOutpin(GetPin(pSourceF, PINDIR_OUTPUT));
      CComPtr<IPin> pInpin(GetPin(pGrabberF, PINDIR_INPUT));
      if (FAILED(pGraph->Connect(pOutpin, pInpin))) {
          // ここで失敗します
          return false;
      }
      // NULLレンダリングフィルタを作成・サンプルグラバフィルタに接続
      // イメージ取得
          :
  }

  IPin* GetPin(IBaseFilter* pFilter, PIN_DIRECTION direct) {
      CComPtr<IEnumPins> pEnumpins;
      pFilter->EnumPins(&pEnumpins);
      IPin* pPin;
      PIN_DIRECTION pd;
      while (SUCCEEDED(pEnumpins->Next(1, &pPin, 0))) {
          if (SUCCEEDED(pPin->QueryDirection(&pd)) && pd == direct) {
              return pPin;
          }
          pPin->Release();
      }
      return NULL;
  }


ボコノン教徒  2005-11-15 20:50:17  No: 59581

VideoInfoはきちんと設定してるのでしょうか。
WMVのVideoデコーダの出力ピンのチェックがきついのかもしれません。
GraphEditで試してみても、エラーになりますか?

> if (FAILED(pGraph->Connect(pOutpin, pInpin))) {
エラーコードは何でしょうか。
Connect使うと、intelligent機能が働いてエラーの特定がしづらいので、
自力でWMVのVideoデコーダとNULLレンダラの間にサンプルグラバを
挟むようにすると、エラーが特定しやすいかも。


t.aoki  2005-11-15 22:43:46  No: 59582

ボコノン教徒さん。回答ありがとうございます。

> VideoInfoはきちんと設定してるのでしょうか。
行っている処理は上記コードの処理のみです。
なのでVideoInfoの設定というものが行えていないのかもしれません。
VideoInfoはどのインターフェイスを介して設定できるか教えていただけるでしょうか。

> GraphEditで試してみても、エラーになりますか?
GraphEdit ではいけていると思われます。
「wmv」->「WMVideo Decoder DMO」->「SampleGrabber」->「Null Renderer」
と繋げたところ再生できました。(「Null Renderer」のためイメージは表示され
ませんでしたが)

>> if (FAILED(pGraph->Connect(pOutpin, pInpin))) {
>エラーコードは何でしょうか。
VFW_E_CANNOT_CONNECT : 接続を確立する中間フィルタの組み合わせが見つからなかった
が返ってきております。

> 自力でWMVのVideoデコーダとNULLレンダラの間にサンプルグラバを
> 挟むようにすると、エラーが特定しやすいかも。
これは IPin::Connect() で接続するという判断で正しいでしょうか。
調査して試してみたいと思います。


ボコノン教徒  2005-11-16 00:51:19  No: 59583

> VideoInfoはどのインターフェイスを介して設定できるか教えていただけるでしょうか。

CMediaType::FormatType()でメディアサンプルから取得します。
これでAM_MEDIA_TYPE.pbFormatにアクセスします。

>これは IPin::Connect() で接続するという判断で正しいでしょうか。

IPin::Connect()のヘルプを見ると、「アプリケーションからこのメソッド
を呼び出さないこと」とあります。
サンプルグラバの入出力ピンにこれを実装すると、
どのようなAM_MEDIA_TYPEでピンが接続されたかがわかります。

「WMVideo Decoder DMO」->「SampleGrabber」ができないのか、
「SampleGrabber」->「Null Renderer」ができないのか、
の切り分けがまず必要かと。


ボコノン教徒  2005-11-16 01:15:18  No: 59584

日本語ヘルプの「サンプル グラバの使い方」の項を
見ながらやってるのか。

1.「Video Renderer」を自前で生成し、AddFilterした後、WMVファイルに
    対して通常のRenderをかける。
2.「Video Renderer」の入力ピンと「WMVideo Decoder DMO」の出力ピン
    にIFilterGraph::Disconnect()をかけて、ピンを切断し、
    IFilterGraph::RemoveFilter()で「Video Renderer」を削除。
3.「SampleGrabber」をAddFilterして、「WMVideo Decoder DMO」と接続。
4.「NULLレンダリングフィルタ」をAddFilterして「SampleGrabber」と接続。

まずこれができるかどうか、です。


t.aoki  2005-11-16 01:45:20  No: 59585

レスありがとうございます。

> 日本語ヘルプの「サンプル グラバの使い方」の項を
> 見ながらやってるのか
参考にしたページがこの項を参考にしていたようで、基本的な処理は同じでした。

> まずこれができるかどうか、です。
やらなければならない事を提示して頂きありがとうございます。
これから実装していきます。
少し時間がかかるかも知れませんが1段落着いた時点でまた報告致します。


t.aoki  2005-11-16 18:45:52  No: 59586

ボコノン教徒さんが示してくれた手順通り組む事で該当WMVファイルから
イメージを取得する事ができました。

ただ、現在「WMVideo Decoder DMO」は

  CComPtr<IBaseFilter> pDMO;
  CComPtr<IEnumFilters>  pEnum;
  pGraph->EnumFilters(&pEnum);
  FILTER_INFO obfi;
  IBaseFilter* pFilter;
  while (pEnum->Next(1, &pFilter, NULL) == S_OK) {
      pFilter->QueryFilterInfo(&obfi);
      if (obfi.pGraph != NULL) {
          obfi.pGraph->Release();
      }
      if (wcscmp(obfi.achName, L"WMVideo Decoder DMO") == 0 ||
          wcscmp(obfi.achName, L"WMV Screen decoder DMO") == 0
      ) {
          pDMO.Attach(pFilter);
          break;
      }
      pFilter->Release();
  }

という処理で取得しています。
ですが、この処理ですと「WMVideo Decoder DMO」のような対象となる全てのフィルタの
名前を事前に知っておかなければいけなくなります。
この「WMVideo Decoder DMO」をもっと汎用的に取得する方法はありませんでしょうか。


ボコノン教徒  2005-11-17 00:40:25  No: 59587

レンダラーの入力ピンが分かっているなら、
IPin::ConnectedTo()、IPin::QueryPinInfo()でひとつ上流の
フィルタにたどり着けます。


t.aoki  2005-11-17 01:19:07  No: 59588

> レンダラーの入力ピンが分かっているなら、
> IPin::ConnectedTo()、IPin::QueryPinInfo()でひとつ上流の
> フィルタにたどり着けます。
上記の関数を使用する事で希望する対象フィルタに辿りつく事ができました。
また、当初のイメージを取得するという目的もほぼ果たす事ができました。

ボコノン教徒さんには1から導いて頂き感謝の念に堪えません。
本当にありがとうございました。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加