Skip to content

Commit

Permalink
Add call stats reporting (#1030)
Browse files Browse the repository at this point in the history
  • Loading branch information
liviu-timar authored Mar 13, 2024
1 parent 491f793 commit f76e503
Show file tree
Hide file tree
Showing 17 changed files with 1,651 additions and 435 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ object Configuration {
const val snapshotVersionName = "$majorVersion.$minorVersion.${patchVersion + 1}-SNAPSHOT"
const val artifactGroup = "io.getstream"
const val streamVideoCallGooglePlayVersion = "1.0.5"
const val streamWebRtcVersionName = "1.1.1"
}
81 changes: 71 additions & 10 deletions stream-video-android-core/api/stream-video-android-core.api
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ public final class io/getstream/video/android/core/Call {
public final fun sendCustomEvent (Ljava/util/Map;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public final fun sendReaction (Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun sendReaction$default (Lio/getstream/video/android/core/Call;Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
public final fun sendStats (Ljava/util/Map;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public final fun setAudioFilter (Lio/getstream/video/android/core/call/audio/AudioFilter;)V
public final fun setVideoFilter (Lio/getstream/video/android/core/call/video/VideoFilter;)V
public final fun setVisibility (Ljava/lang/String;Lstream/video/sfu/models/TrackType;Z)V
Expand Down Expand Up @@ -193,6 +192,10 @@ public final class io/getstream/video/android/core/CallStats {
public final fun updateLocalStats ()V
}

public final class io/getstream/video/android/core/CallStatsReportKt {
public static final fun toJson (Lio/getstream/video/android/core/CallStatsReport;Lio/getstream/video/android/core/model/StreamPeerType;)Ljava/lang/String;
}

public final class io/getstream/video/android/core/CameraDeviceWrapped {
public fun <init> (Ljava/lang/String;Landroid/hardware/camera2/CameraCharacteristics;Ljava/util/List;ILio/getstream/video/android/core/CameraDirection;)V
public final fun component1 ()Ljava/lang/String;
Expand Down Expand Up @@ -839,6 +842,7 @@ public abstract interface class io/getstream/video/android/core/api/SignalServer
public abstract fun iceRestart (Lstream/video/sfu/signal/ICERestartRequest;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun iceTrickle (Lstream/video/sfu/models/ICETrickle;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun sendAnswer (Lstream/video/sfu/signal/SendAnswerRequest;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun sendStats (Lstream/video/sfu/signal/SendStatsRequest;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun setPublisher (Lstream/video/sfu/signal/SetPublisherRequest;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun updateMuteStates (Lstream/video/sfu/signal/UpdateMuteStatesRequest;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun updateSubscriptions (Lstream/video/sfu/signal/UpdateSubscriptionsRequest;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
Expand Down Expand Up @@ -7604,17 +7608,18 @@ public final class org/openapitools/client/models/JoinCallRequest {
}

public final class org/openapitools/client/models/JoinCallResponse {
public fun <init> (Lorg/openapitools/client/models/CallResponse;ZLorg/openapitools/client/models/Credentials;Ljava/lang/String;Ljava/util/List;Ljava/util/List;Lorg/openapitools/client/models/MemberResponse;)V
public synthetic fun <init> (Lorg/openapitools/client/models/CallResponse;ZLorg/openapitools/client/models/Credentials;Ljava/lang/String;Ljava/util/List;Ljava/util/List;Lorg/openapitools/client/models/MemberResponse;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Lorg/openapitools/client/models/CallResponse;ZLorg/openapitools/client/models/Credentials;Ljava/lang/String;Ljava/util/List;Ljava/util/List;Lorg/openapitools/client/models/StatsOptions;Lorg/openapitools/client/models/MemberResponse;)V
public synthetic fun <init> (Lorg/openapitools/client/models/CallResponse;ZLorg/openapitools/client/models/Credentials;Ljava/lang/String;Ljava/util/List;Ljava/util/List;Lorg/openapitools/client/models/StatsOptions;Lorg/openapitools/client/models/MemberResponse;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1 ()Lorg/openapitools/client/models/CallResponse;
public final fun component2 ()Z
public final fun component3 ()Lorg/openapitools/client/models/Credentials;
public final fun component4 ()Ljava/lang/String;
public final fun component5 ()Ljava/util/List;
public final fun component6 ()Ljava/util/List;
public final fun component7 ()Lorg/openapitools/client/models/MemberResponse;
public final fun copy (Lorg/openapitools/client/models/CallResponse;ZLorg/openapitools/client/models/Credentials;Ljava/lang/String;Ljava/util/List;Ljava/util/List;Lorg/openapitools/client/models/MemberResponse;)Lorg/openapitools/client/models/JoinCallResponse;
public static synthetic fun copy$default (Lorg/openapitools/client/models/JoinCallResponse;Lorg/openapitools/client/models/CallResponse;ZLorg/openapitools/client/models/Credentials;Ljava/lang/String;Ljava/util/List;Ljava/util/List;Lorg/openapitools/client/models/MemberResponse;ILjava/lang/Object;)Lorg/openapitools/client/models/JoinCallResponse;
public final fun component7 ()Lorg/openapitools/client/models/StatsOptions;
public final fun component8 ()Lorg/openapitools/client/models/MemberResponse;
public final fun copy (Lorg/openapitools/client/models/CallResponse;ZLorg/openapitools/client/models/Credentials;Ljava/lang/String;Ljava/util/List;Ljava/util/List;Lorg/openapitools/client/models/StatsOptions;Lorg/openapitools/client/models/MemberResponse;)Lorg/openapitools/client/models/JoinCallResponse;
public static synthetic fun copy$default (Lorg/openapitools/client/models/JoinCallResponse;Lorg/openapitools/client/models/CallResponse;ZLorg/openapitools/client/models/Credentials;Ljava/lang/String;Ljava/util/List;Ljava/util/List;Lorg/openapitools/client/models/StatsOptions;Lorg/openapitools/client/models/MemberResponse;ILjava/lang/Object;)Lorg/openapitools/client/models/JoinCallResponse;
public fun equals (Ljava/lang/Object;)Z
public final fun getCall ()Lorg/openapitools/client/models/CallResponse;
public final fun getCreated ()Z
Expand All @@ -7623,6 +7628,7 @@ public final class org/openapitools/client/models/JoinCallResponse {
public final fun getMembers ()Ljava/util/List;
public final fun getMembership ()Lorg/openapitools/client/models/MemberResponse;
public final fun getOwnCapabilities ()Ljava/util/List;
public final fun getStatsOptions ()Lorg/openapitools/client/models/StatsOptions;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
Expand Down Expand Up @@ -8481,6 +8487,17 @@ public final class org/openapitools/client/models/StartTranscriptionResponse {
public fun toString ()Ljava/lang/String;
}

public final class org/openapitools/client/models/StatsOptions {
public fun <init> (I)V
public final fun component1 ()I
public final fun copy (I)Lorg/openapitools/client/models/StatsOptions;
public static synthetic fun copy$default (Lorg/openapitools/client/models/StatsOptions;IILjava/lang/Object;)Lorg/openapitools/client/models/StatsOptions;
public fun equals (Ljava/lang/Object;)Z
public final fun getReportingIntervalMs ()I
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}

public final class org/openapitools/client/models/StopBroadcastingResponse {
public fun <init> (Ljava/lang/String;)V
public final fun component1 ()Ljava/lang/String;
Expand Down Expand Up @@ -9616,14 +9633,15 @@ public final class stream/video/sfu/event/VideoLayerSetting : com/squareup/wire/
public static final field ADAPTER Lcom/squareup/wire/ProtoAdapter;
public static final field Companion Lstream/video/sfu/event/VideoLayerSetting$Companion;
public fun <init> ()V
public fun <init> (Ljava/lang/String;ZIFLstream/video/sfu/event/VideoLayerSetting$Priority;Lstream/video/sfu/models/Codec;Lokio/ByteString;)V
public synthetic fun <init> (Ljava/lang/String;ZIFLstream/video/sfu/event/VideoLayerSetting$Priority;Lstream/video/sfu/models/Codec;Lokio/ByteString;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun copy (Ljava/lang/String;ZIFLstream/video/sfu/event/VideoLayerSetting$Priority;Lstream/video/sfu/models/Codec;Lokio/ByteString;)Lstream/video/sfu/event/VideoLayerSetting;
public static synthetic fun copy$default (Lstream/video/sfu/event/VideoLayerSetting;Ljava/lang/String;ZIFLstream/video/sfu/event/VideoLayerSetting$Priority;Lstream/video/sfu/models/Codec;Lokio/ByteString;ILjava/lang/Object;)Lstream/video/sfu/event/VideoLayerSetting;
public fun <init> (Ljava/lang/String;ZIFLstream/video/sfu/event/VideoLayerSetting$Priority;Lstream/video/sfu/models/Codec;ILokio/ByteString;)V
public synthetic fun <init> (Ljava/lang/String;ZIFLstream/video/sfu/event/VideoLayerSetting$Priority;Lstream/video/sfu/models/Codec;ILokio/ByteString;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun copy (Ljava/lang/String;ZIFLstream/video/sfu/event/VideoLayerSetting$Priority;Lstream/video/sfu/models/Codec;ILokio/ByteString;)Lstream/video/sfu/event/VideoLayerSetting;
public static synthetic fun copy$default (Lstream/video/sfu/event/VideoLayerSetting;Ljava/lang/String;ZIFLstream/video/sfu/event/VideoLayerSetting$Priority;Lstream/video/sfu/models/Codec;ILokio/ByteString;ILjava/lang/Object;)Lstream/video/sfu/event/VideoLayerSetting;
public fun equals (Ljava/lang/Object;)Z
public final fun getActive ()Z
public final fun getCodec ()Lstream/video/sfu/models/Codec;
public final fun getMax_bitrate ()I
public final fun getMax_framerate ()I
public final fun getName ()Ljava/lang/String;
public final fun getPriority ()Lstream/video/sfu/event/VideoLayerSetting$Priority;
public final fun getScale_resolution_down_by ()F
Expand Down Expand Up @@ -10373,6 +10391,49 @@ public final class stream/video/sfu/signal/SendAnswerResponse : com/squareup/wir
public final class stream/video/sfu/signal/SendAnswerResponse$Companion {
}

public final class stream/video/sfu/signal/SendStatsRequest : com/squareup/wire/Message {
public static final field ADAPTER Lcom/squareup/wire/ProtoAdapter;
public static final field Companion Lstream/video/sfu/signal/SendStatsRequest$Companion;
public fun <init> ()V
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lokio/ByteString;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lokio/ByteString;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun copy (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lokio/ByteString;)Lstream/video/sfu/signal/SendStatsRequest;
public static synthetic fun copy$default (Lstream/video/sfu/signal/SendStatsRequest;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lokio/ByteString;ILjava/lang/Object;)Lstream/video/sfu/signal/SendStatsRequest;
public fun equals (Ljava/lang/Object;)Z
public final fun getPublisher_stats ()Ljava/lang/String;
public final fun getSdk ()Ljava/lang/String;
public final fun getSdk_version ()Ljava/lang/String;
public final fun getSession_id ()Ljava/lang/String;
public final fun getSubscriber_stats ()Ljava/lang/String;
public final fun getWebrtc_version ()Ljava/lang/String;
public fun hashCode ()I
public synthetic fun newBuilder ()Lcom/squareup/wire/Message$Builder;
public synthetic fun newBuilder ()Ljava/lang/Void;
public fun toString ()Ljava/lang/String;
}

public final class stream/video/sfu/signal/SendStatsRequest$Companion {
}

public final class stream/video/sfu/signal/SendStatsResponse : com/squareup/wire/Message {
public static final field ADAPTER Lcom/squareup/wire/ProtoAdapter;
public static final field Companion Lstream/video/sfu/signal/SendStatsResponse$Companion;
public fun <init> ()V
public fun <init> (Lstream/video/sfu/models/Error;Lokio/ByteString;)V
public synthetic fun <init> (Lstream/video/sfu/models/Error;Lokio/ByteString;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun copy (Lstream/video/sfu/models/Error;Lokio/ByteString;)Lstream/video/sfu/signal/SendStatsResponse;
public static synthetic fun copy$default (Lstream/video/sfu/signal/SendStatsResponse;Lstream/video/sfu/models/Error;Lokio/ByteString;ILjava/lang/Object;)Lstream/video/sfu/signal/SendStatsResponse;
public fun equals (Ljava/lang/Object;)Z
public final fun getError ()Lstream/video/sfu/models/Error;
public fun hashCode ()I
public synthetic fun newBuilder ()Lcom/squareup/wire/Message$Builder;
public synthetic fun newBuilder ()Ljava/lang/Void;
public fun toString ()Ljava/lang/String;
}

public final class stream/video/sfu/signal/SendStatsResponse$Companion {
}

public final class stream/video/sfu/signal/SetPublisherRequest : com/squareup/wire/Message {
public static final field ADAPTER Lcom/squareup/wire/ProtoAdapter;
public static final field Companion Lstream/video/sfu/signal/SetPublisherRequest$Companion;
Expand Down
4 changes: 2 additions & 2 deletions stream-video-android-core/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ android {
buildConfigField("Integer", "STREAM_VIDEO_VERSION_MAJOR", "${Configuration.majorVersion}")
buildConfigField("Integer", "STREAM_VIDEO_VERSION_MINOR", "${Configuration.minorVersion}")
buildConfigField("Integer", "STREAM_VIDEO_VERSION_PATCH", "${Configuration.patchVersion}")

buildConfigField("String", "STREAM_WEBRTC_VERSION", "\"${Configuration.streamWebRtcVersionName}\"")
}

buildFeatures {
Expand Down Expand Up @@ -142,7 +142,7 @@ dependencies {
implementation(libs.audioswitch)

// video filter dependencies
implementation (libs.libyuv)
implementation(libs.libyuv)

// androidx
implementation(libs.androidx.core.ktx)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,15 +112,15 @@ public class Call(
val id: String,
val user: User,
) {
private var statsGatheringJob: Job? = null
private var location: String? = null

private var subscriptions = mutableSetOf<EventSubscription>()

internal val clientImpl = client as StreamVideoImpl
private val logger by taggedLogger("Call")

private val logger by taggedLogger("Call")
private val supervisorJob = SupervisorJob()
private var callStatsReportingJob: Job? = null

private val scope = CoroutineScope(clientImpl.scope.coroutineContext + supervisorJob)

/** The call state contains all state such as the participant list, reactions etc */
Expand Down Expand Up @@ -435,18 +435,36 @@ public class Call(
}

monitor.start()
client.state.setActiveCall(this)
startCallStatsReporting(result.value.statsOptions.reportingIntervalMs.toLong())

val statsGatheringInterval = 5000L
// listen to Signal WS
scope.launch {
session?.let {
it.sfuSocketState.collect { sfuSocketState ->
if (sfuSocketState is SocketState.DisconnectedPermanently) {
handleSignalChannelDisconnect(isRetry = false)
}
}
}
}

timer.finish()

return Success(value = session!!)
}

statsGatheringJob = scope.launch {
// wait a bit before we capture stats
delay(statsGatheringInterval)
private suspend fun startCallStatsReporting(reportingIntervalMs: Long = 10_000) {
callStatsReportingJob?.cancel()
callStatsReportingJob = scope.launch {
// Wait a bit before we start capturing stats
delay(reportingIntervalMs)

while (isActive) {
delay(statsGatheringInterval)
delay(reportingIntervalMs)

val publisherStats = session?.publisher?.getStats()
val subscriberStats = session?.subscriber?.getStats()
val publisherStats = session?.getPublisherStats()
val subscriberStats = session?.getSubscriberStats()
state.stats.updateFromRTCStats(publisherStats, isPublisher = true)
state.stats.updateFromRTCStats(subscriberStats, isPublisher = false)
state.stats.updateLocalStats()
Expand All @@ -463,25 +481,10 @@ public class Call(
if (statLatencyHistory.value.size > 20) {
statLatencyHistory.value = statLatencyHistory.value.takeLast(20)
}
}
}

client.state.setActiveCall(this)

// listen to Signal WS
scope.launch {
session?.let {
it.sfuSocketState.collect { sfuSocketState ->
if (sfuSocketState is SocketState.DisconnectedPermanently) {
handleSignalChannelDisconnect(isRetry = false)
}
}
session?.sendCallStats(report)
}
}

timer.finish()

return Success(value = session!!)
}

private suspend fun handleSignalChannelDisconnect(isRetry: Boolean) {
Expand Down Expand Up @@ -533,10 +536,6 @@ public class Call(
}
}

suspend fun sendStats(data: Map<String, Any>) {
return clientImpl.sendStats(type, id, data)
}

suspend fun switchSfu() {
state._connection.value = RealtimeConnection.Migrating

Expand Down Expand Up @@ -1001,7 +1000,7 @@ public class Call(
monitor.stop()
session?.cleanup()
supervisorJob.cancel()
statsGatheringJob?.cancel()
callStatsReportingJob?.cancel()
mediaManager.cleanup()
session = null
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import org.webrtc.CameraEnumerationAndroid
import org.webrtc.RTCStats
import stream.video.sfu.models.TrackType
Expand Down Expand Up @@ -206,12 +205,6 @@ public class CallStats(val call: Call, val callScope: CoroutineScope) {
statGroups.forEach {
logger.i { "statgroup ${it.key}:${it.value}" }
}

scope.launch {
val toMap = mutableMapOf<String, Any>()
toMap["data"] = stats.origin.statsMap
call.sendStats(toMap)
}
}

fun updateLocalStats() {
Expand Down Expand Up @@ -239,13 +232,5 @@ public class CallStats(val call: Call, val callScope: CoroutineScope) {
deviceModel = deviceModel,
)
_local.value = local

scope.launch {
val toMap = mutableMapOf<String, Any>()
toMap["availableResolutions"] = availableResolutions
toMap["maxResolution"] = maxResolution ?: ""
toMap["displayingAt"] = displayingAt
call.sendStats(toMap)
}
}
}
Loading

0 comments on commit f76e503

Please sign in to comment.