Ubuntu 12.04 で,TS ファイルを H.264 形式に圧縮する方法をご紹介.
動画が HDD に貯まってきたので整理しようと思い立ったものの,この手のツールの状況には詳しくなく,解にたどり着くまでに時間がかかりました.問題は音ズレ.動画と音声のタイミングが時間が進むにつれて合わなくなってしまいます.
上手くいかなかった方法
いろいろ調べて,以下のような方法を試してみたものの,いずれも微妙な結果.
- ffmpeg の -vsync オプション
- 今回使用した TS ファイルでは効果無し.
- ffmpeg の -async オプション
- ズレの最大幅は緩和されたものの,違和感有り.
- x264 + MP4Box の dur 指定オプション
- dur で動画と音声の短い方の時間を指定するんですが,指定が面倒.違和感はほとんど無い.
お手軽 HandBrake
さらにいろいろ調べていくと,手軽にエンコードするためのツールとして HandBrake というものがあることを発見.
早速インストール.
1 2 3 4 5 |
% sudo add-apt-repository ppa:stebbins/handbrake-snapshots % sudo apt-get update % sudo apt-get install handbrake-cli |
そして,エンコード開始.
1 |
% HandBrakeCLI --input 「入力 .ts ファイル」 --encoder x264 --x264-preset slower --x264-tune ssim --quality 20 --encopts 'vbv-maxrate=26000:vbv-bufsize=22000' --rate 29.97 --ab 128 --deinterlace slower --output 「出力 .mp4 ファイル」 |
できあがったファイルを再生してみると,音ズレ無し!
やったね!
画質設定
エンコードで気になるのは,画質.そこで, --quality で指定する値と,エンコード時間(time),圧縮率(compression),画質の劣化(SSIM)の関係を調べてみました.SSIM の縦軸は右側です.SSIM は 1 が劣化無しで,そこから値が小さくなるにつれ,劣化度合いが大きくなります.
私の場合,1/3 程度には圧縮したかったので,20 を指定しています.ちなみに, vbv-maxrate の指定については,このページが参考になります.
自動エンコード
HandBrake によるエンコードには時間がかかります.先ほどのグラフの時間を見ていただくと分かるように,だいたい動画の長さの 1.5 倍ほどの時間がかかっています.
わざわざ待つのは時間の無駄なので,特定のディレクトリにファイルを置くだけで自動的にエンコードするようにしてみました.
まず,指定したディレクトリにファイルが追加されたらエンコードするスクリプト(encode.zsh)を作成.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
#!/bin/zsh # configuration typeset -r QUEUE_PATH=/storage/encode/queue typeset -r OUTPUT_PATH=/storage/encode typeset -r TEMP_PATH=/storage/encode/temp typeset -r PROCESSED_PATH=/storage/encode/processed typeset -r LOG_PATH=/storage/encode/log VIDEO_OPT="--encoder x264 --x264-preset slower --x264-tune ssim --quality 20 --encopts vbv-maxrate=26000:vbv-bufsize=22000" AUDIO_OPT="--ab 128 --deinterlace slower" function scan_and_encode { for i in ${QUEUE_PATH}/*.ts; do # check if the file is opend sudo lsof $i > /dev/null 2>&1 if (( $? != 1 )); then continue fi fps=<code>avprobe ${i} |& grep -m 1 'mpeg2video (Main)' | ruby -n -e 'puts $1 if $_=~/([\d.]+) tbr/' dur=<code>avprobe ${i} |& grep -m 1 Duration | ruby -n -e 'puts ($1.to_i*3600+$2.to_i*60+$3.to_i) if $_=~/([\d]+):([\d]+):([\d]+)/' temp=<code>mktemp -u "${TEMP_PATH}/.${i:t:r}.XXXXXX"</code>.mp4 # encode echo HandBrakeCLI --input "${i}" ${=VIDEO_OPT} --rate ${fps} ${=AUDIO_OPT} --output ${temp} | tee ${LOG_PATH}/${i:t:r}.log HandBrakeCLI --input "${i}" ${=VIDEO_OPT} --rate ${fps} ${=AUDIO_OPT} --output ${temp} >>& ${LOG_PATH}/${i:t:r}.log if (( $? != 0 )); then echo "ERROR[handbrake] ${i}" >&2 continue fi # check durarion dur_out=<code>avprobe ${temp} |& grep -m 1 Duration | ruby -n -e 'puts ($1.to_i*3600+$2.to_i*60+$3.to_i) if $_=~/([\d]+):([\d]+):([\d]+)/' dur_diff=<code>echo "sqrt((${dur} - ${dur_out})^2)" | bc if (( $dur_diff > 2 )); then echo "ERROR[duration] ${i}" >&2 continue fi mv ${temp} ${OUTPUT_PATH}/${i:t:r}.mp4 mv ${i} ${PROCESSED_PATH}/ echo OK ${i} done } scan_and_encode while inotifywait -e close_write ${QUEUE_PATH} &>/dev/null; do scan_and_encode done |
このスクリプトは,QUEUE_PATH で指定したディレクトリを監視し,*.ts ファイルがあればエンコードして,OUTPUT_PATH に出力します.成功した場合は,*.ts ファイルを PROCESSED_PATH に移動します.
lsof を使って,他プロセスのファイルアクセス有無を確認しているので,QUEUE_PATH にファイルをコピーする際も安心です.
このスクリプトを cron で定期的に実行するようにすれば,自動化完了.具体的には,下記のようなエントリーを追記します.
1 |
0 * * * * kimata setlock /tmp/encode.lock /path/to/encode.zsh |
コメント