Skip to content

Commit 7676d9a

Browse files
Merge branch 'develop'
2 parents 7171cf8 + 3b6026c commit 7676d9a

File tree

29 files changed

+516
-210
lines changed

29 files changed

+516
-210
lines changed

buildSrc/src/main/kotlin/io/getstream/video/android/Configuration.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@ object Configuration {
66
const val minSdk = 24
77
const val majorVersion = 1
88
const val minorVersion = 4
9-
const val patchVersion = 1
9+
const val patchVersion = 2
1010
const val versionName = "$majorVersion.$minorVersion.$patchVersion"
1111
const val versionCode = 49
1212
const val snapshotVersionName = "$majorVersion.$minorVersion.${patchVersion + 1}-SNAPSHOT"
1313
const val artifactGroup = "io.getstream"
14-
const val streamVideoCallGooglePlayVersion = "1.4.1"
14+
const val streamVideoCallGooglePlayVersion = "1.4.2"
1515
const val streamWebRtcVersionName = "1.3.6"
1616
}

demo-app/src/main/kotlin/io/getstream/video/android/CallActivity.kt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,14 @@ import androidx.compose.runtime.Composable
2323
import androidx.compose.runtime.LaunchedEffect
2424
import androidx.compose.runtime.collectAsState
2525
import androidx.compose.runtime.getValue
26+
import androidx.lifecycle.compose.collectAsStateWithLifecycle
2627
import io.getstream.chat.android.client.ChatClient
2728
import io.getstream.chat.android.models.Filters
2829
import io.getstream.chat.android.models.querysort.QuerySortByField
2930
import io.getstream.result.onSuccessSuspend
3031
import io.getstream.video.android.compose.ui.ComposeStreamCallActivity
3132
import io.getstream.video.android.compose.ui.StreamCallActivityComposeDelegate
33+
import io.getstream.video.android.compose.ui.components.call.activecall.AudioOnlyCallContent
3234
import io.getstream.video.android.core.Call
3335
import io.getstream.video.android.core.StreamVideo
3436
import io.getstream.video.android.core.notifications.NotificationHandler
@@ -125,6 +127,18 @@ class CallActivity : ComposeStreamCallActivity() {
125127
}
126128
}
127129

