DirectShow関係


3.キャプチャ

前回カメラから表示させた映像をファイルに保存する方法を説明します。
まずは前回同様、GraphEditでグラフを設計します。

今回も当然カメラを使いますので、カメラのフィルタを追加します。

次にInfinite Pin Tee Filterというフィルタを追加します。(DirectShow Filtersカテゴリにあります)
このフィルタは入力が1つで、出力が複数(可変)のフィルタで、
データの流れを2つに以上に分けることができます。

今回はファイルに出力しながらプレビュー表示も行いたいので
ファイル出力する流れと、画面に表示する流れの2つに分けます。

2つのフィルタが追加された状態で、
カメラの出力ピン(~Capture)からRender Pinを行います。
(出力ピンで右クリック、ポップアップメニューからRender Pinをクリック)

これでプレビューの流れが完成しました。
次にファイルに出力する流れを作ります。

AVI MuxフィルタとFile writerフィルタを追加します。(どちらもDirectShow Filtersカテゴリにあります)
File writerフィルタを追加しようとすると、ファイル名を入力するダイアログが表示されます。
ここで出力するファイル名を入力してください。

AVI Muxフィルタは通常ビデオの流れと音声の流れを入力し、AVI形式のストリームを出力します。
で、出力されたAVI形式のストリームをFile writerフィルタでファイルに出力すれば
AVIファイルが出来上がるわけです。

最後にInfinite Pin Tee Filterの出力ピンで開いているピンでRenderPinを行えば
AVI MuxフィルタとFile writerフィルタが接続され、グラフが完成します。
Infinite Pin Tee Filterの出力ピンは何度も接続/切断を繰り返していると名前が変わってきますが特に問題はないです。

これでグラフを実行すれば別ウィンドウでプレビューが表示されつつ、AVIファイルが出力されます。

このままだと無圧縮のAVIファイルで出力されます。
何らかの圧縮(コーディック)を行いたいならば、AVI Muxフィルタの前にコーディックフィルタを追加します。
コーディックフィルタはVideo Compressorsカテゴリにあります。

上図はMpeg4コーディックを追加したみた例です。(MPEG4圧縮されたAVIファイルが出力されます)

ではVBで同じことを行うコードを紹介します。

Option Explicit

'カメラのフィルタ名
Private Const CAMERA_FILTER_NAME$ = "STV0680 Camera" 'Che-es! SPYZ
Private Const CAMERA_OUTPUTPIN_NAME$ = "~Capture"

'グラフ
Private mGrp As QuartzTypeLib.FilgraphManager

'Previewボタン
Private Sub Command1_Click()
  'プレビューを開始
  MakePreviewGraph
  mGrp.Run
End Sub

'Captureボタン
Private Sub Command2_Click()
  'キャプチャを開始
  MakeCaptureGraph
  mGrp.Run
End Sub

'Stopボタン
Private Sub Command3_Click()
  If Not mGrp Is Nothing Then
    mGrp.Stop
  End If
End Sub


'プレビューグラフの作成
Private Sub MakePreviewGraph()

  '新しいグラフの作成
  Set mGrp = New QuartzTypeLib.FilgraphManager

  'グラフにキャプチャ(カメラ)フィルタを追加する
  Dim cameraflt As QuartzTypeLib.IFilterInfo
  Set cameraflt = AddFilter(mGrp, CAMERA_FILTER_NAME$)

  'カメラの出力ピンを取得
  Dim camerapin As QuartzTypeLib.IPinInfo
  cameraflt.FindPin CAMERA_OUTPUTPIN_NAME$, camerapin

  'カメラの出力ピンからフィルタを接続し、グラフを構築する
  camerapin.Render

End Sub


'キャプチャグラフの作成
Private Sub MakeCaptureGraph()

  '新しいグラフの作成
  Set mGrp = New QuartzTypeLib.FilgraphManager

  'グラフにキャプチャ(カメラ)フィルタを追加する
  Dim cameraflt As QuartzTypeLib.IFilterInfo
  Set cameraflt = AddFilter(mGrp, CAMERA_FILTER_NAME$)

  'カメラの出力ピンを取得
  Dim camerapin As QuartzTypeLib.IPinInfo
  cameraflt.FindPin CAMERA_OUTPUTPIN_NAME$, camerapin

  'Infinite Pin Tee Filterを追加
  Dim infflt As QuartzTypeLib.IFilterInfo
  Set infflt = AddFilter(mGrp, "Infinite Pin Tee Filter")

  'プレビュー用の流れを繋げる
  camerapin.Render

  'AVI Muxフィルタを追加
  AddFilter mGrp, "AVI Mux"

  'FileWriterフィルタを追加
  Dim writerflt As QuartzTypeLib.IFilterInfo
  Set writerflt = AddFilter(mGrp, "File writer")

  '出力ファイル名の設定
  Dim snk As DshowForVBLib.IFileSinkFilterForVB
  Set snk = writerflt.Filter
  snk.SetFileName Text1.Text, Nothing

  'Infinite Pin Tee Filterの開いている出力ピンを探す
  Dim pn As QuartzTypeLib.IPinInfo
  Dim pn2 As QuartzTypeLib.IPinInfo
  Dim infpin As QuartzTypeLib.IPinInfo
  For Each pn In infflt.Pins
    On Error Resume Next
      Set pn2 = pn.ConnectedTo
      If Err Then
        Set pn2 = Nothing
      End If
    On Error GoTo 0
    If pn2 Is Nothing Then
      Set infpin = pn
      Exit For
    End If
  Next

  'キャプチャの流れを繋げる
  infpin.Render

