# サイマルキャスト機能

## 概要

サイマルキャスト (Simulcast) は、配信時に 1 つの RTCPeerConnection から複数種類のエンコードした映像を配信する技術です。

映像を配信する際、 Sora に対して、 1 つの RTCPeerConnection で複数種類のエンコードした映像を送信することにより、
受信者がどの映像を受信するかを選択できるようになります。

## 注意

### 現時点でのサイマルキャストの仕様

2026 年 6 月 時点で libwebrtc を利用したサイマルキャストで出力されるストリームの本数は解像度とビットレートに依存しています。

#### VP8 / H264

**解像度とビットレートとストリーム数の関係**

* - 解像度
  - ビットレート
  - ストリーム数
* - 1920x1080
  - 5000 kbps
  - 3
* - 1280x720
  - 2500 kbps
  - 3
* - 960x540
  - 1200 kbps
  - 3
* - 640x360
  - 700 kbps
  - 2
* - 480x270
  - 450 kbps
  - 2
* - 320x180
  - 200 kbps
  - 1

#### VP9 / AV1 / H265

**解像度とビットレートとストリーム数の関係**

* - 解像度
  - ビットレート
  - ストリーム数
* - 1920x1080
  - 3367 kbps
  - 3
* - 1280x720
  - 1524 kbps
  - 3
* - 960x540
  - 879 kbps
  - 3
* - 640x360
  - 420 kbps
  - 2
* - 480x270
  - 257 kbps
  - 1
* - 320x180
  - 142 kbps
  - 1
* - 240x135
  - 101 kbps
  - 1

#### libwebrtc の仕様

この値は libwebrtc にハードコードされています。 VP8 (AV1/H264) と VP9(H265) で異なります。

```cpp
// These tables describe from which resolution we can use how many
// simulcast layers at what bitrates (maximum, target, and minimum).
// Important!! Keep this table from high resolution to low resolution.
constexpr const SimulcastFormat kSimulcastFormatsVP8[] = {
    {.width = 1920,
     .height = 1080,
     .max_layers = 3,
     .max_bitrate = DataRate::KilobitsPerSec(5000),
     .target_bitrate = DataRate::KilobitsPerSec(4000),
     .min_bitrate = DataRate::KilobitsPerSec(800)},
    {.width = 1280,
     .height = 720,
     .max_layers = 3,
     .max_bitrate = DataRate::KilobitsPerSec(2500),
     .target_bitrate = DataRate::KilobitsPerSec(2500),
     .min_bitrate = DataRate::KilobitsPerSec(600)},
    {.width = 960,
     .height = 540,
     .max_layers = 3,
     .max_bitrate = DataRate::KilobitsPerSec(1200),
     .target_bitrate = DataRate::KilobitsPerSec(1200),
     .min_bitrate = DataRate::KilobitsPerSec(350)},
    {.width = 640,
     .height = 360,
     .max_layers = 2,
     .max_bitrate = DataRate::KilobitsPerSec(700),
     .target_bitrate = DataRate::KilobitsPerSec(500),
     .min_bitrate = DataRate::KilobitsPerSec(150)},
    {.width = 480,
     .height = 270,
     .max_layers = 2,
     .max_bitrate = DataRate::KilobitsPerSec(450),
     .target_bitrate = DataRate::KilobitsPerSec(350),
     .min_bitrate = DataRate::KilobitsPerSec(150)},
    {.width = 320,
     .height = 180,
     .max_layers = 1,
     .max_bitrate = DataRate::KilobitsPerSec(200),
     .target_bitrate = DataRate::KilobitsPerSec(150),
     .min_bitrate = DataRate::KilobitsPerSec(30)},
    // As the resolution goes down, interpolate the target and max bitrates down
    // towards zero. The min bitrate is still limited at 30 kbps and the target
    // and the max will be capped from below accordingly.
    {.width = 0,
     .height = 0,
     .max_layers = 1,
     .max_bitrate = DataRate::KilobitsPerSec(0),
     .target_bitrate = DataRate::KilobitsPerSec(0),
     .min_bitrate = DataRate::KilobitsPerSec(30)}};
```

