FFmpegで欠落フレームがある場合の音ズレ対策

 映像ソースがノイズだらけの放送波とか、バグ持ちな機器で撮影とかで、動画ファイルに欠落したフレームが存在する場合、フレームの枚数で時間を管理しているプログラムやコンテナにおいては、欠落したフレーム分の時間がないものとなってしまう。こうなると、FFmpegで-asyncなどで時間を同期しようとしても、動画側の時間がずれてしまっているので、盛大に音ズレが生じてしまう。

 したがって、元のデータの時間軸を維持した状態でフレームを補完することになる。FFmpegではフレームレートを指定する方法がいくつかあるものの、欠落フレームがある場合は、時間軸を維持するためにfpsフィルターやframerateフィルター、minterpolateフィルターで欠落したフレームを補完をしてやる必要がある。

 なお、-rオプションでフレームレートを指定する方法は実在するフレームの数を維持して、フレームの数の合計×フレームレートを時間にする結果になるので不適当である。時間軸が無視される。

 一番軽いのはfpsフィルター。こいつは指定したfpsよりも元データのフレームが多い場合はフレームを捨て、少ない場合は既存のフレームで補完する。逆に一番重いのはminterpolateフィルターで、フレームを予測して補完する。

 まあ、自分の場合は映画やらアニメーションは逆テレシネして24 fpsにしてしまうので、fpsフィルターで一旦30 fpsにして、そこからdecimateをかけてしまう。

% ffmpeg -i in.ts -vf  bwdif=0:-1:1,fps=30,decimate,removelogo=logo.png -acodec copy -async 100 -vcodec  -pix_fmt yuv420p -vcodec libx264 -tune film -x264opts "nal-hrd=vbr:aud:colorprim=bt709:transfer=bt709:colormatrix=bt709:sar=4/3:weightp=0:b-pyramid=strict:slices=4:bframes=3:keyint=24:vbv-maxrate=40000:vbv-bufsize=30000:ref=4:level=4.1" out.m2ts

 指定するfpsは、入力するファイルのfpsを維持して、ただ単純に欠落したフレームを補完するなら30000/1001(約29.97 fps)のようにする。一方で扱いやすい形にしたいのであれば整数の30にする(NTSC系統の30000/1001 fpsだとドロップフレームがあるし)。映画やらアニメーションは本来NTSCの24000/1001 fpsではなく、24.0 fpsであるので、逆テレシネ前の段階で30.0 fpsにしておけばdecimateフィルターが24.0 fpsにしてくれる。

 追記。Past duration 0.xxxxxx too largeというメッセージが出る場合は、fpsフィルターを別のものに変えてみる。自分のところではminterpolateフィルターではこのメッセージは消えず、framerateフィルターにすると消えた。 なお、decimateの後ろにfps=24とやってもメッセージは消えるものの、これでは元ソースに欠けがあると音ズレの原因になる。