🧊

フロントエンド x RTC界隈の最近とこれから

フロントエンドエンジニアからみる、この界隈で今どんなIssueが話題になってるのかと、この先どういう動きがありそうかについて。

そこまで自分に先見の明があるとも思ってないけど、アウトプットしておかないと忘れてしまいそうなので・・。

ちなみにここでいうフロントエンドは、いわゆるブラウザとかJavaScriptAPIのことです。
プロトコル的な側面はそこまで詳しくないのであまり触れません。

WebRTC 1.0

GitHub - w3c/webrtc-pc: WebRTC 1.0 API

まず、RTCといえばズバリのWebRTCから。

昨年末にWDからCRへ格上げということで、もうAPIが激変したりはしない・・はず。
実際のところ、ここ半年くらい大きな対応した覚えがないです。(WebRTCそのものを実装してる人は、地味にいろいろ対応してると思うけど)

ガワのAPIという観点でいうと、最近はもうユースケースを掘り下げていくフェーズというよりか、仕様としてのつじつま合わせやら、こんなときどうなるの?っていうコーナーケースへのIssueが多い印象。

たとえば通信中のノートPCをパタンって閉じたら、その通信はどうなる?とか、APIとしてこういうケースは可能になってるけど、その場合の状態遷移はどうあるべきか?とか。

JSのAPIネタでいうと、Perfect Negotiationの一連APIたちが実装された暁には、何かしら対応してみてもよいかもしれない。

WebRTCのPerfect negotiationについて - console.lealog();

まあ、シグナリングのグレアは他にも回避方法あるし、今さら・・とは思う。

(そんなことよりFirefoxに`setConfiguration()`実装してほしい。)

蛇足: Unified-plan

この記事を書いてて思い出したネタ。

Is everyone switching to Unified Plan? - webrtcHacks

Unified-planってそういえばどうなったっけ・・みんなちゃんと使ってる?恩恵受けてる?っていうやつ。

まぁその記事としても、Chromeの中の人たちとしても、どうやらあんましらしい。

857004 - chromium - An open-source project to help move the web forward. - Monorail

まあ実際は1トランスポートあたり1audio+1video以上やりとりしたいユースケースってあまり思いつかん + 別に2トランスポートでなんとかなるので、ほんとシビアな仕様やったんやなとは思う・・。

セマンティクスとしてはこっちのほうがシュッとしてると思うけど。

webrtc-extensions

GitHub - w3c/webrtc-extensions: A repository for "WebRTC 1.1+" features

そんな1.0のスコープから漏れた機能群は、だいたい拡張仕様って扱いになってます。

たとえばTPACでも話題になってた`RTCRtpReceiver`の`playoutDelay`。
いまだと受け取ったメディアはASAPで再生されるけど、そこに恣意的な遅延をつけられるようにしてちょっとバッファしたい・・とか。

あとはHWエンコードできるコーデックがAPIで事前にわかると嬉しいよね、とか。

まあこの界隈は論者が限られてるので、全般的にアクティブではないです。
そういう意味ではNextVersionに関しても、ここのところ下火。

GitHub - w3c/webrtc-nv-use-cases: Use cases for WebRTC NV

Statistics

GitHub - w3c/webrtc-stats: WebRTC Statistics

おなじみ`getStats()`で取れるレポートあれこれについて。
これも年明けにCRになり、いろいろ頑張ってるところ。

ただ実際のところはブラウザ差異がまったく埋まってなくて、我々としてはもう一声といった感じ。

GitHub - leader22/webrtc-stats-impl-status

ここに現時点の最新ブラウザでの実装差異をチマチマ集めてますのでご参照をば。

ただこれで定量的な値が取れたとて、実際は手札(そういう細かいAPI)が存在しなくてほとんどアクションできないんですよね〜。

Media Capture and Streams

GitHub - w3c/mediacapture-main: Media Capture and Streams specification (aka getUserMedia)

おなじみの`getUserMedia()`とか、`enumerateDevices()`とか。

こちらもCRながら、セマンティクスの問題とプライバシー方面の議論が入り混じって大乱闘状態。

すごくかいつまんでトピックを並べると、

  • バイス機能の詳細はFingerprintになるから見せられないよ
  • `{ video: true }`ですべてのデバイスの許可取れるのおかしくない?
  • `getDisplayMedia()`みたいに、ブラウザがピッカー持つべきでは?
    • `{ video: true, semantics: "user-chooses" }`的な
  • そもそも許可なしでデバイス一覧見れるのがおかしくない?
  • 一口にaudioって言っても、チャット用と演説用とで性質違うしその旨も指定したいです
  • etc..

`enumerateDevices()`もついこの間も変更があったところなので、これらに関してはまだこれからも変わりそうやなーという感じ。

ここに関しては、PSAが出たらさっさと検証するしかなさそう・・。

WebTransport

GitHub - WICG/web-transport: WebTransport is a web API for flexible data transport

ここからはちょっと先の話題。

サーバーとクライアント間の双方向で低遅延なやり取りがしたい場合の選択肢として、現状ではWebSocketかWebRTCのDataChannelかが主な選択肢になるはず。

ただWebSocketだとHoLBでパフォーマンスの問題があるし、WebRTCだとICEいらないしDTLSでSCTPなので実装が大変+そもそもサバクラで使いにくい。

ちょうどいいUDPベースのWebSocketライクなやつが欲しい・・ということで生まれたのが、QUICの上で動くWebTransport。