End Sub


'レジストリに登録されているフィルタをグラフに追加する
Public Function AddFilter(ByRef Grp As QuartzTypeLib.FilgraphManager, ByVal FilterName$) As IFilterInfo
  Dim regflt As QuartzTypeLib.IRegFilterInfo
  For Each regflt In Grp.RegFilterCollection
    If regflt.Name = FilterName$ Then
      regflt.Filter AddFilter
      Exit Function
    End If
  Next
End Function

今回はフォームもあります。

コントロール 名前 適用
テキストボックス Text1 出力するファイル名を入力する。
コマンドボタン Command1 表示名「Preview」。プレビュー表示を開始する。
コマンドボタン Command2 表示名「Capture」。キャプチャを開始する。
コマンドボタン Command3 表示名「Stop」。プレビュー又はキャプチャを終了する。

また参照設定で下記のタイプライブラリを指定する必要があります。

DshowForVBLib

これはデフォルトでは一覧に表示されていないと思います。
実はこのタイプライブラリはSDKのサンプルディレクトリにあります。

(SDKインストールフォルダ)\samples\Multimedia\VBSamples\DirectShow\Editing\DShowVBLib\dshowvblib.tlb

このタイプライブラリを参照設定ダイアログの参照ボタンで選択してください。

使い方は、起動後Previewボタンを押すとプレビューを始め、
Captureボタンを押すとキャプチャを開始します。Stopボタンで終了です。
キャプチャ前にテキストボックスに出力ファイル名を入力しておきます。

例によって順に説明していきます。

Option Explicit

'カメラのフィルタ名
Private Const CAMERA_FILTER_NAME$ = "STV0680 Camera" 'Che-es! SPYZ
Private Const CAMERA_OUTPUTPIN_NAME$ = "~Capture"

'フィルタグラフマネージャ
Private mGrp As QuartzTypeLib.FilgraphManager

