Chienomi

H.264 vs H.265 vs VP8 vs VP9 vs AV1

ソフトウェア

実は先のビデオ関連の記事はMimir Yokohamaのほうにupしようかと思っていた。

こっちにしてよかった…Mimir Yokohamaのフォーマットで書いてたら絶対地獄を見た…

さて、先の記事の途中でAV1を試したが、実際AV1がどの程度使い物になるのか(いや、実際は強烈に遅いので全く使い物にならないのだが)試してみたくてちょっと検証してみた。

ソースビデオはコントラスト差、動きともに激しく、部分的にはほとんど更新されないドラムマニアのプレイ動画を使用した。 10MbpsほどのH.264ビデオである。

  Metadata:
    major_brand     : isom
    minor_version   : 512
    compatible_brands: isomiso2avc1mp41
    encoder         : Lavf58.12.100
  Duration: 00:00:04.79, start: 0.308970, bitrate: 9998 kb/s
    Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p(tv, smpte170m/unknown/smpte170m), 1080x1920 [SAR 1:1 DAR 9:16], 9995 kb/s, 29.88 fps, 29.88 tbr, 15296 tbn, 59.75 tbc (default)
    Metadata:
      handler_name    : VideoHandler

libvpxはビットレートコントロールがうまくいかなかったことから、aomav1はそもそも変換できなかったことからこれらはrawvideoに変換し、それぞれvpxenc, aomencを使用して2パスで変換した。

メインビットレートはH.264で荒れる512kを選択。 VP8はあまりに小さいビットレートコントロールが困難だったため、512kについては200kを指定したCBRとした。

比較

ソース動画

Base H.264 @10Mbps

ソースは10Mbpsということもあり、暗い中非常にキレイに撮れている。 なお、撮影機材はZenfone Selfie(ZD511KL)である。

H.264

$ ffmepg -i base.mp4 -c:v libx264 -b:v 512k h264.mp4
H.264 @502kbps
H.264 @733kbps
H.264 @971kbps

そもそもH.264で荒れさせるのが基準だったため、512kでは動きのある部分は完全にブロックだし、パッド部分も相当粗い。

ビットレートが上がると順当にスムーズになっていき、素直な特性のようだ。

H.265

$ ffmepg -i base.mp4 -c:v libx265 -b:v 512k h265.mp4
H.265 @474kbps
H.265 @716kbs
H.265 @962kbps

現状最も有力なH.265。 さすがというか、静止画にすると打点のスティックが消えてしまっていて、VP9のほうが良さそうなのだが、 全体でみれば安定していてキレイ。動画では画質がよく感じられる。

全体的なざらつきがないので綺麗に見えるがこの静止画からわかるとおり動きの激しい部分でディティールがVP9よりも消えやすい。 ディティールにこだわる場合はビットレートに余裕を取るべきだと思うけれども、静止画に切り出す場合などはVP9のほうがうまくいく。

ビットレートが上がったときにも順当によくなっている。 H.264よりひとまわりいいといった感じで、ビットレートが上がると差は縮まり、ビットレートが非常に低いときはがんばっている感じだ。

H.265 (NVENC)

$ ffmpeg -i base.mp4 -c:v hevc_nvenc -b:v 512k h265-nvenc.mp4
H.265 (NVENC) @552kbps

NVENCは画質がよくないと言われているので、比較してみた。

テクスチャが雑で、なんか「下塗りです」といった感じ。 成る程、VP9 QSVのようにあからさまに目立つわけではないのだが、ディティールを見てしまうとなかなか厳しい。 利用できる状況は限られそうだ。

VP8

$ vpxenc --end-usage=cbr --bias-pct=0 --codec=vp8 --target-bitrate=200 --passes=2 -h 1920 -w 1080 -o vp8-cbr.webm raw.y4m
VP8 @586kbps

普及しなかったし残念なことになったVP8。768kならがんばってくれるが、512kはどうがんばっても到達せず、このセッティングでもちょっと大きめの586k。

画質は全体的にザラザラ。大きなブロックがないので静止画だとキレイっぽいけれど、常時こんな感じ(H.264なんかは部分的に、そしてちょいちょいブロック化してしまう感じである)なのでちょっとお話にならない。

