Skip to content

Commit

Permalink
Reconnect v2 (#1219)
Browse files Browse the repository at this point in the history
* Initial socket from chat implementation

* Authenticate on connected in coordinator socket.

* Spotless

* Migrate `CoordinatorSocket`

* Spotless and API dump

* Fix an issue where StreamCallActivity would leave the call when going into background

* Initial reconnect V2 implementation

* API dump

* Spotless

* Correct JoinRequest

* [WIP] Refactored Coordinator and SFU sockets

* Update correct type for SfuSocketStateEvent

* Update state in Connection module, package renaming

* Resolve merge conflicts

* Continue refactoring

* Add connection common implementation

* Keep refactoring the SFU socket

* Merge conflicts

* Update

* Update

* Update

* Update

* Update

* Update

* Update

* Update

* Update

* Update

* Update

* Update

* Update

* Update

* Update

* Update

* Update

* Update

* Update

* Update

* Update

* Update

* Update

* Update

* Update

* Update

* Update

* Update

* Spotless & ApiDump

* Adapt for the new token provider

* Update tests

* Spotless apidump

* Update

* Sotless api dump

* Update

* Spotless

* Move call health monitor into call

* Spotless

* Update

* Revert "Update"

This reverts commit fdd64d2.

* Revert "Spotless"

This reverts commit e064aa9.

* Revert "Move call health monitor into call"

This reverts commit c0d260f.

* Revert "Spotless"

This reverts commit 0c445e3.

* Revert "Update"

This reverts commit 084bf88.

* Revert "Sotless api dump"

This reverts commit 0be905d.

* Revert "Update"

This reverts commit b73572c.

* Update

* Spotless

* Update

* Update

* Update

* Update

* Update

* Update

* Update

* Add timeout to the scheduled jobs

* Update tests

* Update tests

* Add missing dependencies

* Add javadoc, make some methods internal, remove myself when rejoin to speedup participant list update

* Add javadoc, make some methods internal, remove myself when rejoin to speedup participant list update

* Add javadoc, make some methods internal, remove myself when rejoin to speedup participant list update

* Rejoin if PC state is FAILED

* Try fast reconnect instead.

* Cancel previous job instead

* Synchronize the job handling process
  • Loading branch information
aleksandar-apostolov authored Nov 15, 2024
1 parent 738a7eb commit 6c06ce4
Show file tree
Hide file tree
Showing 112 changed files with 10,279 additions and 4,129 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ import io.getstream.video.android.ui.menu.availableVideoFilters
import io.getstream.video.android.util.config.AppConfig
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.launch
import org.openapitools.client.models.OwnCapability

Expand Down Expand Up @@ -331,33 +332,36 @@ fun CallScreen(
)
},
floatingVideoRenderer = { _, _ ->
FloatingParticipantVideo(
call = call,
participant = me!!,
parentBounds = IntSize(
this@BoxWithConstraints.constraints.maxWidth,
this@BoxWithConstraints.constraints.maxHeight,
),
videoRenderer = { participant ->
ParticipantVideo(
modifier = Modifier
.fillMaxSize()
.clip(VideoTheme.shapes.dialog),
call = call,
participant = participant,
reactionContent = {
CustomReactionContent(
participant = participant,
style = RegularVideoRendererStyle().copy(
isShowingConnectionQualityIndicator = false,
reactionPosition = Alignment.TopCenter,
reactionDuration = 5000,
),
)
},
)
},
)
val myself = me ?: participantsSize.firstOrNull { it.sessionId == call.sessionId }
myself?.let {
FloatingParticipantVideo(
call = call,
participant = it,
parentBounds = IntSize(
this@BoxWithConstraints.constraints.maxWidth,
this@BoxWithConstraints.constraints.maxHeight,
),
videoRenderer = { participant ->
ParticipantVideo(
modifier = Modifier
.fillMaxSize()
.clip(VideoTheme.shapes.dialog),
call = call,
participant = participant,
reactionContent = {
CustomReactionContent(
participant = participant,
style = RegularVideoRendererStyle().copy(
isShowingConnectionQualityIndicator = false,
reactionPosition = Alignment.TopCenter,
reactionDuration = 5000,
),
)
},
)
},
)
}
},
videoOverlayContent = {
Crossfade(
Expand Down Expand Up @@ -463,7 +467,8 @@ fun CallScreen(
mutableStateOf(call.isAudioProcessingEnabled())
}
val settings by call.state.settings.collectAsStateWithLifecycle()
val noiseCancellationFeatureEnabled = settings?.audio?.noiseCancellation?.isEnabled == true
val noiseCancellationFeatureEnabled =
settings?.audio?.noiseCancellation?.isEnabled == true
SettingsMenu(
call = call,
selectedVideoFilter = selectedVideoFilter,
Expand All @@ -478,11 +483,14 @@ fun CallScreen(
is VideoFilter.None -> {
call.videoFilter = null
}

is VideoFilter.BlurredBackground -> {
call.videoFilter = BlurredBackgroundVideoFilter()
}

is VideoFilter.VirtualBackground -> {
call.videoFilter = VirtualBackgroundVideoFilter(context, filter.drawable)
call.videoFilter =
VirtualBackgroundVideoFilter(context, filter.drawable)
}
}
},
Expand All @@ -493,10 +501,11 @@ fun CallScreen(
onNoiseCancellation = {
isNoiseCancellationEnabled = call.toggleAudioProcessing()
},
) {
isShowingStats = true
isShowingSettingMenu = false
}
onShowCallStats = {
isShowingStats = true
isShowingSettingMenu = false
},
)
}

