13. 実践: moq.dev の実装

🎯 この章のゴール

  • moq.dev のオープンソース実装の全体像(moq-lite / hang / moq-relay / ライブラリ)を知る
  • IETF の moq-transport と moq-lite の違いを説明できる
  • ブラウザで MoQ の配信を視聴するコードのイメージを掴む

💡 ひとことで言うと

moq.dev は、標準が固まるのを待たずに今使える MoQ を作っています。標準のサブセットに機能を絞り、シンプルさと CDN での実装しやすさを優先しています。

moq.dev とは

moq.dev は、MoQ のオープンソース実装(MIT / Apache 2.0)と、そのマネージド CDN moq.pro を提供するプロジェクトです。ドキュメントは doc.moq.dev にあります。

層moq.devIETF
メディアフォーマットhang(カタログ + コンテナ)MSF / CMSF + LOC
pub/submoq-litemoq-transport
転送QUIC / WebTransport(+ WebSocket フォールバック)QUIC / WebTransport

moq.dev の実装は moq-transport も話せます(ドキュメント執筆時点で draft-14〜22 を ALPN で交渉)。hang の代わりに MSF のカタログを読み書きしたり、LOC コンテナを選んだりもできます。

moq-lite: 小さなサブセット

moq-lite は「moq-transport の意図的に小さなサブセット」です。データモデルは次のとおり。

moq-lite意味moq-transport での近いもの
Session1 本の接続。配信と視聴を同時にできるSession
Originセッションから見える Broadcast の集合(URL パスでスコープ)スコープ
Broadcast1 つのパブリッシャのトラックの集まり(名前付き)Track Namespace
TrackGroup の列Track
Group1 本の QUIC ストリームで順序どおり確実に届く Frame の列。先頭は新規参加者が復号を始められる点(キーフレームなど)Group(≒ 1 Subgroup)
Frameサイズ付きのバイト列Object
Datagram再送しない小さな Frame(moq-lite 05 以降)Datagram

省いたもの

moq-transport の機能moq-lite での扱い
Request IDリクエストごとにストリームを使うので不要
Push(PUBLISH)なし。サブスクライバが常に頼む
複数 Group にまたがる FETCHFETCH は 1 Group 内だけ
Subgroupなし。SVC のレイヤーはトラックを分ける
Object 番号の飛び(Gap)なし
Object ごとのメタデータ(プロパティ)なし。必要ならペイロードに入れる
一時停止なし。購読をやめる
バイト列の名前UTF-8 の名前

CDN(リレー)は多数のパブリッシャ・サブスクライバの要求をまとめて上流に流します。機能が多いほど「下流 A は一時停止、下流 B は別フィルタ、下流 C は別の Group Order……」の組み合わせが爆発し、集約が難しくなります。moq-lite は CDN が衝突なく実装できる部分に絞っています。

購読のつまみ

moq-lite の購読は、輻輳時のふるまいを 3 つのつまみで決めます。

  • Priority(0〜255): 優先度の高いトラックが先に帯域を得る
  • Order: ライブなら新しい順、追いかけ再生なら古い順
  • Max age: 最新でない Group がどれだけ古くなったらスキップするか(メディアの時間軸で測る)。0 ならライブエッジだけ

ドキュメントの例では、音声(priority 100、max age 500ms)と映像(priority 50、max age 2s)を購読すると、激しい輻輳時には映像が止まり、音声は最大 500ms の遅れで聞こえ続けます。

この例では音声(100)が映像(50)より優先されています。moq-transport の優先度は「小さいほど優先(0 が最高)」なので、実装をまたいで設定するときは向きを確認しましょう。

アナウンス = 経路

moq-lite では、パスのプレフィックスに対するアナウンスを要求すると、相手は今提供できるものを返し、その後は変化を流し続けます。アナウンスは「このプレフィックスの Broadcast を提供できる」という経路(route)で、ループ検出用のリレーのホップ列と、経路選択用のコストを持ちます。. で始まるパスセグメントは発見の対象から隠れますが、名前を知っていれば購読できます。

moq-transport の PUBLISH_NAMESPACE はルーティングプロトコルではありません(07 章)。moq-lite のアナウンスは、リレー網での経路選択にも使われます。

hang: メディアフォーマット

hang は moq-lite の上のメディアフォーマットで、カタログトラックと、各フレームにタイムスタンプを付けるコンテナの 2 つからなります。WebCodecs をモデルにしているので、ブラウザがそのままデコードできます。平文の Broadcast 名は .hang で終わります。

