ついに映像同期したインライン Data に Webが対応。WebRTC Insertable Streams でのインラインData送受信のやり方

# これは何？

[WebRTC Insertable Streams](https://w3c.github.io/webrtc-insertable-streams/) では、E2EE（エンド to エンドの暗号化）が注目されていますが、僕は **映像に完全同期したインラインDataが可能となる** ほうが大きいニュースだと思っています。てことで、それのデモ紹介と使い方。

WebRTC Insertable Streams そのものについては

* https://qiita.com/massie_g/items/2b0b6d4f61f1865b4da5
や
* https://qiita.com/komasshu/items/13b0225ed839ac20548d
を参照ください。

# 映像同期したインラインDataの嬉しさ

これまでの WebRTC では映像と同時に通常のデータも送受信できました（WebRTC Data Channel）。しかし、Media Stream とはアウトバウンドの関係にあるため、送受信のタイミングをとることが難しく、例えば

* 特定フレームにあわせて、広告を挿入する。
* 特定フレームにあわせて、商品紹介ページを表示する
* 特定フレームにあわせて、何かしらのエフェクトを効かせる

といったことが困難でした。なんていうのかな。これまでの WebRTC は Web1.0 な感じで、Web ページの中に
**「映像通信を埋め込む」**
ことはできたのですが、Web2.0 的な
**「映像と外部データとが、がんがん連携して、イノベーティブなアプリケーションを生み出す（古い言葉でいうとマッシュアップ）」**
ということができなかった。

でも、これが Insertable Streams でついに搭載された、インライン Data のサポートにより可能となります。 **Realtime Web 2.0** の幕開けといっていいんじゃないかな？と個人的には興奮していたりします。Webでここまでワクワクするのはホント久しぶり ;)

# てことで使い方

インラインDataのやり方は、そんなに難しくないです。。。。バイナリデータのやりとりになれているネットワークエンジニア民にとっては。

Insertable Streams では、 `TransformStream` インスタンスを定義して、そこでエンコードデータに対して自由にストリーム処理ができるというものになっていますが、ここで、映像データの **末尾** に、バイナリ形式で additional データを突っ込んだり／とったりするだけです。

例えば、映像フレームに併せて、何かしらの色素情報（RGB3バイト）をやりとりするのであれば、送信側は

```js
    const transformStream = new TransformStream({
      transform: (chunk, controller) => {
        // videoストリームに、データを追加する場合
        if( kind === 'video' ) {
          // 最後に 3 バイトの固定フィールドを足す（RGBそれぞれの値を1バイトづつ指定）
          const rgb = new Uint8Array( 3 )
          rgb[0] = this._red
          rgb[1] = this._green
          rgb[2] = this._blue

          // 新たな、データコンテナ（Uint8Array)を作る。
          // 最初に、もともとの映像データを書き込んだ後、
          // RBGデータを末尾追加する
          const len = chunk.data.byteLength
          const container = new Uint8Array(len + 3)
          container.set( new Uint8Array( chunk.data ), 0)
          container.set( rgb, chunk.data.byteLength)

          // chunk.data をRGBデータが追加されたものに差し替える
          chunk.data = container.buffer
        }
        controller.enqueue( chunk )
      },
    }
```

てな感じでいけますし、受け側で抜き出す場合は、

```js
      const transformStream = new TransformStream({
        transform: (chunk, controller) => {
          if( kind === 'video' ) {
            // 最後の3バイト（RGB） を取り出し、border color としてセットする
            const rgb = new Uint8Array( chunk.data.slice(-3) )
            // rgb[0], rgb[1], rgb[2] に、それぞれ色素データ (0 - 255 )
            // が入っているので、これをよしなに処理するコードをここに記述
            
            // 最後の3バイトを除外することで映像データのみをとりだす
            const mediaData = new Uint8Array( chunk.data.slice(0, -3) )

            // chunk.data を映像データのみのものに差し替える　
            chunk.data = mediaData.buffer
          }
          controller.enqueue(chunk)
        },
      });
```

といった形でいけます（コードの詳細は、コメントを参照ください）。

# デモ Web アプリ

https://conf.kokutele.com/labs/insertable-streams-meta/ に、デモの Web アプリをおいておきました。
( Chrome の M85(stable) で動作確認。 chrome://flags/#enable-experimental-web-platform-features の enable を忘れずに ）

画面中央部の `add` ボタンクリック後、ボタン上部の red, blue, green の数字を変えてやると、それに応じて下部映像（WebRTC loopback P2P 受信映像）の border color が同期して変わっていきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/83466/9bdcc195-7262-cada-3dd2-e67ff812a817.png)

ソースコードは https://github.com/kokutele/kokutele/blob/master/public/labs/insertable-streams-meta/script.js においておきましたので、詳しくはそちらを御覧ください。

# おわりに

loopback デモだと、ありがたみ分かりづらいですが、送信側のデータ変更タイミングとタイミングばちこんで、リモート視聴側のエフェクトが変わっていきます。これまでの Web ではこれができなかった。

今回のデモと同じ考え方で、例えばライブイベントでアーティスト登場瞬間に、着ている洋服のURLを送って EC 販売とリアルタイム連動する・・・なんてなことも当然ながら可能です。

アイディア次第で、様々なリアルタイムマッシュアップアプリを作れると思います。バイナリデータの操作は一見面倒ですが、なれてしまえば大したことないですので、是非お試しいただければ。

気になる実装状況ですが、[Chrome Status](https://www.chromestatus.com/feature/6321945865879552) によると、M86 (今のベータチャネル）が stable に落ちてくれば（たぶん3ヶ月後）、普通に使えるようになります。いやーーーー楽しみですねぇ :)