09. 優先度と遅延タイムアウト

🎯 この章のゴール

  • Subscriber Priority / Publisher Priority / Group Order によるスケジューリング規則を説明できる
  • 2 種類の遅延タイムアウト(SUBGROUP / OBJECT)の違いを理解する
  • 輻輳時に MoQ が「古いものを捨てて最新を届ける」仕組みをイメージできる

💡 ひとことで言うと

ライブ配信では、遅れて届いたデータは使い道がありません。MoQ では、帯域が足りないときに何を先に送り、何を諦めるかをプロトコルの中で決められます。

なぜ優先度が必要か

帯域が十分あれば、全 Object を順に送れば済みます。問題は輻輳したときです。TCP ベースの配信では、古いデータの送信・再送が詰まって新しいデータも遅れ、遅延がどんどん積み上がります。

MoQ では Group ごとに別ストリームなので、送信側は「どのストリームのデータを次に送るか」を選べます。さらに古すぎるデータはストリームごとリセットして捨てられます。

送信キュー(輻輳中)音声(優先度 高)A40A41A42← 最優先で全部送る映像(優先度 低)V40V41V42← 新しい V42 を先に(Descending)V40: タイムアウト → リセットして破棄V41: 帯域が空けば送る送信順: A40 → A41 → A42 → V42 → V41 …(V40 は送らない)
輻輳時のふるまい。音声を優先し、映像は新しい Group を優先、古い Group はタイムアウトで捨てる

3 つの優先度のつまみ

つまみ誰が決めるどこで指定単位
Subscriber PriorityサブスクライバSUBSCRIBER_PRIORITY パラメータ(後から REQUEST_UPDATE で変更可)リクエスト(購読・FETCH)ごと
Publisher Priorityオリジナルのパブリッシャ既定値はトラックプロパティ DEFAULT_PUBLISHER_PRIORITY、Subgroup ヘッダーやデータグラムで上書きSubgroup / データグラムごと
Group Orderサブスクライバ(省略時はパブリッシャの希望)GROUP_ORDER パラメータ / DEFAULT_PUBLISHER_GROUP_ORDER プロパティ購読ごと(後から変更不可)

優先度は 0〜255、数値が小さいほど優先(0 が最高)。既定値は 128 です。

スケジューリング規則

送れるもの(スケジュール可能なもの)が複数あるとき、パブリッシャは次の順で選ぶべき(SHOULD)とされています。

  1. Subscriber Priority が高い方
  2. 同じなら Publisher Priority が高い方
  3. 同じ購読の別 Group なら Group Order(Ascending なら古い Group、Descending なら新しい Group)
  4. 同じ Group なら、fill で届く方を先に。それ以外は Subgroup ID(データグラムなら Object ID)が小さい方。モードが違えばデータグラムが先

「スケジュール可能なもの」は、Subgroup の次の Object、データグラム、FETCH 応答の次の Object です。異なる購読で優先度が同じ場合の順序は実装次第ですが、どの購読も少しずつは送れることが期待されています。

制御ストリームは最優先、次にリクエストストリーム、その後に Object を送るのが推奨です。

1 つのセッションに複数の名前空間(別々のアプリやパブリッシャ)が混在すると、パブリッシャ同士の優先度方針は調整されていないかもしれません。Subscriber Priority を最初に比べることで、受信側が最終的な優先順位を決められるようになっています。

設計例

トラックSubscriber PriorityPublisher PriorityGroup Order
音声64128Descending
映像(ベースレイヤー Subgroup)128100Descending
映像(拡張レイヤー Subgroup)128200Descending
チャット32128Ascending(全部順に欲しい)

リレーは下流への送信ではサブスクライバとパブリッシャの優先度を尊重すべきですが、下流の Subscriber Priority や Group Order をそのまま上流の購読に使うべきではありません(多数の下流で値がバラバラなため)。上流ではコンテンツの人気度やポリシーなど、リレー独自の基準で決めます。

遅延タイムアウト: 諦める仕組み

どんなに優先度を工夫しても、帯域が足りなければいずれ古いデータが溜まります。そこで、各購読には 2 つのタイムアウト(ミリ秒、0 ならなし)があります。

SUBGROUP_DELIVERY_TIMEOUTOBJECT_DELIVERY_TIMEOUT
計測開始Subgroup の FIN を受け取った(全 Object が公開された)ときObject ヘッダーの最後のバイトを受け取ったとき
チェック全データが確認応答されるまでタイマーで監視Object をトランスポートに渡す時点
タイムアウト時Subgroup はストリームをリセット、データグラムは破棄同左
  • パブリッシャはトラックプロパティ(または Subgroup の最初の Object のプロパティ)で、サブスクライバはメッセージパラメータで指定
  • 両方とも 0 以外なら小さい方が使われる
  • OBJECT_DELIVERY_TIMEOUT を超えた Subgroup はストリームを DELIVERY_TIMEOUT でリセットし、その Subgroup のための新しいストリームは開かない
  • トランスポート層に溜め込んだデータはもうタイムアウトできないので、実装はバッファを小さく保つべき

どちらのタイムアウトも設定されていなければ、フィルタに一致する Object はすべて(Group Order と優先度に従って)届けられます。サブスクライバの受信が遅すぎてパブリッシャの資源を圧迫したら、TOO_FAR_BEHIND の PUBLISH_DONE で購読を終わらせてもかまいません。

輻輳制御について

MOQT は特定の輻輳制御アルゴリズムを指定しませんが、選ぶときの注意点を挙げています。

  • Bufferbloat: CUBIC や Reno のような AIMD 方式は経路上にキューを溜めやすく、ロスがなくても遅延が増える
  • Application-Limited: ライブ配信は帯域いっぱいまで送らない(送るデータがない)ことが多く、輻輳制御が帯域を過小評価しがち。低優先度(255)の追加トラックやパディングで帯域をプローブする手がある
  • スループットの安定性: BBR の PROBE_RTT や Reno の窓半減のような急な送信レート低下は、遅延に敏感なアプリでは問題になりうる

🧪 考えてみよう

同じ MoQ のプロトコルで、遅延 200ms のビデオ通話と、遅延 10 秒でも途切れない大規模配信の両方を作れるのはなぜでしょう? 違うのは主にタイムアウトと Group Order の設定です。通話なら短いタイムアウトと Descending で古いデータをどんどん捨て、配信なら長いタイムアウトと Ascending で取りこぼしを防ぎます。どちらも同じリレーで扱えます。

📝 まとめ

  • 優先度は 0〜255 で小さいほど優先。Subscriber Priority → Publisher Priority → Group Order → Subgroup ID の順に比べる
  • Group Order が Descending なら新しい Group が先。ライブで最新に追いつくのに向く
  • SUBGROUP / OBJECT の遅延タイムアウトで、間に合わないデータはリセット・破棄して遅延の積み上がりを防ぐ

参考

✅ 理解度クイズ

Q1
MOQT の優先度の数値について正しいものは?
Q2
送信候補が複数あるとき、スケジューリングで最初に比べるのは?
Q3
ライブ視聴で輻輳が起きたとき、新しい Group を古い Group より先に送ってほしい。どの設定?
Q4
OBJECT_DELIVERY_TIMEOUT を超えた Subgroup の Object はどう扱われる?
Q5
パブリッシャとサブスクライバがどちらも OBJECT_DELIVERY_TIMEOUT を 0 以外で指定した。使われるのは?