何より目立つのは「色がおかしい」ことだが。

VP9

$ vpxenc --codec=vp9 --target-bitrate=512 --passes=2 -h 1920 -w 1080 -o vp9.webm raw.y4m 
VP9 @522kbps
VP9 @771kbps
VP9 @1020kbps

VP9は結構優秀で、H.264よりは良いし、H.265とどちらが良いかというと好みの問題と言えるくらいには仕上がっている。 ディティールを見ると損なわれ感があるのでやや厳しいが、ビットレートにある程度余裕を持たせればH.264よりも良い。

フリーコーデックに魅力を見ると感じるなら画質面ではアリだ。

だが、いかんせん遅い。x265の1/10くらいの速度である。 速度優先にしないと10fpsも夢のまた夢。

VP9もまともにマルチスレッドしないので、マルチスレッド化するだけでだいぶ実用になる気がするのだけど… (少なくともlibx265に負けない程度には)

ビットレートが上がるとH.265より良いように見える。 1Mbpsは完全に及第点だろう。

VP9 (QSV)

ffmpeg -init_hw_device vaapi=foo:/dev/dri/renderD128 -hwaccel vaapi -hwaccel_output_format vaapi -hwaccel_device foo -i invideo.mp4 -filter_hw_device foo -vf 'format=nv12|vaapi,hwupload' -c:v h264_vaapi -b:v 512k -c:a copy outvideo.mp4
VP9 (QSV) @555kbps

VP9を唯一実用する方法であるQSV。しかし、QSVのVP9は相当画質が悪い。 ビデオでの印象としてはVP8に近い。ちょっとこの品質ではよほど高ビットレートでない限り実用にならないという感じだ。 エンコードは100fpsほど出るのだが…

AV1

AV1はデフォルト設定では到底終わらないほどの時間がかかったため、次のようにした。

% aomenc --codec=av1 -t 40 --cpu-used=8 --target-bitrate=512 --passes=2 -h 1920 -w 1080 -o av1.mkv raw.y4m
Pass 1/2 frame  142/143    27456B    1546b/f   46186b/s   19657 ms (7.22 fps)
Pass 2/2 frame  142/142   298568B   16820b/f  502497b/s 2741931 ms (0.05 fps)

なお、どちらもスレッド数を指定しているが、AV1は完全にシングルスレッドで動作していた。

AV1 508kbps
AV1 @774kbps
AV1 @1032kbps

H.265よりもディティールが消え、のっぺりしている。 VP9とはほとんど見分けがつかないが、よく見ればなんとVP9のほうが良い。 VP9(512k)に対してAV1(512k)は

  • シャツの前のほうのシワが消滅
  • 打点部分のスティックが巨大なブロックによって消滅
  • シャツはのっぺりしてしまっている

と残念な結果に。

ビットレートがあがってもあまり改善していない。 速度優先の--cpu-usedのせいかもしれないがVP9の10倍は時間をかけた意味はどこへ…

追記: VP9/AV1 のマルチスレッド処理

テスト

コメントにてyusukeさんから

AV1がシングルスレッドでしか動かないのは–tile-columnsと–tile-rowsを指定していないからではないでしょうか

と情報頂いたので試してみた。

% aomenc --codec=av1 --cpu-used=8 --target-bitrate=512 --threads=16 --tile-columns=2 --tile-rows=2 --passes=2 -h 1920 -w 1080 -o av1-512-pararell.mkv raw.y4m

この場合、シングルスレッドで動作した。

% aomenc --codec=av1 --cpu-used=8 --target-bitrate=512 --threads=16 --tile-columns=6 --frame-parallel=1 --passes=2 -h 1920 -w 1080 -o av1-512-pararell.mkv raw.y4m

--tile-rowsをやめて、デコード用オプションである--frame-parallelを有効にしたところ、 マルチスレッドで動作した。 ただし、大部分はシングルスレッドで、ときどき6から10スレッド程度で動作するような挙動。

結果は

