次世代ビデオコーデック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対多の会員制映像配信に特に強みがあり、初期の利用コストが低価格に抑えられます。