if (isShowingFeedbackDialog) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,7 @@ private fun JoinCallForm(
mutableStateOf(
TextFieldValue(
if (BuildConfig.FLAVOR == StreamFlavors.development) {
"default:79cYh3J5JgGk"
"default:123lexgvg"
} else {
""
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@ import androidx.compose.material.icons.filled.BluetoothAudio
import androidx.compose.material.icons.filled.Feedback
import androidx.compose.material.icons.filled.Headphones
import androidx.compose.material.icons.filled.HeadsetMic
import androidx.compose.material.icons.filled.PortableWifiOff
import androidx.compose.material.icons.filled.Replay
import androidx.compose.material.icons.filled.RestartAlt
import androidx.compose.material.icons.filled.SettingsBackupRestore
import androidx.compose.material.icons.filled.SettingsVoice
import androidx.compose.material.icons.filled.SpatialAudioOff
import androidx.compose.material.icons.filled.SpeakerPhone
Expand Down Expand Up @@ -57,11 +58,12 @@ fun defaultStreamMenu(
onToggleAudioFilterClick: () -> Unit,
onRestartSubscriberIceClick: () -> Unit,
onRestartPublisherIceClick: () -> Unit,
onKillSfuWsClick: () -> Unit,
onSwitchSfuClick: () -> Unit,
onShowFeedback: () -> Unit,
onNoiseCancellation: () -> Unit,
onDeviceSelected: (StreamAudioDevice) -> Unit,
onSfuRejoinClick: () -> Unit,
onSfuFastReconnectClick: () -> Unit,
availableDevices: List<StreamAudioDevice>,
loadRecordings: suspend () -> List<MenuItem>,
) = buildList<MenuItem> {
Expand Down Expand Up @@ -133,8 +135,9 @@ fun defaultStreamMenu(
onToggleAudioFilterClick,
onRestartSubscriberIceClick,
onRestartPublisherIceClick,
onKillSfuWsClick,
onSwitchSfuClick,
onSfuRejoinClick,
onSfuFastReconnectClick,
),
),
)
Expand All @@ -159,17 +162,52 @@ fun codecMenu(codecList: List<MediaCodecInfo>, onCodecSelected: (MediaCodecInfo)
)
}

fun reconnectMenu(
onRestartPublisherIceClick: () -> Unit,
onRestartSubscriberIceClick: () -> Unit,
onSwitchSfuClick: () -> Unit,
onSfuRejoinClick: () -> Unit,
onSfuFastReconnectClick: () -> Unit,
) = listOf(
ActionMenuItem(
title = "Publisher - ICE restart",
icon = Icons.Default.SettingsBackupRestore,
action = onRestartPublisherIceClick,
),
ActionMenuItem(
title = "Subscriber - ICE restart",
icon = Icons.Default.SettingsBackupRestore,
action = onRestartSubscriberIceClick,
),
ActionMenuItem(
title = "Reconnect SFU - migrate",
icon = Icons.Default.SwitchLeft,
action = onSwitchSfuClick,
),
ActionMenuItem(
title = "Reconnect SFU - rejoin",
icon = Icons.Default.Replay,
action = onSfuRejoinClick,
),
ActionMenuItem(
title = "Reconnect SFU - fast",
icon = Icons.Default.RestartAlt,
action = onSfuFastReconnectClick,
),
)

/**
* Optionally defines the debug sub-menu of the demo app.
*/
fun debugSubmenu(
codecList: List<MediaCodecInfo>,
onCodecSelected: (MediaCodecInfo) -> Unit,
onToggleAudioFilterClick: () -> Unit,
onRestartSubscriberIceClick: () -> Unit,
onRestartPublisherIceClick: () -> Unit,
onKillSfuWsClick: () -> Unit,
onRestartSubscriberIceClick: () -> Unit,
onSwitchSfuClick: () -> Unit,
onSfuRejoinClick: () -> Unit,
onSfuFastReconnectClick: () -> Unit,
) = listOf(
SubMenuItem(
title = "Available video codecs",
Expand All @@ -181,24 +219,15 @@ fun debugSubmenu(
icon = Icons.Default.Audiotrack,
action = onToggleAudioFilterClick,
),
ActionMenuItem(
title = "Restart subscriber Ice",
icon = Icons.Default.RestartAlt,
action = onRestartSubscriberIceClick,
),
ActionMenuItem(
title = "Restart publisher Ice",
icon = Icons.Default.RestartAlt,
action = onRestartPublisherIceClick,
),
ActionMenuItem(
title = "Shut down SFU web-socket",
icon = Icons.Default.PortableWifiOff,
action = onKillSfuWsClick,
),
ActionMenuItem(
title = "Switch SFU",
icon = Icons.Default.SwitchLeft,
action = onSwitchSfuClick,
SubMenuItem(
title = "Reconnect V2",
icon = Icons.Default.Replay,
items = reconnectMenu(
onRestartPublisherIceClick,
onRestartSubscriberIceClick,
onSwitchSfuClick,
onSfuRejoinClick,
onSfuFastReconnectClick,
),
),
)
Original file line number Diff line number Diff line change
Expand Up @@ -135,19 +135,25 @@ internal fun SettingsMenu(
Toast.makeText(context, "Restart Publisher Ice", Toast.LENGTH_SHORT).show()
}

val onKillSfuWsClick: () -> Unit = {
call.debug.doFullReconnection()
val onSfuRejoinClick: () -> Unit = {
call.debug.rejoin()
onDismissed.invoke()
Toast.makeText(context, "Killing SFU WS. Should trigger reconnect...", Toast.LENGTH_SHORT)
.show()
}

val onSwitchSfuClick: () -> Unit = {
call.debug.switchSfu()
call.debug.migrate()
onDismissed.invoke()
Toast.makeText(context, "Switch sfu", Toast.LENGTH_SHORT).show()
}

val onSfuFastReconnectClick: () -> Unit = {
call.debug.fastReconnect()
onDismissed.invoke()
Toast.makeText(context, "Fast Reconnect SFU", Toast.LENGTH_SHORT).show()
}

val codecInfos = remember {
MediaCodecList(MediaCodecList.ALL_CODECS).codecInfos.filter {
it.name.contains("encoder") && it.supportedTypes.firstOrNull {
Expand Down Expand Up @@ -222,13 +228,14 @@ internal fun SettingsMenu(
},
onShowFeedback = onShowFeedback,
onToggleScreenShare = onScreenShareClick,
onKillSfuWsClick = onKillSfuWsClick,
onRestartPublisherIceClick = onRestartPublisherIceClick,
onRestartSubscriberIceClick = onRestartSubscriberIceClick,
onToggleAudioFilterClick = onToggleAudioFilterClick,
onSwitchSfuClick = onSwitchSfuClick,
onShowCallStats = onShowCallStats,
onNoiseCancellation = onNoiseCancellation,
onSfuRejoinClick = onSfuRejoinClick,
onSfuFastReconnectClick = onSfuFastReconnectClick,
isScreenShareEnabled = isScreenSharing,
loadRecordings = onLoadRecordings,
),
Expand Down Expand Up @@ -285,7 +292,8 @@ private fun SettingsMenuPreview() {
onToggleAudioFilterClick = { },
onRestartSubscriberIceClick = { },
onRestartPublisherIceClick = { },
onKillSfuWsClick = { },
onSfuRejoinClick = { },
onSfuFastReconnectClick = {},
onSwitchSfuClick = { },
availableDevices = emptyList(),
onDeviceSelected = {},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import io.getstream.video.android.compose.ui.components.base.StreamToggleButton
import io.getstream.video.android.compose.ui.components.base.styling.StyleSize
import io.getstream.video.android.ui.menu.debugSubmenu
import io.getstream.video.android.ui.menu.defaultStreamMenu
import io.getstream.video.android.ui.menu.reconnectMenu

/**
* A composable capable of loading a menu based on a list structure of menu items and sub menus.
Expand Down Expand Up @@ -217,7 +218,8 @@ private fun DynamicMenuPreview() {
onToggleAudioFilterClick = { },
onRestartSubscriberIceClick = { },
onRestartPublisherIceClick = { },
onKillSfuWsClick = { },
onSfuRejoinClick = { },
onSfuFastReconnectClick = {},
onSwitchSfuClick = { },
availableDevices = emptyList(),
onDeviceSelected = {},
Expand All @@ -244,7 +246,8 @@ private fun DynamicMenuDebugOptionPreview() {
onToggleAudioFilterClick = { },
onRestartSubscriberIceClick = { },
onRestartPublisherIceClick = { },
onKillSfuWsClick = { },
onSfuRejoinClick = { },
onSfuFastReconnectClick = {},
onSwitchSfuClick = { },
availableDevices = emptyList(),
onDeviceSelected = {},
Expand All @@ -264,7 +267,8 @@ private fun DynamicMenuDebugPreview() {
items = debugSubmenu(
codecList = emptyList(),
onCodecSelected = {},
onKillSfuWsClick = { },
onSfuRejoinClick = { },
onSfuFastReconnectClick = {},
onRestartPublisherIceClick = { },
onRestartSubscriberIceClick = { },
onToggleAudioFilterClick = { },
Expand All @@ -273,3 +277,19 @@ private fun DynamicMenuDebugPreview() {
)
}
}

@Preview
@Composable
private fun DynamicMenuReconnectPreview() {
VideoTheme {
DynamicMenu(
items = reconnectMenu(
onSfuRejoinClick = { },
onSfuFastReconnectClick = {},
onRestartPublisherIceClick = { },
onRestartSubscriberIceClick = { },
onSwitchSfuClick = { },
),
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import io.getstream.video.android.core.StreamVideo
import io.getstream.video.android.core.StreamVideoBuilder
import io.getstream.video.android.core.logging.LoggingLevel
import io.getstream.video.android.core.notifications.NotificationConfig
import io.getstream.video.android.core.socket.common.token.TokenProvider
import io.getstream.video.android.data.services.stream.GetAuthDataResponse
import io.getstream.video.android.data.services.stream.StreamService
import io.getstream.video.android.datastore.delegate.StreamUserDataStore
Expand Down Expand Up @@ -199,13 +200,15 @@ object StreamVideoInitHelper {
FirebasePushDeviceGenerator(providerName = "firebase"),
),
),
tokenProvider = {
val email = user.custom?.get("email")
val authData = StreamService.instance.getAuthData(
environment = AppConfig.currentEnvironment.value!!.env,
userId = email,
)
authData.token
tokenProvider = object : TokenProvider {
override suspend fun loadToken(): String {
val email = user.custom?.get("email")
val authData = StreamService.instance.getAuthData(
environment = AppConfig.currentEnvironment.value!!.env,
userId = email,
)
return authData.token
}
},
appName = "Stream Video Demo App",
audioProcessing = NoiseCancellation(context),
Expand Down
Loading

0 comments on commit 6c06ce4

Please sign in to comment.