次世代ビデオコーデックAV1を使ったライブ配信
こんにちは、テリーです。コロナ騒動による自宅待機・フルリモートワークになって以来、ビデオ会議や動画再生をほぼ毎日必ず使うようになっています。当たり前のように使えるようになったビデオ会議ですが、ケータイの回線を使用したテザリングのように、インターネット回線が低速で不安定な状況の場合は、音声がブチブチと切れたり、ビデオがコマ落ちしたりという状況が今でも発生しています。そんな低速・不安定な回線でもそれなりの画質・音質で安定したビデオ会議をするためには、使用帯域幅を減らすことが必要です。さらなる高圧縮のビデオコーデックが期待されています。
ビデオコーデックは単に最新であればよいというものではなく、配信側・中継側・視聴側の3つのコンピュータで解釈可能でなければならないという点で、開発からリリースまでの期間が他のソフトウェアと大きく異なります。たとえばOSやプログラミング言語は開発元が自分たちの都合のよいペースで頻繁に更新しています。ビデオコーデックもいずれはそういう時代になるでしょう。
2021年現在、もっとも期待されている次世代ビデオコーデックAV1が普及期に入りました。Netflixは2020年からAV1の使用を開始しました。YouTubeをChromeで再生するとほとんどのコンテンツでAV1コーデックが使用されています。ChromeではWebRTCでもAV1が使えるようになりました。最大の問題は、Apple製品が未対応ということです。macOS、Safari、iPhone、iPadでAV1のコンテンツが再生できません。
本記事では、普及待ったなしとなったAV1コーデックがライブ配信に使用できるかを意識して使ってみたいと思います。
動作環境
本記事は以下のバージョンを用いて動作を確認しています。
- MacBook Pro (14-inch, 2021) M1 Max
- macOS Monterey 12.0.1
- ffmpeg 4.4.1
- Chrome 96.0.4664.93(arm)
ffmpegのインストール
AV1のコーデックは、ffmpegのソースコードにすでに組み込まれています。本記事ではmacOS用のビルド済バイナリを使用しますが、独自でビルドすることも可能です。
ターミナルを開いて下記を実行してください。
mkdir ffmpeg cd ffmpeg curl -O https://evermeet.cx/ffmpeg/ffmpeg-4.4.1.zip unzip ffmpeg-4.4.1.zip cp ffmpeg /usr/local/bin ffmpeg -version
下図のように表示されます。
比較用動画の入手
本記事ではBigBuckBunnyという動画をサンプルとして使用します。読者ご自身で手配された動画データでも構いません。ffmpegがデコードに対応している形式ならば使用できます。短時間で検証できるように、ダウンロードした動画を30秒間だけ部分的にカットし、base.mp4という名前にします。
curl -O http://distribution.bbb3d.renderfarming.net/video/mp4/bbb_sunflower_1080p_30fps_normal.mp4 ffmpeg -ss 15 -t 30 -i bbb_sunflower_1080p_30fps_normal.mp4 -vcodec copy -an base.mp4
ffmpegでエンコード
まずはエンコードをしてみましょう。
time ffmpeg -i base.mp4 -vcodec libaom-av1 -b:v 3000k -cpu-used 4 -an -y av1_4.mp4 time ffmpeg -i base.mp4 -vcodec libaom-av1 -b:v 3000k -cpu-used 5 -an -y av1_5.mp4 time ffmpeg -i base.mp4 -vcodec libaom-av1 -b:v 3000k -cpu-used 6 -an -y av1_6.mp4 time ffmpeg -i base.mp4 -vcodec libaom-av1 -b:v 3000k -cpu-used 7 -an -y av1_7.mp4 time ffmpeg -i base.mp4 -vcodec libaom-av1 -b:v 3000k -cpu-used 8 -an -y av1_8.mp4
それぞれの結果は以下のようになります。
- cpu-used=4の場合
frame= 907 fps=1.1 q=0.0 Lsize= 15022kB time=00:00:30.20 bitrate=4074.9kbits/s dup=5 drop=0 speed=0.0351x video:15019kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 0.020781% ffmpeg -i base.mp4 -vcodec libaom-av1 -b:v 3000k -cpu-used 4 -an -y av1_4.mp4 2342.95s user 9.16s system 273% cpu 14:21.08 total
- cpu-used=5の場合
frame= 907 fps=3.4 q=0.0 Lsize= 14372kB time=00:00:30.20 bitrate=3898.6kbits/s dup=5 drop=0 speed=0.112x video:14369kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 0.021938% ffmpeg -i base.mp4 -vcodec libaom-av1 -b:v 3000k -cpu-used 5 -an -y av1_5.mp4 989.59s user 7.06s system 370% cpu 4:29.13 total
- cpu-used=6の場合
frame= 907 fps=4.8 q=0.0 Lsize= 14702kB time=00:00:30.20 bitrate=3988.1kbits/s dup=5 drop=0 speed=0.159x video:14699kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 0.021366% ffmpeg -i base.mp4 -vcodec libaom-av1 -b:v 3000k -cpu-used 6 -an -y av1_6.mp4 698.55s user 6.04s system 370% cpu 3:10.40 total
- cpu-used=7の場合
frame= 907 fps=4.8 q=0.0 Lsize= 14702kB time=00:00:30.20 bitrate=3988.1kbits/s dup=5 drop=0 speed=0.158x video:14699kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 0.021366% ffmpeg -i base.mp4 -vcodec libaom-av1 -b:v 3000k -cpu-used 7 -an -y av1_7.mp4 699.49s user 6.24s system 369% cpu 3:10.89 total
- cpu-used=8の場合
frame= 907 fps=4.7 q=0.0 Lsize= 14702kB time=00:00:30.20 bitrate=3988.1kbits/s dup=5 drop=0 speed=0.158x video:14699kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 0.021366% ffmpeg -i base.mp4 -vcodec libaom-av1 -b:v 3000k -cpu-used 8 -an -y av1_8.mp4 700.12s user 6.44s system 369% cpu 3:11.14 total
CPUの使用数(-cpu-used)の値に応じて、処理速度が大きく変わってきますが、ある程度以上のCPU数になると、処理速度に変化がなくなります。「1」を指定すると半日近くかかりそうな数字になったので、途中で断念しました。
こちらの記事によると、cpu-usedの値を5にするのが速度と品質のバランスが最も良いとされています。下表を見ると、私の環境では6が良さそうです。(下表の時間は、実行結果の末尾に表示されています。例えばcpu-used=8の場合は「3:11.14 total」で、3分11秒=191秒かかっています)
CPU数 | 1 | …… | 4 | 5 | 6 | 7 | 8 |
時間 | 遅すぎて断念 | 861秒 | 269秒 | 190秒 | 190秒 | 191秒 |
いずれにしても、30秒の動画をエンコードするのに190秒もかかっているようではライブ配信のエンコードには使えません。これはエンコードパラメータのデフォルト設定がライブ用に不適であるためで、何かしらの最適化が必要です。
WebRTCでAV1を使用
Chrome90以降はWebRTCでAV1コーデックが使用できるようになりました。さっそく使ってみましょう。こちらのページを別タブで開き、真ん中の「▶Send▶」ボタンを押してください。AV1に対応していれば数秒後に左右に同じ映像が表示されます。左がカメラの生映像、右がAV1でエンコードした物をデコードした映像です。全く見分けがつきません。
本当にAV1が使われているか確認するために、上のサンプルを実行した状態で chrome://webrtc-internals/ というURLを別ウインドウで開いてください。たくさんの項目がありますが、中ほどの「RTCOutboundRTPVideoStream」で始まる項目をクリックして展開すると、上から7つ目の項目に「[codec] AV1 (35)」の記載があり、下から6つ目のには「encoderImplementation libaom」と書いてありますので間違いなさそうです。あなたのパソコンでAV1のライブエンコード・デコードを行っています。処理の負荷はどうでしょうか? 私の環境ではマウスやブラウザが重いとか遅いという感覚は感じられません。フレームレートは30FPS出ています。CPU負荷もかなり余裕があります。エンコードパラメータ次第では720pのライブエンコードが充分実用可能であることが確認できました。
ChromeのAV1エンコードパラメータ
同じlibaomを使用してAV1エンコードしているのであれば、Chromeで使用しているエンコードパラメータを参考にすることで、ffmpegのエンコード速度が改善できるのではないでしょうか? Chromeで使用しているAV1エンコードのコードはこちらです。43行目付近に注目してください。
// Encoder configuration parameters constexpr int kQpMin = 10; constexpr int kUsageProfile = 1; // 0 = good quality; 1 = real-time. constexpr int kMinQindex = 58; // Min qindex threshold for QP scaling. constexpr int kMaxQindex = 180; // Max qindex threshold for QP scaling. constexpr int kBitDepth = 8; constexpr int kLagInFrames = 0; // No look ahead. constexpr int kRtpTicksPerSecond = 90000; constexpr float kMinimumFrameRate = 1.0;
その次の54行目から始まるGetCpuSpeed関数では、-cpu-usedに相当する値を決定しています。720pでは8個のCPU、それ以下の解像度では7個のCPUが割り当てられていることがわかります。
int GetCpuSpeed(int width, int height, int number_of_cores) { // For smaller resolutions, use lower speed setting (get some coding gain at // the cost of increased encoding complexity). if (number_of_cores > 2 && width * height <= 320 * 180) return 6; else if (width * height >= 1280 * 720) return 8; else return 7; }
このソースコードを全て注意深く読み、設定しているパラメータを書き出すと以下のような値になります。
threads=8 timebase=1/90000 target-bitrate=3000 bit-depth=8 disable-kf=1 min-q=10 max-q=63 usage=1 error-resilient=0 end-usage=1 // cbr pass=1 // onepass lag-in-frames=0 cpu-used=8 enable-cdef=1 enable-tpl-model=0 deltaq-mode=0 enable-order-hint=0 aq-mode=3 max-intra-rate=300 coeff-cost-upd-freq=2 mode-cost-upd-freq=2 mv-cost-upd-freq=3 tile-columns=4 row-mt=1
これらの値のうち、ffmpegが受け入れられるようにコマンドを書いてみるとこのようになります。
time ffmpeg -i base.mp4 -vcodec libaom-av1 -b:v 3000k -threads 8 -qmin 10 -qmax 63 -usage 1 -lag-in-frames 0 -cpu-used 8 -aom-params error-resilient=0:enable-cdef=1:enable-tpl-model=0:deltaq-mode=0:enable-order-hint=0:aq-mode=3:max-intra-rate=300:coeff-cost-upd-freq=2:mode-cost-upd-freq=2:mv-cost-upd-freq=3:tile-columns=4:row-mt=1 -an -y av1_chrome.mp4
結果はこのように表示されます。190秒かかっていた処理がなんと12秒で終わりました。元の動画が30秒なので、ライブ配信に使えます。
frame= 907 fps= 72 q=0.0 Lsize= 11220kB time=00:00:30.20 bitrate=3043.5kbits/s dup=5 drop=0 speed=2.39x video:11217kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 0.027302% ffmpeg -i base.mp4 -vcodec libaom-av1 -b:v 3000k -threads 8 -qmin 10 -qmax 63 77.00s user 0.78s system 611% cpu 12.717 total
まとめ
このように最新のパソコンを使うとAV1はライブ配信に使用可能なことがわかりました。エンコードパラメータが多すぎて、値が間違っていないかどうか確認するのはとても大変ですが、Chromeのソースコードを読み、設定値・設定方法を参考にすることで、自信を持って設定することが可能となります。
有料サービスのご紹介
WebRTCを利用した会員制ライブ配信サービスを短期間で導入したい方、大規模配信のためのサーバ構築・インフラ運用を専門家に任せたい方は「ImageFlux Live Streaming」をご検討下さい。WebRTC SFUという技術を用いて、配信映像をサーバに中継させる仕組みです。1対多の会員制映像配信に特に強みがあり、初期の利用コストが低価格に抑えられます。