コマンドラインからmp4動画の任意場面をキャプチャする

キャプチャに使用するプログラムはフリーの動画プレイヤーとして有名なVLC Media Playerです。
インストールするとGUIで使用できるのはもちろんですが、CUIでも使用できます。
そして、VLCにはキャプチャ機能が備わっており、CUIからも使用できます。つまり、これはプログラムから起動(例えばfork&exec)させ、裏(GUI表示なし)でキャプチャができることを意味しています。

キャプチャは以下のコマンドで可能です。

cvlc hoge.mp4 --rate=1 --video-filter=scene --vout=dummy --start-time=0 --stop-time=0.5 --scene-format=jpg --scene-ratio=500 --scene-prefix=foo --scene-replace --scene-path=. vlc://quit

start-timeとstop-timeでキャプチャする対象となるシーンの位置を指定します。(単位は秒)
できるだけコマンド実行を早く終わらせるために、start-timeとstop-timeの間は小さくしたほうがいいです。(ちなみに同じ値にするとキャプチャできません。)
scene-ratioはキャプチャをする間隔(何フレーム毎にキャプチャするか)で、上記のように比較的大きな値にしておけばキャプチャは1度しか行われません。(上記の場合0.5秒間なので500フレームもあるはずないということです。最初のフレームをキャプチャした後、次のキャプチャまで500フレーム待ちますが、その前にstop-timeになって終了、、、という流れです。)

実行後、実行したディレクトリに
foo.jpg
というファイルができているはずです。

vectorに詰め込んだオブジェクト解放するための便利関数

久しぶりにテンプレート関数を使おうとしたときのいくつかの間違いを覚書として書いておきます。

Utilityクラスなるものを用意し、次のような静的メソッドを宣言、定義しました。

Utility.h

template<typename T>
static void Utility::releaser(std::vector<T*>& v);

Utility.cpp

template<typename T>
void Utility::releaser(std::vector<T*>& v)
{
  std::vector<T*>::iterator it;
  it = v.begin();
  while(it != v.end()){
    delete (*it);
    it = v.erase(it);
  }
}

これをコンパイルすると、

.cpp: 静的メンバ関数 ‘static void Utility::releaser(std::vector<T*>&)’ 内:
.cpp:???:???: エラー: need ‘typename’ before ‘std::vector<T*>::iterator’ because ‘std::vector<T*>’ is a dependent scope
.cpp:???:???: エラー: expected ‘;’ before ‘it’
.cpp:???:???: エラー: ‘it’ was not declared in this scope

といったエラーがでます。

because ‘std::vector’ is a dependent scope
はstd::vectorという部分がTに依存するため、型なのか変数名なのか、何なのか分からない、ということ言っています。
(VC++の場合は、問題なくコンパイルが通るらしいです。)

need ‘typename’ before ‘std::vector::iterator’
型だと分かるように「typename」をつけてください、と言っています。
この時点でこのコンパイラ自身は型だとわかっているようですが、どんなコンパイラでも通るように厳しく戒めてくれている、という理解です。

Utility.cpp

template<typename T>
void Utility::releaser(std::vector<T*>& v)
{
  typename std::vector<T*>::iterator it;

  it = v.begin();
  while(it != v.end()){
    delete (*it);
    it = v.erase(it);
  }
}

言われた通りにつけてみましたが、今度は、

Hoge.o: In function `hogehoge(void)':
Hoge.cpp:???: undefined reference to `void Utility::releaser<Hoge>(std::vector<Hoge*, std::allocator<Hoge*> >&)'

と怒られてしまいました。
これは実際にHoge.cppでUtility::releaserを使用している個所でその実体が何か分からない、ということを言っています。
Utility.cppにおいて、クラスHogeを考慮したテンプレート関数の実体化を行っていない、かつ、Hoge.cppでも実体化を行っていないためです。
そこで、Hoge.cppで実体化が行われるように、Utility.h、つまりヘッダにテンプレートの実装を書きます。
Utility.cppの実装は消去し、

Utility.h

template<typename T>
static void Utility::releaser(std::vector<T*>& v)
{
  typename std::vector<T*>::iterator it;

  it = v.begin();
  while(it != v.end()){
    delete (*it);
    it = v.erase(it);
  }
};

これで、テンプレート関数が使えます。

どうしても定義と実装を分けたい場合は、
明示的実装というものがあり、分けることもできるそうです。
予めテンプレートの使用パターンを宣言しておくというやり方です。
また、exportという記述子があり、定義と実装を分け、予めのパターン列挙も必要ないそうです。
ただし、こちらは対応コンパイラが少ないそうです。

このあたりのお話は、こちらが参考になりました。
http://d.hatena.ne.jp/pknight/20090826/1251303641
http://d.hatena.ne.jp/pknight/20090826/1251307608