```cpp
// These tables describe from which resolution we can use how many
// simulcast layers at what bitrates (maximum, target, and minimum).
// Important!! Keep this table from high resolution to low resolution.
constexpr const SimulcastFormat kSimulcastFormatsVP9[] = {
    {.width = 1920,
    .height = 1080,
    .max_layers = 3,
    .max_bitrate = DataRate::KilobitsPerSec(3367),
    .target_bitrate = DataRate::KilobitsPerSec(3367),
    .min_bitrate = DataRate::KilobitsPerSec(769)},
    {.width = 1280,
    .height = 720,
    .max_layers = 3,
    .max_bitrate = DataRate::KilobitsPerSec(1524),
    .target_bitrate = DataRate::KilobitsPerSec(1524),
    .min_bitrate = DataRate::KilobitsPerSec(481)},
    {.width = 960,
    .height = 540,
    .max_layers = 3,
    .max_bitrate = DataRate::KilobitsPerSec(879),
    .target_bitrate = DataRate::KilobitsPerSec(879),
    .min_bitrate = DataRate::KilobitsPerSec(337)},
    {.width = 640,
    .height = 360,
    .max_layers = 2,
    .max_bitrate = DataRate::KilobitsPerSec(420),
    .target_bitrate = DataRate::KilobitsPerSec(420),
    .min_bitrate = DataRate::KilobitsPerSec(193)},
    {.width = 480,
    .height = 270,
    .max_layers = 2,
    .max_bitrate = DataRate::KilobitsPerSec(257),
    .target_bitrate = DataRate::KilobitsPerSec(257),
    .min_bitrate = DataRate::KilobitsPerSec(121)},
    {.width = 320,
    .height = 180,
    .max_layers = 1,
    .max_bitrate = DataRate::KilobitsPerSec(142),
    .target_bitrate = DataRate::KilobitsPerSec(142),
    .min_bitrate = DataRate::KilobitsPerSec(30)},
    {.width = 240,
    .height = 135,
    .max_layers = 1,
    .max_bitrate = DataRate::KilobitsPerSec(101),
    .target_bitrate = DataRate::KilobitsPerSec(101),
    .min_bitrate = DataRate::KilobitsPerSec(30)},
    // As the resolution goes down, interpolate the target and max bitrates down
    // towards zero. The min bitrate is still limited at 30 kbps and the target
    // and the max will be capped from below accordingly.
    {.width = 0,
    .height = 0,
    .max_layers = 1,
    .max_bitrate = DataRate::KilobitsPerSec(0),
    .target_bitrate = DataRate::KilobitsPerSec(0),
    .min_bitrate = DataRate::KilobitsPerSec(30)}};
```

- VP8 向けが H.264 と VP8 に利用されます
- VP9 向けが VP9 と AV1 と H.265 に利用されます



### SDK 対応状況

以下の SDK の最新版で、配信と視聴ともに VP8 と VP9 と AV1 と H.264 と H.265 に対応済みです。

- JavaScript SDK
- iOS SDK
- Android SDK
- Unity SDK
- C++ SDK
- Python SDK

### 配信ブラウザの対応状況

配信側は Chrome と Edge と Safari の最新バージョンに対応しています。

> **警告**
>
> Firefox は配信に対応していません。

### 視聴ブラウザの対応状況

視聴側は Chrome と Safari と Firefox と Edge の最新バージョンに対応しています。

> **警告**
>
> Firefox は AV1 に対応していません。

### 映像コーデック

配信時に利用できる映像コーデックは VP8 と VP9 と AV1 と H.264 と H.265 です。

### 映像ビットレート

利用するコーデックと解像度に合わせてビットレートを選択してください。

