ようこそゲストさん

mitc - 日記

2008/10/19(日) ffmpegで動画から画像を切り出す

はてブ 2008/10/19 15:18 雑記miff
ffmpegFFmpeg 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: そら 2009年09月14日(月) 深夜1時07分

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

2: miff 2009年09月15日(火) 午後7時43分

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

3: flv 2011年06月06日(月) 午後7時50分

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


名前:  非公開コメント   

E-Mail(任意/非公開):
URL(任意):
  • TB-URL  http://mitc.s279.xrea.com/adiary.cgi/088/tb/