シグナリング

概要

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

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

シグナリングの手順

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

  1. SoraMediaChannel.Listener を準備する
  2. SoraMediaOption を準備する
    • 映像と音声の送受信設定を行います。
    • 映像の送信にはキャプチャラー、映像の送受信には EGL コンテキストが必要です。生成方法について video をご確認ください。
  3. SoraMediaChannel を生成し接続する

使用例

// (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 の主な設定内容を次に示します。

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

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

  • enableVideoUpstream()
    • 映像を送信します。引数にキャプチャラーと EGL コンテキストを与えます。
  • enableVideoDownstream()
    • 映像を受信します。引数に EGL コンテキストを与えます。
  • enableAudioUpstream()
    • 音声を送信します。
  • enableAudioDownstream()
    • 音声を受信します。
  • videoCodec
    • 映像コーデックを指定します。デフォルトは VP9 です。
  • videoBitrate
    • 映像ビットレートを指定します。
  • videoVp9Params
  • videoAv1Params
  • videoH264Params
  • videoH265Params
  • audioCodec
    • 音声コーデックを指定します。デフォルトは OPUS です。
  • audioBitrate
    • 音声ビットレートを指定します。
  • enableMultistream()
  • enableSimulcast()
  • enableSpotlight()
  • proxy
  • forwardingFilterOption
  • hardwareVideoEncoderResolutionAdjustment
    • 解像度の縦横のピクセル数が指定した値の整数倍となるように調整します。デフォルトは 16 です。
  • degradationPreference
    • ネットワーク状況の逼迫等により、設定した解像度やフレームレートを維持できなくなった場合にどのように質を下げるか制御します。デフォルトは MAINTAIN_FRAMERATE です。詳細は ネットワーク逼迫時の映像品質制御を設定する をご確認ください。

SoraMediaChannel の主な設定内容

SoraMediaChannel の主な設定内容を次に示します。

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

  • context
    • android.content.Context を設定してください
  • signalingEndpoint
    • シグナリング URL を指定します。複数指定する場合は signalingEndpointCandidates を使用してください。
  • signalingEndpointCandidates
  • channelId
    • 接続するチャネル ID を指定します。
  • mediaOption
  • listener

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

  • dataChannelSignaling
  • dataChannels
  • signalingMetadata
  • signalingNotifyMetadata
  • clientId
    • 接続時に任意に指定可能な ID を指定します。
  • bundleId
    • マルチストリーム利用時、同一の bundle_id が設定されている別の接続からの音声や映像、リアルタイムメッセージングを受信しなくなります。
  • insecure
  • caCertificate
    • WebSocket と TURN-TLS のサーバー証明書検証に使用する CA 証明書を指定します。詳細は CA 証明書を指定する をご確認ください。
  • clientCertificate
    • WebSocket と TURN-TLS の mTLS で使用するクライアント証明書、またはクライアント証明書チェーンを指定します。単一証明書の場合は要素数 1 のリストを指定します。詳細は クライアント証明書と秘密鍵を指定する をご確認ください。
  • clientPrivateKey

role の自動設定について

映像または音声の送受信の設定により Sora 接続時のロールが自動的に判定されます。 ロールについては Sora ドキュメントの ロール の項も参考にしてください。

  • 送信と受信両方の設定が行われている
    • SoraChannelRole.SENDRECV
  • 送信のみの設定が行われている
    • SoraChannelRole.SENDONLY
  • 受信のみの設定が行われている
    • SoraChannelRole.RECVONLY
// 以下は映像と音声の送受信を設定しているので Sora 接続時のロールは SoraChannelRole.SENDRECV となります。
val option = SoraMediaOption().apply {

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

認証メタデータの送信

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

シグナリング接続時に送信する metadata については Sora ドキュメントの 認証メタデータ をご確認ください。

Sora Android SDK では metadata を、 SoraMediaChannelsignalingMetadata に値を設定します。 JSON 形式に変換可能なデータを設定してください。設定したデータは Gson により変換されます。

// 接続して映像を送受信します
// 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" の項をご確認ください。

Sora Android SDK では offer メッセージの受信が、 SoraMediaChannel.ListeneronOfferMessage で通知されます。

認証サーバから払い出されたメタデータは OfferMessage の metadata に格納されます。

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

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

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

  }
}

