Committed by
GitHub
PeerConnectionState instead of IceConnectionState (#43)
* connection state enum * proper cleanup of signal client * fix tests * Observe peer connection state instead of ice connection * fix tests
正在显示
11 个修改的文件
包含
127 行增加
和
64 行删除
| @@ -12,8 +12,7 @@ class PublisherTransportObserver( | @@ -12,8 +12,7 @@ class PublisherTransportObserver( | ||
| 12 | private val client: SignalClient, | 12 | private val client: SignalClient, |
| 13 | ) : PeerConnection.Observer, PeerConnectionTransport.Listener { | 13 | ) : PeerConnection.Observer, PeerConnectionTransport.Listener { |
| 14 | 14 | ||
| 15 | - var iceConnectionChangeListener: ((newState: PeerConnection.IceConnectionState?) -> Unit)? = | ||
| 16 | - null | 15 | + var connectionChangeListener: ((newState: PeerConnection.PeerConnectionState?) -> Unit)? = null |
| 17 | 16 | ||
| 18 | override fun onIceCandidate(iceCandidate: IceCandidate?) { | 17 | override fun onIceCandidate(iceCandidate: IceCandidate?) { |
| 19 | val candidate = iceCandidate ?: return | 18 | val candidate = iceCandidate ?: return |
| @@ -27,7 +26,6 @@ class PublisherTransportObserver( | @@ -27,7 +26,6 @@ class PublisherTransportObserver( | ||
| 27 | 26 | ||
| 28 | override fun onIceConnectionChange(newState: PeerConnection.IceConnectionState?) { | 27 | override fun onIceConnectionChange(newState: PeerConnection.IceConnectionState?) { |
| 29 | LKLog.v { "onIceConnection new state: $newState" } | 28 | LKLog.v { "onIceConnection new state: $newState" } |
| 30 | - iceConnectionChangeListener?.invoke(newState) | ||
| 31 | } | 29 | } |
| 32 | 30 | ||
| 33 | override fun onOffer(sd: SessionDescription) { | 31 | override fun onOffer(sd: SessionDescription) { |
| @@ -38,6 +36,8 @@ class PublisherTransportObserver( | @@ -38,6 +36,8 @@ class PublisherTransportObserver( | ||
| 38 | } | 36 | } |
| 39 | 37 | ||
| 40 | override fun onConnectionChange(newState: PeerConnection.PeerConnectionState?) { | 38 | override fun onConnectionChange(newState: PeerConnection.PeerConnectionState?) { |
| 39 | + LKLog.v { "onConnection new state: $newState" } | ||
| 40 | + connectionChangeListener?.invoke(newState) | ||
| 41 | } | 41 | } |
| 42 | 42 | ||
| 43 | override fun onSelectedCandidatePairChanged(event: CandidatePairChangeEvent?) { | 43 | override fun onSelectedCandidatePairChanged(event: CandidatePairChangeEvent?) { |
| @@ -10,6 +10,7 @@ import io.livekit.android.util.CloseableCoroutineScope | @@ -10,6 +10,7 @@ import io.livekit.android.util.CloseableCoroutineScope | ||
| 10 | import io.livekit.android.util.Either | 10 | import io.livekit.android.util.Either |
| 11 | import io.livekit.android.util.LKLog | 11 | import io.livekit.android.util.LKLog |
| 12 | import io.livekit.android.webrtc.isConnected | 12 | import io.livekit.android.webrtc.isConnected |
| 13 | +import io.livekit.android.webrtc.isDisconnected | ||
| 13 | import io.livekit.android.webrtc.toProtoSessionDescription | 14 | import io.livekit.android.webrtc.toProtoSessionDescription |
| 14 | import kotlinx.coroutines.* | 15 | import kotlinx.coroutines.* |
| 15 | import livekit.LivekitModels | 16 | import livekit.LivekitModels |
| @@ -36,7 +37,11 @@ internal constructor( | @@ -36,7 +37,11 @@ internal constructor( | ||
| 36 | @Named(InjectionNames.DISPATCHER_IO) ioDispatcher: CoroutineDispatcher, | 37 | @Named(InjectionNames.DISPATCHER_IO) ioDispatcher: CoroutineDispatcher, |
| 37 | ) : SignalClient.Listener, DataChannel.Observer { | 38 | ) : SignalClient.Listener, DataChannel.Observer { |
| 38 | internal var listener: Listener? = null | 39 | internal var listener: Listener? = null |
| 39 | - internal var iceState: IceState = IceState.DISCONNECTED | 40 | + |
| 41 | + /** | ||
| 42 | + * Reflects the combined connection state of SignalClient and primary PeerConnection. | ||
| 43 | + */ | ||
| 44 | + internal var connectionState: ConnectionState = ConnectionState.DISCONNECTED | ||
| 40 | set(value) { | 45 | set(value) { |
| 41 | val oldVal = field | 46 | val oldVal = field |
| 42 | field = value | 47 | field = value |
| @@ -44,18 +49,18 @@ internal constructor( | @@ -44,18 +49,18 @@ internal constructor( | ||
| 44 | return | 49 | return |
| 45 | } | 50 | } |
| 46 | when (value) { | 51 | when (value) { |
| 47 | - IceState.CONNECTED -> { | ||
| 48 | - if (oldVal == IceState.DISCONNECTED) { | 52 | + ConnectionState.CONNECTED -> { |
| 53 | + if (oldVal == ConnectionState.DISCONNECTED) { | ||
| 49 | LKLog.d { "primary ICE connected" } | 54 | LKLog.d { "primary ICE connected" } |
| 50 | - listener?.onIceConnected() | ||
| 51 | - } else if (oldVal == IceState.RECONNECTING) { | 55 | + listener?.onEngineConnected() |
| 56 | + } else if (oldVal == ConnectionState.RECONNECTING) { | ||
| 52 | LKLog.d { "primary ICE reconnected" } | 57 | LKLog.d { "primary ICE reconnected" } |
| 53 | - listener?.onIceReconnected() | 58 | + listener?.onEngineReconnected() |
| 54 | } | 59 | } |
| 55 | } | 60 | } |
| 56 | - IceState.DISCONNECTED -> { | 61 | + ConnectionState.DISCONNECTED -> { |
| 57 | LKLog.d { "primary ICE disconnected" } | 62 | LKLog.d { "primary ICE disconnected" } |
| 58 | - if (oldVal == IceState.CONNECTED) { | 63 | + if (oldVal == ConnectionState.CONNECTED) { |
| 59 | reconnect() | 64 | reconnect() |
| 60 | } | 65 | } |
| 61 | } | 66 | } |
| @@ -167,17 +172,14 @@ internal constructor( | @@ -167,17 +172,14 @@ internal constructor( | ||
| 167 | null, | 172 | null, |
| 168 | ) | 173 | ) |
| 169 | 174 | ||
| 170 | - val iceConnectionStateListener: (PeerConnection.IceConnectionState?) -> Unit = { newState -> | 175 | + val connectionStateListener: (PeerConnection.PeerConnectionState?) -> Unit = { newState -> |
| 171 | val state = | 176 | val state = |
| 172 | newState ?: throw NullPointerException("unexpected null new state, what do?") | 177 | newState ?: throw NullPointerException("unexpected null new state, what do?") |
| 173 | LKLog.v { "onIceConnection new state: $newState" } | 178 | LKLog.v { "onIceConnection new state: $newState" } |
| 174 | if (state.isConnected()) { | 179 | if (state.isConnected()) { |
| 175 | - iceState = IceState.CONNECTED | ||
| 176 | - } else if (state == PeerConnection.IceConnectionState.DISCONNECTED || | ||
| 177 | - state == PeerConnection.IceConnectionState.FAILED | ||
| 178 | - ) { | ||
| 179 | - // when we publish tracks, some WebRTC versions will send out disconnected events periodically | ||
| 180 | - iceState = IceState.DISCONNECTED | 180 | + connectionState = ConnectionState.CONNECTED |
| 181 | + } else if (state.isDisconnected()) { | ||
| 182 | + connectionState = ConnectionState.DISCONNECTED | ||
| 181 | } | 183 | } |
| 182 | } | 184 | } |
| 183 | 185 | ||
| @@ -191,9 +193,10 @@ internal constructor( | @@ -191,9 +193,10 @@ internal constructor( | ||
| 191 | } | 193 | } |
| 192 | dataChannel.registerObserver(this) | 194 | dataChannel.registerObserver(this) |
| 193 | } | 195 | } |
| 194 | - subscriberObserver.iceConnectionChangeListener = iceConnectionStateListener | 196 | + |
| 197 | + subscriberObserver.connectionChangeListener = connectionStateListener | ||
| 195 | } else { | 198 | } else { |
| 196 | - publisherObserver.iceConnectionChangeListener = iceConnectionStateListener | 199 | + publisherObserver.connectionChangeListener = connectionStateListener |
| 197 | } | 200 | } |
| 198 | 201 | ||
| 199 | // data channels | 202 | // data channels |
| @@ -270,7 +273,7 @@ internal constructor( | @@ -270,7 +273,7 @@ internal constructor( | ||
| 270 | } | 273 | } |
| 271 | 274 | ||
| 272 | val job = coroutineScope.launch { | 275 | val job = coroutineScope.launch { |
| 273 | - listener?.onReconnecting() | 276 | + listener?.onEngineReconnecting() |
| 274 | 277 | ||
| 275 | for (wsRetries in 0 until MAX_SIGNAL_RETRIES) { | 278 | for (wsRetries in 0 until MAX_SIGNAL_RETRIES) { |
| 276 | var startDelay = wsRetries.toLong() * wsRetries * 500 | 279 | var startDelay = wsRetries.toLong() * wsRetries * 500 |
| @@ -292,7 +295,7 @@ internal constructor( | @@ -292,7 +295,7 @@ internal constructor( | ||
| 292 | listener?.onSignalConnected() | 295 | listener?.onSignalConnected() |
| 293 | 296 | ||
| 294 | subscriber.prepareForIceRestart() | 297 | subscriber.prepareForIceRestart() |
| 295 | - iceState = IceState.RECONNECTING | 298 | + connectionState = ConnectionState.RECONNECTING |
| 296 | // trigger publisher reconnect | 299 | // trigger publisher reconnect |
| 297 | // only restart publisher if it's needed | 300 | // only restart publisher if it's needed |
| 298 | if (hasPublished) { | 301 | if (hasPublished) { |
| @@ -302,20 +305,20 @@ internal constructor( | @@ -302,20 +305,20 @@ internal constructor( | ||
| 302 | // wait until ICE connected | 305 | // wait until ICE connected |
| 303 | val endTime = SystemClock.elapsedRealtime() + MAX_ICE_CONNECT_TIMEOUT_MS; | 306 | val endTime = SystemClock.elapsedRealtime() + MAX_ICE_CONNECT_TIMEOUT_MS; |
| 304 | while (SystemClock.elapsedRealtime() < endTime) { | 307 | while (SystemClock.elapsedRealtime() < endTime) { |
| 305 | - if (iceState == IceState.CONNECTED) { | 308 | + if (connectionState == ConnectionState.CONNECTED) { |
| 306 | LKLog.v { "reconnected to ICE" } | 309 | LKLog.v { "reconnected to ICE" } |
| 307 | break | 310 | break |
| 308 | } | 311 | } |
| 309 | delay(100) | 312 | delay(100) |
| 310 | } | 313 | } |
| 311 | 314 | ||
| 312 | - if (iceState == IceState.CONNECTED) { | 315 | + if (connectionState == ConnectionState.CONNECTED) { |
| 313 | return@launch | 316 | return@launch |
| 314 | } | 317 | } |
| 315 | } | 318 | } |
| 316 | 319 | ||
| 317 | 320 | ||
| 318 | - listener?.onDisconnect("failed reconnecting.") | 321 | + listener?.onEngineDisconnected("failed reconnecting.") |
| 319 | close() | 322 | close() |
| 320 | } | 323 | } |
| 321 | 324 | ||
| @@ -409,7 +412,7 @@ internal constructor( | @@ -409,7 +412,7 @@ internal constructor( | ||
| 409 | MediaConstraintKeys.FALSE | 412 | MediaConstraintKeys.FALSE |
| 410 | ) | 413 | ) |
| 411 | ) | 414 | ) |
| 412 | - if (iceState == IceState.RECONNECTING) { | 415 | + if (connectionState == ConnectionState.RECONNECTING) { |
| 413 | add( | 416 | add( |
| 414 | MediaConstraints.KeyValuePair( | 417 | MediaConstraints.KeyValuePair( |
| 415 | MediaConstraintKeys.ICE_RESTART, | 418 | MediaConstraintKeys.ICE_RESTART, |
| @@ -422,8 +425,11 @@ internal constructor( | @@ -422,8 +425,11 @@ internal constructor( | ||
| 422 | } | 425 | } |
| 423 | 426 | ||
| 424 | internal interface Listener { | 427 | internal interface Listener { |
| 425 | - fun onIceConnected() | ||
| 426 | - fun onIceReconnected() | 428 | + fun onEngineConnected() |
| 429 | + fun onEngineReconnected() | ||
| 430 | + fun onEngineReconnecting() | ||
| 431 | + fun onEngineDisconnected(reason: String) | ||
| 432 | + fun onFailToConnect(error: Throwable) | ||
| 427 | fun onAddTrack(track: MediaStreamTrack, streams: Array<out MediaStream>) | 433 | fun onAddTrack(track: MediaStreamTrack, streams: Array<out MediaStream>) |
| 428 | fun onUpdateParticipants(updates: List<LivekitModels.ParticipantInfo>) | 434 | fun onUpdateParticipants(updates: List<LivekitModels.ParticipantInfo>) |
| 429 | fun onActiveSpeakersUpdate(speakers: List<LivekitModels.SpeakerInfo>) | 435 | fun onActiveSpeakersUpdate(speakers: List<LivekitModels.SpeakerInfo>) |
| @@ -431,14 +437,11 @@ internal constructor( | @@ -431,14 +437,11 @@ internal constructor( | ||
| 431 | fun onRoomUpdate(update: LivekitModels.Room) | 437 | fun onRoomUpdate(update: LivekitModels.Room) |
| 432 | fun onConnectionQuality(updates: List<LivekitRtc.ConnectionQualityInfo>) | 438 | fun onConnectionQuality(updates: List<LivekitRtc.ConnectionQualityInfo>) |
| 433 | fun onSpeakersChanged(speakers: List<LivekitModels.SpeakerInfo>) | 439 | fun onSpeakersChanged(speakers: List<LivekitModels.SpeakerInfo>) |
| 434 | - fun onDisconnect(reason: String) | ||
| 435 | - fun onFailToConnect(error: Throwable) | ||
| 436 | fun onUserPacket(packet: LivekitModels.UserPacket, kind: LivekitModels.DataPacket.Kind) | 440 | fun onUserPacket(packet: LivekitModels.UserPacket, kind: LivekitModels.DataPacket.Kind) |
| 437 | fun onStreamStateUpdate(streamStates: List<LivekitRtc.StreamStateInfo>) | 441 | fun onStreamStateUpdate(streamStates: List<LivekitRtc.StreamStateInfo>) |
| 438 | fun onSubscribedQualityUpdate(subscribedQualityUpdate: LivekitRtc.SubscribedQualityUpdate) | 442 | fun onSubscribedQualityUpdate(subscribedQualityUpdate: LivekitRtc.SubscribedQualityUpdate) |
| 439 | fun onSubscriptionPermissionUpdate(subscriptionPermissionUpdate: LivekitRtc.SubscriptionPermissionUpdate) | 443 | fun onSubscriptionPermissionUpdate(subscriptionPermissionUpdate: LivekitRtc.SubscriptionPermissionUpdate) |
| 440 | fun onSignalConnected() | 444 | fun onSignalConnected() |
| 441 | - fun onReconnecting() | ||
| 442 | } | 445 | } |
| 443 | 446 | ||
| 444 | companion object { | 447 | companion object { |
| @@ -564,7 +567,7 @@ internal constructor( | @@ -564,7 +567,7 @@ internal constructor( | ||
| 564 | 567 | ||
| 565 | override fun onLeave(leave: LivekitRtc.LeaveRequest) { | 568 | override fun onLeave(leave: LivekitRtc.LeaveRequest) { |
| 566 | close() | 569 | close() |
| 567 | - listener?.onDisconnect("server leave") | 570 | + listener?.onEngineDisconnected("server leave") |
| 568 | } | 571 | } |
| 569 | 572 | ||
| 570 | // Signal error | 573 | // Signal error |
| @@ -627,10 +630,4 @@ internal constructor( | @@ -627,10 +630,4 @@ internal constructor( | ||
| 627 | 630 | ||
| 628 | client.sendSyncState(syncState) | 631 | client.sendSyncState(syncState) |
| 629 | } | 632 | } |
| 630 | -} | ||
| 631 | - | ||
| 632 | -internal enum class IceState { | ||
| 633 | - DISCONNECTED, | ||
| 634 | - RECONNECTING, | ||
| 635 | - CONNECTED, | ||
| 636 | } | 633 | } |
| @@ -419,17 +419,17 @@ constructor( | @@ -419,17 +419,17 @@ constructor( | ||
| 419 | 419 | ||
| 420 | 420 | ||
| 421 | //----------------------------------- RTCEngine.Listener ------------------------------------// | 421 | //----------------------------------- RTCEngine.Listener ------------------------------------// |
| 422 | - override fun onIceConnected() { | 422 | + override fun onEngineConnected() { |
| 423 | state = State.CONNECTED | 423 | state = State.CONNECTED |
| 424 | } | 424 | } |
| 425 | 425 | ||
| 426 | - override fun onIceReconnected() { | 426 | + override fun onEngineReconnected() { |
| 427 | state = State.CONNECTED | 427 | state = State.CONNECTED |
| 428 | listener?.onReconnected(this) | 428 | listener?.onReconnected(this) |
| 429 | eventBus.postEvent(RoomEvent.Reconnected(this), coroutineScope) | 429 | eventBus.postEvent(RoomEvent.Reconnected(this), coroutineScope) |
| 430 | } | 430 | } |
| 431 | 431 | ||
| 432 | - override fun onReconnecting() { | 432 | + override fun onEngineReconnecting() { |
| 433 | state = State.RECONNECTING | 433 | state = State.RECONNECTING |
| 434 | listener?.onReconnecting(this) | 434 | listener?.onReconnecting(this) |
| 435 | eventBus.postEvent(RoomEvent.Reconnecting(this), coroutineScope) | 435 | eventBus.postEvent(RoomEvent.Reconnecting(this), coroutineScope) |
| @@ -546,7 +546,7 @@ constructor( | @@ -546,7 +546,7 @@ constructor( | ||
| 546 | /** | 546 | /** |
| 547 | * @suppress | 547 | * @suppress |
| 548 | */ | 548 | */ |
| 549 | - override fun onDisconnect(reason: String) { | 549 | + override fun onEngineDisconnected(reason: String) { |
| 550 | LKLog.v { "engine did disconnect: $reason" } | 550 | LKLog.v { "engine did disconnect: $reason" } |
| 551 | handleDisconnect() | 551 | handleDisconnect() |
| 552 | } | 552 | } |
| @@ -46,7 +46,7 @@ constructor( | @@ -46,7 +46,7 @@ constructor( | ||
| 46 | @Named(InjectionNames.SIGNAL_JSON_ENABLED) | 46 | @Named(InjectionNames.SIGNAL_JSON_ENABLED) |
| 47 | private val useJson: Boolean, | 47 | private val useJson: Boolean, |
| 48 | @Named(InjectionNames.DISPATCHER_IO) | 48 | @Named(InjectionNames.DISPATCHER_IO) |
| 49 | - ioDispatcher: CoroutineDispatcher, | 49 | + private val ioDispatcher: CoroutineDispatcher, |
| 50 | ) : WebSocketListener() { | 50 | ) : WebSocketListener() { |
| 51 | var isConnected = false | 51 | var isConnected = false |
| 52 | private set | 52 | private set |
| @@ -57,7 +57,7 @@ constructor( | @@ -57,7 +57,7 @@ constructor( | ||
| 57 | private var lastUrl: String? = null | 57 | private var lastUrl: String? = null |
| 58 | 58 | ||
| 59 | private var joinContinuation: CancellableContinuation<Either<LivekitRtc.JoinResponse, Unit>>? = null | 59 | private var joinContinuation: CancellableContinuation<Either<LivekitRtc.JoinResponse, Unit>>? = null |
| 60 | - private val coroutineScope = CloseableCoroutineScope(SupervisorJob() + ioDispatcher) | 60 | + private lateinit var coroutineScope: CloseableCoroutineScope |
| 61 | 61 | ||
| 62 | private val responseFlow = MutableSharedFlow<LivekitRtc.SignalResponse>(Int.MAX_VALUE) | 62 | private val responseFlow = MutableSharedFlow<LivekitRtc.SignalResponse>(Int.MAX_VALUE) |
| 63 | 63 | ||
| @@ -109,13 +109,10 @@ constructor( | @@ -109,13 +109,10 @@ constructor( | ||
| 109 | 109 | ||
| 110 | LKLog.i { "connecting to $wsUrlString" } | 110 | LKLog.i { "connecting to $wsUrlString" } |
| 111 | 111 | ||
| 112 | - isConnected = false | ||
| 113 | - currentWs?.cancel() | ||
| 114 | - currentWs = null | ||
| 115 | - | ||
| 116 | - joinContinuation?.cancel() | ||
| 117 | - joinContinuation = null | 112 | + // Clean up any pre-existing connection. |
| 113 | + close() | ||
| 118 | 114 | ||
| 115 | + coroutineScope = CloseableCoroutineScope(SupervisorJob() + ioDispatcher) | ||
| 119 | lastUrl = wsUrlString | 116 | lastUrl = wsUrlString |
| 120 | 117 | ||
| 121 | val request = Request.Builder() | 118 | val request = Request.Builder() |
| @@ -142,6 +139,7 @@ constructor( | @@ -142,6 +139,7 @@ constructor( | ||
| 142 | //--------------------------------- WebSocket Listener --------------------------------------// | 139 | //--------------------------------- WebSocket Listener --------------------------------------// |
| 143 | override fun onOpen(webSocket: WebSocket, response: Response) { | 140 | override fun onOpen(webSocket: WebSocket, response: Response) { |
| 144 | if (isReconnecting) { | 141 | if (isReconnecting) { |
| 142 | + // no need to wait for join response on reconnection. | ||
| 145 | isReconnecting = false | 143 | isReconnecting = false |
| 146 | isConnected = true | 144 | isConnected = true |
| 147 | joinContinuation?.resumeWith(Result.success(Either.Right(Unit))) | 145 | joinContinuation?.resumeWith(Result.success(Either.Right(Unit))) |
| @@ -482,10 +480,15 @@ constructor( | @@ -482,10 +480,15 @@ constructor( | ||
| 482 | }.safe() | 480 | }.safe() |
| 483 | } | 481 | } |
| 484 | 482 | ||
| 485 | - fun close() { | 483 | + fun close(code: Int = 1000, reason: String = "Normal Closure") { |
| 486 | isConnected = false | 484 | isConnected = false |
| 487 | - coroutineScope.close() | ||
| 488 | - currentWs?.close(1000, "Normal Closure") | 485 | + if(::coroutineScope.isInitialized) { |
| 486 | + coroutineScope.close() | ||
| 487 | + } | ||
| 488 | + currentWs?.close(code, reason) | ||
| 489 | + currentWs = null | ||
| 490 | + joinContinuation?.cancel() | ||
| 491 | + joinContinuation = null | ||
| 489 | } | 492 | } |
| 490 | 493 | ||
| 491 | interface Listener { | 494 | interface Listener { |
| @@ -13,7 +13,7 @@ class SubscriberTransportObserver( | @@ -13,7 +13,7 @@ class SubscriberTransportObserver( | ||
| 13 | ) : PeerConnection.Observer { | 13 | ) : PeerConnection.Observer { |
| 14 | 14 | ||
| 15 | var dataChannelListener: ((DataChannel) -> Unit)? = null | 15 | var dataChannelListener: ((DataChannel) -> Unit)? = null |
| 16 | - var iceConnectionChangeListener: ((PeerConnection.IceConnectionState?) -> Unit)? = null | 16 | + var connectionChangeListener: ((PeerConnection.PeerConnectionState?) -> Unit)? = null |
| 17 | 17 | ||
| 18 | override fun onIceCandidate(candidate: IceCandidate) { | 18 | override fun onIceCandidate(candidate: IceCandidate) { |
| 19 | LKLog.v { "onIceCandidate: $candidate" } | 19 | LKLog.v { "onIceCandidate: $candidate" } |
| @@ -43,6 +43,7 @@ class SubscriberTransportObserver( | @@ -43,6 +43,7 @@ class SubscriberTransportObserver( | ||
| 43 | 43 | ||
| 44 | override fun onConnectionChange(newState: PeerConnection.PeerConnectionState?) { | 44 | override fun onConnectionChange(newState: PeerConnection.PeerConnectionState?) { |
| 45 | LKLog.v { "onConnectionChange new state: $newState" } | 45 | LKLog.v { "onConnectionChange new state: $newState" } |
| 46 | + connectionChangeListener?.invoke(newState) | ||
| 46 | } | 47 | } |
| 47 | 48 | ||
| 48 | override fun onSelectedCandidatePairChanged(event: CandidatePairChangeEvent?) { | 49 | override fun onSelectedCandidatePairChanged(event: CandidatePairChangeEvent?) { |
| @@ -53,7 +54,6 @@ class SubscriberTransportObserver( | @@ -53,7 +54,6 @@ class SubscriberTransportObserver( | ||
| 53 | 54 | ||
| 54 | override fun onIceConnectionChange(newState: PeerConnection.IceConnectionState?) { | 55 | override fun onIceConnectionChange(newState: PeerConnection.IceConnectionState?) { |
| 55 | LKLog.v { "onIceConnection new state: $newState" } | 56 | LKLog.v { "onIceConnection new state: $newState" } |
| 56 | - iceConnectionChangeListener?.invoke(newState) | ||
| 57 | } | 57 | } |
| 58 | 58 | ||
| 59 | override fun onIceConnectionReceivingChange(p0: Boolean) { | 59 | override fun onIceConnectionReceivingChange(p0: Boolean) { |
| @@ -6,13 +6,22 @@ import org.webrtc.PeerConnection | @@ -6,13 +6,22 @@ import org.webrtc.PeerConnection | ||
| 6 | * Completed state is a valid state for a connected connection, so this should be used | 6 | * Completed state is a valid state for a connected connection, so this should be used |
| 7 | * when checking for a connected state | 7 | * when checking for a connected state |
| 8 | */ | 8 | */ |
| 9 | -internal fun PeerConnection.isConnected(): Boolean = iceConnectionState().isConnected() | 9 | +internal fun PeerConnection.isConnected(): Boolean = connectionState().isConnected() |
| 10 | 10 | ||
| 11 | -internal fun PeerConnection.IceConnectionState.isConnected(): Boolean { | 11 | +internal fun PeerConnection.isDisconnected(): Boolean = connectionState().isDisconnected() |
| 12 | + | ||
| 13 | +internal fun PeerConnection.PeerConnectionState.isConnected(): Boolean { | ||
| 14 | + return this == PeerConnection.PeerConnectionState.CONNECTED | ||
| 15 | +} | ||
| 16 | + | ||
| 17 | +internal fun PeerConnection.PeerConnectionState.isDisconnected(): Boolean { | ||
| 12 | return when (this) { | 18 | return when (this) { |
| 13 | - PeerConnection.IceConnectionState.CONNECTED, | ||
| 14 | - PeerConnection.IceConnectionState.COMPLETED -> true | 19 | + /** |
| 20 | + * [PeerConnection.PeerConnectionState.DISCONNECTED] is explicitly not included here, | ||
| 21 | + * as that is a temporary state and may return to connected state by itself. | ||
| 22 | + */ | ||
| 23 | + PeerConnection.PeerConnectionState.FAILED, | ||
| 24 | + PeerConnection.PeerConnectionState.CLOSED -> true | ||
| 15 | else -> false | 25 | else -> false |
| 16 | } | 26 | } |
| 17 | } | 27 | } |
| 18 | - |
| @@ -29,7 +29,7 @@ abstract class MockE2ETest { | @@ -29,7 +29,7 @@ abstract class MockE2ETest { | ||
| 29 | @get:Rule | 29 | @get:Rule |
| 30 | var coroutineRule = TestCoroutineRule() | 30 | var coroutineRule = TestCoroutineRule() |
| 31 | 31 | ||
| 32 | - lateinit var component: TestLiveKitComponent | 32 | + internal lateinit var component: TestLiveKitComponent |
| 33 | lateinit var context: Context | 33 | lateinit var context: Context |
| 34 | lateinit var room: Room | 34 | lateinit var room: Room |
| 35 | lateinit var wsFactory: MockWebSocketFactory | 35 | lateinit var wsFactory: MockWebSocketFactory |
| @@ -10,6 +10,7 @@ class MockPeerConnection( | @@ -10,6 +10,7 @@ class MockPeerConnection( | ||
| 10 | val observer: PeerConnection.Observer? | 10 | val observer: PeerConnection.Observer? |
| 11 | ) : PeerConnection(MockNativePeerConnectionFactory()) { | 11 | ) : PeerConnection(MockNativePeerConnectionFactory()) { |
| 12 | 12 | ||
| 13 | + private var closed = false | ||
| 13 | var localDesc: SessionDescription? = null | 14 | var localDesc: SessionDescription? = null |
| 14 | var remoteDesc: SessionDescription? = null | 15 | var remoteDesc: SessionDescription? = null |
| 15 | override fun getLocalDescription(): SessionDescription? = localDesc | 16 | override fun getLocalDescription(): SessionDescription? = localDesc |
| @@ -140,7 +141,27 @@ class MockPeerConnection( | @@ -140,7 +141,27 @@ class MockPeerConnection( | ||
| 140 | } | 141 | } |
| 141 | 142 | ||
| 142 | override fun signalingState(): SignalingState { | 143 | override fun signalingState(): SignalingState { |
| 143 | - return super.signalingState() | 144 | + if (closed) { |
| 145 | + return SignalingState.CLOSED | ||
| 146 | + } | ||
| 147 | + | ||
| 148 | + if ((localDesc?.type == null && localDesc?.type == null) || | ||
| 149 | + (localDesc?.type == SessionDescription.Type.OFFER && | ||
| 150 | + remoteDesc?.type == SessionDescription.Type.ANSWER) || | ||
| 151 | + (localDesc?.type == SessionDescription.Type.ANSWER && | ||
| 152 | + remoteDesc?.type == SessionDescription.Type.OFFER) | ||
| 153 | + ) { | ||
| 154 | + return SignalingState.STABLE | ||
| 155 | + } | ||
| 156 | + | ||
| 157 | + if (localDesc?.type == SessionDescription.Type.OFFER && remoteDesc?.type == null) { | ||
| 158 | + return SignalingState.HAVE_LOCAL_OFFER | ||
| 159 | + } | ||
| 160 | + if (remoteDesc?.type == SessionDescription.Type.OFFER && localDesc?.type == null) { | ||
| 161 | + return SignalingState.HAVE_REMOTE_OFFER | ||
| 162 | + } | ||
| 163 | + | ||
| 164 | + throw IllegalStateException("Illegal signalling state? localDesc: $localDesc, remoteDesc: $remoteDesc") | ||
| 144 | } | 165 | } |
| 145 | 166 | ||
| 146 | private var iceConnectionState = IceConnectionState.NEW | 167 | private var iceConnectionState = IceConnectionState.NEW |
| @@ -148,12 +169,34 @@ class MockPeerConnection( | @@ -148,12 +169,34 @@ class MockPeerConnection( | ||
| 148 | if (field != value) { | 169 | if (field != value) { |
| 149 | field = value | 170 | field = value |
| 150 | observer?.onIceConnectionChange(field) | 171 | observer?.onIceConnectionChange(field) |
| 172 | + | ||
| 173 | + connectionState = when (field) { | ||
| 174 | + IceConnectionState.NEW -> PeerConnectionState.NEW | ||
| 175 | + IceConnectionState.CHECKING -> PeerConnectionState.CONNECTING | ||
| 176 | + IceConnectionState.CONNECTED, | ||
| 177 | + IceConnectionState.COMPLETED -> PeerConnectionState.CONNECTED | ||
| 178 | + IceConnectionState.DISCONNECTED -> PeerConnectionState.DISCONNECTED | ||
| 179 | + IceConnectionState.FAILED -> PeerConnectionState.FAILED | ||
| 180 | + IceConnectionState.CLOSED -> PeerConnectionState.CLOSED | ||
| 181 | + } | ||
| 182 | + } | ||
| 183 | + } | ||
| 184 | + | ||
| 185 | + private var connectionState = PeerConnectionState.NEW | ||
| 186 | + set(value) { | ||
| 187 | + if (field != value) { | ||
| 188 | + field = value | ||
| 189 | + observer?.onConnectionChange(field) | ||
| 151 | } | 190 | } |
| 152 | } | 191 | } |
| 153 | 192 | ||
| 154 | override fun iceConnectionState(): IceConnectionState = iceConnectionState | 193 | override fun iceConnectionState(): IceConnectionState = iceConnectionState |
| 155 | 194 | ||
| 156 | fun moveToIceConnectionState(newState: IceConnectionState) { | 195 | fun moveToIceConnectionState(newState: IceConnectionState) { |
| 196 | + if (closed && newState != IceConnectionState.CLOSED) { | ||
| 197 | + throw IllegalArgumentException("peer connection closed, but attempting to move to $newState") | ||
| 198 | + } | ||
| 199 | + | ||
| 157 | when (newState) { | 200 | when (newState) { |
| 158 | IceConnectionState.NEW, | 201 | IceConnectionState.NEW, |
| 159 | IceConnectionState.CHECKING, | 202 | IceConnectionState.CHECKING, |
| @@ -189,9 +232,12 @@ class MockPeerConnection( | @@ -189,9 +232,12 @@ class MockPeerConnection( | ||
| 189 | } | 232 | } |
| 190 | 233 | ||
| 191 | override fun close() { | 234 | override fun close() { |
| 235 | + dispose() | ||
| 192 | } | 236 | } |
| 193 | 237 | ||
| 194 | override fun dispose() { | 238 | override fun dispose() { |
| 239 | + iceConnectionState = IceConnectionState.CLOSED | ||
| 240 | + closed = true | ||
| 195 | } | 241 | } |
| 196 | 242 | ||
| 197 | override fun getNativePeerConnection(): Long = 0L | 243 | override fun getNativePeerConnection(): Long = 0L |
| @@ -55,7 +55,7 @@ class RTCEngineMockE2ETest : MockE2ETest() { | @@ -55,7 +55,7 @@ class RTCEngineMockE2ETest : MockE2ETest() { | ||
| 55 | 55 | ||
| 56 | subPeerConnection.moveToIceConnectionState(PeerConnection.IceConnectionState.CONNECTED) | 56 | subPeerConnection.moveToIceConnectionState(PeerConnection.IceConnectionState.CONNECTED) |
| 57 | 57 | ||
| 58 | - Assert.assertEquals(IceState.CONNECTED, rtcEngine.iceState) | 58 | + Assert.assertEquals(ConnectionState.CONNECTED, rtcEngine.connectionState) |
| 59 | } | 59 | } |
| 60 | 60 | ||
| 61 | @Test | 61 | @Test |
| @@ -104,7 +104,7 @@ class RoomTest { | @@ -104,7 +104,7 @@ class RoomTest { | ||
| 104 | connect() | 104 | connect() |
| 105 | 105 | ||
| 106 | val eventCollector = EventCollector(room.events, coroutineRule.scope) | 106 | val eventCollector = EventCollector(room.events, coroutineRule.scope) |
| 107 | - room.onDisconnect("") | 107 | + room.onEngineDisconnected("") |
| 108 | val events = eventCollector.stopCollecting() | 108 | val events = eventCollector.stopCollecting() |
| 109 | 109 | ||
| 110 | Assert.assertEquals(1, events.size) | 110 | Assert.assertEquals(1, events.size) |
-
请 注册 或 登录 后发表评论