ffmpegで動画から画像を切り出す


ffmpeg FFmpeg SVN-r15533

ffmpegのバイナリはこちらのサイトから頂きました:
http://blog.k-tai-douga.com/article/20486571.html


最近、語学学習に携帯音楽プレイヤーが欲しいと思い、

  1. どうせ買うならiPod touchを試してみたいな……
  2. iPod touch使うならiTunesだよな……
  3. そういえばカバーフローとかあったよな……

という流れで手持ちのCDなどから気に入った曲をiTunesに登録し、Amazonなどから手軽にジャケット画像が入手できるものはアートワークを登録していっています。

そこで問題になったのが個人が作成してWebで配布されている音楽。
昔はそういう音楽は基本的に元々ジャケットなど存在する筈も無いのでどうこう考える以前の問題だったんですが、昨年のVOCALOIDブームで作成された曲などはニコニコ動画やYoutubeなどの動画共有サイトに投稿されたものが主であり、私としてはせっかくだからそういうサイトでサムネイルとして登録されていた画像を登録したいなと思いました。
しかし、サムネイルの画像は解像度が低い……!というわけでflvファイルからある瞬間の画像を切り出せると嬉しいと考えて色々試してみました。

参考

結論から書くと、次のオプションを使います

オプション 概要
i 入力ファイル指定
ss 何秒目から処理するか
vframes 何フレームを対象にするか
f image2 画像として出力する
ffmpeg -i in.flv -ss 1 -vframes 1 -f image2 out.jpg

これでin.flvの1秒目から1フレームをJPEG形式でout.jpgという名前で出力することを指定しています。
(画像形式は拡張子から判断しているようです。out.pngとするとPNG形式で出力されます)

これで万事OKと思ったんですが、試しに100秒の動画で1-100秒まで1枚ずつ画像を切り出していくとおかしなことに……。

バッチファイル:VideoSplit.bat

for /l %%B in (0,1,100) do ffmpeg -i in.flv -ss %%B -vframes 1 -f image2 X://out/%%B.jpg

こんな感じでバッチファイルを作ると、1秒,2秒……と動画の頭の方では一瞬で画像が出力されるのが、動画の後ろの方に移るに従って段々と次のようなログが出て時間がかかるようになっていきました。

Seems stream 0 codec frame rate differs from container frame rate: 1000.00 (1000/1) -> 24.00 (24/1)
Input #0, flv, from 'in.flv':
Duration: 00:01:40.25, start: 0.000000, bitrate: 128 kb/s
Stream #0.0: Video: vp6f, yuv420p, 512x384, 24.00 tb(r)
Stream #0.1: Audio: mp3, 44100 Hz, stereo, s16, 128 kb/s
Output #0, image2, to 'out2.jpg':
Stream #0.0: Video: mjpeg, yuvj420p, 512x384, q=2-31, 200 kb/s, 24.00 tb(c)
Stream mapping:
Stream #0.0 -> #0.0
Press [q] to stop encoding
frame=    0 fps=  0 q=0.0 size=      -0kB time=10000000000.00 bitrate=  -0.0kbit
frame=    0 fps=  0 q=0.0 size=      -0kB time=10000000000.00 bitrate=  -0.0kbit
frame=    0 fps=  0 q=0.0 size=      -0kB time=10000000000.00 bitrate=  -0.0kbit
frame=    0 fps=  0 q=0.0 size=      -0kB time=10000000000.00 bitrate=  -0.0kbit
frame=    0 fps=  0 q=0.0 size=      -0kB time=10000000000.00 bitrate=  -0.0kbit
frame=    1 fps=  0 q=6.9 Lsize=      -0kB time=0.04 bitrate=  -4.2kbits/s
video:21kB audio:0kB global headers:0kB muxing overhead -100.103083%

意味はよくわからないんですが、とにかく100個の画像を切り出すだけで非常に時間がかかってしまいます。
これはこういうものなのだろうと諦めていたんですが、色々試しているうちに回避方法がわかりました。

上のffmpegへのオプションの渡し方に問題があり、次のようにすれば何秒目の画像であろうと一瞬で切り出せます。

ffmpeg -ss 1 -vframes 1 -i in.flv -f image2 out.jpg

違いは、-i in.flvが-ssより前にあるか後にあるかです。
理由はよくわからないんですが、検索してみるとffmpegはオプションの順番によって処理の順番が変わり、オプションの順番はかなりシビアに決まっているとのこと。
先にファイルを読み込んだ後に50秒目まで読みに行くか、50秒目とわかっておいてファイルの50秒目を読み込みに行くかの違い、といったところでしょうか?わかりませんが。
とにかく、これで手軽に画像の切り出しが利用できそうで嬉しいです。

おまけ

音のみ抽出

ffmpeg -i in.flv -acodec copy out.mp3

acodecは音コーデックを指定して出力するオプションで、そこにcopyを渡すと動画に格納されているデータをそのままコピーして出力するという意味になるようです。
(要するに無劣化で取り出せるということ?)

動画の全フレームを画像に切り分ける

ffmpeg -i in.flv -f image2 %d.jpg

%dで0-Nまでの連続した番号をあらわすみたいです。
この場合なら動画が2000フレームで構成されていた場合は1.jpgから2000.jpgの連番ファイルが出力されます。

動画の指定した秒数から指定したフレーム数を画像に切り分ける

ffmpeg -ss 50 -vframes 50 -i in.flv -f image2 %d.jpg

指定した秒数から1枚の画像を切り出す方法と、動画の全フレームを画像に切り分ける方法の組み合わせです。
この場合も-i in.flvをオプション指定の頭に持ってくると処理時間が余分にかかることになります。

動画の情報を取得

ffmpeg -i in.flv

動画が全部で何秒なのかとか、フレームレートが何fpsだとか、動画の形式は何なのかといった情報を取得します。
実行すると標準エラー出力に次のように出力されます。

Seems stream 0 codec frame rate differs from container frame rate: 1000.00 (1000/1) -> 24.00 (24/1)
Input #0, flv, from 'in.flv':
Duration: 00:01:40.25, start: 0.000000, bitrate: 128 kb/s
Stream #0.0: Video: vp6f, yuv420p, 512x384, 24.00 tb(r)
Stream #0.1: Audio: mp3, 44100 Hz, stereo, s16, 128 kb/s
Must supply at least one output file

プログラムから利用する時には、この情報を文字列処理して取得する方法が取られているみたいですね。
本当はライブラリ版を使えるといいんですが……。

  1. 長い動画の後ろの方のサムネイルを取得しようとすると時間がかかる問題に悩まされていましたが、この記事で解決することができました!
    ありがとうございます!

  2. お役に立てて何よりです.
    ffmpeg,初見はややこしいですが,使い方がわかると処理も速くていいですよね.

  3. ffmpegとMyHome Portalと組み合わせると、ブラウザで、ローカルデイスクの写真や動画のサムネイル表示や動画の再生が出来ます。
    http://ok2nd.blog87.fc2.com/blog-entry-221.html

Leave a Comment


NOTE - You can use these HTML tags and attributes:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>