Pass 1/2 frame  142/143    27456B    1546b/f   46186b/s   19934 ms (7.12 fps)
Pass 2/2 frame  142/142   303279B   17086b/f  510444b/s 1562704 ms (0.09 fps)
aomenc --codec=av1 --cpu-used=8 --target-bitrate=512 --threads=16   --passes=  3502.70s user 67.02s system 224% cpu 26:29.29 total
マルチスレッドAV1 CPU利用率 (cpu-used=8)

CPUは200%越えてるけれど、速度的には2倍にはなっていない程度。

そして次の場合

% aomenc -w 1080 -h 1920 --tile-columns=6 --limit=48 -t 24 -b 8 --input-bit-depth=8 --end-usage=vbr --target-bitrate=512 -p 1 --webm --num-tile-groups=32 -o av1-512-parallel2.webm raw.y4m
Pass 1/1 frame  142/142   290129B   16345b/f  488306b/s 7850851 ms (0.02 fps)
aomenc -w 1080 -h 1920 --tile-columns=6 -t 24 -b 8 --input-bit-depth=8   -p 1  38460.66s user 114.83s system 491% cpu 2:10:55.73 total
マルチスレッドAV1 CPU利用率 (cpu-used=1)

ずっと激しく使われているのでなかなか効率はよさそう。 この違いは--cpu-usedにあるようだった。

--cpu-used=4の場合:

Pass 1/1 frame  142/142   295824B   16666b/f  497896b/s 1581372 ms (0.09 fps)
aomenc -w 1080 -h 1920 --cpu-used=$i --tile-columns=6 -t 24 -b 8    -p 1   -o  4379.28s user 54.32s system 279% cpu 26:26.07 total
マルチスレッドAV1 CPU利用率 (cpu-used=4)

--cpu-used=8より少し多めに動いている。

--cpu-used=2:

Pass 1/1 frame  142/142   297030B   16734b/f  499928b/s 3251395 ms (0.04 fps)
aomenc -w 1080 -h 1920 --cpu-used=$i --tile-columns=6 -t 24 -b 8    -p 1   -o  10423.02s user 70.17s system 322% cpu 54:16.15 total
マルチスレッドAV1 CPU利用率 (cpu-used=2)

結構動くようになった。

では待望の画質はというと

AV1 並列 cpu-used=1
AV1 並列 cpu-used=2
AV1 並列 cpu-used=4
AV1 並列 cpu-used=8

結構差は大きい。 cpu-used=8のAV1よりはcpu-used=1のVP9のほうが綺麗だけども、4だとディティールは潰れているけれどざらつきが減るため好みで済ませられるように思える。cpu-used=4より上であればH.265に対するアドバンテージは認められる。 のっぺりしているのはAV1のキャラクターのようだが、ノイズが消えるため感覚的には綺麗に見える。再変換したときに情報の欠落が気になりそうだが。

おまけ。同じセッティングでVP9を作ってみる(vpxencaomencはだいたいコマンド互換性がある)。 --num-tile-groupsは使えないので外し、--codec=vp9を追加する。

% vpxenc --codec=vp9 -w 1080 -h 1920 --cpu-used=8 --tile-columns=6 -t 24 -b 8 --input-bit-depth=8 --end-usage=vbr --target-bitrate=512 -p 1 --webm -o vp9-parallel.webm raw.y4m
Pass 1/1 frame  142/118   242873B 5835948 us 24.33 fps [ETA  0:00:01]    1125F   1298F  11937F    943F    792F    849F   1104F   1271F   1022F   1030F  24348F   1382F   1316F   1264F   2220F   1162F   1152F   1072F   1084F   1386F  13097FPass 1/1 frame  142/142   316309B   17820b/f  532379b/s 6950835 us (20.43 fps)
vpxenc --codec=vp9 -w 1080 -h 1920 --cpu-used=8 --tile-columns=6 -t 24 -b 8    16.91s user 0.73s system 235% cpu 7.478 total

おぉぉ、速いぞ!!! 実用になるぞ!!!よくみたらちょこっとだけ粗いけれど、全然いけるいける。

VP9 並列 cpu-used=8

ただ、--cpu-usedしないとさすがに厳しい。

