# シグナリング

## 概要

WebRTC SFU Sora に SDK を利用して接続する仕組みを **シグナリング** と呼びます。

WebRTC SFU Sora のシグナリングの仕様については以下をご確認ください。

- [Sora ドキュメント WebSocket 経由のシグナリング](https://sora-doc.shiguredo.jp/SIGNALING)
- [DataChannel 経由のシグナリング](https://sora-doc.shiguredo.jp/DATA_CHANNEL_SIGNALING)

## シグナリングの手順

Sora へのシグナリングは次の手順で行います。

1. [SoraMediaChannel.Listener](https://shiguredo.github.io/sora-android-sdk/sora-android-sdk/jp.shiguredo.sora.sdk.channel/-sora-media-channel/-listener/) を準備する
2. [SoraMediaOption](https://shiguredo.github.io/sora-android-sdk/sora-android-sdk/jp.shiguredo.sora.sdk.channel.option/-sora-media-option/) を準備する- 映像と音声の送受信設定を行います。
   - 映像の送信にはキャプチャラー、映像の送受信には EGL コンテキストが必要です。生成方法について [video](video.html) を参照してください。
3. [SoraMediaChannel](https://shiguredo.github.io/sora-android-sdk/sora-android-sdk/jp.shiguredo.sora.sdk.channel/-sora-media-channel/) を生成し接続する


### 使用例

```kotlin
// (1) SoraMediaChannel.Listener の準備
private val channelListener = object : SoraMediaChannel.Listener {

  override fun onConnect(mediaChannel: SoraMediaChannel) {
  }
  // その他の Listener インタフェースの実装
}

fun start() {

  // (2) SoraMediaOption の準備
  val option = SoraMediaOption().apply {
    // キャプチャラー(capturer)と EGL コンテキスト(eglBaseContext)は別途作成済みの想定です。
    enableAudioDownstream()
    enableVideoDownstream(egl!!.eglBaseContext)

    enableAudioUpstream()
    enableVideoUpstream(capturer!!, egl!!.eglBaseContext)
  }

  // (3) SoraMediaChannel の作成、接続
  mediaChannel = SoraMediaChannel(
    context           = this,
    signalingEndpoint = "wss://sora.example.com/signaling",
    channelId         = "sora",
    mediaOption       = option,
    listener          = channelListener)

  mediaChannel.connect()
}

fun stop() {
  mediaChannel.disconnect()
}
```

## シグナリング時の主な設定内容

### SoraMediaOption の主な設定内容

[SoraMediaOption](https://shiguredo.github.io/sora-android-sdk/sora-android-sdk/jp.shiguredo.sora.sdk.channel.option/-sora-media-option/) の主な設定内容を次に示します。

[DataChannel を利用したリアルタイムメッセージングのみで接続する](messaging.html#f9c041) ケースを除き映像、音声の送受信設定が必要です。

デフォルトの設定はいずれも無効です。

- `enableVideoUpstream()`- 映像を送信します。引数にキャプチャラーと EGL コンテキストを与えます。
- `enableVideoDownstream()`- 映像を受信します。引数に EGL コンテキストを与えます。
- `enableAudioUpstream()`- 音声を送信します。
- `enableAudioDownstream()`- 音声を受信します。
- `videoCodec`- 映像コーデックを指定します。デフォルトは VP9 です。
- `videoBitrate`- 映像ビットレートを指定します。
- `videoVp9Params`- 映像の VP9 設定を指定します。詳細は [映像コーデックパラメーターの設定](signaling.html#bb41fd) を参照してください。
- `videoAv1Params`- 映像の AV1 設定を指定します。詳細は [映像コーデックパラメーターの設定](signaling.html#bb41fd) を参照してください。
- `videoH264Params`- 映像の H264 設定を指定します。詳細は [映像コーデックパラメーターの設定](signaling.html#bb41fd) を参照してください。
- `audioCodec`- 音声コーデックを指定します。デフォルトは OPUS です。
- `audioBitrate`- 音声ビットレートを指定します。
- `enableMultistream()`- [マルチストリーム機能](multistream.html) を有効にします。
- `enableSimulcast()`- [サイマルキャスト機能](simulcast.html) を有効にします。
- `enableSpotlight()`- [スポットライト機能](spotlight.html) を有効にします。
- `proxy`- [HTTP プロキシ・サーバーの設定](signaling.html#a240d1) を行います。
- `forwardingFilterOption`- [転送フィルター機能](signaling.html#edd221) の設定を行います。
- `hardwareVideoEncoderResolutionAdjustment`- 解像度の縦横のピクセル数が指定した値の整数倍となるように調整します。デフォルトは 16 です。
- `degradationPreference`- ネットワーク状況の逼迫等により、設定した解像度やフレームレートを維持できなくなった場合にどのように質を下げるか制御します。デフォルトは `MAINTAIN_FRAMERATE` です。詳細は [ネットワーク逼迫時の映像品質制御を設定する](signaling.html#2b481c) を参照してください。

### SoraMediaChannel の主な設定内容

[SoraMediaChannel](https://shiguredo.github.io/sora-android-sdk/sora-android-sdk/jp.shiguredo.sora.sdk.channel/-sora-media-channel/) の主な設定内容を次に示します。

以下の項目は必須で設定する項目です。signalingEndpoint, signalingEndpointCandidates についてはいずれかを設定します。listener は必須ではありませんが、通常アプリケーションでは SDK のイベントを受け取る必要があるため必須としています。

- `context`- `android.content.Context` を設定してください
- `signalingEndpoint`- シグナリング URL を指定します。複数指定する場合は `signalingEndpointCandidates` を使用してください。
- `signalingEndpointCandidates`- シグナリング URL を複数指定します。詳細は [複数のシグナリング URL を指定する](signaling.html#648d56) を参照してください。
- `channelId`- 接続するチャネル ID を指定します。
- `mediaOption`- [SoraMediaOption](https://shiguredo.github.io/sora-android-sdk/sora-android-sdk/jp.shiguredo.sora.sdk.channel.option/-sora-media-option/)
- `listener`- イベント通知をうけるための [SoraMediaChannel.Listener](https://shiguredo.github.io/sora-android-sdk/sora-android-sdk/jp.shiguredo.sora.sdk.channel/-sora-media-channel/-listener/) を設定してください。

オプションで以下を設定することが可能です。利用する機能に応じて設定を行います。

- `dataChannelSignaling`- [DataChannel 経由のシグナリング](signaling.html#bb7699) を有効にします
- `dataChannels`- [リアルタイムメッセージング機能](messaging.html) 利用時に使用します
- `signalingMetadata`- 認証時に Sora に送信する任意のメタデータを指定します。詳細は [認証メタデータの送信](signaling.html#f9c2f8) を参照してください。
- `signalingNotifyMetadata`- シグナリング通知時に連携される任意のメタデータを指定します。詳細は [シグナリング通知メタデータの送信](signaling.html#32359b) を参照してください。
- `clientId`- 接続時に任意に指定可能な ID を指定します。
- `bundleId`- マルチストリーム利用時、同一の bundle_id が設定されている別の接続からの音声や映像、リアルタイムメッセージングを受信しなくなります。

## role の自動設定について

映像または音声の送受信の設定により Sora 接続時のロールが自動的に判定されます。
ロールについては Sora ドキュメントの  [ロール](https://sora-doc.shiguredo.jp/SIGNALING#6d21b9) の項も参考にしてください。

- 送信と受信両方の設定が行われている- `SoraChannelRole.SENDRECV`
- 送信のみの設定が行われている- `SoraChannelRole.SENDONLY`
- 受信のみの設定が行われている- `SoraChannelRole.RECVONLY`

```kotlin
// 以下は映像と音声の送受信を設定しているので Sora 接続時のロールは SoraChannelRole.SENDRECV となります。
val option = SoraMediaOption().apply {

  enableVideoUpstream(capturer, egl.eglBaseContext)
  enableAudioUpstream()
  enableVideoDownstream()
  enableAudioDownstream()
}
```


## 認証メタデータの送信

Sora との接続時に認証に利用するメタデータを送信することができます。

シグナリング接続時に送信する `metadata` については Sora ドキュメントの [認証メタデータ](https://sora-doc.shiguredo.jp/SIGNALING#f914ad) をご確認ください。

Sora Android SDK では `metadata` を、 [SoraMediaChannel](https://shiguredo.github.io/sora-android-sdk/sora-android-sdk/jp.shiguredo.sora.sdk.channel/-sora-media-channel/) の `signalingMetadata` に値を設定します。
JSON 形式に変換可能なデータを設定してください。設定したデータは [Gson](https://github.com/google/gson) により変換されます。

```kotlin
// 接続して映像を送受信します
// signalingMetadata, signalingNotifyMetadata には JSON 形式に変換可能なデータを設定します
val mediaChannel = SoraMediaChannel(
              context                     = this,
              signalingEndpoint           = "wss://example.com/signaling",
              channelId                   = "sora",
              signalingMetadata           = mapOf("spam" to "egg"),
              mediaOption                 = option,
              listener                    = channelListener)

mediaChannel.connect()
```


## 認証サーバーから払い出されたメタデータの取得

認証サーバから払い出されたメタデータを取得することができます。

認証サーバーから払い出された `metadata` については Sora ドキュメントの ["type": "offer"](https://sora-doc.shiguredo.jp/SIGNALING#d20d6f) の項を参照してください。

Sora Android SDK では offer メッセージの受信が、 [SoraMediaChannel.Listener](https://shiguredo.github.io/sora-android-sdk/sora-android-sdk/jp.shiguredo.sora.sdk.channel/-sora-media-channel/-listener/) の `onOfferMessage` で通知されます。

認証サーバから払い出されたメタデータは [OfferMessage](https://shiguredo.github.io/sora-android-sdk/sora-android-sdk/jp.shiguredo.sora.sdk.channel.signaling.message/-offer-message/) の metadata に格納されます。

```kotlin
// 接続通知を受信するための Listener を宣言します。
private val channelListener = object : SoraMediaChannel.Listener {

  // Sora から type: offer メッセージを受信した際に呼び出されるコールバック
  override fun onOfferMessage(mediaChannel: SoraMediaChannel, offer: OfferMessage) {

    // 認証サーバーから返却された metadata は OfferMessage.metadata に設定されます
    val offerMetadata = offer.metadata

  }
}
```


## シグナリング通知メタデータの送信

Sora との接続時に認証に利用するメタデータを送信することができます。

シグナリング接続時に送信する `signaling_notify_metadata` については Sora ドキュメントの [シグナリング通知メタデータ](https://sora-doc.shiguredo.jp/SIGNALING_NOTIFY_METADATA) をご確認ください。

Sora Android SDK では `signaling_notify_metadata` を、 [SoraMediaChannel](https://shiguredo.github.io/sora-android-sdk/sora-android-sdk/jp.shiguredo.sora.sdk.channel/-sora-media-channel/) の `signalingNotifyMetadata` に値を設定します。
JSON 形式に変換可能なデータを設定してください。設定したデータは [Gson](https://github.com/google/gson) により変換されます。

```kotlin
// 接続して映像を送受信します
// signalingMetadata, signalingNotifyMetadata には JSON 形式に変換可能なデータを設定します
val mediaChannel = SoraMediaChannel(
              context                 = this,
              signalingEndpoint       = "wss://example.com/signaling",
              channelId               = "sora",
              signalingNotifyMetadata = mapOf("spam" to "egg"),
              mediaOption             = option,
              listener                = channelListener)

mediaChannel.connect()
```


## シグナリング通知メタデータの取得

シグナリング通知時に連携されるメタデータについては、
Sora のドキュメント [シグナリング通知メタデータ](https://sora-doc.shiguredo.jp/SIGNALING_NOTIFY_METADATA) をご確認ください。

Sora Android SDK ではシグナリング通知メタデータが、 [SoraMediaChannel.Listener](https://shiguredo.github.io/sora-android-sdk/sora-android-sdk/jp.shiguredo.sora.sdk.channel/-sora-media-channel/-listener/) の  `onNotificationMessage` で通知されます。

シグナリング通知メタデータは [NotificationMessage](https://shiguredo.github.io/sora-android-sdk/sora-android-sdk/jp.shiguredo.sora.sdk.channel.signaling.message/-notification-message/) 内に格納されます。項目の詳細については Sora のドキュメントを参照してください。

```kotlin
// 接続通知を受信するための Listener を宣言します。
private val channelListener = object : SoraMediaChannel.Listener {

  // Sora のシグナリング通知機能の通知を受信したときに呼び出されるコールバック
  override fun onNotificationMessage(mediaChannel: SoraMediaChannel, notification: NotificationMessage) {

    when (notification.eventType) {
      // シグナリング通知メタデータは NotificationMessage に格納されます
      // 項目の詳細については Sora のドキュメントを参照してください
      "connection.created", "connection.destroyed", "connection.updated" -> {

        var signalingNotifyConnectionId  = notification.clientId
        var signalingNotifyAuthnMetadata = notification.authnMetadata
        var signalingNotifyAuthzMetadata = notification.authzMetadata
        var signalingNotifyDataList      = notification.data
      }
    }
  }
}
```


## プッシュ通知の取得

プッシュ通知については Sora のドキュメントの [プッシュ通知 API](https://sora-doc.shiguredo.jp/API_PUSH) をご確認ください。

Sora Android SDK では ブッシュ通知の受信が、 [SoraMediaChannel.Listener](https://shiguredo.github.io/sora-android-sdk/sora-android-sdk/jp.shiguredo.sora.sdk.channel/-sora-media-channel/-listener/) の [PushMessage](https://shiguredo.github.io/sora-android-sdk/sora-android-sdk/jp.shiguredo.sora.sdk.channel.signaling.message/-push-message/) で通知されます。

```kotlin
// 接続通知を受信するための Listener を宣言します。
private val channelListener = object : SoraMediaChannel.Listener {

  // Sora のプッシュ通知機能の通知を受信したときに呼び出されるコールバック
  override fun onPushMessage(mediaChannel: SoraMediaChannel, push: PushMessage) {

    // プッシュされたメッセージは PushMessage.data に格納されます
    val data = push.data
    if (data is Map<*, *>) {
        data.forEach { (key, value) ->
            Log.d(TAG, "received push data: $key=$value")
        }
    }
  }
}
```


## DataChannel 経由のシグナリング

Sora Android SDK で Sora と DataChannel を経由したシグナリング通信を行う場合は [SoraMediaChannel](https://shiguredo.github.io/sora-android-sdk/sora-android-sdk/jp.shiguredo.sora.sdk.channel/-sora-media-channel/) の `dataChannelSignaling` に  `true` を設定します。
デフォルトの設定はいずれも無効です。

`ignoreDisconnectWebsocket` プロパティに `true` をセットした場合、 DataChannel への切り替え完了後に Android SDK は WebSocket を切断します。

```kotlin
// 接続して映像を送受信します
// DataChannel 経由のシグナリングを有効にしています
// ignoreDisconnectWebSocket = true にすると DataChannel への切り替え完了後に WebSocket は切断されます
val mediaChannel = SoraMediaChannel(
              context                     = this,
              signalingEndpoint           = "wss://example.com/signaling",
              channelId                   = "sora",
              dataChannelSignaling        = true,
              ignoreDisconnectWebSocket   = true,
              mediaOption                 = option,
              listener                    = channelListener)
              
mediaChannel.connect()
```


## HTTP プロキシ・サーバーの設定

Sora Android SDK は HTTP プロキシ・サーバーを経由した通信を行うことができます。

- HTTP プロキシに対応しています- SOCKS プロキシも設定できますが、動作を確認していません
- HTTP の CONNECT メソッドは HTTPS ではなく HTTP で送信します
- `Proxy-Authorization` ヘッダーを利用した Basic 認証に対応しています
- Android OS の Wi-Fi に設定されたプロキシの項目を参照しません

プロキシ・サーバーの情報は `SoraMediaOption.proxy` に設定します。

```kotlin
val mediaOption = SoraMediaOption().apply {

  proxy.type = ProxyType.HTTPS

  // ホスト名、または IP アドレスを指定します
  proxy.hostname = "proxy.example.com"
  proxy.port = 3128

  // プロキシに認証が設定されている場合は username と password を指定します
  proxy.username = "ham"
  proxy.password = "egg"

  // エージェントの設定は任意です
  // 未設定の場合は、シグナリング・メッセージに含まれる `sora_client` の値が使用されます
  proxy.agent = "spam"

}
```


## 転送フィルター機能

転送フィルター機能は、Sora 側でクライアントへ転送する音声や映像のパケットをフィルターする機能です。
詳しくは [Sora ドキュメントの転送フィルター機能](https://develop.shiguredo-sora-doc.pages.dev/FORWARDING_FILTER) を参照してください。

この機能は `SoraChannelRole` が `SENDRECV` または `RECVONLY` の場合にのみ利用できます。

### フィルターを設定する

サーバー接続時に `SoraMediaChannel` に `forwardingFiltersOption` を設定します。

### 設定例

以下の例は、自分の接続に対して、以下の条件で映像または音声の受信をブロックします。

- connection_id が "S8YEN0TSE13JDC2991NG4XZ150" の場合は音声と映像の受信をブロックする、または client_id が "screen-share" の場合は音声の受信をブロックする

```kotlin
val forwardingFilters =
    listOf(
        SoraForwardingFilterOption(
            name = "filter1",
            priority = 1,
            action = SoraForwardingFilterOption.Action.BLOCK,
            rules =
                listOf(
                    listOf(
                        SoraForwardingFilterOption.Rule(
                            SoraForwardingFilterOption.Rule.Field.CONNECTION_ID,
                            SoraForwardingFilterOption.Rule.Operator.IS_IN,
                            listOf("S8YEN0TSE13JDC2991NG4XZ150")
                        )
                    ),
                    listOf(
                        SoraForwardingFilterOption.Rule(
                            SoraForwardingFilterOption.Rule.Field.CLIENT_ID,
                            SoraForwardingFilterOption.Rule.Operator.IS_IN,
                            listOf("screen-share")
                        ),
                        SoraForwardingFilterOption.Rule(
                            SoraForwardingFilterOption.Rule.Field.KIND,
                            SoraForwardingFilterOption.Rule.Operator.IS_IN,
                            listOf("audio")
                        )
                    )
                )
        )
    )


val mediaChannel =
    SoraMediaChannel(
        context = this,
        signalingEndpoint = "wss://sora.example.com/signaling",
        channelId = "sora",
        mediaOption = option,
        listener = channelListener,
        forwardingFiltersOption = forwardingFilters
    )
```


## 映像コーデックパラメーターの設定

送信する映像コーデックの設定と合わせて、映像コーデックのパラメータを指定可能です。
この機能は映像を送信する際に利用できます。

指定できる値の内容については以下の Sora ドキュメントを参照してください。

> - [Sora ドキュメント - ビデオの VP9 設定指定](https://sora-doc.shiguredo.jp/SIGNALING#b7556a)
> - [Sora ドキュメント - ビデオの AV1 設定指定](https://sora-doc.shiguredo.jp/SIGNALING#48f92d)
> - [Sora ドキュメント - ビデオの H.264 設定指定](https://sora-doc.shiguredo.jp/SIGNALING#ffc4cb)

Sora Android SDK では上記の [SoraMediaOption](https://shiguredo.github.io/sora-android-sdk/sora-android-sdk/jp.shiguredo.sora.sdk.channel.option/-sora-media-option/) の `videoVp9Params`, `videoAv1Params`, `videoH264Params` に値を設定します。
JSON 形式に変換可能なデータを設定してください。設定したデータは Gson により変換されます。

```kotlin
val mediaOption = SoraMediaOption().apply {

  // 映像コーデックパラメーターを指定する場合は必ず映像コーデックを指定する必要があります
  // 映像コーデックの設定は VP9 / AV1 / H.264 のいずれか 1 種類のみです
  // 利用しない映像コーデックパラメーターは設定しないでください

  // VP9 の映像コーデックパラメーターを指定する
  // videoCodec = SoraVideoOption.Codec.VP9
  // videoVp9Params = object {
  //   var profile_id: Int = 0
  // }

  // AV1 の映像コーデックパラメーターを指定する
  // videoCodec = SoraVideoOption.Codec.AV1
  // videoAv1Params = object {
  //   var profile: Int = 0
  // }

  // H.264 の映像コーデックパラメーターを指定する
  // videoCodec = SoraVideoOption.Codec.H264
  // videoH264Params = object {
  //   var profile_level_id: String = "42e034"
  // }
}
```


## ネットワーク逼迫時の映像品質制御を設定する

ネットワーク状況の逼迫等により設定した解像度やフレームレートを維持できなくなった場合の挙動を制御するためのパラメータを設定可能です。
この機能は映像を送信する際に利用できます。

指定できる値は以下のようになります。指定は任意であり指定しない場合はデフォルトの `MAINTAIN_FRAMERATE` で動作します。

- `MAINTAIN_RESOLUTION`: 解像度を維持し、フレームレートを下げる
- `MAINTAIN_FRAMERATE`: フレームレートを維持し、解像度を下げる(デフォルト)
- `BALANCED`: フレームレートと解像度のバランスを取る
- `DISABLED`: 品質調整機能を無効化

```kotlin
fun connect() {
    ...
    val mediaOption = SoraMediaOption().apply {
       ...
       // 解像度維持優先を設定する例
       degradationPreference = SoraVideoOption.DegradationPreference.MAINTAIN_RESOLUTION
       ...
    }
 }
```


## 複数のシグナリング URL を指定する

Sora Android SDK では、 `signalingEndpointCandidates` に複数のシグナリング URL を指定することができます。

Sora Android SDK は全ての URL に対して接続を試行し、最初に成功した接続をシグナリングに使用します。
このため、１台でも正常なサーバーが残っていれば Sora への接続が成功します。
クラスター機能は複数の URL を指定しなくても利用できますが、冗長性を高めるために複数 URL の指定を推奨します。

```kotlin
// 接続して映像を送受信する
val mediaChannel = SoraMediaChannel(
              context           = this,
              // signalingEndpointCandidates に複数の URL を指定する
              signalingEndpointCandidates = [
                  "wss://sora1.example.com/signaling",
                  "wss://sora2.example.com/signaling",
                  "wss://sora3.example.com/signaling",
              ],
              channelId         = "sora",
              mediaOption       = option,
              listener          = channelListener)
mediaChannel.connect()
```

- 実際にシグナリングに利用されている URL は `SoraMediaChannel` クラスの `connectedSignalingEndpoint` プロパティから確認できます。


## クラスター機能利用時のシグナリングのリダイレクト

クラスター機能を有効にすると、 Sora から、 type: offer の代わりに type: redirect が送られてくることがあります。
この場合、Sora Android SDK は Sora の接続を一度切断し、 type: redirect で指定された location に再接続します。

クラスター機能の詳細な仕組みについては [Sora のドキュメント](https://sora-doc.shiguredo.jp/CLUSTER) を参照してください。







