Skip to content

Commit

Permalink
Improve which participant is shown in PiP, allow for mirroring the `m…
Browse files Browse the repository at this point in the history
…e` video stream for better experience. (#1185)

* Better determine the participant to show in PiP, improve video orientation for `me` participant.

* Api

* Make the `mirror` property a `val`

* Spotless and API
  • Loading branch information
aleksandar-apostolov authored Sep 23, 2024
1 parent b1e411c commit b7391b4
Show file tree
Hide file tree
Showing 5 changed files with 199 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1857,6 +1857,7 @@ public final class io/getstream/video/android/compose/ui/components/video/Compos
}

public final class io/getstream/video/android/compose/ui/components/video/VideoRendererKt {
public static final fun VideoRenderer (Landroidx/compose/ui/Modifier;Lio/getstream/video/android/core/Call;Lio/getstream/video/android/core/ParticipantState$Media;Lio/getstream/video/android/compose/ui/components/video/config/VideoRendererConfig;Lkotlin/jvm/functions/Function1;Landroidx/compose/runtime/Composer;II)V
public static final fun VideoRenderer (Lio/getstream/video/android/core/Call;Lio/getstream/video/android/core/ParticipantState$Media;Landroidx/compose/ui/Modifier;Lio/getstream/video/android/compose/ui/components/video/VideoScalingType;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function1;Landroidx/compose/runtime/Composer;II)V
}

Expand All @@ -1878,3 +1879,56 @@ public final class io/getstream/video/android/compose/ui/components/video/VideoS
public static fun values ()[Lio/getstream/video/android/compose/ui/components/video/VideoScalingType;
}

public final class io/getstream/video/android/compose/ui/components/video/config/ComposableSingletons$VideoRendererConfigKt {
public static final field INSTANCE Lio/getstream/video/android/compose/ui/components/video/config/ComposableSingletons$VideoRendererConfigKt;
public static field lambda-1 Lkotlin/jvm/functions/Function3;
public static field lambda-2 Lkotlin/jvm/functions/Function3;
public fun <init> ()V
public final fun getLambda-1$stream_video_android_ui_compose_release ()Lkotlin/jvm/functions/Function3;
public final fun getLambda-2$stream_video_android_ui_compose_release ()Lkotlin/jvm/functions/Function3;
}