モジュールセクションは前回とまったく同じです。流用して作ったので(笑

'Previewボタン
Private Sub Command1_Click()
   'プレビューを開始
   MakePreviewGraph
   mGrp.Run
End Sub
'プレビューグラフの作成
Private Sub MakePreviewGraph()

   '新しいグラフの作成
   Set mGrp = New QuartzTypeLib.FilgraphManager

   'グラフにキャプチャ(カメラ)フィルタを追加する
   Dim cameraflt As QuartzTypeLib.IFilterInfo
   Set cameraflt = AddFilter(mGrp, CAMERA_FILTER_NAME$)

   'カメラの出力ピンを取得
   Dim camerapin As QuartzTypeLib.IPinInfo
   cameraflt.FindPin CAMERA_OUTPUTPIN_NAME$, camerapin

   'カメラの出力ピンからフィルタを接続し、グラフを構築する
   camerapin.Render

End Sub

Previewボタン押下時の処理で、プレビュー用のグラフを作成し実行しています。
コードは前回と同じなので前のページを参照してください。(この辺も前回のをコピペしただけ〜)

'Captureボタン
Private Sub Command2_Click()
   'キャプチャを開始
   MakeCaptureGraph
   mGrp.Run
End Sub
'キャプチャグラフの作成
Private Sub MakeCaptureGraph()

   '新しいグラフの作成
   Set mGrp = New QuartzTypeLib.FilgraphManager

   'グラフにキャプチャ(カメラ)フィルタを追加する
   Dim cameraflt As QuartzTypeLib.IFilterInfo
   Set cameraflt = AddFilter(mGrp, CAMERA_FILTER_NAME$)

   'カメラの出力ピンを取得
   Dim camerapin As QuartzTypeLib.IPinInfo
   cameraflt.FindPin CAMERA_OUTPUTPIN_NAME$, camerapin

   'Infinite Pin Tee Filterを追加
   Dim infflt As QuartzTypeLib.IFilterInfo
   Set infflt = AddFilter(mGrp, "Infinite Pin Tee Filter")

   'プレビュー用の流れを繋げる
   camerapin.Render

   'AVI Muxフィルタを追加
   AddFilter mGrp, "AVI Mux"

   'FileWriterフィルタを追加
   Dim writerflt As QuartzTypeLib.IFilterInfo
   Set writerflt = AddFilter(mGrp, "File writer")

   '出力ファイル名の設定
   Dim snk As DshowForVBLib.IFileSinkFilterForVB
   Set snk = writerflt.Filter
   snk.SetFileName Text1.Text, Nothing

   'Infinite Pin Tee Filterの開いている出力ピンを探す
   Dim pn As QuartzTypeLib.IPinInfo
   Dim pn2 As QuartzTypeLib.IPinInfo
   Dim infpin As QuartzTypeLib.IPinInfo
   For Each pn In infflt.Pins
     On Error Resume Next
       Set pn2 = pn.ConnectedTo
       If Err Then
         Set pn2 = Nothing
       End If
     On Error GoTo 0
     If pn2 Is Nothing Then
       Set infpin = pn
       Exit For
     End If
   Next

   'キャプチャの流れを繋げる
   infpin.Render

End Sub

今回の肝の部分です。長いですが順に説明します。

'キャプチャグラフの作成
Private Sub MakeCaptureGraph()

   '新しいグラフの作成
   Set mGrp = New QuartzTypeLib.FilgraphManager

   'グラフにキャプチャ(カメラ)フィルタを追加する
   Dim cameraflt As QuartzTypeLib.IFilterInfo
   Set cameraflt = AddFilter(mGrp, CAMERA_FILTER_NAME$)

   'カメラの出力ピンを取得
   Dim camerapin As QuartzTypeLib.IPinInfo
   cameraflt.FindPin CAMERA_OUTPUTPIN_NAME$, camerapin

個々まではプレビューと同じです。
カメラフィルタを追加して、出力ピンを取得しています。

   'Infinite Pin Tee Filterを追加
   Dim infflt As QuartzTypeLib.IFilterInfo
   Set infflt = AddFilter(mGrp, "Infinite Pin Tee Filter")

   'プレビュー用の流れを繋げる
   camerapin.Render

Infinite Pin Tee Filterを追加して、プレビュー用の流れを作っています。
やり方は他のフィルタの追加と変わりません。
この状態で、上の2番目の図の状態になります。

   'AVI Muxフィルタを追加
   AddFilter mGrp, "AVI Mux"

   'FileWriterフィルタを追加
   Dim writerflt As QuartzTypeLib.IFilterInfo
   Set writerflt = AddFilter(mGrp, "File writer")

AVI MuxフィルタとFile writerフィルタを追加しています。
AVI Muxフィルタはあとでなにかする必要がないので、戻り値は破棄してます。

   '出力ファイル名の設定
   Dim snk As DshowForVBLib.IFileSinkFilterForVB
   Set snk = writerflt.Filter
   snk.SetFileName Text1.Text, Nothing

File writerフィルタからIFileSinkFilterForVBインターフェースを取得し、
SetFileNameプロパティでファイル名を設定します。
2番目の引数はNothingでかまいません(メディアタイプを指定したりします)。

   'Infinite Pin Tee Filterの開いている出力ピンを探す
   Dim pn As QuartzTypeLib.IPinInfo
   Dim pn2 As QuartzTypeLib.IPinInfo
   Dim infpin As QuartzTypeLib.IPinInfo
   For Each pn In infflt.Pins
     On Error Resume Next
       Set pn2 = pn.ConnectedTo
       If Err Then
         Set pn2 = Nothing
       End If
     On Error GoTo 0
     If pn2 Is Nothing Then
       Set infpin = pn
       Exit For
     End If
   Next

   'キャプチャの流れを繋げる
   infpin.Render

End Sub

キャプチャ用の流れを繋げるために、Infinite Pin Tee Filterの開いている出力ピンを検索し、
Renderメソッドでつなげています。
FindPinで"Output2"とかを探させれば楽なのですが、
名前が変わる可能性があるのちゃんと検索してみました。
ConnectedToメソッドで接続されている相手のピンを取得できるのですが、
その戻りが無いもの(=エラーが発生するもの)を探して
そのピンからRenderメソッドを呼び出してつなげてます。

これでキャプチャ用のグラフ(プレビュー表示付き)が作成されます。

このままだと無圧縮のAVIが出力されますが、
キャプチャ用のRenderを行う前にエンコーダフィルタを追加すれば
MPEGなどのエンコードをかけることもできます。

今回はカメラからソースを取得しましたが、
既に存在するファイルからグラフを作成すれば、
エンコード形式を変換する変換ツールも作れます。

ファイルに動画をキャプチャするときに大切なのは出力ファイル名を指定する部分です。
それ以外は前回までのテクニックの組み合わせですが、
出力するには今回使ったDshowForVBLibなどを使う必要があります。

ファイル名を指定する方法はヘルプに記述がみあたらなかったのですが、
サンプルプログラムをみればすぐに見つかりました。
DirectXはVer2から使ってますが、
DirectXの場合サンプルソースもヘルプの一部と考えるべきだと思いますね。

 


上に戻る