QUICとか、プロトコルの詳細については、別に詳しい人がいると思うので気になった人は調べてみてください。
ドラフトを訳したやつがあるのでいちおう置いておきます。

GitHub - leader22/webtransport-rfcs

たとえばクラウドゲーミングだと、クライアントからサーバーにはユーザー操作をデータで送って、サーバーからは結果がメディアで返ってくるのでそれを描画する感じ。

APIとしては`whatwg/streams`のそれ。

// Example of sending unreliable game state to server using QUIC datagrams

const transport = new QuicTransport('example.com', 10001);
const datagramWriter = transport.sendDatagrams().getWriter();

setInterval(() => {
  const message = getSerializedGameState();
  datagramWriter.write(message);
}, 100);

`ReadableStream`しかり`WritableStream`しかりをよしなに使うことになるはずで、このあたりのAPIは慣れが必要そう。

ただChromeですらまだIn developmentなので、まっだまだ先の話です。

WebRTC-QUIC

GitHub - w3c/webrtc-quic: Interface to create and manage QUIC streams

ChromeではOriginTrialまでしてたけど、最近はWebTransportのこともあってかやや下火。
というか、気付けばWebTransportありきの仕様になってる感。

クライアントP2PでのWebRTCは、しばらく今のままって感じですかねー。

WebSocketStream

GitHub - ricea/websocketstream-explainer: Explainer for the WebSocketStream JavaScript API

WebSocketで`Stream`のAPIを使えるようにして、BackPressureに対応したモダンな書き味にしたいよねというプロポーザル。

const wss = new WebSocketStream(url);
const { readable } = await wss.connection;

const reader = readable.getReader();
while (true) {
  const { value, done } = await reader.read();
  if (done) break;

  await process(value);
}
done();

こちらもモノはおなじく`whatwg/streams`ですね。

QUIC上で動くWebTransportのほうが上位互換ではあるけど、UDPが通らない環境とかもあると思うし、WebTransportがくるまでのつなぎとしての狙いもありそう。

ともあれデータをいい感じに流す土管は揃い踏みした感あるので、あとは何をやり取りするか。

ChromeCanaryではもう動くので、WebTransportよりは早そうではある。

WebCodecs

GitHub - WICG/web-codecs: WebCodecs is a flexible web API for encoding and decoding audio and video

いい感じの土管ができたら、そのデータを描画する部分のAPIがあるといいよね、ということで。

今もJS/WASMで自前エンコード・デコードすることはできるけど、それだとメモリ効率とかHWも有効利用できないし、そもそもブラウザに載ってるコーデックを再実装するのが無駄という話もあり。

WebRTCでも生のメディアには触れないし、やっぱあらゆるユースケースに対応するためには、コンポーネントを細かく切っていくしか無い世相を感じますね。

// Example of video rendering to Canvas for low-latency live streaming or cloud gaming

class CanvasRendererSink {
  constructor(canvas) {
    this._context = canvas.getContext('bitmaprenderer');
  }

  write(videoFrame) {
    _context.transferFromImageBitmap(videoFrame.image);
    return Promise.resolve();
  }
}

const canvas = document.getElementById("canvas");

const renderingStream = new WritableStream(new CanvasRendererSink(canvas));
const videoDecoder = new VideoDecoder({ codec: "vp8" } );

encodedVideoStream.pipeThrough(videoDecoder).pipeTo(renderingStream);

これもAPIとしては`whatwg/streams`のそれ(だいたい`TransformStream`)で、用途に応じていろいろ用意されるクラスを使い分けることになる想定。

これもIn developmentなやつ。

Insertable Streams API for WebRTC

GitHub - alvestrand/webrtc-media-streams: Insertable Streams API for WebRTC

話は戻ってまたWebRTC。
これもまだ単なるプロポーザルではあるけど、WebRTCでやり取りするメディアを手元で加工したいよねという話。

// それ用のフラグ
const pc = new RTCPeerConnection({
  forceEncodedVideoInsertableStreams: true,
  forceEncodedAudioInsertableStreams: true
});

// TransformStream
const senderTransform = new TransformStream({
  async transform(chunk, controller) { /* ... */ }
});

const videoSender = pc.addTrack(track, stream)
const senderStreams = videoSender.getEncodedVideoStreams();

senderStreams.readable
  .pipeThrough(senderTransform)
  .pipeTo(senderStreams.writable);

これは先述のWebCodecsありきなコードになってるけど、今の時点でも有効なユースケースが実はある。

それが、このAPIを使って今話題のE2Eの暗号化を実現するサンプル。

Peer connection end to end encryption

まだChromeのCanaryでしか動かないけど。

あとは受け取ったvideoのキーフレームをカウントするサンプルとかもレビュー中。

Video analyzer by alvestrand · Pull Request #1276 · webrtc/samples · GitHub

なんしか、画一的なやり方でメディアを加工できるようになるのはよいですね。

フロントエンドエンジニアとして

SDKを作る側としては、今後も仕様の動向やプロトコルそれ自体についてもある程度知っておく必要がありそう + APIとしては`Stream`に慣れておくとよいかも?

一般的なフロントエンドエンジニアがRTC系のことをやる場合は、基本的に何某かのSDKを使うはず。
なので、その背景のプロトコルAPIを知っておく必要は実際ほぼないと思ってて、それよりもそのSDKの通信モデルやらAPIを熟知して、できること・できないことを判断できるようになっておくほうがよいのかなと思います。

とは言っても汎用性の高いSDKを選んだなら、自分で`Stream`経由で生メディアに触れることもあるかもしれんけど・・。