Peering

Peering / ブログ

PeeringがMacの画面をiPhoneに届ける仕組み

リモートデスクトップは映像ストリームですが、見ている人が同時に操作している人でもあるという、特殊な条件があります。以下の設計上の選択はすべて、このループを短く保つことから来ています。

キャプチャ:システムに映像を頼む

MacアプリはAppleのScreenCaptureKitでフレームを取得します。取得元はディスプレイ全体、1つのアプリのすべてのウィンドウ、 または1つのウィンドウで、接続を切らずに切り替えられます。macOSが初回に画面収録の権限を求めるのもこのためで、 許可がないと、Macは真っ黒な画面しか渡せません。

コードエディタを開いたMacのデスクトップが、Peeringを動かしているiPhoneにライブで映し出されています。
iPhoneに映っているのは、スクリーンショットの連続ではなく、エンコードされた映像ストリームです。

エンコード:低遅延で、並べ替えなし

フレームはMacのAppleのVideoToolboxエンコーダーに渡され、iPhoneがデコードできるほうのH.264またはHEVCになります。 設定は、ストリーミングサービスが使うものとは正反対です。

デコーダーの設定情報(パラメータセット)は、フレームごとに繰り返さず、独立したメッセージとして1回だけ送ります。 途中から参加したiPhoneは、そのメッセージを受け取り、続いてキーフレームを受け取ります。

送信:捨てられるフレームと、守られるキーフレーム

各フレームには、25バイトの小さなヘッダーが付きます。キーフレームかどうか、ピクセルサイズ、表示時刻です。 リレー経由でも直接の接続でも、フレームは約1,080バイトのデータグラムに分割され、 2台のデバイスだけが知る鍵でChaCha20-Poly1305により封をされます。 各データグラムのカウンターは64エントリのリプレイウィンドウに渡されるので、取得されたパケットを後から再生することはできません。

輻輳に対するルールは単純です。映像は捨ててよく、キーフレームは捨てません。古いフレームがまだ送信待ちのうちに新しいフレームの準備ができたら、 古いほうが置き換えられます。ただし、未配信のキーフレームが通常のフレームに置き換えられることはありません。 存在したすべてのフレームを見せるより、今の画面を見せることのほうが大切です。

自宅のネットワークでは、映像は専用のTCP接続を通ります。ネットワークが十分に信頼できるので、同じルールが働くことはまずありません。 どのソケットもTCP_NODELAYを設定しています。小さな書き込みをまとめる初期の動作は、1ホップごとに数十ミリ秒を足します。 ダウンロードでは気づきませんが、カーソルを動かしているときには非常にはっきり分かります。

回復:損失は遅延ではなく映像で払う

UDPは再送しないので、データグラムが失われるとフレームが乱れます。iPhoneはそれを待ちません。デコードを続け、 乱れは最大2秒後の次のキーフレームで消えます。Wi-Fiの調子が悪かったあとに映像がにじんで見えたら、これが起きています。 映像を一時停止して再開すると、すぐにキーフレームが送られます。

高速な経路がまったく届かなくなった場合、接続についての記事で説明したとおり、 ストリームは低いビットレートで制御用の接続にフォールバックし、高速な経路が回復すると戻ります。

表示:Macではなく、iPhoneに合わせる

iPhoneは画質の段階を選び、毎秒のフレーム数を減らすように頼むこともできます。Mac側はそれを妥当な範囲に収めます。 アプリはデスクトップ全体を画面に収めることも、画面いっぱいに広げることもでき、iPhoneの形をした仮想ディスプレイをMacに頼むこともできるので、 横長のモニターを縦長の画面に押し込むことがありません。映像上のタップは逆方向に届きます。それは タップがクリックになる仕組みで扱っています。

よくある質問

PeeringはどのビデオコーデックをMacで使いますか?

H.264またはHEVCで、MacのVideoToolboxでエンコードします。Bフレームなし、低遅延の設定です。どちらを使うかは、iPhoneがデコードできるほうをMacに伝えます。

映像が一瞬にじむことがあるのはなぜですか?

ネットワークのパケットが失われるとフレームが乱れ、映像は少なくとも2秒ごとに届く次のキーフレームで回復します。Peeringは、再送を待つよりも、一瞬のにじみを選びます。