public final class io/getstream/video/android/compose/ui/components/video/config/VideoRendererConfig {
public static final field $stable I
public fun <init> ()V
public fun <init> (ZLio/getstream/video/android/compose/ui/components/video/VideoScalingType;Lkotlin/jvm/functions/Function3;)V
public synthetic fun <init> (ZLio/getstream/video/android/compose/ui/components/video/VideoScalingType;Lkotlin/jvm/functions/Function3;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1 ()Z
public final fun component2 ()Lio/getstream/video/android/compose/ui/components/video/VideoScalingType;
public final fun component3 ()Lkotlin/jvm/functions/Function3;
public final fun copy (ZLio/getstream/video/android/compose/ui/components/video/VideoScalingType;Lkotlin/jvm/functions/Function3;)Lio/getstream/video/android/compose/ui/components/video/config/VideoRendererConfig;
public static synthetic fun copy$default (Lio/getstream/video/android/compose/ui/components/video/config/VideoRendererConfig;ZLio/getstream/video/android/compose/ui/components/video/VideoScalingType;Lkotlin/jvm/functions/Function3;ILjava/lang/Object;)Lio/getstream/video/android/compose/ui/components/video/config/VideoRendererConfig;
public fun equals (Ljava/lang/Object;)Z
public final fun getFallbackContent ()Lkotlin/jvm/functions/Function3;
public final fun getMirrorStream ()Z
public final fun getScalingType ()Lio/getstream/video/android/compose/ui/components/video/VideoScalingType;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}

public final class io/getstream/video/android/compose/ui/components/video/config/VideoRendererConfigCreationScope {
public static final field $stable I
public fun <init> ()V
public fun <init> (ZLio/getstream/video/android/compose/ui/components/video/VideoScalingType;Lkotlin/jvm/functions/Function3;)V
public synthetic fun <init> (ZLio/getstream/video/android/compose/ui/components/video/VideoScalingType;Lkotlin/jvm/functions/Function3;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1 ()Z
public final fun component2 ()Lio/getstream/video/android/compose/ui/components/video/VideoScalingType;
public final fun component3 ()Lkotlin/jvm/functions/Function3;
public final fun copy (ZLio/getstream/video/android/compose/ui/components/video/VideoScalingType;Lkotlin/jvm/functions/Function3;)Lio/getstream/video/android/compose/ui/components/video/config/VideoRendererConfigCreationScope;
public static synthetic fun copy$default (Lio/getstream/video/android/compose/ui/components/video/config/VideoRendererConfigCreationScope;ZLio/getstream/video/android/compose/ui/components/video/VideoScalingType;Lkotlin/jvm/functions/Function3;ILjava/lang/Object;)Lio/getstream/video/android/compose/ui/components/video/config/VideoRendererConfigCreationScope;
public fun equals (Ljava/lang/Object;)Z
public final fun getFallbackContent ()Lkotlin/jvm/functions/Function3;
public final fun getMirrorStream ()Z
public final fun getVideoScalingType ()Lio/getstream/video/android/compose/ui/components/video/VideoScalingType;
public fun hashCode ()I
public final fun setFallbackContent (Lkotlin/jvm/functions/Function3;)V
public final fun setMirrorStream (Z)V
public final fun setVideoScalingType (Lio/getstream/video/android/compose/ui/components/video/VideoScalingType;)V
public fun toString ()Ljava/lang/String;
}

public final class io/getstream/video/android/compose/ui/components/video/config/VideoRendererConfigKt {
public static final fun videoRenderConfig (Lkotlin/jvm/functions/Function1;)Lio/getstream/video/android/compose/ui/components/video/config/VideoRendererConfig;
public static synthetic fun videoRenderConfig$default (Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lio/getstream/video/android/compose/ui/components/video/config/VideoRendererConfig;
}

Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.material.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
Expand Down Expand Up @@ -254,19 +255,36 @@ internal fun DefaultPictureInPictureContent(call: Call) {
video = video?.value,
)
} else {
val activeSpeakers by call.state.activeSpeakers.collectAsStateWithLifecycle()
val me by call.state.me.collectAsStateWithLifecycle()
val participants by call.state.participants.collectAsStateWithLifecycle()
val notMeOfTwo by remember {
// Special case where there are only two participants to take always the other participant,
// regardless of video track.
derivedStateOf {
participants.takeIf {
it.size == 2
}?.firstOrNull { it.sessionId != me?.sessionId }
}
}
val activeSpeakers by call.state.activeSpeakers.collectAsStateWithLifecycle()
val dominantSpeaker by call.state.dominantSpeaker.collectAsStateWithLifecycle()
val notMeActiveOrDominant by remember {
derivedStateOf {
val activeNotMe = activeSpeakers.firstOrNull {
it.sessionId != me?.sessionId
}
val dominantNotMe = dominantSpeaker?.takeUnless {
it.sessionId == me?.sessionId
}

if (activeSpeakers.isNotEmpty()) {
ParticipantVideo(
call = call,
participant = activeSpeakers.first(),
style = RegularVideoRendererStyle(labelPosition = Alignment.BottomStart),
)
} else if (me != null) {
activeNotMe ?: dominantNotMe
}
}
val participantToShow = notMeOfTwo ?: notMeActiveOrDominant ?: me
if (participantToShow != null) {
ParticipantVideo(
call = call,
participant = me!!,
participant = participantToShow,
style = RegularVideoRendererStyle(labelPosition = Alignment.BottomStart),
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,9 @@ import io.getstream.video.android.compose.ui.components.indicator.GenericIndicat
import io.getstream.video.android.compose.ui.components.indicator.NetworkQualityIndicator
import io.getstream.video.android.compose.ui.components.indicator.SoundIndicator
import io.getstream.video.android.compose.ui.components.video.VideoRenderer
import io.getstream.video.android.compose.ui.components.video.config.videoRenderConfig
import io.getstream.video.android.core.Call
import io.getstream.video.android.core.CameraDirection
import io.getstream.video.android.core.ParticipantState
import io.getstream.video.android.core.model.NetworkQuality
import io.getstream.video.android.core.model.Reaction
Expand Down Expand Up @@ -246,11 +248,20 @@ public fun ParticipantVideoRenderer(
}

val video by participant.video.collectAsStateWithLifecycle()

val cameraDirection by call.camera.direction.collectAsStateWithLifecycle()
val me by call.state.me.collectAsStateWithLifecycle()
val mirror by remember {
derivedStateOf {
participant.sessionId == me?.sessionId && cameraDirection == CameraDirection.Front
}
}
VideoRenderer(
call = call,
video = video,
videoFallbackContent = videoFallbackContent,
videoRendererConfig = videoRenderConfig {
mirrorStream = mirror
this.fallbackContent = videoFallbackContent
},
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ import androidx.compose.ui.viewinterop.AndroidView
import io.getstream.log.StreamLog
import io.getstream.video.android.compose.theme.VideoTheme
import io.getstream.video.android.compose.ui.components.video.VideoScalingType.Companion.toCommonScalingType
import io.getstream.video.android.compose.ui.components.video.config.VideoRendererConfig
import io.getstream.video.android.compose.ui.components.video.config.videoRenderConfig
import io.getstream.video.android.core.Call
import io.getstream.video.android.core.ParticipantState
import io.getstream.video.android.core.model.MediaTrack
Expand All @@ -55,28 +57,12 @@ import io.getstream.video.android.mock.previewCall
import io.getstream.video.android.ui.common.renderer.StreamVideoTextureViewRenderer
import io.getstream.webrtc.android.ui.VideoTextureViewRenderer

/**
* Renders a single video track based on the call state.
*
* @param call The call state that contains all the tracks and participants.
* @param video A media contains a video track or an audio track to be rendered.
* @param modifier Modifier for styling.
* @param videoScalingType Setup the video scale type of this renderer.
* @param videoFallbackContent Content is shown the video track is failed to load or not available.
* @param onRendered An interface that will be invoked when the video is rendered.
*/
@Composable
public fun VideoRenderer(
modifier: Modifier = Modifier,
call: Call,
video: ParticipantState.Media?,
modifier: Modifier = Modifier,
videoScalingType: VideoScalingType = VideoScalingType.SCALE_ASPECT_FILL,
videoFallbackContent: @Composable (Call) -> Unit = {
DefaultMediaTrackFallbackContent(
modifier,
call,
)
},
videoRendererConfig: VideoRendererConfig = videoRenderConfig(),
onRendered: (VideoTextureViewRenderer) -> Unit = {},
) {
if (LocalInspectionMode.current) {
Expand All @@ -94,7 +80,7 @@ public fun VideoRenderer(
}

// Show avatar always behind the video.
videoFallbackContent.invoke(call)
videoRendererConfig.fallbackContent.invoke(call)

if (video?.enabled == true) {
val mediaTrack = video.track
Expand Down Expand Up @@ -125,7 +111,10 @@ public fun VideoRenderer(
trackType = trackType,
onRendered = onRendered,
)
setScalingType(scalingType = videoScalingType.toCommonScalingType())
setMirror(videoRendererConfig.mirrorStream)
setScalingType(
scalingType = videoRendererConfig.scalingType.toCommonScalingType(),
)
setupVideo(mediaTrack, this)

view = this
Expand All @@ -139,6 +128,43 @@ public fun VideoRenderer(
}
}

/**
* Renders a single video track based on the call state.
*
* @param call The call state that contains all the tracks and participants.
* @param video A media contains a video track or an audio track to be rendered.
* @param modifier Modifier for styling.
* @param videoScalingType Setup the video scale type of this renderer.
* @param videoFallbackContent Content is shown the video track is failed to load or not available.
* @param onRendered An interface that will be invoked when the video is rendered.
*/
@Deprecated("Use VideoRenderer which accepts `videoConfig` instead.")
@Composable
public fun VideoRenderer(
call: Call,
video: ParticipantState.Media?,
modifier: Modifier = Modifier,
videoScalingType: VideoScalingType = VideoScalingType.SCALE_ASPECT_FILL,
videoFallbackContent: @Composable (Call) -> Unit = {
DefaultMediaTrackFallbackContent(
modifier,
call,
)
},
onRendered: (VideoTextureViewRenderer) -> Unit = {},
) {
VideoRenderer(
call = call,
video = video,
modifier = modifier,
videoRendererConfig = videoRenderConfig {
this.videoScalingType = videoScalingType
this.fallbackContent = videoFallbackContent
},
onRendered = onRendered,
)
}

private fun cleanTrack(
view: VideoTextureViewRenderer?,
mediaTrack: MediaTrack?,
Expand All @@ -154,7 +180,6 @@ private fun cleanTrack(
}
}
}

private fun setupVideo(
mediaTrack: MediaTrack?,
renderer: VideoTextureViewRenderer,
Expand All @@ -171,7 +196,7 @@ private fun setupVideo(
}

@Composable
private fun DefaultMediaTrackFallbackContent(
internal fun DefaultMediaTrackFallbackContent(
modifier: Modifier,
call: Call,
) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright (c) 2014-2024 Stream.io Inc. All rights reserved.
*
* Licensed under the Stream License;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://github.com/GetStream/stream-video-android/blob/main/LICENSE
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.getstream.video.android.compose.ui.components.video.config

import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.ui.Modifier
import io.getstream.video.android.compose.ui.components.video.DefaultMediaTrackFallbackContent
import io.getstream.video.android.compose.ui.components.video.VideoScalingType
import io.getstream.video.android.core.Call

@Immutable
public data class VideoRendererConfig(
val mirrorStream: Boolean = false,
val scalingType: VideoScalingType = VideoScalingType.SCALE_ASPECT_FILL,
val fallbackContent: @Composable (Call) -> Unit = {},
)

@Immutable
public data class VideoRendererConfigCreationScope(
public var mirrorStream: Boolean = false,
public var videoScalingType: VideoScalingType = VideoScalingType.SCALE_ASPECT_FILL,
public var fallbackContent: @Composable (Call) -> Unit = {
DefaultMediaTrackFallbackContent(
modifier = Modifier,
call = it,
)
},
)

/**
* A builder method for a video renderer config.
*/
public inline fun videoRenderConfig(
block: VideoRendererConfigCreationScope.() -> Unit = {},
): VideoRendererConfig {
val scope = VideoRendererConfigCreationScope()
scope.block()
return VideoRendererConfig(
mirrorStream = scope.mirrorStream,
scalingType = scope.videoScalingType,
fallbackContent = scope.fallbackContent,
)
}

0 comments on commit b7391b4

Please sign in to comment.