低画質動画も拡大しなければ鑑賞にたえなくもない(ffmpeg な話)

何言いたいのかはわからないことはないよね、きっと。

低画質動画も拡大しなければ鑑賞にたえなくもない(ffmpeg な話)

動機

以下動画はいわゆる「低画質」な動画ね:

これを「フルスクリーン」で鑑賞してみるがよろし。アナログ放送時代の「低画質」とは違うわけよね、「デジタル圧縮による低画質」て。ストレスたまらんけ? (ブロックノイズなんかデジタル動画固有のもんだろ、かつて地上波でアナログでも「ストレス」を感じることはなかった。)

こうした動画をね、「フルスクリーンで」みたいのは、要するにこういう視聴スタイルがイヤだからだ:

そう、「フルスクリーン」は動画を拡大したいわけではなくて、「動画に集中したい」からフルスクリーンにしたいわけだ。

ffmpeg で画像にパディング

youtube から「ダウンロードする」という行為は規約違反なので注意。ひとさまの動画をどうこうする以外でありえるユースケースとしては、たとえばスクリーンキャプチャなんかで画面全体ではなくクリップした動画を作りたい場合なんかがそれにあたるだろうね。つまりこういうこと:

これは ffmpeg でこんなふうに作った:

例によって MSYS コマンドライン (unix 流儀)
1 me@host: ~$ ffmpeg -y -i input.mp4 \
2 > -vf "pad=1280:720:(ow-iw)/2:(oh-ih)/2:color=black" \
3 > output.mp4

一応基礎的な話としては、-vf として記述してる「魔法」は 4 Filtergraph description から読まないと絶対に理解出来ないわけだけれど、簡単な説明をあえて試みるとするなら:

  • 複数種類のフィルターのチェインはカンマでつなぐ
  • 個々のフィルターはパラメータを持つことがある
    • パラメータは key-value を key=value の形式で記述
    • 複数パラメータはコロン区切りで記述
  • 変数が利用でき、計算を記述出来る ((ow-iw)/2 など)

てところ。まぁなんであれ「手っ取り早く簡単に理解」なんてことは出来ないんで、詳しくなりたければ「ffmpeg プロ」にならねばならんよ、「お気楽ご気楽」は諦めなはれ。

ffmpeg で画像を縮小しつつパディング

ありがちなのがフレームサイズとして「640×480」なのに、画質としては「320×240」てヤツね。要するに「640×480 のまま視聴してもなおブロックノイズが気になるほどヒドい画質」みたいなことで、これってのはつまり「本来の画像を拡大しちゃってある」状態、てことだろうね。

なので、一つ前でやった「パディング」だけでなくて、縮小すらやりたいわけだね:

これは ffmpeg でこんなふうに作った:

例によって MSYS コマンドライン (unix 流儀)
1 me@host: ~$ ffmpeg -y -i input.mp4 \
2 > -vf "scale=iw/4*3:-1,pad=1280:720:(ow-iw)/2:(oh-ih)/2:color=black" \
3 > output.mp4

さっき説明したことから想像は付くだろうと思う。pad フィルタの前に scale フィルタをかけてるわけだけれど、「入力の幅 (iw)」の 3/4 に縮小してる。(-1 は「よきにはからえ」の意味ね、念のため。)

真っ黒だと寂しいのよね

tile フィルタは目的のものと違ったが面白いちゃぁ面白い

同じフレームを並べるものだと思って愚直に使うと全然違う:

以下で作った:

例によって MSYS コマンドライン (unix 流儀)
1 me@host: ~$ ffmpeg -y -i input.mp4 \
2 > -vf "scale=iw/4*3:-1,tile=4x3,pad=1280:720:(ow-iw)/2:(oh-ih)/2:color=black" \
3 > output.mp4

不思議な動画でしょ? 何が起こってるのかを知るには、こういう作為的な動画が必要:

同じ要領で「tile」するとこうなる:

元動画が 12fps なので1秒間の「動き」を一枚絵で見てるようなもん。スポーツの動画なんかで使う用途なのでしょうな。

ただワタシが今やりたいこととは違うわけなのね、一応完全に目的のものとは違うけれど、最新の ffmpeg では使える overlap パラメータ、init_padding パラメータを使って:

例によって MSYS コマンドライン (unix 流儀)
1 me@host: ~$ ffmpeg -y -i input.mp4 \
2 > -vf "scale=iw/4*3:-1,tile=4x3:overlap=11:init_padding=11,pad=1280:720:(ow-iw)/2:(oh-ih)/2:color=black" \
3 > output.mp4

として出来るのがこんなの:

コマ送り出来る UI がないとツラいと思うんで、細かく何が起こっているか見たいならダウンロードして何かいいプレイヤーで見てちょ。

俺的目的なら hstack, vstack だが

普通はこれは この質問と回答のように、異なる動画を並べるのに使う。結構みかけるよね、たとえばアニメやらドラマのライブリアクション動画を複数人ぶん並べるみたいなね。

複雑だし一撃で可能なやり方がわかんなかった。(その場限りのだけど)シェルスクリプトにした(2×2 の例):

音声ありの場合。scale は適宜。
 1 #! /bin/sh
 2 trap 'rm "${ofilebase}-tmp".mp4' 0 1 2 3 15
 3 
 4 ifile="$1"
 5 base="`basename \"$1\"`"
 6 ofilebase="$base-2x2"
 7 
 8 ffmpeg -y -i "$ifile" -i "$ifile" -i "$ifile" -i "$ifile" -i "$ifile" \
 9 -filter_complex \
10   "[0:v][1:v]hstack[t];[2:v][3:v]hstack[b];[t][b]vstack[v]" \
11 -map "[v]" -map 4:a -c:a copy "${ofilebase}-tmp".mp4
12 
13 ffmpeg -y -i "${ofilebase}-tmp".mp4 \
14   -vf "scale=iw:-1,pad=1280:720:(ow-iw)/2:(oh-ih)/2:color=black" \
15   "${ofilebase}".mp4
音声なしの場合。scale は適宜。
 1 #! /bin/sh
 2 trap 'rm "${ofilebase}-tmp".mp4' 0 1 2 3 15
 3 
 4 ifile="$1"
 5 base="`basename \"$1\"`"
 6 ofilebase="$base-2x2"
 7 
 8 ffmpeg -y -i "$ifile" -i "$ifile" -i "$ifile" -i "$ifile" \
 9 -filter_complex \
10   "[0:v][1:v]hstack[t];[2:v][3:v]hstack[b];[t][b]vstack[v]" \
11 -map "[v]" -an "${ofilebase}-tmp".mp4
12 
13 ffmpeg -y -i "${ofilebase}-tmp".mp4 \
14   -vf "scale=iw/2:-1,pad=1280:720:(ow-iw)/2:(oh-ih)/2:color=black" \
15   "${ofilebase}".mp4

雰囲気はおそらく想像付くとは思う。t とか b とかは実際は「変数定義」をしてて、これは t、b でなくてもいい。とにかく hstack 2回と vstack 1回で 2×2 を作ってるわけなので…、4×3 にしたければどうすればいいかはわかるよね。面倒すぎるのでやらんけど。てわけでこれで処理するとこんな動画になる:

2018-06-17追記: 「一撃で」の方法はここに書いた

最後に文句

ブラウザで動作するプレイヤーもデスクトップアプリケーションとしてのプレイヤーも、「動画を拡大せずにフルスクリーン」出来るならね、こんなこと考えなくていいわけだよ。

そういうプレイヤーが主流になってくれるといいのだがなぁ。