シグナリングメッセージの取得

WebSocket または DataChannel を経由して送受信されるシグナリングメッセージを、 SoraMediaChannel.ListeneronSignalingMessage で取得できます。

onSignalingMessage は、送受信方向 (SoraSignalingDirection)、経路種別 (SoraSignalingTransportType)、およびシグナリングメッセージの JSON 文字列を通知します。

onSignalingMessage で取得できる type は次のとおりです。

  • WebSocket で受信: offer, re-offer, switched, redirect
  • WebSocket で送信: connect, answer, candidate, re-answer, disconnect
  • DataChannel (label = signaling) で受信: re-offer, close
  • DataChannel (label = signaling) で送信: re-answer, disconnect

以下は offer から connection_id を取得する例と、 offer 以外を処理する例です。

import android.util.Log
import jp.shiguredo.sora.sdk.channel.SoraMediaChannel
import jp.shiguredo.sora.sdk.channel.SoraSignalingDirection
import jp.shiguredo.sora.sdk.channel.SoraSignalingTransportType
import org.json.JSONObject

private const val TAG = "Sora"

private val channelListener =
    object : SoraMediaChannel.Listener {
        override fun onSignalingMessage(
            mediaChannel: SoraMediaChannel,
            direction: SoraSignalingDirection,
            transportType: SoraSignalingTransportType,
            rawMessage: String,
        ) {
            val json = JSONObject(rawMessage)
            when (json.optString("type")) {
                // offer 受信時は connection_id を取り出す例
                "offer" -> {
                    val connectionId = json.optString("connection_id")
                    Log.d(
                        TAG,
                        "offer: direction=$direction " +
                            "transportType=$transportType " +
                            "connectionId=$connectionId",
                    )
                }
                // offer 以外も同様に処理できます
                "re-offer", "switched", "redirect", "close" -> {
                    Log.d(
                        TAG,
                        "type=${json.optString("type")}: direction=$direction " +
                            "transportType=$transportType",
                    )
                }
            }
        }
    }

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

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

シグナリング接続時に送信する signaling_notify_metadata については Sora ドキュメントの シグナリング通知メタデータ をご確認ください。

Sora Android SDK では signaling_notify_metadata を、 SoraMediaChannelsignalingNotifyMetadata に値を設定します。 JSON 形式に変換可能なデータを設定してください。設定したデータは Gson により変換されます。

// 接続して映像を送受信します
// 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 のドキュメント シグナリング通知メタデータ をご確認ください。

Sora Android SDK ではシグナリング通知メタデータが、 SoraMediaChannel.ListeneronNotificationMessage で通知されます。

シグナリング通知メタデータは NotificationMessage 内に格納されます。項目の詳細については Sora のドキュメントをご確認ください。

// 接続通知を受信するための 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 をご確認ください。

Sora Android SDK では ブッシュ通知の受信が、 SoraMediaChannel.ListenerPushMessage で通知されます。

// 接続通知を受信するための 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 を経由したシグナリング通信を行う場合は SoraMediaChanneldataChannelSignalingtrue を設定します。 デフォルトの設定はいずれも無効です。

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

// 接続して映像を送受信します
// 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()

TLS 証明書検証

SoraMediaChannelinsecurecaCertificateclientCertificateclientPrivateKey は、 WebSocket シグナリング (WSS) と TURN-TLS の両方に適用されます。

insecurecaCertificate はサーバー証明書検証に使用します。 clientCertificateclientPrivateKey は mTLS のクライアント認証に使用します。

WebSocket シグナリング (WSS) は Sora とのシグナリング接続に、TURN-TLS は ICE の TURN サーバーへの TLS 接続に利用されます。

WebSocket シグナリング (WSS) のサーバー証明書検証

WebSocket シグナリング (WSS) 利用時のサーバー証明書検証では、 Android OS のトラストストアを利用します。

caCertificateinsecure を指定しない場合は、 Android OS のトラストストアに含まれるルート証明書でサーバー証明書チェーンを検証します。 caCertificate を指定した場合は、トラストストアを利用せず、指定した CA 証明書のみで検証します。 insecure = true を指定した場合は、サーバー証明書検証をスキップします。

