refactor: adding reconnecting logic and visuals

This commit is contained in:
jubb 2022-03-03 17:03:32 +11:00
parent 573f0930df
commit a90bd89c9a
6 changed files with 47 additions and 22 deletions

View file

@ -40,6 +40,7 @@ import org.thoughtcrime.securesms.webrtc.CallViewModel.State.CALL_CONNECTED
import org.thoughtcrime.securesms.webrtc.CallViewModel.State.CALL_INCOMING import org.thoughtcrime.securesms.webrtc.CallViewModel.State.CALL_INCOMING
import org.thoughtcrime.securesms.webrtc.CallViewModel.State.CALL_OUTGOING import org.thoughtcrime.securesms.webrtc.CallViewModel.State.CALL_OUTGOING
import org.thoughtcrime.securesms.webrtc.CallViewModel.State.CALL_PRE_INIT import org.thoughtcrime.securesms.webrtc.CallViewModel.State.CALL_PRE_INIT
import org.thoughtcrime.securesms.webrtc.CallViewModel.State.CALL_RECONNECTING
import org.thoughtcrime.securesms.webrtc.CallViewModel.State.CALL_RINGING import org.thoughtcrime.securesms.webrtc.CallViewModel.State.CALL_RINGING
import org.thoughtcrime.securesms.webrtc.audio.SignalAudioManager.AudioDevice.EARPIECE import org.thoughtcrime.securesms.webrtc.audio.SignalAudioManager.AudioDevice.EARPIECE
import org.thoughtcrime.securesms.webrtc.audio.SignalAudioManager.AudioDevice.SPEAKER_PHONE import org.thoughtcrime.securesms.webrtc.audio.SignalAudioManager.AudioDevice.SPEAKER_PHONE
@ -223,6 +224,8 @@ class WebRtcCallActivity : PassphraseRequiredActionBarActivity() {
state !in listOf(CALL_CONNECTED, CALL_RINGING, CALL_PRE_INIT) || wantsToAnswer state !in listOf(CALL_CONNECTED, CALL_RINGING, CALL_PRE_INIT) || wantsToAnswer
incomingControlGroup.isVisible = incomingControlGroup.isVisible =
state in listOf(CALL_RINGING, CALL_PRE_INIT) && !wantsToAnswer state in listOf(CALL_RINGING, CALL_PRE_INIT) && !wantsToAnswer
reconnectingText.isVisible = state == CALL_RECONNECTING
endCallButton.isVisible = endCallButton.isVisible || state == CALL_RECONNECTING
} }
} }
} }

View file

