こんにちは。先日、cocone tech blogの編集長に就任しましたNです。正直まだ実感ございませんが、皆さまに少しでもココネやCARROT、また、その中で扱っている技術について興味を持っていただけるよう頑張っていきたいと思います!

さて、今回は「#私を布教して」で行った、ストリーミング配信(HLS、HTTP Live Streaming)における遅延問題の改善についてご紹介いたします。

「#私を布教して」とは

「#私を布教して」は、スマートフォンを利用して誰でも気軽に音声ライブ配信や、その受信ができるサービスです。

「#私を布教して」は、スマートフォンを利用して誰でも気軽に音声配信をしたり、聴くことができるサービスです。
アプリを通して簡単に音声をリスナーに届けることができるため、おしゃべりを楽しみたい人も、歌や楽器などの自分の特技を披露したい人も、様々なジャンルでの表現が可能です。
また、気に入った配信を「布教」して他の人に広めることや、「ギフト」を送って配信者を応援することが可能です。

(「#私を布教して」オフィシャルサイト(https://fukyou.me/)より)
(2020年8月より「#私を布教して」は「CARROT株式会社」にて運営しております!)

以下が大きな特徴です。

  • スマホ一台で簡単にライブストリーミング配信。
  • 顔出しなし、音声のみ(メイクや髪型のセットなどが一切不要!)。
  • アーカイブできる
  • ライバー(配信者)は音声で、リスナー(視聴者)は文字でお互いにコミュニケーションができる

「#私を布教して」のライブ配信基盤について

「#私を布教して」ではライブ配信基盤として Wowza Streaming Engine を採用しています。
ライバー(配信者)側では RTMPプロトコル(TCP)で送信(ライブ配信)を、
リスナー(視聴者)側では HLS(HTTP Live Streaming) という規格で受信します。

HLSはApple社が開発した通信プロトコルです。
特徴としては以下が挙げられます。

  • オンデマンド配信・ライブ配信に対応
  • 様々なプラットフォームに対応(iOS, Android, Mac, Windows, … )
  • インデックスファイル(m3u8プレイリスト)とセグメントファイル(tsファイル)に分かれた構成

今回は特に、インデックスファイル(m3u8プレイリスト)とセグメントファイル(tsファイル)に分かれて構成されている点に着目します。

インデックスファイルは、セグメントファイルの場所や再生時間、再生順序を定義したメタデータです。
一方、セグメントファイル(以下、チャンクと記載)は、MPEG2 Transport Streamプロトコルで細かく分割された、複数の動画や音声のデータファイルです。

インデックスファイルは以下のようになっています(表題の件についての改善前の例です)。

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:10
#EXT-X-MEDIA-SEQUENCE:1
#EXTINF:10.0,
media-uwdww9jbk_1.ts
#EXTINF:10.0,
media-uwdww9jbk_2.ts
#EXTINF:10.0,
media-uwdww9jbk_3.ts

見方としては1行目から順に以下のようになります。

EXTM3U ファイル形式。この場合はm3u8形式であることを示す
EXT-X-TARGETDURATION 各チャンクの最大秒数。この例では10秒
EXT-X-MEDIA-SEQUENCE このインデックスファイルについて、最初のチャンク(tsファイル)が動画・音声の全体の何番目のチャンクか
EXTINF チャンクの秒数
チャンクのファイル名

HLSの問題点とその原因

HLSでライブを受信すると、遅延する問題が発生します。

一方的に動画や音声を受信するだけのアプリであれば、ある程度の遅延は問題なかったかもしれません。
しかし「#私を布教して」はライバーとリスナーがリアルタイムにコミュニケーションを行うアプリのため、受信の遅延をより抑えることが課題になりました。

遅延の最大の原因はバッファリングです。
バッファサイズ(チャンクサイズ)を小さくする、つまりチャンクの秒数を減らすことで遅延を抑えることが可能です。ただし、ある過度に小さくすると、バッファリング(リバッファリング)が多発し、配信がブツブツ途切れて聞こえるようになってしまいます。

当初の仕様としては、3つのチャンクを読み込んでから再生するようになっていたようです(というよりも、Appleがそういう仕様と謳っておりました)。

この仕様通りのHLSプレイヤーで、先ほど挙げたインデックスファイルの例で再生したとしましょう。
すると、10秒のチャンクを3つ読み込んでから再生するので、単純計算でも 10 × 3 で 約30秒ほどの遅延が発生します(実際には別の要因もあるので、さらに遅延する可能性があります)。

ライブ配信……とは……?ライバー・リスナー相互のコミュニケーションとは……?となりかねない、致命的な問題です。

改善

ということで、Wowza Streaming Engineの設定で改善できる値について、まずはWowzaの推奨値に変更しました。

  1. チャンクの長さ(cupertinoChunkDurationTarget)を 10秒(10000msec) から 1秒(1000msec) に変更する
  2. Wowzaが保存するチャンクの最大数(cupertinoMaxChunkCount)を増やす
    前述のチャンクの長さ(cupertinoChunkDurationTarget)に合わせて調整します。今回は1秒チャンクのため、50に変更しました(0.5秒チャンクの場合は値を倍(100)にする必要があります)。
  3. インデックスファイルに追加するチャンクの数(cupertinoPlaylistChunkCount)を調整する
    これもチャンクの長さにより調整します。1秒チャンクの場合の推奨値は12のため、12に変更(0.5秒チャンクの場合は値を倍(24)にする必要があります)。
  4. チャンク数の最小値(cupertinoMinPlaylistChunkCount)を決定する
    1秒チャンクの推奨値の6秒になるように調整します(0.5秒チャンクの場合は12)。

改善後のインデックスファイルを確認してみましょう。

#EXTM3U
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:2
#EXT-X-MEDIA-SEQUENCE:26
#EXTINF:2.0,
media-urr49rux4_26.ts
#EXTINF:2.0,
media-urr49rux4_27.ts
#EXTINF:2.0,
media-urr49rux4_28.ts
#EXTINF:2.0,
media-urr49rux4_29.ts
#EXTINF:2.0,
media-urr49rux4_30.ts
#EXTINF:2.0,
media-urr49rux4_31.ts
#EXTINF:2.0,
media-urr49rux4_32.ts
#EXTINF:2.0,
media-urr49rux4_33.ts
#EXTINF:2.0,
media-urr49rux4_34.ts
#EXTINF:2.0,
media-urr49rux4_35.ts
#EXTINF:2.0,
media-urr49rux4_36.ts
#EXTINF:2.0,
media-urr49rux4_37.ts

(チャンクの最大秒数が2になってしまっているのが何故かはよくわかっておりませんが)これで単純計算で遅延を約6秒程度に縮めることができています。

補足

チャンクを小さくすると、今度はHTTPリクエストが増大します。
これは、ライブ配信の特性上の問題になります(ライブ配信はいつ終わるかがわからないためです。次々に情報を取得する必要があり、リクエストをしないと配信を視聴できなくなるため)。

これはサーバー負荷の原因にもなり得ます(「#私を布教して」ではクライアントとの通信間で、CDN(Amazon CloudFront)を挟むことでカバーしています)し、クライアント端末が熱くなってしまう問題などにもなります。

また、「HLSの問題点とその原因」でも述べましたが、チャンクを小さくしすぎるとバッファリングが多発して配信がブツブツ切れるようになってしまいます。
「遅延を減らすこと」と「快適な視聴体験」はトレードオフの関係であるため、ベストな点を探って調整することが必要となります。

 

以上、「ストリーミング配信の遅延を抑える#私を布教して」でした。

「#私を布教して」のダウンロードはこちらから!

 

参考:https://www.wowza.com/docs/how-to-improve-playback-of-lower-latency-apple-hls-streams