RheomeshでScalable Video Coding(SVC)をサポートする

h3poteto.hatenablog.com

ここで作ったOSSにScalable Video Coding(SVC)のサポートを入れた.

0.7.1から使えるようになっている.

github.com

SVCとは

https://zenn.dev/yohhoy/articles/webrtc-svcext-av1

この辺が参考になる.やりたいこととしては,受信側がPCのスペックや回線スピードに合わせて適切な解像度/FPSで映像を受信するためのものだ.もともとRheomeshではSimulcastをサポートしており,SVCはサポートしていなかった.

Simulcastの場合,本当に複数本の映像を送信側が送る必要がある.高解像度・中解像度・低解像度の映像をそれぞれ送信しつつ,受信側で適切なものを選んで受信させるという方式だ.この場合の問題点は,送信側が3本映像を送らなきゃいけないとういことだ.同じ映像なのに,解像度ごとに2本3本と送る必要が有り,これは結構無駄な気がする.

SVCはこれに対して,送る映像は1本で良い.その中で,空間スケーラビリティ・時間スケーラビリティに分割されたパケットが送られてきて,SFUサーバ側でフィルタリングして受信側に渡してやる.例えば受信側が十分はスペック・回線スピードであれば,すべてのデータを渡すことで,高解像度・高FPSの映像を受信させる.逆に受信側が低スペックの場合であれば,SFUサーバ側で低解像度・低FPSのパケットのみをフィルタリングして送ってやることで,実際に低スペックに合わせた映像を受信することができる.

www.w3.org

使い方

配信側

配信側はサーバでの設定は一切不要だ,クライアント側の設定だけで完結する.

https://h3poteto.github.io/rheomesh//pages/02_getting_started/#publish

この例でpublishする際に,

import { SVCEncodings } from "rheomesh";

const publisher = await publishTransport.current!.publish(track, {
  encodings: SVCEncodings(),
  preferredCodec: "AV1",
});

とすることでSVCが有効なpublisherとなる.ちなみにSVCはAV1もしくはVP9でサポートされている. SVCEncodings は現状 L2T3_KEY になっているが,別にここは好きに変更してもらっても構わない.型は Array<SVCRTCRtpEncodingParameters> なので,

encodings: [{ scalabilityMode: "L2T3_KEY" }]

みたいな形になっていれば問題ない.

受信側

受信側は,受信クライアントから「どのSID, TIDのパケットを受信したいか」を受け取る必要がある.

exampleでは,このようなWebSocketのメッセージを受信する.

https://github.com/h3poteto/rheomesh/blob/a9a63ef08a995ea7f6b430cc743d3fc7a8d9b748/sfu/examples/media_server.rs#L381-L396

そして,

if let Err(err) = subscriber.set_preferred_layer(sid, tid).await {
    tracing::error!("Failed to set preferred layer: {}", err);
}

こんなメソッドを呼び出してやればよい.これで,このsubscriberは指定されたSID, TIDのパケットのみを受け取るようになる.

今後の予定とか

今のところ受信側が手動で,というか意図的にSIDやTIDを指定して映像を切り替える必要がある.これはこれで必要なシーンはあると思う.例えば特定の映像だけデカく表示したいとか,その他の映像はアイコン程度の大きさで良い場合とかね.

ただ,一般的なミーティングにおけるユースケースだと,いちいちクライアントがどのSID/TIDを指定すべきかの判断材料があまりない.なので,できれば受信側から受け取れるRTCPに含まれる情報から,帯域等のフィードバックを得られれば,それによりSFUサーバ側で自動判定してやるロジックを入れたいとは思っている.そうすれば,送信側でSVC指定するだけで,受信側は自動選択になるはずだ.