@ -51,6 +51,7 @@ import org.webrtc.SessionDescription
import java.util.UUID import java.util.UUID
import java.util.concurrent.ExecutionException import java.util.concurrent.ExecutionException
import java.util.concurrent.Executors import java.util.concurrent.Executors
import java.util.concurrent.ScheduledFuture
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import javax.inject.Inject import javax.inject.Inject
import org.thoughtcrime.securesms.webrtc.data.State as CallState import org.thoughtcrime.securesms.webrtc.data.State as CallState
@ -101,7 +102,8 @@ class WebRtcCallService: Service(), CallManager.WebRtcListener {
const val INVALID_NOTIFICATION_ID = -1 const val INVALID_NOTIFICATION_ID = -1
private const val TIMEOUT_SECONDS = 90L private const val TIMEOUT_SECONDS = 90L
private const val MAX_TIMEOUTS = 3 private const val RECONNECT_SECONDS = 5L
private const val MAX_RECONNECTS = 5
fun cameraEnabled(context: Context, enabled: Boolean) = Intent(context, WebRtcCallService::class.java) fun cameraEnabled(context: Context, enabled: Boolean) = Intent(context, WebRtcCallService::class.java)
.setAction(ACTION_SET_MUTE_VIDEO) .setAction(ACTION_SET_MUTE_VIDEO)
@ -188,6 +190,9 @@ class WebRtcCallService: Service(), CallManager.WebRtcListener {
private var wantsToAnswer = false private var wantsToAnswer = false
private var currentTimeouts = 0 private var currentTimeouts = 0
private var isNetworkAvailable = true private var isNetworkAvailable = true
private var scheduledTimeout: ScheduledFuture<*>? = null
private var scheduledReconnectTimeout: ScheduledFuture<*>? = null
private var scheduledReconnect: ScheduledFuture<*>? = null
private val lockManager by lazy { LockManager(this) } private val lockManager by lazy { LockManager(this) }
private val serviceExecutor = Executors.newSingleThreadExecutor() private val serviceExecutor = Executors.newSingleThreadExecutor()
@ -407,7 +412,7 @@ class WebRtcCallService: Service(), CallManager.WebRtcListener {
callManager.startOutgoingRinger(OutgoingRinger.Type.RINGING) callManager.startOutgoingRinger(OutgoingRinger.Type.RINGING)
setCallInProgressNotification(TYPE_OUTGOING_RINGING, callManager.recipient) setCallInProgressNotification(TYPE_OUTGOING_RINGING, callManager.recipient)
callManager.insertCallMessage(recipient.address.serialize(), CallMessageType.CALL_OUTGOING) callManager.insertCallMessage(recipient.address.serialize(), CallMessageType.CALL_OUTGOING)
timeoutExecutor.schedule(TimeoutRunnable(callId, this), TIMEOUT_SECONDS, TimeUnit.SECONDS) scheduledTimeout = timeoutExecutor.schedule(TimeoutRunnable(callId, this), TIMEOUT_SECONDS, TimeUnit.SECONDS)
callManager.setAudioEnabled(true) callManager.setAudioEnabled(true)
val expectedState = callManager.currentConnectionState val expectedState = callManager.currentConnectionState
@ -463,7 +468,7 @@ class WebRtcCallService: Service(), CallManager.WebRtcListener {
callManager.silenceIncomingRinger() callManager.silenceIncomingRinger()
callManager.postViewModelState(CallViewModel.State.CALL_INCOMING) callManager.postViewModelState(CallViewModel.State.CALL_INCOMING)
timeoutExecutor.schedule(TimeoutRunnable(callId, this), TIMEOUT_SECONDS, TimeUnit.SECONDS) scheduledTimeout = timeoutExecutor.schedule(TimeoutRunnable(callId, this), TIMEOUT_SECONDS, TimeUnit.SECONDS)
callManager.initializeAudioForCall() callManager.initializeAudioForCall()
callManager.initializeVideo(this) callManager.initializeVideo(this)
@ -598,8 +603,13 @@ class WebRtcCallService: Service(), CallManager.WebRtcListener {
val callId = callManager.callId ?: return val callId = callManager.callId ?: return
val numTimeouts = ++currentTimeouts val numTimeouts = ++currentTimeouts
if (callId == getCallId(intent) && isNetworkAvailable && numTimeouts <= 5) { if (callId == getCallId(intent) && isNetworkAvailable && numTimeouts <= MAX_RECONNECTS) {
Log.d("Loki", "Trying to re-connect")
callManager.networkReestablished() callManager.networkReestablished()
scheduledTimeout = timeoutExecutor.schedule(TimeoutRunnable(callId, this), TIMEOUT_SECONDS, TimeUnit.SECONDS)
} else {
Log.d("Loki", "Network isn't available, timeouts == $numTimeouts out of $MAX_RECONNECTS")
scheduledReconnect = timeoutExecutor.schedule(CheckReconnectedRunnable(callId, this), RECONNECT_SECONDS, TimeUnit.SECONDS)
} }
} }
@ -783,17 +793,22 @@ class WebRtcCallService: Service(), CallManager.WebRtcListener {
override fun onIceConnectionChange(newState: PeerConnection.IceConnectionState?) { override fun onIceConnectionChange(newState: PeerConnection.IceConnectionState?) {
if (newState == CONNECTED) { if (newState == CONNECTED) {
scheduledTimeout?.cancel(false)
scheduledReconnect?.cancel(false)
scheduledReconnectTimeout?.cancel(false)
scheduledTimeout = null
scheduledReconnect = null
scheduledReconnectTimeout = null
val intent = Intent(this, WebRtcCallService::class.java) val intent = Intent(this, WebRtcCallService::class.java)
.setAction(ACTION_ICE_CONNECTED) .setAction(ACTION_ICE_CONNECTED)
startService(intent) startService(intent)
} else if (newState == FAILED) { } else if (newState in arrayOf(FAILED, DISCONNECTED) && scheduledReconnectTimeout == null) {
val intent = hangupIntent(this)
startService(intent)
} else if (newState == DISCONNECTED) {
callManager.callId?.let { callId -> callManager.callId?.let { callId ->
callManager.postViewModelState(CallViewModel.State.CALL_RECONNECTING) callManager.postConnectionEvent(Event.IceDisconnect) {
timeoutExecutor.schedule(CheckReconnectedRunnable(callId, this), 5, TimeUnit.SECONDS) callManager.postViewModelState(CallViewModel.State.CALL_RECONNECTING)
timeoutExecutor.schedule(ReconnectTimeoutRunnable(callId, this), TIMEOUT_SECONDS, TimeUnit.SECONDS) scheduledReconnect = timeoutExecutor.schedule(CheckReconnectedRunnable(callId, this), RECONNECT_SECONDS, TimeUnit.SECONDS)
scheduledReconnectTimeout = timeoutExecutor.schedule(ReconnectTimeoutRunnable(callId, this), TIMEOUT_SECONDS, TimeUnit.SECONDS)
}
} ?: run { } ?: run {
val intent = hangupIntent(this) val intent = hangupIntent(this)
startService(intent) startService(intent)

View file

@ -675,17 +675,15 @@ class CallManager(context: Context, audioManager: AudioManagerCompat, private va
val callId = callId ?: return val callId = callId ?: return
val recipient = recipient ?: return val recipient = recipient ?: return
if (isReestablishing) return postConnectionEvent(Event.NetworkReconnect) {
isReestablishing = true Log.d("Loki", "start re-establish")
Log.d("Loki", "start re-establish")
val offer = connection.createOffer(MediaConstraints().apply { val offer = connection.createOffer(MediaConstraints().apply {
mandatory.add(MediaConstraints.KeyValuePair("IceRestart", "true")) mandatory.add(MediaConstraints.KeyValuePair("IceRestart", "true"))
}) })
connection.setLocalDescription(offer) connection.setLocalDescription(offer)
MessageSender.sendNonDurably(CallMessage.offer(offer.description, callId), recipient.address).success { MessageSender.sendNonDurably(CallMessage.offer(offer.description, callId), recipient.address)
isReestablishing = false
} }
} }

View file

@ -1,6 +1,5 @@
package org.thoughtcrime.securesms.webrtc.video package org.thoughtcrime.securesms.webrtc.video
import org.session.libsignal.utilities.Log
import org.thoughtcrime.securesms.webrtc.data.quadrantRotation import org.thoughtcrime.securesms.webrtc.data.quadrantRotation
import org.webrtc.VideoFrame import org.webrtc.VideoFrame
import org.webrtc.VideoSink import org.webrtc.VideoSink
@ -19,7 +18,6 @@ class RemoteRotationVideoProxySink: VideoSink {
val modifiedRotation = thisFrame.rotation - quadrantRotation val modifiedRotation = thisFrame.rotation - quadrantRotation
val newFrame = VideoFrame(thisFrame.buffer, modifiedRotation, thisFrame.timestampNs) val newFrame = VideoFrame(thisFrame.buffer, modifiedRotation, thisFrame.timestampNs)
Log.d("Loki", "sending frame: w=${newFrame.buffer.width}, h=${newFrame.buffer.height}, rw=${newFrame.rotatedWidth}, rh=${newFrame.rotatedHeight}, rot=${newFrame.rotation}")
thisSink.onFrame(newFrame) thisSink.onFrame(newFrame)
} }

View file

@ -46,6 +46,16 @@
app:layout_constraintTop_toBottomOf="@id/remote_recipient" app:layout_constraintTop_toBottomOf="@id/remote_recipient"
tools:visibility="visible" /> tools:visibility="visible" />
<TextView
android:visibility="gone"
app:layout_constraintTop_toBottomOf="@+id/remote_loading_view"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:text="@string/WebRtcCallActivity_Reconnecting"
android:id="@+id/reconnecting_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView <TextView
android:id="@+id/sessionCallText" android:id="@+id/sessionCallText"
android:layout_width="wrap_content" android:layout_width="wrap_content"

View file

@ -903,5 +903,6 @@
<string name="CallNotificationBuilder_first_call_title">Call Missed</string> <string name="CallNotificationBuilder_first_call_title">Call Missed</string>
<string name="CallNotificationBuilder_first_call_message">You missed a call because you need to enable the \'Voice and video calls\' permission in the Privacy Settings.</string> <string name="CallNotificationBuilder_first_call_message">You missed a call because you need to enable the \'Voice and video calls\' permission in the Privacy Settings.</string>
<string name="WebRtcCallActivity_Session_Call">Session Call</string> <string name="WebRtcCallActivity_Session_Call">Session Call</string>
<string name="WebRtcCallActivity_Reconnecting">Reconnecting…</string>
</resources> </resources>