Skip to content

Commit

Permalink
Merge branch 'refs/heads/develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
aleksandar-apostolov committed Oct 23, 2024
2 parents 921c7c8 + bd82e9f commit 8c7d359
Show file tree
Hide file tree
Showing 26 changed files with 509 additions and 210 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ object Configuration {
const val minSdk = 24
const val majorVersion = 1
const val minorVersion = 0
const val patchVersion = 16
const val patchVersion = 17
const val versionName = "$majorVersion.$minorVersion.$patchVersion"
const val versionCode = 39
const val versionCode = 40
const val snapshotVersionName = "$majorVersion.$minorVersion.${patchVersion + 1}-SNAPSHOT"
const val artifactGroup = "io.getstream"
const val streamVideoCallGooglePlayVersion = "1.1.9"
const val streamVideoCallGooglePlayVersion = "1.1.10"
const val streamWebRtcVersionName = "1.2.1"
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,17 @@
package io.getstream.video.android.data.services.stream

import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
import io.getstream.video.android.model.User
import io.getstream.video.android.models.UserCredentials
import io.getstream.video.android.models.builtInCredentials
import kotlinx.serialization.json.Json
import okhttp3.MediaType.Companion.toMediaType
import retrofit2.Retrofit
import retrofit2.create
import retrofit2.http.GET
import retrofit2.http.Query

interface StreamService {
fun interface StreamService {
@GET("api/auth/create-token")
suspend fun getAuthData(
@Query("environment") environment: String,
Expand All @@ -41,6 +44,15 @@ interface StreamService {
.addConverterFactory(json.asConverterFactory("application/json".toMediaType()))
.build()

val instance = retrofit.create<StreamService>()
private val serviceInstance = retrofit.create<StreamService>()

val instance = StreamService { environment, userId ->
User.builtInCredentials[userId]?.toAuthDataResponse()
?: serviceInstance.getAuthData(environment, userId)
}
}
}

private fun UserCredentials.toAuthDataResponse(): GetAuthDataResponse {
return GetAuthDataResponse(userId, apiKey, token)
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ package io.getstream.video.android.models

import io.getstream.video.android.model.User

data class UserCredentials(val userId: String, val apiKey: String, val token: String)

public val User.Companion.builtInCredentials: Map<String, UserCredentials>
get() = mapOf()

public fun User.Companion.builtInUsers(): List<User> {
return listOf(
User(
Expand Down
19 changes: 19 additions & 0 deletions docusaurus/docs/Android/03-guides/01-client-auth.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,25 @@ val streamVideo = StreamVideoBuilder(
).build()
```

Anonymous users don't establish an active web socket connection, therefore they won't receive any events. They are just able to watch a livestream or join a call.

The token for an anonymous user should contain the `call_cids` field, which is an array of the call `cid`'s that the user is allowed to join.

Here's an example JWT token payload for an anonymous user:

```kotlin
{
"iss": "@stream-io/dashboard",
"iat": 1726406693,
"exp": 1726493093,
"user_id": "!anon",
"role": "viewer",
"call_cids": [
"livestream:123"
]
}
```

### Client options

Here's a more complete example of the client options:
Expand Down
4 changes: 2 additions & 2 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ coil = "2.6.0"
landscapist = "2.3.6"
accompanist = "0.34.0"
telephoto = "0.3.0"
audioswitch = "1.1.8"
audioswitch = "1.2.0"
libyuv = "0.30.0"

wire = "4.7.0"
Expand All @@ -43,7 +43,7 @@ threetenAbp = "1.4.7"
tink = "1.9.0"
turbine = "0.13.0"

streamWebRTC = "1.2.1"
streamWebRTC = "1.2.2"
streamNoiseCancellation = "1.0.1"
streamResult = "1.2.0"
streamChat = "6.0.13"
Expand Down
17 changes: 17 additions & 0 deletions stream-video-android-core/api/stream-video-android-core.api
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public final class io/getstream/video/android/core/Call {
public final fun isLocalPin (Ljava/lang/String;)Z
public final fun isPinnedParticipant (Ljava/lang/String;)Z
public final fun isServerPin (Ljava/lang/String;)Z
public final fun isVideoEnabled ()Z
public final fun join (ZLio/getstream/video/android/core/CreateCallOptions;ZZLkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun join$default (Lio/getstream/video/android/core/Call;ZLio/getstream/video/android/core/CreateCallOptions;ZZLkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
public final fun leave ()V
Expand Down Expand Up @@ -428,6 +429,10 @@ public final class io/getstream/video/android/core/MediaManagerImpl {
public final fun getVideoTrack ()Lorg/webrtc/VideoTrack;
}

public final class io/getstream/video/android/core/MediaManagerKt {
public static final fun trySetEnabled (Lorg/webrtc/MediaStreamTrack;Z)V
}

public final class io/getstream/video/android/core/MediaStatsInfo {
public static final field Companion Lio/getstream/video/android/core/MediaStatsInfo$Companion;
public fun <init> (Ljava/lang/String;Ljava/lang/Double;Ljava/lang/Long;Ljava/lang/Long;Ljava/lang/Double;Ljava/lang/Double;)V
Expand Down Expand Up @@ -544,6 +549,7 @@ public final class io/getstream/video/android/core/ParticipantState {
public final fun muteVideo (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public final fun pin (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public final fun setSessionId (Ljava/lang/String;)V
public final fun setVideoTrack (Lio/getstream/video/android/core/model/VideoTrack;)V
public fun toString ()Ljava/lang/String;
public final fun unpin (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public final fun updateAudioLevel (F)V
Expand Down Expand Up @@ -3034,6 +3040,17 @@ public final class io/getstream/video/android/core/events/GoAwayEvent : io/getst
public fun toString ()Ljava/lang/String;
}

public final class io/getstream/video/android/core/events/ICERestartEvent : io/getstream/video/android/core/events/SfuDataEvent {
public fun <init> (Lstream/video/sfu/models/PeerType;)V
public final fun component1 ()Lstream/video/sfu/models/PeerType;
public final fun copy (Lstream/video/sfu/models/PeerType;)Lio/getstream/video/android/core/events/ICERestartEvent;
public static synthetic fun copy$default (Lio/getstream/video/android/core/events/ICERestartEvent;Lstream/video/sfu/models/PeerType;ILjava/lang/Object;)Lio/getstream/video/android/core/events/ICERestartEvent;
public fun equals (Ljava/lang/Object;)Z
public final fun getPeerType ()Lstream/video/sfu/models/PeerType;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}

public final class io/getstream/video/android/core/events/ICETrickleEvent : io/getstream/video/android/core/events/SfuDataEvent {
public fun <init> (Ljava/lang/String;Lstream/video/sfu/models/PeerType;)V
public final fun component1 ()Ljava/lang/String;
Expand Down
2 changes: 1 addition & 1 deletion stream-video-android-core/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@

<service
android:name=".notifications.internal.service.AudioCallService"
android:foregroundServiceType="microphone"
android:foregroundServiceType="microphone|shortService"
android:exported="false" />
</application>
</manifest>
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,9 @@ public class Call(
ring: Boolean = false,
notify: Boolean = false,
): Result<RtcSession> {
logger.d { "[join] #ringing; create: $create, ring: $ring, notify: $notify" }
logger.d {
"[join] #ringing; #track; create: $create, ring: $ring, notify: $notify, createOptions: $createOptions"
}
val permissionPass =
clientImpl.permissionCheck.checkAndroidPermissions(clientImpl.context, this)
// Check android permissions and log a warning to make sure developers requested adequate permissions prior to using the call.
Expand Down Expand Up @@ -378,6 +380,9 @@ public class Call(
"Call $cid has already been joined. Please use call.leave before joining it again",
)
}
logger.d {
"[joinInternal] #track; create: $create, ring: $ring, notify: $notify, createOptions: $createOptions"
}

// step 1. call the join endpoint to get a list of SFUs

Expand Down Expand Up @@ -493,14 +498,19 @@ public class Call(
// first check if sfuSocketReconnectionTime isn't already set - if yes
// then we are already doing a full reconnect
if (state._connection.value == RealtimeConnection.Migrating) {
logger.d { "Skipping disconnected channel event - we are migrating" }
logger.d {
"[handleSignalChannelDisconnect] #track; Skipping disconnected channel event - we are migrating"
}
return
}

if (!isRetry && sfuSocketReconnectionTime != null) {
logger.d { "[handleSignalChannelDisconnect] Already doing a full reconnect cycle - ignoring call" }
logger.d {
"[handleSignalChannelDisconnect] #track; Already doing a full reconnect cycle - ignoring call"
}
return
}
logger.d { "[handleSignalChannelDisconnect] #track; isRetry: $isRetry" }

if (!isRetry) {
state._connection.value = RealtimeConnection.Reconnecting
Expand Down Expand Up @@ -675,11 +685,14 @@ public class Call(
}

fun setVisibility(sessionId: String, trackType: TrackType, visible: Boolean) {
logger.i {
"[setVisibility] #track; #sfu; sessionId: $sessionId, trackType: $trackType, visible: $visible"
}
session?.updateTrackDimensions(sessionId, trackType, visible)
}

fun handleEvent(event: VideoEvent) {
logger.i { "[call handleEvent] #sfu; event: $event" }
logger.v { "[call handleEvent] #sfu; event.type: ${event.getEventType()}" }

when (event) {
is GoAwayEvent ->
Expand Down Expand Up @@ -708,40 +721,52 @@ public class Call(
trackType: TrackType,
onRendered: (VideoTextureViewRenderer) -> Unit = {},
) {
logger.d { "[initRenderer] #sfu; sessionId: $sessionId" }
logger.d { "[initRenderer] #sfu; #track; sessionId: $sessionId" }

// Note this comes from peerConnectionFactory.eglBase
videoRenderer.init(
clientImpl.peerConnectionFactory.eglBase.eglBaseContext,
object : RendererCommon.RendererEvents {
override fun onFirstFrameRendered() {
logger.d { "[initRenderer.onFirstFrameRendered] #sfu; sessionId: $sessionId" }
val width = videoRenderer.measuredWidth
val height = videoRenderer.measuredHeight
logger.i {
"[initRenderer.onFirstFrameRendered] #sfu; #track; " +
"trackType: $trackType, dimension: ($width - $height), " +
"sessionId: $sessionId"
}
if (trackType != TrackType.TRACK_TYPE_SCREEN_SHARE) {
session?.updateTrackDimensions(
sessionId,
trackType,
true,
VideoDimension(
videoRenderer.measuredWidth,
videoRenderer.measuredHeight,
),
VideoDimension(width, height),
)
}
onRendered(videoRenderer)
}

override fun onFrameResolutionChanged(p0: Int, p1: Int, p2: Int) {
logger.d { "[initRenderer.onFrameResolutionChanged] #sfu; sessionId: $sessionId" }
override fun onFrameResolutionChanged(
videoWidth: Int,
videoHeight: Int,
rotation: Int,
) {
val width = videoRenderer.measuredWidth
val height = videoRenderer.measuredHeight
logger.v {
"[initRenderer.onFrameResolutionChanged] #sfu; #track; " +
"trackType: $trackType, " +
"dimension1: ($width - $height), " +
"dimension2: ($videoWidth - $videoHeight), " +
"sessionId: $sessionId"
}

if (trackType != TrackType.TRACK_TYPE_SCREEN_SHARE) {
session?.updateTrackDimensions(
sessionId,
trackType,
true,
VideoDimension(
videoRenderer.measuredWidth,
videoRenderer.measuredHeight,
),
VideoDimension(videoWidth, videoHeight),
)
}
}
Expand Down Expand Up @@ -900,10 +925,20 @@ public class Call(

private fun updateMediaManagerFromSettings(callSettings: CallSettingsResponse) {
// Speaker
speaker.setEnabled(
enabled = callSettings.audio.defaultDevice == AudioSettingsResponse.DefaultDevice.Speaker ||
callSettings.audio.speakerDefaultOn,
)
if (speaker.status.value is DeviceStatus.NotSelected) {
val enableSpeaker = if (callSettings.video.cameraDefaultOn || camera.status.value is DeviceStatus.Enabled) {
// if camera is enabled then enable speaker. Eventually this should
// be a new audio.defaultDevice setting returned from backend
true
} else {
callSettings.audio.defaultDevice == AudioSettingsResponse.DefaultDevice.Speaker ||
callSettings.audio.speakerDefaultOn
}

speaker.setEnabled(
enabled = enableSpeaker,
)
}

// Camera
if (camera.status.value is DeviceStatus.NotSelected) {
Expand Down Expand Up @@ -1068,6 +1103,10 @@ public class Call(
return state.ownCapabilities.value.containsAll(elements)
}

fun isVideoEnabled(): Boolean {
return state.settings.value?.video?.enabled ?: false
}

fun isAudioProcessingEnabled(): Boolean {
return clientImpl.isAudioProcessingEnabled()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -264,8 +264,9 @@ public class CallState(
private val livestreamFlow: Flow<ParticipantState.Video?> = channelFlow {
fun emitLivestreamVideo() {
val participants = participants.value
val filteredVideo =
participants.mapNotNull { it.video.value }.firstOrNull { it.track != null }
val filteredVideo = participants.firstOrNull {
it.video.value?.enabled == true
}?.video?.value
scope.launch {
if (_backstage.value) {
send(null)
Expand All @@ -277,12 +278,17 @@ public class CallState(

scope.launch {
_participants.collect {
logger.v {
"[livestreamFlow] #track; participants: ${it.size} =>" +
"${it.map { "${it.value.userId.value} - ${it.value.video.value?.enabled}" }}"
}
emitLivestreamVideo()
}
}

// TODO: could optimize performance by subscribing only to relevant events
call.subscribe {
logger.v { "[livestreamFlow] #track; event.type: ${it.getEventType()}" }
if (it is TrackPublishedEvent) {
val participant = getOrCreateParticipant(it.sessionId, it.userId)

Expand All @@ -307,6 +313,7 @@ public class CallState(
}

// emit livestream Video
logger.d { "[livestreamFlow] #track; no args" }
emitLivestreamVideo()

awaitClose { }
Expand Down
Loading

0 comments on commit 8c7d359

Please sign in to comment.