130+
@Composable
131+
override fun StreamCallActivity.AudioCallContent(call: Call) {
132+
val micEnabled by call.microphone.isEnabled.collectAsStateWithLifecycle()
133+
134+
AudioOnlyCallContent(
135+
call = call,
136+
isMicrophoneEnabled = micEnabled,
137+
onCallAction = { onCallAction(call, it) },
138+
onBackPressed = { onBackPressed(call) },
139+
)
140+
}
141+
128142
private fun StreamCallActivity.goBackToMainScreen() {
129143
if (!isFinishing) {
130144
val intent = Intent(this, MainActivity::class.java).apply {

stream-video-android-core/api/stream-video-android-core.api

Lines changed: 26 additions & 22 deletions
Large diffs are not rendered by default.

stream-video-android-core/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ baselineProfile {
129129
include("io.getstream.video.android.core.**")
130130
include("io.getstream.video.android.datastore.**")
131131
include("io.getstream.video.android.model.**")
132-
include("org.openapitools.client.**")
132+
include("io.getstream.android.video.generated.**")
133133
include("org.webrtc.**")
134134
}
135135
}

stream-video-android-core/consumer-proguard-rules.pro

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
-keep class kotlin.reflect.jvm.internal.* { *; }
99

1010
## Moshi model classes
11-
-keep class org.openapitools.client.** { *; }
11+
-keep class io.getstream.android.video.generated.** { *; }
1212

1313
## Kotlin serialized classes
1414
-keep @kotlinx.serialization.Serializable class * {*;}

stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/StreamVideoBuilder.kt

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,13 @@ public class StreamVideoBuilder @JvmOverloads constructor(
109109
private var ensureSingleInstance: Boolean = true,
110110
private val videoDomain: String = "video.stream-io-api.com",
111111
@Deprecated(
112-
"Use 'callServiceConfigRegistry' instead",
112+
"This property is ignored. Set runCallServiceInForeground in the callServiceConfigRegistry parameter instead.",
113+
replaceWith = ReplaceWith("callServiceConfigRegistry"),
114+
level = DeprecationLevel.WARNING,
115+
)
116+
private val runForegroundServiceForCalls: Boolean = true,
117+
@Deprecated(
118+
"Use callServiceConfigRegistry instead",
113119
replaceWith = ReplaceWith("callServiceConfigRegistry"),
114120
level = DeprecationLevel.WARNING,
115121
)
@@ -119,6 +125,11 @@ public class StreamVideoBuilder @JvmOverloads constructor(
119125
private val sounds: Sounds = defaultResourcesRingingConfig(context).toSounds(),
120126
private val crashOnMissingPermission: Boolean = false,
121127
private val permissionCheck: StreamPermissionCheck = DefaultStreamPermissionCheck(),
128+
@Deprecated(
129+
message = "This property is ignored. Set audioUsage in the callServiceConfigRegistry parameter instead.",
130+
level = DeprecationLevel.WARNING,
131+
)
132+
private val audioUsage: Int = defaultAudioUsage,
122133
private val appName: String? = null,
123134
private val audioProcessing: ManagedAudioProcessingFactory? = null,
124135
private val leaveAfterDisconnectSeconds: Long = 30,
@@ -239,6 +250,7 @@ public class StreamVideoBuilder @JvmOverloads constructor(
239250
lifecycle = lifecycle,
240251
coordinatorConnectionModule = coordinatorConnectionModule,
241252
streamNotificationManager = streamNotificationManager,
253+
enableCallNotificationUpdates = notificationConfig.enableCallNotificationUpdates,
242254
callServiceConfigRegistry = callConfigRegistry,
243255
testSfuAddress = localSfuAddress,
244256
sounds = sounds,

stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/StreamVideoClient.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ internal class StreamVideoClient internal constructor(
152152
internal val coordinatorConnectionModule: CoordinatorConnectionModule,
153153
internal val tokenProvider: TokenProvider = ConstantTokenProvider(token),
154154
internal val streamNotificationManager: StreamNotificationManager,
155+
internal val enableCallNotificationUpdates: Boolean,
155156
internal val callServiceConfigRegistry: CallServiceConfigRegistry = CallServiceConfigRegistry(),
156157
internal val testSfuAddress: String? = null,
157158
internal val sounds: Sounds,

stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/RtcSession.kt

Lines changed: 9 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,6 @@ import stream.video.sfu.signal.UpdateMuteStatesResponse
150150
import stream.video.sfu.signal.UpdateSubscriptionsRequest
151151
import stream.video.sfu.signal.UpdateSubscriptionsResponse
152152
import java.util.Collections
153-
import java.util.UUID
154153

155154
/**
156155
* Keeps track of which track is being rendered at what resolution.
@@ -571,24 +570,19 @@ public class RtcSession internal constructor(
571570
setMuteState(isEnabled = it == DeviceStatus.Enabled, TrackType.TRACK_TYPE_VIDEO)
572571

573572
if (it == DeviceStatus.Enabled) {
574-
val newTrack = call.peerConnectionFactory.makeVideoTrack(
575-
call.mediaManager.videoSource,
576-
UUID.randomUUID().toString(),
577-
)
578-
publisher?.publishStream(
579-
newTrack,
573+
val track = publisher?.publishStream(
580574
TrackType.TRACK_TYPE_VIDEO,
581575
call.mediaManager.camera.resolution.value,
582576
)
583577
setLocalTrack(
584578
TrackType.TRACK_TYPE_VIDEO,
585579
VideoTrack(
586580
streamId = buildTrackId(TrackType.TRACK_TYPE_VIDEO),
587-
video = newTrack,
581+
video = track as org.webrtc.VideoTrack,
588582
),
589583
)
590584
} else {
591-
publisher?.unpublishStream(TrackType.TRACK_TYPE_VIDEO, false)
585+
publisher?.unpublishStream(TrackType.TRACK_TYPE_VIDEO)
592586
}
593587
}
594588
}
@@ -599,23 +593,18 @@ public class RtcSession internal constructor(
599593
setMuteState(isEnabled = it == DeviceStatus.Enabled, TrackType.TRACK_TYPE_AUDIO)
600594

601595
if (it == DeviceStatus.Enabled) {
602-
val newTrack = call.peerConnectionFactory.makeAudioTrack(
603-
call.mediaManager.audioSource,
604-
UUID.randomUUID().toString(),
605-
)
606-
publisher?.publishStream(
607-
newTrack,
596+
val track = publisher?.publishStream(
608597
TrackType.TRACK_TYPE_AUDIO,
609598
)
610599
setLocalTrack(
611600
TrackType.TRACK_TYPE_AUDIO,
612601
AudioTrack(
613602
streamId = buildTrackId(TrackType.TRACK_TYPE_AUDIO),
614-
audio = newTrack,
603+
audio = track as org.webrtc.AudioTrack,
615604
),
616605
)
617606
} else {
618-
publisher?.unpublishStream(TrackType.TRACK_TYPE_AUDIO, false)
607+
publisher?.unpublishStream(TrackType.TRACK_TYPE_AUDIO)
619608
}
620609
}
621610
}
@@ -628,23 +617,18 @@ public class RtcSession internal constructor(
628617
TrackType.TRACK_TYPE_SCREEN_SHARE,
629618
)
630619
if (it == DeviceStatus.Enabled) {
631-
val newTrack = call.peerConnectionFactory.makeVideoTrack(
632-
call.mediaManager.screenShareVideoSource,
633-
UUID.randomUUID().toString(),
634-
)
635-
publisher?.publishStream(
636-
newTrack,
620+
val track = publisher?.publishStream(
637621
TrackType.TRACK_TYPE_SCREEN_SHARE,
638622
)
639623
setLocalTrack(
640624
TrackType.TRACK_TYPE_SCREEN_SHARE,
641625
VideoTrack(
642626
streamId = buildTrackId(TrackType.TRACK_TYPE_SCREEN_SHARE),
643-
video = newTrack,
627+
video = track as org.webrtc.VideoTrack,
644628
),
645629
)
646630
} else {
647-
publisher?.unpublishStream(TrackType.TRACK_TYPE_SCREEN_SHARE, false)
631+
publisher?.unpublishStream(TrackType.TRACK_TYPE_SCREEN_SHARE)
648632
}
649633
}
650634
}
@@ -959,7 +943,6 @@ public class RtcSession internal constructor(
959943
publishOptions = publishOptions,
960944
coroutineScope = coroutineScope,
961945
mediaConstraints = mediaConstraints,
962-
onStreamAdded = { addStream(it) },
963946
onNegotiationNeeded = { _, _ -> },
964947
onIceCandidate = ::sendIceCandidate,
965948
) {

stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/connection/Publisher.kt

Lines changed: 53 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import io.getstream.video.android.core.call.connection.utils.toVideoDimension
3131
import io.getstream.video.android.core.call.connection.utils.toVideoLayers
3232
import io.getstream.video.android.core.model.IceCandidate
3333
import io.getstream.video.android.core.model.StreamPeerType
34+
import io.getstream.video.android.core.trySetEnabled
3435
import io.getstream.video.android.core.utils.SdpSession
3536
import io.getstream.video.android.core.utils.safeCall
3637
import io.getstream.video.android.core.utils.safeCallWithDefault
@@ -81,8 +82,8 @@ internal class Publisher(
8182
onIceCandidate,
8283
maxBitRate,
8384
) {
84-
private val defaultScreenShareFormat = CaptureFormat(1080, 720, 24, 30)
85-
private val defaultFormat = CaptureFormat(1080, 720, 24, 30)
85+
private val defaultScreenShareFormat = CaptureFormat(1280, 720, 24, 30)
86+
private val defaultFormat = CaptureFormat(1280, 720, 24, 30)
8687
private var isIceRestarting = false
8788

8889
override fun onRenegotiationNeeded() {
@@ -155,45 +156,64 @@ internal class Publisher(
155156
* Starts publishing the given track.
156157
*/
157158
suspend fun publishStream(
158-
track: MediaStreamTrack,
159159
trackType: TrackType,
160160
captureFormat: CaptureFormat? = null,
161-
) {
162-
logger.i { "Publishing track: $trackType" }
163-
if (track.state() == MediaStreamTrack.State.ENDED) {
164-
logger.e { "Can't publish a track that has ended already." }
165-
return
166-
}
161+
): MediaStreamTrack? {
162+
logger.i { "[trackPublishing] Publishing track: $trackType" }
167163

168164
if (publishOptions.none { it.track_type == trackType }) {
169-
logger.e { "No publish options found for $trackType" }
170-
return
165+
logger.e { "[trackPublishing] No publish options found for $trackType" }
166+
return null
171167
}
172168

173-
// enable the track if disabled
174-
if (!track.enabled()) track.setEnabled(true)
175-
176169
for (publishOption in publishOptions) {
177170
if (publishOption.track_type != trackType) continue
178171

179-
val trackToPublish = newTrackFromSource(publishOption.track_type)
180172
val transceiver = transceiverCache.get(publishOption)
181173
if (transceiver != null) {
182174
try {
183-
transceiver.sender?.setTrack(trackToPublish, true)
175+
val sender = transceiver.sender
176+
val senderTrack = sender.track()
177+
if (senderTrack != null && !senderTrack.isDisposed) {
178+
logger.d { "[trackPublishing] Track already exists." }
179+
senderTrack.trySetEnabled(true)
180+
logTrack(senderTrack)
181+
return senderTrack
182+
} else {
183+
logger.d { "[trackPublishing] Track is disposed, creating new one." }
184+
val newTrack = newTrackFromSource(publishOption.track_type)
185+
sender.setTrack(newTrack, true)
186+
return newTrack
187+
}
184188
} catch (e: Exception) {
185189
// Fallback if anything happens with the sender
186190
logger.w { "Failed to set track for ${publishOption.track_type}, creating new transceiver" }
187191
transceiverCache.remove(publishOption)
188-
addTransceiver(captureFormat, trackToPublish, publishOption)
192+
val fallbackTrack = newTrackFromSource(publishOption.track_type)
193+
addTransceiver(captureFormat, fallbackTrack, publishOption)
194+
return fallbackTrack
189195
}
190196
} else {
191-
addTransceiver(captureFormat, trackToPublish, publishOption)
197+
logger.d {
198+
"[trackPublishing] No transceiver found for $trackType, creating new track and transceiver."
199+
}
200+
// This is the first time we are adding the transceiver.
201+
val newTrack = newTrackFromSource(publishOption.track_type)
202+
addTransceiver(captureFormat, newTrack, publishOption)
203+
return newTrack
192204
}
193205
}
206+
return null
194207
}
195208

196-
private fun newTrackFromSource(trackType: TrackType): MediaStreamTrack {
209+
private fun logTrack(senderTrack: MediaStreamTrack?) {
210+
logger.d {
211+
"[trackPublishing] Track: ${senderTrack?.enabled()}:${senderTrack?.state()}:${senderTrack?.id()}"
212+
}
213+
}
214+
215+
@VisibleForTesting
216+
public fun newTrackFromSource(trackType: TrackType): MediaStreamTrack {
197217
return when (trackType) {
198218
TrackType.TRACK_TYPE_AUDIO -> {
199219
val id = UUID.randomUUID().toString()
@@ -265,17 +285,14 @@ internal class Publisher(
265285
}
266286
}
267287

268-
suspend fun unpublishStream(trackType: TrackType, stopTrack: Boolean) {
269-
for (option in publishOptions) {
270-
if (option.track_type != trackType) continue
271-
val transceiver = transceiverCache.get(option)
272-
if (transceiver?.sender?.track()?.isDisposed == false) continue
273-
try {
274-
transceiver?.stop()
275-
transceiver?.dispose()
276-
transceiverCache.remove(option)
277-
} catch (e: Exception) {
278-
logger.w { "Transceiver for option ${option.id}-${option.track_type}" }
288+
suspend fun unpublishStream(trackType: TrackType) {
289+
transceiverCache.getByTrackType(trackType).forEach { transceiver ->
290+
logger.d { "[trackPublishing] Unpublishing track: $trackType" }
291+
val sender = transceiver.sender
292+
val senderTrack = sender.track()
293+
senderTrack?.let { track ->
294+
track.trySetEnabled(false)
295+
logTrack(track)
279296
}
280297
}
281298
}
@@ -332,10 +349,9 @@ internal class Publisher(
332349
"Update publish quality ($publishOptionId-$trackType-${videoSender.codec?.name}), requested layers by SFU: $enabledLayers"
333350
}
334351

335-
val sender =
336-
transceiverCache.get(videoSender.toPublishOption())?.sender?.takeUnless {
337-
it.track()?.isDisposed == true
338-
}
352+
val sender = transceiverCache.get(videoSender.toPublishOption())?.sender?.takeUnless {
353+
it.track()?.isDisposed == true
354+
}
339355

340356
if (sender == null) {
341357
logger.w { "Update publish quality, no video sender found." }
@@ -360,6 +376,9 @@ internal class Publisher(
360376

361377
sender.parameters = params
362378
logger.i { "Update publish quality, enabled rids: $activeLayers" }
379+
activeLayers.forEach { layer ->
380+
logger.i { "Update publish quality, enabled rid: ${layer.rid}" }
381+
}
363382
}
364383

365384
internal fun updateEncodings(

stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/connection/transceivers/TransceiverCache.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package io.getstream.video.android.core.call.connection.transceivers
1919
import io.getstream.video.android.core.call.connection.utils.OptimalVideoLayer
2020
import org.webrtc.RtpTransceiver
2121
import stream.video.sfu.models.PublishOption
22+
import stream.video.sfu.models.TrackType
2223
import java.util.Collections
2324

2425
internal class TransceiverCache {
@@ -74,6 +75,10 @@ internal class TransceiverCache {
7475
private fun findLayer(publishOption: PublishOption): TrackLayersCache? {
7576
return layers[publishOption.key()]
7677
}
78+
79+
fun getByTrackType(trackType: TrackType): List<RtpTransceiver> {
80+
return items().filter { it.publishOption.track_type == trackType }.map { it.transceiver }
81+
}
7782
}
7883

7984
// Helper data classes:

0 commit comments

Comments
 (0)