ffmpegのoverlayフィルタが遅いので条件を変えてエンコード時間を見てみた
ffmpegのoverlayフィルタが遅くフルHDのビデオではトランスコード再生に必要な30fps以上で変換できません。そこでこのフィルタの条件を変えパフォーマンスに変化があるのか見てみます。
2つの音声トラックと2つのPGS字幕トラックがあるフルHD動画 TEST1.mkvを使って字幕を焼き付けます。焼き付けたファイルはa.tsとして出力します。
まずは基本的な書き方で字幕を焼き付けてみます。
-filter_complex "[0:v:0][0:s:0]overlay"
と書いています。1番目入力ファイルの1番目のビデオトラックと1番目の字幕トラックを重ねて表示します。これをVIERA用に修飾すると次のコマンドになります。ffmpeg -ss 00:10:30 -i TEST1.mkv -threads 4 -filter_complex "[0:v:0][0:s:0]overlay" -c:v mpeg2video -r 30000/1001 -c:a ac3 -async 1000 -map 0:v:0 -map 0:a:0 -map 0:a:1 -streamid 0:33 -streamid 1:34 -streamid 2:35 -f mpegts -y a.ts
この結果は、frame= 103 fps= 29 q=31.0 size= 1122kB time=00:00:03.67 bitrate=2501.6kbits/ frame= 117 fps= 28 q=31.0 size= 1237kB time=00:00:04.12 bitrate=2458.1kbits/ frame= 133 fps= 29 q=31.0 size= 1361kB time=00:00:04.57 bitrate=2439.3kbits/ frame= 147 fps= 28 q=31.0 size= 1538kB time=00:00:05.17 bitrate=2433.2kbits/となりました。ぎりぎり30fpsに届きません。トランスコードに使うと変換が間に合わないです。
次にスケールとfpsをフィルタ内のストリーム内で変換して重ねてみます。
-filter_complex "[0:v:0]scale=1920x1080,fps=fps=30000/1001[v1];[0:s:0]scale=1920x1080,fps=fps=30000/1001[v2];[v1][v2]overlay"
コマンドは、ffmpeg -ss 00:10:30 -i TEST1.mkv -threads 4 -filter_complex "[0:v:0]scale=1920x1080,fps=fps=30000/1001[v1];[0:s:0]scale=1920x1080,fps=fps=30000/1001[v2];[v1][v2]overlay" -c:v mpeg2video -r 30000/1001 -c:a ac3 -async 1000 -map 0:v:0 -map 0:a:0 -map 0:a:1 -streamid 0:33 -streamid 1:34 -streamid 2:35 -f mpegts -y a.ts
となります。この結果は、frame= 110 fps= 31 q=24.8 size= 1213kB time=00:00:03.83 bitrate=2590.6kbits/ frame= 126 fps= 31 q=31.0 size= 1331kB time=00:00:04.37 bitrate=2489.6kbits/ frame= 141 fps= 31 q=31.0 size= 1452kB time=00:00:04.95 bitrate=2401.1kbits/ frame= 158 fps= 31 q=24.8 size= 1642kB time=00:00:05.56 bitrate=2417.4kbits/となりました。若干速くなっています。なぜ?
かろうじて30fpsを超えています。しかし、余裕は無いのでトランスコードに使うには無理がありそうです。
これらのエンコード中のCPU使用率を見ると50%ほどにしかなりません。threadsオプションの値を増やすとエンコード速度が遅くなったりします。
マルチスレッド処理が有効に活用されていないようなので、パイプ処理を使って映像エンコードと音声エンコードを分離してみる事にします。
コマンドは、
ffmpeg -ss 00:10:30 -i TEST1.mkv -threads 4 -filter_complex "[0:v:0]scale=1920x1080,fps=fps=30000/1001[v1];[0:s:0]scale=1920x1080,fps=fps=30000/1001[v2];[v1][v2]overlay" -c:v mpeg2video -r 30000/1001 -an -f mpegts - | ffmpeg -ss 00:10:30 -i TEST1.mkv -f mpegts -i - -c:v copy -c:a ac3 -async 1000 -map 1:v:0 -map 0:a:0 -map 0:a:1 -streamid 0:33 -streamid 1:34 -streamid 2:35 -f mpegts -y a.ts
とします。先に字幕を焼き付けmpegtsにトランスコードします。その後、音声をトランスコードして映像と合わせファイルを出力します。この結果は、frame= 98 fps= 32 q=24.8 size= 943kB time=00:00:03.20 bitrate=2411.0kbits/ frame= 116 fps= 32 q=31.0 size= 1039kB time=00:00:03.80 bitrate=2238.3kbits/ frame= 132 fps= 32 q=31.0 size= 1134kB time=00:00:04.33 bitrate=2141.4kbits/ frame= 147 fps= 32 q=31.0 size= 1288kB time=00:00:04.83 bitrate=2181.0kbits/となりました。う~ん、変わったのだろうか。気持ち速くなったようですが常用できるパフォーマンスではないです。
他にマルチスレッドが活用されそうな条件を想定してみます。1画面を何分割かして並行してoverlayフィルタをかければよいのでは?と思いました。ビデオストリームをいくつかcropし、複数のトリームを作ります。そしてoverlayをすれば・・・しかし、最後には1画面に合成しなくてはなりません。この時画面全面のoverlayフィルタを使わなくてはなりませんので意味がありません。
検索をすると、この様に一つの画面をいくつかに分割する動作をしてくれそうなslicifyフィルタなるものがあるようなのですが、私が使っているffmpegのビルドには存在しません。どのようにビルドすると使えるのかもよく判りませんでした。
しかたがないので、最後の手段として字幕を小さくしてoverlayしてみます。字幕の大きさを1/2にしてしまいます。
ffmpeg -ss 00:10:30 -i TEST1.mkv -threads 4 -filter_complex "[0:s:0]scale=w=iw/2:h=ih/2,fps=fps=30000/1001[sub];[0:v:0][sub]overlay=(W-w)/2:H-h" -c:v mpeg2video -r 30000/1001 -c:a ac3 -async 1000 -map 0:v:0 -map 0:a:0 -map 0:a:1 -streamid 0:33 -streamid 1:34 -streamid 2:35 -f mpegts -y a.ts
これで1/2にした字幕を画面中央下に表示します。シーンによっては字幕の位置がずれてしまいますが基本的な字幕の機能ははたします。ちょっと字が小さくて見にくくなりますけど。この結果は、frame= 106 fps= 42 q=31.0 size= 1110kB time=00:00:03.80 bitrate=2392.1kbits/ frame= 131 fps= 43 q=31.0 size= 1324kB time=00:00:04.57 bitrate=2373.5kbits/ frame= 153 fps= 43 q=31.0 size= 1529kB time=00:00:05.37 bitrate=2332.2kbits/ frame= 175 fps= 43 q=31.0 size= 1724kB time=00:00:06.04 bitrate=2336.6kbits/となりました。40fpsを超えていますのでトランスコードに使えます。
意地でもffmpegで字幕を表示したいという場合はこのように実装するしかなさそうです。
あとはslicifyフィルタを試してみたいです。
コメント
コメントを投稿