From 6f469798caf93790455748ae1e88576f3ad0b68c Mon Sep 17 00:00:00 2001 From: miosakuma Date: Fri, 15 Oct 2021 11:22:45 +0900 Subject: [PATCH 01/85] =?UTF-8?q?dependencyUpdates=20=E3=81=AE=E6=A4=9C?= =?UTF-8?q?=E7=B4=A2=E9=99=A4=E5=A4=96=E6=9D=A1=E4=BB=B6=E3=81=AB=E2=80=99?= =?UTF-8?q?M'=E3=82=92=E8=BF=BD=E5=8A=A0=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index c997a3a9..16b2fff2 100644 --- a/build.gradle +++ b/build.gradle @@ -40,7 +40,7 @@ task clean(type: Delete) { dependencyUpdates.resolutionStrategy = { componentSelection { rules -> rules.all { ComponentSelection selection -> - boolean rejected = ['alpha', 'beta', 'rc'].any { qualifier -> + boolean rejected = ['alpha', 'beta', 'rc', 'M'].any { qualifier -> selection.candidate.version ==~ /(?i).*[.-]${qualifier}[.\d-]*/ } if (rejected) { From 9b3cd1b200a0451ba9a7bd786034fa8e16a6f59d Mon Sep 17 00:00:00 2001 From: miosakuma Date: Fri, 15 Oct 2021 15:16:18 +0900 Subject: [PATCH 02/85] =?UTF-8?q?dokka=20=E3=82=92=201.5.31=20=E3=81=AB?= =?UTF-8?q?=E3=81=82=E3=81=92=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 +- sora-android-sdk/build.gradle | 23 ++++++++++++----------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/build.gradle b/build.gradle index 16b2fff2..b51a2243 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { ext.kotlin_version = '1.4.31' ext.libwebrtc_version = '93.4577.8.2' - ext.dokka_version = '0.10.1' + ext.dokka_version = '1.5.31' repositories { google() diff --git a/sora-android-sdk/build.gradle b/sora-android-sdk/build.gradle index 057a1709..bbaaca0d 100644 --- a/sora-android-sdk/build.gradle +++ b/sora-android-sdk/build.gradle @@ -46,22 +46,23 @@ android { } } -dokka { - outputFormat = 'javadoc' - outputDirectory = "$buildDir/dokka" +dokkaJavadoc.configure { + outputDirectory.set(new File("${buildDir}/dokka")) + moduleName.set("sora") + // "dafault" を指定すると $USER_HOME/.cache/dokka を使用するとあるが "${projectDir}/default" を見てしまうのでコメントアウトしている + //cacheRoot.set(file("default")) sourceSets { configureEach { - moduleName = 'sora' - reportUndocumented true - includes = ['packages.md'] - cacheRoot = "default" - disableAutoconfiguration = false + reportUndocumented.set(true) + includes.from("packages.md") + //1.4 で削除されたため移行不可。Kotlin Model が利用されるが上書き可能. + //disableAutoconfiguration = false sourceLink { - path = "sora-android-sdk/src/main/kotlin" - url = "https://github.com/shiguredo/sora-android-sdk/tree/master/sora-android-sdk/src/main/kotlin" - lineSuffix = "#L" + localDirectory.set(file("sora-android-sdk/src/main/kotlin")) + remoteUrl.set(uri("https://github.com/shiguredo/sora-android-sdk/tree/master/sora-android-sdk/src/main/kotlin").toURL()) + remoteLineSuffix.set("#L") } } } From 0fd75940c159ffc3084958542c0511ba6ac18fc4 Mon Sep 17 00:00:00 2001 From: miosakuma Date: Tue, 19 Oct 2021 18:27:12 +0900 Subject: [PATCH 03/85] =?UTF-8?q?dokka=20=E3=81=AE=20build.gradle=20?= =?UTF-8?q?=E8=A8=AD=E5=AE=9A=E3=82=92=E4=BF=AE=E6=AD=A3=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sora-android-sdk/build.gradle | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/sora-android-sdk/build.gradle b/sora-android-sdk/build.gradle index bbaaca0d..c3161150 100644 --- a/sora-android-sdk/build.gradle +++ b/sora-android-sdk/build.gradle @@ -47,20 +47,19 @@ android { } dokkaJavadoc.configure { - outputDirectory.set(new File("${buildDir}/dokka")) - moduleName.set("sora") - // "dafault" を指定すると $USER_HOME/.cache/dokka を使用するとあるが "${projectDir}/default" を見てしまうのでコメントアウトしている - //cacheRoot.set(file("default")) - - sourceSets { - configureEach { + // デフォルトの出力先は "${buildDir}/dokka". 変更したいときにコメントアウトを行う. + // outputDirectory.set(new File("${buildDir}/dokka")) + moduleName.set("Sora Android SDK") + // "dafault" を指定すると $USER_HOME/.cache/dokka を使用するとあるが実際には "${projectDir}/default" を見てしまうのでコメントアウトしている. + // cacheRoot.set(file("default")) + + dokkaSourceSets { + named("main") { reportUndocumented.set(true) - includes.from("packages.md") - //1.4 で削除されたため移行不可。Kotlin Model が利用されるが上書き可能. - //disableAutoconfiguration = false + includes.from(files("packages.md")) sourceLink { - localDirectory.set(file("sora-android-sdk/src/main/kotlin")) + localDirectory.set(file("src/main/kotlin")) remoteUrl.set(uri("https://github.com/shiguredo/sora-android-sdk/tree/master/sora-android-sdk/src/main/kotlin").toURL()) remoteLineSuffix.set("#L") } From f75265c1aab22cf6d64713990056e5eeaa820af3 Mon Sep 17 00:00:00 2001 From: miosakuma Date: Tue, 19 Oct 2021 18:28:54 +0900 Subject: [PATCH 04/85] =?UTF-8?q?dokka=201.5.31=20=E5=90=91=E3=81=91?= =?UTF-8?q?=E3=81=AB=E3=83=89=E3=82=AD=E3=83=A5=E3=83=A1=E3=83=B3=E3=83=86?= =?UTF-8?q?=E3=83=BC=E3=82=B7=E3=83=A7=E3=83=B3=E3=82=B3=E3=83=A1=E3=83=B3?= =?UTF-8?q?=E3=83=88=E3=82=92=E4=BF=AE=E6=AD=A3=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sora-android-sdk/packages.md | 4 +- .../main/kotlin/jp/shiguredo/sora/sdk/Sora.kt | 8 +- .../sora/sdk/camera/CameraCapturerFactory.kt | 14 ++-- .../sora/sdk/channel/SoraMediaChannel.kt | 75 ++++++++++--------- .../sdk/channel/data/ChannelAttendeesCount.kt | 14 ++-- .../channel/option/PeerConnectionOption.kt | 6 +- .../sdk/channel/option/SoraAudioOption.kt | 52 ++++++------- .../sdk/channel/option/SoraChannelRole.kt | 2 +- .../sdk/channel/option/SoraMediaOption.kt | 40 +++++----- .../sdk/channel/option/SoraSpotlightOption.kt | 6 +- .../sdk/channel/option/SoraVideoOption.kt | 10 +-- .../sora/sdk/error/SoraErrorReason.kt | 2 +- .../jp/shiguredo/sora/sdk/util/SoraLogger.kt | 2 +- 13 files changed, 117 insertions(+), 118 deletions(-) diff --git a/sora-android-sdk/packages.md b/sora-android-sdk/packages.md index dd4937f5..6a71cd0d 100644 --- a/sora-android-sdk/packages.md +++ b/sora-android-sdk/packages.md @@ -1,5 +1,5 @@ # Module Sora Android SDK -Sora Android SDK は [WebRTC SFU Sora](https://sora.shiguredo.jp) の Android クライアントアプリケーションを開発するためのライブラリです。 +Sora Android SDK は [WebRTC SFU Sora](https://sora.shiguredo.jp) の Android クライアントアプリケーションを開発するためのライブラリです. -使い方は [Sora Android SDK ドキュメント](https://sora-android-sdk.shiguredo.jp/) を参照してください。 +使い方は [Sora Android SDK ドキュメント](https://sora-android-sdk.shiguredo.jp/) を参照してください. diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/Sora.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/Sora.kt index cfdc9739..eb4f7379 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/Sora.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/Sora.kt @@ -3,11 +3,11 @@ package jp.shiguredo.sora.sdk object Sora { /** - * スポットライトレガシー機能の使用の可否を指定します。 + * スポットライトレガシー機能の使用の可否を指定します. * - * true をセットすると、シグナリングメッセージの内容がスポットライトレガシー向けに変更されます。 - * ただし、サーバー側でスポットライトレガシー機能が有効にされていなければシグナリングに失敗します。 - * このプロパティを使用する際は必ずサーバーの設定を確認してください。 + * true をセットすると、シグナリングメッセージの内容がスポットライトレガシー向けに変更されます. + * ただし、サーバー側でスポットライトレガシー機能が有効にされていなければシグナリングに失敗します. + * このプロパティを使用する際は必ずサーバーの設定を確認してください. */ @Deprecated("スポットライトレガシー機能は 2021 年 12 月に廃止が予定されています。") var usesSpotlightLegacy: Boolean = false diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/camera/CameraCapturerFactory.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/camera/CameraCapturerFactory.kt index d98f665b..57ef5f91 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/camera/CameraCapturerFactory.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/camera/CameraCapturerFactory.kt @@ -9,10 +9,10 @@ import org.webrtc.CameraEnumerator import org.webrtc.CameraVideoCapturer /** - * カメラからの映像を取得するための `CameraVideoCapturer` のファクトリクラスです。 + * カメラからの映像を取得するための `CameraVideoCapturer` のファクトリクラスです. * - * Camera1, Camera2 を統一的に扱うことが出来ます。 - * cf: + * Camera1, Camera2 を統一的に扱うことが出来ます. + * cf. * - `org.webrtc.CameraVideoCapturer` */ class CameraCapturerFactory { @@ -21,15 +21,15 @@ class CameraCapturerFactory { val TAG = CameraCapturerFactory::class.simpleName /** - * `CameraVideoCapturer` のインスタンスを生成します。 + * `CameraVideoCapturer` のインスタンスを生成します. * - * 複数のカメラがある場合はフロントのカメラを優先します。 + * 複数のカメラがある場合はフロントのカメラを優先します. * * @param context application context * @param fixedResolution true の場合は解像度維持を優先、false の場合は - * フレームレート維持を優先する。デフォルト値は false 。 + * フレームレート維持を優先する. デフォルト値は false. * @param frontFacingFirst true の場合はフロントカメラを優先、false の場合は - * リアカメラを優先して選択する。デフォルト値は true 。 + * リアカメラを優先して選択する. デフォルト値は true. * @return 生成された `CameraVideoCapturer` */ @JvmOverloads diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt index 084ffc8f..4a3244a5 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt @@ -27,14 +27,13 @@ import kotlin.concurrent.schedule * 管理、協調動作させるためのクラス * * Sora に接続するアプリケーションは、このクラスを利用することでシグナリングの - * 詳細が隠蔽され、単一の [Listener] でイベントを受けることが出来ます。 + * 詳細が隠蔽され、単一の [Listener] でイベントを受けることが出来ます. * - * @constructor - * SoraMediaChannel インスタンスを生成します。 + * シグナリングの手順とデータに関しては Sora のドキュメント + * [https://sora.shiguredo.jp/doc/SIGNALING.html](https://sora.shiguredo.jp/doc/SIGNALING.html)を参照ください. * - * cf. - * - シグナリングの手順とデータに関しては Sora のドキュメント - * [](https://sora.shiguredo.jp/doc/SIGNALING.html)を参照ください + * @constructor + * SoraMediaChannel インスタンスを生成します. * * @param context `android.content.Context` * @param signalingEndpoint シグナリングの URL @@ -81,7 +80,7 @@ class SoraMediaChannel @JvmOverloads constructor( */ interface Listener { /** - * ローカルストリームが追加されたときに呼び出されるコールバック + * ローカルストリームが追加されたときに呼び出されるコールバック. * * cf. * - `org.webrtc.MediaStream` @@ -93,7 +92,7 @@ class SoraMediaChannel @JvmOverloads constructor( fun onAddLocalStream(mediaChannel: SoraMediaChannel, ms: MediaStream) {} /** - * リモートストリームが追加されたときに呼び出されるコールバック + * リモートストリームが追加されたときに呼び出されるコールバック. * * cf. * - `org.webrtc.MediaStream` @@ -105,7 +104,7 @@ class SoraMediaChannel @JvmOverloads constructor( fun onAddRemoteStream(mediaChannel: SoraMediaChannel, ms: MediaStream) {} /** - * リモートストリームが削除されたときに呼び出されるコールバック + * リモートストリームが削除されたときに呼び出されるコールバック. * * cf. * - `org.webrtc.MediaStream.label()` @@ -116,73 +115,77 @@ class SoraMediaChannel @JvmOverloads constructor( fun onRemoveRemoteStream(mediaChannel: SoraMediaChannel, label: String) {} /** - * Sora との接続が確立されたときに呼び出されるコールバック + * Sora との接続が確立されたときに呼び出されるコールバック. + * + * cf. + * - [PeerChannel] * - * @see PeerChannel * @param mediaChannel イベントが発生したチャネル */ fun onConnect(mediaChannel: SoraMediaChannel) {} /** - * Sora との接続が切断されたときに呼び出されるコールバック + * Sora との接続が切断されたときに呼び出されるコールバック. + * + * cf. + * - [PeerChannel] * - * @see PeerChannel * @param mediaChannel イベントが発生したチャネル */ fun onClose(mediaChannel: SoraMediaChannel) {} /** - * Sora との通信やメディアでエラーが発生したときに呼び出されるコールバック + * Sora との通信やメディアでエラーが発生したときに呼び出されるコールバック. * * cf. * - `org.webrtc.PeerConnection` * - `org.webrtc.PeerConnection.Observer` + * - [PeerChannel] * - * @see PeerChannel * @param reason エラーの理由 */ fun onError(mediaChannel: SoraMediaChannel, reason: SoraErrorReason) {} /** - * Sora との通信やメディアでエラーが発生したときに呼び出されるコールバック + * Sora との通信やメディアでエラーが発生したときに呼び出されるコールバック. * * cf. * - `org.webrtc.PeerConnection` * - `org.webrtc.PeerConnection.Observer` + * - [PeerChannel] * - * @see PeerChannel * @param reason エラーの理由 * @param message エラーの情報 */ fun onError(mediaChannel: SoraMediaChannel, reason: SoraErrorReason, message: String) {} /** - * Sora との通信やメディアで警告が発生したときに呼び出されるコールバック + * Sora との通信やメディアで警告が発生したときに呼び出されるコールバック. * * cf. * - `org.webrtc.PeerConnection` * - `org.webrtc.PeerConnection.Observer` + * - [PeerChannel] * - * @see PeerChannel * @param reason 警告の理由 */ fun onWarning(mediaChannel: SoraMediaChannel, reason: SoraErrorReason) {} /** - * Sora との通信やメディアで警告が発生したときに呼び出されるコールバック + * Sora との通信やメディアで警告が発生したときに呼び出されるコールバック. * * cf. * - `org.webrtc.PeerConnection` * - `org.webrtc.PeerConnection.Observer` + * - [PeerChannel] * - * @see PeerChannel * @param reason 警告の理由 * @param message 警告の情報 */ fun onWarning(mediaChannel: SoraMediaChannel, reason: SoraErrorReason, message: String) {} /** - * 接続しているチャネルの参加者が増減したときに呼び出されるコールバック + * 接続しているチャネルの参加者が増減したときに呼び出されるコールバック. * * @param mediaChannel イベントが発生したチャネル * @param attendees 配信者数と視聴者数を含むオブジェクト @@ -190,7 +193,7 @@ class SoraMediaChannel @JvmOverloads constructor( fun onAttendeesCountUpdated(mediaChannel: SoraMediaChannel, attendees: ChannelAttendeesCount) {} /** - * Sora のシグナリング通知機能の通知を受信したときに呼び出されるコールバック + * Sora のシグナリング通知機能の通知を受信したときに呼び出されるコールバック. * * @param mediaChannel イベントが発生したチャネル * @param notification プッシュ API により受信したメッセージ @@ -198,7 +201,7 @@ class SoraMediaChannel @JvmOverloads constructor( fun onNotificationMessage(mediaChannel: SoraMediaChannel, notification : NotificationMessage) {} /** - * Sora のプッシュ API によりメッセージを受信したときに呼び出されるコールバック + * Sora のプッシュ API によりメッセージを受信したときに呼び出されるコールバック. * * @param mediaChannel イベントが発生したチャネル * @param push プッシュ API により受信したメッセージ @@ -206,7 +209,7 @@ class SoraMediaChannel @JvmOverloads constructor( fun onPushMessage(mediaChannel: SoraMediaChannel, push : PushMessage) {} /** - * PeerConnection の getStats() 統計情報を取得したときに呼び出されるコールバック + * PeerConnection の getStats() 統計情報を取得したときに呼び出されるコールバック. * * cf. * - https://w3c.github.io/webrtc-stats/ @@ -218,15 +221,15 @@ class SoraMediaChannel @JvmOverloads constructor( fun onPeerConnectionStatsReady(mediaChannel: SoraMediaChannel, statsReport: RTCStatsReport) {} /** - * サイマルキャスト配信のエンコーダ設定を変更するためのコールバック + * サイマルキャスト配信のエンコーダ設定を変更するためのコールバック. * - * 引数の encodings は Sora が送ってきた設定を反映した RtpParameters.Encoding のリストです。 - * デフォルトの実装ではなにも行いません。 + * 引数の encodings は Sora が送ってきた設定を反映した RtpParameters.Encoding のリストです. + * デフォルトの実装ではなにも行いません. * このコールバックを実装し、引数のオブジェクトを変更することで、アプリケーションの要件に従った - * 設定をエンコーダにわたすことができます。 + * 設定をエンコーダにわたすことができます. * - * cf. Web 標準の対応 API は次のとおりです。libwebrtc の native(C++) と android の実装は - * 異なりますので注意してください。 + * cf. Web 標準の対応 API は次のとおりです. libwebrtc の native(C++) と android の実装は + * 異なりますので注意してください. * - https://w3c.github.io/webrtc-pc/#dom-rtcrtpencodingparameters * - https://w3c.github.io/webrtc-pc/#dom-rtcrtpsender-setparameters * @@ -244,7 +247,7 @@ class SoraMediaChannel @JvmOverloads constructor( private var closing = false /** - * コネクション ID + * コネクション ID. */ var connectionId: String? = null private set @@ -444,9 +447,9 @@ class SoraMediaChannel @JvmOverloads constructor( } /** - * Sora に接続します。 + * Sora に接続します. * - * アプリケーションで接続後の処理が必要な場合は [Listener.onConnect] で行います。 + * アプリケーションで接続後の処理が必要な場合は [Listener.onConnect] で行います. */ fun connect() { try { @@ -717,9 +720,9 @@ class SoraMediaChannel @JvmOverloads constructor( } /** - * Sora への接続を切断します。 + * Sora への接続を切断します. * - * アプリケーションとして切断後の処理が必要な場合は [Listener.onClose] で行います。 + * アプリケーションとして切断後の処理が必要な場合は [Listener.onClose] で行います. */ fun disconnect() { if (closing) diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/data/ChannelAttendeesCount.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/data/ChannelAttendeesCount.kt index fd171f5f..ca544a01 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/data/ChannelAttendeesCount.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/data/ChannelAttendeesCount.kt @@ -1,39 +1,39 @@ package jp.shiguredo.sora.sdk.channel.data /** - * チャネルの参加者数を表すクラスです + * チャネルの参加者数を表すクラスです. */ data class ChannelAttendeesCount( /** - * 配信者数 + * 配信者数. */ @Deprecated("numberOfUpstreams は 2021 年 6 月リリース予定の Sora にて廃止されます。") val numberOfUpstreams: Int, /** - * 視聴者数 + * 視聴者数. */ @Deprecated("numberOfDownstreams は 2021 年 6 月リリース予定の Sora にて廃止されます。") val numberOfDownstreams: Int, /** - * sendrecv の接続数 + * sendrecv の接続数. */ val numberOfSendrecvConnections: Int, /** - * sendonly の接続数 + * sendonly の接続数. */ val numberOfSendonlyConnections: Int, /** - * recvonly の接続数 + * recvonly の接続数. */ val numberOfRecvonlyConnections: Int, ) { /** - * 配信者数と視聴者数の合計 + * 配信者数と視聴者数の合計. */ val numberOfConnections: Int get() = numberOfSendrecvConnections + numberOfSendonlyConnections + numberOfRecvonlyConnections diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/PeerConnectionOption.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/PeerConnectionOption.kt index 1b4c9539..9dd666ef 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/PeerConnectionOption.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/PeerConnectionOption.kt @@ -1,14 +1,14 @@ package jp.shiguredo.sora.sdk.channel.option /** - * PeerConnection に関するオプションをまとめるクラスです + * PeerConnection に関するオプションをまとめるクラスです. */ class PeerConnectionOption { /** - * PeerConnection の getStats() 統計情報を取得するインターバルのミリ秒 + * PeerConnection の getStats() 統計情報を取得するインターバルのミリ秒. * - * 0 の場合、統計情報を取得しません。 + * 0 の場合、統計情報を取得しません. * * cf. * - https://w3c.github.io/webrtc-stats/ diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraAudioOption.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraAudioOption.kt index bfbf623d..f062d724 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraAudioOption.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraAudioOption.kt @@ -6,7 +6,7 @@ import org.webrtc.MediaConstraints import org.webrtc.audio.AudioDeviceModule /** - * 音声に関するオプションをまとめるクラスです + * 音声に関するオプションをまとめるクラスです. */ class SoraAudioOption { @@ -17,7 +17,7 @@ class SoraAudioOption { const val NOISE_SUPPRESSION_CONSTRAINT = "googNoiseSuppression" } /** - * 利用できる音声コーデックを示します。 + * 利用できる音声コーデックを示します. */ enum class Codec { /** Opus */ @@ -25,95 +25,95 @@ class SoraAudioOption { } /** - * 端末組み込みの acoustic echo canceler を使うかどうかの設定 + * 端末組み込みの acoustic echo canceler を使うかどうかの設定. * * cf. `org.webrtc.JavaAudioDeviceModule.Builder#setUseHardwareAcousticEchoCanceler()` */ var useHardwareAcousticEchoCanceler: Boolean = true /** - * 端末組み込みの noise suppressor を使うかどうかの設定 + * 端末組み込みの noise suppressor を使うかどうかの設定. * * cf. `org.webrtc.JavaAudioDeviceModule.Builder#setUseHardwareNoiseSuppressor()` */ var useHardwareNoiseSuppressor: Boolean = true /** - * 利用する AudioDeviceModule を指定します + * 利用する AudioDeviceModule を指定します. * * null でない場合、 [useHardwareAcousticEchoCanceler] と [useHardwareNoiseSuppressor] の - * 設定は無視されます。 + * 設定は無視されます. * * cf `org.webrtc.AudioDeviceModule` */ var audioDeviceModule: AudioDeviceModule? = null /** - * 入力音声のエコーキャンセル処理の有無の設定 + * 入力音声のエコーキャンセル処理の有無の設定. * - * false に設定すると音声の `org.webrtc.MediaConstraints` に以下の設定を追加します。 + * false に設定すると音声の `org.webrtc.MediaConstraints` に以下の設定を追加します. * - `googEchoCancellation` : false */ var audioProcessingEchoCancellation: Boolean = true /** - * 入力音声の自動ゲイン調整処理の有無の設定 + * 入力音声の自動ゲイン調整処理の有無の設定. * - * false に設定すると音声の `org.webrtc.MediaConstraints` に以下の設定を追加します。 + * false に設定すると音声の `org.webrtc.MediaConstraints` に以下の設定を追加します. * - `googAutoGainControl` : false */ var audioProcessingAutoGainControl: Boolean = true /** - * 入力音声のハイパスフィルタ処理の有無の設定 + * 入力音声のハイパスフィルタ処理の有無の設定. * - * false に設定すると音声の `org.webrtc.MediaConstraints` に以下の設定を追加します。 + * false に設定すると音声の `org.webrtc.MediaConstraints` に以下の設定を追加します. * - `googHighpassFilter` : false */ var audioProcessingHighpassFilter: Boolean = true /** - * 入力音声のノイズ抑制処理の有無の設定 + * 入力音声のノイズ抑制処理の有無の設定. * - * false に設定すると音声の `org.webrtc.MediaConstraints` に以下の設定を追加します。 + * false に設定すると音声の `org.webrtc.MediaConstraints` に以下の設定を追加します. * - `googNoiseSuppression` : false */ var audioProcessingNoiseSuppression: Boolean = true /** - * 音声の `org.webrtc.MediaConstraints` を設定します + * 音声の `org.webrtc.MediaConstraints` を設定します. * * null でない場合、 [audioProcessingEchoCancellation], [audioProcessingAutoGainControl], - * [audioProcessingHighpassFilter], [audioProcessingNoiseSuppression] の設定は無視されます。 + * [audioProcessingHighpassFilter], [audioProcessingNoiseSuppression] の設定は無視されます. */ var mediaConstraints: MediaConstraints? = null /** - * 音声ソースの指定 + * 音声ソースの指定. * - * AudioDeviceModule 生成時に利用されます。 - * デフォルト値は `android.media.MediaRecorder.AudioSource.VOICE_COMMUNICATION です。 + * AudioDeviceModule 生成時に利用されます. + * デフォルト値は `android.media.MediaRecorder.AudioSource.VOICE_COMMUNICATION です. */ var audioSource: Int = MediaRecorder.AudioSource.VOICE_COMMUNICATION /** - * 入力をステレオにするかどうかのフラグ + * 入力をステレオにするかどうかのフラグ. * - * AudioDeviceModule 生成時に利用されます。 - * デフォルト値は false (モノラル) です。 + * AudioDeviceModule 生成時に利用されます. + * デフォルト値は false (モノラル) です. */ var useStereoInput: Boolean = false /** - * 出力をステレオにするかどうかのフラグ + * 出力をステレオにするかどうかのフラグ. * - * AudioDeviceModule 生成時に利用されます。 - * デフォルト値は false (モノラル) です。 + * AudioDeviceModule 生成時に利用されます. + * デフォルト値は false (モノラル) です. */ var useStereoOutput: Boolean = false /** - * opus_params + * opus_params. */ var opusParams: OpusParams? = null } diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraChannelRole.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraChannelRole.kt index b727da03..5f089111 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraChannelRole.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraChannelRole.kt @@ -3,7 +3,7 @@ package jp.shiguredo.sora.sdk.channel.option import java.util.* /** - * チャネルの役割を示します + * チャネルの役割を示します. */ enum class SoraChannelRole { diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraMediaOption.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraMediaOption.kt index 25c4ced0..d97a0a22 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraMediaOption.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraMediaOption.kt @@ -4,7 +4,7 @@ import jp.shiguredo.sora.sdk.Sora import org.webrtc.* /** - * Sora への接続オプションを表すクラスです + * Sora への接続オプションを表すクラスです. */ class SoraMediaOption { @@ -25,12 +25,12 @@ class SoraMediaOption { get() = spotlightOption != null /** - * 利用する VideoEncoderFactory を指定します + * 利用する VideoEncoderFactory を指定します. */ var videoEncoderFactory: VideoEncoderFactory? = null /** - * 利用する VideoDecoderFactory を指定します + * 利用する VideoDecoderFactory を指定します. */ var videoDecoderFactory: VideoDecoderFactory? = null @@ -45,7 +45,7 @@ class SoraMediaOption { var videoBitrate: Int? = null /** - * 映像の視聴を有効にします + * 映像の視聴を有効にします. * * cf. * - `org.webrtc.EglBase` @@ -59,7 +59,7 @@ class SoraMediaOption { } /** - * 映像の配信を有効にします + * 映像の配信を有効にします. * * cf. * - `org.webrtc.VideoCapturer` @@ -77,7 +77,7 @@ class SoraMediaOption { } /** - * サイマルキャスト機能を有効にします。 + * サイマルキャスト機能を有効にします. */ fun enableSimulcast(rid: SoraVideoOption.SimulcastRid? = null) { simulcastEnabled = true @@ -85,13 +85,13 @@ class SoraMediaOption { } /** - * スポットライト機能を有効にします。 + * スポットライト機能を有効にします. * - * スポットライト機能はサイマルキャスト機能を利用します。 - * スポットライト機能を有効にすると、マルチストリームとサイマルキャスト機能も有効になります。 + * スポットライト機能はサイマルキャスト機能を利用します. + * スポットライト機能を有効にすると、マルチストリームとサイマルキャスト機能も有効になります. * * サーバがスポットライトレガシー機能を利用している場合は、 - * [Sora.usesSpotlightLegacy] に `true` をセットしてください。 + * [Sora.usesSpotlightLegacy] に `true` をセットしてください. */ fun enableSpotlight(option: SoraSpotlightOption) { spotlightOption = option @@ -103,37 +103,37 @@ class SoraMediaOption { } /** - * 音声のオプション設定を指定します + * 音声のオプション設定を指定します. */ var audioOption: SoraAudioOption = SoraAudioOption() /** - * 音声の視聴を有効にします + * 音声の視聴を有効にします. */ fun enableAudioDownstream() { audioDownstreamEnabled = true } /** - * 音声の配信を有効にします + * 音声の配信を有効にします. */ fun enableAudioUpstream() { audioUpstreamEnabled = true } /** - * 音声コーデック + * 音声コーデック. */ var audioCodec = SoraAudioOption.Codec.OPUS // audioBitRate が正しい綴りだが後方互換性を壊すほどではないので放置する /** - * 音声ビットレート + * 音声ビットレート. */ var audioBitrate: Int? = null /** - * マルチストリームを有効にします + * マルチストリームを有効にします. * * cf. * - Sora ドキュメントのマルチストリーム @@ -186,16 +186,12 @@ class SoraMediaOption { SoraChannelRole.RECVONLY /** - * enableCpuOveruseDetection - * - * JavaScript API の "googCpuOveruseDetection" に相当する設定項目です。 + * JavaScript API の "googCpuOveruseDetection" に相当する設定項目です. */ var enableCpuOveruseDetection: Boolean = true /** - * tcpCandidatePolicy - * - * TcpCandidatePolicy を設定します。 + * TcpCandidatePolicy を設定します. */ var tcpCandidatePolicy: PeerConnection.TcpCandidatePolicy = PeerConnection.TcpCandidatePolicy.ENABLED diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraSpotlightOption.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraSpotlightOption.kt index 228f58d2..e4c467cb 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraSpotlightOption.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraSpotlightOption.kt @@ -1,16 +1,16 @@ package jp.shiguredo.sora.sdk.channel.option /** - * スポットライト機能のオプションです。 + * スポットライト機能のオプションです. */ class SoraSpotlightOption { /** - * スポットライト機能のアクティブな配信数を指定します + * スポットライト機能のアクティブな配信数を指定します. * * cf. * - Sora ドキュメントのスポットライト機能 - * [](https://sora.shiguredo.jp/doc/SPOTLIGHT.html) + * [https://sora.shiguredo.jp/doc/SPOTLIGHT.html](https://sora.shiguredo.jp/doc/SPOTLIGHT.html) */ var spotlightNumber: Int? = null diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraVideoOption.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraVideoOption.kt index 14156a2c..9de8d47f 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraVideoOption.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraVideoOption.kt @@ -3,12 +3,12 @@ package jp.shiguredo.sora.sdk.channel.option import android.graphics.Point /** - * 映像に関するオプションをまとめるクラスです + * 映像に関するオプションをまとめるクラスです. */ class SoraVideoOption { /** - * 利用できる映像コーデックを示します + * 利用できる映像コーデックを示します. */ enum class Codec { /** H.264 */ @@ -20,12 +20,12 @@ class SoraVideoOption { } /** - * 映像のフレームサイズをまとめるクラスです + * 映像のフレームサイズをまとめるクラスです. */ class FrameSize { /** - * ランドスケープモードで利用できるフレームサイズを示します + * ランドスケープモードで利用できるフレームサイズを示します. */ class Landscape { @@ -56,7 +56,7 @@ class SoraVideoOption { } /** - * ポートレートモードで利用できるフレームサイズを示します + * ポートレートモードで利用できるフレームサイズを示します. */ class Portrait { diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/error/SoraErrorReason.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/error/SoraErrorReason.kt index 8b0ad925..190fee55 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/error/SoraErrorReason.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/error/SoraErrorReason.kt @@ -1,7 +1,7 @@ package jp.shiguredo.sora.sdk.error /** - * Sora との通信やメディアに関するエラーを示します + * Sora との通信やメディアに関するエラーを示します. */ enum class SoraErrorReason { // Sora との接続のエラー diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/util/SoraLogger.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/util/SoraLogger.kt index dfe2fc11..2c469d7e 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/util/SoraLogger.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/util/SoraLogger.kt @@ -3,7 +3,7 @@ package jp.shiguredo.sora.sdk.util import android.util.Log /** - * ログ機能を提供します + * ログ機能を提供します. * * cf. * - `android.util.Log` From 0ffa13b3d636a05f625e6ac9a04333b7a1aea9fa Mon Sep 17 00:00:00 2001 From: miosakuma Date: Thu, 21 Oct 2021 11:56:17 +0900 Subject: [PATCH 05/85] =?UTF-8?q?=E5=87=BA=E5=8A=9B=E5=BD=A2=E5=BC=8F?= =?UTF-8?q?=E3=82=92=20HTML=20=E5=BD=A2=E5=BC=8F=E3=81=AB=E5=A4=89?= =?UTF-8?q?=E6=9B=B4=E3=81=99=E3=82=8B=E3=80=82=E3=83=89=E3=82=AD=E3=83=A5?= =?UTF-8?q?=E3=83=A1=E3=83=B3=E3=83=88=E3=81=AE=E3=82=BF=E3=82=A4=E3=83=88?= =?UTF-8?q?=E3=83=AB=E3=82=92=20"sora-android-sdk"=20=E3=81=AB=E5=A4=89?= =?UTF-8?q?=E6=9B=B4=E3=81=99=E3=82=8B=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sora-android-sdk/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sora-android-sdk/build.gradle b/sora-android-sdk/build.gradle index c3161150..d66459dd 100644 --- a/sora-android-sdk/build.gradle +++ b/sora-android-sdk/build.gradle @@ -46,10 +46,10 @@ android { } } -dokkaJavadoc.configure { +dokkaHtml.configure { // デフォルトの出力先は "${buildDir}/dokka". 変更したいときにコメントアウトを行う. // outputDirectory.set(new File("${buildDir}/dokka")) - moduleName.set("Sora Android SDK") + moduleName.set("sora-android-sdk") // "dafault" を指定すると $USER_HOME/.cache/dokka を使用するとあるが実際には "${projectDir}/default" を見てしまうのでコメントアウトしている. // cacheRoot.set(file("default")) From 8c117d980d6555d88a6ab4a05d4a8a2432311bc6 Mon Sep 17 00:00:00 2001 From: miosakuma Date: Thu, 21 Oct 2021 14:39:58 +0900 Subject: [PATCH 06/85] =?UTF-8?q?SoraMediaChannel=20=E3=81=AE=E3=83=89?= =?UTF-8?q?=E3=82=AD=E3=83=A5=E3=83=A1=E3=83=B3=E3=83=86=E3=83=BC=E3=82=B7?= =?UTF-8?q?=E3=83=A7=E3=83=B3=E3=82=B3=E3=83=A1=E3=83=B3=E3=83=88=E5=86=85?= =?UTF-8?q?=E5=AE=B9=E3=82=92=E8=A6=8B=E7=9B=B4=E3=81=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt index 4a3244a5..28990fc6 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt @@ -23,11 +23,10 @@ import java.util.* import kotlin.concurrent.schedule /** - * [SignalingChannel] と [PeerChannel] を - * 管理、協調動作させるためのクラス + * Sora への接続を行うクラスです. + * [SignalingChannel] と [PeerChannel] の管理、協調動作制御を行っています. + * このクラスを利用することでシグナリングの詳細が隠蔽され、単一の [Listener] でイベントを受けることが出来ます. * - * Sora に接続するアプリケーションは、このクラスを利用することでシグナリングの - * 詳細が隠蔽され、単一の [Listener] でイベントを受けることが出来ます. * * シグナリングの手順とデータに関しては Sora のドキュメント * [https://sora.shiguredo.jp/doc/SIGNALING.html](https://sora.shiguredo.jp/doc/SIGNALING.html)を参照ください. From 14d59340954a024b6d93e779120131afa3e3b43c Mon Sep 17 00:00:00 2001 From: miosakuma Date: Fri, 22 Oct 2021 10:02:04 +0900 Subject: [PATCH 07/85] =?UTF-8?q?CHAGES.md=20=E3=82=92=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGES.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 1f875516..ad29509a 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -9,6 +9,11 @@ - FIX - バグ修正 +## develop + + - [UPDATE] dokka を 1.5.31 に上げる + - @miosakuma + ## 2021.3 - [UPDATE] libwebrtc を 93.4577.8.2 に上げる From 7ae5cfbf2f88280d382decaeb773eaa1469c8ed6 Mon Sep 17 00:00:00 2001 From: enm10k Date: Wed, 17 Nov 2021 17:52:49 +0900 Subject: [PATCH 08/85] =?UTF-8?q?signalingEndpoint=20=E3=81=AE=E5=9E=8B?= =?UTF-8?q?=E3=82=92=E5=A4=89=E6=9B=B4=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sora/sdk/channel/SoraMediaChannel.kt | 4 +-- .../sdk/channel/signaling/SignalingChannel.kt | 25 +++++++++++++++---- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt index 28990fc6..6f9534e6 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt @@ -48,7 +48,7 @@ import kotlin.concurrent.schedule */ class SoraMediaChannel @JvmOverloads constructor( private val context: Context, - private val signalingEndpoint: String, + private val signalingEndpoint: List, private val channelId: String, private val signalingMetadata: Any? = "", private val mediaOption: SoraMediaOption, @@ -571,7 +571,7 @@ class SoraMediaChannel @JvmOverloads constructor( private fun connectSignalingChannel(clientOfferSdp : SessionDescription?) { signaling = SignalingChannelImpl( - endpoint = signalingEndpoint, + endpoints = signalingEndpoint, role = role, channelId = channelId, connectDataChannelSignaling = dataChannelSignaling, diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt index aec33861..da94b964 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt @@ -36,7 +36,7 @@ interface SignalingChannel { } class SignalingChannelImpl @JvmOverloads constructor( - private val endpoint: String, + private val endpoints: List, private val role: SoraChannelRole, private val channelId: String, private val connectDataChannelSignaling: Boolean? = null, @@ -57,13 +57,20 @@ class SignalingChannelImpl @JvmOverloads constructor( OkHttpClient.Builder().readTimeout(0, TimeUnit.MILLISECONDS).build() private var webSocket: WebSocket? = null + @Synchronized set + private var closing = false override fun connect() { - val url = "${endpoint}?channel_id=${channelId}" - SoraLogger.i(TAG, "[signaling:$role] start to connect ${url}") - val request = Request.Builder().url(url).build() - client.newWebSocket(request, webSocketListener) + SoraLogger.i(TAG, "[signaling:$role] endpoints=$endpoints") + // TODO: endpoints.isEmpty をチェックする? + + for (endpoint in endpoints) { + val url = "$endpoint?channel_id=$channelId" + val request = Request.Builder().url(url).build() + SoraLogger.i(TAG, "connecting to $url") + client.newWebSocket(request, webSocketListener) + } } override fun sendAnswer(sdp: String) { @@ -253,6 +260,14 @@ class SignalingChannelImpl @JvmOverloads constructor( return } + if (this@SignalingChannelImpl.webSocket != null) { + SoraLogger.i(TAG, "already connected. closing connection with ${webSocket.request().url.host}") + webSocket.close(1000, null) + return + } + + SoraLogger.i(TAG, "succeeded to connect with ${webSocket.request().url.host}") + this@SignalingChannelImpl.webSocket = webSocket listener?.onConnect() sendConnectMessage() From 6698a00fac94b584535fa0c542a45b6052b2a4e0 Mon Sep 17 00:00:00 2001 From: enm10k Date: Wed, 17 Nov 2021 20:12:52 +0900 Subject: [PATCH 09/85] =?UTF-8?q?WebSocket=20=E5=88=87=E6=96=AD=E6=99=82?= =?UTF-8?q?=E3=81=AE=E3=82=B3=E3=83=BC=E3=83=AB=E3=83=90=E3=83=83=E3=82=AF?= =?UTF-8?q?=E3=81=8C=E7=99=BA=E7=81=AB=E3=81=99=E3=82=8B=E6=9D=A1=E4=BB=B6?= =?UTF-8?q?=E3=82=92=E8=A6=8B=E7=9B=B4=E3=81=99=20&=20type:=20redirect=20?= =?UTF-8?q?=E3=81=8C=E5=8B=95=E3=81=84=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sdk/channel/signaling/SignalingChannel.kt | 62 ++++++++++++++++--- .../sdk/channel/signaling/message/Catalog.kt | 8 ++- .../signaling/message/MessageConverter.kt | 13 +++- 3 files changed, 71 insertions(+), 12 deletions(-) diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt index da94b964..bbf2d794 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt @@ -61,18 +61,24 @@ class SignalingChannelImpl @JvmOverloads constructor( private var closing = false + private var redirect = false + override fun connect() { SoraLogger.i(TAG, "[signaling:$role] endpoints=$endpoints") // TODO: endpoints.isEmpty をチェックする? for (endpoint in endpoints) { - val url = "$endpoint?channel_id=$channelId" - val request = Request.Builder().url(url).build() - SoraLogger.i(TAG, "connecting to $url") - client.newWebSocket(request, webSocketListener) + connect(endpoint) } } + private fun connect(endpoint: String) { + val url = "$endpoint?channel_id=$channelId" + val request = Request.Builder().url(url).build() + SoraLogger.i(TAG, "connecting to $url") + client.newWebSocket(request, webSocketListener) + } + override fun sendAnswer(sdp: String) { SoraLogger.d(TAG, "[signaling:$role] -> answer") @@ -170,14 +176,15 @@ class SignalingChannelImpl @JvmOverloads constructor( metadata = connectMetadata, sdp = clientOfferSdp?.description, clientId = clientId, - signalingNotifyMetadata = signalingNotifyMetadata + signalingNotifyMetadata = signalingNotifyMetadata, + redirect = redirect ) it.send(message) } } private fun closeWithError(reason: String) { - SoraLogger.i(TAG, "[signaling:$role] $reason") + SoraLogger.i(TAG, "[signaling:$role] closeWithError: reason=$reason") disconnect() } @@ -249,6 +256,22 @@ class SignalingChannelImpl @JvmOverloads constructor( } } + private fun onRedirectMessage(text: String) { + SoraLogger.d(TAG, "[signaling:$role] <- redirect") + + val redirect = MessageConverter.parseRedirectMessage(text) + this@SignalingChannelImpl.redirect = true + this@SignalingChannelImpl.webSocket?.let { + SoraLogger.d(TAG, "closing old connection with ${it.request().url.host}") + + this@SignalingChannelImpl.webSocket = null + it.close(1000, null) + } + + SoraLogger.d(TAG, "redirect to ${redirect.location}") + connect(redirect.location) + } + private val webSocketListener = object : WebSocketListener() { override fun onOpen(webSocket: WebSocket, response: Response) { @@ -261,7 +284,9 @@ class SignalingChannelImpl @JvmOverloads constructor( } if (this@SignalingChannelImpl.webSocket != null) { - SoraLogger.i(TAG, "already connected. closing connection with ${webSocket.request().url.host}") + SoraLogger.i(TAG, + "already connected with ${this@SignalingChannelImpl.webSocket?.request()?.url?.host}. " + + "closing connection to ${webSocket.request().url.host}") webSocket.close(1000, null) return } @@ -297,6 +322,7 @@ class SignalingChannelImpl @JvmOverloads constructor( "re-offer" -> onReOfferMessage(json) "notify" -> onNotifyMessage(json) "push" -> onPushMessage(json) + "redirect" -> onRedirectMessage(json) else -> SoraLogger.i(TAG, "received unknown-type message") } @@ -314,12 +340,26 @@ class SignalingChannelImpl @JvmOverloads constructor( } override fun onClosed(webSocket: WebSocket, code: Int, reason: String) { + if (this@SignalingChannelImpl.webSocket?.request()?.url?.host != webSocket.request().url.host) { + /* + endpoints が複数の値を保つ場合、 SignalingChannelImpl は endpoints に含まれる全てのエンドポイントに + WebSocket の接続を試み、その内の1つを利用する + 利用しなかった WebSocket を切断する際に SignalingChannelImpl の disconnect が実行されると困るので、 + 切断している WebSocket が SignalingChannelImpl の保持するものと等しい場合のみ、後続の処理を実行する + + type: redirect の処理でも、再接続中に WebSocket を切断するので同様の考慮が必要だが、 + onRedirectMessage 内で SignalingChannelImpl の WebSocket に null を設定しているため、 + 同じチェック方法でカバーできている + */ + return + } try { if (code == 1000) { - SoraLogger.i(TAG, "[signaling:$role] @onClosed: reason = [${reason}], code = ${code}") + SoraLogger.i(TAG, "[signaling:$role] @onClosed: reason = [${reason}], code = $code") } else { - SoraLogger.w(TAG, "[signaling:$role] @onClosed: reason = [${reason}], code = ${code}") + SoraLogger.w(TAG, "[signaling:$role] @onClosed: reason = [${reason}], code = $code") } + disconnect() } catch (e: Exception) { SoraLogger.w(TAG, e.toString()) @@ -327,6 +367,10 @@ class SignalingChannelImpl @JvmOverloads constructor( } override fun onClosing(webSocket: WebSocket, code: Int, reason: String) { + if (this@SignalingChannelImpl.webSocket?.request()?.url?.host != webSocket.request().url.host) { + // onClosed のコメントを参照 + return + } SoraLogger.d(TAG, "[signaling:$role] @onClosing") disconnect() } diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/message/Catalog.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/message/Catalog.kt index 67ad23d1..be7525a8 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/message/Catalog.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/message/Catalog.kt @@ -43,7 +43,8 @@ data class ConnectMessage( @SerializedName("data_channel_signaling") val dataChannelSignaling: Boolean? = null, @SerializedName("ignore_disconnect_websocket") - val ignoreDisconnectWebsocket: Boolean? = null + val ignoreDisconnectWebsocket: Boolean? = null, + @SerializedName("redirect") var redirect: Boolean? = null ) data class VideoSetting( @@ -87,6 +88,11 @@ data class Encoding( @SerializedName("scaleResolutionDownBy") val scaleResolutionDownBy: Double? ) +data class RedirectMessage( + @SerializedName("type") val type: String = "redirect", + @SerializedName("location") val location: String +) + data class OfferMessage( @SerializedName("type") val type: String = "offer", @SerializedName("sdp") val sdp: String, diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/message/MessageConverter.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/message/MessageConverter.kt index ecb43f6e..f5231c77 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/message/MessageConverter.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/message/MessageConverter.kt @@ -32,7 +32,8 @@ class MessageConverter { metadata: Any?, sdp: String? = null, clientId: String? = null, - signalingNotifyMetadata: Any? = null + signalingNotifyMetadata: Any? = null, + redirect: Boolean = false ): String { val msg = ConnectMessage( @@ -56,7 +57,7 @@ class MessageConverter { }, sdp = sdp, clientId = clientId, - signalingNotifyMetadata = signalingNotifyMetadata + signalingNotifyMetadata = signalingNotifyMetadata, ) if (mediaOption.upstreamIsRequired) { @@ -111,6 +112,10 @@ class MessageConverter { msg.spotlightUnfocusRid = mediaOption.spotlightOption?.spotlightUnfocusRid?.toString() } + if (redirect) { + msg.redirect = true + } + val jsonMsg = gson.toJson(msg) SoraLogger.d(TAG, "connect: message=$jsonMsg") return jsonMsg @@ -182,6 +187,10 @@ class MessageConverter { fun parseReqStatsMessage(text: String): ReqStatsMessage { return gson.fromJson(text, ReqStatsMessage::class.java) } + + fun parseRedirectMessage(text: String): RedirectMessage { + return gson.fromJson(text, RedirectMessage::class.java) + } } } From f7d6b6607add7f01da4695446d246a62f71d935b Mon Sep 17 00:00:00 2001 From: enm10k Date: Thu, 18 Nov 2021 18:22:11 +0900 Subject: [PATCH 10/85] =?UTF-8?q?type:=20redirect=20=E3=82=92=E5=8F=97?= =?UTF-8?q?=E4=BF=A1=E3=81=97=E3=81=9F=E3=82=89=20SignalingChannel=20?= =?UTF-8?q?=E3=82=92=E4=BD=9C=E3=82=8A=E7=9B=B4=E3=81=97=E3=81=A6=E3=81=BF?= =?UTF-8?q?=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sora/sdk/channel/SoraMediaChannel.kt | 21 ++++++++--- .../sdk/channel/signaling/SignalingChannel.kt | 35 ++++++++----------- 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt index 6f9534e6..f8c4fe48 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt @@ -323,6 +323,15 @@ class SoraMediaChannel @JvmOverloads constructor( } } + override fun onRedirect(location: String) { + SoraLogger.d(TAG, "[channel:$role] @peer:onLocalIceCandidateFound") + + SoraLogger.i(TAG, "[channel:$role] opening new SignalingChannel") + requestClientOfferSdp(location) + SoraLogger.i(TAG, "[channel:$role] closing old SignalingChannel") + signaling?.disconnect() + } + } private val peerListener = object : PeerChannel.Listener { @@ -527,7 +536,7 @@ class SoraMediaChannel @JvmOverloads constructor( } - private fun requestClientOfferSdp() { + private fun requestClientOfferSdp(redirectLocation: String? = null) { val mediaOption = SoraMediaOption().apply { enableVideoDownstream(null) enableAudioDownstream() @@ -555,7 +564,7 @@ class SoraMediaChannel @JvmOverloads constructor( } val handler = Handler(Looper.getMainLooper()) handler.post() { - connectSignalingChannel(it.getOrNull()) + connectSignalingChannel(redirectLocation, it.getOrNull()) } }, onError = { @@ -569,9 +578,10 @@ class SoraMediaChannel @JvmOverloads constructor( } } - private fun connectSignalingChannel(clientOfferSdp : SessionDescription?) { + private fun connectSignalingChannel(redirectLocation: String?, clientOfferSdp : SessionDescription?) { + val endpoints = if (redirectLocation != null) { listOf(redirectLocation) } else { signalingEndpoint } signaling = SignalingChannelImpl( - endpoints = signalingEndpoint, + endpoints = endpoints, role = role, channelId = channelId, connectDataChannelSignaling = dataChannelSignaling, @@ -581,7 +591,8 @@ class SoraMediaChannel @JvmOverloads constructor( listener = signalingListener, clientOfferSdp = clientOfferSdp, clientId = clientId, - signalingNotifyMetadata = signalingNotifyMetadata + signalingNotifyMetadata = signalingNotifyMetadata, + redirect = redirectLocation != null ) signaling!!.connect() } diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt index bbf2d794..40c63e95 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt @@ -31,6 +31,7 @@ interface SignalingChannel { fun onError(reason: SoraErrorReason) fun onNotificationMessage(notification: NotificationMessage) fun onPushMessage(push: PushMessage) + fun onRedirect(location: String) fun getStats(handler: (RTCStatsReport?) -> Unit) } } @@ -46,7 +47,8 @@ class SignalingChannelImpl @JvmOverloads constructor( private var listener: SignalingChannel.Listener?, private val clientOfferSdp: SessionDescription?, private val clientId: String? = null, - private val signalingNotifyMetadata: Any? = null + private val signalingNotifyMetadata: Any? = null, + private val redirect: Boolean = false ) : SignalingChannel { companion object { @@ -61,7 +63,8 @@ class SignalingChannelImpl @JvmOverloads constructor( private var closing = false - private var redirect = false + // TODO: フラグ名に改善の余地あり? + private var redirecting = false override fun connect() { SoraLogger.i(TAG, "[signaling:$role] endpoints=$endpoints") @@ -154,7 +157,10 @@ class SignalingChannelImpl @JvmOverloads constructor( closing = true client.dispatcher.executorService.shutdown() webSocket?.close(1000, null) - listener?.onDisconnect() + + if (!redirecting) { + listener?.onDisconnect() + } listener = null } } @@ -258,18 +264,11 @@ class SignalingChannelImpl @JvmOverloads constructor( private fun onRedirectMessage(text: String) { SoraLogger.d(TAG, "[signaling:$role] <- redirect") + redirecting = true - val redirect = MessageConverter.parseRedirectMessage(text) - this@SignalingChannelImpl.redirect = true - this@SignalingChannelImpl.webSocket?.let { - SoraLogger.d(TAG, "closing old connection with ${it.request().url.host}") - - this@SignalingChannelImpl.webSocket = null - it.close(1000, null) - } - - SoraLogger.d(TAG, "redirect to ${redirect.location}") - connect(redirect.location) + val msg = MessageConverter.parseRedirectMessage(text) + SoraLogger.d(TAG, "redirect to ${msg.location}") + listener?.onRedirect(msg.location) } private val webSocketListener = object : WebSocketListener() { @@ -340,16 +339,12 @@ class SignalingChannelImpl @JvmOverloads constructor( } override fun onClosed(webSocket: WebSocket, code: Int, reason: String) { - if (this@SignalingChannelImpl.webSocket?.request()?.url?.host != webSocket.request().url.host) { + if (redirecting || this@SignalingChannelImpl.webSocket?.request()?.url?.host != webSocket.request().url.host) { /* endpoints が複数の値を保つ場合、 SignalingChannelImpl は endpoints に含まれる全てのエンドポイントに WebSocket の接続を試み、その内の1つを利用する 利用しなかった WebSocket を切断する際に SignalingChannelImpl の disconnect が実行されると困るので、 切断している WebSocket が SignalingChannelImpl の保持するものと等しい場合のみ、後続の処理を実行する - - type: redirect の処理でも、再接続中に WebSocket を切断するので同様の考慮が必要だが、 - onRedirectMessage 内で SignalingChannelImpl の WebSocket に null を設定しているため、 - 同じチェック方法でカバーできている */ return } @@ -367,7 +362,7 @@ class SignalingChannelImpl @JvmOverloads constructor( } override fun onClosing(webSocket: WebSocket, code: Int, reason: String) { - if (this@SignalingChannelImpl.webSocket?.request()?.url?.host != webSocket.request().url.host) { + if (redirecting || this@SignalingChannelImpl.webSocket?.request()?.url?.host != webSocket.request().url.host) { // onClosed のコメントを参照 return } From 33d7855d6e87a366def31f90d10f1879de7cbbac Mon Sep 17 00:00:00 2001 From: enm10k Date: Thu, 18 Nov 2021 23:52:12 +0900 Subject: [PATCH 11/85] =?UTF-8?q?=E6=8E=A1=E7=94=A8=E3=81=95=E3=82=8C?= =?UTF-8?q?=E3=81=AA=E3=81=8B=E3=81=A3=E3=81=9F=20WebSocket=20=E3=82=92?= =?UTF-8?q?=E7=A0=B4=E6=A3=84=E3=81=99=E3=82=8B=E3=82=BF=E3=82=A4=E3=83=9F?= =?UTF-8?q?=E3=83=B3=E3=82=B0=E3=82=92=E6=97=A9=E3=82=81=E3=82=8B=20&=20We?= =?UTF-8?q?bSocket=20=E3=82=92=E6=AF=94=E8=BC=83=E3=81=99=E3=82=8B?= =?UTF-8?q?=E6=96=B9=E6=B3=95=E3=82=92=E4=BF=AE=E6=AD=A3=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sdk/channel/signaling/SignalingChannel.kt | 45 ++++++++++--------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt index 40c63e95..b780530d 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt @@ -60,6 +60,9 @@ class SignalingChannelImpl @JvmOverloads constructor( private var webSocket: WebSocket? = null @Synchronized set + @Synchronized get + + private var webSocketCandidates = mutableListOf() private var closing = false @@ -71,15 +74,14 @@ class SignalingChannelImpl @JvmOverloads constructor( // TODO: endpoints.isEmpty をチェックする? for (endpoint in endpoints) { - connect(endpoint) + webSocketCandidates.add(connect(endpoint)) } } - private fun connect(endpoint: String) { - val url = "$endpoint?channel_id=$channelId" - val request = Request.Builder().url(url).build() - SoraLogger.i(TAG, "connecting to $url") - client.newWebSocket(request, webSocketListener) + private fun connect(endpoint: String): WebSocket { + SoraLogger.i(TAG, "connecting to $endpoint") + val request = Request.Builder().url(endpoint).build() + return client.newWebSocket(request, webSocketListener) } override fun sendAnswer(sdp: String) { @@ -283,16 +285,20 @@ class SignalingChannelImpl @JvmOverloads constructor( } if (this@SignalingChannelImpl.webSocket != null) { - SoraLogger.i(TAG, - "already connected with ${this@SignalingChannelImpl.webSocket?.request()?.url?.host}. " + - "closing connection to ${webSocket.request().url.host}") - webSocket.close(1000, null) return } - SoraLogger.i(TAG, "succeeded to connect with ${webSocket.request().url.host}") + SoraLogger.i(TAG, "succeeded to connect with ${webSocket.request().url}") this@SignalingChannelImpl.webSocket = webSocket + for (candidate in this@SignalingChannelImpl.webSocketCandidates) { + if (candidate != webSocket) { + SoraLogger.d(TAG, "closing connection with ${candidate.request().url}") + candidate.cancel() + } + } + this@SignalingChannelImpl.webSocketCandidates.clear() + listener?.onConnect() sendConnectMessage() } catch (e: Exception) { @@ -339,13 +345,8 @@ class SignalingChannelImpl @JvmOverloads constructor( } override fun onClosed(webSocket: WebSocket, code: Int, reason: String) { - if (redirecting || this@SignalingChannelImpl.webSocket?.request()?.url?.host != webSocket.request().url.host) { - /* - endpoints が複数の値を保つ場合、 SignalingChannelImpl は endpoints に含まれる全てのエンドポイントに - WebSocket の接続を試み、その内の1つを利用する - 利用しなかった WebSocket を切断する際に SignalingChannelImpl の disconnect が実行されると困るので、 - 切断している WebSocket が SignalingChannelImpl の保持するものと等しい場合のみ、後続の処理を実行する - */ + if (redirecting || this@SignalingChannelImpl.webSocket != webSocket) { + // WebSocket が SignalingChannelImpl で保持しているものと等しい場合のみ後続の処理を実行する return } try { @@ -362,8 +363,8 @@ class SignalingChannelImpl @JvmOverloads constructor( } override fun onClosing(webSocket: WebSocket, code: Int, reason: String) { - if (redirecting || this@SignalingChannelImpl.webSocket?.request()?.url?.host != webSocket.request().url.host) { - // onClosed のコメントを参照 + if (redirecting || this@SignalingChannelImpl.webSocket != webSocket) { + // WebSocket が SignalingChannelImpl で保持しているものと等しい場合のみ後続の処理を実行する return } SoraLogger.d(TAG, "[signaling:$role] @onClosing") @@ -371,6 +372,10 @@ class SignalingChannelImpl @JvmOverloads constructor( } override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) { + if (redirecting || this@SignalingChannelImpl.webSocket != webSocket) { + // WebSocket が SignalingChannelImpl で保持しているものと等しい場合のみ後続の処理を実行する + return + } try { response?.let { SoraLogger.i(TAG, "[signaling:$role] @onFailure: ${it.message}, $t") From 8127ae5782df747a8de850165761e31f87e306dd Mon Sep 17 00:00:00 2001 From: enm10k Date: Wed, 24 Nov 2021 10:47:30 +0900 Subject: [PATCH 12/85] =?UTF-8?q?=E3=83=AD=E3=82=B0=E3=81=AE=E3=83=A1?= =?UTF-8?q?=E3=83=83=E3=82=BB=E3=83=BC=E3=82=B8=E3=81=8C=E8=AA=A4=E3=81=A3?= =?UTF-8?q?=E3=81=A6=E3=81=84=E3=81=9F=E3=81=AE=E3=81=A7=E4=BF=AE=E6=AD=A3?= =?UTF-8?q?=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt index f8c4fe48..51e48ddb 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt @@ -324,7 +324,7 @@ class SoraMediaChannel @JvmOverloads constructor( } override fun onRedirect(location: String) { - SoraLogger.d(TAG, "[channel:$role] @peer:onLocalIceCandidateFound") + SoraLogger.d(TAG, "[channel:$role] @peer:onRedirect") SoraLogger.i(TAG, "[channel:$role] opening new SignalingChannel") requestClientOfferSdp(location) From fc767a41c9bba7851e2a0ce4303b6f10f17cae66 Mon Sep 17 00:00:00 2001 From: enm10k Date: Wed, 24 Nov 2021 11:20:27 +0900 Subject: [PATCH 13/85] =?UTF-8?q?type:=20redirect=20=E3=81=AE=E9=9A=9B?= =?UTF-8?q?=E3=81=AB=E3=80=81=E5=88=9D=E5=9B=9E=E6=8E=A5=E7=B6=9A=E6=99=82?= =?UTF-8?q?=E3=81=AE=20clientOffer=20=E3=82=92=E5=86=8D=E5=88=A9=E7=94=A8?= =?UTF-8?q?=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sora/sdk/channel/SoraMediaChannel.kt | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt index 51e48ddb..0a2a84d6 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt @@ -245,6 +245,9 @@ class SoraMediaChannel @JvmOverloads constructor( private var switchedToDataChannel = false private var closing = false + // type: redirect で再利用するために、初回接続時の clientOffer を保持する + private var clientOffer: SessionDescription? = null + /** * コネクション ID. */ @@ -326,10 +329,14 @@ class SoraMediaChannel @JvmOverloads constructor( override fun onRedirect(location: String) { SoraLogger.d(TAG, "[channel:$role] @peer:onRedirect") - SoraLogger.i(TAG, "[channel:$role] opening new SignalingChannel") - requestClientOfferSdp(location) SoraLogger.i(TAG, "[channel:$role] closing old SignalingChannel") signaling?.disconnect() + + SoraLogger.i(TAG, "[channel:$role] opening new SignalingChannel") + val handler = Handler(Looper.getMainLooper()) + handler.post() { + connectSignalingChannel(clientOffer, location) + } } } @@ -536,7 +543,7 @@ class SoraMediaChannel @JvmOverloads constructor( } - private fun requestClientOfferSdp(redirectLocation: String? = null) { + private fun requestClientOfferSdp() { val mediaOption = SoraMediaOption().apply { enableVideoDownstream(null) enableAudioDownstream() @@ -563,8 +570,9 @@ class SoraMediaChannel @JvmOverloads constructor( SoraLogger.d(TAG, "[channel:$role] failed to create client offer SDP: ${it.exceptionOrNull()?.message}") } val handler = Handler(Looper.getMainLooper()) + clientOffer = it.getOrNull() handler.post() { - connectSignalingChannel(redirectLocation, it.getOrNull()) + connectSignalingChannel(clientOffer) } }, onError = { @@ -578,7 +586,7 @@ class SoraMediaChannel @JvmOverloads constructor( } } - private fun connectSignalingChannel(redirectLocation: String?, clientOfferSdp : SessionDescription?) { + private fun connectSignalingChannel(clientOfferSdp : SessionDescription?, redirectLocation: String? = null) { val endpoints = if (redirectLocation != null) { listOf(redirectLocation) } else { signalingEndpoint } signaling = SignalingChannelImpl( endpoints = endpoints, From 1ec16352e97476dcd67956be5b64183df0b38924 Mon Sep 17 00:00:00 2001 From: enm10k Date: Wed, 24 Nov 2021 12:38:18 +0900 Subject: [PATCH 14/85] =?UTF-8?q?=E3=82=B7=E3=82=B0=E3=83=8A=E3=83=AA?= =?UTF-8?q?=E3=83=B3=E3=82=B0=E3=81=AE=20URL=20=E3=82=92=E6=8C=87=E5=AE=9A?= =?UTF-8?q?=E3=81=99=E3=82=8B=E5=BC=95=E6=95=B0=E3=82=92=E5=88=86=E3=81=91?= =?UTF-8?q?=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sora/sdk/channel/SoraMediaChannel.kt | 17 +++++++++++++++-- .../sdk/channel/signaling/SignalingChannel.kt | 2 -- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt index 0a2a84d6..493702de 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt @@ -36,6 +36,7 @@ import kotlin.concurrent.schedule * * @param context `android.content.Context` * @param signalingEndpoint シグナリングの URL + * @param signalingUrlCandidates シグナリングの URL (クラスター機能で複数の URL を利用したい場合はこちらを指定する) * @param signalingMetadata connect メッセージに含める `metadata` * @param channelId Sora に接続するためのチャネル ID * @param mediaOption 映像、音声に関するオプション @@ -48,7 +49,8 @@ import kotlin.concurrent.schedule */ class SoraMediaChannel @JvmOverloads constructor( private val context: Context, - private val signalingEndpoint: List, + private val signalingEndpoint: String? = null, + private val signalingUrlCandidates: List = emptyList(), private val channelId: String, private val signalingMetadata: Any? = "", private val mediaOption: SoraMediaOption, @@ -66,6 +68,12 @@ class SoraMediaChannel @JvmOverloads constructor( const val DEFAULT_TIMEOUT_SECONDS = 10L } + init { + if (signalingEndpoint == null && signalingUrlCandidates.isEmpty()) { + throw IllegalArgumentException("Either signalingEndpoint or signalingUrlCandidates must be specified") + } + } + /** * ロール */ @@ -587,7 +595,12 @@ class SoraMediaChannel @JvmOverloads constructor( } private fun connectSignalingChannel(clientOfferSdp : SessionDescription?, redirectLocation: String? = null) { - val endpoints = if (redirectLocation != null) { listOf(redirectLocation) } else { signalingEndpoint } + val endpoints = when { + redirectLocation != null -> listOf(redirectLocation) + signalingUrlCandidates.isNotEmpty() -> signalingUrlCandidates + else -> listOf(signalingEndpoint!!) + } + signaling = SignalingChannelImpl( endpoints = endpoints, role = role, diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt index b780530d..b9857118 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt @@ -71,8 +71,6 @@ class SignalingChannelImpl @JvmOverloads constructor( override fun connect() { SoraLogger.i(TAG, "[signaling:$role] endpoints=$endpoints") - // TODO: endpoints.isEmpty をチェックする? - for (endpoint in endpoints) { webSocketCandidates.add(connect(endpoint)) } From 40e67a4150f84ec142e80ff69b974c718e58582e Mon Sep 17 00:00:00 2001 From: enm10k Date: Wed, 24 Nov 2021 14:32:53 +0900 Subject: [PATCH 15/85] =?UTF-8?q?=E5=A4=89=E6=95=B0=E5=90=8D=E3=82=92?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E3=81=99=E3=82=8B=20s/signalingUrlCandidates?= =?UTF-8?q?/signalingEndpointCandidates/g?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sora/sdk/channel/SoraMediaChannel.kt | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt index 493702de..8b955b8d 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt @@ -36,7 +36,7 @@ import kotlin.concurrent.schedule * * @param context `android.content.Context` * @param signalingEndpoint シグナリングの URL - * @param signalingUrlCandidates シグナリングの URL (クラスター機能で複数の URL を利用したい場合はこちらを指定する) + * @param signalingEndpointCandidates シグナリングの URL (クラスター機能で複数の URL を利用したい場合はこちらを指定する) * @param signalingMetadata connect メッセージに含める `metadata` * @param channelId Sora に接続するためのチャネル ID * @param mediaOption 映像、音声に関するオプション @@ -48,19 +48,19 @@ import kotlin.concurrent.schedule * @param ignoreDisconnectWebSocket connect メッセージに含める `ignore_disconnect_websocket` */ class SoraMediaChannel @JvmOverloads constructor( - private val context: Context, - private val signalingEndpoint: String? = null, - private val signalingUrlCandidates: List = emptyList(), - private val channelId: String, - private val signalingMetadata: Any? = "", - private val mediaOption: SoraMediaOption, - private val timeoutSeconds: Long = DEFAULT_TIMEOUT_SECONDS, - private var listener: Listener?, - private val clientId: String? = null, - private val signalingNotifyMetadata: Any? = null, - private val peerConnectionOption: PeerConnectionOption = PeerConnectionOption(), - private val dataChannelSignaling: Boolean? = null, - private var ignoreDisconnectWebSocket: Boolean? = null + private val context: Context, + private val signalingEndpoint: String? = null, + private val signalingEndpointCandidates: List = emptyList(), + private val channelId: String, + private val signalingMetadata: Any? = "", + private val mediaOption: SoraMediaOption, + private val timeoutSeconds: Long = DEFAULT_TIMEOUT_SECONDS, + private var listener: Listener?, + private val clientId: String? = null, + private val signalingNotifyMetadata: Any? = null, + private val peerConnectionOption: PeerConnectionOption = PeerConnectionOption(), + private val dataChannelSignaling: Boolean? = null, + private var ignoreDisconnectWebSocket: Boolean? = null ) { companion object { private val TAG = SoraMediaChannel::class.simpleName @@ -69,7 +69,7 @@ class SoraMediaChannel @JvmOverloads constructor( } init { - if (signalingEndpoint == null && signalingUrlCandidates.isEmpty()) { + if (signalingEndpoint == null && signalingEndpointCandidates.isEmpty()) { throw IllegalArgumentException("Either signalingEndpoint or signalingUrlCandidates must be specified") } } @@ -597,7 +597,7 @@ class SoraMediaChannel @JvmOverloads constructor( private fun connectSignalingChannel(clientOfferSdp : SessionDescription?, redirectLocation: String? = null) { val endpoints = when { redirectLocation != null -> listOf(redirectLocation) - signalingUrlCandidates.isNotEmpty() -> signalingUrlCandidates + signalingEndpointCandidates.isNotEmpty() -> signalingEndpointCandidates else -> listOf(signalingEndpoint!!) } From 8055e6f2df58e4df2e2debd31cc2dcb7d761f5da Mon Sep 17 00:00:00 2001 From: enm10k Date: Wed, 24 Nov 2021 16:24:16 +0900 Subject: [PATCH 16/85] =?UTF-8?q?=E5=A4=89=E6=95=B0=E5=90=8D=E3=82=92?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E3=81=99=E3=82=8B=20s/redirecting/receivedRe?= =?UTF-8?q?directMessage/g?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sora/sdk/channel/signaling/SignalingChannel.kt | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt index b9857118..835ff2ff 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt @@ -66,8 +66,7 @@ class SignalingChannelImpl @JvmOverloads constructor( private var closing = false - // TODO: フラグ名に改善の余地あり? - private var redirecting = false + private var receivedRedirectMessage = false override fun connect() { SoraLogger.i(TAG, "[signaling:$role] endpoints=$endpoints") @@ -158,7 +157,7 @@ class SignalingChannelImpl @JvmOverloads constructor( client.dispatcher.executorService.shutdown() webSocket?.close(1000, null) - if (!redirecting) { + if (!receivedRedirectMessage) { listener?.onDisconnect() } listener = null @@ -264,7 +263,7 @@ class SignalingChannelImpl @JvmOverloads constructor( private fun onRedirectMessage(text: String) { SoraLogger.d(TAG, "[signaling:$role] <- redirect") - redirecting = true + receivedRedirectMessage = true val msg = MessageConverter.parseRedirectMessage(text) SoraLogger.d(TAG, "redirect to ${msg.location}") @@ -343,7 +342,7 @@ class SignalingChannelImpl @JvmOverloads constructor( } override fun onClosed(webSocket: WebSocket, code: Int, reason: String) { - if (redirecting || this@SignalingChannelImpl.webSocket != webSocket) { + if (receivedRedirectMessage || this@SignalingChannelImpl.webSocket != webSocket) { // WebSocket が SignalingChannelImpl で保持しているものと等しい場合のみ後続の処理を実行する return } @@ -361,7 +360,7 @@ class SignalingChannelImpl @JvmOverloads constructor( } override fun onClosing(webSocket: WebSocket, code: Int, reason: String) { - if (redirecting || this@SignalingChannelImpl.webSocket != webSocket) { + if (receivedRedirectMessage || this@SignalingChannelImpl.webSocket != webSocket) { // WebSocket が SignalingChannelImpl で保持しているものと等しい場合のみ後続の処理を実行する return } @@ -370,7 +369,7 @@ class SignalingChannelImpl @JvmOverloads constructor( } override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) { - if (redirecting || this@SignalingChannelImpl.webSocket != webSocket) { + if (receivedRedirectMessage || this@SignalingChannelImpl.webSocket != webSocket) { // WebSocket が SignalingChannelImpl で保持しているものと等しい場合のみ後続の処理を実行する return } From 7340bb08a91c865ef06470135f9055f9db72b8b7 Mon Sep 17 00:00:00 2001 From: enm10k Date: Wed, 24 Nov 2021 17:43:44 +0900 Subject: [PATCH 17/85] =?UTF-8?q?=E3=83=AD=E3=82=B0=E3=81=AE=20typo=20?= =?UTF-8?q?=E3=82=92=E4=BF=AE=E6=AD=A3=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt index 8b955b8d..3d344fd2 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt @@ -70,7 +70,7 @@ class SoraMediaChannel @JvmOverloads constructor( init { if (signalingEndpoint == null && signalingEndpointCandidates.isEmpty()) { - throw IllegalArgumentException("Either signalingEndpoint or signalingUrlCandidates must be specified") + throw IllegalArgumentException("Either signalingEndpoint or signalingEndpointCandidates must be specified") } } From ca7ef3bd11c52bfcd56dd29d66a167b665d50742 Mon Sep 17 00:00:00 2001 From: enm10k Date: Thu, 25 Nov 2021 11:42:35 +0900 Subject: [PATCH 18/85] =?UTF-8?q?synchronized=20=E3=81=AE=E6=8C=87?= =?UTF-8?q?=E5=AE=9A=E3=82=92=E8=A6=8B=E7=9B=B4=E3=81=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sdk/channel/signaling/SignalingChannel.kt | 51 +++++++++++-------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt index 835ff2ff..35d90fa0 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt @@ -59,8 +59,6 @@ class SignalingChannelImpl @JvmOverloads constructor( OkHttpClient.Builder().readTimeout(0, TimeUnit.MILLISECONDS).build() private var webSocket: WebSocket? = null - @Synchronized set - @Synchronized get private var webSocketCandidates = mutableListOf() @@ -281,20 +279,22 @@ class SignalingChannelImpl @JvmOverloads constructor( return } - if (this@SignalingChannelImpl.webSocket != null) { - return - } + synchronized (this) { + if (this@SignalingChannelImpl.webSocket != null) { + return + } - SoraLogger.i(TAG, "succeeded to connect with ${webSocket.request().url}") + SoraLogger.i(TAG, "succeeded to connect with ${webSocket.request().url}") - this@SignalingChannelImpl.webSocket = webSocket - for (candidate in this@SignalingChannelImpl.webSocketCandidates) { - if (candidate != webSocket) { - SoraLogger.d(TAG, "closing connection with ${candidate.request().url}") - candidate.cancel() + this@SignalingChannelImpl.webSocket = webSocket + for (candidate in this@SignalingChannelImpl.webSocketCandidates) { + if (candidate != webSocket) { + SoraLogger.d(TAG, "closing connection with ${candidate.request().url}") + candidate.cancel() + } } + this@SignalingChannelImpl.webSocketCandidates.clear() } - this@SignalingChannelImpl.webSocketCandidates.clear() listener?.onConnect() sendConnectMessage() @@ -342,10 +342,13 @@ class SignalingChannelImpl @JvmOverloads constructor( } override fun onClosed(webSocket: WebSocket, code: Int, reason: String) { - if (receivedRedirectMessage || this@SignalingChannelImpl.webSocket != webSocket) { - // WebSocket が SignalingChannelImpl で保持しているものと等しい場合のみ後続の処理を実行する - return + synchronized (this) { + if (receivedRedirectMessage || this@SignalingChannelImpl.webSocket != webSocket) { + // WebSocket が SignalingChannelImpl で保持しているものと等しい場合のみ後続の処理を実行する + return + } } + try { if (code == 1000) { SoraLogger.i(TAG, "[signaling:$role] @onClosed: reason = [${reason}], code = $code") @@ -360,19 +363,25 @@ class SignalingChannelImpl @JvmOverloads constructor( } override fun onClosing(webSocket: WebSocket, code: Int, reason: String) { - if (receivedRedirectMessage || this@SignalingChannelImpl.webSocket != webSocket) { - // WebSocket が SignalingChannelImpl で保持しているものと等しい場合のみ後続の処理を実行する - return + synchronized (this) { + if (receivedRedirectMessage || this@SignalingChannelImpl.webSocket != webSocket) { + // WebSocket が SignalingChannelImpl で保持しているものと等しい場合のみ後続の処理を実行する + return + } } + SoraLogger.d(TAG, "[signaling:$role] @onClosing") disconnect() } override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) { - if (receivedRedirectMessage || this@SignalingChannelImpl.webSocket != webSocket) { - // WebSocket が SignalingChannelImpl で保持しているものと等しい場合のみ後続の処理を実行する - return + synchronized (this) { + if (receivedRedirectMessage || this@SignalingChannelImpl.webSocket != webSocket) { + // WebSocket が SignalingChannelImpl で保持しているものと等しい場合のみ後続の処理を実行する + return + } } + try { response?.let { SoraLogger.i(TAG, "[signaling:$role] @onFailure: ${it.message}, $t") From ecd4fdb508ab060d3d77bea8bb2c4ff647af009a Mon Sep 17 00:00:00 2001 From: enm10k Date: Thu, 25 Nov 2021 11:49:54 +0900 Subject: [PATCH 19/85] =?UTF-8?q?synchronized=20=E3=81=AE=E6=8C=87?= =?UTF-8?q?=E5=AE=9A=E3=82=92=E8=A6=8B=E7=9B=B4=E3=81=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sora/sdk/channel/signaling/SignalingChannel.kt | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt index 35d90fa0..ace42fef 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt @@ -261,7 +261,9 @@ class SignalingChannelImpl @JvmOverloads constructor( private fun onRedirectMessage(text: String) { SoraLogger.d(TAG, "[signaling:$role] <- redirect") - receivedRedirectMessage = true + synchronized (this) { + receivedRedirectMessage = true + } val msg = MessageConverter.parseRedirectMessage(text) SoraLogger.d(TAG, "redirect to ${msg.location}") @@ -279,7 +281,7 @@ class SignalingChannelImpl @JvmOverloads constructor( return } - synchronized (this) { + synchronized (this@SignalingChannelImpl) { if (this@SignalingChannelImpl.webSocket != null) { return } @@ -342,7 +344,7 @@ class SignalingChannelImpl @JvmOverloads constructor( } override fun onClosed(webSocket: WebSocket, code: Int, reason: String) { - synchronized (this) { + synchronized (this@SignalingChannelImpl) { if (receivedRedirectMessage || this@SignalingChannelImpl.webSocket != webSocket) { // WebSocket が SignalingChannelImpl で保持しているものと等しい場合のみ後続の処理を実行する return @@ -363,7 +365,7 @@ class SignalingChannelImpl @JvmOverloads constructor( } override fun onClosing(webSocket: WebSocket, code: Int, reason: String) { - synchronized (this) { + synchronized (this@SignalingChannelImpl) { if (receivedRedirectMessage || this@SignalingChannelImpl.webSocket != webSocket) { // WebSocket が SignalingChannelImpl で保持しているものと等しい場合のみ後続の処理を実行する return @@ -375,7 +377,7 @@ class SignalingChannelImpl @JvmOverloads constructor( } override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) { - synchronized (this) { + synchronized (this@SignalingChannelImpl) { if (receivedRedirectMessage || this@SignalingChannelImpl.webSocket != webSocket) { // WebSocket が SignalingChannelImpl で保持しているものと等しい場合のみ後続の処理を実行する return From d740f78f59342fe8185ab83ee7eed761c5e929bc Mon Sep 17 00:00:00 2001 From: enm10k Date: Thu, 25 Nov 2021 13:18:31 +0900 Subject: [PATCH 20/85] =?UTF-8?q?receivedRedirectMessage=20=E3=81=AE?= =?UTF-8?q?=E3=83=AD=E3=83=83=E3=82=AF=E3=81=AF=E4=B8=8D=E8=A6=81=E3=81=A0?= =?UTF-8?q?=E3=81=A3=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt index ace42fef..edd9aaa4 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt @@ -261,9 +261,7 @@ class SignalingChannelImpl @JvmOverloads constructor( private fun onRedirectMessage(text: String) { SoraLogger.d(TAG, "[signaling:$role] <- redirect") - synchronized (this) { - receivedRedirectMessage = true - } + receivedRedirectMessage = true val msg = MessageConverter.parseRedirectMessage(text) SoraLogger.d(TAG, "redirect to ${msg.location}") From ab6d54e3ffd48835536a981d77df3ed9640bcc69 Mon Sep 17 00:00:00 2001 From: enm10k Date: Wed, 1 Dec 2021 11:51:58 +0900 Subject: [PATCH 21/85] =?UTF-8?q?=E8=A4=87=E6=95=B0=E3=81=82=E3=82=8B=20We?= =?UTF-8?q?bSocket=20=E3=81=AE=E6=8E=A5=E7=B6=9A=E3=81=8C=E3=80=81?= =?UTF-8?q?=E5=A4=B1=E6=95=97=E3=81=97=E3=81=A6=E3=81=8B=E3=82=89=E6=88=90?= =?UTF-8?q?=E5=8A=9F=E3=81=99=E3=82=8B=E3=83=91=E3=82=BF=E3=83=BC=E3=83=B3?= =?UTF-8?q?=E3=82=92=E8=80=83=E6=85=AE=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sdk/channel/signaling/SignalingChannel.kt | 151 +++++++++++------- 1 file changed, 95 insertions(+), 56 deletions(-) diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt index edd9aaa4..e2b37b1f 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt @@ -10,6 +10,7 @@ import okio.ByteString import org.webrtc.RTCStatsReport import org.webrtc.SessionDescription import java.util.concurrent.TimeUnit +import java.util.concurrent.atomic.AtomicBoolean interface SignalingChannel { @@ -58,18 +59,44 @@ class SignalingChannelImpl @JvmOverloads constructor( private val client = OkHttpClient.Builder().readTimeout(0, TimeUnit.MILLISECONDS).build() - private var webSocket: WebSocket? = null + private var ws: WebSocket? = null - private var webSocketCandidates = mutableListOf() + private var wsCandidates = mutableListOf() - private var closing = false + private var closing = AtomicBoolean(false) private var receivedRedirectMessage = false + // WebSocketListener の onClosed, onClosing, onFailure で使用する + private fun propagatesWebSocketTerminateEventToSignalingChannel(webSocket: WebSocket): Boolean { + // 接続状態になる可能性がなくなった WebSocket を webSocketCandidates から削除 + wsCandidates.remove(webSocket) + + // type: redirect を受信しているのでイベントは無視する + if (receivedRedirectMessage) { + return false + } + + // シグナリングに使う WebSocket (= ws) が未決定だが、 wsCandidates が残っているのでイベントは無視する + if (ws == null && wsCandidates.size != 0) { + return false + } + + // シグナリングに使用していない WebSocket のイベントは無視する + if (ws != null && ws != webSocket) { + return false + } + + return true + } + + override fun connect() { SoraLogger.i(TAG, "[signaling:$role] endpoints=$endpoints") - for (endpoint in endpoints) { - webSocketCandidates.add(connect(endpoint)) + synchronized (this) { + for (endpoint in endpoints) { + wsCandidates.add(connect(endpoint)) + } } } @@ -82,12 +109,12 @@ class SignalingChannelImpl @JvmOverloads constructor( override fun sendAnswer(sdp: String) { SoraLogger.d(TAG, "[signaling:$role] -> answer") - if (closing) { - SoraLogger.i(TAG, "signaling is closing") + if (closing.get()) { + SoraLogger.i(TAG, "signaling is closing.get()") return } - webSocket?.let { + ws?.let { val msg = MessageConverter.buildAnswerMessage(sdp) it.send(msg) } @@ -96,14 +123,14 @@ class SignalingChannelImpl @JvmOverloads constructor( override fun sendUpdateAnswer(sdp: String) { SoraLogger.d(TAG, "[signaling:$role] -> re-answer(update)") - if (closing) { - SoraLogger.i(TAG, "signaling is closing") + if (closing.get()) { + SoraLogger.i(TAG, "signaling is closing.get()") return } SoraLogger.d(TAG, sdp) - webSocket?.let { + ws?.let { val msg = MessageConverter.buildUpdateAnswerMessage(sdp) it.send(msg) } @@ -112,14 +139,14 @@ class SignalingChannelImpl @JvmOverloads constructor( override fun sendReAnswer(sdp: String) { SoraLogger.d(TAG, "[signaling:$role] -> re-answer") - if (closing) { - SoraLogger.i(TAG, "signaling is closing") + if (closing.get()) { + SoraLogger.i(TAG, "signaling is closing.get()") return } SoraLogger.d(TAG, sdp) - webSocket?.let { + ws?.let { val msg = MessageConverter.buildReAnswerMessage(sdp) it.send(msg) } @@ -128,47 +155,55 @@ class SignalingChannelImpl @JvmOverloads constructor( override fun sendCandidate(sdp: String) { SoraLogger.d(TAG, "[signaling:$role] -> candidate") - if (closing) { - SoraLogger.i(TAG, "signaling is closing") + if (closing.get()) { + SoraLogger.i(TAG, "signaling is closing.get()") return } SoraLogger.d(TAG, sdp) - webSocket?.let { + ws?.let { val msg = MessageConverter.buildCandidateMessage(sdp) it.send(msg) } } override fun sendDisconnectMessage() { - SoraLogger.d(TAG, "[signaling:$role] -> type:disconnect, webSocket=$webSocket") - webSocket?.let { + SoraLogger.d(TAG, "[signaling:$role] -> type:disconnect, webSocket=$ws") + ws?.let { val disconnectMessage = MessageConverter.buildDisconnectMessage() it.send(disconnectMessage) } } override fun disconnect() { - if (!closing) { - closing = true - client.dispatcher.executorService.shutdown() - webSocket?.close(1000, null) + if (closing.get()) { + return + } - if (!receivedRedirectMessage) { - listener?.onDisconnect() - } - listener = null + closing.set(true) + client.dispatcher.executorService.shutdown() + ws?.close(1000, null) + + // onDisconnect を synchronized の中で実行したくないため変数を追加 + var shouldExecuteOnDisconnect: Boolean + synchronized (this) { + shouldExecuteOnDisconnect = !receivedRedirectMessage } + + if (!shouldExecuteOnDisconnect) { + listener?.onDisconnect() + } + listener = null } private fun sendConnectMessage() { - if (closing) { - SoraLogger.i(TAG, "signaling is closing") + if (closing.get()) { + SoraLogger.i(TAG, "signaling is closing.get()") return } - webSocket?.let { + ws?.let { SoraLogger.d(TAG, "[signaling:$role] -> connect") val message = MessageConverter.buildConnectMessage( role = role, @@ -252,7 +287,7 @@ class SignalingChannelImpl @JvmOverloads constructor( } private fun sendPongMessage(report: RTCStatsReport?) { - webSocket?.let { ws -> + ws?.let { ws -> val msg = MessageConverter.buildPongMessage(report) SoraLogger.d(TAG, msg) ws.send(msg) @@ -261,7 +296,9 @@ class SignalingChannelImpl @JvmOverloads constructor( private fun onRedirectMessage(text: String) { SoraLogger.d(TAG, "[signaling:$role] <- redirect") - receivedRedirectMessage = true + synchronized (this) { + receivedRedirectMessage = true + } val msg = MessageConverter.parseRedirectMessage(text) SoraLogger.d(TAG, "redirect to ${msg.location}") @@ -274,26 +311,26 @@ class SignalingChannelImpl @JvmOverloads constructor( try { SoraLogger.d(TAG, "[signaling:$role] @onOpen") - if (closing) { - SoraLogger.i(TAG, "signaling is closing") + if (closing.get()) { + SoraLogger.i(TAG, "signaling is closing.get()") return } synchronized (this@SignalingChannelImpl) { - if (this@SignalingChannelImpl.webSocket != null) { + if (ws != null) { return } SoraLogger.i(TAG, "succeeded to connect with ${webSocket.request().url}") - this@SignalingChannelImpl.webSocket = webSocket - for (candidate in this@SignalingChannelImpl.webSocketCandidates) { + ws = webSocket + for (candidate in this@SignalingChannelImpl.wsCandidates) { if (candidate != webSocket) { - SoraLogger.d(TAG, "closing connection with ${candidate.request().url}") + SoraLogger.d(TAG, "closing.get() connection with ${candidate.request().url}") candidate.cancel() } } - this@SignalingChannelImpl.webSocketCandidates.clear() + this@SignalingChannelImpl.wsCandidates.clear() } listener?.onConnect() @@ -308,8 +345,8 @@ class SignalingChannelImpl @JvmOverloads constructor( SoraLogger.d(TAG, "[signaling:$role] @onMessage(text)") SoraLogger.d(TAG, text) - if (closing) { - SoraLogger.i(TAG, "signaling is closing") + if (closing.get()) { + SoraLogger.i(TAG, "signaling is closing.get()") return } @@ -342,18 +379,21 @@ class SignalingChannelImpl @JvmOverloads constructor( } override fun onClosed(webSocket: WebSocket, code: Int, reason: String) { + if (code == 1000) { + SoraLogger.i(TAG, "[signaling:$role] @onClosed: reason = [${reason}], code = $code") + } else { + SoraLogger.w(TAG, "[signaling:$role] @onClosed: reason = [${reason}], code = $code") + } + synchronized (this@SignalingChannelImpl) { - if (receivedRedirectMessage || this@SignalingChannelImpl.webSocket != webSocket) { - // WebSocket が SignalingChannelImpl で保持しているものと等しい場合のみ後続の処理を実行する + if (!propagatesWebSocketTerminateEventToSignalingChannel(webSocket)) { return } } try { - if (code == 1000) { - SoraLogger.i(TAG, "[signaling:$role] @onClosed: reason = [${reason}], code = $code") - } else { - SoraLogger.w(TAG, "[signaling:$role] @onClosed: reason = [${reason}], code = $code") + if (code != 1000) { + listener?.onError(SoraErrorReason.SIGNALING_FAILURE) } disconnect() @@ -363,30 +403,29 @@ class SignalingChannelImpl @JvmOverloads constructor( } override fun onClosing(webSocket: WebSocket, code: Int, reason: String) { + SoraLogger.d(TAG, "[signaling:$role] @onClosing") + synchronized (this@SignalingChannelImpl) { - if (receivedRedirectMessage || this@SignalingChannelImpl.webSocket != webSocket) { - // WebSocket が SignalingChannelImpl で保持しているものと等しい場合のみ後続の処理を実行する + if (!propagatesWebSocketTerminateEventToSignalingChannel(webSocket)) { return } } - SoraLogger.d(TAG, "[signaling:$role] @onClosing") disconnect() } override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) { synchronized (this@SignalingChannelImpl) { - if (receivedRedirectMessage || this@SignalingChannelImpl.webSocket != webSocket) { - // WebSocket が SignalingChannelImpl で保持しているものと等しい場合のみ後続の処理を実行する + response?.let { + SoraLogger.i(TAG, "[signaling:$role] @onFailure: ${it.message}, $t") + } ?: SoraLogger.i(TAG, "[signaling:$role] @onFailure: $t") + + if (!propagatesWebSocketTerminateEventToSignalingChannel(webSocket)) { return } } try { - response?.let { - SoraLogger.i(TAG, "[signaling:$role] @onFailure: ${it.message}, $t") - } ?: SoraLogger.i(TAG, "[signaling:$role] @onFailure: $t") - listener?.onError(SoraErrorReason.SIGNALING_FAILURE) disconnect() } catch (e: Exception) { From 96cc713f312e03a3ae4eee2ae4a09c308d243218 Mon Sep 17 00:00:00 2001 From: enm10k Date: Wed, 1 Dec 2021 15:45:06 +0900 Subject: [PATCH 22/85] wip --- .../sdk/channel/signaling/SignalingChannel.kt | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt index e2b37b1f..5638a544 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt @@ -59,6 +59,14 @@ class SignalingChannelImpl @JvmOverloads constructor( private val client = OkHttpClient.Builder().readTimeout(0, TimeUnit.MILLISECONDS).build() + /* + 接続中 (= type: connect を送信する前) は複数の WebSocket が存在する可能性があるため、 + WebSocketListener から参照される以下の変数はスレッド・セーフである必要がある + - ws + - wsCandidates + - receivedRedirectMessage + - closing + */ private var ws: WebSocket? = null private var wsCandidates = mutableListOf() @@ -72,12 +80,11 @@ class SignalingChannelImpl @JvmOverloads constructor( // 接続状態になる可能性がなくなった WebSocket を webSocketCandidates から削除 wsCandidates.remove(webSocket) - // type: redirect を受信しているのでイベントは無視する if (receivedRedirectMessage) { return false } - // シグナリングに使う WebSocket (= ws) が未決定だが、 wsCandidates が残っているのでイベントは無視する + // ws が未決定だが、 wsCandidates が残っているのでイベントは無視する if (ws == null && wsCandidates.size != 0) { return false } @@ -191,6 +198,7 @@ class SignalingChannelImpl @JvmOverloads constructor( shouldExecuteOnDisconnect = !receivedRedirectMessage } + // type: redirect を受信している場合は onDisconnect を発火させない if (!shouldExecuteOnDisconnect) { listener?.onDisconnect() } @@ -326,7 +334,7 @@ class SignalingChannelImpl @JvmOverloads constructor( ws = webSocket for (candidate in this@SignalingChannelImpl.wsCandidates) { if (candidate != webSocket) { - SoraLogger.d(TAG, "closing.get() connection with ${candidate.request().url}") + SoraLogger.d(TAG, "closing.get connection with ${candidate.request().url}") candidate.cancel() } } @@ -387,6 +395,7 @@ class SignalingChannelImpl @JvmOverloads constructor( synchronized (this@SignalingChannelImpl) { if (!propagatesWebSocketTerminateEventToSignalingChannel(webSocket)) { + SoraLogger.d(TAG, "[signaling:$role] @onClosed: skipped") return } } @@ -407,6 +416,7 @@ class SignalingChannelImpl @JvmOverloads constructor( synchronized (this@SignalingChannelImpl) { if (!propagatesWebSocketTerminateEventToSignalingChannel(webSocket)) { + SoraLogger.d(TAG, "[signaling:$role] @onClosing: skipped") return } } @@ -415,12 +425,13 @@ class SignalingChannelImpl @JvmOverloads constructor( } override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) { - synchronized (this@SignalingChannelImpl) { - response?.let { - SoraLogger.i(TAG, "[signaling:$role] @onFailure: ${it.message}, $t") - } ?: SoraLogger.i(TAG, "[signaling:$role] @onFailure: $t") + response?.let { + SoraLogger.i(TAG, "[signaling:$role] @onFailure: ${it.message}, $t") + } ?: SoraLogger.i(TAG, "[signaling:$role] @onFailure: $t") + synchronized (this@SignalingChannelImpl) { if (!propagatesWebSocketTerminateEventToSignalingChannel(webSocket)) { + SoraLogger.d(TAG, "[signaling:$role] @onFailure: skipped") return } } From a226903720729c7f0a341cce6fa397d7b9e40b37 Mon Sep 17 00:00:00 2001 From: enm10k Date: Wed, 1 Dec 2021 17:41:22 +0900 Subject: [PATCH 23/85] =?UTF-8?q?type:=20redirect=20=E5=8F=97=E4=BF=A1?= =?UTF-8?q?=E6=99=82=E3=81=AB=20SignalingChannel.Listener=20=E3=81=AE=20on?= =?UTF-8?q?Disconnect=20=E3=81=8C=E8=AA=A4=E3=81=A3=E3=81=A6=E7=99=BA?= =?UTF-8?q?=E7=81=AB=E3=81=97=E3=81=A6=E3=81=84=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt index 5638a544..e07044a8 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt @@ -199,7 +199,7 @@ class SignalingChannelImpl @JvmOverloads constructor( } // type: redirect を受信している場合は onDisconnect を発火させない - if (!shouldExecuteOnDisconnect) { + if (shouldExecuteOnDisconnect) { listener?.onDisconnect() } listener = null From 79dec5bed3509112fa0dfae4abaf4ac43bdf8d23 Mon Sep 17 00:00:00 2001 From: enm10k Date: Thu, 2 Dec 2021 11:09:57 +0900 Subject: [PATCH 24/85] =?UTF-8?q?receivedRedirectMessage=20=E3=82=92=20Ato?= =?UTF-8?q?micBoolean=20=E3=81=AB=E3=81=99=E3=82=8B=20&=20=E3=82=B3?= =?UTF-8?q?=E3=83=A1=E3=83=B3=E3=83=88=E3=81=A8=E3=83=AD=E3=82=B0=E3=82=92?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sdk/channel/signaling/SignalingChannel.kt | 44 ++++++++----------- 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt index e07044a8..0e53045c 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt @@ -73,23 +73,25 @@ class SignalingChannelImpl @JvmOverloads constructor( private var closing = AtomicBoolean(false) - private var receivedRedirectMessage = false + private var receivedRedirectMessage = AtomicBoolean(false) // WebSocketListener の onClosed, onClosing, onFailure で使用する private fun propagatesWebSocketTerminateEventToSignalingChannel(webSocket: WebSocket): Boolean { - // 接続状態になる可能性がなくなった WebSocket を webSocketCandidates から削除 + // 接続状態になる可能性がなくなった WebSocket を wsCandidates から削除 wsCandidates.remove(webSocket) - if (receivedRedirectMessage) { - return false - } + /* + ここで receivedRedirectMessage をチェックすることを検討したが、不要だという結論に至った + type: redirect を既に受信している場合、 onDisconnect が発火しない限り、 + SignalingChannelImpl の disconnect が実行されても問題ない + */ - // ws が未決定だが、 wsCandidates が残っているのでイベントは無視する + // 採用する WebSocket が決まっていないが、 wsCandidates が残っているのでイベントは無視する if (ws == null && wsCandidates.size != 0) { return false } - // シグナリングに使用していない WebSocket のイベントは無視する + // 採用されなかった WebSocket を始末する際のイベントを無視する if (ws != null && ws != webSocket) { return false } @@ -117,7 +119,7 @@ class SignalingChannelImpl @JvmOverloads constructor( SoraLogger.d(TAG, "[signaling:$role] -> answer") if (closing.get()) { - SoraLogger.i(TAG, "signaling is closing.get()") + SoraLogger.i(TAG, "signaling is closing") return } @@ -131,7 +133,7 @@ class SignalingChannelImpl @JvmOverloads constructor( SoraLogger.d(TAG, "[signaling:$role] -> re-answer(update)") if (closing.get()) { - SoraLogger.i(TAG, "signaling is closing.get()") + SoraLogger.i(TAG, "signaling is closing") return } @@ -147,7 +149,7 @@ class SignalingChannelImpl @JvmOverloads constructor( SoraLogger.d(TAG, "[signaling:$role] -> re-answer") if (closing.get()) { - SoraLogger.i(TAG, "signaling is closing.get()") + SoraLogger.i(TAG, "signaling is closing") return } @@ -163,7 +165,7 @@ class SignalingChannelImpl @JvmOverloads constructor( SoraLogger.d(TAG, "[signaling:$role] -> candidate") if (closing.get()) { - SoraLogger.i(TAG, "signaling is closing.get()") + SoraLogger.i(TAG, "signaling is closing") return } @@ -192,14 +194,8 @@ class SignalingChannelImpl @JvmOverloads constructor( client.dispatcher.executorService.shutdown() ws?.close(1000, null) - // onDisconnect を synchronized の中で実行したくないため変数を追加 - var shouldExecuteOnDisconnect: Boolean - synchronized (this) { - shouldExecuteOnDisconnect = !receivedRedirectMessage - } - // type: redirect を受信している場合は onDisconnect を発火させない - if (shouldExecuteOnDisconnect) { + if (!receivedRedirectMessage.get()) { listener?.onDisconnect() } listener = null @@ -207,7 +203,7 @@ class SignalingChannelImpl @JvmOverloads constructor( private fun sendConnectMessage() { if (closing.get()) { - SoraLogger.i(TAG, "signaling is closing.get()") + SoraLogger.i(TAG, "signaling is closing") return } @@ -304,9 +300,7 @@ class SignalingChannelImpl @JvmOverloads constructor( private fun onRedirectMessage(text: String) { SoraLogger.d(TAG, "[signaling:$role] <- redirect") - synchronized (this) { - receivedRedirectMessage = true - } + receivedRedirectMessage.set(true) val msg = MessageConverter.parseRedirectMessage(text) SoraLogger.d(TAG, "redirect to ${msg.location}") @@ -320,7 +314,7 @@ class SignalingChannelImpl @JvmOverloads constructor( SoraLogger.d(TAG, "[signaling:$role] @onOpen") if (closing.get()) { - SoraLogger.i(TAG, "signaling is closing.get()") + SoraLogger.i(TAG, "signaling is closing") return } @@ -334,7 +328,7 @@ class SignalingChannelImpl @JvmOverloads constructor( ws = webSocket for (candidate in this@SignalingChannelImpl.wsCandidates) { if (candidate != webSocket) { - SoraLogger.d(TAG, "closing.get connection with ${candidate.request().url}") + SoraLogger.d(TAG, "closing connection with ${candidate.request().url}") candidate.cancel() } } @@ -354,7 +348,7 @@ class SignalingChannelImpl @JvmOverloads constructor( SoraLogger.d(TAG, text) if (closing.get()) { - SoraLogger.i(TAG, "signaling is closing.get()") + SoraLogger.i(TAG, "signaling is closing") return } From 0680f245e0a77799fb1406d3cf85b86ecf4cdc26 Mon Sep 17 00:00:00 2001 From: enm10k Date: Thu, 2 Dec 2021 11:32:52 +0900 Subject: [PATCH 25/85] =?UTF-8?q?=E3=82=B3=E3=82=B9=E3=83=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt index 0e53045c..1055624c 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt @@ -326,13 +326,13 @@ class SignalingChannelImpl @JvmOverloads constructor( SoraLogger.i(TAG, "succeeded to connect with ${webSocket.request().url}") ws = webSocket - for (candidate in this@SignalingChannelImpl.wsCandidates) { + for (candidate in wsCandidates) { if (candidate != webSocket) { SoraLogger.d(TAG, "closing connection with ${candidate.request().url}") candidate.cancel() } } - this@SignalingChannelImpl.wsCandidates.clear() + wsCandidates.clear() } listener?.onConnect() From b2d585307d3a3484a2e1cc8ba4a8296645876888 Mon Sep 17 00:00:00 2001 From: enm10k Date: Thu, 2 Dec 2021 14:02:46 +0900 Subject: [PATCH 26/85] =?UTF-8?q?CHANGES.md=20=E3=81=AE=E3=82=A4=E3=83=B3?= =?UTF-8?q?=E3=83=87=E3=83=B3=E3=83=88=E3=82=92=E4=BF=AE=E6=AD=A3=E3=81=99?= =?UTF-8?q?=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGES.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index ad29509a..fa0c68c1 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -11,14 +11,14 @@ ## develop - - [UPDATE] dokka を 1.5.31 に上げる +- [UPDATE] dokka を 1.5.31 に上げる - @miosakuma ## 2021.3 - - [UPDATE] libwebrtc を 93.4577.8.2 に上げる +- [UPDATE] libwebrtc を 93.4577.8.2 に上げる - @miosakuma - - [FIX] stats メッセージに含まれる統計情報のフォーマットを修正する +- [FIX] stats メッセージに含まれる統計情報のフォーマットを修正する - @enm10k ## 2021.2 From 29ff63da0a9b77ecb586118d7138af5eb7b555e3 Mon Sep 17 00:00:00 2001 From: enm10k Date: Thu, 2 Dec 2021 14:07:15 +0900 Subject: [PATCH 27/85] =?UTF-8?q?README.md=20=E3=82=92=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGES.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index fa0c68c1..80b75d12 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -11,6 +11,10 @@ ## develop +- [UPDATE] 複数シグナリング URL の指定に対応する + - @enm10k +- [UPDATE] redirect メッセージに対応する + - @enm10k - [UPDATE] dokka を 1.5.31 に上げる - @miosakuma From 0f0bf8da33d792322d0f70b64d90b8f3b433856a Mon Sep 17 00:00:00 2001 From: enm10k Date: Thu, 2 Dec 2021 15:00:01 +0900 Subject: [PATCH 28/85] =?UTF-8?q?propagatesWebSocketTerminateEventToSignal?= =?UTF-8?q?ingChannel=20=E3=81=AB=20synchronized=20=E3=82=92=E3=81=A4?= =?UTF-8?q?=E3=81=91=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sdk/channel/signaling/SignalingChannel.kt | 25 ++++++++----------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt index 1055624c..b92e390d 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt @@ -76,6 +76,7 @@ class SignalingChannelImpl @JvmOverloads constructor( private var receivedRedirectMessage = AtomicBoolean(false) // WebSocketListener の onClosed, onClosing, onFailure で使用する + @Synchronized private fun propagatesWebSocketTerminateEventToSignalingChannel(webSocket: WebSocket): Boolean { // 接続状態になる可能性がなくなった WebSocket を wsCandidates から削除 wsCandidates.remove(webSocket) @@ -387,11 +388,9 @@ class SignalingChannelImpl @JvmOverloads constructor( SoraLogger.w(TAG, "[signaling:$role] @onClosed: reason = [${reason}], code = $code") } - synchronized (this@SignalingChannelImpl) { - if (!propagatesWebSocketTerminateEventToSignalingChannel(webSocket)) { - SoraLogger.d(TAG, "[signaling:$role] @onClosed: skipped") - return - } + if (!propagatesWebSocketTerminateEventToSignalingChannel(webSocket)) { + SoraLogger.d(TAG, "[signaling:$role] @onClosed: skipped") + return } try { @@ -408,11 +407,9 @@ class SignalingChannelImpl @JvmOverloads constructor( override fun onClosing(webSocket: WebSocket, code: Int, reason: String) { SoraLogger.d(TAG, "[signaling:$role] @onClosing") - synchronized (this@SignalingChannelImpl) { - if (!propagatesWebSocketTerminateEventToSignalingChannel(webSocket)) { - SoraLogger.d(TAG, "[signaling:$role] @onClosing: skipped") - return - } + if (!propagatesWebSocketTerminateEventToSignalingChannel(webSocket)) { + SoraLogger.d(TAG, "[signaling:$role] @onClosing: skipped") + return } disconnect() @@ -423,11 +420,9 @@ class SignalingChannelImpl @JvmOverloads constructor( SoraLogger.i(TAG, "[signaling:$role] @onFailure: ${it.message}, $t") } ?: SoraLogger.i(TAG, "[signaling:$role] @onFailure: $t") - synchronized (this@SignalingChannelImpl) { - if (!propagatesWebSocketTerminateEventToSignalingChannel(webSocket)) { - SoraLogger.d(TAG, "[signaling:$role] @onFailure: skipped") - return - } + if (!propagatesWebSocketTerminateEventToSignalingChannel(webSocket)) { + SoraLogger.d(TAG, "[signaling:$role] @onFailure: skipped") + return } try { From 42e5201d8ec7a8ce97a12c86dd44a6bdb8b8330d Mon Sep 17 00:00:00 2001 From: enm10k Date: Thu, 2 Dec 2021 15:08:58 +0900 Subject: [PATCH 29/85] =?UTF-8?q?signalingEndpoint=20=E3=81=A8=20signaling?= =?UTF-8?q?EndpointCandidates=20=E3=81=8C=E4=B8=A1=E6=96=B9=E6=8C=87?= =?UTF-8?q?=E5=AE=9A=E3=81=95=E3=82=8C=E3=81=A6=E3=81=84=E3=82=8B=E5=A0=B4?= =?UTF-8?q?=E5=90=88=E3=82=82=E4=BE=8B=E5=A4=96=E3=82=92=E6=8A=95=E3=81=92?= =?UTF-8?q?=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt index 3d344fd2..9b71bc5a 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt @@ -69,7 +69,8 @@ class SoraMediaChannel @JvmOverloads constructor( } init { - if (signalingEndpoint == null && signalingEndpointCandidates.isEmpty()) { + if ((signalingEndpoint == null && signalingEndpointCandidates.isEmpty()) + || (signalingEndpoint != null && signalingEndpointCandidates.isNotEmpty())) { throw IllegalArgumentException("Either signalingEndpoint or signalingEndpointCandidates must be specified") } } From 86e043292c0b33ec1279e318134fbcaa264ff9b9 Mon Sep 17 00:00:00 2001 From: SUZUKI Tetsuya Date: Thu, 2 Dec 2021 15:14:41 +0900 Subject: [PATCH 30/85] =?UTF-8?q?actions/setup-java=20=E3=81=AE=E3=83=90?= =?UTF-8?q?=E3=83=BC=E3=82=B8=E3=83=A7=E3=83=B3=E3=82=92=E4=B8=8A=E3=81=92?= =?UTF-8?q?=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1834e4ac..caf9deba 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,10 +17,11 @@ jobs: steps: - uses: actions/checkout@v2 - - name: set up JDK 1.8 - uses: actions/setup-java@v1 + - name: Set up JDK + uses: actions/setup-java@v2 with: - java-version: 1.8 + distribution: 'temurin' + java-version: '17' - name: Copy gradle.properties run: cp gradle.properties.example gradle.properties - name: Build with Gradle From 71d132a02952cf5353e5d8517bbc6d60ef4e2687 Mon Sep 17 00:00:00 2001 From: SUZUKI Tetsuya Date: Thu, 2 Dec 2021 15:29:26 +0900 Subject: [PATCH 31/85] =?UTF-8?q?JDK=20=E3=82=92=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index caf9deba..a7bb2727 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -20,8 +20,8 @@ jobs: - name: Set up JDK uses: actions/setup-java@v2 with: - distribution: 'temurin' - java-version: '17' + distribution: 'adopt' + java-version: '11' - name: Copy gradle.properties run: cp gradle.properties.example gradle.properties - name: Build with Gradle From 9ff4da12b16a695af78601f6d845f821eb26585d Mon Sep 17 00:00:00 2001 From: enm10k Date: Thu, 2 Dec 2021 15:36:15 +0900 Subject: [PATCH 32/85] =?UTF-8?q?=E4=B8=8D=E8=A6=81=E3=81=AA=20var=20?= =?UTF-8?q?=E3=82=92=20val=20=E3=81=AB=E4=BF=AE=E6=AD=A3=E3=81=99=E3=82=8B?= =?UTF-8?q?=20&=20propagatesWebSocketTerminateEventToSignalingChannel=20?= =?UTF-8?q?=E3=82=92=E5=AE=9A=E7=BE=A9=E3=81=99=E3=82=8B=E4=BD=8D=E7=BD=AE?= =?UTF-8?q?=E3=82=92=20WebSocketListener=20=E3=81=AE=E8=BF=91=E3=81=8F?= =?UTF-8?q?=E3=81=AB=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sdk/channel/signaling/SignalingChannel.kt | 57 +++++++++---------- 1 file changed, 28 insertions(+), 29 deletions(-) diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt index b92e390d..44d060c7 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt @@ -69,37 +69,11 @@ class SignalingChannelImpl @JvmOverloads constructor( */ private var ws: WebSocket? = null - private var wsCandidates = mutableListOf() + private val wsCandidates = mutableListOf() - private var closing = AtomicBoolean(false) - - private var receivedRedirectMessage = AtomicBoolean(false) - - // WebSocketListener の onClosed, onClosing, onFailure で使用する - @Synchronized - private fun propagatesWebSocketTerminateEventToSignalingChannel(webSocket: WebSocket): Boolean { - // 接続状態になる可能性がなくなった WebSocket を wsCandidates から削除 - wsCandidates.remove(webSocket) - - /* - ここで receivedRedirectMessage をチェックすることを検討したが、不要だという結論に至った - type: redirect を既に受信している場合、 onDisconnect が発火しない限り、 - SignalingChannelImpl の disconnect が実行されても問題ない - */ - - // 採用する WebSocket が決まっていないが、 wsCandidates が残っているのでイベントは無視する - if (ws == null && wsCandidates.size != 0) { - return false - } - - // 採用されなかった WebSocket を始末する際のイベントを無視する - if (ws != null && ws != webSocket) { - return false - } - - return true - } + private val closing = AtomicBoolean(false) + private val receivedRedirectMessage = AtomicBoolean(false) override fun connect() { SoraLogger.i(TAG, "[signaling:$role] endpoints=$endpoints") @@ -308,6 +282,31 @@ class SignalingChannelImpl @JvmOverloads constructor( listener?.onRedirect(msg.location) } + // WebSocketListener の onClosed, onClosing, onFailure で使用する + @Synchronized + private fun propagatesWebSocketTerminateEventToSignalingChannel(webSocket: WebSocket): Boolean { + // 接続状態になる可能性がなくなった WebSocket を wsCandidates から削除 + wsCandidates.remove(webSocket) + + /* + ここで receivedRedirectMessage をチェックすることを検討したが、不要だという結論に至った + type: redirect を既に受信している場合、 onDisconnect が発火しない限り、 + SignalingChannelImpl の disconnect が実行されても問題ない + */ + + // 採用する WebSocket が決まっていないが、 wsCandidates が残っているのでイベントは無視する + if (ws == null && wsCandidates.size != 0) { + return false + } + + // 採用されなかった WebSocket を始末する際のイベントを無視する + if (ws != null && ws != webSocket) { + return false + } + + return true + } + private val webSocketListener = object : WebSocketListener() { override fun onOpen(webSocket: WebSocket, response: Response) { From fa7e0c79c8c71e2f40f10c4a97c300db58d7750e Mon Sep 17 00:00:00 2001 From: SUZUKI Tetsuya Date: Thu, 2 Dec 2021 15:40:21 +0900 Subject: [PATCH 33/85] =?UTF-8?q?=E4=B8=80=E5=9B=9E=E3=81=AE=E3=83=93?= =?UTF-8?q?=E3=83=AB=E3=83=89=E3=81=AE=E3=81=9F=E3=82=81=E3=81=AB=E3=83=87?= =?UTF-8?q?=E3=83=BC=E3=83=A2=E3=83=B3=E3=81=AF=E4=B8=8D=E8=A6=81=E3=81=AA?= =?UTF-8?q?=E3=81=AE=E3=81=A7=E3=82=AA=E3=83=97=E3=82=B7=E3=83=A7=E3=83=B3?= =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a7bb2727..587bd448 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -25,4 +25,4 @@ jobs: - name: Copy gradle.properties run: cp gradle.properties.example gradle.properties - name: Build with Gradle - run: ./gradlew build + run: ./gradlew build --no-daemon From a3101439e912882dc814873fbcf7b72efd9b6466 Mon Sep 17 00:00:00 2001 From: SUZUKI Tetsuya Date: Thu, 2 Dec 2021 15:44:05 +0900 Subject: [PATCH 34/85] =?UTF-8?q?Gradle=20=E3=81=AE=E5=AE=9F=E8=A1=8C?= =?UTF-8?q?=E7=B5=90=E6=9E=9C=E3=82=92=E3=82=AD=E3=83=A3=E3=83=83=E3=82=B7?= =?UTF-8?q?=E3=83=A5=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 587bd448..0b091b61 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -22,6 +22,7 @@ jobs: with: distribution: 'adopt' java-version: '11' + cache: 'gradle' - name: Copy gradle.properties run: cp gradle.properties.example gradle.properties - name: Build with Gradle From 4c5ac7bc9d2001e615d6ef6ba12ba500acdee075 Mon Sep 17 00:00:00 2001 From: enm10k Date: Thu, 2 Dec 2021 16:11:27 +0900 Subject: [PATCH 35/85] =?UTF-8?q?=E6=8E=92=E4=BB=96=E5=88=B6=E5=BE=A1?= =?UTF-8?q?=E3=81=AB=E3=81=A4=E3=81=84=E3=81=A6=E3=81=AE=E3=82=B3=E3=83=A1?= =?UTF-8?q?=E3=83=B3=E3=83=88=E3=82=92=E6=94=B9=E5=96=84=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt index 44d060c7..ca206ff2 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt @@ -60,8 +60,9 @@ class SignalingChannelImpl @JvmOverloads constructor( OkHttpClient.Builder().readTimeout(0, TimeUnit.MILLISECONDS).build() /* - 接続中 (= type: connect を送信する前) は複数の WebSocket が存在する可能性があるため、 - WebSocketListener から参照される以下の変数はスレッド・セーフである必要がある + 接続中 (= type: connect を送信する前) は複数の WebSocket が存在する可能性がある + その場合、以下の変数は WebSocketListener 及びそこから呼び出される SignalingChannelImpl の + メソッドから同時にアクセスされる可能性があるため、排他制御が必要となる - ws - wsCandidates - receivedRedirectMessage From d12617bfacfdd083ee2646aa23a7234d408b6964 Mon Sep 17 00:00:00 2001 From: enm10k Date: Thu, 2 Dec 2021 16:26:08 +0900 Subject: [PATCH 36/85] =?UTF-8?q?=E3=82=B3=E3=83=A1=E3=83=B3=E3=83=88?= =?UTF-8?q?=E3=82=92=E4=BF=AE=E6=AD=A3=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt index ca206ff2..85521e34 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt @@ -62,11 +62,14 @@ class SignalingChannelImpl @JvmOverloads constructor( /* 接続中 (= type: connect を送信する前) は複数の WebSocket が存在する可能性がある その場合、以下の変数は WebSocketListener 及びそこから呼び出される SignalingChannelImpl の - メソッドから同時にアクセスされる可能性があるため、排他制御が必要となる + メソッドから同時にアクセスされる可能性があるため、スレッドセーフである必要がある - ws - wsCandidates - receivedRedirectMessage - closing + + ws と wsCandidates については両方を同時に更新するため、このクラスのインスタンスで排他制御する + receivedRedirectMessage と closing には上記のような要件がないため、 AtomicBoolean を使う */ private var ws: WebSocket? = null From eda60eeba4ab2bd95f3bdd34dfb008852c3bbbb7 Mon Sep 17 00:00:00 2001 From: SUZUKI Tetsuya Date: Thu, 2 Dec 2021 16:56:45 +0900 Subject: [PATCH 37/85] =?UTF-8?q?ktlint=20=E3=83=97=E3=83=A9=E3=82=B0?= =?UTF-8?q?=E3=82=A4=E3=83=B3=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 1 + sora-android-sdk/build.gradle | 1 + 2 files changed, 2 insertions(+) diff --git a/build.gradle b/build.gradle index b51a2243..b8ba4e44 100644 --- a/build.gradle +++ b/build.gradle @@ -22,6 +22,7 @@ buildscript { classpath "org.jetbrains.dokka:dokka-gradle-plugin:${dokka_version}" classpath "com.github.ben-manes:gradle-versions-plugin:0.38.0" + classpath "org.jlleitschuh.gradle:ktlint-gradle:10.2.0" } } diff --git a/sora-android-sdk/build.gradle b/sora-android-sdk/build.gradle index d66459dd..3b51a0bf 100644 --- a/sora-android-sdk/build.gradle +++ b/sora-android-sdk/build.gradle @@ -3,6 +3,7 @@ apply plugin: 'com.github.dcendents.android-maven' apply plugin: 'kotlin-android' apply plugin: 'org.jetbrains.dokka' +apply plugin: 'org.jlleitschuh.gradle.ktlint' group = 'com.github.shiguredo' From d542175e10693d0e875dcc600db5ba8ca9370aed Mon Sep 17 00:00:00 2001 From: SUZUKI Tetsuya Date: Mon, 6 Dec 2021 15:26:48 +0900 Subject: [PATCH 38/85] =?UTF-8?q?[CI]=20lint=20=E3=83=81=E3=82=A7=E3=83=83?= =?UTF-8?q?=E3=82=AF=E3=82=92=E8=A1=8C=E3=81=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0b091b61..9a5f7f9a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -26,4 +26,6 @@ jobs: - name: Copy gradle.properties run: cp gradle.properties.example gradle.properties - name: Build with Gradle - run: ./gradlew build --no-daemon + run: ./gradlew build + - name: Lint Check + run: ./gradlew ktlintCheck From 4a9eb86b8abe3c3e4e110af423d9112d705059a8 Mon Sep 17 00:00:00 2001 From: SUZUKI Tetsuya Date: Mon, 6 Dec 2021 17:01:51 +0900 Subject: [PATCH 39/85] =?UTF-8?q?ktlint=20=E3=82=92=E9=81=A9=E7=94=A8?= =?UTF-8?q?=E3=81=99=E3=82=8B=20&=20=E3=83=AF=E3=82=A4=E3=83=AB=E3=83=89?= =?UTF-8?q?=E3=82=AB=E3=83=BC=E3=83=89=E3=81=AE=E3=82=A4=E3=83=B3=E3=83=9D?= =?UTF-8?q?=E3=83=BC=E3=83=88=E3=82=92=E3=82=84=E3=82=81=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/kotlin/jp/shiguredo/sora/sdk/Sora.kt | 3 +- .../sora/sdk/camera/CameraCapturerFactory.kt | 23 +- .../sdk/camera/CameraVideoCapturerWrapper.kt | 15 +- .../sora/sdk/channel/SoraMediaChannel.kt | 320 +++++++++-------- .../sdk/channel/data/ChannelAttendeesCount.kt | 52 +-- .../channel/option/PeerConnectionOption.kt | 2 +- .../sdk/channel/option/SoraAudioOption.kt | 2 +- .../sdk/channel/option/SoraChannelRole.kt | 3 +- .../sdk/channel/option/SoraMediaOption.kt | 51 +-- .../sdk/channel/option/SoraSpotlightOption.kt | 3 +- .../sdk/channel/option/SoraVideoOption.kt | 23 +- .../sora/sdk/channel/rtc/PeerChannel.kt | 331 +++++++++++------- .../sora/sdk/channel/rtc/PeerNetworkConfig.kt | 33 +- .../sdk/channel/rtc/RTCComponentFactory.kt | 90 +++-- .../sdk/channel/rtc/RTCLocalAudioManager.kt | 34 +- .../sdk/channel/rtc/RTCLocalVideoManager.kt | 40 ++- .../sdk/channel/signaling/SignalingChannel.kt | 86 ++--- .../sdk/channel/signaling/message/Catalog.kt | 216 ++++++------ .../signaling/message/MessageConverter.kt | 86 ++--- .../SimulcastVideoEncoderFactoryWrapper.kt | 79 +++-- .../sdk/util/ByteBufferBackedInputStream.kt | 1 - .../sdk/util/ReusableCompositeDisposable.kt | 3 +- .../jp/shiguredo/sora/sdk/util/SDKInfo.kt | 26 +- .../jp/shiguredo/sora/sdk/util/SoraLogger.kt | 1 - .../shiguredo/sora/sdk/ConnectClientIdTest.kt | 15 +- .../sora/sdk/ConnectMetadataJsonTest.kt | 30 +- .../jp/shiguredo/sora/sdk/TrivialTest.kt | 1 - 27 files changed, 854 insertions(+), 715 deletions(-) diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/Sora.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/Sora.kt index eb4f7379..f4a81440 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/Sora.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/Sora.kt @@ -11,5 +11,4 @@ object Sora { */ @Deprecated("スポットライトレガシー機能は 2021 年 12 月に廃止が予定されています。") var usesSpotlightLegacy: Boolean = false - -} \ No newline at end of file +} diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/camera/CameraCapturerFactory.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/camera/CameraCapturerFactory.kt index 57ef5f91..8b6a4bc9 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/camera/CameraCapturerFactory.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/camera/CameraCapturerFactory.kt @@ -33,9 +33,11 @@ class CameraCapturerFactory { * @return 生成された `CameraVideoCapturer` */ @JvmOverloads - fun create(context: Context, - fixedResolution: Boolean = false, - frontFacingFirst: Boolean = true) : CameraVideoCapturer? { + fun create( + context: Context, + fixedResolution: Boolean = false, + frontFacingFirst: Boolean = true + ): CameraVideoCapturer? { SoraLogger.d(TAG, "create camera capturer") var videoCapturer: CameraVideoCapturer? = null if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { @@ -60,14 +62,14 @@ class CameraCapturerFactory { videoCapturer } else -> { - SoraLogger.d(TAG, "Wrap capturer: original.isScreencast=${videoCapturer.isScreencast}, fixedResolution=${fixedResolution}") + SoraLogger.d(TAG, "Wrap capturer: original.isScreencast=${videoCapturer.isScreencast}, fixedResolution=$fixedResolution") CameraVideoCapturerWrapper(videoCapturer, fixedResolution) } } } private fun createCapturer(enumerator: CameraEnumerator, frontFacingFirst: Boolean): CameraVideoCapturer? { - var capturer : CameraVideoCapturer? = null + var capturer: CameraVideoCapturer? = null enumerator.deviceNames.forEach { deviceName -> if (capturer == null) { @@ -86,17 +88,16 @@ class CameraCapturerFactory { return capturer } - private fun findDeviceCamera(enumerator: CameraEnumerator, - deviceName: String, - frontFacing: Boolean) : CameraVideoCapturer? { + private fun findDeviceCamera( + enumerator: CameraEnumerator, + deviceName: String, + frontFacing: Boolean + ): CameraVideoCapturer? { var capturer: CameraVideoCapturer? = null if (enumerator.isFrontFacing(deviceName) == frontFacing) { capturer = enumerator.createCapturer(deviceName, null) } return capturer } - } - } - diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/camera/CameraVideoCapturerWrapper.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/camera/CameraVideoCapturerWrapper.kt index ab14defc..dae16027 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/camera/CameraVideoCapturerWrapper.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/camera/CameraVideoCapturerWrapper.kt @@ -5,8 +5,10 @@ import org.webrtc.CameraVideoCapturer import org.webrtc.CapturerObserver import org.webrtc.SurfaceTextureHelper -class CameraVideoCapturerWrapper(private val capturer: CameraVideoCapturer, - private val fixedResolution: Boolean = false): CameraVideoCapturer { +class CameraVideoCapturerWrapper( + private val capturer: CameraVideoCapturer, + private val fixedResolution: Boolean = false +) : CameraVideoCapturer { override fun startCapture(width: Int, height: Int, framerate: Int) { capturer.startCapture(width, height, framerate) } @@ -31,12 +33,15 @@ class CameraVideoCapturerWrapper(private val capturer: CameraVideoCapturer, return fixedResolution } - override fun initialize(surfaceTextureHelper: SurfaceTextureHelper?, context: Context?, - capturerObserver: CapturerObserver?) { + override fun initialize( + surfaceTextureHelper: SurfaceTextureHelper?, + context: Context?, + capturerObserver: CapturerObserver? + ) { capturer.initialize(surfaceTextureHelper, context, capturerObserver) } override fun dispose() { capturer.dispose() } -} \ No newline at end of file +} diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt index 28990fc6..86c1bad0 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt @@ -14,12 +14,24 @@ import jp.shiguredo.sora.sdk.channel.rtc.PeerChannelImpl import jp.shiguredo.sora.sdk.channel.rtc.PeerNetworkConfig import jp.shiguredo.sora.sdk.channel.signaling.SignalingChannel import jp.shiguredo.sora.sdk.channel.signaling.SignalingChannelImpl -import jp.shiguredo.sora.sdk.channel.signaling.message.* +import jp.shiguredo.sora.sdk.channel.signaling.message.MessageConverter +import jp.shiguredo.sora.sdk.channel.signaling.message.NotificationMessage +import jp.shiguredo.sora.sdk.channel.signaling.message.OfferConfig +import jp.shiguredo.sora.sdk.channel.signaling.message.OfferMessage +import jp.shiguredo.sora.sdk.channel.signaling.message.PushMessage +import jp.shiguredo.sora.sdk.channel.signaling.message.SwitchedMessage import jp.shiguredo.sora.sdk.error.SoraErrorReason import jp.shiguredo.sora.sdk.util.ReusableCompositeDisposable import jp.shiguredo.sora.sdk.util.SoraLogger -import org.webrtc.* -import java.util.* +import org.webrtc.DataChannel +import org.webrtc.IceCandidate +import org.webrtc.MediaStream +import org.webrtc.RTCStatsCollectorCallback +import org.webrtc.RTCStatsReport +import org.webrtc.RtpParameters +import org.webrtc.SessionDescription +import java.util.Timer +import java.util.TimerTask import kotlin.concurrent.schedule /** @@ -47,18 +59,18 @@ import kotlin.concurrent.schedule * @param ignoreDisconnectWebSocket connect メッセージに含める `ignore_disconnect_websocket` */ class SoraMediaChannel @JvmOverloads constructor( - private val context: Context, - private val signalingEndpoint: String, - private val channelId: String, - private val signalingMetadata: Any? = "", - private val mediaOption: SoraMediaOption, - private val timeoutSeconds: Long = DEFAULT_TIMEOUT_SECONDS, - private var listener: Listener?, - private val clientId: String? = null, - private val signalingNotifyMetadata: Any? = null, - private val peerConnectionOption: PeerConnectionOption = PeerConnectionOption(), - private val dataChannelSignaling: Boolean? = null, - private var ignoreDisconnectWebSocket: Boolean? = null + private val context: Context, + private val signalingEndpoint: String, + private val channelId: String, + private val signalingMetadata: Any? = "", + private val mediaOption: SoraMediaOption, + private val timeoutSeconds: Long = DEFAULT_TIMEOUT_SECONDS, + private var listener: Listener?, + private val clientId: String? = null, + private val signalingNotifyMetadata: Any? = null, + private val peerConnectionOption: PeerConnectionOption = PeerConnectionOption(), + private val dataChannelSignaling: Boolean? = null, + private var ignoreDisconnectWebSocket: Boolean? = null ) { companion object { private val TAG = SoraMediaChannel::class.simpleName @@ -197,7 +209,7 @@ class SoraMediaChannel @JvmOverloads constructor( * @param mediaChannel イベントが発生したチャネル * @param notification プッシュ API により受信したメッセージ */ - fun onNotificationMessage(mediaChannel: SoraMediaChannel, notification : NotificationMessage) {} + fun onNotificationMessage(mediaChannel: SoraMediaChannel, notification: NotificationMessage) {} /** * Sora のプッシュ API によりメッセージを受信したときに呼び出されるコールバック. @@ -205,7 +217,7 @@ class SoraMediaChannel @JvmOverloads constructor( * @param mediaChannel イベントが発生したチャネル * @param push プッシュ API により受信したメッセージ */ - fun onPushMessage(mediaChannel: SoraMediaChannel, push : PushMessage) {} + fun onPushMessage(mediaChannel: SoraMediaChannel, push: PushMessage) {} /** * PeerConnection の getStats() 統計情報を取得したときに呼び出されるコールバック. @@ -215,7 +227,7 @@ class SoraMediaChannel @JvmOverloads constructor( * - https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/getStats * * @param mediaChannel イベントが発生したチャネル - * @param statsReports 統計レポート + * @param statsReport 統計レポート */ fun onPeerConnectionStatsReady(mediaChannel: SoraMediaChannel, statsReport: RTCStatsReport) {} @@ -236,11 +248,10 @@ class SoraMediaChannel @JvmOverloads constructor( * @param encodings Sora から送信された encodings */ fun onSenderEncodings(mediaChannel: SoraMediaChannel, encodings: List) {} - } - private var peer: PeerChannel? = null - private var signaling: SignalingChannel? = null + private var peer: PeerChannel? = null + private var signaling: SignalingChannel? = null private var switchedToDataChannel = false private var closing = false @@ -249,16 +260,19 @@ class SoraMediaChannel @JvmOverloads constructor( * コネクション ID. */ var connectionId: String? = null - private set + private set private val compositeDisposable = ReusableCompositeDisposable() private val signalingListener = object : SignalingChannel.Listener { override fun onDisconnect() { - SoraLogger.d(TAG, "[channel:$role] @signaling:onDisconnect " - + "switchedToDataChannel=$switchedToDataChannel, " - + "ignoreDisconnectWebSocket=$ignoreDisconnectWebSocket" ) + SoraLogger.d( + TAG, + "[channel:$role] @signaling:onDisconnect " + + "switchedToDataChannel=$switchedToDataChannel, " + + "ignoreDisconnectWebSocket=$ignoreDisconnectWebSocket" + ) val ignoreDisconnect = ignoreDisconnectWebSocket ?: false if (switchedToDataChannel && ignoreDisconnect) { // なにもしない @@ -322,7 +336,6 @@ class SoraMediaChannel @JvmOverloads constructor( handler(null) } } - } private val peerListener = object : PeerChannel.Listener { @@ -342,7 +355,7 @@ class SoraMediaChannel @JvmOverloads constructor( } override fun onAddRemoteStream(ms: MediaStream) { - SoraLogger.d(TAG, "[channel:$role] @peer:onAddRemoteStream msid=:${ms.id}, connectionId=${connectionId}") + SoraLogger.d(TAG, "[channel:$role] @peer:onAddRemoteStream msid=:${ms.id}, connectionId=$connectionId") if (mediaOption.multistreamEnabled && connectionId != null && ms.id == connectionId) { SoraLogger.d(TAG, "[channel:$role] this stream is mine, ignore: ${ms.id}") return @@ -373,7 +386,7 @@ class SoraMediaChannel @JvmOverloads constructor( "push" -> "push" "stats" -> "req-stats" "e2ee" -> "NOT-IMPLEMENTED" - else -> label // 追加が発生した時に備えて許容する + else -> label // 追加が発生した時に備えて許容する } MessageConverter.parseType(messageData)?.let { type -> @@ -402,7 +415,6 @@ class SoraMediaChannel @JvmOverloads constructor( else -> SoraLogger.i(TAG, "Unknown label: label=$label, type=$type, message=$messageData") } - } else -> SoraLogger.i(TAG, "Unknown type: label=$label, type=$type, message=$messageData") } @@ -458,13 +470,15 @@ class SoraMediaChannel @JvmOverloads constructor( val maintVersion = kClass.getField("maint_version").get(null) val webrtcRevision = kClass.getField("webrtc_revision").get(null) val webrtcBuildVersion = listOf(webrtcBranch, webrtcCommit, maintVersion) - .joinToString(separator = ".") - SoraLogger.d(TAG, "libwebrtc version = ${webrtcBuildVersion} @ ${webrtcRevision}") - } catch (e : ClassNotFoundException) { + .joinToString(separator = ".") + SoraLogger.d(TAG, "libwebrtc version = $webrtcBuildVersion @ $webrtcRevision") + } catch (e: ClassNotFoundException) { SoraLogger.d(TAG, "connect: libwebrtc other than Shiguredo build is used.") } - SoraLogger.d(TAG, """connect: SoraMediaOption + SoraLogger.d( + TAG, + """connect: SoraMediaOption |requiredRole = ${mediaOption.requiredRole} |upstreamIsRequired = ${mediaOption.upstreamIsRequired} |downstreamIsRequired = ${mediaOption.downstreamIsRequired} @@ -493,7 +507,8 @@ class SoraMediaChannel @JvmOverloads constructor( |spotlightNumber = ${mediaOption.spotlightOption?.spotlightNumber} |signalingMetadata = ${this.signalingMetadata} |clientId = ${this.clientId} - |signalingNotifyMetadata = ${this.signalingNotifyMetadata}""".trimMargin()) + |signalingNotifyMetadata = ${this.signalingNotifyMetadata}""".trimMargin() + ) if (closing) { return @@ -507,12 +522,15 @@ class SoraMediaChannel @JvmOverloads constructor( private fun startTimer() { stopTimer() timer = Timer() - timer!!.schedule(object: TimerTask() { - override fun run() { - timer = null - onTimeout() - } - }, timeoutSeconds*1000) + timer!!.schedule( + object : TimerTask() { + override fun run() { + timer = null + onTimeout() + } + }, + timeoutSeconds * 1000 + ) } private fun stopTimer() { @@ -526,62 +544,65 @@ class SoraMediaChannel @JvmOverloads constructor( disconnect() } - private fun requestClientOfferSdp() { val mediaOption = SoraMediaOption().apply { enableVideoDownstream(null) enableAudioDownstream() } val clientOfferPeer = PeerChannelImpl( - appContext = context, - networkConfig = PeerNetworkConfig( - serverConfig = OfferConfig( - iceServers = emptyList(), - iceTransportPolicy = ""), - mediaOption = mediaOption), - mediaOption = mediaOption, - listener = null + appContext = context, + networkConfig = PeerNetworkConfig( + serverConfig = OfferConfig( + iceServers = emptyList(), + iceTransportPolicy = "" + ), + mediaOption = mediaOption + ), + mediaOption = mediaOption, + listener = null ) clientOfferPeer.run { val subscription = requestClientOfferSdp() - .observeOn(Schedulers.io()) - .subscribeBy( - onSuccess = { - SoraLogger.d(TAG, "[channel:$role] @peer:clientOfferSdp") - disconnect() - - if (it.isFailure) { - SoraLogger.d(TAG, "[channel:$role] failed to create client offer SDP: ${it.exceptionOrNull()?.message}") - } - val handler = Handler(Looper.getMainLooper()) - handler.post() { - connectSignalingChannel(it.getOrNull()) - } - }, - onError = { - SoraLogger.w(TAG, - "[channel:$role] failed request client offer SDP: ${it.message}") - disconnect() - } + .observeOn(Schedulers.io()) + .subscribeBy( + onSuccess = { + SoraLogger.d(TAG, "[channel:$role] @peer:clientOfferSdp") + disconnect() + + if (it.isFailure) { + SoraLogger.d(TAG, "[channel:$role] failed to create client offer SDP: ${it.exceptionOrNull()?.message}") + } + val handler = Handler(Looper.getMainLooper()) + handler.post() { + connectSignalingChannel(it.getOrNull()) + } + }, + onError = { + SoraLogger.w( + TAG, + "[channel:$role] failed request client offer SDP: ${it.message}" + ) + disconnect() + } - ) + ) compositeDisposable.add(subscription) } } - private fun connectSignalingChannel(clientOfferSdp : SessionDescription?) { + private fun connectSignalingChannel(clientOfferSdp: SessionDescription?) { signaling = SignalingChannelImpl( - endpoint = signalingEndpoint, - role = role, - channelId = channelId, - connectDataChannelSignaling = dataChannelSignaling, - connectIgnoreDisconnectWebSocket = ignoreDisconnectWebSocket, - mediaOption = mediaOption, - connectMetadata = signalingMetadata, - listener = signalingListener, - clientOfferSdp = clientOfferSdp, - clientId = clientId, - signalingNotifyMetadata = signalingNotifyMetadata + endpoint = signalingEndpoint, + role = role, + channelId = channelId, + connectDataChannelSignaling = dataChannelSignaling, + connectIgnoreDisconnectWebSocket = ignoreDisconnectWebSocket, + mediaOption = mediaOption, + connectMetadata = signalingMetadata, + listener = signalingListener, + clientOfferSdp = clientOfferSdp, + clientId = clientId, + signalingNotifyMetadata = signalingNotifyMetadata ) signaling!!.connect() } @@ -591,39 +612,41 @@ class SoraMediaChannel @JvmOverloads constructor( SoraLogger.d(TAG, "[channel:$role] @peer:starting") peer = PeerChannelImpl( - appContext = context, - networkConfig = PeerNetworkConfig( - serverConfig = offerMessage.config, - mediaOption = mediaOption - ), - mediaOption = mediaOption, - dataChannelConfigs = offerMessage.dataChannels, - listener = peerListener + appContext = context, + networkConfig = PeerNetworkConfig( + serverConfig = offerMessage.config, + mediaOption = mediaOption + ), + mediaOption = mediaOption, + dataChannelConfigs = offerMessage.dataChannels, + listener = peerListener ) if (0 < peerConnectionOption.getStatsIntervalMSec) { getStatsTimer = Timer() SoraLogger.d(TAG, "Schedule getStats with interval ${peerConnectionOption.getStatsIntervalMSec} [msec]") getStatsTimer?.schedule(0L, peerConnectionOption.getStatsIntervalMSec) { - peer?.getStats(RTCStatsCollectorCallback { - listener?.onPeerConnectionStatsReady(this@SoraMediaChannel, it) - }) + peer?.getStats( + RTCStatsCollectorCallback { + listener?.onPeerConnectionStatsReady(this@SoraMediaChannel, it) + } + ) } } peer?.run { val subscription = handleInitialRemoteOffer(offerMessage.sdp, offerMessage.mid, offerMessage.encodings) - .observeOn(Schedulers.io()) - .subscribeBy( - onSuccess = { - SoraLogger.d(TAG, "[channel:$role] @peer:answer") - signaling?.sendAnswer(it.description) - }, - onError = { - val msg = "[channel:$role] failure in handleInitialOffer: ${it.message}" - SoraLogger.w(TAG, msg) - disconnect() - } - ) + .observeOn(Schedulers.io()) + .subscribeBy( + onSuccess = { + SoraLogger.d(TAG, "[channel:$role] @peer:answer") + signaling?.sendAnswer(it.description) + }, + onError = { + val msg = "[channel:$role] failure in handleInitialOffer: ${it.message}" + SoraLogger.w(TAG, msg) + disconnect() + } + ) compositeDisposable.add(subscription) } } @@ -640,18 +663,18 @@ class SoraMediaChannel @JvmOverloads constructor( private fun handleUpdateOffer(sdp: String) { peer?.run { val subscription = handleUpdatedRemoteOffer(sdp) - .observeOn(Schedulers.io()) - .subscribeBy( - onSuccess = { - SoraLogger.d(TAG, "[channel:$role] @peer:about to send updated answer") - signaling?.sendUpdateAnswer(it.description) - }, - onError = { - val msg = "[channel:$role] failed handle updated offer: ${it.message}" - SoraLogger.w(TAG, msg) - disconnect() - } - ) + .observeOn(Schedulers.io()) + .subscribeBy( + onSuccess = { + SoraLogger.d(TAG, "[channel:$role] @peer:about to send updated answer") + signaling?.sendUpdateAnswer(it.description) + }, + onError = { + val msg = "[channel:$role] failed handle updated offer: ${it.message}" + SoraLogger.w(TAG, msg) + disconnect() + } + ) compositeDisposable.add(subscription) } } @@ -659,18 +682,18 @@ class SoraMediaChannel @JvmOverloads constructor( private fun handleReOffer(sdp: String) { peer?.run { val subscription = handleUpdatedRemoteOffer(sdp) - .observeOn(Schedulers.io()) - .subscribeBy( - onSuccess = { - SoraLogger.d(TAG, "[channel:$role] @peer:about to send re-answer") - signaling?.sendReAnswer(it.description) - }, - onError = { - val msg = "[channel:$role] failed handle re-offer: ${it.message}" - SoraLogger.w(TAG, msg) - disconnect() - } - ) + .observeOn(Schedulers.io()) + .subscribeBy( + onSuccess = { + SoraLogger.d(TAG, "[channel:$role] @peer:about to send re-answer") + signaling?.sendReAnswer(it.description) + }, + onError = { + val msg = "[channel:$role] failed handle re-offer: ${it.message}" + SoraLogger.w(TAG, msg) + disconnect() + } + ) compositeDisposable.add(subscription) } } @@ -678,18 +701,18 @@ class SoraMediaChannel @JvmOverloads constructor( private fun handleReOfferViaDataChannel(dataChannel: DataChannel, sdp: String) { peer?.run { val subscription = handleUpdatedRemoteOffer(sdp) - .observeOn(Schedulers.io()) - .subscribeBy( - onSuccess = { - SoraLogger.d(TAG, "[channel:$role] @peer:about to send re-answer") - peer?.sendReAnswer(dataChannel, it.description) - }, - onError = { - val msg = "[channel:$role] failed handle re-offer: ${it.message}" - SoraLogger.w(TAG, msg) - disconnect() - } - ) + .observeOn(Schedulers.io()) + .subscribeBy( + onSuccess = { + SoraLogger.d(TAG, "[channel:$role] @peer:about to send re-answer") + peer?.sendReAnswer(dataChannel, it.description) + }, + onError = { + val msg = "[channel:$role] failed handle re-offer: ${it.message}" + SoraLogger.w(TAG, msg) + disconnect() + } + ) compositeDisposable.add(subscription) } } @@ -706,11 +729,11 @@ class SoraMediaChannel @JvmOverloads constructor( when (notification.eventType) { "connection.created", "connection.destroyed" -> { val attendees = ChannelAttendeesCount( - numberOfDownstreams = notification.numberOfDownstreamConnections?: 0, - numberOfUpstreams = notification.numberOfUpstreamConnections?: 0, - numberOfSendrecvConnections = notification.numberOfSendrecvConnections!!, - numberOfSendonlyConnections = notification.numberOfSendonlyConnections!!, - numberOfRecvonlyConnections = notification.numberOfRecvonlyConnections!!, + numberOfDownstreams = notification.numberOfDownstreamConnections ?: 0, + numberOfUpstreams = notification.numberOfUpstreamConnections ?: 0, + numberOfSendrecvConnections = notification.numberOfSendrecvConnections!!, + numberOfSendonlyConnections = notification.numberOfSendonlyConnections!!, + numberOfRecvonlyConnections = notification.numberOfRecvonlyConnections!!, ) listener?.onAttendeesCountUpdated(this@SoraMediaChannel, attendees) } @@ -747,8 +770,11 @@ class SoraMediaChannel @JvmOverloads constructor( private fun sendDisconnectMessage() { val dataChannel = dataChannels["signaling"] - SoraLogger.d(TAG, "[channel:$role] sendDisconnectMessage switched=$switchedToDataChannel, " - + "dataChannel.label=${dataChannel?.label()}") + SoraLogger.d( + TAG, + "[channel:$role] sendDisconnectMessage switched=$switchedToDataChannel, " + + "dataChannel.label=${dataChannel?.label()}" + ) if (switchedToDataChannel && dataChannel != null) { peer?.sendDisconnectMessage(dataChannel) } else { diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/data/ChannelAttendeesCount.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/data/ChannelAttendeesCount.kt index ca544a01..7a687551 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/data/ChannelAttendeesCount.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/data/ChannelAttendeesCount.kt @@ -4,37 +4,37 @@ package jp.shiguredo.sora.sdk.channel.data * チャネルの参加者数を表すクラスです. */ data class ChannelAttendeesCount( - /** - * 配信者数. - */ - @Deprecated("numberOfUpstreams は 2021 年 6 月リリース予定の Sora にて廃止されます。") - val numberOfUpstreams: Int, + /** + * 配信者数. + */ + @Deprecated("numberOfUpstreams は 2021 年 6 月リリース予定の Sora にて廃止されます。") + val numberOfUpstreams: Int, - /** - * 視聴者数. - */ - @Deprecated("numberOfDownstreams は 2021 年 6 月リリース予定の Sora にて廃止されます。") - val numberOfDownstreams: Int, + /** + * 視聴者数. + */ + @Deprecated("numberOfDownstreams は 2021 年 6 月リリース予定の Sora にて廃止されます。") + val numberOfDownstreams: Int, - /** - * sendrecv の接続数. - */ - val numberOfSendrecvConnections: Int, + /** + * sendrecv の接続数. + */ + val numberOfSendrecvConnections: Int, - /** - * sendonly の接続数. - */ - val numberOfSendonlyConnections: Int, + /** + * sendonly の接続数. + */ + val numberOfSendonlyConnections: Int, - /** - * recvonly の接続数. - */ - val numberOfRecvonlyConnections: Int, + /** + * recvonly の接続数. + */ + val numberOfRecvonlyConnections: Int, - ) { - /** - * 配信者数と視聴者数の合計. - */ +) { + /** + * 配信者数と視聴者数の合計. + */ val numberOfConnections: Int get() = numberOfSendrecvConnections + numberOfSendonlyConnections + numberOfRecvonlyConnections } diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/PeerConnectionOption.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/PeerConnectionOption.kt index 9dd666ef..662b0716 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/PeerConnectionOption.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/PeerConnectionOption.kt @@ -7,7 +7,7 @@ class PeerConnectionOption { /** * PeerConnection の getStats() 統計情報を取得するインターバルのミリ秒. - * + * * 0 の場合、統計情報を取得しません. * * cf. diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraAudioOption.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraAudioOption.kt index f062d724..e60427b7 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraAudioOption.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraAudioOption.kt @@ -13,7 +13,7 @@ class SoraAudioOption { companion object { const val ECHO_CANCELLATION_CONSTRAINT = "googEchoCancellation" const val AUTO_GAIN_CONTROL_CONSTRAINT = "googAutoGainControl" - const val HIGH_PASS_FILTER_CONSTRAINT = "googHighpassFilter" + const val HIGH_PASS_FILTER_CONSTRAINT = "googHighpassFilter" const val NOISE_SUPPRESSION_CONSTRAINT = "googNoiseSuppression" } /** diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraChannelRole.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraChannelRole.kt index 5f089111..c2c82001 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraChannelRole.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraChannelRole.kt @@ -1,6 +1,6 @@ package jp.shiguredo.sora.sdk.channel.option -import java.util.* +import java.util.Locale /** * チャネルの役割を示します. @@ -18,5 +18,4 @@ enum class SoraChannelRole { internal val signaling: String get() = this.toString().toLowerCase(Locale.getDefault()) - } diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraMediaOption.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraMediaOption.kt index d97a0a22..fca40d33 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraMediaOption.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraMediaOption.kt @@ -1,7 +1,11 @@ package jp.shiguredo.sora.sdk.channel.option import jp.shiguredo.sora.sdk.Sora -import org.webrtc.* +import org.webrtc.EglBase +import org.webrtc.PeerConnection +import org.webrtc.VideoCapturer +import org.webrtc.VideoDecoderFactory +import org.webrtc.VideoEncoderFactory /** * Sora への接続オプションを表すクラスです. @@ -13,12 +17,12 @@ class SoraMediaOption { } internal var audioDownstreamEnabled = false - internal var audioUpstreamEnabled = false + internal var audioUpstreamEnabled = false internal var videoDownstreamEnabled = false - internal var videoUpstreamEnabled = false - internal var multistreamEnabled = false + internal var videoUpstreamEnabled = false + internal var multistreamEnabled = false internal var spotlightOption: SoraSpotlightOption? = null - internal var simulcastEnabled = false + internal var simulcastEnabled = false internal var simulcastRid: SoraVideoOption.SimulcastRid? = null internal val spotlightEnabled: Boolean @@ -34,10 +38,10 @@ class SoraMediaOption { */ var videoDecoderFactory: VideoDecoderFactory? = null - internal var videoCapturer: VideoCapturer? = null + internal var videoCapturer: VideoCapturer? = null internal var videoDownstreamContext: EglBase.Context? = null - internal var videoUpstreamContext: EglBase.Context? = null + internal var videoUpstreamContext: EglBase.Context? = null var videoCodec = SoraVideoOption.Codec.VP9 @@ -69,10 +73,12 @@ class SoraMediaOption { * @param capturer `VideoCapturer` インスタンス * @param eglContext Egl コンテキスト */ - fun enableVideoUpstream(capturer: VideoCapturer, - eglContext: EglBase.Context?) { + fun enableVideoUpstream( + capturer: VideoCapturer, + eglContext: EglBase.Context? + ) { videoUpstreamEnabled = true - videoCapturer = capturer + videoCapturer = capturer videoUpstreamContext = eglContext } @@ -145,19 +151,19 @@ class SoraMediaOption { // Just for internal usage internal val videoIsRequired: Boolean - get() = videoDownstreamEnabled || videoUpstreamEnabled + get() = videoDownstreamEnabled || videoUpstreamEnabled internal val videoHwAccelerationIsRequired: Boolean - get() = (videoUpstreamContext != null) || (videoDownstreamContext != null) + get() = (videoUpstreamContext != null) || (videoDownstreamContext != null) internal val audioIsRequired: Boolean - get() = audioDownstreamEnabled || audioUpstreamEnabled + get() = audioDownstreamEnabled || audioUpstreamEnabled internal val downstreamIsRequired: Boolean - get() = audioDownstreamEnabled || videoDownstreamEnabled + get() = audioDownstreamEnabled || videoDownstreamEnabled internal val upstreamIsRequired: Boolean - get() = audioUpstreamEnabled || videoUpstreamEnabled + get() = audioUpstreamEnabled || videoUpstreamEnabled internal var _multistreamIsRequired: Boolean? = null @@ -178,12 +184,12 @@ class SoraMediaOption { internal var _requiredRole: SoraChannelRole? = null internal val requiredRole: SoraChannelRole - get() = if (upstreamIsRequired && downstreamIsRequired) - SoraChannelRole.SENDRECV - else if (upstreamIsRequired) - SoraChannelRole.SENDONLY - else - SoraChannelRole.RECVONLY + get() = if (upstreamIsRequired && downstreamIsRequired) + SoraChannelRole.SENDRECV + else if (upstreamIsRequired) + SoraChannelRole.SENDONLY + else + SoraChannelRole.RECVONLY /** * JavaScript API の "googCpuOveruseDetection" に相当する設定項目です. @@ -194,6 +200,5 @@ class SoraMediaOption { * TcpCandidatePolicy を設定します. */ var tcpCandidatePolicy: PeerConnection.TcpCandidatePolicy = - PeerConnection.TcpCandidatePolicy.ENABLED + PeerConnection.TcpCandidatePolicy.ENABLED } - diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraSpotlightOption.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraSpotlightOption.kt index e4c467cb..d20ff932 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraSpotlightOption.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraSpotlightOption.kt @@ -16,5 +16,4 @@ class SoraSpotlightOption { var spotlightFocusRid: SoraVideoOption.SpotlightRid? = null var spotlightUnfocusRid: SoraVideoOption.SpotlightRid? = null - -} \ No newline at end of file +} diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraVideoOption.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraVideoOption.kt index 9de8d47f..fe6b4fb7 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraVideoOption.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraVideoOption.kt @@ -34,24 +34,23 @@ class SoraVideoOption { /** QQVGA 160x120 */ val QQVGA = Point(160, 120) /** QCIF 176x144 */ - val QCIF = Point(176, 144) + val QCIF = Point(176, 144) /** HQVGA 240x160 */ val HQVGA = Point(240, 160) /** QVGA 320x240 */ - val QVGA = Point(320, 240) + val QVGA = Point(320, 240) /** VGA 640x480 */ - val VGA = Point(640, 480) + val VGA = Point(640, 480) /** HD 1280x720 */ - val HD = Point(1280, 720) + val HD = Point(1280, 720) /** FHD 1920x1080 */ - val FHD = Point(1920, 1080) + val FHD = Point(1920, 1080) /** Res3840x1920 3840x1920 */ val Res3840x1920 = Point(3840, 1920) /** UHD3840x2160 3840x2160 */ val UHD3840x2160 = Point(3840, 2160) /** UHD4096x2160 4096x2160 */ val UHD4096x2160 = Point(4096, 2160) - } } @@ -65,27 +64,25 @@ class SoraVideoOption { /** QQVGA 120x160 */ val QQVGA = Point(120, 160) /** QCIF 144x176 */ - val QCIF = Point(144, 176) + val QCIF = Point(144, 176) /** HQVGA 160x240 */ val HQVGA = Point(160, 240) /** QVGA 240x320 */ - val QVGA = Point(240, 320) + val QVGA = Point(240, 320) /** VGA 480x640 */ - val VGA = Point(480, 640) + val VGA = Point(480, 640) /** HD 720x1280 */ - val HD = Point(720, 1280) + val HD = Point(720, 1280) /** FHD 1080x1920 */ - val FHD = Point(1080, 1920) + val FHD = Point(1080, 1920) /** Res1920x3840 1920x3840 */ val Res1920x3840 = Point(1920, 3840) /** UHD2160x3840 2160x3840 */ val UHD2160x3840 = Point(2160, 3840) /** UHD2160x4096 2160x4096 */ val UHD2160x4096 = Point(2160, 4096) - } } - } enum class SimulcastRid(private val value: String) { diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/PeerChannel.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/PeerChannel.kt index 6881121f..38aee476 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/PeerChannel.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/PeerChannel.kt @@ -10,17 +10,34 @@ import jp.shiguredo.sora.sdk.channel.signaling.message.MessageConverter import jp.shiguredo.sora.sdk.error.SoraErrorReason import jp.shiguredo.sora.sdk.util.ByteBufferBackedInputStream import jp.shiguredo.sora.sdk.util.SoraLogger -import org.webrtc.* +import org.webrtc.DataChannel +import org.webrtc.IceCandidate +import org.webrtc.Logging +import org.webrtc.MediaStream +import org.webrtc.MediaStreamTrack +import org.webrtc.PeerConnection +import org.webrtc.PeerConnectionFactory +import org.webrtc.RTCStatsCollectorCallback +import org.webrtc.RTCStatsReport +import org.webrtc.RtpParameters +import org.webrtc.RtpReceiver +import org.webrtc.RtpSender +import org.webrtc.RtpTransceiver +import org.webrtc.SdpObserver +import org.webrtc.SessionDescription import java.io.ByteArrayInputStream import java.nio.ByteBuffer -import java.util.* +import java.util.UUID import java.util.concurrent.Executors -import java.util.zip.* +import java.util.zip.DeflaterInputStream +import java.util.zip.InflaterInputStream interface PeerChannel { - fun handleInitialRemoteOffer(offer: String, - mid: Map?, - encodings: List?): Single + fun handleInitialRemoteOffer( + offer: String, + mid: Map?, + encodings: List? + ): Single fun handleUpdatedRemoteOffer(offer: String): Single // 失敗しても問題のない処理が含まれる (client offer SDP は生成に失敗しても問題ない) ので、 @@ -54,13 +71,13 @@ interface PeerChannel { } class PeerChannelImpl( - private val appContext: Context, - private val networkConfig: PeerNetworkConfig, - private val mediaOption: SoraMediaOption, - dataChannelConfigs: List>? = null, - private var listener: PeerChannel.Listener?, - private var useTracer: Boolean = false -): PeerChannel { + private val appContext: Context, + private val networkConfig: PeerNetworkConfig, + private val mediaOption: SoraMediaOption, + dataChannelConfigs: List>? = null, + private var listener: PeerChannel.Listener?, + private var useTracer: Boolean = false +) : PeerChannel { companion object { private val TAG = PeerChannelImpl::class.simpleName @@ -69,12 +86,12 @@ class PeerChannelImpl( fun initializeIfNeeded(context: Context, useTracer: Boolean) { if (!isInitialized) { val options = PeerConnectionFactory.InitializationOptions - .builder(context) - .setEnableInternalTracer(useTracer) - .setFieldTrials("") - .createInitializationOptions() + .builder(context) + .setEnableInternalTracer(useTracer) + .setFieldTrials("") + .createInitializationOptions() PeerConnectionFactory.initialize(options) - if(SoraLogger.libjingle_enabled) { + if (SoraLogger.libjingle_enabled) { Logging.enableLogToDebugOutput(Logging.Severity.LS_INFO) } isInitialized = true @@ -84,12 +101,12 @@ class PeerChannelImpl( private val componentFactory = RTCComponentFactory(mediaOption, listener) - private var conn: PeerConnection? = null + private var conn: PeerConnection? = null private var factory: PeerConnectionFactory? = null - private val executor = Executors.newSingleThreadExecutor() + private val executor = Executors.newSingleThreadExecutor() - private val sdpConstraints = componentFactory.createSDPConstraints() + private val sdpConstraints = componentFactory.createSDPConstraints() private val localAudioManager = componentFactory.createAudioManager() private val localVideoManager = componentFactory.createVideoManager() @@ -114,7 +131,7 @@ class PeerChannelImpl( init { val compressedDataChannels = (dataChannelConfigs ?: emptyList()) - .filter { (it["compress"] ?: false) == true } + .filter { (it["compress"] ?: false) == true } compressLabels = compressedDataChannels.map { it["label"] as String } SoraLogger.d(TAG, "[rtc] compressedLabels=$compressLabels, dataChannelConfigs=$dataChannelConfigs") } @@ -122,7 +139,7 @@ class PeerChannelImpl( private val connectionObserver = object : PeerConnection.Observer { override fun onSignalingChange(state: PeerConnection.SignalingState?) { - SoraLogger.d(TAG, "[rtc] @onSignalingChange: ${state.toString()}") + SoraLogger.d(TAG, "[rtc] @onSignalingChange: $state") } override fun onIceCandidate(candidate: IceCandidate?) { @@ -151,7 +168,7 @@ class PeerChannelImpl( SoraLogger.d(TAG, "[rtc] @onRemoveTrack") } - override fun onTrack(transceiver: RtpTransceiver) { + override fun onTrack(transceiver: RtpTransceiver) { SoraLogger.d(TAG, "[rtc] @onTrack direction=${transceiver.direction}") SoraLogger.d(TAG, "[rtc] @onTrack currentDirection=${transceiver.currentDirection}") SoraLogger.d(TAG, "[rtc] @onTrack sender.track=${transceiver.sender.track()}") @@ -161,34 +178,45 @@ class PeerChannelImpl( } override fun onDataChannel(dataChannel: DataChannel) { - SoraLogger.d(TAG, "[rtc] @onDataChannel label=${dataChannel.label()}, id=${dataChannel.id()}" - + " state=${dataChannel.state()}, bufferedAmount=${dataChannel.bufferedAmount()}") + SoraLogger.d( + TAG, + "[rtc] @onDataChannel label=${dataChannel.label()}, id=${dataChannel.id()}" + + " state=${dataChannel.state()}, bufferedAmount=${dataChannel.bufferedAmount()}" + ) dataChannel.registerObserver(object : DataChannel.Observer { val label = dataChannel.label() override fun onBufferedAmountChange(previouAmount: Long) { - SoraLogger.d(TAG, "[rtc] @dataChannel.onBufferedAmountChange" - + " label=$label, id=${dataChannel.id()}" - + " state=${dataChannel.state()}, bufferedAmount=${dataChannel.bufferedAmount()}," - + " previousAmount=$previouAmount)") + SoraLogger.d( + TAG, + "[rtc] @dataChannel.onBufferedAmountChange" + + " label=$label, id=${dataChannel.id()}" + + " state=${dataChannel.state()}, bufferedAmount=${dataChannel.bufferedAmount()}," + + " previousAmount=$previouAmount)" + ) } override fun onStateChange() { - SoraLogger.d(TAG, "[rtc] @dataChannel.onStateChange" - + " label=$label, id=${dataChannel.id()}, state=${dataChannel.state()}") + SoraLogger.d( + TAG, + "[rtc] @dataChannel.onStateChange" + + " label=$label, id=${dataChannel.id()}, state=${dataChannel.state()}" + ) if (dataChannel.state() == DataChannel.State.CLOSED) { listener?.onDataChannelClosed(dataChannel.label(), dataChannel) } } override fun onMessage(buffer: DataChannel.Buffer) { - SoraLogger.d(TAG, "[rtc] @dataChannel.onMessage" - + " label=$label, state=${dataChannel.state()}, binary=${buffer.binary}") + SoraLogger.d( + TAG, + "[rtc] @dataChannel.onMessage" + + " label=$label, state=${dataChannel.state()}, binary=${buffer.binary}" + ) val messageData = dataChannelBufferToString(label, buffer) listener?.onDataChannelMessage(dataChannel.label(), dataChannel, messageData) } - }) listener?.onDataChannelOpen(dataChannel.label(), dataChannel) @@ -259,20 +287,22 @@ class PeerChannelImpl( } } - private fun setup(): Single = Single.create(SingleOnSubscribe { - try { - setupInternal() - it.onSuccess(true) - } catch (e: Exception) { - SoraLogger.w(TAG, e.toString()) - it.onError(e) + private fun setup(): Single = Single.create( + SingleOnSubscribe { + try { + setupInternal() + it.onSuccess(true) + } catch (e: Exception) { + SoraLogger.w(TAG, e.toString()) + it.onError(e) + } } - }).subscribeOn(Schedulers.from(executor)) + ).subscribeOn(Schedulers.from(executor)) override fun handleUpdatedRemoteOffer(offer: String): Single { val offerSDP = - SessionDescription(SessionDescription.Type.OFFER, offer) + SessionDescription(SessionDescription.Type.OFFER, offer) return setRemoteDescription(offerSDP).flatMap { // active: false が無効化されてしまう問題に対応 @@ -286,9 +316,11 @@ class PeerChannelImpl( } } - override fun handleInitialRemoteOffer(offer: String, - mid: Map?, - encodings: List?): Single { + override fun handleInitialRemoteOffer( + offer: String, + mid: Map?, + encodings: List? + ): Single { val offerSDP = SessionDescription(SessionDescription.Type.OFFER, offer) offerEncodings = encodings @@ -304,9 +336,9 @@ class PeerChannelImpl( val audioMid = mid?.get("audio") if (audioMid != null) { - val transceiver = this.conn?.transceivers?.find { it.mid == audioMid } + val transceiver = this.conn?.transceivers?.find { it.mid == audioMid } transceiver?.direction = RtpTransceiver.RtpTransceiverDirection.SEND_ONLY - SoraLogger.d(TAG, "set audio sender: mid=${audioMid}, transceiver=${transceiver}") + SoraLogger.d(TAG, "set audio sender: mid=$audioMid, transceiver=$transceiver") audioSender = transceiver?.sender audioSender?.streams = listOf(localStream!!.id) @@ -324,7 +356,7 @@ class PeerChannelImpl( if (videoMid != null) { val transceiver = this.conn?.transceivers?.find { it.mid == videoMid } transceiver?.direction = RtpTransceiver.RtpTransceiverDirection.SEND_ONLY - SoraLogger.d(TAG, "set video sender: mid=${mid}, transceiver=${transceiver} ") + SoraLogger.d(TAG, "set video sender: mid=$mid, transceiver=$transceiver ") videoSender = transceiver?.sender videoSender?.streams = listOf(localStream!!.id) @@ -369,14 +401,17 @@ class PeerChannelImpl( listener?.onSenderEncodings(parameters.encodings) parameters.encodings.forEach { with(it) { - SoraLogger.d(TAG, "update sender encoding: " + + SoraLogger.d( + TAG, + "update sender encoding: " + "id=${sender.id()}, " + "rid=$rid, " + "active=$active, " + "scaleResolutionDownBy=$scaleResolutionDownBy, " + "maxFramerate=$maxFramerate, " + "maxBitrateBps=$maxBitrateBps, " + - "ssrc=$ssrc") + "ssrc=$ssrc" + ) } } @@ -391,33 +426,39 @@ class PeerChannelImpl( } } - private fun createClientOfferSdp() : Single> = - Single.create(SingleOnSubscribe> { + private fun createClientOfferSdp(): Single> = + Single.create( + SingleOnSubscribe> { - val directionRecvOnly = RtpTransceiver.RtpTransceiverInit( - RtpTransceiver.RtpTransceiverDirection.RECV_ONLY) + val directionRecvOnly = RtpTransceiver.RtpTransceiverInit( + RtpTransceiver.RtpTransceiverDirection.RECV_ONLY + ) - conn?.addTransceiver(MediaStreamTrack.MediaType.MEDIA_TYPE_AUDIO, directionRecvOnly) - conn?.addTransceiver(MediaStreamTrack.MediaType.MEDIA_TYPE_VIDEO, directionRecvOnly) + conn?.addTransceiver(MediaStreamTrack.MediaType.MEDIA_TYPE_AUDIO, directionRecvOnly) + conn?.addTransceiver(MediaStreamTrack.MediaType.MEDIA_TYPE_VIDEO, directionRecvOnly) - conn?.createOffer(object : SdpObserver { - override fun onCreateSuccess(sdp: SessionDescription?) { - SoraLogger.d(TAG, "createOffer:onCreateSuccess: ${sdp?.type}") - it.onSuccess(Result.success(sdp!!)) - } - override fun onCreateFailure(error: String) { - SoraLogger.d(TAG, "createOffer:onCreateFailure: $error") - // Offer SDP は生成に失敗しても問題ないので、エラーメッセージを onSuccess で渡す - it.onSuccess(Result.failure(Error(error))) - } - override fun onSetSuccess() { - it.onError(Error("must not come here")) - } - override fun onSetFailure(s: String?) { - it.onError(Error("must not come here")) - } - }, sdpConstraints) - }).subscribeOn(Schedulers.from(executor)) + conn?.createOffer( + object : SdpObserver { + override fun onCreateSuccess(sdp: SessionDescription?) { + SoraLogger.d(TAG, "createOffer:onCreateSuccess: ${sdp?.type}") + it.onSuccess(Result.success(sdp!!)) + } + override fun onCreateFailure(error: String) { + SoraLogger.d(TAG, "createOffer:onCreateFailure: $error") + // Offer SDP は生成に失敗しても問題ないので、エラーメッセージを onSuccess で渡す + it.onSuccess(Result.failure(Error(error))) + } + override fun onSetSuccess() { + it.onError(Error("must not come here")) + } + override fun onSetFailure(s: String?) { + it.onError(Error("must not come here")) + } + }, + sdpConstraints + ) + } + ).subscribeOn(Schedulers.from(executor)) private fun setupInternal() { SoraLogger.d(TAG, "setupInternal") @@ -427,8 +468,9 @@ class PeerChannelImpl( SoraLogger.d(TAG, "createPeerConnection") conn = factory!!.createPeerConnection( - networkConfig.createConfiguration(), - connectionObserver) + networkConfig.createConfiguration(), + connectionObserver + ) SoraLogger.d(TAG, "local managers' initTrack: audio") localAudioManager.initTrack(factory!!, mediaOption.audioOption) @@ -474,7 +516,7 @@ class PeerChannelImpl( dataChannel.send(stringToDataChannelBuffer(dataChannel.label(), disconnectMessage)) } - private fun stringToDataChannelBuffer(label: String, data: String) : DataChannel.Buffer { + private fun stringToDataChannelBuffer(label: String, data: String): DataChannel.Buffer { val inStream = when (compressLabels.contains(label)) { true -> DeflaterInputStream(ByteArrayInputStream(data.toByteArray())) @@ -485,7 +527,7 @@ class PeerChannelImpl( return DataChannel.Buffer(byteBuffer, true) } - private fun dataChannelBufferToString(label: String, buffer: DataChannel.Buffer) : String { + private fun dataChannelBufferToString(label: String, buffer: DataChannel.Buffer): String { val inStream = when (compressLabels.contains(label)) { true -> InflaterInputStream(ByteBufferBackedInputStream(buffer.data)) @@ -496,65 +538,83 @@ class PeerChannelImpl( } private fun createAnswer(): Single = - Single.create(SingleOnSubscribe { - conn?.createAnswer(object : SdpObserver { - override fun onCreateSuccess(sdp: SessionDescription?) { - SoraLogger.d(TAG, """createAnswer:onCreateSuccess: ${sdp!!.type} - |${sdp.description}""".trimMargin()) - it.onSuccess(sdp) - } - override fun onCreateFailure(s: String?) { - SoraLogger.w(TAG, "createAnswer:onCreateFailure: reason=${s}") - it.onError(Error(s)) - } - override fun onSetSuccess() { - it.onError(Error("must not come here")) - } - override fun onSetFailure(s: String?) { - it.onError(Error("must not come here")) - } - }, sdpConstraints) - }).subscribeOn(Schedulers.from(executor)) + Single.create( + SingleOnSubscribe { + conn?.createAnswer( + object : SdpObserver { + override fun onCreateSuccess(sdp: SessionDescription?) { + SoraLogger.d( + TAG, + """createAnswer:onCreateSuccess: ${sdp!!.type} + |${sdp.description}""".trimMargin() + ) + it.onSuccess(sdp) + } + override fun onCreateFailure(s: String?) { + SoraLogger.w(TAG, "createAnswer:onCreateFailure: reason=$s") + it.onError(Error(s)) + } + override fun onSetSuccess() { + it.onError(Error("must not come here")) + } + override fun onSetFailure(s: String?) { + it.onError(Error("must not come here")) + } + }, + sdpConstraints + ) + } + ).subscribeOn(Schedulers.from(executor)) private fun setLocalDescription(sdp: SessionDescription): Single = - Single.create(SingleOnSubscribe { - conn?.setLocalDescription(object : SdpObserver { - override fun onCreateSuccess(p0: SessionDescription?) { - it.onError(Error("must not come here")) - } - override fun onCreateFailure(p0: String?) { - it.onError(Error("must not come here")) - } - override fun onSetSuccess() { - SoraLogger.d(TAG, "setLocalDescription.onSetSuccess ${this@PeerChannelImpl}") - it.onSuccess(sdp) - } - override fun onSetFailure(s: String?) { - SoraLogger.d(TAG, "setLocalDescription.onSetFailure reason=${s} ${this@PeerChannelImpl}") - it.onError(Error(s)) - } - }, sdp) - }).subscribeOn(Schedulers.from(executor)) + Single.create( + SingleOnSubscribe { + conn?.setLocalDescription( + object : SdpObserver { + override fun onCreateSuccess(p0: SessionDescription?) { + it.onError(Error("must not come here")) + } + override fun onCreateFailure(p0: String?) { + it.onError(Error("must not come here")) + } + override fun onSetSuccess() { + SoraLogger.d(TAG, "setLocalDescription.onSetSuccess ${this@PeerChannelImpl}") + it.onSuccess(sdp) + } + override fun onSetFailure(s: String?) { + SoraLogger.d(TAG, "setLocalDescription.onSetFailure reason=$s ${this@PeerChannelImpl}") + it.onError(Error(s)) + } + }, + sdp + ) + } + ).subscribeOn(Schedulers.from(executor)) private fun setRemoteDescription(sdp: SessionDescription): Single = - Single.create(SingleOnSubscribe { - conn?.setRemoteDescription(object : SdpObserver { - override fun onCreateSuccess(p0: SessionDescription?) { - it.onError(Error("must not come here")) - } - override fun onCreateFailure(p0: String?) { - it.onError(Error("must not come here")) - } - override fun onSetSuccess() { - SoraLogger.d(TAG, "setRemoteDescription.onSetSuccess ${this@PeerChannelImpl}") - it.onSuccess(sdp) - } - override fun onSetFailure(s: String?) { - SoraLogger.w(TAG, "setRemoteDescription.onSetFailures reason=${s} ${this@PeerChannelImpl}") - it.onError(Error(s)) - } - }, sdp) - }).subscribeOn(Schedulers.from(executor)) + Single.create( + SingleOnSubscribe { + conn?.setRemoteDescription( + object : SdpObserver { + override fun onCreateSuccess(p0: SessionDescription?) { + it.onError(Error("must not come here")) + } + override fun onCreateFailure(p0: String?) { + it.onError(Error("must not come here")) + } + override fun onSetSuccess() { + SoraLogger.d(TAG, "setRemoteDescription.onSetSuccess ${this@PeerChannelImpl}") + it.onSuccess(sdp) + } + override fun onSetFailure(s: String?) { + SoraLogger.w(TAG, "setRemoteDescription.onSetFailures reason=$s ${this@PeerChannelImpl}") + it.onError(Error(s)) + } + }, + sdp + ) + } + ).subscribeOn(Schedulers.from(executor)) private fun closeInternal() { if (closing) @@ -589,5 +649,4 @@ class PeerChannelImpl( handler(null) } } - } diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/PeerNetworkConfig.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/PeerNetworkConfig.kt index 85e4c4b1..3aa31073 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/PeerNetworkConfig.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/PeerNetworkConfig.kt @@ -6,8 +6,8 @@ import org.webrtc.CryptoOptions import org.webrtc.PeerConnection class PeerNetworkConfig( - private val serverConfig: OfferConfig?, - private val mediaOption: SoraMediaOption + private val serverConfig: OfferConfig?, + private val mediaOption: SoraMediaOption ) { fun createConfiguration(): PeerConnection.RTCConfiguration { @@ -19,10 +19,10 @@ class PeerNetworkConfig( conf.iceTransportsType = PeerConnection.IceTransportsType.RELAY } - conf.bundlePolicy = PeerConnection.BundlePolicy.MAXBUNDLE - conf.rtcpMuxPolicy = PeerConnection.RtcpMuxPolicy.REQUIRE + conf.bundlePolicy = PeerConnection.BundlePolicy.MAXBUNDLE + conf.rtcpMuxPolicy = PeerConnection.RtcpMuxPolicy.REQUIRE conf.continualGatheringPolicy = PeerConnection.ContinualGatheringPolicy.GATHER_CONTINUALLY - conf.keyType = PeerConnection.KeyType.ECDSA + conf.keyType = PeerConnection.KeyType.ECDSA val cryptoOptions = CryptoOptions.builder() .setEnableGcmCryptoSuites(true) .createCryptoOptions() @@ -38,19 +38,20 @@ class PeerNetworkConfig( private fun gatherIceServerSetting(serverConfig: OfferConfig?): List { val iceServers = mutableListOf() - serverConfig?.let { it.iceServers.forEach { - val server = it - server.urls.forEach { - val url = it - iceServers.add(PeerConnection.IceServer.builder(url) - .setUsername(server.username) - .setPassword(server.credential) - .createIceServer()) + serverConfig?.let { + it.iceServers.forEach { + val server = it + server.urls.forEach { + val url = it + iceServers.add( + PeerConnection.IceServer.builder(url) + .setUsername(server.username) + .setPassword(server.credential) + .createIceServer() + ) + } } } - } return iceServers } - } - diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/RTCComponentFactory.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/RTCComponentFactory.kt index 653016d9..21165904 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/RTCComponentFactory.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/RTCComponentFactory.kt @@ -6,13 +6,19 @@ import jp.shiguredo.sora.sdk.channel.option.SoraVideoOption import jp.shiguredo.sora.sdk.codec.SimulcastVideoEncoderFactoryWrapper import jp.shiguredo.sora.sdk.error.SoraErrorReason import jp.shiguredo.sora.sdk.util.SoraLogger -import org.webrtc.* +import org.webrtc.DefaultVideoDecoderFactory +import org.webrtc.DefaultVideoEncoderFactory +import org.webrtc.MediaConstraints +import org.webrtc.PeerConnectionFactory +import org.webrtc.SoftwareVideoDecoderFactory +import org.webrtc.SoftwareVideoEncoderFactory import org.webrtc.audio.AudioDeviceModule import org.webrtc.audio.JavaAudioDeviceModule - -class RTCComponentFactory(private val mediaOption: SoraMediaOption, - private val listener: PeerChannel.Listener?) { +class RTCComponentFactory( + private val mediaOption: SoraMediaOption, + private val listener: PeerChannel.Listener? +) { companion object { private val TAG = RTCComponentFactory::class.simpleName } @@ -21,10 +27,10 @@ class RTCComponentFactory(private val mediaOption: SoraMediaOption, // そうでないと Effect の ClassLoader.loadClass で NPE が発生する。 fun createPeerConnectionFactory(appContext: Context): PeerConnectionFactory { val cl = Thread.currentThread().contextClassLoader - SoraLogger.d(TAG, "createPeerConnectionFactory(): classloader=${cl}") + SoraLogger.d(TAG, "createPeerConnectionFactory(): classloader=$cl") val factoryOptions = PeerConnectionFactory.Options() val factoryBuilder = PeerConnectionFactory.builder() - .setOptions(factoryOptions) + .setOptions(factoryOptions) // DefaultVideoEncoderFactory, DefaultVideoDecoderFactory は // EglBase.Context を与えるとハードウェアエンコーダーを使用する @@ -34,13 +40,17 @@ class RTCComponentFactory(private val mediaOption: SoraMediaOption, mediaOption.videoEncoderFactory != null -> mediaOption.videoEncoderFactory!! mediaOption.simulcastEnabled -> - SimulcastVideoEncoderFactoryWrapper(mediaOption.videoUpstreamContext, - true, - false) + SimulcastVideoEncoderFactoryWrapper( + mediaOption.videoUpstreamContext, + true, + false + ) mediaOption.videoUpstreamContext != null -> - DefaultVideoEncoderFactory(mediaOption.videoUpstreamContext, - true /* enableIntelVp8Encoder */, - false /* enableH264HighProfile */) + DefaultVideoEncoderFactory( + mediaOption.videoUpstreamContext, + true /* enableIntelVp8Encoder */, + false /* enableH264HighProfile */ + ) // 注意: 視聴のみかつ H.264 のみの場合のワークアラウンド // upstream context が設定されていない場合、 @@ -49,10 +59,12 @@ class RTCComponentFactory(private val mediaOption: SoraMediaOption, // H.264 に限定する理由は、 VP8/VP9 対応のハードウェアエンコーダーを // 搭載していない端末があるため mediaOption.videoCodec == SoraVideoOption.Codec.H264 && - mediaOption.videoDownstreamContext != null -> - DefaultVideoEncoderFactory(mediaOption.videoDownstreamContext, - false /* enableIntelVp8Encoder */, - false /* enableH264HighProfile */) + mediaOption.videoDownstreamContext != null -> + DefaultVideoEncoderFactory( + mediaOption.videoDownstreamContext, + false /* enableIntelVp8Encoder */, + false /* enableH264HighProfile */ + ) else -> // context が指定されていなければソフトウェアエンコーダーを使用する SoftwareVideoEncoderFactory() @@ -85,9 +97,9 @@ class RTCComponentFactory(private val mediaOption: SoraMediaOption, createJavaAudioDevice(appContext) } factoryBuilder - .setAudioDeviceModule(audioDeviceModule) - .setVideoEncoderFactory(encoderFactory) - .setVideoDecoderFactory(decoderFactory) + .setAudioDeviceModule(audioDeviceModule) + .setVideoEncoderFactory(encoderFactory) + .setVideoDecoderFactory(decoderFactory) // option で渡ってきた場合の所有権はアプリケーションにある。 // ここで生成した場合だけ解放する。 if (mediaOption.audioOption.audioDeviceModule == null) { @@ -99,15 +111,15 @@ class RTCComponentFactory(private val mediaOption: SoraMediaOption, fun createSDPConstraints(): MediaConstraints { val constraints = MediaConstraints() - SoraLogger.d(TAG, "createSDPConstraints: ${constraints}") + SoraLogger.d(TAG, "createSDPConstraints: $constraints") return constraints } - fun createVideoManager() : RTCLocalVideoManager { + fun createVideoManager(): RTCLocalVideoManager { val videoManager = mediaOption.videoCapturer?.let { RTCLocalVideoManagerImpl(it) } ?: RTCNullLocalVideoManager() - SoraLogger.d(TAG, "videoManager created: ${videoManager}") + SoraLogger.d(TAG, "videoManager created: $videoManager") return videoManager } @@ -124,7 +136,9 @@ class RTCComponentFactory(private val mediaOption: SoraMediaOption, } override fun onWebRtcAudioRecordStartError( - errorCode: JavaAudioDeviceModule.AudioRecordStartErrorCode, errorMessage: String) { + errorCode: JavaAudioDeviceModule.AudioRecordStartErrorCode, + errorMessage: String + ) { SoraLogger.e(TAG, "onWebRtcAudioRecordStartError: $errorCode. $errorMessage") reportError(SoraErrorReason.AUDIO_RECORD_START_ERROR, "$errorMessage [$errorCode]") } @@ -142,7 +156,9 @@ class RTCComponentFactory(private val mediaOption: SoraMediaOption, } override fun onWebRtcAudioTrackStartError( - errorCode: JavaAudioDeviceModule.AudioTrackStartErrorCode, errorMessage: String) { + errorCode: JavaAudioDeviceModule.AudioTrackStartErrorCode, + errorMessage: String + ) { SoraLogger.e(TAG, "onWebRtcAudioTrackStartError: $errorCode. $errorMessage") reportError(SoraErrorReason.AUDIO_TRACK_START_ERROR, "$errorMessage [$errorCode]") } @@ -154,18 +170,20 @@ class RTCComponentFactory(private val mediaOption: SoraMediaOption, } return JavaAudioDeviceModule.builder(appContext) - .setUseHardwareAcousticEchoCanceler( - JavaAudioDeviceModule.isBuiltInAcousticEchoCancelerSupported() - && mediaOption.audioOption.useHardwareAcousticEchoCanceler) - .setUseHardwareNoiseSuppressor( - JavaAudioDeviceModule.isBuiltInNoiseSuppressorSupported() - && mediaOption.audioOption.useHardwareNoiseSuppressor) - .setAudioRecordErrorCallback(audioRecordErrorCallback) - .setAudioTrackErrorCallback(audioTrackErrorCallback) - .setAudioSource(mediaOption.audioOption.audioSource) - .setUseStereoInput(mediaOption.audioOption.useStereoInput) - .setUseStereoOutput(mediaOption.audioOption.useStereoOutput) - .createAudioDeviceModule() + .setUseHardwareAcousticEchoCanceler( + JavaAudioDeviceModule.isBuiltInAcousticEchoCancelerSupported() && + mediaOption.audioOption.useHardwareAcousticEchoCanceler + ) + .setUseHardwareNoiseSuppressor( + JavaAudioDeviceModule.isBuiltInNoiseSuppressorSupported() && + mediaOption.audioOption.useHardwareNoiseSuppressor + ) + .setAudioRecordErrorCallback(audioRecordErrorCallback) + .setAudioTrackErrorCallback(audioTrackErrorCallback) + .setAudioSource(mediaOption.audioOption.audioSource) + .setUseStereoInput(mediaOption.audioOption.useStereoInput) + .setUseStereoOutput(mediaOption.audioOption.useStereoOutput) + .createAudioDeviceModule() } private fun reportError(errorReason: SoraErrorReason, errorMessage: String) { diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/RTCLocalAudioManager.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/RTCLocalAudioManager.kt index ea61c490..0ba96e3c 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/RTCLocalAudioManager.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/RTCLocalAudioManager.kt @@ -2,12 +2,15 @@ package jp.shiguredo.sora.sdk.channel.rtc import jp.shiguredo.sora.sdk.channel.option.SoraAudioOption import jp.shiguredo.sora.sdk.util.SoraLogger -import org.webrtc.* -import java.util.* - +import org.webrtc.AudioSource +import org.webrtc.AudioTrack +import org.webrtc.MediaConstraints +import org.webrtc.MediaStream +import org.webrtc.PeerConnectionFactory +import java.util.UUID class RTCLocalAudioManager( - private val send: Boolean + private val send: Boolean ) { companion object { @@ -15,18 +18,18 @@ class RTCLocalAudioManager( } private var source: AudioSource? = null - private var track: AudioTrack? = null + private var track: AudioTrack? = null fun initTrack(factory: PeerConnectionFactory, audioOption: SoraAudioOption) { - SoraLogger.d(TAG, "initTrack: send=${send}") + SoraLogger.d(TAG, "initTrack: send=$send") if (send) { val constraints = createSourceConstraints(audioOption) source = factory.createAudioSource(constraints) - SoraLogger.d(TAG, "audio source created: ${source}") + SoraLogger.d(TAG, "audio source created: $source") val trackId = UUID.randomUUID().toString() track = factory.createAudioTrack(trackId, source) track!!.setEnabled(true) - SoraLogger.d(TAG, "audio track created: ${track}") + SoraLogger.d(TAG, "audio track created: $track") } } @@ -34,19 +37,23 @@ class RTCLocalAudioManager( val constraints = MediaConstraints() if (!audioOption.audioProcessingEchoCancellation) { constraints.mandatory.add( - MediaConstraints.KeyValuePair(SoraAudioOption.ECHO_CANCELLATION_CONSTRAINT, "false")) + MediaConstraints.KeyValuePair(SoraAudioOption.ECHO_CANCELLATION_CONSTRAINT, "false") + ) } - if(!audioOption.audioProcessingAutoGainControl) { + if (!audioOption.audioProcessingAutoGainControl) { constraints.mandatory.add( - MediaConstraints.KeyValuePair(SoraAudioOption.AUTO_GAIN_CONTROL_CONSTRAINT, "false")) + MediaConstraints.KeyValuePair(SoraAudioOption.AUTO_GAIN_CONTROL_CONSTRAINT, "false") + ) } if (!audioOption.audioProcessingHighpassFilter) { constraints.mandatory.add( - MediaConstraints.KeyValuePair(SoraAudioOption.HIGH_PASS_FILTER_CONSTRAINT, "false")) + MediaConstraints.KeyValuePair(SoraAudioOption.HIGH_PASS_FILTER_CONSTRAINT, "false") + ) } if (!audioOption.audioProcessingNoiseSuppression) { constraints.mandatory.add( - MediaConstraints.KeyValuePair(SoraAudioOption.NOISE_SUPPRESSION_CONSTRAINT, "false")) + MediaConstraints.KeyValuePair(SoraAudioOption.NOISE_SUPPRESSION_CONSTRAINT, "false") + ) } return constraints } @@ -63,4 +70,3 @@ class RTCLocalAudioManager( source = null } } - diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/RTCLocalVideoManager.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/RTCLocalVideoManager.kt index 9d48d8e4..b29f7e88 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/RTCLocalVideoManager.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/RTCLocalVideoManager.kt @@ -2,42 +2,52 @@ package jp.shiguredo.sora.sdk.channel.rtc import android.content.Context import jp.shiguredo.sora.sdk.util.SoraLogger -import org.webrtc.* -import java.util.* +import org.webrtc.EglBase +import org.webrtc.MediaStream +import org.webrtc.PeerConnectionFactory +import org.webrtc.SurfaceTextureHelper +import org.webrtc.VideoCapturer +import org.webrtc.VideoSource +import org.webrtc.VideoTrack +import java.util.UUID interface RTCLocalVideoManager { - fun initTrack(factory: PeerConnectionFactory, - eglContext: EglBase.Context?, - appContext: Context) + fun initTrack( + factory: PeerConnectionFactory, + eglContext: EglBase.Context?, + appContext: Context + ) fun attachTrackToStream(stream: MediaStream) fun dispose() } // just for Null-Object-Pattern -class RTCNullLocalVideoManager: RTCLocalVideoManager { - override fun initTrack(factory: PeerConnectionFactory, - eglContext: EglBase.Context?, - appContext: Context) {} +class RTCNullLocalVideoManager : RTCLocalVideoManager { + override fun initTrack( + factory: PeerConnectionFactory, + eglContext: EglBase.Context?, + appContext: Context + ) {} override fun attachTrackToStream(stream: MediaStream) {} override fun dispose() {} } -class RTCLocalVideoManagerImpl(private val capturer: VideoCapturer): RTCLocalVideoManager { +class RTCLocalVideoManagerImpl(private val capturer: VideoCapturer) : RTCLocalVideoManager { companion object { private val TAG = RTCLocalVideoManagerImpl::class.simpleName } var source: VideoSource? = null - var track: VideoTrack? = null + var track: VideoTrack? = null var surfaceTextureHelper: SurfaceTextureHelper? = null override fun initTrack(factory: PeerConnectionFactory, eglContext: EglBase.Context?, appContext: Context) { SoraLogger.d(TAG, "initTrack isScreencast=${capturer.isScreencast}") surfaceTextureHelper = - SurfaceTextureHelper.create("CaptureThread", eglContext); - source = factory.createVideoSource(capturer.isScreencast); - capturer.initialize(surfaceTextureHelper, appContext, source!!.capturerObserver); + SurfaceTextureHelper.create("CaptureThread", eglContext) + source = factory.createVideoSource(capturer.isScreencast) + capturer.initialize(surfaceTextureHelper, appContext, source!!.capturerObserver) val trackId = UUID.randomUUID().toString() track = factory.createVideoTrack(trackId, source) @@ -47,7 +57,7 @@ class RTCLocalVideoManagerImpl(private val capturer: VideoCapturer): RTCLocalVid override fun attachTrackToStream(stream: MediaStream) { SoraLogger.d(TAG, "attachTrackToStream") - track?.let{ stream.addTrack(it) } + track?.let { stream.addTrack(it) } } override fun dispose() { diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt index aec33861..11277525 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt @@ -2,10 +2,18 @@ package jp.shiguredo.sora.sdk.channel.signaling import jp.shiguredo.sora.sdk.channel.option.SoraChannelRole import jp.shiguredo.sora.sdk.channel.option.SoraMediaOption -import jp.shiguredo.sora.sdk.channel.signaling.message.* +import jp.shiguredo.sora.sdk.channel.signaling.message.MessageConverter +import jp.shiguredo.sora.sdk.channel.signaling.message.NotificationMessage +import jp.shiguredo.sora.sdk.channel.signaling.message.OfferMessage +import jp.shiguredo.sora.sdk.channel.signaling.message.PushMessage +import jp.shiguredo.sora.sdk.channel.signaling.message.SwitchedMessage import jp.shiguredo.sora.sdk.error.SoraErrorReason import jp.shiguredo.sora.sdk.util.SoraLogger -import okhttp3.* +import okhttp3.OkHttpClient +import okhttp3.Request +import okhttp3.Response +import okhttp3.WebSocket +import okhttp3.WebSocketListener import okio.ByteString import org.webrtc.RTCStatsReport import org.webrtc.SessionDescription @@ -36,17 +44,17 @@ interface SignalingChannel { } class SignalingChannelImpl @JvmOverloads constructor( - private val endpoint: String, - private val role: SoraChannelRole, - private val channelId: String, - private val connectDataChannelSignaling: Boolean? = null, - private val connectIgnoreDisconnectWebSocket: Boolean? = null, - private val mediaOption: SoraMediaOption, - private val connectMetadata: Any?, - private var listener: SignalingChannel.Listener?, - private val clientOfferSdp: SessionDescription?, - private val clientId: String? = null, - private val signalingNotifyMetadata: Any? = null + private val endpoint: String, + private val role: SoraChannelRole, + private val channelId: String, + private val connectDataChannelSignaling: Boolean? = null, + private val connectIgnoreDisconnectWebSocket: Boolean? = null, + private val mediaOption: SoraMediaOption, + private val connectMetadata: Any?, + private var listener: SignalingChannel.Listener?, + private val clientOfferSdp: SessionDescription?, + private val clientId: String? = null, + private val signalingNotifyMetadata: Any? = null ) : SignalingChannel { companion object { @@ -57,11 +65,11 @@ class SignalingChannelImpl @JvmOverloads constructor( OkHttpClient.Builder().readTimeout(0, TimeUnit.MILLISECONDS).build() private var webSocket: WebSocket? = null - private var closing = false + private var closing = false override fun connect() { - val url = "${endpoint}?channel_id=${channelId}" - SoraLogger.i(TAG, "[signaling:$role] start to connect ${url}") + val url = "$endpoint?channel_id=$channelId" + SoraLogger.i(TAG, "[signaling:$role] start to connect $url") val request = Request.Builder().url(url).build() client.newWebSocket(request, webSocketListener) } @@ -155,15 +163,15 @@ class SignalingChannelImpl @JvmOverloads constructor( webSocket?.let { SoraLogger.d(TAG, "[signaling:$role] -> connect") val message = MessageConverter.buildConnectMessage( - role = role, - channelId = channelId, - dataChannelSignaling = connectDataChannelSignaling, - ignoreDisconnectWebSocket = connectIgnoreDisconnectWebSocket, - mediaOption = mediaOption, - metadata = connectMetadata, - sdp = clientOfferSdp?.description, - clientId = clientId, - signalingNotifyMetadata = signalingNotifyMetadata + role = role, + channelId = channelId, + dataChannelSignaling = connectDataChannelSignaling, + ignoreDisconnectWebSocket = connectIgnoreDisconnectWebSocket, + mediaOption = mediaOption, + metadata = connectMetadata, + sdp = clientOfferSdp?.description, + clientId = clientId, + signalingNotifyMetadata = signalingNotifyMetadata ) it.send(message) } @@ -176,15 +184,18 @@ class SignalingChannelImpl @JvmOverloads constructor( private fun onOfferMessage(text: String) { val offerMessage = MessageConverter.parseOfferMessage(text) - SoraLogger.d(TAG, """[signaling:$role] <- offer - |${offerMessage.sdp}""".trimMargin()) + SoraLogger.d( + TAG, + """[signaling:$role] <- offer + |${offerMessage.sdp}""".trimMargin() + ) listener?.onInitialOffer(offerMessage) } private fun onSwitchedMessage(text: String) { val switchMessage = MessageConverter.parseSwitchMessage(text) - SoraLogger.d(TAG, "[signaling:$role] <- switch ${switchMessage}") + SoraLogger.d(TAG, "[signaling:$role] <- switch $switchMessage") listener?.onSwitched(switchMessage) } @@ -275,19 +286,17 @@ class SignalingChannelImpl @JvmOverloads constructor( val json = it MessageConverter.parseType(json)?.let { when (it) { - "offer" -> onOfferMessage(json) + "offer" -> onOfferMessage(json) "switched" -> onSwitchedMessage(json) - "ping" -> onPingMessage(json) - "update" -> onUpdateMessage(json) + "ping" -> onPingMessage(json) + "update" -> onUpdateMessage(json) "re-offer" -> onReOfferMessage(json) - "notify" -> onNotifyMessage(json) - "push" -> onPushMessage(json) - else -> SoraLogger.i(TAG, "received unknown-type message") + "notify" -> onNotifyMessage(json) + "push" -> onPushMessage(json) + else -> SoraLogger.i(TAG, "received unknown-type message") } - } ?: closeWithError("failed to parse 'type' from message") } - } catch (e: Exception) { SoraLogger.w(TAG, e.toString()) } @@ -301,9 +310,9 @@ class SignalingChannelImpl @JvmOverloads constructor( override fun onClosed(webSocket: WebSocket, code: Int, reason: String) { try { if (code == 1000) { - SoraLogger.i(TAG, "[signaling:$role] @onClosed: reason = [${reason}], code = ${code}") + SoraLogger.i(TAG, "[signaling:$role] @onClosed: reason = [$reason], code = $code") } else { - SoraLogger.w(TAG, "[signaling:$role] @onClosed: reason = [${reason}], code = ${code}") + SoraLogger.w(TAG, "[signaling:$role] @onClosed: reason = [$reason], code = $code") } disconnect() } catch (e: Exception) { @@ -330,4 +339,3 @@ class SignalingChannelImpl @JvmOverloads constructor( } } } - diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/message/Catalog.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/message/Catalog.kt index 67ad23d1..b7037599 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/message/Catalog.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/message/Catalog.kt @@ -4,175 +4,175 @@ import com.google.gson.annotations.SerializedName import jp.shiguredo.sora.sdk.util.SDKInfo data class MessageCommonPart( - @SerializedName("type") val type: String? + @SerializedName("type") val type: String? ) data class PingMessage( - @SerializedName("type") val type: String = "ping", - @SerializedName("stats") val stats: Boolean? + @SerializedName("type") val type: String = "ping", + @SerializedName("stats") val stats: Boolean? ) data class PongMessage( - @SerializedName("type") val type: String = "pong", - @SerializedName("stats") val stats: List? = null + @SerializedName("type") val type: String = "pong", + @SerializedName("stats") val stats: List? = null ) data class ConnectMessage( - @SerializedName("type") val type: String = "connect", - @SerializedName("role") var role: String, - @SerializedName("channel_id") val channelId: String, - @SerializedName("client_id") val clientId: String? = null, - @SerializedName("metadata") val metadata: Any? = null, - @SerializedName("signaling_notify_metadata") - val signalingNotifyMetadata: Any? = null, - @SerializedName("multistream") val multistream: Boolean = false, - @SerializedName("spotlight") var spotlight: Any? = null, - @SerializedName("spotlight_number") - var spotlightNumber: Int? = null, - @SerializedName("spotlight_focus_rid") var spotlightFocusRid: String? = null, - @SerializedName("spotlight_unfocus_rid") var spotlightUnfocusRid: String? = null, - @SerializedName("simulcast") var simulcast: Boolean? = false, - @SerializedName("simulcast_rid") - var simulcastRid: String? = null, - @SerializedName("video") var video: Any? = null, - @SerializedName("audio") var audio: Any? = null, - @SerializedName("sora_client") val soraClient: String = SDKInfo.sdkInfo(), - @SerializedName("libwebrtc") val libwebrtc: String = SDKInfo.libwebrtcInfo(), - @SerializedName("environment") val environment: String = SDKInfo.deviceInfo(), - @SerializedName("sdp") val sdp: String? = null, - @SerializedName("data_channel_signaling") - val dataChannelSignaling: Boolean? = null, - @SerializedName("ignore_disconnect_websocket") - val ignoreDisconnectWebsocket: Boolean? = null + @SerializedName("type") val type: String = "connect", + @SerializedName("role") var role: String, + @SerializedName("channel_id") val channelId: String, + @SerializedName("client_id") val clientId: String? = null, + @SerializedName("metadata") val metadata: Any? = null, + @SerializedName("signaling_notify_metadata") + val signalingNotifyMetadata: Any? = null, + @SerializedName("multistream") val multistream: Boolean = false, + @SerializedName("spotlight") var spotlight: Any? = null, + @SerializedName("spotlight_number") + var spotlightNumber: Int? = null, + @SerializedName("spotlight_focus_rid") var spotlightFocusRid: String? = null, + @SerializedName("spotlight_unfocus_rid") var spotlightUnfocusRid: String? = null, + @SerializedName("simulcast") var simulcast: Boolean? = false, + @SerializedName("simulcast_rid") + var simulcastRid: String? = null, + @SerializedName("video") var video: Any? = null, + @SerializedName("audio") var audio: Any? = null, + @SerializedName("sora_client") val soraClient: String = SDKInfo.sdkInfo(), + @SerializedName("libwebrtc") val libwebrtc: String = SDKInfo.libwebrtcInfo(), + @SerializedName("environment") val environment: String = SDKInfo.deviceInfo(), + @SerializedName("sdp") val sdp: String? = null, + @SerializedName("data_channel_signaling") + val dataChannelSignaling: Boolean? = null, + @SerializedName("ignore_disconnect_websocket") + val ignoreDisconnectWebsocket: Boolean? = null ) data class VideoSetting( - @SerializedName("codec_type") val codecType: String, - @SerializedName("bit_rate") var bitRate: Int? = null + @SerializedName("codec_type") val codecType: String, + @SerializedName("bit_rate") var bitRate: Int? = null ) data class AudioSetting( - @SerializedName("codec_type") val codecType: String?, - @SerializedName("bit_rate") var bitRate: Int? = null, - @SerializedName("opus_params") var opusParams: OpusParams? = null + @SerializedName("codec_type") val codecType: String?, + @SerializedName("bit_rate") var bitRate: Int? = null, + @SerializedName("opus_params") var opusParams: OpusParams? = null ) data class OpusParams( - @SerializedName("channels") var channels: Int? = null, - @SerializedName("clock_rate") var clockRate: Int? = null, - @SerializedName("maxplaybackrate") var maxplaybackrate: Int? = null, - @SerializedName("stereo") var stereo: Boolean? = null, - @SerializedName("sprop_stereo") var spropStereo: Boolean? = null, - @SerializedName("minptime") var minptime: Int? = null, - @SerializedName("useinbandfec") var useinbandfec: Boolean? = null, - @SerializedName("usedtx") var usedtx: Boolean? = null + @SerializedName("channels") var channels: Int? = null, + @SerializedName("clock_rate") var clockRate: Int? = null, + @SerializedName("maxplaybackrate") var maxplaybackrate: Int? = null, + @SerializedName("stereo") var stereo: Boolean? = null, + @SerializedName("sprop_stereo") var spropStereo: Boolean? = null, + @SerializedName("minptime") var minptime: Int? = null, + @SerializedName("useinbandfec") var useinbandfec: Boolean? = null, + @SerializedName("usedtx") var usedtx: Boolean? = null ) data class IceServer( - @SerializedName("urls") val urls: List, - @SerializedName("credential") val credential: String, - @SerializedName("username") val username: String + @SerializedName("urls") val urls: List, + @SerializedName("credential") val credential: String, + @SerializedName("username") val username: String ) data class OfferConfig( - @SerializedName("iceServers") val iceServers: List, - @SerializedName("iceTransportPolicy") val iceTransportPolicy: String + @SerializedName("iceServers") val iceServers: List, + @SerializedName("iceTransportPolicy") val iceTransportPolicy: String ) data class Encoding( - @SerializedName("rid") val rid: String?, - @SerializedName("active") val active: Boolean?, - @SerializedName("maxBitrate") val maxBitrate: Int?, - @SerializedName("maxFramerate") val maxFramerate: Int?, - @SerializedName("scaleResolutionDownBy") val scaleResolutionDownBy: Double? + @SerializedName("rid") val rid: String?, + @SerializedName("active") val active: Boolean?, + @SerializedName("maxBitrate") val maxBitrate: Int?, + @SerializedName("maxFramerate") val maxFramerate: Int?, + @SerializedName("scaleResolutionDownBy") val scaleResolutionDownBy: Double? ) data class OfferMessage( - @SerializedName("type") val type: String = "offer", - @SerializedName("sdp") val sdp: String, - @SerializedName("client_id") val clientId: String, - @SerializedName("connection_id") val connectionId: String, - @SerializedName("metadata") val metadata: Any?, - @SerializedName("config") val config: OfferConfig? = null, - @SerializedName("mid") val mid: Map? = null, - @SerializedName("encodings") val encodings: List?, - @SerializedName("data_channels") val dataChannels: List>? = null + @SerializedName("type") val type: String = "offer", + @SerializedName("sdp") val sdp: String, + @SerializedName("client_id") val clientId: String, + @SerializedName("connection_id") val connectionId: String, + @SerializedName("metadata") val metadata: Any?, + @SerializedName("config") val config: OfferConfig? = null, + @SerializedName("mid") val mid: Map? = null, + @SerializedName("encodings") val encodings: List?, + @SerializedName("data_channels") val dataChannels: List>? = null ) data class SwitchedMessage( - @SerializedName("type") val type: String = "switched", - @SerializedName("ignore_disconnect_websocket") val ignoreDisconnectWebsocket: Boolean? = null + @SerializedName("type") val type: String = "switched", + @SerializedName("ignore_disconnect_websocket") val ignoreDisconnectWebsocket: Boolean? = null ) data class UpdateMessage( - @SerializedName("type") val type: String = "update", - @SerializedName("sdp") val sdp: String + @SerializedName("type") val type: String = "update", + @SerializedName("sdp") val sdp: String ) data class ReOfferMessage( - @SerializedName("type") val type: String = "re-offer", - @SerializedName("sdp") val sdp: String + @SerializedName("type") val type: String = "re-offer", + @SerializedName("sdp") val sdp: String ) data class ReAnswerMessage( - @SerializedName("type") val type: String = "re-answer", - @SerializedName("sdp") val sdp: String + @SerializedName("type") val type: String = "re-answer", + @SerializedName("sdp") val sdp: String ) data class AnswerMessage( - @SerializedName("type") val type: String = "answer", - @SerializedName("sdp") val sdp: String + @SerializedName("type") val type: String = "answer", + @SerializedName("sdp") val sdp: String ) data class CandidateMessage( - @SerializedName("type") val type: String = "candidate", - @SerializedName("candidate") val candidate: String + @SerializedName("type") val type: String = "candidate", + @SerializedName("candidate") val candidate: String ) data class PushMessage( - @SerializedName("type") val type: String = "push", - @SerializedName("data") var data: Any? = null + @SerializedName("type") val type: String = "push", + @SerializedName("data") var data: Any? = null ) data class ReqStatsMessage( - @SerializedName("type") val type: String = "req-stats" + @SerializedName("type") val type: String = "req-stats" ) data class StatsMessage( - @SerializedName("type") val type: String = "stats", - @SerializedName("reports") val reports: List + @SerializedName("type") val type: String = "stats", + @SerializedName("reports") val reports: List ) data class NotificationMessage( - @SerializedName("type") val type: String = "notify", - @SerializedName("event_type") val eventType: String, - @SerializedName("role") val role: String?, - @SerializedName("client_id") val clientId: String, - @SerializedName("connection_id") val connectionId: String?, - @SerializedName("audio") val audio: Boolean?, - @SerializedName("video") val video: Boolean?, - @SerializedName("metadata") val metadata: Any?, - @Deprecated("metadata_list は将来の Sora のリリースでフィールド名を data に変更する予定です。") - @SerializedName("metadata_list") val metadataList: Any?, - @SerializedName("minutes") val connectionTime: Long?, - @SerializedName("channel_connections") val numberOfConnections: Int?, - @Deprecated("numberOfUpstreamConnections は 2021 年 6 月リリース予定の Sora にて廃止されます。") - @SerializedName("channel_upstream_connections") val numberOfUpstreamConnections: Int?, - @Deprecated("numberOfDownstreamConnections は 2021 年 6 月リリース予定の Sora にて廃止されます。") - @SerializedName("channel_downstream_connections") val numberOfDownstreamConnections: Int?, - @SerializedName("channel_sendrecv_connections") val numberOfSendrecvConnections: Int?, - @SerializedName("channel_sendonly_connections") val numberOfSendonlyConnections: Int?, - @SerializedName("channel_recvonly_connections") val numberOfRecvonlyConnections: Int?, - @SerializedName("unstable_level") val unstableLevel: Int?, - @SerializedName("channel_id") val channelId: String?, - @SerializedName("spotlight_id") val spotlightId: String?, - @SerializedName("fixed") val fixed: Boolean?, - @SerializedName("authn_metadata") val authnMetadata: Any?, - @SerializedName("authz_metadata") val authzMetadata: Any?, - @SerializedName("data") val data: Any?, - @SerializedName("turn_transport_type") val turnTransportType: String?, + @SerializedName("type") val type: String = "notify", + @SerializedName("event_type") val eventType: String, + @SerializedName("role") val role: String?, + @SerializedName("client_id") val clientId: String, + @SerializedName("connection_id") val connectionId: String?, + @SerializedName("audio") val audio: Boolean?, + @SerializedName("video") val video: Boolean?, + @SerializedName("metadata") val metadata: Any?, + @Deprecated("metadata_list は将来の Sora のリリースでフィールド名を data に変更する予定です。") + @SerializedName("metadata_list") val metadataList: Any?, + @SerializedName("minutes") val connectionTime: Long?, + @SerializedName("channel_connections") val numberOfConnections: Int?, + @Deprecated("numberOfUpstreamConnections は 2021 年 6 月リリース予定の Sora にて廃止されます。") + @SerializedName("channel_upstream_connections") val numberOfUpstreamConnections: Int?, + @Deprecated("numberOfDownstreamConnections は 2021 年 6 月リリース予定の Sora にて廃止されます。") + @SerializedName("channel_downstream_connections") val numberOfDownstreamConnections: Int?, + @SerializedName("channel_sendrecv_connections") val numberOfSendrecvConnections: Int?, + @SerializedName("channel_sendonly_connections") val numberOfSendonlyConnections: Int?, + @SerializedName("channel_recvonly_connections") val numberOfRecvonlyConnections: Int?, + @SerializedName("unstable_level") val unstableLevel: Int?, + @SerializedName("channel_id") val channelId: String?, + @SerializedName("spotlight_id") val spotlightId: String?, + @SerializedName("fixed") val fixed: Boolean?, + @SerializedName("authn_metadata") val authnMetadata: Any?, + @SerializedName("authz_metadata") val authzMetadata: Any?, + @SerializedName("data") val data: Any?, + @SerializedName("turn_transport_type") val turnTransportType: String?, ) data class DisconnectMessage( - @SerializedName("type") val type: String = "disconnect" + @SerializedName("type") val type: String = "disconnect" ) diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/message/MessageConverter.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/message/MessageConverter.kt index ecb43f6e..dc588bed 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/message/MessageConverter.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/message/MessageConverter.kt @@ -8,11 +8,14 @@ import jp.shiguredo.sora.sdk.util.SoraLogger import org.webrtc.RTCStats import org.webrtc.RTCStatsReport -class SoraRTCStats(private val map: Map): Map by map { - constructor(stats: RTCStats) : this(mapOf( - "id" to stats.id, - "type" to stats.type, - "timestamp" to stats.timestampUs) + stats.members) {} +class SoraRTCStats(private val map: Map) : Map by map { + constructor(stats: RTCStats) : this( + mapOf( + "id" to stats.id, + "type" to stats.type, + "timestamp" to stats.timestampUs + ) + stats.members + ) {} } class MessageConverter { @@ -24,39 +27,40 @@ class MessageConverter { val gson = Gson() @JvmOverloads - fun buildConnectMessage(role: SoraChannelRole, - channelId: String, - dataChannelSignaling: Boolean?, - ignoreDisconnectWebSocket: Boolean?, - mediaOption: SoraMediaOption, - metadata: Any?, - sdp: String? = null, - clientId: String? = null, - signalingNotifyMetadata: Any? = null + fun buildConnectMessage( + role: SoraChannelRole, + channelId: String, + dataChannelSignaling: Boolean?, + ignoreDisconnectWebSocket: Boolean?, + mediaOption: SoraMediaOption, + metadata: Any?, + sdp: String? = null, + clientId: String? = null, + signalingNotifyMetadata: Any? = null ): String { val msg = ConnectMessage( - role = role.signaling, - channelId = channelId, - dataChannelSignaling = dataChannelSignaling, - ignoreDisconnectWebsocket = ignoreDisconnectWebSocket, - metadata = metadata, - multistream = mediaOption.multistreamIsRequired, - spotlight = mediaOption.spotlightOption?.let { - if (Sora.usesSpotlightLegacy) - it.spotlightNumber - else - true - }, - spotlightNumber = mediaOption.spotlightOption?.let { - if (Sora.usesSpotlightLegacy) - null - else - it.spotlightNumber - }, - sdp = sdp, - clientId = clientId, - signalingNotifyMetadata = signalingNotifyMetadata + role = role.signaling, + channelId = channelId, + dataChannelSignaling = dataChannelSignaling, + ignoreDisconnectWebsocket = ignoreDisconnectWebSocket, + metadata = metadata, + multistream = mediaOption.multistreamIsRequired, + spotlight = mediaOption.spotlightOption?.let { + if (Sora.usesSpotlightLegacy) + it.spotlightNumber + else + true + }, + spotlightNumber = mediaOption.spotlightOption?.let { + if (Sora.usesSpotlightLegacy) + null + else + it.spotlightNumber + }, + sdp = sdp, + clientId = clientId, + signalingNotifyMetadata = signalingNotifyMetadata ) if (mediaOption.upstreamIsRequired) { @@ -70,7 +74,6 @@ class MessageConverter { } msg.audio = audioSetting - } else { msg.audio = false } @@ -117,9 +120,13 @@ class MessageConverter { } fun buildPongMessage(stats: RTCStatsReport?): String { - return gson.toJson(PongMessage(stats = stats?.let { - stats.statsMap.values.map { stats -> SoraRTCStats(stats) } - })) + return gson.toJson( + PongMessage( + stats = stats?.let { + stats.statsMap.values.map { stats -> SoraRTCStats(stats) } + } + ) + ) } fun buildUpdateAnswerMessage(sdp: String): String { @@ -184,4 +191,3 @@ class MessageConverter { } } } - diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/codec/SimulcastVideoEncoderFactoryWrapper.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/codec/SimulcastVideoEncoderFactoryWrapper.kt index 9d4915c3..0457f825 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/codec/SimulcastVideoEncoderFactoryWrapper.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/codec/SimulcastVideoEncoderFactoryWrapper.kt @@ -4,9 +4,11 @@ import jp.shiguredo.sora.sdk.util.SoraLogger import org.webrtc.* import java.util.concurrent.* -internal class SimulcastVideoEncoderFactoryWrapper(sharedContext: EglBase.Context?, - enableIntelVp8Encoder: Boolean, - enableH264HighProfile: Boolean) : VideoEncoderFactory { +internal class SimulcastVideoEncoderFactoryWrapper( + sharedContext: EglBase.Context?, + enableIntelVp8Encoder: Boolean, + enableH264HighProfile: Boolean +) : VideoEncoderFactory { /* * ソフトウェアエンコーダーの利用を優先するファクトリーです。 @@ -45,7 +47,6 @@ internal class SimulcastVideoEncoderFactoryWrapper(sharedContext: EglBase.Contex supportedCodecInfos.addAll(hardwareVideoEncoderFactory.supportedCodecs) return supportedCodecInfos.toTypedArray() } - } // ストリーム単位のエンコーダをラップした上で以下を行うクラス。 @@ -64,8 +65,11 @@ internal class SimulcastVideoEncoderFactoryWrapper(sharedContext: EglBase.Contex override fun initEncode(settings: VideoEncoder.Settings, callback: VideoEncoder.Callback?): VideoCodecStatus { streamSettings = settings - val future = executor.submit(Callable { - SoraLogger.i(TAG, """initEncode() thread=${Thread.currentThread().name} [${Thread.currentThread().id}] + val future = executor.submit( + Callable { + SoraLogger.i( + TAG, + """initEncode() thread=${Thread.currentThread().name} [${Thread.currentThread().id}] | streamSettings: | numberOfCores=${settings.numberOfCores} | width=${settings.width} @@ -75,9 +79,11 @@ internal class SimulcastVideoEncoderFactoryWrapper(sharedContext: EglBase.Contex | automaticResizeOn=${settings.automaticResizeOn} | numberOfSimulcastStreams=${settings.numberOfSimulcastStreams} | lossNotification=${settings.capabilities.lossNotification} - """.trimMargin()) - return@Callable encoder.initEncode(settings, callback) - }) + """.trimMargin() + ) + return@Callable encoder.initEncode(settings, callback) + } + ) return future.get() } @@ -87,29 +93,33 @@ internal class SimulcastVideoEncoderFactoryWrapper(sharedContext: EglBase.Contex } override fun encode(frame: VideoFrame, encodeInfo: VideoEncoder.EncodeInfo?): VideoCodecStatus { - val future = executor.submit(Callable { - // SoraLogger.d(TAG, "encode() buffer=${frame.buffer}, thread=${Thread.currentThread().name} " - // + "[${Thread.currentThread().id}]") - if (streamSettings == null) { - return@Callable encoder.encode(frame, encodeInfo) - } else if (frame.buffer.width == streamSettings!!.width) { - return@Callable encoder.encode(frame, encodeInfo) - } else { - // 上がってきたバッファと initEncode() の設定が違うパターン、ここでスケールする必要がある - val originalBuffer = frame.buffer - // val ratio = originalBuffer.width / streamSettings!!.width - // SoraLogger.d(TAG, "encode: Scaling needed, " + - // "${buffer.width}x${buffer.height} to ${streamSettings!!.width}x${streamSettings!!.height}, " + - // "ratio=$ratio") - // TODO(shino): へんなスケールファクタの場合に正しく動作するか? - val adaptedBuffer = originalBuffer.cropAndScale(0, 0, originalBuffer.width, originalBuffer.height, - streamSettings!!.width, streamSettings!!.height) - val adaptedFrame = VideoFrame(adaptedBuffer, frame.rotation, frame.timestampNs) - val result = encoder.encode(adaptedFrame, encodeInfo) - adaptedBuffer.release() - return@Callable result + val future = executor.submit( + Callable { + // SoraLogger.d(TAG, "encode() buffer=${frame.buffer}, thread=${Thread.currentThread().name} " + // + "[${Thread.currentThread().id}]") + if (streamSettings == null) { + return@Callable encoder.encode(frame, encodeInfo) + } else if (frame.buffer.width == streamSettings!!.width) { + return@Callable encoder.encode(frame, encodeInfo) + } else { + // 上がってきたバッファと initEncode() の設定が違うパターン、ここでスケールする必要がある + val originalBuffer = frame.buffer + // val ratio = originalBuffer.width / streamSettings!!.width + // SoraLogger.d(TAG, "encode: Scaling needed, " + + // "${buffer.width}x${buffer.height} to ${streamSettings!!.width}x${streamSettings!!.height}, " + + // "ratio=$ratio") + // TODO(shino): へんなスケールファクタの場合に正しく動作するか? + val adaptedBuffer = originalBuffer.cropAndScale( + 0, 0, originalBuffer.width, originalBuffer.height, + streamSettings!!.width, streamSettings!!.height + ) + val adaptedFrame = VideoFrame(adaptedBuffer, frame.rotation, frame.timestampNs) + val result = encoder.encode(adaptedFrame, encodeInfo) + adaptedBuffer.release() + return@Callable result + } } - }) + ) return future.get() } @@ -143,14 +153,14 @@ internal class SimulcastVideoEncoderFactoryWrapper(sharedContext: EglBase.Contex } } - private val primary: VideoEncoderFactory private val fallback: VideoEncoderFactory private val native: SimulcastVideoEncoderFactory init { val hardwareVideoEncoderFactory = HardwareVideoEncoderFactory( - sharedContext, enableIntelVp8Encoder, enableH264HighProfile) + sharedContext, enableIntelVp8Encoder, enableH264HighProfile + ) primary = StreamEncoderWrapperFactory(hardwareVideoEncoderFactory) fallback = StreamEncoderWrapperFactory(Fallback(primary)) native = SimulcastVideoEncoderFactory(primary, fallback) @@ -163,5 +173,4 @@ internal class SimulcastVideoEncoderFactoryWrapper(sharedContext: EglBase.Contex override fun getSupportedCodecs(): Array { return native.supportedCodecs } - -} \ No newline at end of file +} diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/util/ByteBufferBackedInputStream.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/util/ByteBufferBackedInputStream.kt index 715237d1..77379499 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/util/ByteBufferBackedInputStream.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/util/ByteBufferBackedInputStream.kt @@ -1,7 +1,6 @@ package jp.shiguredo.sora.sdk.util import java.io.InputStream -import java.io.OutputStream import java.nio.ByteBuffer class ByteBufferBackedInputStream(private val buf: ByteBuffer) : InputStream() { diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/util/ReusableCompositeDisposable.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/util/ReusableCompositeDisposable.kt index 5d9bee6e..f61f0a5c 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/util/ReusableCompositeDisposable.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/util/ReusableCompositeDisposable.kt @@ -21,5 +21,4 @@ class ReusableCompositeDisposable { compositeDisposable?.dispose() compositeDisposable = null } - -} \ No newline at end of file +} diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/util/SDKInfo.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/util/SDKInfo.kt index 70017f18..3947e74a 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/util/SDKInfo.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/util/SDKInfo.kt @@ -10,27 +10,25 @@ class SDKInfo { fun sdkInfo(): String { return "Sora Android SDK " + BuildConfig.VERSION_NAME + - " (" + BuildConfig.REVISION + ")" + " (" + BuildConfig.REVISION + ")" } fun libwebrtcInfo(): String { return "Shiguredo-build " + WebrtcBuildVersion.webrtc_branch + - " (" + BuildConfig.LIBWEBRTC_VERSION + " " + - WebrtcBuildVersion.webrtc_revision.substring(0, 7) + ")" + " (" + BuildConfig.LIBWEBRTC_VERSION + " " + + WebrtcBuildVersion.webrtc_revision.substring(0, 7) + ")" } fun deviceInfo(): String { return "Android-SDK: " + Build.VERSION.SDK_INT + ", " + - "Release: " + Build.VERSION.RELEASE + ", " + - "Id: " + Build.ID + ", " + - "Device: " + Build.DEVICE + ", " + - "Hardware: " + Build.HARDWARE + ", " + - "Brand: " + Build.BRAND + ", " + - "Manufacturer: " + Build.MANUFACTURER + ", " + - "Model: " + Build.MODEL + ", " + - "Product: " + Build.PRODUCT + "Release: " + Build.VERSION.RELEASE + ", " + + "Id: " + Build.ID + ", " + + "Device: " + Build.DEVICE + ", " + + "Hardware: " + Build.HARDWARE + ", " + + "Brand: " + Build.BRAND + ", " + + "Manufacturer: " + Build.MANUFACTURER + ", " + + "Model: " + Build.MODEL + ", " + + "Product: " + Build.PRODUCT } - } - -} \ No newline at end of file +} diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/util/SoraLogger.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/util/SoraLogger.kt index 2c469d7e..06104f98 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/util/SoraLogger.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/util/SoraLogger.kt @@ -45,4 +45,3 @@ class SoraLogger { } } } - diff --git a/sora-android-sdk/src/test/kotlin/jp/shiguredo/sora/sdk/ConnectClientIdTest.kt b/sora-android-sdk/src/test/kotlin/jp/shiguredo/sora/sdk/ConnectClientIdTest.kt index db9f1e10..14833957 100644 --- a/sora-android-sdk/src/test/kotlin/jp/shiguredo/sora/sdk/ConnectClientIdTest.kt +++ b/sora-android-sdk/src/test/kotlin/jp/shiguredo/sora/sdk/ConnectClientIdTest.kt @@ -5,9 +5,6 @@ import jp.shiguredo.sora.sdk.channel.signaling.message.ConnectMessage import org.junit.Test import kotlin.test.assertEquals import kotlin.test.assertFalse -import kotlin.test.assertTrue -import kotlin.test.fail - class ConnectClientIdTest { val gson = Gson() @@ -26,14 +23,14 @@ class ConnectClientIdTest { assertEquals("It's me", message["client_id"]) } - private fun roundtrip(clientId: String?) : Map<*, *> { + private fun roundtrip(clientId: String?): Map<*, *> { val original = ConnectMessage( - role = "upstream", - channelId = "sora", - sdp = "", - clientId = clientId + role = "upstream", + channelId = "sora", + sdp = "", + clientId = clientId ) val serialized = gson.toJson(original) return gson.fromJson(serialized, Map::class.java) } -} \ No newline at end of file +} diff --git a/sora-android-sdk/src/test/kotlin/jp/shiguredo/sora/sdk/ConnectMetadataJsonTest.kt b/sora-android-sdk/src/test/kotlin/jp/shiguredo/sora/sdk/ConnectMetadataJsonTest.kt index 8ef2d46f..a5147ba9 100644 --- a/sora-android-sdk/src/test/kotlin/jp/shiguredo/sora/sdk/ConnectMetadataJsonTest.kt +++ b/sora-android-sdk/src/test/kotlin/jp/shiguredo/sora/sdk/ConnectMetadataJsonTest.kt @@ -6,7 +6,6 @@ import org.junit.Test import kotlin.test.assertEquals import kotlin.test.fail - class ConnectMetadataJsonTest { val gson = Gson() @@ -32,7 +31,7 @@ class ConnectMetadataJsonTest { @Test fun listMetadata1() { val message = roundtrip(listOf(1, 2, 3)) - if(message.metadata !is List<*>) { + if (message.metadata !is List<*>) { fail("metadata should be list: ${message.metadata}") } val metadata = message.metadata as List<*> @@ -44,7 +43,7 @@ class ConnectMetadataJsonTest { @Test fun listMetadata2() { val message = roundtrip(listOf("foo", "bar", "baz")) - if(message.metadata !is List<*>) { + if (message.metadata !is List<*>) { fail("metadata should be list: ${message.metadata}") } val metadata = message.metadata as List<*> @@ -55,12 +54,14 @@ class ConnectMetadataJsonTest { } @Test fun mapMetadata() { - val message = roundtrip(hashMapOf( + val message = roundtrip( + hashMapOf( "foo" to 1, "bar" to "baz", "baz" to listOf("ham", "eggs", "bacon") - )) - if(message.metadata !is Map<*, *>) { + ) + ) + if (message.metadata !is Map<*, *>) { fail("metadata should be map: ${message.metadata}") } val metadata = message.metadata as Map<*, *> @@ -71,9 +72,9 @@ class ConnectMetadataJsonTest { } @Test fun setMetadata() { - val message = roundtrip(setOf(1,2,3)) + val message = roundtrip(setOf(1, 2, 3)) // Set は List で返ってくる - if(message.metadata !is List<*>) { + if (message.metadata !is List<*>) { fail("metadata should be set: ${message.metadata}") } val metadata = message.metadata as List<*> @@ -81,15 +82,14 @@ class ConnectMetadataJsonTest { assertEquals(listOf(1.0, 2.0, 3.0), metadata) } - - private fun roundtrip(metadata: Any?) : ConnectMessage { + private fun roundtrip(metadata: Any?): ConnectMessage { val original = ConnectMessage( - role = "upstream", - channelId = "sora", - sdp = "", - metadata = metadata + role = "upstream", + channelId = "sora", + sdp = "", + metadata = metadata ) val serialized = gson.toJson(original) return gson.fromJson(serialized, ConnectMessage::class.java) } -} \ No newline at end of file +} diff --git a/sora-android-sdk/src/test/kotlin/jp/shiguredo/sora/sdk/TrivialTest.kt b/sora-android-sdk/src/test/kotlin/jp/shiguredo/sora/sdk/TrivialTest.kt index c723bb76..4b34f2c0 100644 --- a/sora-android-sdk/src/test/kotlin/jp/shiguredo/sora/sdk/TrivialTest.kt +++ b/sora-android-sdk/src/test/kotlin/jp/shiguredo/sora/sdk/TrivialTest.kt @@ -3,7 +3,6 @@ package jp.shiguredo.sora.sdk import org.junit.Test import kotlin.test.assertTrue - class TrivialTest { @Test fun myFirstTest() { From 078ffe6dfa4241f762d1fad441be0933f78536f4 Mon Sep 17 00:00:00 2001 From: SUZUKI Tetsuya Date: Mon, 6 Dec 2021 17:07:18 +0900 Subject: [PATCH 40/85] =?UTF-8?q?Android=20Studio=20=E3=81=AE=E8=A8=AD?= =?UTF-8?q?=E5=AE=9A=E3=82=92=20ktlint=20=E3=81=A8=E7=AB=B6=E5=90=88?= =?UTF-8?q?=E3=81=97=E3=81=AA=E3=81=84=E3=82=88=E3=81=86=E3=81=AB=E3=81=99?= =?UTF-8?q?=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/codeStyles/Project.xml | 124 +++++++++++++++++++++++++++ .idea/codeStyles/codeStyleConfig.xml | 5 ++ 2 files changed, 129 insertions(+) create mode 100644 .idea/codeStyles/Project.xml create mode 100644 .idea/codeStyles/codeStyleConfig.xml diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 00000000..61a2c750 --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,124 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 00000000..79ee123c --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file From c0204e2970f62b29949221439bc6c84ff16f90d8 Mon Sep 17 00:00:00 2001 From: miosakuma Date: Mon, 6 Dec 2021 17:19:59 +0900 Subject: [PATCH 41/85] =?UTF-8?q?=E8=A6=96=E8=81=B4=E3=81=AE=E3=81=BF?= =?UTF-8?q?=E3=81=8B=E3=81=A4=20H.264=20=E3=81=97=E3=81=9F=E5=A0=B4?= =?UTF-8?q?=E5=90=88=E3=81=AB=E6=8E=A5=E7=B6=9A=E3=81=A7=E3=81=8D=E3=81=AA?= =?UTF-8?q?=E3=81=84=E5=95=8F=E9=A1=8C=E3=81=AB=E3=81=A4=E3=81=84=E3=81=A6?= =?UTF-8?q?=E3=81=AE=E3=83=AF=E3=83=BC=E3=82=AF=E3=82=A2=E3=83=A9=E3=82=A6?= =?UTF-8?q?=E3=83=B3=E3=83=89=E3=82=92=E5=89=8A=E9=99=A4=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGES.md | 2 ++ .../sora/sdk/channel/rtc/RTCComponentFactory.kt | 11 ++--------- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 80b75d12..27640128 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -17,6 +17,8 @@ - @enm10k - [UPDATE] dokka を 1.5.31 に上げる - @miosakuma +- [FIX] 視聴のみかつ H.264 した場合に接続できない問題についてのワークアラウンドを削除する + - @miosakuma ## 2021.3 diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/RTCComponentFactory.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/RTCComponentFactory.kt index 653016d9..420fbb4a 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/RTCComponentFactory.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/RTCComponentFactory.kt @@ -42,16 +42,9 @@ class RTCComponentFactory(private val mediaOption: SoraMediaOption, true /* enableIntelVp8Encoder */, false /* enableH264HighProfile */) - // 注意: 視聴のみかつ H.264 のみの場合のワークアラウンド - // upstream context が設定されていない場合、 - // downstream context が設定されていればそれを使って - // DefaultVideoEncoderFactory を用意する - // H.264 に限定する理由は、 VP8/VP9 対応のハードウェアエンコーダーを - // 搭載していない端末があるため - mediaOption.videoCodec == SoraVideoOption.Codec.H264 && - mediaOption.videoDownstreamContext != null -> + mediaOption.videoDownstreamContext != null -> DefaultVideoEncoderFactory(mediaOption.videoDownstreamContext, - false /* enableIntelVp8Encoder */, + true /* enableIntelVp8Encoder */, false /* enableH264HighProfile */) else -> // context が指定されていなければソフトウェアエンコーダーを使用する From a9346b0807cbf5661308d4832c50553ef8cf569b Mon Sep 17 00:00:00 2001 From: miosakuma Date: Tue, 7 Dec 2021 10:55:11 +0900 Subject: [PATCH 42/85] =?UTF-8?q?=E3=82=B3=E3=83=A1=E3=83=B3=E3=83=88?= =?UTF-8?q?=E3=82=92=E4=BF=AE=E6=AD=A3=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 27640128..0d01c028 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -18,6 +18,8 @@ - [UPDATE] dokka を 1.5.31 に上げる - @miosakuma - [FIX] 視聴のみかつ H.264 した場合に接続できない問題についてのワークアラウンドを削除する + - SoraMediaOption.videoUpstreamContext が無く SoraMediaOption.videoDownstreamContext + がある場合はコーデック指定に依らず、 DefaultVideoEncoderFactory を使用する - @miosakuma ## 2021.3 From 921a1ec80cd86309539b2795c2fc76e12175ef2f Mon Sep 17 00:00:00 2001 From: SUZUKI Tetsuya Date: Tue, 7 Dec 2021 15:38:13 +0900 Subject: [PATCH 43/85] =?UTF-8?q?org.ajoberstar.grgit=20=E3=81=AE=E3=83=90?= =?UTF-8?q?=E3=83=BC=E3=82=B8=E3=83=A7=E3=83=B3=E3=82=92=E4=B8=8A=E3=81=92?= =?UTF-8?q?=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index b8ba4e44..33578c53 100644 --- a/build.gradle +++ b/build.gradle @@ -18,7 +18,7 @@ buildscript { classpath 'com.android.tools.build:gradle:4.2.2' classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlin_version}" - classpath 'org.ajoberstar.grgit:grgit-gradle:4.1.0' + classpath 'org.ajoberstar.grgit:grgit-gradle:4.1.1' classpath "org.jetbrains.dokka:dokka-gradle-plugin:${dokka_version}" classpath "com.github.ben-manes:gradle-versions-plugin:0.38.0" From c3140741337726f24046e2b7b8bf793433885057 Mon Sep 17 00:00:00 2001 From: SUZUKI Tetsuya Date: Tue, 7 Dec 2021 15:57:33 +0900 Subject: [PATCH 44/85] =?UTF-8?q?=E8=AD=98=E5=88=A5=E5=AD=90=E3=82=92?= =?UTF-8?q?=E5=80=8B=E5=88=A5=E3=81=AB=E3=82=A4=E3=83=B3=E3=83=9D=E3=83=BC?= =?UTF-8?q?=E3=83=88=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../codec/SimulcastVideoEncoderFactoryWrapper.kt | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/codec/SimulcastVideoEncoderFactoryWrapper.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/codec/SimulcastVideoEncoderFactoryWrapper.kt index 0457f825..dc029de7 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/codec/SimulcastVideoEncoderFactoryWrapper.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/codec/SimulcastVideoEncoderFactoryWrapper.kt @@ -1,8 +1,19 @@ package jp.shiguredo.sora.sdk.codec import jp.shiguredo.sora.sdk.util.SoraLogger -import org.webrtc.* -import java.util.concurrent.* +import org.webrtc.EglBase +import org.webrtc.HardwareVideoEncoderFactory +import org.webrtc.SimulcastVideoEncoderFactory +import org.webrtc.SoftwareVideoEncoderFactory +import org.webrtc.VideoCodecInfo +import org.webrtc.VideoCodecStatus +import org.webrtc.VideoEncoder +import org.webrtc.VideoEncoderFactory +import org.webrtc.VideoEncoderFallback +import org.webrtc.VideoFrame +import java.util.concurrent.Callable +import java.util.concurrent.ExecutorService +import java.util.concurrent.Executors internal class SimulcastVideoEncoderFactoryWrapper( sharedContext: EglBase.Context?, From 44fc15ed0fe7bc7f545054e7a253459959021ae1 Mon Sep 17 00:00:00 2001 From: SUZUKI Tetsuya Date: Tue, 7 Dec 2021 16:41:46 +0900 Subject: [PATCH 45/85] =?UTF-8?q?ktlint=20=E3=81=AE=E8=A8=AD=E5=AE=9A?= =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sora-android-sdk/build.gradle | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sora-android-sdk/build.gradle b/sora-android-sdk/build.gradle index 3b51a0bf..6781cea1 100644 --- a/sora-android-sdk/build.gradle +++ b/sora-android-sdk/build.gradle @@ -68,6 +68,16 @@ dokkaHtml.configure { } } +ktlint { + version = "0.43.2" + android = false + outputToConsole = true + reporters { + reporter "checkstyle" + } + ignoreFailures = false +} + dependencies { api "com.github.shiguredo:shiguredo-webrtc-android:${libwebrtc_version}" From faf2172c57d40346b5be160db445aa214bd918ce Mon Sep 17 00:00:00 2001 From: SUZUKI Tetsuya Date: Tue, 7 Dec 2021 17:04:40 +0900 Subject: [PATCH 46/85] =?UTF-8?q?=E3=83=93=E3=83=AB=E3=83=89=E6=99=82?= =?UTF-8?q?=E3=81=AB=E3=83=95=E3=82=A9=E3=83=BC=E3=83=9E=E3=83=83=E3=83=88?= =?UTF-8?q?=E3=82=92=E8=A1=8C=E3=81=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sora-android-sdk/build.gradle | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sora-android-sdk/build.gradle b/sora-android-sdk/build.gradle index 6781cea1..ce4f393b 100644 --- a/sora-android-sdk/build.gradle +++ b/sora-android-sdk/build.gradle @@ -41,9 +41,12 @@ android { testOptions { unitTests.includeAndroidResources = true } +} +tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach { + dependsOn("ktlintFormat") kotlinOptions { - jvmTarget = '1.8' + jvmTarget = "1.8" } } From d4724727599052f6f02989b0c9184a4b91593354 Mon Sep 17 00:00:00 2001 From: SUZUKI Tetsuya Date: Wed, 8 Dec 2021 15:25:08 +0900 Subject: [PATCH 47/85] =?UTF-8?q?=E3=82=B3=E3=83=BC=E3=83=89=E3=81=AE?= =?UTF-8?q?=E3=83=95=E3=82=A9=E3=83=BC=E3=83=9E=E3=83=83=E3=83=88=E3=81=AB?= =?UTF-8?q?=E3=81=A4=E3=81=84=E3=81=A6=E8=AA=AC=E6=98=8E=E3=82=92=E6=9B=B8?= =?UTF-8?q?=E3=81=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/CODE_FORMAT.md | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 docs/CODE_FORMAT.md diff --git a/docs/CODE_FORMAT.md b/docs/CODE_FORMAT.md new file mode 100644 index 00000000..c206090e --- /dev/null +++ b/docs/CODE_FORMAT.md @@ -0,0 +1,41 @@ +# コードのフォーマット + +Sora Android SDK ではソースコードの lint チェックとフォーマットが可能です。 + + +## ツール + +ktlint を利用します。 ktlint の Gradle プラグインを導入しているので、 Gradle プロジェクトを同期すればインストールされます。 + + +## 実行方法 + +Android Studio ではビルド時に lint チェックとフォーマットが実行されます。コマンドラインでは Gradle のタスクとして実行できます。 + +チェックのみ: + +``` +./gradlew ktlintCheck +``` + +フォーマット: + +``` +./gradlew ktlintFormat +``` + +ルール違反があった場合、詳細はコンソールの他に `sora-android-sdk/build/reports/ktlint` 以下のディレクトリにファイルとして出力されます。 `sora-android-sdk/main/src/main` 以下のソースコードの lint チェック結果は `ktlintMainSourceSetCheck/ktlintMainSourceSetCheck.txt` を参照してください。 + + +## ルール + +[Kotlin スタイルガイド](https://developer.android.com/kotlin/style-guide) に従います。 ktlint はデフォルトの設定で同スタイルに従うので、特に ktlint の設定はしていません。 + + +## 注意 + +本リポジトリには Android Studio の設定が含まれています。設定を変更しないでください。 + +設定内容は次の通りです。 + +- Editor > Code Style > Kotlin > Imports にて、 `import` 文でのワイルドカードの使用を無効にします。 From accfff7e68bbe939c52e4710c544705ae56e14cb Mon Sep 17 00:00:00 2001 From: SUZUKI Tetsuya Date: Thu, 2 Dec 2021 15:14:41 +0900 Subject: [PATCH 48/85] =?UTF-8?q?actions/setup-java=20=E3=81=AE=E3=83=90?= =?UTF-8?q?=E3=83=BC=E3=82=B8=E3=83=A7=E3=83=B3=E3=82=92=E4=B8=8A=E3=81=92?= =?UTF-8?q?=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1834e4ac..caf9deba 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,10 +17,11 @@ jobs: steps: - uses: actions/checkout@v2 - - name: set up JDK 1.8 - uses: actions/setup-java@v1 + - name: Set up JDK + uses: actions/setup-java@v2 with: - java-version: 1.8 + distribution: 'temurin' + java-version: '17' - name: Copy gradle.properties run: cp gradle.properties.example gradle.properties - name: Build with Gradle From d149a67580297ba5d74e597b32f72858fd295302 Mon Sep 17 00:00:00 2001 From: SUZUKI Tetsuya Date: Thu, 2 Dec 2021 15:29:26 +0900 Subject: [PATCH 49/85] =?UTF-8?q?JDK=20=E3=82=92=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index caf9deba..a7bb2727 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -20,8 +20,8 @@ jobs: - name: Set up JDK uses: actions/setup-java@v2 with: - distribution: 'temurin' - java-version: '17' + distribution: 'adopt' + java-version: '11' - name: Copy gradle.properties run: cp gradle.properties.example gradle.properties - name: Build with Gradle From 7ce224e7bfa17064e7ab6d2895c2742f5bea49b3 Mon Sep 17 00:00:00 2001 From: SUZUKI Tetsuya Date: Thu, 2 Dec 2021 15:40:21 +0900 Subject: [PATCH 50/85] =?UTF-8?q?=E4=B8=80=E5=9B=9E=E3=81=AE=E3=83=93?= =?UTF-8?q?=E3=83=AB=E3=83=89=E3=81=AE=E3=81=9F=E3=82=81=E3=81=AB=E3=83=87?= =?UTF-8?q?=E3=83=BC=E3=83=A2=E3=83=B3=E3=81=AF=E4=B8=8D=E8=A6=81=E3=81=AA?= =?UTF-8?q?=E3=81=AE=E3=81=A7=E3=82=AA=E3=83=97=E3=82=B7=E3=83=A7=E3=83=B3?= =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a7bb2727..587bd448 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -25,4 +25,4 @@ jobs: - name: Copy gradle.properties run: cp gradle.properties.example gradle.properties - name: Build with Gradle - run: ./gradlew build + run: ./gradlew build --no-daemon From 472f8293c98139921177a9bbc1c567a9f548151f Mon Sep 17 00:00:00 2001 From: SUZUKI Tetsuya Date: Thu, 2 Dec 2021 15:44:05 +0900 Subject: [PATCH 51/85] =?UTF-8?q?Gradle=20=E3=81=AE=E5=AE=9F=E8=A1=8C?= =?UTF-8?q?=E7=B5=90=E6=9E=9C=E3=82=92=E3=82=AD=E3=83=A3=E3=83=83=E3=82=B7?= =?UTF-8?q?=E3=83=A5=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 587bd448..0b091b61 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -22,6 +22,7 @@ jobs: with: distribution: 'adopt' java-version: '11' + cache: 'gradle' - name: Copy gradle.properties run: cp gradle.properties.example gradle.properties - name: Build with Gradle From f28c7ec5e930c21d4283bfbd24424d70857b13b1 Mon Sep 17 00:00:00 2001 From: SUZUKI Tetsuya Date: Thu, 2 Dec 2021 16:56:45 +0900 Subject: [PATCH 52/85] =?UTF-8?q?ktlint=20=E3=83=97=E3=83=A9=E3=82=B0?= =?UTF-8?q?=E3=82=A4=E3=83=B3=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 1 + sora-android-sdk/build.gradle | 1 + 2 files changed, 2 insertions(+) diff --git a/build.gradle b/build.gradle index b51a2243..b8ba4e44 100644 --- a/build.gradle +++ b/build.gradle @@ -22,6 +22,7 @@ buildscript { classpath "org.jetbrains.dokka:dokka-gradle-plugin:${dokka_version}" classpath "com.github.ben-manes:gradle-versions-plugin:0.38.0" + classpath "org.jlleitschuh.gradle:ktlint-gradle:10.2.0" } } diff --git a/sora-android-sdk/build.gradle b/sora-android-sdk/build.gradle index d66459dd..3b51a0bf 100644 --- a/sora-android-sdk/build.gradle +++ b/sora-android-sdk/build.gradle @@ -3,6 +3,7 @@ apply plugin: 'com.github.dcendents.android-maven' apply plugin: 'kotlin-android' apply plugin: 'org.jetbrains.dokka' +apply plugin: 'org.jlleitschuh.gradle.ktlint' group = 'com.github.shiguredo' From f378879d00781b70d0125d3c75ea96097ec3b306 Mon Sep 17 00:00:00 2001 From: SUZUKI Tetsuya Date: Mon, 6 Dec 2021 15:26:48 +0900 Subject: [PATCH 53/85] =?UTF-8?q?[CI]=20lint=20=E3=83=81=E3=82=A7=E3=83=83?= =?UTF-8?q?=E3=82=AF=E3=82=92=E8=A1=8C=E3=81=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0b091b61..9a5f7f9a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -26,4 +26,6 @@ jobs: - name: Copy gradle.properties run: cp gradle.properties.example gradle.properties - name: Build with Gradle - run: ./gradlew build --no-daemon + run: ./gradlew build + - name: Lint Check + run: ./gradlew ktlintCheck From 3f7545b8be16a50465d24005f7e5186237507265 Mon Sep 17 00:00:00 2001 From: SUZUKI Tetsuya Date: Mon, 6 Dec 2021 17:01:51 +0900 Subject: [PATCH 54/85] =?UTF-8?q?ktlint=20=E3=82=92=E9=81=A9=E7=94=A8?= =?UTF-8?q?=E3=81=99=E3=82=8B=20&=20=E3=83=AF=E3=82=A4=E3=83=AB=E3=83=89?= =?UTF-8?q?=E3=82=AB=E3=83=BC=E3=83=89=E3=81=AE=E3=82=A4=E3=83=B3=E3=83=9D?= =?UTF-8?q?=E3=83=BC=E3=83=88=E3=82=92=E3=82=84=E3=82=81=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/kotlin/jp/shiguredo/sora/sdk/Sora.kt | 3 +- .../sora/sdk/camera/CameraCapturerFactory.kt | 23 +- .../sdk/camera/CameraVideoCapturerWrapper.kt | 15 +- .../sora/sdk/channel/SoraMediaChannel.kt | 231 ++++++------ .../sdk/channel/data/ChannelAttendeesCount.kt | 52 +-- .../channel/option/PeerConnectionOption.kt | 2 +- .../sdk/channel/option/SoraAudioOption.kt | 2 +- .../sdk/channel/option/SoraChannelRole.kt | 3 +- .../sdk/channel/option/SoraMediaOption.kt | 51 +-- .../sdk/channel/option/SoraSpotlightOption.kt | 3 +- .../sdk/channel/option/SoraVideoOption.kt | 23 +- .../sora/sdk/channel/rtc/PeerChannel.kt | 331 +++++++++++------- .../sora/sdk/channel/rtc/PeerNetworkConfig.kt | 33 +- .../sdk/channel/rtc/RTCComponentFactory.kt | 80 +++-- .../sdk/channel/rtc/RTCLocalAudioManager.kt | 34 +- .../sdk/channel/rtc/RTCLocalVideoManager.kt | 40 ++- .../sdk/channel/signaling/SignalingChannel.kt | 30 +- .../sdk/channel/signaling/message/Catalog.kt | 164 ++++----- .../signaling/message/MessageConverter.kt | 25 +- .../SimulcastVideoEncoderFactoryWrapper.kt | 79 +++-- .../sdk/util/ByteBufferBackedInputStream.kt | 1 - .../sdk/util/ReusableCompositeDisposable.kt | 3 +- .../jp/shiguredo/sora/sdk/util/SDKInfo.kt | 26 +- .../jp/shiguredo/sora/sdk/util/SoraLogger.kt | 1 - .../shiguredo/sora/sdk/ConnectClientIdTest.kt | 15 +- .../sora/sdk/ConnectMetadataJsonTest.kt | 30 +- .../jp/shiguredo/sora/sdk/TrivialTest.kt | 1 - 27 files changed, 718 insertions(+), 583 deletions(-) diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/Sora.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/Sora.kt index eb4f7379..f4a81440 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/Sora.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/Sora.kt @@ -11,5 +11,4 @@ object Sora { */ @Deprecated("スポットライトレガシー機能は 2021 年 12 月に廃止が予定されています。") var usesSpotlightLegacy: Boolean = false - -} \ No newline at end of file +} diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/camera/CameraCapturerFactory.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/camera/CameraCapturerFactory.kt index 57ef5f91..8b6a4bc9 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/camera/CameraCapturerFactory.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/camera/CameraCapturerFactory.kt @@ -33,9 +33,11 @@ class CameraCapturerFactory { * @return 生成された `CameraVideoCapturer` */ @JvmOverloads - fun create(context: Context, - fixedResolution: Boolean = false, - frontFacingFirst: Boolean = true) : CameraVideoCapturer? { + fun create( + context: Context, + fixedResolution: Boolean = false, + frontFacingFirst: Boolean = true + ): CameraVideoCapturer? { SoraLogger.d(TAG, "create camera capturer") var videoCapturer: CameraVideoCapturer? = null if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { @@ -60,14 +62,14 @@ class CameraCapturerFactory { videoCapturer } else -> { - SoraLogger.d(TAG, "Wrap capturer: original.isScreencast=${videoCapturer.isScreencast}, fixedResolution=${fixedResolution}") + SoraLogger.d(TAG, "Wrap capturer: original.isScreencast=${videoCapturer.isScreencast}, fixedResolution=$fixedResolution") CameraVideoCapturerWrapper(videoCapturer, fixedResolution) } } } private fun createCapturer(enumerator: CameraEnumerator, frontFacingFirst: Boolean): CameraVideoCapturer? { - var capturer : CameraVideoCapturer? = null + var capturer: CameraVideoCapturer? = null enumerator.deviceNames.forEach { deviceName -> if (capturer == null) { @@ -86,17 +88,16 @@ class CameraCapturerFactory { return capturer } - private fun findDeviceCamera(enumerator: CameraEnumerator, - deviceName: String, - frontFacing: Boolean) : CameraVideoCapturer? { + private fun findDeviceCamera( + enumerator: CameraEnumerator, + deviceName: String, + frontFacing: Boolean + ): CameraVideoCapturer? { var capturer: CameraVideoCapturer? = null if (enumerator.isFrontFacing(deviceName) == frontFacing) { capturer = enumerator.createCapturer(deviceName, null) } return capturer } - } - } - diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/camera/CameraVideoCapturerWrapper.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/camera/CameraVideoCapturerWrapper.kt index ab14defc..dae16027 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/camera/CameraVideoCapturerWrapper.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/camera/CameraVideoCapturerWrapper.kt @@ -5,8 +5,10 @@ import org.webrtc.CameraVideoCapturer import org.webrtc.CapturerObserver import org.webrtc.SurfaceTextureHelper -class CameraVideoCapturerWrapper(private val capturer: CameraVideoCapturer, - private val fixedResolution: Boolean = false): CameraVideoCapturer { +class CameraVideoCapturerWrapper( + private val capturer: CameraVideoCapturer, + private val fixedResolution: Boolean = false +) : CameraVideoCapturer { override fun startCapture(width: Int, height: Int, framerate: Int) { capturer.startCapture(width, height, framerate) } @@ -31,12 +33,15 @@ class CameraVideoCapturerWrapper(private val capturer: CameraVideoCapturer, return fixedResolution } - override fun initialize(surfaceTextureHelper: SurfaceTextureHelper?, context: Context?, - capturerObserver: CapturerObserver?) { + override fun initialize( + surfaceTextureHelper: SurfaceTextureHelper?, + context: Context?, + capturerObserver: CapturerObserver? + ) { capturer.initialize(surfaceTextureHelper, context, capturerObserver) } override fun dispose() { capturer.dispose() } -} \ No newline at end of file +} diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt index 9b71bc5a..8ecfef13 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt @@ -14,12 +14,24 @@ import jp.shiguredo.sora.sdk.channel.rtc.PeerChannelImpl import jp.shiguredo.sora.sdk.channel.rtc.PeerNetworkConfig import jp.shiguredo.sora.sdk.channel.signaling.SignalingChannel import jp.shiguredo.sora.sdk.channel.signaling.SignalingChannelImpl -import jp.shiguredo.sora.sdk.channel.signaling.message.* +import jp.shiguredo.sora.sdk.channel.signaling.message.MessageConverter +import jp.shiguredo.sora.sdk.channel.signaling.message.NotificationMessage +import jp.shiguredo.sora.sdk.channel.signaling.message.OfferConfig +import jp.shiguredo.sora.sdk.channel.signaling.message.OfferMessage +import jp.shiguredo.sora.sdk.channel.signaling.message.PushMessage +import jp.shiguredo.sora.sdk.channel.signaling.message.SwitchedMessage import jp.shiguredo.sora.sdk.error.SoraErrorReason import jp.shiguredo.sora.sdk.util.ReusableCompositeDisposable import jp.shiguredo.sora.sdk.util.SoraLogger -import org.webrtc.* -import java.util.* +import org.webrtc.DataChannel +import org.webrtc.IceCandidate +import org.webrtc.MediaStream +import org.webrtc.RTCStatsCollectorCallback +import org.webrtc.RTCStatsReport +import org.webrtc.RtpParameters +import org.webrtc.SessionDescription +import java.util.Timer +import java.util.TimerTask import kotlin.concurrent.schedule /** @@ -206,7 +218,7 @@ class SoraMediaChannel @JvmOverloads constructor( * @param mediaChannel イベントが発生したチャネル * @param notification プッシュ API により受信したメッセージ */ - fun onNotificationMessage(mediaChannel: SoraMediaChannel, notification : NotificationMessage) {} + fun onNotificationMessage(mediaChannel: SoraMediaChannel, notification: NotificationMessage) {} /** * Sora のプッシュ API によりメッセージを受信したときに呼び出されるコールバック. @@ -214,7 +226,7 @@ class SoraMediaChannel @JvmOverloads constructor( * @param mediaChannel イベントが発生したチャネル * @param push プッシュ API により受信したメッセージ */ - fun onPushMessage(mediaChannel: SoraMediaChannel, push : PushMessage) {} + fun onPushMessage(mediaChannel: SoraMediaChannel, push: PushMessage) {} /** * PeerConnection の getStats() 統計情報を取得したときに呼び出されるコールバック. @@ -224,7 +236,7 @@ class SoraMediaChannel @JvmOverloads constructor( * - https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/getStats * * @param mediaChannel イベントが発生したチャネル - * @param statsReports 統計レポート + * @param statsReport 統計レポート */ fun onPeerConnectionStatsReady(mediaChannel: SoraMediaChannel, statsReport: RTCStatsReport) {} @@ -245,11 +257,10 @@ class SoraMediaChannel @JvmOverloads constructor( * @param encodings Sora から送信された encodings */ fun onSenderEncodings(mediaChannel: SoraMediaChannel, encodings: List) {} - } - private var peer: PeerChannel? = null - private var signaling: SignalingChannel? = null + private var peer: PeerChannel? = null + private var signaling: SignalingChannel? = null private var switchedToDataChannel = false private var closing = false @@ -261,16 +272,19 @@ class SoraMediaChannel @JvmOverloads constructor( * コネクション ID. */ var connectionId: String? = null - private set + private set private val compositeDisposable = ReusableCompositeDisposable() private val signalingListener = object : SignalingChannel.Listener { override fun onDisconnect() { - SoraLogger.d(TAG, "[channel:$role] @signaling:onDisconnect " - + "switchedToDataChannel=$switchedToDataChannel, " - + "ignoreDisconnectWebSocket=$ignoreDisconnectWebSocket" ) + SoraLogger.d( + TAG, + "[channel:$role] @signaling:onDisconnect " + + "switchedToDataChannel=$switchedToDataChannel, " + + "ignoreDisconnectWebSocket=$ignoreDisconnectWebSocket" + ) val ignoreDisconnect = ignoreDisconnectWebSocket ?: false if (switchedToDataChannel && ignoreDisconnect) { // なにもしない @@ -367,7 +381,7 @@ class SoraMediaChannel @JvmOverloads constructor( } override fun onAddRemoteStream(ms: MediaStream) { - SoraLogger.d(TAG, "[channel:$role] @peer:onAddRemoteStream msid=:${ms.id}, connectionId=${connectionId}") + SoraLogger.d(TAG, "[channel:$role] @peer:onAddRemoteStream msid=:${ms.id}, connectionId=$connectionId") if (mediaOption.multistreamEnabled && connectionId != null && ms.id == connectionId) { SoraLogger.d(TAG, "[channel:$role] this stream is mine, ignore: ${ms.id}") return @@ -398,7 +412,7 @@ class SoraMediaChannel @JvmOverloads constructor( "push" -> "push" "stats" -> "req-stats" "e2ee" -> "NOT-IMPLEMENTED" - else -> label // 追加が発生した時に備えて許容する + else -> label // 追加が発生した時に備えて許容する } MessageConverter.parseType(messageData)?.let { type -> @@ -427,7 +441,6 @@ class SoraMediaChannel @JvmOverloads constructor( else -> SoraLogger.i(TAG, "Unknown label: label=$label, type=$type, message=$messageData") } - } else -> SoraLogger.i(TAG, "Unknown type: label=$label, type=$type, message=$messageData") } @@ -483,13 +496,15 @@ class SoraMediaChannel @JvmOverloads constructor( val maintVersion = kClass.getField("maint_version").get(null) val webrtcRevision = kClass.getField("webrtc_revision").get(null) val webrtcBuildVersion = listOf(webrtcBranch, webrtcCommit, maintVersion) - .joinToString(separator = ".") - SoraLogger.d(TAG, "libwebrtc version = ${webrtcBuildVersion} @ ${webrtcRevision}") - } catch (e : ClassNotFoundException) { + .joinToString(separator = ".") + SoraLogger.d(TAG, "libwebrtc version = $webrtcBuildVersion @ $webrtcRevision") + } catch (e: ClassNotFoundException) { SoraLogger.d(TAG, "connect: libwebrtc other than Shiguredo build is used.") } - SoraLogger.d(TAG, """connect: SoraMediaOption + SoraLogger.d( + TAG, + """connect: SoraMediaOption |requiredRole = ${mediaOption.requiredRole} |upstreamIsRequired = ${mediaOption.upstreamIsRequired} |downstreamIsRequired = ${mediaOption.downstreamIsRequired} @@ -518,7 +533,8 @@ class SoraMediaChannel @JvmOverloads constructor( |spotlightNumber = ${mediaOption.spotlightOption?.spotlightNumber} |signalingMetadata = ${this.signalingMetadata} |clientId = ${this.clientId} - |signalingNotifyMetadata = ${this.signalingNotifyMetadata}""".trimMargin()) + |signalingNotifyMetadata = ${this.signalingNotifyMetadata}""".trimMargin() + ) if (closing) { return @@ -532,12 +548,15 @@ class SoraMediaChannel @JvmOverloads constructor( private fun startTimer() { stopTimer() timer = Timer() - timer!!.schedule(object: TimerTask() { - override fun run() { - timer = null - onTimeout() - } - }, timeoutSeconds*1000) + timer!!.schedule( + object : TimerTask() { + override fun run() { + timer = null + onTimeout() + } + }, + timeoutSeconds * 1000 + ) } private fun stopTimer() { @@ -551,21 +570,22 @@ class SoraMediaChannel @JvmOverloads constructor( disconnect() } - private fun requestClientOfferSdp() { val mediaOption = SoraMediaOption().apply { enableVideoDownstream(null) enableAudioDownstream() } val clientOfferPeer = PeerChannelImpl( - appContext = context, - networkConfig = PeerNetworkConfig( - serverConfig = OfferConfig( - iceServers = emptyList(), - iceTransportPolicy = ""), - mediaOption = mediaOption), - mediaOption = mediaOption, - listener = null + appContext = context, + networkConfig = PeerNetworkConfig( + serverConfig = OfferConfig( + iceServers = emptyList(), + iceTransportPolicy = "" + ), + mediaOption = mediaOption + ), + mediaOption = mediaOption, + listener = null ) clientOfferPeer.run { val subscription = requestClientOfferSdp() @@ -590,7 +610,7 @@ class SoraMediaChannel @JvmOverloads constructor( disconnect() } - ) + ) compositeDisposable.add(subscription) } } @@ -624,39 +644,41 @@ class SoraMediaChannel @JvmOverloads constructor( SoraLogger.d(TAG, "[channel:$role] @peer:starting") peer = PeerChannelImpl( - appContext = context, - networkConfig = PeerNetworkConfig( - serverConfig = offerMessage.config, - mediaOption = mediaOption - ), - mediaOption = mediaOption, - dataChannelConfigs = offerMessage.dataChannels, - listener = peerListener + appContext = context, + networkConfig = PeerNetworkConfig( + serverConfig = offerMessage.config, + mediaOption = mediaOption + ), + mediaOption = mediaOption, + dataChannelConfigs = offerMessage.dataChannels, + listener = peerListener ) if (0 < peerConnectionOption.getStatsIntervalMSec) { getStatsTimer = Timer() SoraLogger.d(TAG, "Schedule getStats with interval ${peerConnectionOption.getStatsIntervalMSec} [msec]") getStatsTimer?.schedule(0L, peerConnectionOption.getStatsIntervalMSec) { - peer?.getStats(RTCStatsCollectorCallback { - listener?.onPeerConnectionStatsReady(this@SoraMediaChannel, it) - }) + peer?.getStats( + RTCStatsCollectorCallback { + listener?.onPeerConnectionStatsReady(this@SoraMediaChannel, it) + } + ) } } peer?.run { val subscription = handleInitialRemoteOffer(offerMessage.sdp, offerMessage.mid, offerMessage.encodings) - .observeOn(Schedulers.io()) - .subscribeBy( - onSuccess = { - SoraLogger.d(TAG, "[channel:$role] @peer:answer") - signaling?.sendAnswer(it.description) - }, - onError = { - val msg = "[channel:$role] failure in handleInitialOffer: ${it.message}" - SoraLogger.w(TAG, msg) - disconnect() - } - ) + .observeOn(Schedulers.io()) + .subscribeBy( + onSuccess = { + SoraLogger.d(TAG, "[channel:$role] @peer:answer") + signaling?.sendAnswer(it.description) + }, + onError = { + val msg = "[channel:$role] failure in handleInitialOffer: ${it.message}" + SoraLogger.w(TAG, msg) + disconnect() + } + ) compositeDisposable.add(subscription) } } @@ -673,18 +695,18 @@ class SoraMediaChannel @JvmOverloads constructor( private fun handleUpdateOffer(sdp: String) { peer?.run { val subscription = handleUpdatedRemoteOffer(sdp) - .observeOn(Schedulers.io()) - .subscribeBy( - onSuccess = { - SoraLogger.d(TAG, "[channel:$role] @peer:about to send updated answer") - signaling?.sendUpdateAnswer(it.description) - }, - onError = { - val msg = "[channel:$role] failed handle updated offer: ${it.message}" - SoraLogger.w(TAG, msg) - disconnect() - } - ) + .observeOn(Schedulers.io()) + .subscribeBy( + onSuccess = { + SoraLogger.d(TAG, "[channel:$role] @peer:about to send updated answer") + signaling?.sendUpdateAnswer(it.description) + }, + onError = { + val msg = "[channel:$role] failed handle updated offer: ${it.message}" + SoraLogger.w(TAG, msg) + disconnect() + } + ) compositeDisposable.add(subscription) } } @@ -692,18 +714,18 @@ class SoraMediaChannel @JvmOverloads constructor( private fun handleReOffer(sdp: String) { peer?.run { val subscription = handleUpdatedRemoteOffer(sdp) - .observeOn(Schedulers.io()) - .subscribeBy( - onSuccess = { - SoraLogger.d(TAG, "[channel:$role] @peer:about to send re-answer") - signaling?.sendReAnswer(it.description) - }, - onError = { - val msg = "[channel:$role] failed handle re-offer: ${it.message}" - SoraLogger.w(TAG, msg) - disconnect() - } - ) + .observeOn(Schedulers.io()) + .subscribeBy( + onSuccess = { + SoraLogger.d(TAG, "[channel:$role] @peer:about to send re-answer") + signaling?.sendReAnswer(it.description) + }, + onError = { + val msg = "[channel:$role] failed handle re-offer: ${it.message}" + SoraLogger.w(TAG, msg) + disconnect() + } + ) compositeDisposable.add(subscription) } } @@ -711,18 +733,18 @@ class SoraMediaChannel @JvmOverloads constructor( private fun handleReOfferViaDataChannel(dataChannel: DataChannel, sdp: String) { peer?.run { val subscription = handleUpdatedRemoteOffer(sdp) - .observeOn(Schedulers.io()) - .subscribeBy( - onSuccess = { - SoraLogger.d(TAG, "[channel:$role] @peer:about to send re-answer") - peer?.sendReAnswer(dataChannel, it.description) - }, - onError = { - val msg = "[channel:$role] failed handle re-offer: ${it.message}" - SoraLogger.w(TAG, msg) - disconnect() - } - ) + .observeOn(Schedulers.io()) + .subscribeBy( + onSuccess = { + SoraLogger.d(TAG, "[channel:$role] @peer:about to send re-answer") + peer?.sendReAnswer(dataChannel, it.description) + }, + onError = { + val msg = "[channel:$role] failed handle re-offer: ${it.message}" + SoraLogger.w(TAG, msg) + disconnect() + } + ) compositeDisposable.add(subscription) } } @@ -739,11 +761,11 @@ class SoraMediaChannel @JvmOverloads constructor( when (notification.eventType) { "connection.created", "connection.destroyed" -> { val attendees = ChannelAttendeesCount( - numberOfDownstreams = notification.numberOfDownstreamConnections?: 0, - numberOfUpstreams = notification.numberOfUpstreamConnections?: 0, - numberOfSendrecvConnections = notification.numberOfSendrecvConnections!!, - numberOfSendonlyConnections = notification.numberOfSendonlyConnections!!, - numberOfRecvonlyConnections = notification.numberOfRecvonlyConnections!!, + numberOfDownstreams = notification.numberOfDownstreamConnections ?: 0, + numberOfUpstreams = notification.numberOfUpstreamConnections ?: 0, + numberOfSendrecvConnections = notification.numberOfSendrecvConnections!!, + numberOfSendonlyConnections = notification.numberOfSendonlyConnections!!, + numberOfRecvonlyConnections = notification.numberOfRecvonlyConnections!!, ) listener?.onAttendeesCountUpdated(this@SoraMediaChannel, attendees) } @@ -780,8 +802,11 @@ class SoraMediaChannel @JvmOverloads constructor( private fun sendDisconnectMessage() { val dataChannel = dataChannels["signaling"] - SoraLogger.d(TAG, "[channel:$role] sendDisconnectMessage switched=$switchedToDataChannel, " - + "dataChannel.label=${dataChannel?.label()}") + SoraLogger.d( + TAG, + "[channel:$role] sendDisconnectMessage switched=$switchedToDataChannel, " + + "dataChannel.label=${dataChannel?.label()}" + ) if (switchedToDataChannel && dataChannel != null) { peer?.sendDisconnectMessage(dataChannel) } else { diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/data/ChannelAttendeesCount.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/data/ChannelAttendeesCount.kt index ca544a01..7a687551 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/data/ChannelAttendeesCount.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/data/ChannelAttendeesCount.kt @@ -4,37 +4,37 @@ package jp.shiguredo.sora.sdk.channel.data * チャネルの参加者数を表すクラスです. */ data class ChannelAttendeesCount( - /** - * 配信者数. - */ - @Deprecated("numberOfUpstreams は 2021 年 6 月リリース予定の Sora にて廃止されます。") - val numberOfUpstreams: Int, + /** + * 配信者数. + */ + @Deprecated("numberOfUpstreams は 2021 年 6 月リリース予定の Sora にて廃止されます。") + val numberOfUpstreams: Int, - /** - * 視聴者数. - */ - @Deprecated("numberOfDownstreams は 2021 年 6 月リリース予定の Sora にて廃止されます。") - val numberOfDownstreams: Int, + /** + * 視聴者数. + */ + @Deprecated("numberOfDownstreams は 2021 年 6 月リリース予定の Sora にて廃止されます。") + val numberOfDownstreams: Int, - /** - * sendrecv の接続数. - */ - val numberOfSendrecvConnections: Int, + /** + * sendrecv の接続数. + */ + val numberOfSendrecvConnections: Int, - /** - * sendonly の接続数. - */ - val numberOfSendonlyConnections: Int, + /** + * sendonly の接続数. + */ + val numberOfSendonlyConnections: Int, - /** - * recvonly の接続数. - */ - val numberOfRecvonlyConnections: Int, + /** + * recvonly の接続数. + */ + val numberOfRecvonlyConnections: Int, - ) { - /** - * 配信者数と視聴者数の合計. - */ +) { + /** + * 配信者数と視聴者数の合計. + */ val numberOfConnections: Int get() = numberOfSendrecvConnections + numberOfSendonlyConnections + numberOfRecvonlyConnections } diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/PeerConnectionOption.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/PeerConnectionOption.kt index 9dd666ef..662b0716 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/PeerConnectionOption.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/PeerConnectionOption.kt @@ -7,7 +7,7 @@ class PeerConnectionOption { /** * PeerConnection の getStats() 統計情報を取得するインターバルのミリ秒. - * + * * 0 の場合、統計情報を取得しません. * * cf. diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraAudioOption.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraAudioOption.kt index f062d724..e60427b7 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraAudioOption.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraAudioOption.kt @@ -13,7 +13,7 @@ class SoraAudioOption { companion object { const val ECHO_CANCELLATION_CONSTRAINT = "googEchoCancellation" const val AUTO_GAIN_CONTROL_CONSTRAINT = "googAutoGainControl" - const val HIGH_PASS_FILTER_CONSTRAINT = "googHighpassFilter" + const val HIGH_PASS_FILTER_CONSTRAINT = "googHighpassFilter" const val NOISE_SUPPRESSION_CONSTRAINT = "googNoiseSuppression" } /** diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraChannelRole.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraChannelRole.kt index 5f089111..c2c82001 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraChannelRole.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraChannelRole.kt @@ -1,6 +1,6 @@ package jp.shiguredo.sora.sdk.channel.option -import java.util.* +import java.util.Locale /** * チャネルの役割を示します. @@ -18,5 +18,4 @@ enum class SoraChannelRole { internal val signaling: String get() = this.toString().toLowerCase(Locale.getDefault()) - } diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraMediaOption.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraMediaOption.kt index d97a0a22..fca40d33 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraMediaOption.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraMediaOption.kt @@ -1,7 +1,11 @@ package jp.shiguredo.sora.sdk.channel.option import jp.shiguredo.sora.sdk.Sora -import org.webrtc.* +import org.webrtc.EglBase +import org.webrtc.PeerConnection +import org.webrtc.VideoCapturer +import org.webrtc.VideoDecoderFactory +import org.webrtc.VideoEncoderFactory /** * Sora への接続オプションを表すクラスです. @@ -13,12 +17,12 @@ class SoraMediaOption { } internal var audioDownstreamEnabled = false - internal var audioUpstreamEnabled = false + internal var audioUpstreamEnabled = false internal var videoDownstreamEnabled = false - internal var videoUpstreamEnabled = false - internal var multistreamEnabled = false + internal var videoUpstreamEnabled = false + internal var multistreamEnabled = false internal var spotlightOption: SoraSpotlightOption? = null - internal var simulcastEnabled = false + internal var simulcastEnabled = false internal var simulcastRid: SoraVideoOption.SimulcastRid? = null internal val spotlightEnabled: Boolean @@ -34,10 +38,10 @@ class SoraMediaOption { */ var videoDecoderFactory: VideoDecoderFactory? = null - internal var videoCapturer: VideoCapturer? = null + internal var videoCapturer: VideoCapturer? = null internal var videoDownstreamContext: EglBase.Context? = null - internal var videoUpstreamContext: EglBase.Context? = null + internal var videoUpstreamContext: EglBase.Context? = null var videoCodec = SoraVideoOption.Codec.VP9 @@ -69,10 +73,12 @@ class SoraMediaOption { * @param capturer `VideoCapturer` インスタンス * @param eglContext Egl コンテキスト */ - fun enableVideoUpstream(capturer: VideoCapturer, - eglContext: EglBase.Context?) { + fun enableVideoUpstream( + capturer: VideoCapturer, + eglContext: EglBase.Context? + ) { videoUpstreamEnabled = true - videoCapturer = capturer + videoCapturer = capturer videoUpstreamContext = eglContext } @@ -145,19 +151,19 @@ class SoraMediaOption { // Just for internal usage internal val videoIsRequired: Boolean - get() = videoDownstreamEnabled || videoUpstreamEnabled + get() = videoDownstreamEnabled || videoUpstreamEnabled internal val videoHwAccelerationIsRequired: Boolean - get() = (videoUpstreamContext != null) || (videoDownstreamContext != null) + get() = (videoUpstreamContext != null) || (videoDownstreamContext != null) internal val audioIsRequired: Boolean - get() = audioDownstreamEnabled || audioUpstreamEnabled + get() = audioDownstreamEnabled || audioUpstreamEnabled internal val downstreamIsRequired: Boolean - get() = audioDownstreamEnabled || videoDownstreamEnabled + get() = audioDownstreamEnabled || videoDownstreamEnabled internal val upstreamIsRequired: Boolean - get() = audioUpstreamEnabled || videoUpstreamEnabled + get() = audioUpstreamEnabled || videoUpstreamEnabled internal var _multistreamIsRequired: Boolean? = null @@ -178,12 +184,12 @@ class SoraMediaOption { internal var _requiredRole: SoraChannelRole? = null internal val requiredRole: SoraChannelRole - get() = if (upstreamIsRequired && downstreamIsRequired) - SoraChannelRole.SENDRECV - else if (upstreamIsRequired) - SoraChannelRole.SENDONLY - else - SoraChannelRole.RECVONLY + get() = if (upstreamIsRequired && downstreamIsRequired) + SoraChannelRole.SENDRECV + else if (upstreamIsRequired) + SoraChannelRole.SENDONLY + else + SoraChannelRole.RECVONLY /** * JavaScript API の "googCpuOveruseDetection" に相当する設定項目です. @@ -194,6 +200,5 @@ class SoraMediaOption { * TcpCandidatePolicy を設定します. */ var tcpCandidatePolicy: PeerConnection.TcpCandidatePolicy = - PeerConnection.TcpCandidatePolicy.ENABLED + PeerConnection.TcpCandidatePolicy.ENABLED } - diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraSpotlightOption.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraSpotlightOption.kt index e4c467cb..d20ff932 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraSpotlightOption.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraSpotlightOption.kt @@ -16,5 +16,4 @@ class SoraSpotlightOption { var spotlightFocusRid: SoraVideoOption.SpotlightRid? = null var spotlightUnfocusRid: SoraVideoOption.SpotlightRid? = null - -} \ No newline at end of file +} diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraVideoOption.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraVideoOption.kt index 9de8d47f..fe6b4fb7 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraVideoOption.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraVideoOption.kt @@ -34,24 +34,23 @@ class SoraVideoOption { /** QQVGA 160x120 */ val QQVGA = Point(160, 120) /** QCIF 176x144 */ - val QCIF = Point(176, 144) + val QCIF = Point(176, 144) /** HQVGA 240x160 */ val HQVGA = Point(240, 160) /** QVGA 320x240 */ - val QVGA = Point(320, 240) + val QVGA = Point(320, 240) /** VGA 640x480 */ - val VGA = Point(640, 480) + val VGA = Point(640, 480) /** HD 1280x720 */ - val HD = Point(1280, 720) + val HD = Point(1280, 720) /** FHD 1920x1080 */ - val FHD = Point(1920, 1080) + val FHD = Point(1920, 1080) /** Res3840x1920 3840x1920 */ val Res3840x1920 = Point(3840, 1920) /** UHD3840x2160 3840x2160 */ val UHD3840x2160 = Point(3840, 2160) /** UHD4096x2160 4096x2160 */ val UHD4096x2160 = Point(4096, 2160) - } } @@ -65,27 +64,25 @@ class SoraVideoOption { /** QQVGA 120x160 */ val QQVGA = Point(120, 160) /** QCIF 144x176 */ - val QCIF = Point(144, 176) + val QCIF = Point(144, 176) /** HQVGA 160x240 */ val HQVGA = Point(160, 240) /** QVGA 240x320 */ - val QVGA = Point(240, 320) + val QVGA = Point(240, 320) /** VGA 480x640 */ - val VGA = Point(480, 640) + val VGA = Point(480, 640) /** HD 720x1280 */ - val HD = Point(720, 1280) + val HD = Point(720, 1280) /** FHD 1080x1920 */ - val FHD = Point(1080, 1920) + val FHD = Point(1080, 1920) /** Res1920x3840 1920x3840 */ val Res1920x3840 = Point(1920, 3840) /** UHD2160x3840 2160x3840 */ val UHD2160x3840 = Point(2160, 3840) /** UHD2160x4096 2160x4096 */ val UHD2160x4096 = Point(2160, 4096) - } } - } enum class SimulcastRid(private val value: String) { diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/PeerChannel.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/PeerChannel.kt index 6881121f..38aee476 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/PeerChannel.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/PeerChannel.kt @@ -10,17 +10,34 @@ import jp.shiguredo.sora.sdk.channel.signaling.message.MessageConverter import jp.shiguredo.sora.sdk.error.SoraErrorReason import jp.shiguredo.sora.sdk.util.ByteBufferBackedInputStream import jp.shiguredo.sora.sdk.util.SoraLogger -import org.webrtc.* +import org.webrtc.DataChannel +import org.webrtc.IceCandidate +import org.webrtc.Logging +import org.webrtc.MediaStream +import org.webrtc.MediaStreamTrack +import org.webrtc.PeerConnection +import org.webrtc.PeerConnectionFactory +import org.webrtc.RTCStatsCollectorCallback +import org.webrtc.RTCStatsReport +import org.webrtc.RtpParameters +import org.webrtc.RtpReceiver +import org.webrtc.RtpSender +import org.webrtc.RtpTransceiver +import org.webrtc.SdpObserver +import org.webrtc.SessionDescription import java.io.ByteArrayInputStream import java.nio.ByteBuffer -import java.util.* +import java.util.UUID import java.util.concurrent.Executors -import java.util.zip.* +import java.util.zip.DeflaterInputStream +import java.util.zip.InflaterInputStream interface PeerChannel { - fun handleInitialRemoteOffer(offer: String, - mid: Map?, - encodings: List?): Single + fun handleInitialRemoteOffer( + offer: String, + mid: Map?, + encodings: List? + ): Single fun handleUpdatedRemoteOffer(offer: String): Single // 失敗しても問題のない処理が含まれる (client offer SDP は生成に失敗しても問題ない) ので、 @@ -54,13 +71,13 @@ interface PeerChannel { } class PeerChannelImpl( - private val appContext: Context, - private val networkConfig: PeerNetworkConfig, - private val mediaOption: SoraMediaOption, - dataChannelConfigs: List>? = null, - private var listener: PeerChannel.Listener?, - private var useTracer: Boolean = false -): PeerChannel { + private val appContext: Context, + private val networkConfig: PeerNetworkConfig, + private val mediaOption: SoraMediaOption, + dataChannelConfigs: List>? = null, + private var listener: PeerChannel.Listener?, + private var useTracer: Boolean = false +) : PeerChannel { companion object { private val TAG = PeerChannelImpl::class.simpleName @@ -69,12 +86,12 @@ class PeerChannelImpl( fun initializeIfNeeded(context: Context, useTracer: Boolean) { if (!isInitialized) { val options = PeerConnectionFactory.InitializationOptions - .builder(context) - .setEnableInternalTracer(useTracer) - .setFieldTrials("") - .createInitializationOptions() + .builder(context) + .setEnableInternalTracer(useTracer) + .setFieldTrials("") + .createInitializationOptions() PeerConnectionFactory.initialize(options) - if(SoraLogger.libjingle_enabled) { + if (SoraLogger.libjingle_enabled) { Logging.enableLogToDebugOutput(Logging.Severity.LS_INFO) } isInitialized = true @@ -84,12 +101,12 @@ class PeerChannelImpl( private val componentFactory = RTCComponentFactory(mediaOption, listener) - private var conn: PeerConnection? = null + private var conn: PeerConnection? = null private var factory: PeerConnectionFactory? = null - private val executor = Executors.newSingleThreadExecutor() + private val executor = Executors.newSingleThreadExecutor() - private val sdpConstraints = componentFactory.createSDPConstraints() + private val sdpConstraints = componentFactory.createSDPConstraints() private val localAudioManager = componentFactory.createAudioManager() private val localVideoManager = componentFactory.createVideoManager() @@ -114,7 +131,7 @@ class PeerChannelImpl( init { val compressedDataChannels = (dataChannelConfigs ?: emptyList()) - .filter { (it["compress"] ?: false) == true } + .filter { (it["compress"] ?: false) == true } compressLabels = compressedDataChannels.map { it["label"] as String } SoraLogger.d(TAG, "[rtc] compressedLabels=$compressLabels, dataChannelConfigs=$dataChannelConfigs") } @@ -122,7 +139,7 @@ class PeerChannelImpl( private val connectionObserver = object : PeerConnection.Observer { override fun onSignalingChange(state: PeerConnection.SignalingState?) { - SoraLogger.d(TAG, "[rtc] @onSignalingChange: ${state.toString()}") + SoraLogger.d(TAG, "[rtc] @onSignalingChange: $state") } override fun onIceCandidate(candidate: IceCandidate?) { @@ -151,7 +168,7 @@ class PeerChannelImpl( SoraLogger.d(TAG, "[rtc] @onRemoveTrack") } - override fun onTrack(transceiver: RtpTransceiver) { + override fun onTrack(transceiver: RtpTransceiver) { SoraLogger.d(TAG, "[rtc] @onTrack direction=${transceiver.direction}") SoraLogger.d(TAG, "[rtc] @onTrack currentDirection=${transceiver.currentDirection}") SoraLogger.d(TAG, "[rtc] @onTrack sender.track=${transceiver.sender.track()}") @@ -161,34 +178,45 @@ class PeerChannelImpl( } override fun onDataChannel(dataChannel: DataChannel) { - SoraLogger.d(TAG, "[rtc] @onDataChannel label=${dataChannel.label()}, id=${dataChannel.id()}" - + " state=${dataChannel.state()}, bufferedAmount=${dataChannel.bufferedAmount()}") + SoraLogger.d( + TAG, + "[rtc] @onDataChannel label=${dataChannel.label()}, id=${dataChannel.id()}" + + " state=${dataChannel.state()}, bufferedAmount=${dataChannel.bufferedAmount()}" + ) dataChannel.registerObserver(object : DataChannel.Observer { val label = dataChannel.label() override fun onBufferedAmountChange(previouAmount: Long) { - SoraLogger.d(TAG, "[rtc] @dataChannel.onBufferedAmountChange" - + " label=$label, id=${dataChannel.id()}" - + " state=${dataChannel.state()}, bufferedAmount=${dataChannel.bufferedAmount()}," - + " previousAmount=$previouAmount)") + SoraLogger.d( + TAG, + "[rtc] @dataChannel.onBufferedAmountChange" + + " label=$label, id=${dataChannel.id()}" + + " state=${dataChannel.state()}, bufferedAmount=${dataChannel.bufferedAmount()}," + + " previousAmount=$previouAmount)" + ) } override fun onStateChange() { - SoraLogger.d(TAG, "[rtc] @dataChannel.onStateChange" - + " label=$label, id=${dataChannel.id()}, state=${dataChannel.state()}") + SoraLogger.d( + TAG, + "[rtc] @dataChannel.onStateChange" + + " label=$label, id=${dataChannel.id()}, state=${dataChannel.state()}" + ) if (dataChannel.state() == DataChannel.State.CLOSED) { listener?.onDataChannelClosed(dataChannel.label(), dataChannel) } } override fun onMessage(buffer: DataChannel.Buffer) { - SoraLogger.d(TAG, "[rtc] @dataChannel.onMessage" - + " label=$label, state=${dataChannel.state()}, binary=${buffer.binary}") + SoraLogger.d( + TAG, + "[rtc] @dataChannel.onMessage" + + " label=$label, state=${dataChannel.state()}, binary=${buffer.binary}" + ) val messageData = dataChannelBufferToString(label, buffer) listener?.onDataChannelMessage(dataChannel.label(), dataChannel, messageData) } - }) listener?.onDataChannelOpen(dataChannel.label(), dataChannel) @@ -259,20 +287,22 @@ class PeerChannelImpl( } } - private fun setup(): Single = Single.create(SingleOnSubscribe { - try { - setupInternal() - it.onSuccess(true) - } catch (e: Exception) { - SoraLogger.w(TAG, e.toString()) - it.onError(e) + private fun setup(): Single = Single.create( + SingleOnSubscribe { + try { + setupInternal() + it.onSuccess(true) + } catch (e: Exception) { + SoraLogger.w(TAG, e.toString()) + it.onError(e) + } } - }).subscribeOn(Schedulers.from(executor)) + ).subscribeOn(Schedulers.from(executor)) override fun handleUpdatedRemoteOffer(offer: String): Single { val offerSDP = - SessionDescription(SessionDescription.Type.OFFER, offer) + SessionDescription(SessionDescription.Type.OFFER, offer) return setRemoteDescription(offerSDP).flatMap { // active: false が無効化されてしまう問題に対応 @@ -286,9 +316,11 @@ class PeerChannelImpl( } } - override fun handleInitialRemoteOffer(offer: String, - mid: Map?, - encodings: List?): Single { + override fun handleInitialRemoteOffer( + offer: String, + mid: Map?, + encodings: List? + ): Single { val offerSDP = SessionDescription(SessionDescription.Type.OFFER, offer) offerEncodings = encodings @@ -304,9 +336,9 @@ class PeerChannelImpl( val audioMid = mid?.get("audio") if (audioMid != null) { - val transceiver = this.conn?.transceivers?.find { it.mid == audioMid } + val transceiver = this.conn?.transceivers?.find { it.mid == audioMid } transceiver?.direction = RtpTransceiver.RtpTransceiverDirection.SEND_ONLY - SoraLogger.d(TAG, "set audio sender: mid=${audioMid}, transceiver=${transceiver}") + SoraLogger.d(TAG, "set audio sender: mid=$audioMid, transceiver=$transceiver") audioSender = transceiver?.sender audioSender?.streams = listOf(localStream!!.id) @@ -324,7 +356,7 @@ class PeerChannelImpl( if (videoMid != null) { val transceiver = this.conn?.transceivers?.find { it.mid == videoMid } transceiver?.direction = RtpTransceiver.RtpTransceiverDirection.SEND_ONLY - SoraLogger.d(TAG, "set video sender: mid=${mid}, transceiver=${transceiver} ") + SoraLogger.d(TAG, "set video sender: mid=$mid, transceiver=$transceiver ") videoSender = transceiver?.sender videoSender?.streams = listOf(localStream!!.id) @@ -369,14 +401,17 @@ class PeerChannelImpl( listener?.onSenderEncodings(parameters.encodings) parameters.encodings.forEach { with(it) { - SoraLogger.d(TAG, "update sender encoding: " + + SoraLogger.d( + TAG, + "update sender encoding: " + "id=${sender.id()}, " + "rid=$rid, " + "active=$active, " + "scaleResolutionDownBy=$scaleResolutionDownBy, " + "maxFramerate=$maxFramerate, " + "maxBitrateBps=$maxBitrateBps, " + - "ssrc=$ssrc") + "ssrc=$ssrc" + ) } } @@ -391,33 +426,39 @@ class PeerChannelImpl( } } - private fun createClientOfferSdp() : Single> = - Single.create(SingleOnSubscribe> { + private fun createClientOfferSdp(): Single> = + Single.create( + SingleOnSubscribe> { - val directionRecvOnly = RtpTransceiver.RtpTransceiverInit( - RtpTransceiver.RtpTransceiverDirection.RECV_ONLY) + val directionRecvOnly = RtpTransceiver.RtpTransceiverInit( + RtpTransceiver.RtpTransceiverDirection.RECV_ONLY + ) - conn?.addTransceiver(MediaStreamTrack.MediaType.MEDIA_TYPE_AUDIO, directionRecvOnly) - conn?.addTransceiver(MediaStreamTrack.MediaType.MEDIA_TYPE_VIDEO, directionRecvOnly) + conn?.addTransceiver(MediaStreamTrack.MediaType.MEDIA_TYPE_AUDIO, directionRecvOnly) + conn?.addTransceiver(MediaStreamTrack.MediaType.MEDIA_TYPE_VIDEO, directionRecvOnly) - conn?.createOffer(object : SdpObserver { - override fun onCreateSuccess(sdp: SessionDescription?) { - SoraLogger.d(TAG, "createOffer:onCreateSuccess: ${sdp?.type}") - it.onSuccess(Result.success(sdp!!)) - } - override fun onCreateFailure(error: String) { - SoraLogger.d(TAG, "createOffer:onCreateFailure: $error") - // Offer SDP は生成に失敗しても問題ないので、エラーメッセージを onSuccess で渡す - it.onSuccess(Result.failure(Error(error))) - } - override fun onSetSuccess() { - it.onError(Error("must not come here")) - } - override fun onSetFailure(s: String?) { - it.onError(Error("must not come here")) - } - }, sdpConstraints) - }).subscribeOn(Schedulers.from(executor)) + conn?.createOffer( + object : SdpObserver { + override fun onCreateSuccess(sdp: SessionDescription?) { + SoraLogger.d(TAG, "createOffer:onCreateSuccess: ${sdp?.type}") + it.onSuccess(Result.success(sdp!!)) + } + override fun onCreateFailure(error: String) { + SoraLogger.d(TAG, "createOffer:onCreateFailure: $error") + // Offer SDP は生成に失敗しても問題ないので、エラーメッセージを onSuccess で渡す + it.onSuccess(Result.failure(Error(error))) + } + override fun onSetSuccess() { + it.onError(Error("must not come here")) + } + override fun onSetFailure(s: String?) { + it.onError(Error("must not come here")) + } + }, + sdpConstraints + ) + } + ).subscribeOn(Schedulers.from(executor)) private fun setupInternal() { SoraLogger.d(TAG, "setupInternal") @@ -427,8 +468,9 @@ class PeerChannelImpl( SoraLogger.d(TAG, "createPeerConnection") conn = factory!!.createPeerConnection( - networkConfig.createConfiguration(), - connectionObserver) + networkConfig.createConfiguration(), + connectionObserver + ) SoraLogger.d(TAG, "local managers' initTrack: audio") localAudioManager.initTrack(factory!!, mediaOption.audioOption) @@ -474,7 +516,7 @@ class PeerChannelImpl( dataChannel.send(stringToDataChannelBuffer(dataChannel.label(), disconnectMessage)) } - private fun stringToDataChannelBuffer(label: String, data: String) : DataChannel.Buffer { + private fun stringToDataChannelBuffer(label: String, data: String): DataChannel.Buffer { val inStream = when (compressLabels.contains(label)) { true -> DeflaterInputStream(ByteArrayInputStream(data.toByteArray())) @@ -485,7 +527,7 @@ class PeerChannelImpl( return DataChannel.Buffer(byteBuffer, true) } - private fun dataChannelBufferToString(label: String, buffer: DataChannel.Buffer) : String { + private fun dataChannelBufferToString(label: String, buffer: DataChannel.Buffer): String { val inStream = when (compressLabels.contains(label)) { true -> InflaterInputStream(ByteBufferBackedInputStream(buffer.data)) @@ -496,65 +538,83 @@ class PeerChannelImpl( } private fun createAnswer(): Single = - Single.create(SingleOnSubscribe { - conn?.createAnswer(object : SdpObserver { - override fun onCreateSuccess(sdp: SessionDescription?) { - SoraLogger.d(TAG, """createAnswer:onCreateSuccess: ${sdp!!.type} - |${sdp.description}""".trimMargin()) - it.onSuccess(sdp) - } - override fun onCreateFailure(s: String?) { - SoraLogger.w(TAG, "createAnswer:onCreateFailure: reason=${s}") - it.onError(Error(s)) - } - override fun onSetSuccess() { - it.onError(Error("must not come here")) - } - override fun onSetFailure(s: String?) { - it.onError(Error("must not come here")) - } - }, sdpConstraints) - }).subscribeOn(Schedulers.from(executor)) + Single.create( + SingleOnSubscribe { + conn?.createAnswer( + object : SdpObserver { + override fun onCreateSuccess(sdp: SessionDescription?) { + SoraLogger.d( + TAG, + """createAnswer:onCreateSuccess: ${sdp!!.type} + |${sdp.description}""".trimMargin() + ) + it.onSuccess(sdp) + } + override fun onCreateFailure(s: String?) { + SoraLogger.w(TAG, "createAnswer:onCreateFailure: reason=$s") + it.onError(Error(s)) + } + override fun onSetSuccess() { + it.onError(Error("must not come here")) + } + override fun onSetFailure(s: String?) { + it.onError(Error("must not come here")) + } + }, + sdpConstraints + ) + } + ).subscribeOn(Schedulers.from(executor)) private fun setLocalDescription(sdp: SessionDescription): Single = - Single.create(SingleOnSubscribe { - conn?.setLocalDescription(object : SdpObserver { - override fun onCreateSuccess(p0: SessionDescription?) { - it.onError(Error("must not come here")) - } - override fun onCreateFailure(p0: String?) { - it.onError(Error("must not come here")) - } - override fun onSetSuccess() { - SoraLogger.d(TAG, "setLocalDescription.onSetSuccess ${this@PeerChannelImpl}") - it.onSuccess(sdp) - } - override fun onSetFailure(s: String?) { - SoraLogger.d(TAG, "setLocalDescription.onSetFailure reason=${s} ${this@PeerChannelImpl}") - it.onError(Error(s)) - } - }, sdp) - }).subscribeOn(Schedulers.from(executor)) + Single.create( + SingleOnSubscribe { + conn?.setLocalDescription( + object : SdpObserver { + override fun onCreateSuccess(p0: SessionDescription?) { + it.onError(Error("must not come here")) + } + override fun onCreateFailure(p0: String?) { + it.onError(Error("must not come here")) + } + override fun onSetSuccess() { + SoraLogger.d(TAG, "setLocalDescription.onSetSuccess ${this@PeerChannelImpl}") + it.onSuccess(sdp) + } + override fun onSetFailure(s: String?) { + SoraLogger.d(TAG, "setLocalDescription.onSetFailure reason=$s ${this@PeerChannelImpl}") + it.onError(Error(s)) + } + }, + sdp + ) + } + ).subscribeOn(Schedulers.from(executor)) private fun setRemoteDescription(sdp: SessionDescription): Single = - Single.create(SingleOnSubscribe { - conn?.setRemoteDescription(object : SdpObserver { - override fun onCreateSuccess(p0: SessionDescription?) { - it.onError(Error("must not come here")) - } - override fun onCreateFailure(p0: String?) { - it.onError(Error("must not come here")) - } - override fun onSetSuccess() { - SoraLogger.d(TAG, "setRemoteDescription.onSetSuccess ${this@PeerChannelImpl}") - it.onSuccess(sdp) - } - override fun onSetFailure(s: String?) { - SoraLogger.w(TAG, "setRemoteDescription.onSetFailures reason=${s} ${this@PeerChannelImpl}") - it.onError(Error(s)) - } - }, sdp) - }).subscribeOn(Schedulers.from(executor)) + Single.create( + SingleOnSubscribe { + conn?.setRemoteDescription( + object : SdpObserver { + override fun onCreateSuccess(p0: SessionDescription?) { + it.onError(Error("must not come here")) + } + override fun onCreateFailure(p0: String?) { + it.onError(Error("must not come here")) + } + override fun onSetSuccess() { + SoraLogger.d(TAG, "setRemoteDescription.onSetSuccess ${this@PeerChannelImpl}") + it.onSuccess(sdp) + } + override fun onSetFailure(s: String?) { + SoraLogger.w(TAG, "setRemoteDescription.onSetFailures reason=$s ${this@PeerChannelImpl}") + it.onError(Error(s)) + } + }, + sdp + ) + } + ).subscribeOn(Schedulers.from(executor)) private fun closeInternal() { if (closing) @@ -589,5 +649,4 @@ class PeerChannelImpl( handler(null) } } - } diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/PeerNetworkConfig.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/PeerNetworkConfig.kt index 85e4c4b1..3aa31073 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/PeerNetworkConfig.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/PeerNetworkConfig.kt @@ -6,8 +6,8 @@ import org.webrtc.CryptoOptions import org.webrtc.PeerConnection class PeerNetworkConfig( - private val serverConfig: OfferConfig?, - private val mediaOption: SoraMediaOption + private val serverConfig: OfferConfig?, + private val mediaOption: SoraMediaOption ) { fun createConfiguration(): PeerConnection.RTCConfiguration { @@ -19,10 +19,10 @@ class PeerNetworkConfig( conf.iceTransportsType = PeerConnection.IceTransportsType.RELAY } - conf.bundlePolicy = PeerConnection.BundlePolicy.MAXBUNDLE - conf.rtcpMuxPolicy = PeerConnection.RtcpMuxPolicy.REQUIRE + conf.bundlePolicy = PeerConnection.BundlePolicy.MAXBUNDLE + conf.rtcpMuxPolicy = PeerConnection.RtcpMuxPolicy.REQUIRE conf.continualGatheringPolicy = PeerConnection.ContinualGatheringPolicy.GATHER_CONTINUALLY - conf.keyType = PeerConnection.KeyType.ECDSA + conf.keyType = PeerConnection.KeyType.ECDSA val cryptoOptions = CryptoOptions.builder() .setEnableGcmCryptoSuites(true) .createCryptoOptions() @@ -38,19 +38,20 @@ class PeerNetworkConfig( private fun gatherIceServerSetting(serverConfig: OfferConfig?): List { val iceServers = mutableListOf() - serverConfig?.let { it.iceServers.forEach { - val server = it - server.urls.forEach { - val url = it - iceServers.add(PeerConnection.IceServer.builder(url) - .setUsername(server.username) - .setPassword(server.credential) - .createIceServer()) + serverConfig?.let { + it.iceServers.forEach { + val server = it + server.urls.forEach { + val url = it + iceServers.add( + PeerConnection.IceServer.builder(url) + .setUsername(server.username) + .setPassword(server.credential) + .createIceServer() + ) + } } } - } return iceServers } - } - diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/RTCComponentFactory.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/RTCComponentFactory.kt index 420fbb4a..084213f5 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/RTCComponentFactory.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/RTCComponentFactory.kt @@ -6,13 +6,19 @@ import jp.shiguredo.sora.sdk.channel.option.SoraVideoOption import jp.shiguredo.sora.sdk.codec.SimulcastVideoEncoderFactoryWrapper import jp.shiguredo.sora.sdk.error.SoraErrorReason import jp.shiguredo.sora.sdk.util.SoraLogger -import org.webrtc.* +import org.webrtc.DefaultVideoDecoderFactory +import org.webrtc.DefaultVideoEncoderFactory +import org.webrtc.MediaConstraints +import org.webrtc.PeerConnectionFactory +import org.webrtc.SoftwareVideoDecoderFactory +import org.webrtc.SoftwareVideoEncoderFactory import org.webrtc.audio.AudioDeviceModule import org.webrtc.audio.JavaAudioDeviceModule - -class RTCComponentFactory(private val mediaOption: SoraMediaOption, - private val listener: PeerChannel.Listener?) { +class RTCComponentFactory( + private val mediaOption: SoraMediaOption, + private val listener: PeerChannel.Listener? +) { companion object { private val TAG = RTCComponentFactory::class.simpleName } @@ -21,10 +27,10 @@ class RTCComponentFactory(private val mediaOption: SoraMediaOption, // そうでないと Effect の ClassLoader.loadClass で NPE が発生する。 fun createPeerConnectionFactory(appContext: Context): PeerConnectionFactory { val cl = Thread.currentThread().contextClassLoader - SoraLogger.d(TAG, "createPeerConnectionFactory(): classloader=${cl}") + SoraLogger.d(TAG, "createPeerConnectionFactory(): classloader=$cl") val factoryOptions = PeerConnectionFactory.Options() val factoryBuilder = PeerConnectionFactory.builder() - .setOptions(factoryOptions) + .setOptions(factoryOptions) // DefaultVideoEncoderFactory, DefaultVideoDecoderFactory は // EglBase.Context を与えるとハードウェアエンコーダーを使用する @@ -34,13 +40,17 @@ class RTCComponentFactory(private val mediaOption: SoraMediaOption, mediaOption.videoEncoderFactory != null -> mediaOption.videoEncoderFactory!! mediaOption.simulcastEnabled -> - SimulcastVideoEncoderFactoryWrapper(mediaOption.videoUpstreamContext, - true, - false) + SimulcastVideoEncoderFactoryWrapper( + mediaOption.videoUpstreamContext, + true, + false + ) mediaOption.videoUpstreamContext != null -> - DefaultVideoEncoderFactory(mediaOption.videoUpstreamContext, - true /* enableIntelVp8Encoder */, - false /* enableH264HighProfile */) + DefaultVideoEncoderFactory( + mediaOption.videoUpstreamContext, + true /* enableIntelVp8Encoder */, + false /* enableH264HighProfile */ + ) mediaOption.videoDownstreamContext != null -> DefaultVideoEncoderFactory(mediaOption.videoDownstreamContext, @@ -78,9 +88,9 @@ class RTCComponentFactory(private val mediaOption: SoraMediaOption, createJavaAudioDevice(appContext) } factoryBuilder - .setAudioDeviceModule(audioDeviceModule) - .setVideoEncoderFactory(encoderFactory) - .setVideoDecoderFactory(decoderFactory) + .setAudioDeviceModule(audioDeviceModule) + .setVideoEncoderFactory(encoderFactory) + .setVideoDecoderFactory(decoderFactory) // option で渡ってきた場合の所有権はアプリケーションにある。 // ここで生成した場合だけ解放する。 if (mediaOption.audioOption.audioDeviceModule == null) { @@ -92,15 +102,15 @@ class RTCComponentFactory(private val mediaOption: SoraMediaOption, fun createSDPConstraints(): MediaConstraints { val constraints = MediaConstraints() - SoraLogger.d(TAG, "createSDPConstraints: ${constraints}") + SoraLogger.d(TAG, "createSDPConstraints: $constraints") return constraints } - fun createVideoManager() : RTCLocalVideoManager { + fun createVideoManager(): RTCLocalVideoManager { val videoManager = mediaOption.videoCapturer?.let { RTCLocalVideoManagerImpl(it) } ?: RTCNullLocalVideoManager() - SoraLogger.d(TAG, "videoManager created: ${videoManager}") + SoraLogger.d(TAG, "videoManager created: $videoManager") return videoManager } @@ -117,7 +127,9 @@ class RTCComponentFactory(private val mediaOption: SoraMediaOption, } override fun onWebRtcAudioRecordStartError( - errorCode: JavaAudioDeviceModule.AudioRecordStartErrorCode, errorMessage: String) { + errorCode: JavaAudioDeviceModule.AudioRecordStartErrorCode, + errorMessage: String + ) { SoraLogger.e(TAG, "onWebRtcAudioRecordStartError: $errorCode. $errorMessage") reportError(SoraErrorReason.AUDIO_RECORD_START_ERROR, "$errorMessage [$errorCode]") } @@ -135,7 +147,9 @@ class RTCComponentFactory(private val mediaOption: SoraMediaOption, } override fun onWebRtcAudioTrackStartError( - errorCode: JavaAudioDeviceModule.AudioTrackStartErrorCode, errorMessage: String) { + errorCode: JavaAudioDeviceModule.AudioTrackStartErrorCode, + errorMessage: String + ) { SoraLogger.e(TAG, "onWebRtcAudioTrackStartError: $errorCode. $errorMessage") reportError(SoraErrorReason.AUDIO_TRACK_START_ERROR, "$errorMessage [$errorCode]") } @@ -147,18 +161,20 @@ class RTCComponentFactory(private val mediaOption: SoraMediaOption, } return JavaAudioDeviceModule.builder(appContext) - .setUseHardwareAcousticEchoCanceler( - JavaAudioDeviceModule.isBuiltInAcousticEchoCancelerSupported() - && mediaOption.audioOption.useHardwareAcousticEchoCanceler) - .setUseHardwareNoiseSuppressor( - JavaAudioDeviceModule.isBuiltInNoiseSuppressorSupported() - && mediaOption.audioOption.useHardwareNoiseSuppressor) - .setAudioRecordErrorCallback(audioRecordErrorCallback) - .setAudioTrackErrorCallback(audioTrackErrorCallback) - .setAudioSource(mediaOption.audioOption.audioSource) - .setUseStereoInput(mediaOption.audioOption.useStereoInput) - .setUseStereoOutput(mediaOption.audioOption.useStereoOutput) - .createAudioDeviceModule() + .setUseHardwareAcousticEchoCanceler( + JavaAudioDeviceModule.isBuiltInAcousticEchoCancelerSupported() && + mediaOption.audioOption.useHardwareAcousticEchoCanceler + ) + .setUseHardwareNoiseSuppressor( + JavaAudioDeviceModule.isBuiltInNoiseSuppressorSupported() && + mediaOption.audioOption.useHardwareNoiseSuppressor + ) + .setAudioRecordErrorCallback(audioRecordErrorCallback) + .setAudioTrackErrorCallback(audioTrackErrorCallback) + .setAudioSource(mediaOption.audioOption.audioSource) + .setUseStereoInput(mediaOption.audioOption.useStereoInput) + .setUseStereoOutput(mediaOption.audioOption.useStereoOutput) + .createAudioDeviceModule() } private fun reportError(errorReason: SoraErrorReason, errorMessage: String) { diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/RTCLocalAudioManager.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/RTCLocalAudioManager.kt index ea61c490..0ba96e3c 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/RTCLocalAudioManager.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/RTCLocalAudioManager.kt @@ -2,12 +2,15 @@ package jp.shiguredo.sora.sdk.channel.rtc import jp.shiguredo.sora.sdk.channel.option.SoraAudioOption import jp.shiguredo.sora.sdk.util.SoraLogger -import org.webrtc.* -import java.util.* - +import org.webrtc.AudioSource +import org.webrtc.AudioTrack +import org.webrtc.MediaConstraints +import org.webrtc.MediaStream +import org.webrtc.PeerConnectionFactory +import java.util.UUID class RTCLocalAudioManager( - private val send: Boolean + private val send: Boolean ) { companion object { @@ -15,18 +18,18 @@ class RTCLocalAudioManager( } private var source: AudioSource? = null - private var track: AudioTrack? = null + private var track: AudioTrack? = null fun initTrack(factory: PeerConnectionFactory, audioOption: SoraAudioOption) { - SoraLogger.d(TAG, "initTrack: send=${send}") + SoraLogger.d(TAG, "initTrack: send=$send") if (send) { val constraints = createSourceConstraints(audioOption) source = factory.createAudioSource(constraints) - SoraLogger.d(TAG, "audio source created: ${source}") + SoraLogger.d(TAG, "audio source created: $source") val trackId = UUID.randomUUID().toString() track = factory.createAudioTrack(trackId, source) track!!.setEnabled(true) - SoraLogger.d(TAG, "audio track created: ${track}") + SoraLogger.d(TAG, "audio track created: $track") } } @@ -34,19 +37,23 @@ class RTCLocalAudioManager( val constraints = MediaConstraints() if (!audioOption.audioProcessingEchoCancellation) { constraints.mandatory.add( - MediaConstraints.KeyValuePair(SoraAudioOption.ECHO_CANCELLATION_CONSTRAINT, "false")) + MediaConstraints.KeyValuePair(SoraAudioOption.ECHO_CANCELLATION_CONSTRAINT, "false") + ) } - if(!audioOption.audioProcessingAutoGainControl) { + if (!audioOption.audioProcessingAutoGainControl) { constraints.mandatory.add( - MediaConstraints.KeyValuePair(SoraAudioOption.AUTO_GAIN_CONTROL_CONSTRAINT, "false")) + MediaConstraints.KeyValuePair(SoraAudioOption.AUTO_GAIN_CONTROL_CONSTRAINT, "false") + ) } if (!audioOption.audioProcessingHighpassFilter) { constraints.mandatory.add( - MediaConstraints.KeyValuePair(SoraAudioOption.HIGH_PASS_FILTER_CONSTRAINT, "false")) + MediaConstraints.KeyValuePair(SoraAudioOption.HIGH_PASS_FILTER_CONSTRAINT, "false") + ) } if (!audioOption.audioProcessingNoiseSuppression) { constraints.mandatory.add( - MediaConstraints.KeyValuePair(SoraAudioOption.NOISE_SUPPRESSION_CONSTRAINT, "false")) + MediaConstraints.KeyValuePair(SoraAudioOption.NOISE_SUPPRESSION_CONSTRAINT, "false") + ) } return constraints } @@ -63,4 +70,3 @@ class RTCLocalAudioManager( source = null } } - diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/RTCLocalVideoManager.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/RTCLocalVideoManager.kt index 9d48d8e4..b29f7e88 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/RTCLocalVideoManager.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/RTCLocalVideoManager.kt @@ -2,42 +2,52 @@ package jp.shiguredo.sora.sdk.channel.rtc import android.content.Context import jp.shiguredo.sora.sdk.util.SoraLogger -import org.webrtc.* -import java.util.* +import org.webrtc.EglBase +import org.webrtc.MediaStream +import org.webrtc.PeerConnectionFactory +import org.webrtc.SurfaceTextureHelper +import org.webrtc.VideoCapturer +import org.webrtc.VideoSource +import org.webrtc.VideoTrack +import java.util.UUID interface RTCLocalVideoManager { - fun initTrack(factory: PeerConnectionFactory, - eglContext: EglBase.Context?, - appContext: Context) + fun initTrack( + factory: PeerConnectionFactory, + eglContext: EglBase.Context?, + appContext: Context + ) fun attachTrackToStream(stream: MediaStream) fun dispose() } // just for Null-Object-Pattern -class RTCNullLocalVideoManager: RTCLocalVideoManager { - override fun initTrack(factory: PeerConnectionFactory, - eglContext: EglBase.Context?, - appContext: Context) {} +class RTCNullLocalVideoManager : RTCLocalVideoManager { + override fun initTrack( + factory: PeerConnectionFactory, + eglContext: EglBase.Context?, + appContext: Context + ) {} override fun attachTrackToStream(stream: MediaStream) {} override fun dispose() {} } -class RTCLocalVideoManagerImpl(private val capturer: VideoCapturer): RTCLocalVideoManager { +class RTCLocalVideoManagerImpl(private val capturer: VideoCapturer) : RTCLocalVideoManager { companion object { private val TAG = RTCLocalVideoManagerImpl::class.simpleName } var source: VideoSource? = null - var track: VideoTrack? = null + var track: VideoTrack? = null var surfaceTextureHelper: SurfaceTextureHelper? = null override fun initTrack(factory: PeerConnectionFactory, eglContext: EglBase.Context?, appContext: Context) { SoraLogger.d(TAG, "initTrack isScreencast=${capturer.isScreencast}") surfaceTextureHelper = - SurfaceTextureHelper.create("CaptureThread", eglContext); - source = factory.createVideoSource(capturer.isScreencast); - capturer.initialize(surfaceTextureHelper, appContext, source!!.capturerObserver); + SurfaceTextureHelper.create("CaptureThread", eglContext) + source = factory.createVideoSource(capturer.isScreencast) + capturer.initialize(surfaceTextureHelper, appContext, source!!.capturerObserver) val trackId = UUID.randomUUID().toString() track = factory.createVideoTrack(trackId, source) @@ -47,7 +57,7 @@ class RTCLocalVideoManagerImpl(private val capturer: VideoCapturer): RTCLocalVid override fun attachTrackToStream(stream: MediaStream) { SoraLogger.d(TAG, "attachTrackToStream") - track?.let{ stream.addTrack(it) } + track?.let { stream.addTrack(it) } } override fun dispose() { diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt index 85521e34..1f67e99e 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt @@ -2,10 +2,18 @@ package jp.shiguredo.sora.sdk.channel.signaling import jp.shiguredo.sora.sdk.channel.option.SoraChannelRole import jp.shiguredo.sora.sdk.channel.option.SoraMediaOption -import jp.shiguredo.sora.sdk.channel.signaling.message.* +import jp.shiguredo.sora.sdk.channel.signaling.message.MessageConverter +import jp.shiguredo.sora.sdk.channel.signaling.message.NotificationMessage +import jp.shiguredo.sora.sdk.channel.signaling.message.OfferMessage +import jp.shiguredo.sora.sdk.channel.signaling.message.PushMessage +import jp.shiguredo.sora.sdk.channel.signaling.message.SwitchedMessage import jp.shiguredo.sora.sdk.error.SoraErrorReason import jp.shiguredo.sora.sdk.util.SoraLogger -import okhttp3.* +import okhttp3.OkHttpClient +import okhttp3.Request +import okhttp3.Response +import okhttp3.WebSocket +import okhttp3.WebSocketListener import okio.ByteString import org.webrtc.RTCStatsReport import org.webrtc.SessionDescription @@ -211,15 +219,18 @@ class SignalingChannelImpl @JvmOverloads constructor( private fun onOfferMessage(text: String) { val offerMessage = MessageConverter.parseOfferMessage(text) - SoraLogger.d(TAG, """[signaling:$role] <- offer - |${offerMessage.sdp}""".trimMargin()) + SoraLogger.d( + TAG, + """[signaling:$role] <- offer + |${offerMessage.sdp}""".trimMargin() + ) listener?.onInitialOffer(offerMessage) } private fun onSwitchedMessage(text: String) { val switchMessage = MessageConverter.parseSwitchMessage(text) - SoraLogger.d(TAG, "[signaling:$role] <- switch ${switchMessage}") + SoraLogger.d(TAG, "[signaling:$role] <- switch $switchMessage") listener?.onSwitched(switchMessage) } @@ -360,20 +371,18 @@ class SignalingChannelImpl @JvmOverloads constructor( val json = it MessageConverter.parseType(json)?.let { when (it) { - "offer" -> onOfferMessage(json) + "offer" -> onOfferMessage(json) "switched" -> onSwitchedMessage(json) - "ping" -> onPingMessage(json) - "update" -> onUpdateMessage(json) + "ping" -> onPingMessage(json) + "update" -> onUpdateMessage(json) "re-offer" -> onReOfferMessage(json) "notify" -> onNotifyMessage(json) "push" -> onPushMessage(json) "redirect" -> onRedirectMessage(json) else -> SoraLogger.i(TAG, "received unknown-type message") } - } ?: closeWithError("failed to parse 'type' from message") } - } catch (e: Exception) { SoraLogger.w(TAG, e.toString()) } @@ -437,4 +446,3 @@ class SignalingChannelImpl @JvmOverloads constructor( } } } - diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/message/Catalog.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/message/Catalog.kt index be7525a8..83dc8b90 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/message/Catalog.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/message/Catalog.kt @@ -4,17 +4,17 @@ import com.google.gson.annotations.SerializedName import jp.shiguredo.sora.sdk.util.SDKInfo data class MessageCommonPart( - @SerializedName("type") val type: String? + @SerializedName("type") val type: String? ) data class PingMessage( - @SerializedName("type") val type: String = "ping", - @SerializedName("stats") val stats: Boolean? + @SerializedName("type") val type: String = "ping", + @SerializedName("stats") val stats: Boolean? ) data class PongMessage( - @SerializedName("type") val type: String = "pong", - @SerializedName("stats") val stats: List? = null + @SerializedName("type") val type: String = "pong", + @SerializedName("stats") val stats: List? = null ) data class ConnectMessage( @@ -48,44 +48,44 @@ data class ConnectMessage( ) data class VideoSetting( - @SerializedName("codec_type") val codecType: String, - @SerializedName("bit_rate") var bitRate: Int? = null + @SerializedName("codec_type") val codecType: String, + @SerializedName("bit_rate") var bitRate: Int? = null ) data class AudioSetting( - @SerializedName("codec_type") val codecType: String?, - @SerializedName("bit_rate") var bitRate: Int? = null, - @SerializedName("opus_params") var opusParams: OpusParams? = null + @SerializedName("codec_type") val codecType: String?, + @SerializedName("bit_rate") var bitRate: Int? = null, + @SerializedName("opus_params") var opusParams: OpusParams? = null ) data class OpusParams( - @SerializedName("channels") var channels: Int? = null, - @SerializedName("clock_rate") var clockRate: Int? = null, - @SerializedName("maxplaybackrate") var maxplaybackrate: Int? = null, - @SerializedName("stereo") var stereo: Boolean? = null, - @SerializedName("sprop_stereo") var spropStereo: Boolean? = null, - @SerializedName("minptime") var minptime: Int? = null, - @SerializedName("useinbandfec") var useinbandfec: Boolean? = null, - @SerializedName("usedtx") var usedtx: Boolean? = null + @SerializedName("channels") var channels: Int? = null, + @SerializedName("clock_rate") var clockRate: Int? = null, + @SerializedName("maxplaybackrate") var maxplaybackrate: Int? = null, + @SerializedName("stereo") var stereo: Boolean? = null, + @SerializedName("sprop_stereo") var spropStereo: Boolean? = null, + @SerializedName("minptime") var minptime: Int? = null, + @SerializedName("useinbandfec") var useinbandfec: Boolean? = null, + @SerializedName("usedtx") var usedtx: Boolean? = null ) data class IceServer( - @SerializedName("urls") val urls: List, - @SerializedName("credential") val credential: String, - @SerializedName("username") val username: String + @SerializedName("urls") val urls: List, + @SerializedName("credential") val credential: String, + @SerializedName("username") val username: String ) data class OfferConfig( - @SerializedName("iceServers") val iceServers: List, - @SerializedName("iceTransportPolicy") val iceTransportPolicy: String + @SerializedName("iceServers") val iceServers: List, + @SerializedName("iceTransportPolicy") val iceTransportPolicy: String ) data class Encoding( - @SerializedName("rid") val rid: String?, - @SerializedName("active") val active: Boolean?, - @SerializedName("maxBitrate") val maxBitrate: Int?, - @SerializedName("maxFramerate") val maxFramerate: Int?, - @SerializedName("scaleResolutionDownBy") val scaleResolutionDownBy: Double? + @SerializedName("rid") val rid: String?, + @SerializedName("active") val active: Boolean?, + @SerializedName("maxBitrate") val maxBitrate: Int?, + @SerializedName("maxFramerate") val maxFramerate: Int?, + @SerializedName("scaleResolutionDownBy") val scaleResolutionDownBy: Double? ) data class RedirectMessage( @@ -94,91 +94,91 @@ data class RedirectMessage( ) data class OfferMessage( - @SerializedName("type") val type: String = "offer", - @SerializedName("sdp") val sdp: String, - @SerializedName("client_id") val clientId: String, - @SerializedName("connection_id") val connectionId: String, - @SerializedName("metadata") val metadata: Any?, - @SerializedName("config") val config: OfferConfig? = null, - @SerializedName("mid") val mid: Map? = null, - @SerializedName("encodings") val encodings: List?, - @SerializedName("data_channels") val dataChannels: List>? = null + @SerializedName("type") val type: String = "offer", + @SerializedName("sdp") val sdp: String, + @SerializedName("client_id") val clientId: String, + @SerializedName("connection_id") val connectionId: String, + @SerializedName("metadata") val metadata: Any?, + @SerializedName("config") val config: OfferConfig? = null, + @SerializedName("mid") val mid: Map? = null, + @SerializedName("encodings") val encodings: List?, + @SerializedName("data_channels") val dataChannels: List>? = null ) data class SwitchedMessage( - @SerializedName("type") val type: String = "switched", - @SerializedName("ignore_disconnect_websocket") val ignoreDisconnectWebsocket: Boolean? = null + @SerializedName("type") val type: String = "switched", + @SerializedName("ignore_disconnect_websocket") val ignoreDisconnectWebsocket: Boolean? = null ) data class UpdateMessage( - @SerializedName("type") val type: String = "update", - @SerializedName("sdp") val sdp: String + @SerializedName("type") val type: String = "update", + @SerializedName("sdp") val sdp: String ) data class ReOfferMessage( - @SerializedName("type") val type: String = "re-offer", - @SerializedName("sdp") val sdp: String + @SerializedName("type") val type: String = "re-offer", + @SerializedName("sdp") val sdp: String ) data class ReAnswerMessage( - @SerializedName("type") val type: String = "re-answer", - @SerializedName("sdp") val sdp: String + @SerializedName("type") val type: String = "re-answer", + @SerializedName("sdp") val sdp: String ) data class AnswerMessage( - @SerializedName("type") val type: String = "answer", - @SerializedName("sdp") val sdp: String + @SerializedName("type") val type: String = "answer", + @SerializedName("sdp") val sdp: String ) data class CandidateMessage( - @SerializedName("type") val type: String = "candidate", - @SerializedName("candidate") val candidate: String + @SerializedName("type") val type: String = "candidate", + @SerializedName("candidate") val candidate: String ) data class PushMessage( - @SerializedName("type") val type: String = "push", - @SerializedName("data") var data: Any? = null + @SerializedName("type") val type: String = "push", + @SerializedName("data") var data: Any? = null ) data class ReqStatsMessage( - @SerializedName("type") val type: String = "req-stats" + @SerializedName("type") val type: String = "req-stats" ) data class StatsMessage( - @SerializedName("type") val type: String = "stats", - @SerializedName("reports") val reports: List + @SerializedName("type") val type: String = "stats", + @SerializedName("reports") val reports: List ) data class NotificationMessage( - @SerializedName("type") val type: String = "notify", - @SerializedName("event_type") val eventType: String, - @SerializedName("role") val role: String?, - @SerializedName("client_id") val clientId: String, - @SerializedName("connection_id") val connectionId: String?, - @SerializedName("audio") val audio: Boolean?, - @SerializedName("video") val video: Boolean?, - @SerializedName("metadata") val metadata: Any?, - @Deprecated("metadata_list は将来の Sora のリリースでフィールド名を data に変更する予定です。") - @SerializedName("metadata_list") val metadataList: Any?, - @SerializedName("minutes") val connectionTime: Long?, - @SerializedName("channel_connections") val numberOfConnections: Int?, - @Deprecated("numberOfUpstreamConnections は 2021 年 6 月リリース予定の Sora にて廃止されます。") - @SerializedName("channel_upstream_connections") val numberOfUpstreamConnections: Int?, - @Deprecated("numberOfDownstreamConnections は 2021 年 6 月リリース予定の Sora にて廃止されます。") - @SerializedName("channel_downstream_connections") val numberOfDownstreamConnections: Int?, - @SerializedName("channel_sendrecv_connections") val numberOfSendrecvConnections: Int?, - @SerializedName("channel_sendonly_connections") val numberOfSendonlyConnections: Int?, - @SerializedName("channel_recvonly_connections") val numberOfRecvonlyConnections: Int?, - @SerializedName("unstable_level") val unstableLevel: Int?, - @SerializedName("channel_id") val channelId: String?, - @SerializedName("spotlight_id") val spotlightId: String?, - @SerializedName("fixed") val fixed: Boolean?, - @SerializedName("authn_metadata") val authnMetadata: Any?, - @SerializedName("authz_metadata") val authzMetadata: Any?, - @SerializedName("data") val data: Any?, - @SerializedName("turn_transport_type") val turnTransportType: String?, + @SerializedName("type") val type: String = "notify", + @SerializedName("event_type") val eventType: String, + @SerializedName("role") val role: String?, + @SerializedName("client_id") val clientId: String, + @SerializedName("connection_id") val connectionId: String?, + @SerializedName("audio") val audio: Boolean?, + @SerializedName("video") val video: Boolean?, + @SerializedName("metadata") val metadata: Any?, + @Deprecated("metadata_list は将来の Sora のリリースでフィールド名を data に変更する予定です。") + @SerializedName("metadata_list") val metadataList: Any?, + @SerializedName("minutes") val connectionTime: Long?, + @SerializedName("channel_connections") val numberOfConnections: Int?, + @Deprecated("numberOfUpstreamConnections は 2021 年 6 月リリース予定の Sora にて廃止されます。") + @SerializedName("channel_upstream_connections") val numberOfUpstreamConnections: Int?, + @Deprecated("numberOfDownstreamConnections は 2021 年 6 月リリース予定の Sora にて廃止されます。") + @SerializedName("channel_downstream_connections") val numberOfDownstreamConnections: Int?, + @SerializedName("channel_sendrecv_connections") val numberOfSendrecvConnections: Int?, + @SerializedName("channel_sendonly_connections") val numberOfSendonlyConnections: Int?, + @SerializedName("channel_recvonly_connections") val numberOfRecvonlyConnections: Int?, + @SerializedName("unstable_level") val unstableLevel: Int?, + @SerializedName("channel_id") val channelId: String?, + @SerializedName("spotlight_id") val spotlightId: String?, + @SerializedName("fixed") val fixed: Boolean?, + @SerializedName("authn_metadata") val authnMetadata: Any?, + @SerializedName("authz_metadata") val authzMetadata: Any?, + @SerializedName("data") val data: Any?, + @SerializedName("turn_transport_type") val turnTransportType: String?, ) data class DisconnectMessage( - @SerializedName("type") val type: String = "disconnect" + @SerializedName("type") val type: String = "disconnect" ) diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/message/MessageConverter.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/message/MessageConverter.kt index f5231c77..ff9f97d2 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/message/MessageConverter.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/message/MessageConverter.kt @@ -8,11 +8,14 @@ import jp.shiguredo.sora.sdk.util.SoraLogger import org.webrtc.RTCStats import org.webrtc.RTCStatsReport -class SoraRTCStats(private val map: Map): Map by map { - constructor(stats: RTCStats) : this(mapOf( - "id" to stats.id, - "type" to stats.type, - "timestamp" to stats.timestampUs) + stats.members) {} +class SoraRTCStats(private val map: Map) : Map by map { + constructor(stats: RTCStats) : this( + mapOf( + "id" to stats.id, + "type" to stats.type, + "timestamp" to stats.timestampUs + ) + stats.members + ) {} } class MessageConverter { @@ -71,7 +74,6 @@ class MessageConverter { } msg.audio = audioSetting - } else { msg.audio = false } @@ -122,9 +124,13 @@ class MessageConverter { } fun buildPongMessage(stats: RTCStatsReport?): String { - return gson.toJson(PongMessage(stats = stats?.let { - stats.statsMap.values.map { stats -> SoraRTCStats(stats) } - })) + return gson.toJson( + PongMessage( + stats = stats?.let { + stats.statsMap.values.map { stats -> SoraRTCStats(stats) } + } + ) + ) } fun buildUpdateAnswerMessage(sdp: String): String { @@ -193,4 +199,3 @@ class MessageConverter { } } } - diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/codec/SimulcastVideoEncoderFactoryWrapper.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/codec/SimulcastVideoEncoderFactoryWrapper.kt index 9d4915c3..0457f825 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/codec/SimulcastVideoEncoderFactoryWrapper.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/codec/SimulcastVideoEncoderFactoryWrapper.kt @@ -4,9 +4,11 @@ import jp.shiguredo.sora.sdk.util.SoraLogger import org.webrtc.* import java.util.concurrent.* -internal class SimulcastVideoEncoderFactoryWrapper(sharedContext: EglBase.Context?, - enableIntelVp8Encoder: Boolean, - enableH264HighProfile: Boolean) : VideoEncoderFactory { +internal class SimulcastVideoEncoderFactoryWrapper( + sharedContext: EglBase.Context?, + enableIntelVp8Encoder: Boolean, + enableH264HighProfile: Boolean +) : VideoEncoderFactory { /* * ソフトウェアエンコーダーの利用を優先するファクトリーです。 @@ -45,7 +47,6 @@ internal class SimulcastVideoEncoderFactoryWrapper(sharedContext: EglBase.Contex supportedCodecInfos.addAll(hardwareVideoEncoderFactory.supportedCodecs) return supportedCodecInfos.toTypedArray() } - } // ストリーム単位のエンコーダをラップした上で以下を行うクラス。 @@ -64,8 +65,11 @@ internal class SimulcastVideoEncoderFactoryWrapper(sharedContext: EglBase.Contex override fun initEncode(settings: VideoEncoder.Settings, callback: VideoEncoder.Callback?): VideoCodecStatus { streamSettings = settings - val future = executor.submit(Callable { - SoraLogger.i(TAG, """initEncode() thread=${Thread.currentThread().name} [${Thread.currentThread().id}] + val future = executor.submit( + Callable { + SoraLogger.i( + TAG, + """initEncode() thread=${Thread.currentThread().name} [${Thread.currentThread().id}] | streamSettings: | numberOfCores=${settings.numberOfCores} | width=${settings.width} @@ -75,9 +79,11 @@ internal class SimulcastVideoEncoderFactoryWrapper(sharedContext: EglBase.Contex | automaticResizeOn=${settings.automaticResizeOn} | numberOfSimulcastStreams=${settings.numberOfSimulcastStreams} | lossNotification=${settings.capabilities.lossNotification} - """.trimMargin()) - return@Callable encoder.initEncode(settings, callback) - }) + """.trimMargin() + ) + return@Callable encoder.initEncode(settings, callback) + } + ) return future.get() } @@ -87,29 +93,33 @@ internal class SimulcastVideoEncoderFactoryWrapper(sharedContext: EglBase.Contex } override fun encode(frame: VideoFrame, encodeInfo: VideoEncoder.EncodeInfo?): VideoCodecStatus { - val future = executor.submit(Callable { - // SoraLogger.d(TAG, "encode() buffer=${frame.buffer}, thread=${Thread.currentThread().name} " - // + "[${Thread.currentThread().id}]") - if (streamSettings == null) { - return@Callable encoder.encode(frame, encodeInfo) - } else if (frame.buffer.width == streamSettings!!.width) { - return@Callable encoder.encode(frame, encodeInfo) - } else { - // 上がってきたバッファと initEncode() の設定が違うパターン、ここでスケールする必要がある - val originalBuffer = frame.buffer - // val ratio = originalBuffer.width / streamSettings!!.width - // SoraLogger.d(TAG, "encode: Scaling needed, " + - // "${buffer.width}x${buffer.height} to ${streamSettings!!.width}x${streamSettings!!.height}, " + - // "ratio=$ratio") - // TODO(shino): へんなスケールファクタの場合に正しく動作するか? - val adaptedBuffer = originalBuffer.cropAndScale(0, 0, originalBuffer.width, originalBuffer.height, - streamSettings!!.width, streamSettings!!.height) - val adaptedFrame = VideoFrame(adaptedBuffer, frame.rotation, frame.timestampNs) - val result = encoder.encode(adaptedFrame, encodeInfo) - adaptedBuffer.release() - return@Callable result + val future = executor.submit( + Callable { + // SoraLogger.d(TAG, "encode() buffer=${frame.buffer}, thread=${Thread.currentThread().name} " + // + "[${Thread.currentThread().id}]") + if (streamSettings == null) { + return@Callable encoder.encode(frame, encodeInfo) + } else if (frame.buffer.width == streamSettings!!.width) { + return@Callable encoder.encode(frame, encodeInfo) + } else { + // 上がってきたバッファと initEncode() の設定が違うパターン、ここでスケールする必要がある + val originalBuffer = frame.buffer + // val ratio = originalBuffer.width / streamSettings!!.width + // SoraLogger.d(TAG, "encode: Scaling needed, " + + // "${buffer.width}x${buffer.height} to ${streamSettings!!.width}x${streamSettings!!.height}, " + + // "ratio=$ratio") + // TODO(shino): へんなスケールファクタの場合に正しく動作するか? + val adaptedBuffer = originalBuffer.cropAndScale( + 0, 0, originalBuffer.width, originalBuffer.height, + streamSettings!!.width, streamSettings!!.height + ) + val adaptedFrame = VideoFrame(adaptedBuffer, frame.rotation, frame.timestampNs) + val result = encoder.encode(adaptedFrame, encodeInfo) + adaptedBuffer.release() + return@Callable result + } } - }) + ) return future.get() } @@ -143,14 +153,14 @@ internal class SimulcastVideoEncoderFactoryWrapper(sharedContext: EglBase.Contex } } - private val primary: VideoEncoderFactory private val fallback: VideoEncoderFactory private val native: SimulcastVideoEncoderFactory init { val hardwareVideoEncoderFactory = HardwareVideoEncoderFactory( - sharedContext, enableIntelVp8Encoder, enableH264HighProfile) + sharedContext, enableIntelVp8Encoder, enableH264HighProfile + ) primary = StreamEncoderWrapperFactory(hardwareVideoEncoderFactory) fallback = StreamEncoderWrapperFactory(Fallback(primary)) native = SimulcastVideoEncoderFactory(primary, fallback) @@ -163,5 +173,4 @@ internal class SimulcastVideoEncoderFactoryWrapper(sharedContext: EglBase.Contex override fun getSupportedCodecs(): Array { return native.supportedCodecs } - -} \ No newline at end of file +} diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/util/ByteBufferBackedInputStream.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/util/ByteBufferBackedInputStream.kt index 715237d1..77379499 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/util/ByteBufferBackedInputStream.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/util/ByteBufferBackedInputStream.kt @@ -1,7 +1,6 @@ package jp.shiguredo.sora.sdk.util import java.io.InputStream -import java.io.OutputStream import java.nio.ByteBuffer class ByteBufferBackedInputStream(private val buf: ByteBuffer) : InputStream() { diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/util/ReusableCompositeDisposable.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/util/ReusableCompositeDisposable.kt index 5d9bee6e..f61f0a5c 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/util/ReusableCompositeDisposable.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/util/ReusableCompositeDisposable.kt @@ -21,5 +21,4 @@ class ReusableCompositeDisposable { compositeDisposable?.dispose() compositeDisposable = null } - -} \ No newline at end of file +} diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/util/SDKInfo.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/util/SDKInfo.kt index 70017f18..3947e74a 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/util/SDKInfo.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/util/SDKInfo.kt @@ -10,27 +10,25 @@ class SDKInfo { fun sdkInfo(): String { return "Sora Android SDK " + BuildConfig.VERSION_NAME + - " (" + BuildConfig.REVISION + ")" + " (" + BuildConfig.REVISION + ")" } fun libwebrtcInfo(): String { return "Shiguredo-build " + WebrtcBuildVersion.webrtc_branch + - " (" + BuildConfig.LIBWEBRTC_VERSION + " " + - WebrtcBuildVersion.webrtc_revision.substring(0, 7) + ")" + " (" + BuildConfig.LIBWEBRTC_VERSION + " " + + WebrtcBuildVersion.webrtc_revision.substring(0, 7) + ")" } fun deviceInfo(): String { return "Android-SDK: " + Build.VERSION.SDK_INT + ", " + - "Release: " + Build.VERSION.RELEASE + ", " + - "Id: " + Build.ID + ", " + - "Device: " + Build.DEVICE + ", " + - "Hardware: " + Build.HARDWARE + ", " + - "Brand: " + Build.BRAND + ", " + - "Manufacturer: " + Build.MANUFACTURER + ", " + - "Model: " + Build.MODEL + ", " + - "Product: " + Build.PRODUCT + "Release: " + Build.VERSION.RELEASE + ", " + + "Id: " + Build.ID + ", " + + "Device: " + Build.DEVICE + ", " + + "Hardware: " + Build.HARDWARE + ", " + + "Brand: " + Build.BRAND + ", " + + "Manufacturer: " + Build.MANUFACTURER + ", " + + "Model: " + Build.MODEL + ", " + + "Product: " + Build.PRODUCT } - } - -} \ No newline at end of file +} diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/util/SoraLogger.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/util/SoraLogger.kt index 2c469d7e..06104f98 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/util/SoraLogger.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/util/SoraLogger.kt @@ -45,4 +45,3 @@ class SoraLogger { } } } - diff --git a/sora-android-sdk/src/test/kotlin/jp/shiguredo/sora/sdk/ConnectClientIdTest.kt b/sora-android-sdk/src/test/kotlin/jp/shiguredo/sora/sdk/ConnectClientIdTest.kt index db9f1e10..14833957 100644 --- a/sora-android-sdk/src/test/kotlin/jp/shiguredo/sora/sdk/ConnectClientIdTest.kt +++ b/sora-android-sdk/src/test/kotlin/jp/shiguredo/sora/sdk/ConnectClientIdTest.kt @@ -5,9 +5,6 @@ import jp.shiguredo.sora.sdk.channel.signaling.message.ConnectMessage import org.junit.Test import kotlin.test.assertEquals import kotlin.test.assertFalse -import kotlin.test.assertTrue -import kotlin.test.fail - class ConnectClientIdTest { val gson = Gson() @@ -26,14 +23,14 @@ class ConnectClientIdTest { assertEquals("It's me", message["client_id"]) } - private fun roundtrip(clientId: String?) : Map<*, *> { + private fun roundtrip(clientId: String?): Map<*, *> { val original = ConnectMessage( - role = "upstream", - channelId = "sora", - sdp = "", - clientId = clientId + role = "upstream", + channelId = "sora", + sdp = "", + clientId = clientId ) val serialized = gson.toJson(original) return gson.fromJson(serialized, Map::class.java) } -} \ No newline at end of file +} diff --git a/sora-android-sdk/src/test/kotlin/jp/shiguredo/sora/sdk/ConnectMetadataJsonTest.kt b/sora-android-sdk/src/test/kotlin/jp/shiguredo/sora/sdk/ConnectMetadataJsonTest.kt index 8ef2d46f..a5147ba9 100644 --- a/sora-android-sdk/src/test/kotlin/jp/shiguredo/sora/sdk/ConnectMetadataJsonTest.kt +++ b/sora-android-sdk/src/test/kotlin/jp/shiguredo/sora/sdk/ConnectMetadataJsonTest.kt @@ -6,7 +6,6 @@ import org.junit.Test import kotlin.test.assertEquals import kotlin.test.fail - class ConnectMetadataJsonTest { val gson = Gson() @@ -32,7 +31,7 @@ class ConnectMetadataJsonTest { @Test fun listMetadata1() { val message = roundtrip(listOf(1, 2, 3)) - if(message.metadata !is List<*>) { + if (message.metadata !is List<*>) { fail("metadata should be list: ${message.metadata}") } val metadata = message.metadata as List<*> @@ -44,7 +43,7 @@ class ConnectMetadataJsonTest { @Test fun listMetadata2() { val message = roundtrip(listOf("foo", "bar", "baz")) - if(message.metadata !is List<*>) { + if (message.metadata !is List<*>) { fail("metadata should be list: ${message.metadata}") } val metadata = message.metadata as List<*> @@ -55,12 +54,14 @@ class ConnectMetadataJsonTest { } @Test fun mapMetadata() { - val message = roundtrip(hashMapOf( + val message = roundtrip( + hashMapOf( "foo" to 1, "bar" to "baz", "baz" to listOf("ham", "eggs", "bacon") - )) - if(message.metadata !is Map<*, *>) { + ) + ) + if (message.metadata !is Map<*, *>) { fail("metadata should be map: ${message.metadata}") } val metadata = message.metadata as Map<*, *> @@ -71,9 +72,9 @@ class ConnectMetadataJsonTest { } @Test fun setMetadata() { - val message = roundtrip(setOf(1,2,3)) + val message = roundtrip(setOf(1, 2, 3)) // Set は List で返ってくる - if(message.metadata !is List<*>) { + if (message.metadata !is List<*>) { fail("metadata should be set: ${message.metadata}") } val metadata = message.metadata as List<*> @@ -81,15 +82,14 @@ class ConnectMetadataJsonTest { assertEquals(listOf(1.0, 2.0, 3.0), metadata) } - - private fun roundtrip(metadata: Any?) : ConnectMessage { + private fun roundtrip(metadata: Any?): ConnectMessage { val original = ConnectMessage( - role = "upstream", - channelId = "sora", - sdp = "", - metadata = metadata + role = "upstream", + channelId = "sora", + sdp = "", + metadata = metadata ) val serialized = gson.toJson(original) return gson.fromJson(serialized, ConnectMessage::class.java) } -} \ No newline at end of file +} diff --git a/sora-android-sdk/src/test/kotlin/jp/shiguredo/sora/sdk/TrivialTest.kt b/sora-android-sdk/src/test/kotlin/jp/shiguredo/sora/sdk/TrivialTest.kt index c723bb76..4b34f2c0 100644 --- a/sora-android-sdk/src/test/kotlin/jp/shiguredo/sora/sdk/TrivialTest.kt +++ b/sora-android-sdk/src/test/kotlin/jp/shiguredo/sora/sdk/TrivialTest.kt @@ -3,7 +3,6 @@ package jp.shiguredo.sora.sdk import org.junit.Test import kotlin.test.assertTrue - class TrivialTest { @Test fun myFirstTest() { From 047b8824307aa2cce47fff04be887751c85a26f3 Mon Sep 17 00:00:00 2001 From: SUZUKI Tetsuya Date: Mon, 6 Dec 2021 17:07:18 +0900 Subject: [PATCH 55/85] =?UTF-8?q?Android=20Studio=20=E3=81=AE=E8=A8=AD?= =?UTF-8?q?=E5=AE=9A=E3=82=92=20ktlint=20=E3=81=A8=E7=AB=B6=E5=90=88?= =?UTF-8?q?=E3=81=97=E3=81=AA=E3=81=84=E3=82=88=E3=81=86=E3=81=AB=E3=81=99?= =?UTF-8?q?=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/codeStyles/Project.xml | 124 +++++++++++++++++++++++++++ .idea/codeStyles/codeStyleConfig.xml | 5 ++ 2 files changed, 129 insertions(+) create mode 100644 .idea/codeStyles/Project.xml create mode 100644 .idea/codeStyles/codeStyleConfig.xml diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 00000000..61a2c750 --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,124 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 00000000..79ee123c --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file From 26ec899465af8d7913bc22a1fccf938ece16ef21 Mon Sep 17 00:00:00 2001 From: SUZUKI Tetsuya Date: Tue, 7 Dec 2021 15:38:13 +0900 Subject: [PATCH 56/85] =?UTF-8?q?org.ajoberstar.grgit=20=E3=81=AE=E3=83=90?= =?UTF-8?q?=E3=83=BC=E3=82=B8=E3=83=A7=E3=83=B3=E3=82=92=E4=B8=8A=E3=81=92?= =?UTF-8?q?=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index b8ba4e44..33578c53 100644 --- a/build.gradle +++ b/build.gradle @@ -18,7 +18,7 @@ buildscript { classpath 'com.android.tools.build:gradle:4.2.2' classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlin_version}" - classpath 'org.ajoberstar.grgit:grgit-gradle:4.1.0' + classpath 'org.ajoberstar.grgit:grgit-gradle:4.1.1' classpath "org.jetbrains.dokka:dokka-gradle-plugin:${dokka_version}" classpath "com.github.ben-manes:gradle-versions-plugin:0.38.0" From a2dde60f2d267baed546e96ea24c199c86790d4c Mon Sep 17 00:00:00 2001 From: SUZUKI Tetsuya Date: Tue, 7 Dec 2021 15:57:33 +0900 Subject: [PATCH 57/85] =?UTF-8?q?=E8=AD=98=E5=88=A5=E5=AD=90=E3=82=92?= =?UTF-8?q?=E5=80=8B=E5=88=A5=E3=81=AB=E3=82=A4=E3=83=B3=E3=83=9D=E3=83=BC?= =?UTF-8?q?=E3=83=88=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../codec/SimulcastVideoEncoderFactoryWrapper.kt | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/codec/SimulcastVideoEncoderFactoryWrapper.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/codec/SimulcastVideoEncoderFactoryWrapper.kt index 0457f825..dc029de7 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/codec/SimulcastVideoEncoderFactoryWrapper.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/codec/SimulcastVideoEncoderFactoryWrapper.kt @@ -1,8 +1,19 @@ package jp.shiguredo.sora.sdk.codec import jp.shiguredo.sora.sdk.util.SoraLogger -import org.webrtc.* -import java.util.concurrent.* +import org.webrtc.EglBase +import org.webrtc.HardwareVideoEncoderFactory +import org.webrtc.SimulcastVideoEncoderFactory +import org.webrtc.SoftwareVideoEncoderFactory +import org.webrtc.VideoCodecInfo +import org.webrtc.VideoCodecStatus +import org.webrtc.VideoEncoder +import org.webrtc.VideoEncoderFactory +import org.webrtc.VideoEncoderFallback +import org.webrtc.VideoFrame +import java.util.concurrent.Callable +import java.util.concurrent.ExecutorService +import java.util.concurrent.Executors internal class SimulcastVideoEncoderFactoryWrapper( sharedContext: EglBase.Context?, From dd7f9211263725fefff589ff22750ce8151bf60d Mon Sep 17 00:00:00 2001 From: SUZUKI Tetsuya Date: Tue, 7 Dec 2021 16:41:46 +0900 Subject: [PATCH 58/85] =?UTF-8?q?ktlint=20=E3=81=AE=E8=A8=AD=E5=AE=9A?= =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sora-android-sdk/build.gradle | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sora-android-sdk/build.gradle b/sora-android-sdk/build.gradle index 3b51a0bf..6781cea1 100644 --- a/sora-android-sdk/build.gradle +++ b/sora-android-sdk/build.gradle @@ -68,6 +68,16 @@ dokkaHtml.configure { } } +ktlint { + version = "0.43.2" + android = false + outputToConsole = true + reporters { + reporter "checkstyle" + } + ignoreFailures = false +} + dependencies { api "com.github.shiguredo:shiguredo-webrtc-android:${libwebrtc_version}" From 47441c26174d9cc6b240c0798f70299dd83678ce Mon Sep 17 00:00:00 2001 From: SUZUKI Tetsuya Date: Tue, 7 Dec 2021 17:04:40 +0900 Subject: [PATCH 59/85] =?UTF-8?q?=E3=83=93=E3=83=AB=E3=83=89=E6=99=82?= =?UTF-8?q?=E3=81=AB=E3=83=95=E3=82=A9=E3=83=BC=E3=83=9E=E3=83=83=E3=83=88?= =?UTF-8?q?=E3=82=92=E8=A1=8C=E3=81=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sora-android-sdk/build.gradle | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sora-android-sdk/build.gradle b/sora-android-sdk/build.gradle index 6781cea1..ce4f393b 100644 --- a/sora-android-sdk/build.gradle +++ b/sora-android-sdk/build.gradle @@ -41,9 +41,12 @@ android { testOptions { unitTests.includeAndroidResources = true } +} +tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach { + dependsOn("ktlintFormat") kotlinOptions { - jvmTarget = '1.8' + jvmTarget = "1.8" } } From 905ff5184f4ea47590174dbae628aa619b0d2105 Mon Sep 17 00:00:00 2001 From: SUZUKI Tetsuya Date: Wed, 8 Dec 2021 15:25:08 +0900 Subject: [PATCH 60/85] =?UTF-8?q?=E3=82=B3=E3=83=BC=E3=83=89=E3=81=AE?= =?UTF-8?q?=E3=83=95=E3=82=A9=E3=83=BC=E3=83=9E=E3=83=83=E3=83=88=E3=81=AB?= =?UTF-8?q?=E3=81=A4=E3=81=84=E3=81=A6=E8=AA=AC=E6=98=8E=E3=82=92=E6=9B=B8?= =?UTF-8?q?=E3=81=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/CODE_FORMAT.md | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 docs/CODE_FORMAT.md diff --git a/docs/CODE_FORMAT.md b/docs/CODE_FORMAT.md new file mode 100644 index 00000000..c206090e --- /dev/null +++ b/docs/CODE_FORMAT.md @@ -0,0 +1,41 @@ +# コードのフォーマット + +Sora Android SDK ではソースコードの lint チェックとフォーマットが可能です。 + + +## ツール + +ktlint を利用します。 ktlint の Gradle プラグインを導入しているので、 Gradle プロジェクトを同期すればインストールされます。 + + +## 実行方法 + +Android Studio ではビルド時に lint チェックとフォーマットが実行されます。コマンドラインでは Gradle のタスクとして実行できます。 + +チェックのみ: + +``` +./gradlew ktlintCheck +``` + +フォーマット: + +``` +./gradlew ktlintFormat +``` + +ルール違反があった場合、詳細はコンソールの他に `sora-android-sdk/build/reports/ktlint` 以下のディレクトリにファイルとして出力されます。 `sora-android-sdk/main/src/main` 以下のソースコードの lint チェック結果は `ktlintMainSourceSetCheck/ktlintMainSourceSetCheck.txt` を参照してください。 + + +## ルール + +[Kotlin スタイルガイド](https://developer.android.com/kotlin/style-guide) に従います。 ktlint はデフォルトの設定で同スタイルに従うので、特に ktlint の設定はしていません。 + + +## 注意 + +本リポジトリには Android Studio の設定が含まれています。設定を変更しないでください。 + +設定内容は次の通りです。 + +- Editor > Code Style > Kotlin > Imports にて、 `import` 文でのワイルドカードの使用を無効にします。 From 7fac90f781738b56d66c6eaec46871cf17f4c72f Mon Sep 17 00:00:00 2001 From: SUZUKI Tetsuya Date: Wed, 8 Dec 2021 16:10:18 +0900 Subject: [PATCH 61/85] =?UTF-8?q?=E3=83=95=E3=82=A9=E3=83=BC=E3=83=9E?= =?UTF-8?q?=E3=83=83=E3=83=88=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sora/sdk/channel/SoraMediaChannel.kt | 100 +++++++++--------- .../sdk/channel/rtc/RTCComponentFactory.kt | 9 +- .../sdk/channel/signaling/SignalingChannel.kt | 58 +++++----- .../sdk/channel/signaling/message/Catalog.kt | 58 +++++----- .../signaling/message/MessageConverter.kt | 63 +++++------ 5 files changed, 146 insertions(+), 142 deletions(-) diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt index 8ecfef13..f9292ba2 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt @@ -60,19 +60,19 @@ import kotlin.concurrent.schedule * @param ignoreDisconnectWebSocket connect メッセージに含める `ignore_disconnect_websocket` */ class SoraMediaChannel @JvmOverloads constructor( - private val context: Context, - private val signalingEndpoint: String? = null, - private val signalingEndpointCandidates: List = emptyList(), - private val channelId: String, - private val signalingMetadata: Any? = "", - private val mediaOption: SoraMediaOption, - private val timeoutSeconds: Long = DEFAULT_TIMEOUT_SECONDS, - private var listener: Listener?, - private val clientId: String? = null, - private val signalingNotifyMetadata: Any? = null, - private val peerConnectionOption: PeerConnectionOption = PeerConnectionOption(), - private val dataChannelSignaling: Boolean? = null, - private var ignoreDisconnectWebSocket: Boolean? = null + private val context: Context, + private val signalingEndpoint: String? = null, + private val signalingEndpointCandidates: List = emptyList(), + private val channelId: String, + private val signalingMetadata: Any? = "", + private val mediaOption: SoraMediaOption, + private val timeoutSeconds: Long = DEFAULT_TIMEOUT_SECONDS, + private var listener: Listener?, + private val clientId: String? = null, + private val signalingNotifyMetadata: Any? = null, + private val peerConnectionOption: PeerConnectionOption = PeerConnectionOption(), + private val dataChannelSignaling: Boolean? = null, + private var ignoreDisconnectWebSocket: Boolean? = null ) { companion object { private val TAG = SoraMediaChannel::class.simpleName @@ -81,8 +81,9 @@ class SoraMediaChannel @JvmOverloads constructor( } init { - if ((signalingEndpoint == null && signalingEndpointCandidates.isEmpty()) - || (signalingEndpoint != null && signalingEndpointCandidates.isNotEmpty())) { + if ((signalingEndpoint == null && signalingEndpointCandidates.isEmpty()) || + (signalingEndpoint != null && signalingEndpointCandidates.isNotEmpty()) + ) { throw IllegalArgumentException("Either signalingEndpoint or signalingEndpointCandidates must be specified") } } @@ -361,7 +362,6 @@ class SoraMediaChannel @JvmOverloads constructor( connectSignalingChannel(clientOffer, location) } } - } private val peerListener = object : PeerChannel.Listener { @@ -589,33 +589,35 @@ class SoraMediaChannel @JvmOverloads constructor( ) clientOfferPeer.run { val subscription = requestClientOfferSdp() - .observeOn(Schedulers.io()) - .subscribeBy( - onSuccess = { - SoraLogger.d(TAG, "[channel:$role] @peer:clientOfferSdp") - disconnect() - - if (it.isFailure) { - SoraLogger.d(TAG, "[channel:$role] failed to create client offer SDP: ${it.exceptionOrNull()?.message}") - } - val handler = Handler(Looper.getMainLooper()) - clientOffer = it.getOrNull() - handler.post() { - connectSignalingChannel(clientOffer) - } - }, - onError = { - SoraLogger.w(TAG, - "[channel:$role] failed request client offer SDP: ${it.message}") - disconnect() - } + .observeOn(Schedulers.io()) + .subscribeBy( + onSuccess = { + SoraLogger.d(TAG, "[channel:$role] @peer:clientOfferSdp") + disconnect() + + if (it.isFailure) { + SoraLogger.d(TAG, "[channel:$role] failed to create client offer SDP: ${it.exceptionOrNull()?.message}") + } + val handler = Handler(Looper.getMainLooper()) + clientOffer = it.getOrNull() + handler.post() { + connectSignalingChannel(clientOffer) + } + }, + onError = { + SoraLogger.w( + TAG, + "[channel:$role] failed request client offer SDP: ${it.message}" + ) + disconnect() + } ) compositeDisposable.add(subscription) } } - private fun connectSignalingChannel(clientOfferSdp : SessionDescription?, redirectLocation: String? = null) { + private fun connectSignalingChannel(clientOfferSdp: SessionDescription?, redirectLocation: String? = null) { val endpoints = when { redirectLocation != null -> listOf(redirectLocation) signalingEndpointCandidates.isNotEmpty() -> signalingEndpointCandidates @@ -623,18 +625,18 @@ class SoraMediaChannel @JvmOverloads constructor( } signaling = SignalingChannelImpl( - endpoints = endpoints, - role = role, - channelId = channelId, - connectDataChannelSignaling = dataChannelSignaling, - connectIgnoreDisconnectWebSocket = ignoreDisconnectWebSocket, - mediaOption = mediaOption, - connectMetadata = signalingMetadata, - listener = signalingListener, - clientOfferSdp = clientOfferSdp, - clientId = clientId, - signalingNotifyMetadata = signalingNotifyMetadata, - redirect = redirectLocation != null + endpoints = endpoints, + role = role, + channelId = channelId, + connectDataChannelSignaling = dataChannelSignaling, + connectIgnoreDisconnectWebSocket = ignoreDisconnectWebSocket, + mediaOption = mediaOption, + connectMetadata = signalingMetadata, + listener = signalingListener, + clientOfferSdp = clientOfferSdp, + clientId = clientId, + signalingNotifyMetadata = signalingNotifyMetadata, + redirect = redirectLocation != null ) signaling!!.connect() } diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/RTCComponentFactory.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/RTCComponentFactory.kt index 084213f5..bacf444b 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/RTCComponentFactory.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/RTCComponentFactory.kt @@ -2,7 +2,6 @@ package jp.shiguredo.sora.sdk.channel.rtc import android.content.Context import jp.shiguredo.sora.sdk.channel.option.SoraMediaOption -import jp.shiguredo.sora.sdk.channel.option.SoraVideoOption import jp.shiguredo.sora.sdk.codec.SimulcastVideoEncoderFactoryWrapper import jp.shiguredo.sora.sdk.error.SoraErrorReason import jp.shiguredo.sora.sdk.util.SoraLogger @@ -53,9 +52,11 @@ class RTCComponentFactory( ) mediaOption.videoDownstreamContext != null -> - DefaultVideoEncoderFactory(mediaOption.videoDownstreamContext, - true /* enableIntelVp8Encoder */, - false /* enableH264HighProfile */) + DefaultVideoEncoderFactory( + mediaOption.videoDownstreamContext, + true /* enableIntelVp8Encoder */, + false /* enableH264HighProfile */ + ) else -> // context が指定されていなければソフトウェアエンコーダーを使用する SoftwareVideoEncoderFactory() diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt index 1f67e99e..d47c7ebf 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt @@ -46,18 +46,18 @@ interface SignalingChannel { } class SignalingChannelImpl @JvmOverloads constructor( - private val endpoints: List, - private val role: SoraChannelRole, - private val channelId: String, - private val connectDataChannelSignaling: Boolean? = null, - private val connectIgnoreDisconnectWebSocket: Boolean? = null, - private val mediaOption: SoraMediaOption, - private val connectMetadata: Any?, - private var listener: SignalingChannel.Listener?, - private val clientOfferSdp: SessionDescription?, - private val clientId: String? = null, - private val signalingNotifyMetadata: Any? = null, - private val redirect: Boolean = false + private val endpoints: List, + private val role: SoraChannelRole, + private val channelId: String, + private val connectDataChannelSignaling: Boolean? = null, + private val connectIgnoreDisconnectWebSocket: Boolean? = null, + private val mediaOption: SoraMediaOption, + private val connectMetadata: Any?, + private var listener: SignalingChannel.Listener?, + private val clientOfferSdp: SessionDescription?, + private val clientId: String? = null, + private val signalingNotifyMetadata: Any? = null, + private val redirect: Boolean = false ) : SignalingChannel { companion object { @@ -89,7 +89,7 @@ class SignalingChannelImpl @JvmOverloads constructor( override fun connect() { SoraLogger.i(TAG, "[signaling:$role] endpoints=$endpoints") - synchronized (this) { + synchronized(this) { for (endpoint in endpoints) { wsCandidates.add(connect(endpoint)) } @@ -197,16 +197,16 @@ class SignalingChannelImpl @JvmOverloads constructor( ws?.let { SoraLogger.d(TAG, "[signaling:$role] -> connect") val message = MessageConverter.buildConnectMessage( - role = role, - channelId = channelId, - dataChannelSignaling = connectDataChannelSignaling, - ignoreDisconnectWebSocket = connectIgnoreDisconnectWebSocket, - mediaOption = mediaOption, - metadata = connectMetadata, - sdp = clientOfferSdp?.description, - clientId = clientId, - signalingNotifyMetadata = signalingNotifyMetadata, - redirect = redirect + role = role, + channelId = channelId, + dataChannelSignaling = connectDataChannelSignaling, + ignoreDisconnectWebSocket = connectIgnoreDisconnectWebSocket, + mediaOption = mediaOption, + metadata = connectMetadata, + sdp = clientOfferSdp?.description, + clientId = clientId, + signalingNotifyMetadata = signalingNotifyMetadata, + redirect = redirect ) it.send(message) } @@ -333,7 +333,7 @@ class SignalingChannelImpl @JvmOverloads constructor( return } - synchronized (this@SignalingChannelImpl) { + synchronized(this@SignalingChannelImpl) { if (ws != null) { return } @@ -376,10 +376,10 @@ class SignalingChannelImpl @JvmOverloads constructor( "ping" -> onPingMessage(json) "update" -> onUpdateMessage(json) "re-offer" -> onReOfferMessage(json) - "notify" -> onNotifyMessage(json) - "push" -> onPushMessage(json) + "notify" -> onNotifyMessage(json) + "push" -> onPushMessage(json) "redirect" -> onRedirectMessage(json) - else -> SoraLogger.i(TAG, "received unknown-type message") + else -> SoraLogger.i(TAG, "received unknown-type message") } } ?: closeWithError("failed to parse 'type' from message") } @@ -395,9 +395,9 @@ class SignalingChannelImpl @JvmOverloads constructor( override fun onClosed(webSocket: WebSocket, code: Int, reason: String) { if (code == 1000) { - SoraLogger.i(TAG, "[signaling:$role] @onClosed: reason = [${reason}], code = $code") + SoraLogger.i(TAG, "[signaling:$role] @onClosed: reason = [$reason], code = $code") } else { - SoraLogger.w(TAG, "[signaling:$role] @onClosed: reason = [${reason}], code = $code") + SoraLogger.w(TAG, "[signaling:$role] @onClosed: reason = [$reason], code = $code") } if (!propagatesWebSocketTerminateEventToSignalingChannel(webSocket)) { diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/message/Catalog.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/message/Catalog.kt index 83dc8b90..865ca813 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/message/Catalog.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/message/Catalog.kt @@ -18,33 +18,33 @@ data class PongMessage( ) data class ConnectMessage( - @SerializedName("type") val type: String = "connect", - @SerializedName("role") var role: String, - @SerializedName("channel_id") val channelId: String, - @SerializedName("client_id") val clientId: String? = null, - @SerializedName("metadata") val metadata: Any? = null, - @SerializedName("signaling_notify_metadata") - val signalingNotifyMetadata: Any? = null, - @SerializedName("multistream") val multistream: Boolean = false, - @SerializedName("spotlight") var spotlight: Any? = null, - @SerializedName("spotlight_number") - var spotlightNumber: Int? = null, - @SerializedName("spotlight_focus_rid") var spotlightFocusRid: String? = null, - @SerializedName("spotlight_unfocus_rid") var spotlightUnfocusRid: String? = null, - @SerializedName("simulcast") var simulcast: Boolean? = false, - @SerializedName("simulcast_rid") - var simulcastRid: String? = null, - @SerializedName("video") var video: Any? = null, - @SerializedName("audio") var audio: Any? = null, - @SerializedName("sora_client") val soraClient: String = SDKInfo.sdkInfo(), - @SerializedName("libwebrtc") val libwebrtc: String = SDKInfo.libwebrtcInfo(), - @SerializedName("environment") val environment: String = SDKInfo.deviceInfo(), - @SerializedName("sdp") val sdp: String? = null, - @SerializedName("data_channel_signaling") - val dataChannelSignaling: Boolean? = null, - @SerializedName("ignore_disconnect_websocket") - val ignoreDisconnectWebsocket: Boolean? = null, - @SerializedName("redirect") var redirect: Boolean? = null + @SerializedName("type") val type: String = "connect", + @SerializedName("role") var role: String, + @SerializedName("channel_id") val channelId: String, + @SerializedName("client_id") val clientId: String? = null, + @SerializedName("metadata") val metadata: Any? = null, + @SerializedName("signaling_notify_metadata") + val signalingNotifyMetadata: Any? = null, + @SerializedName("multistream") val multistream: Boolean = false, + @SerializedName("spotlight") var spotlight: Any? = null, + @SerializedName("spotlight_number") + var spotlightNumber: Int? = null, + @SerializedName("spotlight_focus_rid") var spotlightFocusRid: String? = null, + @SerializedName("spotlight_unfocus_rid") var spotlightUnfocusRid: String? = null, + @SerializedName("simulcast") var simulcast: Boolean? = false, + @SerializedName("simulcast_rid") + var simulcastRid: String? = null, + @SerializedName("video") var video: Any? = null, + @SerializedName("audio") var audio: Any? = null, + @SerializedName("sora_client") val soraClient: String = SDKInfo.sdkInfo(), + @SerializedName("libwebrtc") val libwebrtc: String = SDKInfo.libwebrtcInfo(), + @SerializedName("environment") val environment: String = SDKInfo.deviceInfo(), + @SerializedName("sdp") val sdp: String? = null, + @SerializedName("data_channel_signaling") + val dataChannelSignaling: Boolean? = null, + @SerializedName("ignore_disconnect_websocket") + val ignoreDisconnectWebsocket: Boolean? = null, + @SerializedName("redirect") var redirect: Boolean? = null ) data class VideoSetting( @@ -89,8 +89,8 @@ data class Encoding( ) data class RedirectMessage( - @SerializedName("type") val type: String = "redirect", - @SerializedName("location") val location: String + @SerializedName("type") val type: String = "redirect", + @SerializedName("location") val location: String ) data class OfferMessage( diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/message/MessageConverter.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/message/MessageConverter.kt index ff9f97d2..fe5e39d4 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/message/MessageConverter.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/message/MessageConverter.kt @@ -27,40 +27,41 @@ class MessageConverter { val gson = Gson() @JvmOverloads - fun buildConnectMessage(role: SoraChannelRole, - channelId: String, - dataChannelSignaling: Boolean?, - ignoreDisconnectWebSocket: Boolean?, - mediaOption: SoraMediaOption, - metadata: Any?, - sdp: String? = null, - clientId: String? = null, - signalingNotifyMetadata: Any? = null, - redirect: Boolean = false + fun buildConnectMessage( + role: SoraChannelRole, + channelId: String, + dataChannelSignaling: Boolean?, + ignoreDisconnectWebSocket: Boolean?, + mediaOption: SoraMediaOption, + metadata: Any?, + sdp: String? = null, + clientId: String? = null, + signalingNotifyMetadata: Any? = null, + redirect: Boolean = false ): String { val msg = ConnectMessage( - role = role.signaling, - channelId = channelId, - dataChannelSignaling = dataChannelSignaling, - ignoreDisconnectWebsocket = ignoreDisconnectWebSocket, - metadata = metadata, - multistream = mediaOption.multistreamIsRequired, - spotlight = mediaOption.spotlightOption?.let { - if (Sora.usesSpotlightLegacy) - it.spotlightNumber - else - true - }, - spotlightNumber = mediaOption.spotlightOption?.let { - if (Sora.usesSpotlightLegacy) - null - else - it.spotlightNumber - }, - sdp = sdp, - clientId = clientId, - signalingNotifyMetadata = signalingNotifyMetadata, + role = role.signaling, + channelId = channelId, + dataChannelSignaling = dataChannelSignaling, + ignoreDisconnectWebsocket = ignoreDisconnectWebSocket, + metadata = metadata, + multistream = mediaOption.multistreamIsRequired, + spotlight = mediaOption.spotlightOption?.let { + if (Sora.usesSpotlightLegacy) + it.spotlightNumber + else + true + }, + spotlightNumber = mediaOption.spotlightOption?.let { + if (Sora.usesSpotlightLegacy) + null + else + it.spotlightNumber + }, + sdp = sdp, + clientId = clientId, + signalingNotifyMetadata = signalingNotifyMetadata, ) if (mediaOption.upstreamIsRequired) { From 29e31e404c2f9d0bf57f67dabb17bcb7cc02d948 Mon Sep 17 00:00:00 2001 From: SUZUKI Tetsuya Date: Wed, 8 Dec 2021 17:02:30 +0900 Subject: [PATCH 62/85] =?UTF-8?q?=E5=BC=95=E6=95=B0=E3=81=AE=E6=95=B0?= =?UTF-8?q?=E3=82=92=E4=BF=AE=E6=AD=A3=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sora/sdk/SoraMediaChannelTest.java | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/sora-android-sdk/src/test/java/jp/shiguredo/sora/sdk/SoraMediaChannelTest.java b/sora-android-sdk/src/test/java/jp/shiguredo/sora/sdk/SoraMediaChannelTest.java index ca0a6e40..fcc1472d 100644 --- a/sora-android-sdk/src/test/java/jp/shiguredo/sora/sdk/SoraMediaChannelTest.java +++ b/sora-android-sdk/src/test/java/jp/shiguredo/sora/sdk/SoraMediaChannelTest.java @@ -7,10 +7,14 @@ import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.Map; import androidx.test.core.app.ApplicationProvider; + import jp.shiguredo.sora.sdk.channel.SoraMediaChannel; import jp.shiguredo.sora.sdk.channel.option.SoraMediaOption; @@ -41,10 +45,10 @@ public class SoraMediaChannelTest { public void constructorCallUntil180() { try { // connectMetadata が null のパターン - new SoraMediaChannel(context, signalingEndpoint, channelId, + new SoraMediaChannel(context, signalingEndpoint, Collections.emptyList(), channelId, null, mediaOption, timeoutSeconds, listener); // connectMetadata が String のパターン - new SoraMediaChannel(context, signalingEndpoint, channelId, + new SoraMediaChannel(context, signalingEndpoint, Collections.emptyList(), channelId, connectMetadataString, mediaOption, timeoutSeconds, listener); } catch (Exception e) { fail(e.toString()); @@ -56,7 +60,7 @@ public void constructorCallUntil180() { @Test public void constructorCallFrom181WithoutTimeoutSeconds() { try { - new SoraMediaChannel(context, signalingEndpoint, channelId, + new SoraMediaChannel(context, signalingEndpoint, new ArrayList(Arrays.asList(channelId)), connectMetadataString, mediaOption, listener); } catch (Exception e) { fail(e.toString()); @@ -69,10 +73,10 @@ public void constructorCallFrom181WithoutTimeoutSeconds() { public void constructorCallFrom181WithMetadataMap() { try { // timeoutSeconds あり - new SoraMediaChannel(context, signalingEndpoint, channelId, + new SoraMediaChannel(context, signalingEndpoint, Collections.emptyList(), channelId, connectMetadataMap, mediaOption, timeoutSeconds, listener); // timeoutSeconds なし - new SoraMediaChannel(context, signalingEndpoint, channelId, + new SoraMediaChannel(context, signalingEndpoint, Collections.emptyList(), channelId, connectMetadataMap, mediaOption, listener); } catch (Exception e) { fail(e.toString()); @@ -86,11 +90,11 @@ public void constructorCallFrom181WithMetadataMap() { public void constructorCallFrom181WithClientId() { try { // 文字列 - new SoraMediaChannel(context, signalingEndpoint, channelId, + new SoraMediaChannel(context, signalingEndpoint, Collections.emptyList(), channelId, connectMetadataMap, mediaOption, SoraMediaChannel.DEFAULT_TIMEOUT_SECONDS, listener, clientId); // マップ - new SoraMediaChannel(context, signalingEndpoint, channelId, + new SoraMediaChannel(context, signalingEndpoint, Collections.emptyList(), channelId, connectMetadataMap, mediaOption, SoraMediaChannel.DEFAULT_TIMEOUT_SECONDS, listener, clientId); } catch (Exception e) { @@ -107,11 +111,11 @@ public void constructorCallFrom181WithClientId() { public void constructorCallFrom181WithSignalingNotifyMetadata() { try { // 文字列 - new SoraMediaChannel(context, signalingEndpoint, channelId, + new SoraMediaChannel(context, signalingEndpoint, Collections.emptyList(), channelId, connectMetadataMap, mediaOption, SoraMediaChannel.DEFAULT_TIMEOUT_SECONDS, listener, clientId, signalingNotifyMetadataString); // マップ - new SoraMediaChannel(context, signalingEndpoint, channelId, + new SoraMediaChannel(context, signalingEndpoint, Collections.emptyList(), channelId, connectMetadataMap, mediaOption, SoraMediaChannel.DEFAULT_TIMEOUT_SECONDS, listener, /* clientId */ null, signalingNotifyMetadataMap); } catch (Exception e) { From 529e0af790f69dee798ffb11e950305eff80dd4d Mon Sep 17 00:00:00 2001 From: enm10k Date: Thu, 9 Dec 2021 11:37:42 +0900 Subject: [PATCH 63/85] =?UTF-8?q?libwebrtc=20=E3=82=92=20M96=20=E3=81=AB?= =?UTF-8?q?=E4=B8=8A=E3=81=92=E3=82=8B=20&=20VP8=20=E3=81=AE=E3=82=B5?= =?UTF-8?q?=E3=82=A4=E3=83=9E=E3=83=AB=E3=82=AD=E3=83=A3=E3=82=B9=E3=83=88?= =?UTF-8?q?=E3=81=8C=E5=8B=95=E3=81=84=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 +- .../SimulcastVideoEncoderFactoryWrapper.kt | 44 +------------------ 2 files changed, 3 insertions(+), 43 deletions(-) diff --git a/build.gradle b/build.gradle index 33578c53..958ef21f 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ apply plugin: 'org.jetbrains.dokka' buildscript { ext.kotlin_version = '1.4.31' - ext.libwebrtc_version = '93.4577.8.2' + ext.libwebrtc_version = '96.4664.2.0' ext.dokka_version = '1.5.31' diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/codec/SimulcastVideoEncoderFactoryWrapper.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/codec/SimulcastVideoEncoderFactoryWrapper.kt index dc029de7..021891dc 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/codec/SimulcastVideoEncoderFactoryWrapper.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/codec/SimulcastVideoEncoderFactoryWrapper.kt @@ -9,7 +9,6 @@ import org.webrtc.VideoCodecInfo import org.webrtc.VideoCodecStatus import org.webrtc.VideoEncoder import org.webrtc.VideoEncoderFactory -import org.webrtc.VideoEncoderFallback import org.webrtc.VideoFrame import java.util.concurrent.Callable import java.util.concurrent.ExecutorService @@ -21,45 +20,6 @@ internal class SimulcastVideoEncoderFactoryWrapper( enableH264HighProfile: Boolean ) : VideoEncoderFactory { - /* - * ソフトウェアエンコーダーの利用を優先するファクトリーです。 - * 指定されたコーデックにソフトウェアエンコーダーが対応していない場合にハードウェアエンコーダーを利用します。 - * ただし、このクラスは libwebrtc の問題を回避するためのワークアラウンドで実質的に用途はありません。 - * - * libwebrtc でサイマルキャストを扱うには SimulcastEncoderAdapter を利用します。 - * SimulcastEncoderAdapter はプライマリのエンコーダーとフォールバックのエンコーダー - * (指定されたコーデックにプライマリのエンコーダーが対応していない場合に利用されます) - * を持ちます。プライマリに HardwareVideoEncoderFactory 、 - * フォールバックに SoftwareVideoEncoderFactory を指定すると、 - * H.264 の使用時に libwebrtc がクラッシュします。 - * これは SoftwareVideoEncoderFactory が H.264 に非対応であり、 - * createEncoder() が null を返すのに対して libwebrtc 側が null 時の処理に対応していないためです。 - * createEncoder() はフォールバックの利用の有無に関わらず実行されるので、 - * null を回避するために HardwareVideoEncoderFactory に処理を委譲しています。 - * プライマリ・フォールバックの両方で HardwareVideoEncoderFactory を使うことになりますが、特に問題ありません。 - */ - private class Fallback(private val hardwareVideoEncoderFactory: VideoEncoderFactory) : VideoEncoderFactory { - - private val softwareVideoEncoderFactory: VideoEncoderFactory = SoftwareVideoEncoderFactory() - - override fun createEncoder(info: VideoCodecInfo): VideoEncoder? { - val softwareEncoder = softwareVideoEncoderFactory.createEncoder(info) - val hardwareEncoder = hardwareVideoEncoderFactory.createEncoder(info) - return if (hardwareEncoder != null && softwareEncoder != null) { - VideoEncoderFallback(hardwareEncoder, softwareEncoder) - } else { - softwareEncoder ?: hardwareEncoder - } - } - - override fun getSupportedCodecs(): Array { - val supportedCodecInfos: MutableList = mutableListOf() - supportedCodecInfos.addAll(softwareVideoEncoderFactory.supportedCodecs) - supportedCodecInfos.addAll(hardwareVideoEncoderFactory.supportedCodecs) - return supportedCodecInfos.toTypedArray() - } - } - // ストリーム単位のエンコーダをラップした上で以下を行うクラス。 // - スレッドをひとつ起動する // - initEncode の width/height と frame buffer のそれが一致しない場合は事前にスケールする @@ -165,7 +125,7 @@ internal class SimulcastVideoEncoderFactoryWrapper( } private val primary: VideoEncoderFactory - private val fallback: VideoEncoderFactory + private val fallback: VideoEncoderFactory? private val native: SimulcastVideoEncoderFactory init { @@ -173,7 +133,7 @@ internal class SimulcastVideoEncoderFactoryWrapper( sharedContext, enableIntelVp8Encoder, enableH264HighProfile ) primary = StreamEncoderWrapperFactory(hardwareVideoEncoderFactory) - fallback = StreamEncoderWrapperFactory(Fallback(primary)) + fallback = SoftwareVideoEncoderFactory() native = SimulcastVideoEncoderFactory(primary, fallback) } From fe1dc038f6f5d24c8dc18e1831301aa9900d36bb Mon Sep 17 00:00:00 2001 From: enm10k Date: Thu, 9 Dec 2021 15:42:52 +0900 Subject: [PATCH 64/85] =?UTF-8?q?StreamEncoderWrapper=20=E3=81=AE=E5=88=9D?= =?UTF-8?q?=E6=9C=9F=E5=8C=96=E6=99=82=E3=81=AB=E3=82=A8=E3=83=B3=E3=82=B3?= =?UTF-8?q?=E3=83=BC=E3=83=80=E3=83=BC=E5=90=8D=E3=82=92=E5=87=BA=E5=8A=9B?= =?UTF-8?q?=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sora/sdk/codec/SimulcastVideoEncoderFactoryWrapper.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/codec/SimulcastVideoEncoderFactoryWrapper.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/codec/SimulcastVideoEncoderFactoryWrapper.kt index 021891dc..187b9f9b 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/codec/SimulcastVideoEncoderFactoryWrapper.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/codec/SimulcastVideoEncoderFactoryWrapper.kt @@ -41,6 +41,7 @@ internal class SimulcastVideoEncoderFactoryWrapper( SoraLogger.i( TAG, """initEncode() thread=${Thread.currentThread().name} [${Thread.currentThread().id}] + | encoder=${encoder.implementationName} | streamSettings: | numberOfCores=${settings.numberOfCores} | width=${settings.width} From ad5452b7bed9e63720441c982fe82e492ee2f1f1 Mon Sep 17 00:00:00 2001 From: enm10k Date: Thu, 9 Dec 2021 15:53:36 +0900 Subject: [PATCH 65/85] =?UTF-8?q?H.264=20=E5=88=A9=E7=94=A8=E6=99=82?= =?UTF-8?q?=E3=81=AF=20SimulcastVideoEncoderFactoryWrapper=20=E3=81=AE=20f?= =?UTF-8?q?allback=20=E3=82=92=20null=20=E3=81=AB=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sora/sdk/channel/rtc/RTCComponentFactory.kt | 3 ++- .../codec/SimulcastVideoEncoderFactoryWrapper.kt | 13 +++++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/RTCComponentFactory.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/RTCComponentFactory.kt index bacf444b..2ba24e15 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/RTCComponentFactory.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/RTCComponentFactory.kt @@ -42,7 +42,8 @@ class RTCComponentFactory( SimulcastVideoEncoderFactoryWrapper( mediaOption.videoUpstreamContext, true, - false + false, + mediaOption.videoCodec, ) mediaOption.videoUpstreamContext != null -> DefaultVideoEncoderFactory( diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/codec/SimulcastVideoEncoderFactoryWrapper.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/codec/SimulcastVideoEncoderFactoryWrapper.kt index 187b9f9b..0ec2f5c8 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/codec/SimulcastVideoEncoderFactoryWrapper.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/codec/SimulcastVideoEncoderFactoryWrapper.kt @@ -1,5 +1,6 @@ package jp.shiguredo.sora.sdk.codec +import jp.shiguredo.sora.sdk.channel.option.SoraVideoOption import jp.shiguredo.sora.sdk.util.SoraLogger import org.webrtc.EglBase import org.webrtc.HardwareVideoEncoderFactory @@ -17,7 +18,8 @@ import java.util.concurrent.Executors internal class SimulcastVideoEncoderFactoryWrapper( sharedContext: EglBase.Context?, enableIntelVp8Encoder: Boolean, - enableH264HighProfile: Boolean + enableH264HighProfile: Boolean, + videoCodec: SoraVideoOption.Codec ) : VideoEncoderFactory { // ストリーム単位のエンコーダをラップした上で以下を行うクラス。 @@ -134,7 +136,14 @@ internal class SimulcastVideoEncoderFactoryWrapper( sharedContext, enableIntelVp8Encoder, enableH264HighProfile ) primary = StreamEncoderWrapperFactory(hardwareVideoEncoderFactory) - fallback = SoftwareVideoEncoderFactory() + + // H.264 のサイマルキャストを利用する場合は fallback に null を設定する + // Sora Android SDK では SW の H.264 を無効化しているため fallback に設定できるものがない + fallback = if (videoCodec != SoraVideoOption.Codec.H264) { + SoftwareVideoEncoderFactory() + } else { + null + } native = SimulcastVideoEncoderFactory(primary, fallback) } From dbfe0bec05ad7f246d69687776fa89fe3824e793 Mon Sep 17 00:00:00 2001 From: enm10k Date: Sat, 11 Dec 2021 20:32:51 +0900 Subject: [PATCH 66/85] =?UTF-8?q?libwebrtc=20=E3=82=92=2096.4664.2.1=20?= =?UTF-8?q?=E3=81=AB=E6=9B=B4=E6=96=B0=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGES.md | 2 ++ build.gradle | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 0d01c028..a3985485 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -11,6 +11,8 @@ ## develop +- [UPDATE] libwebrtc を 96.4664.2.1 に上げる + - @enm10k - [UPDATE] 複数シグナリング URL の指定に対応する - @enm10k - [UPDATE] redirect メッセージに対応する diff --git a/build.gradle b/build.gradle index 958ef21f..b05312c5 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ apply plugin: 'org.jetbrains.dokka' buildscript { ext.kotlin_version = '1.4.31' - ext.libwebrtc_version = '96.4664.2.0' + ext.libwebrtc_version = '96.4664.2.1' ext.dokka_version = '1.5.31' From 7ac068920a0b1574633254b4ec723b30661dc6ed Mon Sep 17 00:00:00 2001 From: enm10k Date: Wed, 15 Dec 2021 11:10:15 +0900 Subject: [PATCH 67/85] =?UTF-8?q?CHANGES.md=20=E3=82=92=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGES.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index a3985485..08bb1daa 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -23,6 +23,11 @@ - SoraMediaOption.videoUpstreamContext が無く SoraMediaOption.videoDownstreamContext がある場合はコーデック指定に依らず、 DefaultVideoEncoderFactory を使用する - @miosakuma +- [FIX] libwebrtc の更新で発生するようになったサイマルキャストのクラッシュを修正する + - SimulcastVideoEncoderFactoryWrapper.kt の Fallback クラスが原因で java.lang.UnsupportedOperationException が発生していた + - 調査の結果、 Fallback クラスを削除できることがわかったので、その方向で修正した + - その過程で、 libwebrtc に適用している Android のサイマルキャスト対応のパッチを更新し、 SimulcastVideoEncoderFactory の fallback に null を指定できるようにした + - @enm10k ## 2021.3 From 8bafc3260d4b92e511eaedc89b44fa0eb49612fc Mon Sep 17 00:00:00 2001 From: enm10k Date: Thu, 9 Dec 2021 10:46:03 +0900 Subject: [PATCH 68/85] =?UTF-8?q?=E3=82=B9=E3=83=9D=E3=83=83=E3=83=88?= =?UTF-8?q?=E3=83=A9=E3=82=A4=E3=83=88=E3=83=AC=E3=82=AC=E3=82=B7=E3=83=BC?= =?UTF-8?q?=E3=82=92=E5=89=8A=E9=99=A4=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGES.md | 2 ++ .../sora/sdk/channel/option/SoraMediaOption.kt | 8 +------- .../signaling/message/MessageConverter.kt | 17 +++-------------- 3 files changed, 6 insertions(+), 21 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 08bb1daa..3e9d743c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -19,6 +19,8 @@ - @enm10k - [UPDATE] dokka を 1.5.31 に上げる - @miosakuma +- [CHANGE] スポットライトレガシーを削除する + - @enm10k - [FIX] 視聴のみかつ H.264 した場合に接続できない問題についてのワークアラウンドを削除する - SoraMediaOption.videoUpstreamContext が無く SoraMediaOption.videoDownstreamContext がある場合はコーデック指定に依らず、 DefaultVideoEncoderFactory を使用する diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraMediaOption.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraMediaOption.kt index fca40d33..3c3dbc95 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraMediaOption.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraMediaOption.kt @@ -1,6 +1,5 @@ package jp.shiguredo.sora.sdk.channel.option -import jp.shiguredo.sora.sdk.Sora import org.webrtc.EglBase import org.webrtc.PeerConnection import org.webrtc.VideoCapturer @@ -96,16 +95,11 @@ class SoraMediaOption { * スポットライト機能はサイマルキャスト機能を利用します. * スポットライト機能を有効にすると、マルチストリームとサイマルキャスト機能も有効になります. * - * サーバがスポットライトレガシー機能を利用している場合は、 - * [Sora.usesSpotlightLegacy] に `true` をセットしてください. */ fun enableSpotlight(option: SoraSpotlightOption) { spotlightOption = option multistreamEnabled = true - - if (!Sora.usesSpotlightLegacy) { - enableSimulcast() - } + enableSimulcast() } /** diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/message/MessageConverter.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/message/MessageConverter.kt index fe5e39d4..9023e7c9 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/message/MessageConverter.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/message/MessageConverter.kt @@ -1,7 +1,6 @@ package jp.shiguredo.sora.sdk.channel.signaling.message import com.google.gson.Gson -import jp.shiguredo.sora.sdk.Sora import jp.shiguredo.sora.sdk.channel.option.SoraChannelRole import jp.shiguredo.sora.sdk.channel.option.SoraMediaOption import jp.shiguredo.sora.sdk.util.SoraLogger @@ -47,18 +46,6 @@ class MessageConverter { ignoreDisconnectWebsocket = ignoreDisconnectWebSocket, metadata = metadata, multistream = mediaOption.multistreamIsRequired, - spotlight = mediaOption.spotlightOption?.let { - if (Sora.usesSpotlightLegacy) - it.spotlightNumber - else - true - }, - spotlightNumber = mediaOption.spotlightOption?.let { - if (Sora.usesSpotlightLegacy) - null - else - it.spotlightNumber - }, sdp = sdp, clientId = clientId, signalingNotifyMetadata = signalingNotifyMetadata, @@ -110,7 +97,9 @@ class MessageConverter { msg.simulcastRid = mediaOption.simulcastRid?.toString() } - if (mediaOption.spotlightOption != null && !Sora.usesSpotlightLegacy) { + if (mediaOption.spotlightOption != null) { + msg.spotlight = true + msg.spotlightNumber = mediaOption.spotlightOption?.spotlightNumber msg.spotlightFocusRid = mediaOption.spotlightOption?.spotlightFocusRid?.toString() msg.spotlightUnfocusRid = mediaOption.spotlightOption?.spotlightUnfocusRid?.toString() } From 130c8b4765e40096b83ddfd67541f65a49c34f01 Mon Sep 17 00:00:00 2001 From: enm10k Date: Sat, 11 Dec 2021 21:27:27 +0900 Subject: [PATCH 69/85] =?UTF-8?q?=E3=82=B3=E3=83=A1=E3=83=B3=E3=83=88?= =?UTF-8?q?=E3=81=A8=20CHANGES.md=20=E3=81=8B=E3=82=89=E4=B8=8D=E8=A6=81?= =?UTF-8?q?=E3=81=AA=E7=A9=BA=E8=A1=8C=E3=82=92=E5=89=8A=E9=99=A4=E3=81=99?= =?UTF-8?q?=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jp/shiguredo/sora/sdk/channel/option/SoraMediaOption.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraMediaOption.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraMediaOption.kt index 3c3dbc95..82e11e0d 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraMediaOption.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/option/SoraMediaOption.kt @@ -94,7 +94,6 @@ class SoraMediaOption { * * スポットライト機能はサイマルキャスト機能を利用します. * スポットライト機能を有効にすると、マルチストリームとサイマルキャスト機能も有効になります. - * */ fun enableSpotlight(option: SoraSpotlightOption) { spotlightOption = option From f300a3c20feda9e0ed12442744c9dfe1bc8e3ac8 Mon Sep 17 00:00:00 2001 From: enm10k Date: Sat, 11 Dec 2021 21:33:03 +0900 Subject: [PATCH 70/85] =?UTF-8?q?=E4=B8=8D=E8=A6=81=E3=81=AB=E3=81=AA?= =?UTF-8?q?=E3=81=A3=E3=81=9F=20Sora.kt=20=E3=82=92=E5=89=8A=E9=99=A4?= =?UTF-8?q?=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/kotlin/jp/shiguredo/sora/sdk/Sora.kt | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/Sora.kt diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/Sora.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/Sora.kt deleted file mode 100644 index f4a81440..00000000 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/Sora.kt +++ /dev/null @@ -1,14 +0,0 @@ -package jp.shiguredo.sora.sdk - -object Sora { - - /** - * スポットライトレガシー機能の使用の可否を指定します. - * - * true をセットすると、シグナリングメッセージの内容がスポットライトレガシー向けに変更されます. - * ただし、サーバー側でスポットライトレガシー機能が有効にされていなければシグナリングに失敗します. - * このプロパティを使用する際は必ずサーバーの設定を確認してください. - */ - @Deprecated("スポットライトレガシー機能は 2021 年 12 月に廃止が予定されています。") - var usesSpotlightLegacy: Boolean = false -} From 0614b23251259be34157e30e06a7beafaa1bb25a Mon Sep 17 00:00:00 2001 From: enm10k Date: Wed, 15 Dec 2021 17:03:44 +0900 Subject: [PATCH 71/85] =?UTF-8?q?SoraMediaChannel=20=E3=81=AB=20connectedS?= =?UTF-8?q?ignalingEndpoint=20=E3=82=92=E8=BF=BD=E5=8A=A0=E3=81=99?= =?UTF-8?q?=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGES.md | 1 + .../jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt | 11 ++++++++++- .../sora/sdk/channel/signaling/SignalingChannel.kt | 4 ++-- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 3e9d743c..5450db6f 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -14,6 +14,7 @@ - [UPDATE] libwebrtc を 96.4664.2.1 に上げる - @enm10k - [UPDATE] 複数シグナリング URL の指定に対応する + - SoraMediaChannel に connectedSignalingEndpoint を追加する - @enm10k - [UPDATE] redirect メッセージに対応する - @enm10k diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt index f9292ba2..0d74410c 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt @@ -275,6 +275,12 @@ class SoraMediaChannel @JvmOverloads constructor( var connectionId: String? = null private set + /** + * シグナリングに利用しているエンドポイント. + */ + var connectedSignalingEndpoint: String? = null + private set + private val compositeDisposable = ReusableCompositeDisposable() private val signalingListener = object : SignalingChannel.Listener { @@ -295,8 +301,9 @@ class SoraMediaChannel @JvmOverloads constructor( } } - override fun onConnect() { + override fun onConnect(connectedEndpoint: String) { SoraLogger.d(TAG, "[channel:$role] @signaling:onOpen") + connectedSignalingEndpoint = connectedEndpoint } override fun onInitialOffer(offerMessage: OfferMessage) { @@ -785,6 +792,8 @@ class SoraMediaChannel @JvmOverloads constructor( return closing = true + connectedSignalingEndpoint = null + stopTimer() sendDisconnectMessage() compositeDisposable.dispose() diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt index d47c7ebf..404a6106 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt @@ -31,7 +31,7 @@ interface SignalingChannel { fun disconnect() interface Listener { - fun onConnect() + fun onConnect(connectedEndpoint: String) fun onDisconnect() fun onInitialOffer(offerMessage: OfferMessage) fun onSwitched(switchedMessage: SwitchedMessage) @@ -350,7 +350,7 @@ class SignalingChannelImpl @JvmOverloads constructor( wsCandidates.clear() } - listener?.onConnect() + listener?.onConnect(webSocket.request().url.toString()) sendConnectMessage() } catch (e: Exception) { SoraLogger.w(TAG, e.toString()) From 30d9603b6f682913c80621ae6d8ca0524df10dc0 Mon Sep 17 00:00:00 2001 From: miosakuma Date: Tue, 21 Dec 2021 12:05:55 +0900 Subject: [PATCH 72/85] =?UTF-8?q?Sora=20=E3=81=AE=E3=83=90=E3=83=BC?= =?UTF-8?q?=E3=82=B8=E3=83=A7=E3=83=B3=E3=82=92=202021.2=20=E3=81=AB?= =?UTF-8?q?=E5=A4=89=E6=9B=B4=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f0bc778a..5cebc116 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ Please read https://github.com/shiguredo/oss before use. - Android 5 以降 (エミュレーターでの動作は保証しません) - Android Studio 4.2 以降 -- WebRTC SFU Sora 2021.1 以降 +- WebRTC SFU Sora 2021.2 以降 ## サンプル From d025fce056ac44b82bd12623c05eba6cf3c2cb52 Mon Sep 17 00:00:00 2001 From: enm10k Date: Wed, 22 Dec 2021 10:54:20 +0900 Subject: [PATCH 73/85] =?UTF-8?q?connectedSignalingEndpoin=20=E3=82=92=20n?= =?UTF-8?q?ull=20=E3=81=AB=E3=81=99=E3=82=8B=E3=82=BF=E3=82=A4=E3=83=9F?= =?UTF-8?q?=E3=83=B3=E3=82=B0=E3=82=92=E4=BF=AE=E6=AD=A3=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt index 0d74410c..c004ddda 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt @@ -792,8 +792,6 @@ class SoraMediaChannel @JvmOverloads constructor( return closing = true - connectedSignalingEndpoint = null - stopTimer() sendDisconnectMessage() compositeDisposable.dispose() @@ -801,6 +799,9 @@ class SoraMediaChannel @JvmOverloads constructor( listener?.onClose(this) listener = null + // アプリケーションで定義された切断処理を実行した後に connectedSignalingEndpoint を null にする + connectedSignalingEndpoint = null + signaling?.disconnect() signaling = null From b076d612eaf78cd655b74e20692db4a6e22f8089 Mon Sep 17 00:00:00 2001 From: enm10k Date: Sun, 12 Dec 2021 21:52:47 +0900 Subject: [PATCH 74/85] =?UTF-8?q?type:=20disconnect=20=E3=81=AB=20reason?= =?UTF-8?q?=20=E3=82=92=E8=BF=BD=E5=8A=A0=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sora/sdk/channel/SoraMediaChannel.kt | 144 ++++++++++++++---- .../sora/sdk/channel/rtc/PeerChannel.kt | 30 ++-- .../sdk/channel/signaling/SignalingChannel.kt | 26 ++-- .../sdk/channel/signaling/message/Catalog.kt | 3 +- .../signaling/message/MessageConverter.kt | 5 +- 5 files changed, 149 insertions(+), 59 deletions(-) diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt index c004ddda..8f82713d 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt @@ -5,7 +5,6 @@ import android.os.Handler import android.os.Looper import io.reactivex.rxkotlin.subscribeBy import io.reactivex.schedulers.Schedulers -import jp.shiguredo.sora.sdk.channel.SoraMediaChannel.Listener import jp.shiguredo.sora.sdk.channel.data.ChannelAttendeesCount import jp.shiguredo.sora.sdk.channel.option.PeerConnectionOption import jp.shiguredo.sora.sdk.channel.option.SoraMediaOption @@ -26,6 +25,7 @@ import jp.shiguredo.sora.sdk.util.SoraLogger import org.webrtc.DataChannel import org.webrtc.IceCandidate import org.webrtc.MediaStream +import org.webrtc.PeerConnection import org.webrtc.RTCStatsCollectorCallback import org.webrtc.RTCStatsReport import org.webrtc.RtpParameters @@ -71,8 +71,8 @@ class SoraMediaChannel @JvmOverloads constructor( private val clientId: String? = null, private val signalingNotifyMetadata: Any? = null, private val peerConnectionOption: PeerConnectionOption = PeerConnectionOption(), - private val dataChannelSignaling: Boolean? = null, - private var ignoreDisconnectWebSocket: Boolean? = null + dataChannelSignaling: Boolean? = null, + ignoreDisconnectWebSocket: Boolean? = null ) { companion object { private val TAG = SoraMediaChannel::class.simpleName @@ -80,12 +80,30 @@ class SoraMediaChannel @JvmOverloads constructor( const val DEFAULT_TIMEOUT_SECONDS = 10L } + // connect メッセージに含める `data_channel_signaling` + private val connectDataChannelSignaling: Boolean? + + // connect メッセージに含める `ignore_disconnect_websocket` + private val connectIgnoreDisconnectWebSocket: Boolean? + + // DataChannel 経由のシグナリングが有効なら true + // Sora から渡された値 (= offer メッセージ) を参照して更新している + private var dataChannelSignaling: Boolean = false + + // DataChannel 経由のシグナリング利用時に WebSocket の切断を無視するなら true + // 同じく switched メッセージを参照して更新している + private var ignoreDisconnectWebSocket: Boolean = false + init { if ((signalingEndpoint == null && signalingEndpointCandidates.isEmpty()) || (signalingEndpoint != null && signalingEndpointCandidates.isNotEmpty()) ) { throw IllegalArgumentException("Either signalingEndpoint or signalingEndpointCandidates must be specified") } + + connectDataChannelSignaling = dataChannelSignaling + connectIgnoreDisconnectWebSocket = ignoreDisconnectWebSocket + SoraLogger.d(TAG, "connectDataChannelSignaling=$connectDataChannelSignaling, connectIgnoreDisconnectWebSocket=$connectIgnoreDisconnectWebSocket") } /** @@ -285,7 +303,7 @@ class SoraMediaChannel @JvmOverloads constructor( private val signalingListener = object : SignalingChannel.Listener { - override fun onDisconnect() { + override fun onDisconnect(reason: DisconnectReason?) { SoraLogger.d( TAG, "[channel:$role] @signaling:onDisconnect " + @@ -297,7 +315,7 @@ class SoraMediaChannel @JvmOverloads constructor( // なにもしない SoraLogger.d(TAG, "[channel:$role] @signaling:onDisconnect: IGNORE") } else { - disconnect() + internalDisconnect(reason) } } @@ -314,7 +332,7 @@ class SoraMediaChannel @JvmOverloads constructor( override fun onSwitched(switchedMessage: SwitchedMessage) { SoraLogger.d(TAG, "[channel:$role] @signaling:onSwitched") - ignoreDisconnectWebSocket = switchedMessage.ignoreDisconnectWebsocket + this@SoraMediaChannel.ignoreDisconnectWebSocket = switchedMessage.ignoreDisconnectWebsocket ?: false handleSwitched(switchedMessage) } @@ -361,7 +379,7 @@ class SoraMediaChannel @JvmOverloads constructor( SoraLogger.d(TAG, "[channel:$role] @peer:onRedirect") SoraLogger.i(TAG, "[channel:$role] closing old SignalingChannel") - signaling?.disconnect() + signaling?.disconnect(null) SoraLogger.i(TAG, "[channel:$role] opening new SignalingChannel") val handler = Handler(Looper.getMainLooper()) @@ -456,7 +474,9 @@ class SoraMediaChannel @JvmOverloads constructor( override fun onDataChannelClosed(label: String, dataChannel: DataChannel) { SoraLogger.d(TAG, "[channel:$role] @peer:onDataChannelClosed label=$label") - disconnect() + + // DataChannel が閉じられたが、その理由を知る方法がないため reason は null にする + internalDisconnect(null) } override fun onSenderEncodings(encodings: List) { @@ -484,9 +504,10 @@ class SoraMediaChannel @JvmOverloads constructor( listener?.onWarning(this@SoraMediaChannel, reason, message) } - override fun onDisconnect() { - SoraLogger.d(TAG, "[channel:$role] @peer:onClose") - disconnect() + override fun onDisconnect(disconnectReason: DisconnectReason?) { + SoraLogger.d(TAG, "[channel:$role] @peer:onClose:$disconnectReason") + + internalDisconnect(disconnectReason) } } @@ -574,7 +595,10 @@ class SoraMediaChannel @JvmOverloads constructor( private fun onTimeout() { SoraLogger.d(TAG, "[channel:$role] @peer:onTimeout") listener?.onError(this, SoraErrorReason.TIMEOUT) - disconnect() + + // ここに来た場合、 Sora に接続出来ていない = disconnect メッセージを送信する必要がない + // そのため、 reason は null で良い + internalDisconnect(null) } private fun requestClientOfferSdp() { @@ -600,7 +624,7 @@ class SoraMediaChannel @JvmOverloads constructor( .subscribeBy( onSuccess = { SoraLogger.d(TAG, "[channel:$role] @peer:clientOfferSdp") - disconnect() + disconnect(null) if (it.isFailure) { SoraLogger.d(TAG, "[channel:$role] failed to create client offer SDP: ${it.exceptionOrNull()?.message}") @@ -616,7 +640,7 @@ class SoraMediaChannel @JvmOverloads constructor( TAG, "[channel:$role] failed request client offer SDP: ${it.message}" ) - disconnect() + disconnect(DisconnectReason.SIGNALING_FAILURE) } ) @@ -635,8 +659,8 @@ class SoraMediaChannel @JvmOverloads constructor( endpoints = endpoints, role = role, channelId = channelId, - connectDataChannelSignaling = dataChannelSignaling, - connectIgnoreDisconnectWebSocket = ignoreDisconnectWebSocket, + connectDataChannelSignaling = connectDataChannelSignaling, + connectIgnoreDisconnectWebSocket = connectIgnoreDisconnectWebSocket, mediaOption = mediaOption, connectMetadata = signalingMetadata, listener = signalingListener, @@ -663,6 +687,10 @@ class SoraMediaChannel @JvmOverloads constructor( listener = peerListener ) + if (offerMessage.dataChannels?.isNotEmpty() == true) { + dataChannelSignaling = true + } + if (0 < peerConnectionOption.getStatsIntervalMSec) { getStatsTimer = Timer() SoraLogger.d(TAG, "Schedule getStats with interval ${peerConnectionOption.getStatsIntervalMSec} [msec]") @@ -685,7 +713,7 @@ class SoraMediaChannel @JvmOverloads constructor( onError = { val msg = "[channel:$role] failure in handleInitialOffer: ${it.message}" SoraLogger.w(TAG, msg) - disconnect() + disconnect(DisconnectReason.SIGNALING_FAILURE) } ) compositeDisposable.add(subscription) @@ -694,10 +722,10 @@ class SoraMediaChannel @JvmOverloads constructor( private fun handleSwitched(switchedMessage: SwitchedMessage) { switchedToDataChannel = true - ignoreDisconnectWebSocket = switchedMessage.ignoreDisconnectWebsocket + ignoreDisconnectWebSocket = switchedMessage.ignoreDisconnectWebsocket ?: false val earlyCloseWebSocket = ignoreDisconnectWebSocket ?: false if (earlyCloseWebSocket) { - signaling?.disconnect() + signaling?.disconnect(null) } } @@ -713,7 +741,7 @@ class SoraMediaChannel @JvmOverloads constructor( onError = { val msg = "[channel:$role] failed handle updated offer: ${it.message}" SoraLogger.w(TAG, msg) - disconnect() + disconnect(DisconnectReason.SIGNALING_FAILURE) } ) compositeDisposable.add(subscription) @@ -732,7 +760,7 @@ class SoraMediaChannel @JvmOverloads constructor( onError = { val msg = "[channel:$role] failed handle re-offer: ${it.message}" SoraLogger.w(TAG, msg) - disconnect() + disconnect(DisconnectReason.SIGNALING_FAILURE) } ) compositeDisposable.add(subscription) @@ -751,7 +779,7 @@ class SoraMediaChannel @JvmOverloads constructor( onError = { val msg = "[channel:$role] failed handle re-offer: ${it.message}" SoraLogger.w(TAG, msg) - disconnect() + disconnect(DisconnectReason.SIGNALING_FAILURE) } ) compositeDisposable.add(subscription) @@ -788,12 +816,18 @@ class SoraMediaChannel @JvmOverloads constructor( * アプリケーションとして切断後の処理が必要な場合は [Listener.onClose] で行います. */ fun disconnect() { + internalDisconnect(DisconnectReason.NO_ERROR) + } + + private fun internalDisconnect(reason: DisconnectReason?) { if (closing) return closing = true stopTimer() - sendDisconnectMessage() + reason?.let { + sendDisconnectIfNeeded(it) + } compositeDisposable.dispose() listener?.onClose(this) @@ -802,27 +836,71 @@ class SoraMediaChannel @JvmOverloads constructor( // アプリケーションで定義された切断処理を実行した後に connectedSignalingEndpoint を null にする connectedSignalingEndpoint = null - signaling?.disconnect() + // 既に type: disconnect を送信しているので、 disconnectReason は null で良い + signaling?.disconnect(null) signaling = null getStatsTimer?.cancel() getStatsTimer = null - peer?.disconnect() + // 既に type: disconnect を送信しているので、 disconnectReason は null で良い + peer?.disconnect(null) peer = null } - private fun sendDisconnectMessage() { - val dataChannel = dataChannels["signaling"] + private fun sendDisconnectOverWebSocket(reason: DisconnectReason) { + signaling?.sendDisconnect(reason) + } + + private fun sendDisconnectOverDataChannel(reason: DisconnectReason) { + dataChannels["signaling"]?.let { + peer?.sendDisconnect(it, reason) + } + } + + private fun sendDisconnectIfNeeded(reason: DisconnectReason) { + val state = peer?.connectionState() ?: null SoraLogger.d( TAG, - "[channel:$role] sendDisconnectMessage switched=$switchedToDataChannel, " + - "dataChannel.label=${dataChannel?.label()}" + "[channel:$role] sendDisconnectIfNeeded switched=$switchedToDataChannel, " + + "ignoreDisconenctWebSocket=$ignoreDisconnectWebSocket, " + + "reason=$reason, PeerConnectionState=$state" ) - if (switchedToDataChannel && dataChannel != null) { - peer?.sendDisconnectMessage(dataChannel) - } else { - signaling?.sendDisconnectMessage() + + if (state == PeerConnection.PeerConnectionState.FAILED) { + // この関数に到達した時点で PeerConnectionState が FAILED なのでメッセージの送信は不要 + return + } + + when (reason) { + DisconnectReason.NO_ERROR -> { + if (!dataChannelSignaling) { + // WebSocket のみ + sendDisconnectOverWebSocket(reason) + } else { + // WebSocket と DataChannel / DataChannel のみ + if (!switchedToDataChannel) { + // type: switched 未受信 + sendDisconnectOverWebSocket(reason) + } else { + // type: switched 受信済 + sendDisconnectOverDataChannel(reason) + } + } + } + DisconnectReason.WEBSOCKET_ONCLOSE, DisconnectReason.WEBSOCKET_ONERROR -> { + if (switchedToDataChannel && !ignoreDisconnectWebSocket) { + sendDisconnectOverDataChannel(reason) + } + } } } } + +enum class DisconnectReason(val value: String?) { + NO_ERROR("NO-ERROR"), + WEBSOCKET_ONCLOSE("WEBSOCKET-ONCLOSE"), + WEBSOCKET_ONERROR("WEBSOCKET-ONERROR"), + PEER_CONNECTION_STATE_FAILED(null), + SIGNALING_FAILURE(null), +} diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/PeerChannel.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/PeerChannel.kt index 38aee476..4503b614 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/PeerChannel.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/PeerChannel.kt @@ -4,6 +4,7 @@ import android.content.Context import io.reactivex.Single import io.reactivex.SingleOnSubscribe import io.reactivex.schedulers.Schedulers +import jp.shiguredo.sora.sdk.channel.DisconnectReason import jp.shiguredo.sora.sdk.channel.option.SoraMediaOption import jp.shiguredo.sora.sdk.channel.signaling.message.Encoding import jp.shiguredo.sora.sdk.channel.signaling.message.MessageConverter @@ -44,13 +45,14 @@ interface PeerChannel { // その場合に Result 型でエラーを含めて返す fun requestClientOfferSdp(): Single> - fun disconnect() + fun disconnect(disconnectReason: DisconnectReason?) + fun connectionState(): PeerConnection.PeerConnectionState? fun getStats(statsCollectorCallback: RTCStatsCollectorCallback) fun getStats(handler: (RTCStatsReport?) -> Unit) fun sendReAnswer(dataChannel: DataChannel, description: String) fun sendStats(dataChannel: DataChannel, report: RTCStatsReport) - fun sendDisconnectMessage(dataChannel: DataChannel) + fun sendDisconnect(dataChannel: DataChannel, reason: DisconnectReason) interface Listener { fun onRemoveRemoteStream(label: String) @@ -58,7 +60,7 @@ interface PeerChannel { fun onAddLocalStream(ms: MediaStream) fun onLocalIceCandidateFound(candidate: IceCandidate) fun onConnect() - fun onDisconnect() + fun onDisconnect(disconnectReason: DisconnectReason?) fun onDataChannelOpen(label: String, dataChannel: DataChannel) fun onDataChannelMessage(label: String, dataChannel: DataChannel, messageData: String) fun onDataChannelClosed(label: String, dataChannel: DataChannel) @@ -102,6 +104,7 @@ class PeerChannelImpl( private val componentFactory = RTCComponentFactory(mediaOption, listener) private var conn: PeerConnection? = null + private var factory: PeerConnectionFactory? = null private val executor = Executors.newSingleThreadExecutor() @@ -254,7 +257,7 @@ class PeerChannelImpl( } PeerConnection.PeerConnectionState.FAILED -> { listener?.onError(SoraErrorReason.PEER_CONNECTION_FAILED) - disconnect() + disconnect(DisconnectReason.PEER_CONNECTION_STATE_FAILED) } PeerConnection.PeerConnectionState.DISCONNECTED -> { if (closing) return @@ -267,7 +270,7 @@ class PeerChannelImpl( if (!closing) { listener?.onError(SoraErrorReason.PEER_CONNECTION_CLOSED) } - disconnect() + disconnect(null) } else -> {} } @@ -491,11 +494,11 @@ class PeerChannelImpl( listener?.onAddLocalStream(localStream!!) } - override fun disconnect() { + override fun disconnect(disconnectReason: DisconnectReason?) { if (closing) return executor.execute { - closeInternal() + closeInternal(disconnectReason) } } @@ -510,9 +513,10 @@ class PeerChannelImpl( dataChannel.send(stringToDataChannelBuffer(dataChannel.label(), statsMessage)) } - override fun sendDisconnectMessage(dataChannel: DataChannel) { + override fun sendDisconnect(dataChannel: DataChannel, reason: DisconnectReason) { SoraLogger.d(TAG, "peer: sendDisconnectMessage, label=${dataChannel.label()}") - val disconnectMessage = MessageConverter.buildDisconnectMessage() + val disconnectMessage = MessageConverter.buildDisconnectMessage(reason) + SoraLogger.d(TAG, "peer: disconnectMessage=$disconnectMessage") dataChannel.send(stringToDataChannelBuffer(dataChannel.label(), disconnectMessage)) } @@ -616,12 +620,12 @@ class PeerChannelImpl( } ).subscribeOn(Schedulers.from(executor)) - private fun closeInternal() { + private fun closeInternal(disconnectReason: DisconnectReason?) { if (closing) return SoraLogger.d(TAG, "disconnect") closing = true - listener?.onDisconnect() + listener?.onDisconnect(disconnectReason) listener = null SoraLogger.d(TAG, "dispose peer connection") conn?.dispose() @@ -638,6 +642,10 @@ class PeerChannelImpl( } } + override fun connectionState(): PeerConnection.PeerConnectionState? { + return conn?.connectionState() + } + override fun getStats(statsCollectorCallback: RTCStatsCollectorCallback) { conn?.getStats(statsCollectorCallback) } diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt index 404a6106..486e31ce 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt @@ -1,5 +1,6 @@ package jp.shiguredo.sora.sdk.channel.signaling +import jp.shiguredo.sora.sdk.channel.DisconnectReason import jp.shiguredo.sora.sdk.channel.option.SoraChannelRole import jp.shiguredo.sora.sdk.channel.option.SoraMediaOption import jp.shiguredo.sora.sdk.channel.signaling.message.MessageConverter @@ -27,12 +28,12 @@ interface SignalingChannel { fun sendUpdateAnswer(sdp: String) fun sendReAnswer(sdp: String) fun sendCandidate(sdp: String) - fun sendDisconnectMessage() - fun disconnect() + fun sendDisconnect(reason: DisconnectReason) + fun disconnect(reason: DisconnectReason?) interface Listener { fun onConnect(connectedEndpoint: String) - fun onDisconnect() + fun onDisconnect(reason: DisconnectReason?) fun onInitialOffer(offerMessage: OfferMessage) fun onSwitched(switchedMessage: SwitchedMessage) fun onUpdatedOffer(sdp: String) @@ -164,15 +165,16 @@ class SignalingChannelImpl @JvmOverloads constructor( } } - override fun sendDisconnectMessage() { + override fun sendDisconnect(reason: DisconnectReason) { SoraLogger.d(TAG, "[signaling:$role] -> type:disconnect, webSocket=$ws") ws?.let { - val disconnectMessage = MessageConverter.buildDisconnectMessage() + val disconnectMessage = MessageConverter.buildDisconnectMessage(reason) + SoraLogger.d(TAG, "[signaling:$role] disconnectMessage=$disconnectMessage") it.send(disconnectMessage) } } - override fun disconnect() { + override fun disconnect(reason: DisconnectReason?) { if (closing.get()) { return } @@ -183,7 +185,7 @@ class SignalingChannelImpl @JvmOverloads constructor( // type: redirect を受信している場合は onDisconnect を発火させない if (!receivedRedirectMessage.get()) { - listener?.onDisconnect() + listener?.onDisconnect(reason) } listener = null } @@ -214,7 +216,7 @@ class SignalingChannelImpl @JvmOverloads constructor( private fun closeWithError(reason: String) { SoraLogger.i(TAG, "[signaling:$role] closeWithError: reason=$reason") - disconnect() + disconnect(DisconnectReason.SIGNALING_FAILURE) } private fun onOfferMessage(text: String) { @@ -410,21 +412,21 @@ class SignalingChannelImpl @JvmOverloads constructor( listener?.onError(SoraErrorReason.SIGNALING_FAILURE) } - disconnect() + disconnect(DisconnectReason.WEBSOCKET_ONCLOSE) } catch (e: Exception) { SoraLogger.w(TAG, e.toString()) } } override fun onClosing(webSocket: WebSocket, code: Int, reason: String) { - SoraLogger.d(TAG, "[signaling:$role] @onClosing") + SoraLogger.d(TAG, "[signaling:$role] @onClosing: = [$reason], code = $code") if (!propagatesWebSocketTerminateEventToSignalingChannel(webSocket)) { SoraLogger.d(TAG, "[signaling:$role] @onClosing: skipped") return } - disconnect() + disconnect(DisconnectReason.WEBSOCKET_ONCLOSE) } override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) { @@ -439,7 +441,7 @@ class SignalingChannelImpl @JvmOverloads constructor( try { listener?.onError(SoraErrorReason.SIGNALING_FAILURE) - disconnect() + disconnect(DisconnectReason.WEBSOCKET_ONERROR) } catch (e: Exception) { SoraLogger.w(TAG, e.toString()) } diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/message/Catalog.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/message/Catalog.kt index 865ca813..36ea383e 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/message/Catalog.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/message/Catalog.kt @@ -180,5 +180,6 @@ data class NotificationMessage( ) data class DisconnectMessage( - @SerializedName("type") val type: String = "disconnect" + @SerializedName("type") val type: String = "disconnect", + @SerializedName("reason") val reason: String? = null ) diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/message/MessageConverter.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/message/MessageConverter.kt index 9023e7c9..0955534f 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/message/MessageConverter.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/message/MessageConverter.kt @@ -1,6 +1,7 @@ package jp.shiguredo.sora.sdk.channel.signaling.message import com.google.gson.Gson +import jp.shiguredo.sora.sdk.channel.DisconnectReason import jp.shiguredo.sora.sdk.channel.option.SoraChannelRole import jp.shiguredo.sora.sdk.channel.option.SoraMediaOption import jp.shiguredo.sora.sdk.util.SoraLogger @@ -143,8 +144,8 @@ class MessageConverter { return gson.toJson(StatsMessage(reports = reports.statsMap.values.map { stats -> SoraRTCStats(stats) })) } - fun buildDisconnectMessage(): String { - return gson.toJson(DisconnectMessage()) + fun buildDisconnectMessage(reason: DisconnectReason?): String { + return gson.toJson(DisconnectMessage(reason = reason?.value ?: null)) } fun parseType(text: String): String? { From da58c3691b258e13cd64db2d433c67e084db1ee8 Mon Sep 17 00:00:00 2001 From: enm10k Date: Wed, 5 Jan 2022 17:48:47 +0900 Subject: [PATCH 75/85] =?UTF-8?q?=E3=83=87=E3=83=90=E3=83=83=E3=82=B0?= =?UTF-8?q?=E4=B8=AD=E3=81=AB=E8=BF=BD=E5=8A=A0=E3=81=97=E3=81=9F=E3=83=AD?= =?UTF-8?q?=E3=82=B0=E3=82=92=E5=89=8A=E9=99=A4=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt index 8f82713d..dba6a8f1 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt @@ -103,7 +103,6 @@ class SoraMediaChannel @JvmOverloads constructor( connectDataChannelSignaling = dataChannelSignaling connectIgnoreDisconnectWebSocket = ignoreDisconnectWebSocket - SoraLogger.d(TAG, "connectDataChannelSignaling=$connectDataChannelSignaling, connectIgnoreDisconnectWebSocket=$connectIgnoreDisconnectWebSocket") } /** From 4e307dd719a795c4f8ca68eeb2d1f17d11194ccc Mon Sep 17 00:00:00 2001 From: enm10k Date: Wed, 5 Jan 2022 17:50:55 +0900 Subject: [PATCH 76/85] =?UTF-8?q?=E3=82=B3=E3=83=A1=E3=83=B3=E3=83=88?= =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=8A=A0=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt index dba6a8f1..f245abfa 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt @@ -815,6 +815,7 @@ class SoraMediaChannel @JvmOverloads constructor( * アプリケーションとして切断後の処理が必要な場合は [Listener.onClose] で行います. */ fun disconnect() { + // アプリケーションから切断された場合は NO-ERROR とする internalDisconnect(DisconnectReason.NO_ERROR) } From 406ff5a20289be3ede6eb9d3cb56f10ae7a3a298 Mon Sep 17 00:00:00 2001 From: enm10k Date: Wed, 5 Jan 2022 17:59:20 +0900 Subject: [PATCH 77/85] =?UTF-8?q?=E5=A4=89=E6=95=B0=E5=90=8D=E3=82=92?= =?UTF-8?q?=E6=8F=83=E3=81=88=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sora/sdk/channel/SoraMediaChannel.kt | 30 +++++++++---------- .../sora/sdk/channel/rtc/PeerChannel.kt | 6 ++-- .../sdk/channel/signaling/SignalingChannel.kt | 14 ++++----- .../signaling/message/MessageConverter.kt | 4 +-- 4 files changed, 27 insertions(+), 27 deletions(-) diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt index f245abfa..dcf00095 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt @@ -302,7 +302,7 @@ class SoraMediaChannel @JvmOverloads constructor( private val signalingListener = object : SignalingChannel.Listener { - override fun onDisconnect(reason: DisconnectReason?) { + override fun onDisconnect(disconnectReason: DisconnectReason?) { SoraLogger.d( TAG, "[channel:$role] @signaling:onDisconnect " + @@ -314,7 +314,7 @@ class SoraMediaChannel @JvmOverloads constructor( // なにもしない SoraLogger.d(TAG, "[channel:$role] @signaling:onDisconnect: IGNORE") } else { - internalDisconnect(reason) + internalDisconnect(disconnectReason) } } @@ -819,13 +819,13 @@ class SoraMediaChannel @JvmOverloads constructor( internalDisconnect(DisconnectReason.NO_ERROR) } - private fun internalDisconnect(reason: DisconnectReason?) { + private fun internalDisconnect(disconnectReason: DisconnectReason?) { if (closing) return closing = true stopTimer() - reason?.let { + disconnectReason?.let { sendDisconnectIfNeeded(it) } compositeDisposable.dispose() @@ -848,23 +848,23 @@ class SoraMediaChannel @JvmOverloads constructor( peer = null } - private fun sendDisconnectOverWebSocket(reason: DisconnectReason) { - signaling?.sendDisconnect(reason) + private fun sendDisconnectOverWebSocket(disconnectReason: DisconnectReason) { + signaling?.sendDisconnect(disconnectReason) } - private fun sendDisconnectOverDataChannel(reason: DisconnectReason) { + private fun sendDisconnectOverDataChannel(disconnectReason: DisconnectReason) { dataChannels["signaling"]?.let { - peer?.sendDisconnect(it, reason) + peer?.sendDisconnect(it, disconnectReason) } } - private fun sendDisconnectIfNeeded(reason: DisconnectReason) { + private fun sendDisconnectIfNeeded(disconnectReason: DisconnectReason) { val state = peer?.connectionState() ?: null SoraLogger.d( TAG, "[channel:$role] sendDisconnectIfNeeded switched=$switchedToDataChannel, " + "ignoreDisconenctWebSocket=$ignoreDisconnectWebSocket, " + - "reason=$reason, PeerConnectionState=$state" + "reason=$disconnectReason, PeerConnectionState=$state" ) if (state == PeerConnection.PeerConnectionState.FAILED) { @@ -872,25 +872,25 @@ class SoraMediaChannel @JvmOverloads constructor( return } - when (reason) { + when (disconnectReason) { DisconnectReason.NO_ERROR -> { if (!dataChannelSignaling) { // WebSocket のみ - sendDisconnectOverWebSocket(reason) + sendDisconnectOverWebSocket(disconnectReason) } else { // WebSocket と DataChannel / DataChannel のみ if (!switchedToDataChannel) { // type: switched 未受信 - sendDisconnectOverWebSocket(reason) + sendDisconnectOverWebSocket(disconnectReason) } else { // type: switched 受信済 - sendDisconnectOverDataChannel(reason) + sendDisconnectOverDataChannel(disconnectReason) } } } DisconnectReason.WEBSOCKET_ONCLOSE, DisconnectReason.WEBSOCKET_ONERROR -> { if (switchedToDataChannel && !ignoreDisconnectWebSocket) { - sendDisconnectOverDataChannel(reason) + sendDisconnectOverDataChannel(disconnectReason) } } } diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/PeerChannel.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/PeerChannel.kt index 4503b614..fc75df46 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/PeerChannel.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/PeerChannel.kt @@ -52,7 +52,7 @@ interface PeerChannel { fun getStats(handler: (RTCStatsReport?) -> Unit) fun sendReAnswer(dataChannel: DataChannel, description: String) fun sendStats(dataChannel: DataChannel, report: RTCStatsReport) - fun sendDisconnect(dataChannel: DataChannel, reason: DisconnectReason) + fun sendDisconnect(dataChannel: DataChannel, disconnectReason: DisconnectReason) interface Listener { fun onRemoveRemoteStream(label: String) @@ -513,9 +513,9 @@ class PeerChannelImpl( dataChannel.send(stringToDataChannelBuffer(dataChannel.label(), statsMessage)) } - override fun sendDisconnect(dataChannel: DataChannel, reason: DisconnectReason) { + override fun sendDisconnect(dataChannel: DataChannel, disconnectReason: DisconnectReason) { SoraLogger.d(TAG, "peer: sendDisconnectMessage, label=${dataChannel.label()}") - val disconnectMessage = MessageConverter.buildDisconnectMessage(reason) + val disconnectMessage = MessageConverter.buildDisconnectMessage(disconnectReason) SoraLogger.d(TAG, "peer: disconnectMessage=$disconnectMessage") dataChannel.send(stringToDataChannelBuffer(dataChannel.label(), disconnectMessage)) } diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt index 486e31ce..6f7a6343 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt @@ -28,12 +28,12 @@ interface SignalingChannel { fun sendUpdateAnswer(sdp: String) fun sendReAnswer(sdp: String) fun sendCandidate(sdp: String) - fun sendDisconnect(reason: DisconnectReason) - fun disconnect(reason: DisconnectReason?) + fun sendDisconnect(disconnectReason: DisconnectReason) + fun disconnect(disconnectReason: DisconnectReason?) interface Listener { fun onConnect(connectedEndpoint: String) - fun onDisconnect(reason: DisconnectReason?) + fun onDisconnect(disconnectReason: DisconnectReason?) fun onInitialOffer(offerMessage: OfferMessage) fun onSwitched(switchedMessage: SwitchedMessage) fun onUpdatedOffer(sdp: String) @@ -165,16 +165,16 @@ class SignalingChannelImpl @JvmOverloads constructor( } } - override fun sendDisconnect(reason: DisconnectReason) { + override fun sendDisconnect(disconnectReason: DisconnectReason) { SoraLogger.d(TAG, "[signaling:$role] -> type:disconnect, webSocket=$ws") ws?.let { - val disconnectMessage = MessageConverter.buildDisconnectMessage(reason) + val disconnectMessage = MessageConverter.buildDisconnectMessage(disconnectReason) SoraLogger.d(TAG, "[signaling:$role] disconnectMessage=$disconnectMessage") it.send(disconnectMessage) } } - override fun disconnect(reason: DisconnectReason?) { + override fun disconnect(disconnectReason: DisconnectReason?) { if (closing.get()) { return } @@ -185,7 +185,7 @@ class SignalingChannelImpl @JvmOverloads constructor( // type: redirect を受信している場合は onDisconnect を発火させない if (!receivedRedirectMessage.get()) { - listener?.onDisconnect(reason) + listener?.onDisconnect(disconnectReason) } listener = null } diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/message/MessageConverter.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/message/MessageConverter.kt index 0955534f..98cf4cc6 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/message/MessageConverter.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/message/MessageConverter.kt @@ -144,8 +144,8 @@ class MessageConverter { return gson.toJson(StatsMessage(reports = reports.statsMap.values.map { stats -> SoraRTCStats(stats) })) } - fun buildDisconnectMessage(reason: DisconnectReason?): String { - return gson.toJson(DisconnectMessage(reason = reason?.value ?: null)) + fun buildDisconnectMessage(disconnectReason: DisconnectReason?): String { + return gson.toJson(DisconnectMessage(reason = disconnectReason?.value ?: null)) } fun parseType(text: String): String? { From 0c4047bb8c52d12016273b91b04a36d360375137 Mon Sep 17 00:00:00 2001 From: enm10k Date: Thu, 6 Jan 2022 14:21:45 +0900 Subject: [PATCH 78/85] =?UTF-8?q?SoraMediaChannel=20=E3=81=AE=E3=82=B3?= =?UTF-8?q?=E3=83=B3=E3=82=B9=E3=83=88=E3=83=A9=E3=82=AF=E3=82=BF=E3=83=BC?= =?UTF-8?q?=E3=81=A8=E3=83=95=E3=82=A3=E3=83=BC=E3=83=AB=E3=83=89=E5=90=8D?= =?UTF-8?q?=E3=82=92=E9=87=8D=E8=A4=87=E3=81=97=E3=81=AA=E3=81=84=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sora/sdk/channel/SoraMediaChannel.kt | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt index dcf00095..618c8b7d 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt @@ -88,11 +88,11 @@ class SoraMediaChannel @JvmOverloads constructor( // DataChannel 経由のシグナリングが有効なら true // Sora から渡された値 (= offer メッセージ) を参照して更新している - private var dataChannelSignaling: Boolean = false + private var offerDataChannelSignaling: Boolean = false // DataChannel 経由のシグナリング利用時に WebSocket の切断を無視するなら true // 同じく switched メッセージを参照して更新している - private var ignoreDisconnectWebSocket: Boolean = false + private var switchedIgnoreDisconnectWebSocket: Boolean = false init { if ((signalingEndpoint == null && signalingEndpointCandidates.isEmpty()) || @@ -101,6 +101,10 @@ class SoraMediaChannel @JvmOverloads constructor( throw IllegalArgumentException("Either signalingEndpoint or signalingEndpointCandidates must be specified") } + // コンストラクタ以外で dataChannelSignaling, ignoreDisconnectWebSocket を参照すべきではない + // 各種ロジックの判定には Sora のメッセージに含まれる値を参照する必要があるため、以下を利用するのが正しい + // - offerDataChannelSignaling + // - switchedIgnoreDisconnectWebSocket connectDataChannelSignaling = dataChannelSignaling connectIgnoreDisconnectWebSocket = ignoreDisconnectWebSocket } @@ -307,10 +311,10 @@ class SoraMediaChannel @JvmOverloads constructor( TAG, "[channel:$role] @signaling:onDisconnect " + "switchedToDataChannel=$switchedToDataChannel, " + - "ignoreDisconnectWebSocket=$ignoreDisconnectWebSocket" + "switchedIgnoreDisconnectWebSocket=$switchedIgnoreDisconnectWebSocket" + ) - val ignoreDisconnect = ignoreDisconnectWebSocket ?: false - if (switchedToDataChannel && ignoreDisconnect) { + if (switchedToDataChannel && switchedIgnoreDisconnectWebSocket) { // なにもしない SoraLogger.d(TAG, "[channel:$role] @signaling:onDisconnect: IGNORE") } else { @@ -331,7 +335,7 @@ class SoraMediaChannel @JvmOverloads constructor( override fun onSwitched(switchedMessage: SwitchedMessage) { SoraLogger.d(TAG, "[channel:$role] @signaling:onSwitched") - this@SoraMediaChannel.ignoreDisconnectWebSocket = switchedMessage.ignoreDisconnectWebsocket ?: false + switchedIgnoreDisconnectWebSocket = switchedMessage.ignoreDisconnectWebsocket ?: false handleSwitched(switchedMessage) } @@ -357,7 +361,7 @@ class SoraMediaChannel @JvmOverloads constructor( override fun onError(reason: SoraErrorReason) { SoraLogger.d(TAG, "[channel:$role] @signaling:onError:$reason") - val ignoreError = ignoreDisconnectWebSocket ?: false + val ignoreError = switchedIgnoreDisconnectWebSocket if (switchedToDataChannel && ignoreError) { // なにもしない SoraLogger.d(TAG, "[channel:$role] @signaling:onError: IGNORE reason=$reason") @@ -687,7 +691,7 @@ class SoraMediaChannel @JvmOverloads constructor( ) if (offerMessage.dataChannels?.isNotEmpty() == true) { - dataChannelSignaling = true + offerDataChannelSignaling = true } if (0 < peerConnectionOption.getStatsIntervalMSec) { @@ -721,8 +725,8 @@ class SoraMediaChannel @JvmOverloads constructor( private fun handleSwitched(switchedMessage: SwitchedMessage) { switchedToDataChannel = true - ignoreDisconnectWebSocket = switchedMessage.ignoreDisconnectWebsocket ?: false - val earlyCloseWebSocket = ignoreDisconnectWebSocket ?: false + switchedIgnoreDisconnectWebSocket = switchedMessage.ignoreDisconnectWebsocket ?: false + val earlyCloseWebSocket = switchedIgnoreDisconnectWebSocket if (earlyCloseWebSocket) { signaling?.disconnect(null) } @@ -863,7 +867,7 @@ class SoraMediaChannel @JvmOverloads constructor( SoraLogger.d( TAG, "[channel:$role] sendDisconnectIfNeeded switched=$switchedToDataChannel, " + - "ignoreDisconenctWebSocket=$ignoreDisconnectWebSocket, " + + "switchedIgnoreDisconnectWebSocket=$switchedIgnoreDisconnectWebSocket, " + "reason=$disconnectReason, PeerConnectionState=$state" ) @@ -874,7 +878,7 @@ class SoraMediaChannel @JvmOverloads constructor( when (disconnectReason) { DisconnectReason.NO_ERROR -> { - if (!dataChannelSignaling) { + if (!offerDataChannelSignaling) { // WebSocket のみ sendDisconnectOverWebSocket(disconnectReason) } else { @@ -889,7 +893,7 @@ class SoraMediaChannel @JvmOverloads constructor( } } DisconnectReason.WEBSOCKET_ONCLOSE, DisconnectReason.WEBSOCKET_ONERROR -> { - if (switchedToDataChannel && !ignoreDisconnectWebSocket) { + if (switchedToDataChannel && !switchedIgnoreDisconnectWebSocket) { sendDisconnectOverDataChannel(disconnectReason) } } From a50c4f7a9bc1ea50b53a1b45fbf7aff17e187eb8 Mon Sep 17 00:00:00 2001 From: enm10k Date: Thu, 6 Jan 2022 15:05:13 +0900 Subject: [PATCH 79/85] =?UTF-8?q?=E3=83=AD=E3=82=B0=E3=81=AE=E3=83=A1?= =?UTF-8?q?=E3=83=83=E3=82=BB=E3=83=BC=E3=82=B8=E3=82=92=E4=BF=AE=E6=AD=A3?= =?UTF-8?q?=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt index 618c8b7d..8be818e4 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt @@ -508,7 +508,7 @@ class SoraMediaChannel @JvmOverloads constructor( } override fun onDisconnect(disconnectReason: DisconnectReason?) { - SoraLogger.d(TAG, "[channel:$role] @peer:onClose:$disconnectReason") + SoraLogger.d(TAG, "[channel:$role] @peer:onDisconnect:$disconnectReason") internalDisconnect(disconnectReason) } From ccf94d70b7ad1464d68f043c91988416abd42714 Mon Sep 17 00:00:00 2001 From: enm10k Date: Thu, 6 Jan 2022 15:48:32 +0900 Subject: [PATCH 80/85] =?UTF-8?q?when=20=E3=81=AE=E6=9D=A1=E4=BB=B6?= =?UTF-8?q?=E3=82=92=E7=B6=B2=E7=BE=85=E7=9A=84=E3=81=AB=E3=81=97=E3=81=A6?= =?UTF-8?q?=E3=82=B3=E3=83=A1=E3=83=B3=E3=83=88=E3=82=92=E8=BF=BD=E5=8A=A0?= =?UTF-8?q?=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt index 8be818e4..a043440c 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt @@ -892,11 +892,16 @@ class SoraMediaChannel @JvmOverloads constructor( } } } + DisconnectReason.WEBSOCKET_ONCLOSE, DisconnectReason.WEBSOCKET_ONERROR -> { if (switchedToDataChannel && !switchedIgnoreDisconnectWebSocket) { sendDisconnectOverDataChannel(disconnectReason) } } + + DisconnectReason.SIGNALING_FAILURE, DisconnectReason.PEER_CONNECTION_STATE_FAILED -> { + // メッセージの送信は不要 + } } } } From e44518c7eae86e8971f7be646fb32a5aba76ab01 Mon Sep 17 00:00:00 2001 From: enm10k Date: Thu, 6 Jan 2022 16:11:55 +0900 Subject: [PATCH 81/85] s/DisconnectReason/SoraDisconnectReason/g --- .../sora/sdk/channel/SoraMediaChannel.kt | 32 +++++++++---------- .../sora/sdk/channel/rtc/PeerChannel.kt | 16 +++++----- .../sdk/channel/signaling/SignalingChannel.kt | 20 ++++++------ .../signaling/message/MessageConverter.kt | 4 +-- 4 files changed, 36 insertions(+), 36 deletions(-) diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt index a043440c..480ce0ae 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt @@ -306,7 +306,7 @@ class SoraMediaChannel @JvmOverloads constructor( private val signalingListener = object : SignalingChannel.Listener { - override fun onDisconnect(disconnectReason: DisconnectReason?) { + override fun onDisconnect(disconnectReason: SoraDisconnectReason?) { SoraLogger.d( TAG, "[channel:$role] @signaling:onDisconnect " + @@ -507,7 +507,7 @@ class SoraMediaChannel @JvmOverloads constructor( listener?.onWarning(this@SoraMediaChannel, reason, message) } - override fun onDisconnect(disconnectReason: DisconnectReason?) { + override fun onDisconnect(disconnectReason: SoraDisconnectReason?) { SoraLogger.d(TAG, "[channel:$role] @peer:onDisconnect:$disconnectReason") internalDisconnect(disconnectReason) @@ -643,7 +643,7 @@ class SoraMediaChannel @JvmOverloads constructor( TAG, "[channel:$role] failed request client offer SDP: ${it.message}" ) - disconnect(DisconnectReason.SIGNALING_FAILURE) + disconnect(SoraDisconnectReason.SIGNALING_FAILURE) } ) @@ -716,7 +716,7 @@ class SoraMediaChannel @JvmOverloads constructor( onError = { val msg = "[channel:$role] failure in handleInitialOffer: ${it.message}" SoraLogger.w(TAG, msg) - disconnect(DisconnectReason.SIGNALING_FAILURE) + disconnect(SoraDisconnectReason.SIGNALING_FAILURE) } ) compositeDisposable.add(subscription) @@ -744,7 +744,7 @@ class SoraMediaChannel @JvmOverloads constructor( onError = { val msg = "[channel:$role] failed handle updated offer: ${it.message}" SoraLogger.w(TAG, msg) - disconnect(DisconnectReason.SIGNALING_FAILURE) + disconnect(SoraDisconnectReason.SIGNALING_FAILURE) } ) compositeDisposable.add(subscription) @@ -763,7 +763,7 @@ class SoraMediaChannel @JvmOverloads constructor( onError = { val msg = "[channel:$role] failed handle re-offer: ${it.message}" SoraLogger.w(TAG, msg) - disconnect(DisconnectReason.SIGNALING_FAILURE) + disconnect(SoraDisconnectReason.SIGNALING_FAILURE) } ) compositeDisposable.add(subscription) @@ -782,7 +782,7 @@ class SoraMediaChannel @JvmOverloads constructor( onError = { val msg = "[channel:$role] failed handle re-offer: ${it.message}" SoraLogger.w(TAG, msg) - disconnect(DisconnectReason.SIGNALING_FAILURE) + disconnect(SoraDisconnectReason.SIGNALING_FAILURE) } ) compositeDisposable.add(subscription) @@ -820,10 +820,10 @@ class SoraMediaChannel @JvmOverloads constructor( */ fun disconnect() { // アプリケーションから切断された場合は NO-ERROR とする - internalDisconnect(DisconnectReason.NO_ERROR) + internalDisconnect(SoraDisconnectReason.NO_ERROR) } - private fun internalDisconnect(disconnectReason: DisconnectReason?) { + private fun internalDisconnect(disconnectReason: SoraDisconnectReason?) { if (closing) return closing = true @@ -852,17 +852,17 @@ class SoraMediaChannel @JvmOverloads constructor( peer = null } - private fun sendDisconnectOverWebSocket(disconnectReason: DisconnectReason) { + private fun sendDisconnectOverWebSocket(disconnectReason: SoraDisconnectReason) { signaling?.sendDisconnect(disconnectReason) } - private fun sendDisconnectOverDataChannel(disconnectReason: DisconnectReason) { + private fun sendDisconnectOverDataChannel(disconnectReason: SoraDisconnectReason) { dataChannels["signaling"]?.let { peer?.sendDisconnect(it, disconnectReason) } } - private fun sendDisconnectIfNeeded(disconnectReason: DisconnectReason) { + private fun sendDisconnectIfNeeded(disconnectReason: SoraDisconnectReason) { val state = peer?.connectionState() ?: null SoraLogger.d( TAG, @@ -877,7 +877,7 @@ class SoraMediaChannel @JvmOverloads constructor( } when (disconnectReason) { - DisconnectReason.NO_ERROR -> { + SoraDisconnectReason.NO_ERROR -> { if (!offerDataChannelSignaling) { // WebSocket のみ sendDisconnectOverWebSocket(disconnectReason) @@ -893,20 +893,20 @@ class SoraMediaChannel @JvmOverloads constructor( } } - DisconnectReason.WEBSOCKET_ONCLOSE, DisconnectReason.WEBSOCKET_ONERROR -> { + SoraDisconnectReason.WEBSOCKET_ONCLOSE, SoraDisconnectReason.WEBSOCKET_ONERROR -> { if (switchedToDataChannel && !switchedIgnoreDisconnectWebSocket) { sendDisconnectOverDataChannel(disconnectReason) } } - DisconnectReason.SIGNALING_FAILURE, DisconnectReason.PEER_CONNECTION_STATE_FAILED -> { + SoraDisconnectReason.SIGNALING_FAILURE, SoraDisconnectReason.PEER_CONNECTION_STATE_FAILED -> { // メッセージの送信は不要 } } } } -enum class DisconnectReason(val value: String?) { +enum class SoraDisconnectReason(val value: String?) { NO_ERROR("NO-ERROR"), WEBSOCKET_ONCLOSE("WEBSOCKET-ONCLOSE"), WEBSOCKET_ONERROR("WEBSOCKET-ONERROR"), diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/PeerChannel.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/PeerChannel.kt index fc75df46..e6358e2d 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/PeerChannel.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/PeerChannel.kt @@ -4,7 +4,7 @@ import android.content.Context import io.reactivex.Single import io.reactivex.SingleOnSubscribe import io.reactivex.schedulers.Schedulers -import jp.shiguredo.sora.sdk.channel.DisconnectReason +import jp.shiguredo.sora.sdk.channel.SoraDisconnectReason import jp.shiguredo.sora.sdk.channel.option.SoraMediaOption import jp.shiguredo.sora.sdk.channel.signaling.message.Encoding import jp.shiguredo.sora.sdk.channel.signaling.message.MessageConverter @@ -45,14 +45,14 @@ interface PeerChannel { // その場合に Result 型でエラーを含めて返す fun requestClientOfferSdp(): Single> - fun disconnect(disconnectReason: DisconnectReason?) + fun disconnect(disconnectReason: SoraDisconnectReason?) fun connectionState(): PeerConnection.PeerConnectionState? fun getStats(statsCollectorCallback: RTCStatsCollectorCallback) fun getStats(handler: (RTCStatsReport?) -> Unit) fun sendReAnswer(dataChannel: DataChannel, description: String) fun sendStats(dataChannel: DataChannel, report: RTCStatsReport) - fun sendDisconnect(dataChannel: DataChannel, disconnectReason: DisconnectReason) + fun sendDisconnect(dataChannel: DataChannel, disconnectReason: SoraDisconnectReason) interface Listener { fun onRemoveRemoteStream(label: String) @@ -60,7 +60,7 @@ interface PeerChannel { fun onAddLocalStream(ms: MediaStream) fun onLocalIceCandidateFound(candidate: IceCandidate) fun onConnect() - fun onDisconnect(disconnectReason: DisconnectReason?) + fun onDisconnect(disconnectReason: SoraDisconnectReason?) fun onDataChannelOpen(label: String, dataChannel: DataChannel) fun onDataChannelMessage(label: String, dataChannel: DataChannel, messageData: String) fun onDataChannelClosed(label: String, dataChannel: DataChannel) @@ -257,7 +257,7 @@ class PeerChannelImpl( } PeerConnection.PeerConnectionState.FAILED -> { listener?.onError(SoraErrorReason.PEER_CONNECTION_FAILED) - disconnect(DisconnectReason.PEER_CONNECTION_STATE_FAILED) + disconnect(SoraDisconnectReason.PEER_CONNECTION_STATE_FAILED) } PeerConnection.PeerConnectionState.DISCONNECTED -> { if (closing) return @@ -494,7 +494,7 @@ class PeerChannelImpl( listener?.onAddLocalStream(localStream!!) } - override fun disconnect(disconnectReason: DisconnectReason?) { + override fun disconnect(disconnectReason: SoraDisconnectReason?) { if (closing) return executor.execute { @@ -513,7 +513,7 @@ class PeerChannelImpl( dataChannel.send(stringToDataChannelBuffer(dataChannel.label(), statsMessage)) } - override fun sendDisconnect(dataChannel: DataChannel, disconnectReason: DisconnectReason) { + override fun sendDisconnect(dataChannel: DataChannel, disconnectReason: SoraDisconnectReason) { SoraLogger.d(TAG, "peer: sendDisconnectMessage, label=${dataChannel.label()}") val disconnectMessage = MessageConverter.buildDisconnectMessage(disconnectReason) SoraLogger.d(TAG, "peer: disconnectMessage=$disconnectMessage") @@ -620,7 +620,7 @@ class PeerChannelImpl( } ).subscribeOn(Schedulers.from(executor)) - private fun closeInternal(disconnectReason: DisconnectReason?) { + private fun closeInternal(disconnectReason: SoraDisconnectReason?) { if (closing) return SoraLogger.d(TAG, "disconnect") diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt index 6f7a6343..8ba5a4cd 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt @@ -1,6 +1,6 @@ package jp.shiguredo.sora.sdk.channel.signaling -import jp.shiguredo.sora.sdk.channel.DisconnectReason +import jp.shiguredo.sora.sdk.channel.SoraDisconnectReason import jp.shiguredo.sora.sdk.channel.option.SoraChannelRole import jp.shiguredo.sora.sdk.channel.option.SoraMediaOption import jp.shiguredo.sora.sdk.channel.signaling.message.MessageConverter @@ -28,12 +28,12 @@ interface SignalingChannel { fun sendUpdateAnswer(sdp: String) fun sendReAnswer(sdp: String) fun sendCandidate(sdp: String) - fun sendDisconnect(disconnectReason: DisconnectReason) - fun disconnect(disconnectReason: DisconnectReason?) + fun sendDisconnect(disconnectReason: SoraDisconnectReason) + fun disconnect(disconnectReason: SoraDisconnectReason?) interface Listener { fun onConnect(connectedEndpoint: String) - fun onDisconnect(disconnectReason: DisconnectReason?) + fun onDisconnect(disconnectReason: SoraDisconnectReason?) fun onInitialOffer(offerMessage: OfferMessage) fun onSwitched(switchedMessage: SwitchedMessage) fun onUpdatedOffer(sdp: String) @@ -165,7 +165,7 @@ class SignalingChannelImpl @JvmOverloads constructor( } } - override fun sendDisconnect(disconnectReason: DisconnectReason) { + override fun sendDisconnect(disconnectReason: SoraDisconnectReason) { SoraLogger.d(TAG, "[signaling:$role] -> type:disconnect, webSocket=$ws") ws?.let { val disconnectMessage = MessageConverter.buildDisconnectMessage(disconnectReason) @@ -174,7 +174,7 @@ class SignalingChannelImpl @JvmOverloads constructor( } } - override fun disconnect(disconnectReason: DisconnectReason?) { + override fun disconnect(disconnectReason: SoraDisconnectReason?) { if (closing.get()) { return } @@ -216,7 +216,7 @@ class SignalingChannelImpl @JvmOverloads constructor( private fun closeWithError(reason: String) { SoraLogger.i(TAG, "[signaling:$role] closeWithError: reason=$reason") - disconnect(DisconnectReason.SIGNALING_FAILURE) + disconnect(SoraDisconnectReason.SIGNALING_FAILURE) } private fun onOfferMessage(text: String) { @@ -412,7 +412,7 @@ class SignalingChannelImpl @JvmOverloads constructor( listener?.onError(SoraErrorReason.SIGNALING_FAILURE) } - disconnect(DisconnectReason.WEBSOCKET_ONCLOSE) + disconnect(SoraDisconnectReason.WEBSOCKET_ONCLOSE) } catch (e: Exception) { SoraLogger.w(TAG, e.toString()) } @@ -426,7 +426,7 @@ class SignalingChannelImpl @JvmOverloads constructor( return } - disconnect(DisconnectReason.WEBSOCKET_ONCLOSE) + disconnect(SoraDisconnectReason.WEBSOCKET_ONCLOSE) } override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) { @@ -441,7 +441,7 @@ class SignalingChannelImpl @JvmOverloads constructor( try { listener?.onError(SoraErrorReason.SIGNALING_FAILURE) - disconnect(DisconnectReason.WEBSOCKET_ONERROR) + disconnect(SoraDisconnectReason.WEBSOCKET_ONERROR) } catch (e: Exception) { SoraLogger.w(TAG, e.toString()) } diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/message/MessageConverter.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/message/MessageConverter.kt index 98cf4cc6..ae962272 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/message/MessageConverter.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/message/MessageConverter.kt @@ -1,7 +1,7 @@ package jp.shiguredo.sora.sdk.channel.signaling.message import com.google.gson.Gson -import jp.shiguredo.sora.sdk.channel.DisconnectReason +import jp.shiguredo.sora.sdk.channel.SoraDisconnectReason import jp.shiguredo.sora.sdk.channel.option.SoraChannelRole import jp.shiguredo.sora.sdk.channel.option.SoraMediaOption import jp.shiguredo.sora.sdk.util.SoraLogger @@ -144,7 +144,7 @@ class MessageConverter { return gson.toJson(StatsMessage(reports = reports.statsMap.values.map { stats -> SoraRTCStats(stats) })) } - fun buildDisconnectMessage(disconnectReason: DisconnectReason?): String { + fun buildDisconnectMessage(disconnectReason: SoraDisconnectReason?): String { return gson.toJson(DisconnectMessage(reason = disconnectReason?.value ?: null)) } From 92fad9b410a4872365cf0fc9d89eb8f88ca52256 Mon Sep 17 00:00:00 2001 From: enm10k Date: Thu, 6 Jan 2022 16:15:52 +0900 Subject: [PATCH 82/85] =?UTF-8?q?SoraDisconnectReason=20=E3=82=92=E3=83=95?= =?UTF-8?q?=E3=82=A1=E3=82=A4=E3=83=AB=E3=81=AB=E5=88=87=E3=82=8A=E5=87=BA?= =?UTF-8?q?=E3=81=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../shiguredo/sora/sdk/channel/SoraMediaChannel.kt | 8 +------- .../jp/shiguredo/sora/sdk/channel/rtc/PeerChannel.kt | 2 +- .../sora/sdk/channel/signaling/SignalingChannel.kt | 2 +- .../channel/signaling/message/MessageConverter.kt | 2 +- .../shiguredo/sora/sdk/error/SoraDisconnectReason.kt | 12 ++++++++++++ 5 files changed, 16 insertions(+), 10 deletions(-) create mode 100644 sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/error/SoraDisconnectReason.kt diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt index 480ce0ae..75014517 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt @@ -19,6 +19,7 @@ import jp.shiguredo.sora.sdk.channel.signaling.message.OfferConfig import jp.shiguredo.sora.sdk.channel.signaling.message.OfferMessage import jp.shiguredo.sora.sdk.channel.signaling.message.PushMessage import jp.shiguredo.sora.sdk.channel.signaling.message.SwitchedMessage +import jp.shiguredo.sora.sdk.error.SoraDisconnectReason import jp.shiguredo.sora.sdk.error.SoraErrorReason import jp.shiguredo.sora.sdk.util.ReusableCompositeDisposable import jp.shiguredo.sora.sdk.util.SoraLogger @@ -906,10 +907,3 @@ class SoraMediaChannel @JvmOverloads constructor( } } -enum class SoraDisconnectReason(val value: String?) { - NO_ERROR("NO-ERROR"), - WEBSOCKET_ONCLOSE("WEBSOCKET-ONCLOSE"), - WEBSOCKET_ONERROR("WEBSOCKET-ONERROR"), - PEER_CONNECTION_STATE_FAILED(null), - SIGNALING_FAILURE(null), -} diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/PeerChannel.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/PeerChannel.kt index e6358e2d..1c2dd702 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/PeerChannel.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/rtc/PeerChannel.kt @@ -4,7 +4,7 @@ import android.content.Context import io.reactivex.Single import io.reactivex.SingleOnSubscribe import io.reactivex.schedulers.Schedulers -import jp.shiguredo.sora.sdk.channel.SoraDisconnectReason +import jp.shiguredo.sora.sdk.error.SoraDisconnectReason import jp.shiguredo.sora.sdk.channel.option.SoraMediaOption import jp.shiguredo.sora.sdk.channel.signaling.message.Encoding import jp.shiguredo.sora.sdk.channel.signaling.message.MessageConverter diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt index 8ba5a4cd..a71819d1 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/SignalingChannel.kt @@ -1,6 +1,6 @@ package jp.shiguredo.sora.sdk.channel.signaling -import jp.shiguredo.sora.sdk.channel.SoraDisconnectReason +import jp.shiguredo.sora.sdk.error.SoraDisconnectReason import jp.shiguredo.sora.sdk.channel.option.SoraChannelRole import jp.shiguredo.sora.sdk.channel.option.SoraMediaOption import jp.shiguredo.sora.sdk.channel.signaling.message.MessageConverter diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/message/MessageConverter.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/message/MessageConverter.kt index ae962272..414dc94f 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/message/MessageConverter.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/signaling/message/MessageConverter.kt @@ -1,7 +1,7 @@ package jp.shiguredo.sora.sdk.channel.signaling.message import com.google.gson.Gson -import jp.shiguredo.sora.sdk.channel.SoraDisconnectReason +import jp.shiguredo.sora.sdk.error.SoraDisconnectReason import jp.shiguredo.sora.sdk.channel.option.SoraChannelRole import jp.shiguredo.sora.sdk.channel.option.SoraMediaOption import jp.shiguredo.sora.sdk.util.SoraLogger diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/error/SoraDisconnectReason.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/error/SoraDisconnectReason.kt new file mode 100644 index 00000000..583fa0a2 --- /dev/null +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/error/SoraDisconnectReason.kt @@ -0,0 +1,12 @@ +package jp.shiguredo.sora.sdk.error + +/** + * type: disconnect メッセージに含める reason の判定に利用します. + */ +enum class SoraDisconnectReason(val value: String?) { + NO_ERROR("NO-ERROR"), + WEBSOCKET_ONCLOSE("WEBSOCKET-ONCLOSE"), + WEBSOCKET_ONERROR("WEBSOCKET-ONERROR"), + PEER_CONNECTION_STATE_FAILED(null), + SIGNALING_FAILURE(null), +} \ No newline at end of file From a2c98a7b240d137823bb2eae148b758abe603b65 Mon Sep 17 00:00:00 2001 From: enm10k Date: Thu, 6 Jan 2022 16:38:39 +0900 Subject: [PATCH 83/85] =?UTF-8?q?DEBUG=20=E3=83=93=E3=83=AB=E3=83=89?= =?UTF-8?q?=E3=81=AE=E3=81=BF=E3=80=81=E6=9D=A1=E4=BB=B6=E3=81=AB=E3=81=AA?= =?UTF-8?q?=E3=81=84=20SoraDisconnectReason=20=E3=81=8C=E6=9D=A5=E3=81=9F?= =?UTF-8?q?=E3=82=89=E4=BE=8B=E5=A4=96=E3=82=92=E4=B8=8A=E3=81=92=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt index 75014517..8798c766 100644 --- a/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt +++ b/sora-android-sdk/src/main/kotlin/jp/shiguredo/sora/sdk/channel/SoraMediaChannel.kt @@ -5,6 +5,7 @@ import android.os.Handler import android.os.Looper import io.reactivex.rxkotlin.subscribeBy import io.reactivex.schedulers.Schedulers +import jp.shiguredo.sora.sdk.BuildConfig import jp.shiguredo.sora.sdk.channel.data.ChannelAttendeesCount import jp.shiguredo.sora.sdk.channel.option.PeerConnectionOption import jp.shiguredo.sora.sdk.channel.option.SoraMediaOption @@ -903,6 +904,14 @@ class SoraMediaChannel @JvmOverloads constructor( SoraDisconnectReason.SIGNALING_FAILURE, SoraDisconnectReason.PEER_CONNECTION_STATE_FAILED -> { // メッセージの送信は不要 } + + else -> { + // SoraDisconnectReason のすべての条件が網羅されていて欲しい + if (BuildConfig.DEBUG) { + throw Exception("when statement should be exhaustive.") + } + SoraLogger.i(TAG, "when statement should be exhaustive.") + } } } } From ced6543306fba7dfcc5b989edebe2b018749ce6a Mon Sep 17 00:00:00 2001 From: enm10k Date: Thu, 6 Jan 2022 17:11:41 +0900 Subject: [PATCH 84/85] =?UTF-8?q?CHANGES.md=20=E3=82=92=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGES.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 5450db6f..10eae865 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -11,6 +11,8 @@ ## develop +- [CHANGE] スポットライトレガシーを削除する + - @enm10k - [UPDATE] libwebrtc を 96.4664.2.1 に上げる - @enm10k - [UPDATE] 複数シグナリング URL の指定に対応する @@ -20,7 +22,7 @@ - @enm10k - [UPDATE] dokka を 1.5.31 に上げる - @miosakuma -- [CHANGE] スポットライトレガシーを削除する +- [UPDATE] type: disconnect に reason を追加する - @enm10k - [FIX] 視聴のみかつ H.264 した場合に接続できない問題についてのワークアラウンドを削除する - SoraMediaOption.videoUpstreamContext が無く SoraMediaOption.videoDownstreamContext From d6cb6c7d3d689cb921d13bba8ebb552c3dd226a6 Mon Sep 17 00:00:00 2001 From: miosakuma Date: Wed, 12 Jan 2022 11:21:40 +0900 Subject: [PATCH 85/85] 2022.1.0 --- CHANGES.md | 2 +- README.md | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 10eae865..4ffa551c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -9,7 +9,7 @@ - FIX - バグ修正 -## develop +## 2022.1.0 - [CHANGE] スポットライトレガシーを削除する - @enm10k diff --git a/README.md b/README.md index 5cebc116..55ef1865 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Sora Android SDK [![Release](https://jitpack.io/v/shiguredo/sora-android-sdk.svg)](https://jitpack.io/#shiguredo/sora-android-sdk) -[![libwebrtc](https://img.shields.io/badge/libwebrtc-m93.4577-blue.svg)](https://chromium.googlesource.com/external/webrtc/+/branch-heads/4515) +[![libwebrtc](https://img.shields.io/badge/libwebrtc-m96.4664-blue.svg)](https://chromium.googlesource.com/external/webrtc/+/branch-heads/4515) [![GitHub tag (latest SemVer)](https://img.shields.io/github/tag/shiguredo/sora-android-sdk.svg)](https://github.com/shiguredo/sora-android-sdk.svg) [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) @@ -21,7 +21,7 @@ Please read https://github.com/shiguredo/oss before use. ## システム条件 - Android 5 以降 (エミュレーターでの動作は保証しません) -- Android Studio 4.2 以降 +- Android Studio 2020.3.1 以降 - WebRTC SFU Sora 2021.2 以降 ## サンプル @@ -39,7 +39,7 @@ Please read https://github.com/shiguredo/oss before use. ``` Copyright 2017, Lyo Kato (Original Author) -Copyright 2017-2021, Shiguredo Inc. +Copyright 2017-2022, Shiguredo Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.