2019年1月20日日曜日

エンコードした動画の簡易チェック法

そのエンコ、ちゃんとできてる?

動画(録画)のエンコードが日課になると、「エンコードはしたものの見ていない」ものが続出します。少なくとも我が家では。
ごく低確率ではあるものの、Avisynthやx264の機嫌が悪いと破綻フレームが紛れ込むことがあります。この有無を確認するにはエンコード済み動画を見ればいい話ですが、時間がなくて、なかなかこれができない。しかしながらHDDを逼迫するエンコ前のソース動画も消してしまいたいわけで、結局のところ、えいやで消してしまったり、エンコ済み動画を適当に高速シークしながら30秒ぐらいで確認したりしています。
ところが先日、高速シークでは十中八九見逃していたであろうフレーム破綻を偶然にも視聴中に見つけてしまったため、現実的な失敗チェック法を考えました。

対象フレーム

小さなブロックノイズみたいな破綻は許容するとして、映像が全体的に乱れる大きな破綻は確実に見つけたいと思います。このフレーム破綻が発生した場合、今までの流れをぶった切るシーンチェンジのようなフレームが発生すると予想され、MP4AVCではIフレームになるはずです。

ということで、動画からIフレームを抽出すれば使えそうです。方法は前回の記事に書きました。

※Iフレームの入る間隔はエンコード設定次第です。私はx264で-refも-bframeもかなり大きくしているのでIフレームは最小限ですが(つまりGOPは大きい)、YouTubeとかのストリーミング系は性質上Iフレームモリモリみたいです。

実装

Iフレームは各々画像として出力できましたが、このままでは扱いづらいので、タイル敷きで1枚にまとめます。
画像のCLI編集といえばImageMagickが有名です。調べたところ、montageコマンドにてできるそうです。
でもできればffmpegだけで完結させたいので、ビデオフィルタを調べたところ、なんとffmpegでも実現できるようです!

ffmpeg.exe  -i "in%04d.jpg" -qmin 1 -q 1 -vf tile=4x6 "tiled_out.jpg"

-q -qmin の意味は前回を参照してください。
-i の入力は出力と同じく正規表現っぽい書き方ができるので、とても扱いやすいです。
-vf tile=4x6 が今回のいちばん重要な所です。4x6は横x縦の並べたい枚数です。ただしいずれも数値を入れないとエラーになります。縦の枚数を不定にすることはできません。これは入力枚数が不明な条件下では結構困ります。そこでバッチファイルにて事前に枚数を数えて縦を確定してしまいましょう。

set /a count=0
for %%f in ("hoge\\in*.jpg") do (if exist %%f (set /a count=count+1))
set /a cols=4
set /a rows=(count+cols-1)/cols


とすることで、横と縦の枚数を計算で決定できます。
あとは tile=%cols%x%rows% としておけば、入力が何枚になろうとも自動で高さ調整されますね。

完成

動画を突っ込めばこんな感じにできあがります。
(ソース動画は、公式配布のはつゆきさくらOP動画をx264で再エンコードしたものです)


なお、24分ぐらいのソースだと、私の環境では30秒弱で生成されます。内容をダイジェストで知りたい場合にも使えそうな気がしてきました。

以上のことを自動でやってくれるバッチファイルを一応載せておきます。自己責任にてどうぞ。頭の変数5つ(少なくとも最初の1つ)を変更して、ffmpegを準備して使ってください。
@echo off
SETLOCAL
:loop
if "%~1"=="" (goto :EOF)
if not "%~x1"==".mp4" (goto exit)

rem ffmpegのある階層に移動
cd /d "D:\hoge"

rem フレーム1枚あたりの画像サイズ(:で区切ること)
rem TIPS: dar(=w/h)という変数が使えるので、1280:1280/darや、720*dar:720などと書ける
set thumbSize=320:320/dar

rem タイル表示にする際に横に何枚並べるか(縦は自動計算する)
set /a cols=6

rem 1パス目でffmpegが画像を出力するフォルダを作る場所(ダブルクオーテーション不要)
set tempFolder=%TEMP%

rem 最終出力画像の出力先(ダブルクオーテーション不要)
set dstFolder=%~dp1


::if not %dstFolder:~-1%==\ (set dstFolder=%dstFolder%\)

set tempImageDir=%tempFolder%\%~n1_ffmpeg
:: ffmpegは存在しない階層に出力できない
md "%tempImageDir%"

:: まずは全てのIフレームの取得
ffmpeg.exe -i %1 -an -qmin 1 -q 1 -vf select="eq(pict_type\,I)",scale=%thumbSize% -vsync 0 "%tempImageDir%\frame%%04d.jpg"

:: tile数を確定するために出力されたフレーム数を数える
set /a count=0
for %%f in ("%tempImageDir%\frame*.jpg") do (if exist %%f (set /a count=count+1))
echo Iフレーム数:%count%
:: 出力がなければ諦める
if count==0 (goto exit)

:: tile数の確定
set /a rows=(count+cols-1)/cols
echo 整列:%cols%x%rows%

:: ばらばらのフレーム画像から1枚の集合画像を作る
ffmpeg.exe -i "%tempImageDir%\frame%%04d.jpg" -qmin 1 -q 1 -vf tile=%cols%x%rows% "%dstFolder%\%~n1_IFs.jpg"

:: 一時画像ファイルをフォルダごと削除
rd /s /q "%tempImageDir%"

:exit
pause
shift
goto loop
ENDLOCAL

終わり。

0 件のコメント:

コメントを投稿