caCertificate の詳細は CA 証明書を指定する を、検証のスキップは TLS 証明書検証をスキップする をご確認ください。

TURN-TLS のサーバー証明書検証

TURN-TLS 利用時のサーバー証明書検証でも、 Android OS のトラストストアを利用します。

caCertificateinsecure を指定しない場合は、 Android OS のトラストストアに含まれるルート証明書でサーバー証明書チェーンを検証します。 caCertificate を指定した場合は、トラストストアを利用せず、指定した CA 証明書のみで検証します。 insecure = true を指定した場合は、サーバー証明書検証をスキップします。

TLS 証明書検証をスキップする

開発環境などで、 WebSocket や TURN-TLS のサーバー証明書検証をスキップしたい場合は、 SoraMediaChannelinsecuretrue を指定してください。

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

mediaChannel.connect()

CA 証明書を指定する

プライベート CA が発行したサーバー証明書を使用している環境など、 Android OS のトラストストアに登録されていない CA 証明書を使って接続したい場合は、 SoraMediaChannelcaCertificate に CA 証明書の PEM 文字列を指定してください。 caCertificate には証明書を 1 個だけ含む PEM を指定します。 複数証明書を連結した PEM を指定した場合は IllegalArgumentException が送出されます。

WebSocket シグナリング (WSS) と TURN-TLS の両方の接続に適用されます。

val caCertificate = """
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
""".trimIndent()

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

mediaChannel.connect()

クライアント証明書と秘密鍵を指定する

クライアント証明書を指定したい場合は、 SoraMediaChannelclientCertificate にクライアント証明書の PEM 文字列を指定してください。 単一証明書の場合は 1 個の CERTIFICATE ブロックを含む PEM を、 証明書チェーンの場合は複数の CERTIFICATE ブロックを連結した PEM を指定します。 また、証明書に対応する秘密鍵を clientPrivateKey に PKCS#8 PEM 文字列で指定してください。

val clientCertificate = """
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
""".trimIndent()

val clientPrivateKey = """
-----BEGIN PRIVATE KEY-----
...
-----END PRIVATE KEY-----
""".trimIndent()

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

mediaChannel.connect()

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

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

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

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

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 ドキュメントの転送フィルター機能 をご確認ください。

この機能は SoraChannelRoleSENDRECV または RECVONLY の場合にのみ利用できます。

フィルターを設定する

サーバー接続時に SoraMediaChannelforwardingFiltersOption を設定します。

設定例

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

  • connection_id が "S8YEN0TSE13JDC2991NG4XZ150" の場合は音声と映像の受信をブロックする、または client_id が "screen-share" の場合は音声の受信をブロックする
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 Android SDK では上記の SoraMediaOptionvideoVp9Params, videoAv1Params, videoH264Params, videoH265Params に値を設定します。 JSON 形式に変換可能なデータを設定してください。設定したデータは Gson により変換されます。

val mediaOption = SoraMediaOption().apply {

  // 映像コーデックパラメーターを指定する場合は必ず映像コーデックを指定する必要があります
  // 映像コーデックの設定は VP9 / AV1 / H.264 / H.265 のいずれか 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"
  // }

  // H.265 の映像コーデックパラメーターを指定する
  // videoCodec = SoraVideoOption.Codec.H265
  // videoH265Params = object {
  //   var level_id: Int = 93
  //   var profile_id: Int = 1
  //   var tier_flag: Int = 0
  //   var tx_mode: String = "SRST"
  // }
}

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

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

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

  • MAINTAIN_RESOLUTION: 解像度を維持し、フレームレートを下げる
  • MAINTAIN_FRAMERATE: フレームレートを維持し、解像度を下げる(デフォルト)
  • BALANCED: フレームレートと解像度のバランスを取る
  • DISABLED: 品質調整機能を無効化
fun connect() {
    ...
    val mediaOption = SoraMediaOption().apply {
       ...
       // 解像度維持優先を設定する例
       degradationPreference = SoraVideoOption.DegradationPreference.MAINTAIN_RESOLUTION
       ...
    }
 }

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

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

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

// 接続して映像を送受信する
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 のドキュメント をご確認ください。