dash.js: ビットレート制御を図にしてまとめてみた

投稿者: | 2017年9月15日

概要

  • dash.js での自動的なビットレート制御についてまとめます
  • 一目でイメージがつかめるように簡単な図も描きました

アダプティブビットレート (ABR)

インターネットを経由して動画を配信する場合、

途中のネットワークの品質によって、

時間あたりに送れるデータ量が変わってきます。

 

そのため、ネットワークの品質などを見ながら、

動画配信時に動的にビットレートを選択して、

適切なレートの動画を配信することが必要になってきます。

 

このように、動的にビットレートを変更することを、

アダプティブビットレート (ABR) と呼びます。

 

YouTube等の数々の有名な動画配信サイトで使われている

動画配信方式 MPEG-DASH は、

この ABR を可能としたストリーミングを提供しています。

 

今回は、MPEG-DASH の参照用実装である dash.js に注目し、

dash.js ではどのように ABR が実装されているかをまとめることにします。

ざっくりストリーミング配信

ABR は、ストリーミング配信時に生じる

通信品質の変化などの問題を上手く吸収して、

動画の再生を止めないなど、

ユーザーが心地よく動画視聴できるようにすることが主な目的です。

 

そこで、まずはストリーミング配信がどのような形で行われ、

どのような問題が起こるのかを簡単に説明します。

ストリーミング配信の形態

ストリーミング配信は、動画ファイルを持った配信サーバーと、

動画ファイルを受信して再生するパソコン(または、スマホなど)間の通信によって行われます。

 

下の図は、ストリーミング配信の概略です。

ストリーミング配信の概略図

 

動画ファイルは、大まかに言えば画像ファイルを並べたもので、

サーバー側は、動画全体の画像を持っています。

パソコン側は、再生するときに必要な画像を順番に受け取って、

画面に表示していくことで動画の再生を行います。

 

つまり、パソコンは一度に動画ファイル全体を受け取る必要はなく、

ある程度のまとまり毎にファイルを受信すれば良いのです。

このまとまりのことを「セグメント」と呼びます。

 

パソコン側は、動画を再生しながら次のセグメントの受信を続けており、

サーバーからは先の先の先の・・・セグメントが送られてきます。

このようなまだ再生されないセグメントは、

一時的にパソコンの内部においておく必要があり、

その置き場所を「バッファ」と呼びます。

ストリーミング配信時の問題

送られてきたセグメントを次々と再生するだけで、

動画の再生ができます。

では、ここにどのような問題があるでしょうか?

 

一番大きな問題は、セグメントがインターネットを通るという点です。

インターネットは、世界中の人が同時に通信を行っているので、

通信品質は常に変化しています。

そのため、ある時にはセグメントを早く送ることができて、

またある時にはセグメントを送るのが遅くなるということが生じます。

 

バッファは、このようなセグメントの到着時間の違いの影響を和らげる働きをします。

つまり、早く受信できる時に、できるだけ受信しておいてしまって、

遅くなったら、受信済みのセグメントを再生すれば良いのです。

 

ところが、通信品質の悪化がある程度続くと、

バッファの中のセグメントを再生し切ってしまい、

再生するセグメントがなくなってしまいます。

そうなると、動画の再生は一時中断されるので、

ユーザーにとってはストレスです。

 

通信品質が悪いなら、

動画を止めてしまうのではなくて、

画質が低くなってもいいから再生し続けてほしいと思います。

そして、通信品質が良くなったら、高画質に戻してほしいですね。

 

このような通信品質によって、

配信する動画のビットレートを動的に変更する仕組みが ABR です。

dash.js の アダプティブビットレート制御

それでは、dash.js では ABR をどのように実装しているのでしょうか。

 

dash.js では、複数のルールに基づいて、レートを決定するという仕組みをとっています。

ルールA では、 〇〇の時に、レートを××にする。

ルールBでは、△△の時に、レートを□□にする。

という形で、複数のルールを持っています。

 

このままだと、ルールの数だけレートが計算されるのですが、

最終的には複数のルールが決めたレートの中で最小のレートが設定されます。

