# DataChannel 経由のシグナリング

## 概要

DataChannel は WebRTC の機能の一つで文字列やバイナリといったデータの送受信を行える機能です。
Sora では WebRTC 接続確立後に、シグナリングを WebSocket 経由から DataChannel 経由に切り替えることができます。

## 目的

WebSocket は TCP ベースのため Head of Line Blocking が存在し、不安定な回線などでパケットが詰まってしまうことがあります。
DataChannel は WebSocket とは異なり、パケットを並列でやりとりできるため、不安定な回線などでもパケットが詰まることが少なくなります。
シグナリングを WebSocket 経由から DataChannel 経由へ切り替える機能を提供することでより安定した接続が維持できます。

> **警告**
>
> DataChannel 経由のシグナリングを利用した場合でも、
> TURN-TCP や TURN-TLS を利用している場合は、
> TCP ベースとなるため Head of Line Blocking が存在します。
> そのため、 WebSocket 経由のシグナリングと同じ問題が発生します。

## SDK 対応状況

- 最新版の JavaScript SDK- 対応済みです
- 最新版の iOS SDK- 対応済みです
- 最新版の Android SDK- 対応済みです
- 最新版の Unity SDK- 対応済みです
- 最新版の C++ SDK- 対応済みです
- 最新版の Python SDK- 対応済みです

## 注意

DataChannel 経由のシグナリングを有効にした場合、ラベルが違うメッセージは並列に処理されます。
そのため、ラベルをまたいだメッセージ間の順序は保証されなくなります。

例えば、WebSocket 経由のシグナリングを利用する場合は、マルチストリーム時の SDP 再交換メッセージ `"type": "re-offer"` によって発火する `ontrack` イベントが、
参加通知である `"type": "notify", "event_type": "connection.created"` イベントより先に発火することが多くなります。
DataChannel 経由のシグナリングでは `ontrack` と `"type": "notify", "event_type": "connection.created"` 順序は不定となります。

不明点がある場合はサポートまでお問い合わせください。

## 制限

- Sora の DataChannel 機能では、クライアント側から DataChannel を作成することはできません。- Sora 側で用意した DataChannel の Label のみを利用できます
- Sora の DataChannel 機能では、クライアント側からデータを送信する際には特定の条件を満たす必要があります。


## 推奨メッセージサイズ

DataChannel で利用している SCTP というプロトコルはもともと小さなメッセージを送るために設計されたため、
大きなメッセージを送るのには向いていません。そのため 1 メッセージサイズは 64 KiB 以内を推奨しています。

64 KiB 以上のメッセージを送る場合は、大きくても 64 KiB 以下のメッセージなるように分割して送るようにしてください。

> **注釈**
>
> Sora は Chrome の仕様に合わせてメッセージサイズの最大値を 256 KiB にしていますが、
> 64 KiB 以下のメッセージを送ることを推奨しています。

### 今後

libwebrtc が DataChannel の SCTP の I-DATA チャンクを実装する事で、
より大きなメッセージに対応できるようになる予定です。

