diff --git a/stream-webrtc-android-compose/src/main/kotlin/io/getstream/webrtc/android/compose/VideoRenderer.kt b/stream-webrtc-android-compose/src/main/kotlin/io/getstream/webrtc/android/compose/VideoRenderer.kt index 3c33836bb..e302eaecf 100644 --- a/stream-webrtc-android-compose/src/main/kotlin/io/getstream/webrtc/android/compose/VideoRenderer.kt +++ b/stream-webrtc-android-compose/src/main/kotlin/io/getstream/webrtc/android/compose/VideoRenderer.kt @@ -16,18 +16,26 @@ package io.getstream.webrtc.android.compose +import androidx.compose.foundation.layout.BoxWithConstraints +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.size import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.MutableState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.unit.IntSize import androidx.compose.ui.viewinterop.AndroidView import io.getstream.webrtc.android.compose.VideoScalingType.Companion.toCommonScalingType import io.getstream.webrtc.android.ui.VideoTextureViewRenderer import org.webrtc.EglBase.Context +import org.webrtc.RendererCommon import org.webrtc.RendererCommon.RendererEvents import org.webrtc.VideoTrack @@ -48,6 +56,22 @@ public fun VideoRenderer( ) { val trackState: MutableState = remember { mutableStateOf(null) } var view: VideoTextureViewRenderer? by remember { mutableStateOf(null) } + var frameSize by remember(videoTrack) { mutableStateOf(IntSize.Zero) } + val currentRendererEvents by rememberUpdatedState(rendererEvents) + val forwardingRendererEvents = remember(videoTrack) { + object : RendererEvents { + override fun onFirstFrameRendered() { + currentRendererEvents.onFirstFrameRendered() + } + + override fun onFrameResolutionChanged(videoWidth: Int, videoHeight: Int, rotation: Int) { + if (videoWidth > 0 && videoHeight > 0) { + frameSize = IntSize(videoWidth, videoHeight) + } + currentRendererEvents.onFrameResolutionChanged(videoWidth, videoHeight, rotation) + } + } + } DisposableEffect(videoTrack) { onDispose { @@ -55,19 +79,66 @@ public fun VideoRenderer( } } - AndroidView( - factory = { context -> - VideoTextureViewRenderer(context).apply { - init(eglBaseContext, rendererEvents) - setScalingType(scalingType = videoScalingType.toCommonScalingType()) - setupVideo(trackState, videoTrack, this) - onTextureViewCreated.invoke(this) - view = this - } - }, - update = { v -> setupVideo(trackState, videoTrack, v) }, + BoxWithConstraints( modifier = modifier, + contentAlignment = Alignment.Center, + ) { + val density = LocalDensity.current + val rendererSize = with(density) { + calculateRendererSize( + maxWidth = maxWidth.roundToPx(), + maxHeight = maxHeight.roundToPx(), + frameSize = frameSize, + videoScalingType = videoScalingType, + ) + } + val rendererModifier = if (rendererSize == IntSize.Zero) { + Modifier.fillMaxSize() + } else { + with(density) { + Modifier.size( + width = rendererSize.width.toDp(), + height = rendererSize.height.toDp(), + ) + } + } + + AndroidView( + factory = { context -> + VideoTextureViewRenderer(context).apply { + init(eglBaseContext, forwardingRendererEvents) + setScalingType(scalingType = videoScalingType.toCommonScalingType()) + setupVideo(trackState, videoTrack, this) + onTextureViewCreated.invoke(this) + view = this + } + }, + update = { v -> + v.setScalingType(scalingType = videoScalingType.toCommonScalingType()) + setupVideo(trackState, videoTrack, v) + }, + modifier = rendererModifier, + ) + } +} + +private fun calculateRendererSize( + maxWidth: Int, + maxHeight: Int, + frameSize: IntSize, + videoScalingType: VideoScalingType, +): IntSize { + if (maxWidth == 0 || maxHeight == 0 || frameSize.width == 0 || frameSize.height == 0) { + return IntSize.Zero + } + + val displaySize = RendererCommon.getDisplaySize( + videoScalingType.toCommonScalingType(), + frameSize.width / frameSize.height.toFloat(), + maxWidth, + maxHeight, ) + return IntSize(displaySize.x, displaySize.y) } private fun cleanTrack(