- 1080p AV1 を利用し、デフォルトのサイマルキャストの設定- 解像度が 1080p / 540p / 270p の 3 種類の映像を配信します
  - 指定すべきビットレートは 3367 + 879 + 257 = 4503 kbps です
  - 1080p の映像は 3367 kbps で配信され、540p の映像は 879 kbps で配信され、270p の映像は 257 kbps で配信されます
- 720p H.264 を利用し、デフォルトのサイマルキャストの設定- 解像度が 720p / 360p / 180p の 3 種類の映像を配信します
  - 指定すべきビットレートは 2500 + 700 + 450 = 3650 kbps です
  - 720p の映像は 2500 kbps で配信され、360p の映像は 700 kbps で配信され、180p の映像は 450 kbps で配信されます


### VP9 と AV1 でのサイマルキャスト

> **注意**
>
> Safari 26.0 ではまだ VP9 と AV1 のサイマルキャストに対応していません

VP9 や AV1 でサイマルキャストを行う場合、ブラウザでは配信側が Chrome 113 / Edge 113 以降である必要があります。

また設定では `scalabilityMode` と `scaleResolutionDownBy` または `scaleResolutionDownTo` のどちらかの 2 つを設定する必要があります。

詳細は [scalabilityMode](SIMULCAST.html#918c4d) をご確認ください。


### H.265 でのサイマルキャスト

H.265 でサイマルキャストを行う場合、
ブラウザでは配信側が Chrome 137 か Safari 18.0 以降を利用する必要があり、
視聴側も同様に Chrome 137 か Safari 18.0 以降を利用する必要があります。

### 非対応機能

**以下は今後対応予定です**

- ULPFEC 機能には対応していません


## 映像の優先度

サイマルキャストでは 1 つのクライアントからの同じ映像を複数のエンコードで配信できます。

Sora では 1 つのクライアントから最大 3 種類のエンコードで映像を受信する仕組みが入っています。
この 3 種類の映像のそれぞれに r0 / r1 / r2 の 3 つの値を定義しています。
この値は rid と呼ばれています。

配信継続の優先度は r0 > r1 > r2 です。

例えば、回線が不安定だったりマシンの負荷が高かった場合、rid が `r2` の映像の配信が最初に停止します。
さらに配信状況が悪化した場合は rid が `r1` の映像の配信が停止します。 `r0` の映像は必ず配信されます。

## 映像の種類のデフォルト

Sora ではサイマルキャストで配信する映像の種類にデフォルト値を設定しています。

```javascript
[
  {"rid": "r0", "active": true, "scaleResolutionDownBy": 4.0, "scalabilityMode": "L1T1"},
  {"rid": "r1", "active": true, "scaleResolutionDownBy": 2.0, "scalabilityMode": "L1T1"},
  {"rid": "r2", "active": true, "scaleResolutionDownBy": 1.0, "scalabilityMode": "L1T1"}
]
```

### r0

`rid` が `r0` の場合は通常の解像度と比べて、一辺 が 1/4 の解像度の映像になるように設定されています。

### r1

`rid` が `r1` の場合は通常の解像度と比べて、一辺 が 1/2 の解像度の映像になるように設定されています。

### r2

`rid` が `r2` の場合は通常の解像度のままの映像に設定されています。


## rid: none

Sora ではシグナリング開始時、認証成功時、および API で `rid` を `none` にリクエストすることができます。 `rid` に `none` を指定した場合、視聴側に映像ストリームが配信されなくなります。

- シグナリング開始時の `simulcast_request_rid` で `none` を指定することができます
- 認証成功時の `simulcast_request_rid` で `none` を指定することができます
- [RequestSimulcastRid](API_SIMULCAST.html#7d26ab) API の `rid` で `none` を指定することができます

> **注意**
>
> `sora.conf` の [simulcast_encodings_file](SORA_CONF.html#dead73) や認証成功時に払いだす `simulcast_encodings` では `rid` に `none` を指定することはできません。


## 視聴側の自動 rid ストリーム切り替え機能

*バージョン 2025.2.0 で追加。*

> **注意**
>
> この機能は [実験的機能](EXPERIMENTAL.html) のため、正式版では仕様が変更される可能性があります。

従来の Sora では、視聴側がどの `rid` ストリームを受信するかは、アプリケーション側が API を利用して明示的に切り替える必要がありました。

Sora 2025.2.0 では新たに、視聴側の環境に合わせて Sora が自動的に `rid` ストリームを切り替えて配信する機能を追加しました。

この機能をデフォルトで有効にする場合は、 `sora.conf` の [default_simulcast_auto_rids](SORA_CONF.html#97a674) に、自動切り替えに利用する rid をリストで指定してください。

接続ごとに設定を指定する場合は、認証成功時に `simulcast_auto_rids` を指定することができます。これは `sora.conf` の [default_simulcast_auto_rids](SORA_CONF.html#97a674) 設定を上書きします。

`simulcast_auto_rids` で `rid` が指定されている状態で、 `simulcast_request_rid` で指定した `rid` が視聴環境の制約により利用できないと判断された場合、 Sora は自動でより低いビットレートの `rid` に切り替えます。

`simulcast_auto_rids` には、 `["r0", "r1"]` のように複数の rid を指定することができます

## シグナリング通知での rid 切り替えイベント

`sora.conf` の `signaling_notify_simulcast_switched` を `true` に設定することで、視聴側の自動 rid 切り替えが発生した際にシグナリング通知が送信されます。


## 映像のエンコーディングパラメーターのカスタマイズ

Sora ではサイマルキャストで配信する映像のエンコーディングパラメーターを自由に設定できます。

- sora.conf のファイルで指定することで全体のサイマルキャストのエンコーディングパラメーターが指定できます
- 認証成功時に simulcast_encodings で払い出すことで個別にサイマルキャストのエンコーディングパラメーターが指定できます

### sora.conf でファイル指定

sora.conf にて `simulcast_encodings_file` で JSON を指定してください。

```ini
simulcast_encodings_file = etc/simulcast_encodings.json
```

### sora.conf で指定する JSON ファイルの記述方法

```javascript
[
  {"rid": "r0", "active": true, "scaleResolutionDownBy": 2.0, "maxFramerate": 5.0},
  {"rid": "r1", "active": true, "scaleResolutionDownBy": 2.0, "maxFramerate": 20.0},
  {"rid": "r2", "active": true, "scaleResolutionDownBy": 1.0, "maxFramerate": 30.0}
]
```

### 認証成功時に simulcast_encodings で払い出す記述方法

```javascript
{
  "allowed": true,
  "simulcast_encodings": [
    {"rid": "r0", "active": true, "scaleResolutionDownBy": 2.0, "maxFramerate": 5.0},
    {"rid": "r1", "active": true, "scaleResolutionDownBy": 2.0, "maxFramerate": 20.0},
    {"rid": "r2", "active": true, "scaleResolutionDownBy": 1.0, "maxFramerate": 30.0}
  ]
}
```


### カスタマイズの設定

- rid- 必須
  - string (r0 / r1 / r2)
- active- このエンコーディングパラメーターを有効にします
  - オプション
  - boolean
- scaleResolutionDownBy- オプション
  - 1 以上の number (float または integer)
- maxBitrate- オプション
  - 最大ビットレートを指定する
  - 0 以上の integer
- maxFramerate- オプション
  - 最大フレームレートを指定する
  - 0 以上の number (float または integer)






- scaleResolutionDownTo- **この設定は Chrome 131 / Edge 131 以降でしか利用できません**
  - オプション
  - `{"maxHeight": 1080, "maxWidth": 1920}` のような JSON Object
  - `maxHeight` と `maxWidth` は 0 よりも大きい整数
  - `scaleResolutionDownBy` よりも優先されます
  - `maxHeight` と `maxWidth` の両方が指定されている場合に有効です
- adaptivePtime- **この設定は Chrome でしか利用できません**
  - オプション
  - boolean
- priority- **この設定は Chrome でしか利用できません**
  - オプション
  - string (`very-low` / `low` / `medium` / `high`)
- networkPriority- **この設定は Chrome でしか利用できません**
  - オプション
  - string (`very-low` / `low` / `medium` / `high`)
- scalabilityMode- **この設定は Chrome / Edge でしか利用できません**
  - オプション
  - string

### active: false の挙動

r1 の active を false にカスタマイズしている配信で r1 を受信をリクエストした場合、r0 が配信されます。

```javascript
[
  {"rid": "r0", "active": true},
  {"rid": "r1", "active": false},
  {"rid": "r2", "active": true}
]
```

r0 の active を false にカスタマイズしている配信で r0 を受信をリクエストした場合、r1 が配信されます。

```javascript
[
  {"rid": "r0", "active": false},
  {"rid": "r1", "active": true},
  {"rid": "r2", "active": true}
]
```

r0 と r1 の active を false にカスタマイズしている配信で r0 を受信をリクエストした場合、r2 が配信されます。

```javascript
[
  {"rid": "r0", "active": false},
  {"rid": "r1", "active": false},
  {"rid": "r2", "active": true}
]
```


### scalabilityMode

**この設定は Chrome / Edge M113 以降でのみ有効です**

> **注釈**
>
> [PSA: VP9/AV1 simulcast support in M113](https://groups.google.com/g/discuss-webrtc/c/-QQ3pxrl-fw?pli=1)

VP9 または AV1 でサイマルキャストを利用する場合の条件として、
`scalabilityMode` と `scaleResolutionDownBy` または `scaleResolutionDownTo` のどちらかをすべての rid に対して設定する必要があります。

サイマルキャスト利用時に `scalabilityMode` に設定可能な値は `L1T1` 、 `L1T2` 、 `L1T3` のみです。

デフォルトでは `L1T1` が指定されています。特に理由がなければ `L1T1` を利用してください。

> **重要**
>
> AV1 でサイマルキャストやスポットライトを利用し、録画をする場合は `L1T1` を利用してください。
> L1T1 以外では正常に録画がされません。

#### 例

```javascript
[
  {"rid": "r0", "active": true, "scaleResolutionDownBy": 4.0, "scalabilityMode": "L1T1"},
  {"rid": "r1", "active": true, "scaleResolutionDownBy": 2.0, "scalabilityMode": "L1T1"},
  {"rid": "r2", "active": true, "scaleResolutionDownBy": 1.0, "scalabilityMode": "L1T1"}
]
```

## 配信側の利用方法

シグナリング開始時に指定できます。

### シグナリング開始時

シグナリングの `"type": "connect"` 時に `"simulcast": true` を指定することで、有効になります。
映像コーデックは `VP8` または `H264` を選択してください。

```javascript
{
  "type": "connect":,
  "role": "sendonly",
  "channel_id": "sora",
  "video": {"codec_type": "VP8"},
  "simulcast": true
}
```

## 視聴側の利用方法

シグナリング開始時に指定できます。

### シグナリング開始時

- シグナリング `"type": "connect"` 時に `"simulcast": true` を指定することで、有効になります
- `simulcast_request_rid` には `none` / `r0` / `r1` / `r2` が指定できます- `simulcast_request_rid` を指定しない場合は `r0` が指定されます
  - この値は **サイマルキャストで配信されている映像を受信する際のデフォルト値** です
  - この値は `sora.conf` の `default_simulcast_rid` で変更できます
  - `none` を指定した場合、映像は配信されません
- 映像コーデックは `VP8` または `H264` を選択してください

```javascript
{
  "type": "connect":,
  "role": "recvonly",
  "channel_id": "sora",
  "video": {"codec_type": "VP8"},
  "simulcast": true,
  "simulcast_request_rid": "r2"
}
```

## 視聴側のストリームの変更 API

詳細は [サイマルキャスト API](API_SIMULCAST.html) をご確認ください

## scalabilityMode

- scalabilityMode は Chrome と Edge でのみ利用できます。
- Edge では H.265 が利用できないため、H.265 の結果は Chrome の結果となります。
- VP8 と H.264 では L1T1 / L1T2 / L1T3 のみが利用できます
- H.265 では L1T1 のみが利用できます
- scalabilityMode の詳細に付いては [Scalable Video Coding (SVC) Extension for WebRTC](https://w3c.github.io/webrtc-svc/) をご確認ください。

**scalabilityMode 対応状況**

* - Mode
  - VP8
  - VP9
  - AV1
  - H.264
  - H.265
* - L1T1
  - o
  - o
  - o
  - o
  - o
* - L1T2
  - o
  - o
  - o
  - o
  - x
* - L1T3
  - o
  - o
  - o
  - o
  - x
* - L2T1
  - x
  - o
  - o
  - x
  - x
* - L2T2
  - x
  - o
  - o
  - x
  - x
* - L2T3
  - x
  - o
  - o
  - x
  - x
* - L3T1
  - x
  - o
  - o
  - x
  - x
* - L3T2
  - x
  - o
  - o
  - x
  - x
* - L3T3
  - x
  - o
  - o
  - x
  - x
* - L2T1h
  - x
  - o
  - o
  - x
  - x
* - L2T2h
  - x
  - o
  - o
  - x
  - x
* - L2T3h
  - x
  - o
  - o
  - x
  - x
* - L3T1h
  - x
  - o
  - o
  - x
  - x
* - L3T2h
  - x
  - o
  - o
  - x
  - x
* - L3T3h
  - x
  - o
  - o
  - x
  - x
* - S2T1
  - x
  - o
  - o
  - x
  - x
* - S2T2
  - x
  - o
  - o
  - x
  - x
* - S2T3
  - x
  - o
  - o
  - x
  - x
* - S2T1h
  - x
  - o
  - o
  - x
  - x
* - S2T2h
  - x
  - o
  - o
  - x
  - x
* - S2T3h
  - x
  - o
  - o
  - x
  - x
* - S3T1
  - x
  - o
  - o
  - x
  - x
* - S3T2
  - x
  - o
  - o
  - x
  - x
* - S3T3
  - x
  - o
  - o
  - x
  - x
* - S3T1h
  - x
  - o
  - o
  - x
  - x
* - S3T2h
  - x
  - o
  - o
  - x
  - x
* - S3T3h
  - x
  - o
  - o
  - x
  - x
* - L2T2_KEY
  - x
  - o
  - o
  - x
  - x
* - L2T2_KEY_SHIFT
  - x
  - o
  - o
  - x
  - x
* - L2T3_KEY
  - x
  - o
  - o
  - x
  - x
* - L2T3_KEY_SHIFT
  - x
  - x
  - x
  - x
  - x
* - L3T1_KEY
  - x
  - o
  - o
  - x
  - x
* - L3T2_KEY
  - x
  - o
  - o
  - x
  - x
* - L3T2_KEY_SHIFT
  - x
  - x
  - x
  - x
  - x
* - L3T3_KEY
  - x
  - o
  - o
  - x
  - x
* - L3T3_KEY_SHIFT
  - x
  - x
  - x
  - x
  - x

## シーケンス図

```mermaid
sequenceDiagram
    participant C1 as クライアント1
    participant S as WebRTC SFU Sora
    participant C2 as クライアント2
    C1->>S: "type": "connect",<br/>"role": "sendonly",<br/>"simulcast":"true"
    note over C1, S: クライアント1 WebRTC 確立
    C2->>S: "type": "connect",<br/>"role": "recvonly",<br/>"simulcast":"true",<br/>"simulcast_rid": "r1"
    note over C2, S: クライアント2 WebRTC 確立
    par
        C1-)S: 映像<br>rid: r0
    and
        C1-)S: 映像<br>rid: r1
        S-)C2: 映像<br>rid: r1
    and
        C1-)S: 映像<br>rid: r2
    end
```