最小のものを選ぶのは、メインのルール 1つと、例外毎に複数のルールを使うことを想定しているためです。

基本的には高いレートを設定したいのですが、

再生が途切れそうになるなどの例外が生じた際には、

低いレートに切り替えて再生を継続しないといけないので、

どこかの例外の網にかかれば、レートを抑えようというレート設定をします。

メインのレート制御ルール

まずは、メインとなるレート制御ルールについて紹介します。

dash.js では 2 つのルールが用意されていて、

ThroughputRule, BolaRule があります。

どちらを使うかは、動画の再生を開始する前に指定します。

特に指定がない場合は ThroughputRule が有効となります。

ThroughputRule

このルールでは、パソコン側でネットワークの通信速度(スループット)を計算し、

その値で、十分に転送可能なレートを選択します。

 

例えば、下の図の例では。

単位時間あたりに 100 のデータを転送できる場合は、

それ以下のレート 90 が選択されます。

ThroughputRule の概略

 

より正確には、計測されたスループットの 90% 以下のレートを選択し、

複数のレートの候補がある場合には、その中で最大のレートが選ばれます。

例えば、動画ファイルとして、スループットが 100 の場合、

55, 70, 95 のレートが候補にあるとすると、90 以下で最大の 70 が選択されます。

 

また、安定して動画が再生できるようになると、レートを維持する仕組みもあります。

最高画質のレートが選択されていて、バッファに十分なセグメントが蓄えられると、

レート維持モードに入り、スループットが一時的に低下してもレートを下げなくなります。

バッファのセグメントが減ってくると、このモードは解除されます。

BolaRule

主にスループットでレートを決める ThroughputRule に対して、

こちらのルールでは、バッファ内のセグメントの量に応じてレートが選択されます。

 

バッファ内のセグメントが多ければ、新しいセグメントの転送に多少時間がかかっても、

すでにダウンロードしたセグメントで再生を継続できます。

逆に、バッファ内のセグメントが少なければ、なるべく早くセグメントの転送を行わないと、

再生が途切れてしまいます。

 

そのため、BolaRule では、

バッファ内のセグメントの量が多い場合は、高レート

バッファ内のセグメントの量が少ない場合は、低レート

を選択します。

 

ちょうど、下の図のように、すでに読み込まれた部分が多く、

当分は再生に困らないというときには、その先を高レートで読み込み、

もうすぐ読み込んだ部分の再生が終わってしまうという時には、

なるべく早く続きを読み込めるように低レートで読み込むという動作をします。

bolaRule の概略

 

ThroughputRule と同様に、こちらのルールにもレートを安定させるための仕組みが入っています。

一つは、スループットを超えるレートを設定しないというものです。

スループットを超えたレートを設定すると、続きをダウンロードする時間よりも、

再生する時間の方が早くなるので、徐々にバッファ内のセグメントは減っていきます。

バッファ内のセグメントが減るということは、BolaRule ではレートを下げるという設定を行います。

レートを下げると、バッファ内にセグメントがたまるようになるので、またレートを上げます。

これが繰り返されるとレートが振動してしまうので、

スループットを超えないようにレートを設定するという仕組みが導入されています。

 

もう一つは、通信品質に関係のないバッファの減少については無視するというものです。

レートの設定は、通信品質と連動して決定したいというのが元々の目的でしたが、

バッファ内のセグメントの数は、通信品質の変化に関わらず、変化する可能性があります。

例としては、ライブ配信時に、配信側が送信するセグメントが少なくなった結果、

受信側のバッファ内のセグメントが減少することがあります。

このような通信品質の変化以外にも対応してレートを変更していると、

レートが安定しないため、バッファが減少した理由を調べた上で、レートを変更するかを決めています。

例外時のレート制御ルール

基本的には、上で紹介した ThroughputRule か BolaRule を使うことで、

通信品質の変化に応じて、レートを選択できます。

 

dash.js では、より状況に合わせた適切なレートを設定するために、

特定の状況が生じた時に、その状況毎に合わせたレート設定のルールが用意されています。