カタログ

  • トラック名 catalog.json(圧縮版 catalog.json.z もある)。トラックの増減に合わせてライブで更新
  • 映像・音声は「レンディション」の集合(トラック名がキー)。各レンディションは WebCodecs の VideoDecoderConfig / AudioDecoderConfig を拡張したもの(codec、description、codedWidth、sampleRate など)
  • 字幕・データ用の text / json / binary セクション、ラベル、別 Broadcast のレンディション参照、jitter / delay のヒントなども表現できる

コンテナ(container.kind)

kind中身特徴
legacyvi64 のマイクロ秒タイムスタンプ + コーデックのペイロード既定。最も軽い(LOC に近い)
cmafmoof + mdatfMP4 のまま HLS / DASH と相互運用。1 フレームあたり約 100 バイト増
loc小さなプロパティブロック + ペイロードIETF の LOC

Group の使い方

  • 映像: Group = GoP。キーフレームで始まり、リレーは Group 単位で捨てられ、視聴者は Group 境界から参加できる
  • 音声: Group は独立していて、典型的には約 1 秒分
  • データ: snapshot(新しい Group が前の Group を置き換え、最新だけ読めばよい)と stream(1 つの Group にずっと追記するログ)

moq-relay とクラスタ

moq-relay はセルフホストできるリレーサーバーです。

  • リレー同士はピアになってアナウンスと購読を転送する。視聴者は最寄りのリレーにつなぎ、リレーは必要ならピアから Broadcast を引いてキャッシュする
  • ピアの発見は静的リスト、HTTP の動的リスト、LAN の mDNS
  • 経路はホップ列(ループ検出・最短経路)とリンクコストで選び、中央のコントローラーなしにフェイルオーバーする
  • 認証は JWT(URL の ?jwt= で渡す)。ピア間は mTLS か JWT

ほかにも、任意の Broadcast をリレーのキャッシュから HLS で配る HLS ゲートウェイや、OBS からの配信取り込みなどのツールがあります。

ブラウザで視聴してみる

TypeScript のライブラリ @moq/watch は Web Component を提供していて、要素を登録すれば HTML に 1 行書くだけでプレイヤーを置けます(doc.moq.dev の例より。npm パッケージなので Vite などのバンドラー経由で読み込む想定です)。

import "@moq/watch/element";
<moq-watch url="https://relay.example.com/anon" name="room/alice.hang"></moq-watch>
  • url: リレーの URL、name: Broadcast 名
  • delay(ライブエッジからどれだけ遅らせるか、既定 auto)や buffer で、遅延とバッファの量を調整できる

ライブラリ一覧

言語主なパッケージ
Rustmoq-net、hang、moq-mux など(リレー本体は moq-relay)
TypeScript@moq/net、@moq/hang、@moq/watch、@moq/publish など
その他Swift、Kotlin、Python、Go、Dart、C のバインディング

パッケージ構成は活発に変わっているので、最新は doc.moq.dev/lib を確認してください。

ブラウザの対応状況

ブラウザでは WebTransport を使います。moq.dev のクライアントは WebSocket 接続も並行して試し、WebTransport が使えない・安定しない環境ではそちらを使います(その場合は TCP の HoL ブロッキングを避けられません)。対応状況はブラウザのバージョンで変わるので、doc.moq.dev の Transport のページ で確認しましょう。

🧪 考えてみよう

SVC(スケーラブル映像符号化)のベースレイヤーと拡張レイヤーを、moq-transport では 1 トラックの別 Subgroup で、moq-lite では別トラックで表します。後者の利点と欠点は? 利点はモデルが単純で、レイヤーごとに購読・優先度を独立に扱えること。欠点はトラックが増え、カタログや購読の管理が少し複雑になることです。

📝 まとめ

  • moq.dev は moq-lite(pub/sub)+ hang(メディア)+ moq-relay(リレー)+ 多言語ライブラリを提供し、moq-transport とも相互運用する
  • moq-lite は Subgroup・push・Gap・プロパティ・一時停止などを省き、CDN で集約しやすくしたサブセット
  • 購読は Priority / Order / Max age で輻輳時のふるまいを決める。優先度の数値の向きは moq-transport と逆の例があるので注意
  • <moq-watch> を置くだけでブラウザ視聴できる

参考

✅ 理解度クイズ

Q1
moq.dev の moq-lite と IETF の moq-transport の関係は?
Q2
moq-lite にない機能はどれ?
Q3
moq-lite の購読で「Max age」を 0 にすると?
Q4
hang のカタログについて正しいものは?