% vpxenc --codec=vp9 -w 1080 -h 1920 --tile-columns=6 -t 24 -b 8 --input-bit-depth=8 --end-usage=vbr --target-bitrate=512 -p 1 --webm -o vp9-parallel-used1.webm raw.y4m     :(
Pass 1/1 frame  142/118   242579B   50719 ms 2.80 fps [ETA  0:00:10]    1121F   1252F  11933F    982F    752F    916F   1059F   1293F    973F   1027F  21951F   1641F   1210F   1324F   2162F   1095F   1044F   1052F   1099F   1382F  13774F Pass 1/1 frame  142/142   314260B   17704b/f  528930b/s   60777 ms (2.34 fps)
vpxenc --codec=vp9 -w 1080 -h 1920 --tile-columns=6 -t 24 -b 8    -p 1 --webm  143.78s user 1.13s system 236% cpu 1:01.31 total

所感

これをテストした上での感想としては

  • --cpu-used=1 はとても遅い。VP9でもかなり厳しい
  • --cpu-used=8 はあまり効果がなく、 --cpu-used=4 が軽量設定としてはよさそう
  • 確かに画質は良いが--cpu-used=8だと悪い。--cpu-used=1でx265並の速度が出れば革命的だけども…
  • マルチスレッドはVP9よりはAV1で改善されている
  • しかしAV1であっても対してCPUを活用できておらず、いまいちと言われているx265のほうがよっぽど計算リソースを有効活用する
  • だいたい活用できているのは8スレッドあたり。クロスしている部分を考慮しても12スレッド
  • AV1はいくらスレッド数を多く指定しても現実的な速度にならない。4114が遅いとしてもCOre i7であってもコアあたり8倍は速くないので、10FPSには到達しない感じ
  • スレッド数を増やしてもせいぜい2倍程度しか速くならないため、スレッド数よりも--cpu-usedのほうが影響が大きい
  • VP9の--cpu-used=4は速度的にも(うちでは30FPS出ないが)使えるが、H.265(libx265)との差は開いてしまい、よほど強い動機がないと厳しい (--cpu-used=2でも結構粗い)
  • AV1がハードウェア支援されたら使えるかというと、現状QSVのVP9がひどいという言葉では片付けられないくらいひどいので、全く期待できない

基本的には「向上するソース画質に対して可能な限り損なわないようにする」には「ソースサイズが大きくなったとき(あるいは画質が向上したときに)に同等ビットレートでソースの改善を反映できるように」動画コーデックは進化しているのであり、つまり情報の少ないソースであれば結果的により小さなサイズに圧縮できる、ということを反映意味する。

そのことを踏まえれば、「画質とサイズを妥協すれば新しいコーデックでも実用できる」というのは完全に本末転倒であり、全く価値がない。これは速度的にVP9の最高画質とAV1の最低画質が同ビットレートで逆転し、かつVP9のほうが速い、というのはまさにそれだ。

確かにAV1の画質は魅力的だが、それはあまりにも現実的ではないリソースをつぎ込めばという話である。 それをいくらか緩和しようとした時点でAV1の画質というメリットは損なわれてしまい、VP9のほうがまだマシという結果になる。

もちろん、一般で使われることは最初から投げ捨てて、配信者向けに作っているのでありユーザーには関係ない、というのであればわからなくはないが、現状のAV1はそれこそGoogleクラスでなければ実用できないようなものであり、ユーザーはデコードすらままならない。 そうすると単に「特殊で扱いにくいもの」になってしまい、そのようなものがどんな経過をたどり今残っていないのかということを完全に忘れ去ってしまっているように思う。

現状は、一般ユーザーにとってVP9であれAV1であれ利用を考慮する価値のないものであり、 フリーであることの価値を理解するのであればVP9は考えられるがAV1は存在自体忘れていても構わない。

ユーザーが使うならばH.265(あるいはH.264)が良い。 (特許問題など気持ち悪いとは思うが…)

画質、処理速度、コスト、処理の安定性などH.265(x265)の何が好まれていて、何を達成しなければならないのか、優位性を口にするくらいなら真剣に考えて欲しいと思う。