基本的には、どれもレートを下げる側に働くルールとなっているため、

どれかのルールに該当すれば、そのルールが定める低レートが実際のレートに反映されます。

InsufficientBufferRule

このルールはバッファが少なくなった時にレートを下げるルールです。

レートを下げることによって、画質を落としてでも、

再生を途切れないようにすることが目的です。

 

このルールが働くのは、バッファ内のセグメントが 1 つだけになった時と、

バッファが空になった時です。

 

セグメントが 1 つになった時には、

今のスループットの半分以下のレートを選択します。

 

バッファが空になった場合には、

最も低いレートを選択します。

バッファが空になった場合

SwitchHistoryRule

このルールは、レートの頻繁な変更を抑えるためのルールです。

直近の過去にレートを落とす選択をした場合、

すぐにまたレートを戻すということはせずに、

それ以下のレートを維持します。

 

具体的には、過去 8 セグメントのうちで、レートを低下させたことがある場合、

低下させる前以上のレートは設定しないというものです。

 

下の図は、SwitchHistoryRule によるレート設定の様子を示しています。

横軸は時間で、黒い三角(▲)が今からレートを設定するセグメントです。

過去 8 セグメント分の期間を青い領域で示しており、

この中に画質の減少があれば、SwitchHistoryRule の対象です。

今の例の場合は、過去 8 セグメントの真ん中あたりで画質を低下させており、

それ以降の 8 セグメントでは、元の画質以上に戻すことは禁止されています。

禁止されている画質は図中の赤い領域で示しています。

SwitchHistoryRule の概略

DroppedFrameRule

このルールは、フレーム落ちが生じた場合にレートを低下させるものです。

 

受信したセグメントは、CPU または GPU によって、モニターに描画されます。

動画のビットレートが高いと、この描画処理が間に合わず、

受信したけど、モニターに映すことが出来ないコマが生じます。

これを「フレーム落ち」と言います。

 

フレーム落ちが生じるのは、送られてきた動画のビットレートが、

動画を描画する CPU の性能に合っていないためなので、

ある程度のフレーム落ちが生じるようなら、レートを一つ下げます。

DroppedFrameRule の概略

AbondonRequestRule

上のルールは全て、次のセグメントのレートについてのルールでしたが、

このルールは、今ダウンロード途中のレートについてのルールです。

 

通信品質は、ダウンロードの途中にも変わる可能性があるので、

一度、レートを決めてしまえば、後はダウンロードするだけとはいきません。

そこで、ダウンロードを開始したセグメントについても、

ダウンロードの進行状況を見ながら、

別のレートでダウンロードし直すかどうかを決める必要があります。

 

下の図では、AbondonRequestRule が働く状況を示しています。

今、バッファに 5 分の間再生できるだけのセグメントが蓄えられていて、

続きのセグメントをダウンロードしている状況です。

ダウンロードの途中に、通信品質が落ちて、

続きのセグメントをダウンロードを完了するために 10 分かかることになりました。

これでは、続きをダウンロードしている間に、

バッファ内のセグメントを再生し終えてしまうので、

再生が途中で止まってしまいます。

 

AbondonRequenstRule の概略

 

AbondonRequestRule では、

セグメントのダウンロードが再生に間に合わないと判断すると、

そのセグメントのダウンロードを中止し、

低いレートで同じセグメントを再ダウンロードします。

これによって、再生が途切れる前に、

続きのセグメントのダウンロードを完了できます。

まとめ

dash.js で実装されている ABR について紹介しました。

基本的には、通信品質に合わせたレートを ThroughputRule または BolaRule で設定し、

レートを落とす必要のある特殊な状況については、

それぞれ個別の例外的なルールで対応するという方式をとっていることが分かります。

 

現状では、ThroughputRule と BolaRule は、動画再生前にどちらか一方を選択する必要がありますが、

将来的には、動画再生時に動的にこの二つのルールを切り替えるようにするようです。(下のリンクを参照)

参考

https://github.com/Dash-Industry-Forum/dash.js/wiki/ABR-Logic

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です