- [5696 - Support ndata for SCTP - webrtc](https://bugs.chromium.org/p/webrtc/issues/detail?id=5696&sort=-modified)
- [RFC 8260 - Stream Schedulers and User Message Interleaving for the Stream Control Transmission Protocol](https://datatracker.ietf.org/doc/html/rfc8260)

### 参考資料











## シグナリング切り替え

DTLS が確立したタイミングで、シグナリングのやりとりを WebSocket 経由から DataChannel 経由に切り替える仕組みです。

> **重要**
>
> 切り替えが完了した場合でも、Sora 側からは WebSocket を切断しません。

シグナリングの切り替えを含めた DataChannel 経由のシグナリングの処理の流れは、[DataChannel シグナリング](DATA_CHANNEL_SIGNALING.html#5626ed) のシーケンス図をご確認ください。

### シグナリングの切り替えを有効にする

シグナリングの切り替えを有効にするには、次の 3 つの方法があります。

- `"type": "connect"` 時に `data_channel_signaling` を `true` にする
- 認証成功時に `data_channel_signaling` を `true` で払い出す
- `sora.conf` の [default_data_channel_signaling](SORA_CONF.html#adceef) を `true` にする

Sora SDK を利用している限りは、これらの設定のみで、シグナリングの WebSocket 経由から DataChannel 経由への切り替えを利用できます。

### "type": "switched"

DataChannel へ完全に切り替わるタイミングで、 Sora は `"type": "switched"` を WebSocket 経由でクライアントへ送ります。

DataChannel に切り替わる前に WebSocket が閉じた場合、Sora はその接続が無効と判断して、接続を終了します。

### DataChannel へ切り替わった後（"type": "switched" 後）の WebSocket での "type": "ping" / "type": "pong" について

WebSocket から DataChannel に切り替わると、 WebSocket の `"type": "ping"` を送る間隔が 5 秒から 30 秒に引き延ばされます。
さらに Sora が `"type": "pong"` を期待しなくなります。

## WebSocket が閉じたことを無視する

Sora は、デフォルトでは接続の切断判定に WebSocket を利用しています。WebSocket が閉じると、Sora は接続が切断したと判断します。

ただし、WebSocket が閉じた場合でも、接続が切断したと判断しないようにしたい場面もあるため、 `ignore_disconnect_websocket` という設定を用意しています。

この設定は `sora.conf` 、 `"type": "connect"` または認証成功時の払い出しで指定できます。

この設定を `true` にすることで、Sora は WebSocket が閉じてもその接続が切断したとは判断せずに、
WebRTC のやりとりを継続します。

> **注意**
>
> `"type": "disconnect"` を送らずに終了した場合、 DataChannel が切断に気付くまでには早くても 10 秒はかかります。

### クライアント側の切断タイミング

もし、 `ignore_disconnect_websocket` を `true` にして WebSocket を切断したい場合は、
必ず Sora から `"type": "switched"` を受け取った後にしてください。

`ignore_disconnect_websocket` が `true` の場合でも、 `"type": "switched"` を受け取る前に WebSocket を切断すると、
Sora はその接続が無効と判断して接続を終了します。


## DataChannel への切り替え要否の判断

`"type": "offer"` に `"data_channels"` が含まれることを確認することで、DataChannel への切り替えが必要と判断できます。

切り替えが不要な場合は `"data_channels"` は送られてきません。

```javascript
{
  "type": "offer",
  "data_channels": [
    {
      "compress": true,
      "label": "stats",
      "direction": "sendrecv"
    },
    {
      "compress": true,
      "label": "push",
      "direction": "recvonly"
    },
    {
      "compress": true,
      "label": "notify",
      "direction": "recvonly"
    },
    {
      "compress": true,
      "label": "signaling",
      "direction": "sendrecv"
    },
    {
      "compress": true,
      "label": "rpc",
      "direction": "sendrecv"
    }      
  ]
}
```

## label 単位の圧縮

Sora は、DataChannel 経由のメッセージを `zlib/deflate` で圧縮した送受信を要求する場合があります。

メッセージの圧縮や展開は Label ごとに要求します。

メッセージの圧縮や展開を要求する Label の情報は、 `"type": "offer"` 時に `data_channels` で `compress` を `true` に指定してクライアントに送ります。

- Sora は圧縮時に zlib ヘッダーを付けて送ってきます
- Sora は展開時に zlib ヘッダーを必要とします
- Sora は圧縮レベルは `default` を利用します

### 圧縮されるデフォルト Label

- signaling
- notify
- push
- stats
- rpc

## 設定

### default_data_channel_signaling

シグナリングを WebSocket から DataChannel 経由に切り替えるかどうかの設定です。
デフォルトは `false` です。

### default_ignore_disconnect_websocket

シグナリングを DataChannel 経由に切り替えた際に、 WebSocket が閉じても、接続が切断したとみなさない設定です。

デフォルトは `false` です。

### data_channel_signaling_close_message

シグナリングを DataChannel 経由かつ `ignore_disconnect_websocket` が `true` の場合に、
`signaling` ラベルに `"type": "close"` メッセージを送るかどうかの設定です。

デフォルトは `false` です。

## label とシグナリングのタイプ

> **重要**
>
> これらの DataChannel 経由のシグナリングで Sora が利用するラベルは `data_channels` で指定することはできません。 `data_channels` で指定するラベルは [リアルタイムメッセージング機能](MESSAGING.html) 用で、必ず先頭に `#` を付ける必要があります。

各 label とシグナリングのタイプについては、[DataChannel シグナリング](DATA_CHANNEL_SIGNALING.html#5626ed) の図も下記の説明と合わせてご確認ください。

### "label": "signaling"

**順番**: あり
**信頼性**: あり
**圧縮**: あり

WebRTC が確立する前の type: connect/offer/answer/candiate については、DataChannel 経由のシグナリングでは利用できません。

`"type": "re-offer"` は `"label": "signaling"` に送られてきます。

`"type": "re-answer"` 、 `"type": "disconnect"` は `"label": "signaling"` に送ります。

`"type": "ping"` と `"type": "pong"` は DataChannel では利用しません。

#### "type": "re-offer"

> **重要**
>
> "type": "update" の代わりに Sora からは "type": "re-offer" が送られてきます。

マルチストリーム利用時に SDP 再交換を行う際に、 Sora から送られてきます。

#### "type": "re-answer"

> **重要**
>
> "type": "update" の代わりに Sora へ "type": "re-answer" を送ります。

マルチストリーム利用時に SDP 再交換を行う際に、 Sora へ送ります。

#### "type": "disconnect"

接続を切断することを通知するために Sora へ送ります。

#### "type": "close"

**DataChannel シグナリングで新しく追加されたタイプです**

Sora から切断する場合に送ります。ただし、 `"type": "disconnect"` が送られてきた時は送りません。

`data_channel_signaling_close_message` が `true` かつ、 `ignore_disconnect_websocket` が `true` の場合に送ります。

### "label": "notify"

**順番**: あり
**信頼性**: あり
**圧縮**: あり

`"type": "notify"` は `"label": "notify"` に送られてきます。

中身は WebSocket 経由の値と変わりません。

### "label": "push"

**順番**: あり
**信頼性**: あり
**圧縮**: あり

`"type": "push"` は `"label": "push"` に送られてきます。

中身は WebSocket 経由の値と変わりません。

### "label": "stats"

**順番**: あり
**信頼性**: あり
**圧縮**: あり

今まで `"type": "ping"` と `"type": "pong"` 内部でやりとりしていた `stats` は DataChannel 経由では独立しました。

Sora から `{"type": "req-stats"}` が送られてきたら、 `{"type": "stats", "reports": [{"id": "...", ...}, ...]}` で `reports` の中に、
WebSocket 経由時には `"type": "pong"` で入れていた統計情報を入れて送り返してください。

#### "type": "req-stats"

**DataChannel シグナリングで新しく追加されたタイプです**

Sora からクライアントに stats 情報を要求します。

#### "type": "stats"

クライアントから Sora に stats 情報を送ります。

### "label": "rpc"

**順番**: あり
**信頼性**: あり
**圧縮**: あり

DataChannel 経由で一部の API を実行する [RPC 機能](RPC.html) で利用します。

## DataChannel 関連の設定

### data_channel_stats_timer_interval

シグナリングを DataChannel 経由に切り替えた際に、 Sora からクライアントへ統計情報の要求を送る間隔の設定です。

デフォルトは `60 s` です。

## シーケンス図


### 認証ウェブフックあり

```mermaid
sequenceDiagram
    autonumber
    participant C as クライアント
    participant S as Sora
    participant A as アプリケーションサーバー
    C->>+S: "type": "connect"
    S->>+A: 認証ウェブフック
    A-->>-S: 200 OK<br/>{"allowed": "true"}
    S->>-C: "type": "offer"
    C->>S: "type": "answer"
    C->>S: "type": "candidate"
    note over C,A: WebRTC 確立
    S->>+A: イベントウェブフック<br/>"type": "connection.created"
    A-->-S: 200 OK
    note over C,A: DataChannel 確立
    S->>C: "type": "switched"
    note over C,A: これ以降は DataChannel が利用される
```


### DataChannel シグナリング

```mermaid
sequenceDiagram
    autonumber
    participant C as クライアント
    participant S as Sora
    note over C,S: WebRTC 確立
    note over C,S: DataChannel 確立
    S->>C: "type": "switched"
    note over C,S: これ以降は DataChannel が利用される
    S->>C: "type": "re-offer"
    note right of S: label: signaling
    C->>S: "type": "re-answer"
    note left of C: label: signaling
    S->>C: "type": "notify"
    note right of S: label: notify
    S->>C: "type": "push"
    note right of S: label: push
    S->>C: "type": "req-stats"
    note right of S: label: stats
    C->>S: "type": "stats"
    note left of C: label: stats
```
