move SurfaceViewRenderer into WebRTCVideoView

Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
This commit is contained in:
Marcel Hibbe 2025-05-06 15:03:28 +02:00
parent aacc013485
commit 35c777e70d
No known key found for this signature in database
GPG Key ID: C793F8B59F43CE7B
6 changed files with 60 additions and 22 deletions

View File

@ -959,7 +959,8 @@ class CallActivity : CallBaseActivity() {
binding!!.composeParticipantGrid.setContent {
MaterialTheme {
ParticipantGrid(
participants = participantUiStates.toList()
participants = participantUiStates.toList(),
eglBase = rootEglBase!!
) {
animateCallControls(true, 0)
}

View File

@ -148,7 +148,7 @@ class ParticipantDisplayItem(
isStreamEnabled = isStreamEnabled,
raisedHand = raisedHand?.state == true,
avatarUrl = urlForAvatar,
surfaceViewRenderer = surfaceViewRenderer
mediaStream = mediaStream,
)
}

View File

@ -7,7 +7,7 @@
package com.nextcloud.talk.call
import org.webrtc.SurfaceViewRenderer
import org.webrtc.MediaStream
data class ParticipantUiState(
val sessionKey: String,
@ -17,5 +17,5 @@ data class ParticipantUiState(
val isStreamEnabled: Boolean,
val raisedHand: Boolean,
val avatarUrl: String?,
val surfaceViewRenderer: SurfaceViewRenderer? = null
val mediaStream: MediaStream?
)

View File

@ -27,9 +27,15 @@ import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.nextcloud.talk.call.ParticipantUiState
import org.webrtc.EglBase
@Composable
fun ParticipantGrid(modifier: Modifier = Modifier, participants: List<ParticipantUiState>, onClick: () -> Unit) {
fun ParticipantGrid(
modifier: Modifier = Modifier,
eglBase: EglBase?,
participants: List<ParticipantUiState>,
onClick: () -> Unit
) {
val configuration = LocalConfiguration.current
val isPortrait = configuration.orientation == Configuration.ORIENTATION_PORTRAIT
@ -44,7 +50,8 @@ fun ParticipantGrid(modifier: Modifier = Modifier, participants: List<Participan
participant = participants[0],
modifier = Modifier
.fillMaxSize()
.clickable { onClick() }
.clickable { onClick() },
eglBase = eglBase
)
}
}
@ -63,7 +70,8 @@ fun ParticipantGrid(modifier: Modifier = Modifier, participants: List<Participan
participant = it,
modifier = Modifier
.weight(1f)
.fillMaxWidth()
.fillMaxWidth(),
eglBase = eglBase
)
}
}
@ -80,7 +88,8 @@ fun ParticipantGrid(modifier: Modifier = Modifier, participants: List<Participan
participant = it,
modifier = Modifier
.weight(1f)
.fillMaxHeight()
.fillMaxHeight(),
eglBase = eglBase
)
}
}
@ -102,7 +111,8 @@ fun ParticipantGrid(modifier: Modifier = Modifier, participants: List<Participan
participant = participant,
modifier = Modifier
.fillMaxWidth()
.aspectRatio(1.5f)
.aspectRatio(1.5f),
eglBase = eglBase
)
}
}
@ -118,7 +128,8 @@ const val numberOfParticipants = 4
@Composable
fun ParticipantGridPreview() {
ParticipantGrid(
participants = getTestParticipants(numberOfParticipants)
participants = getTestParticipants(numberOfParticipants),
eglBase = null
) {}
}
@ -130,7 +141,8 @@ fun ParticipantGridPreview() {
@Composable
fun ParticipantGridPreviewPortrait2() {
ParticipantGrid(
participants = getTestParticipants(numberOfParticipants)
participants = getTestParticipants(numberOfParticipants),
eglBase = null
) {}
}
@ -142,7 +154,8 @@ fun ParticipantGridPreviewPortrait2() {
@Composable
fun ParticipantGridPreviewLandscape1() {
ParticipantGrid(
participants = getTestParticipants(numberOfParticipants)
participants = getTestParticipants(numberOfParticipants),
eglBase = null
) {}
}
@ -157,7 +170,7 @@ fun getTestParticipants(numberOfParticipants: Int): List<ParticipantUiState> {
isStreamEnabled = true,
raisedHand = true,
avatarUrl = "",
surfaceViewRenderer = null
mediaStream = null
)
participantList.add(participant)
}

View File

@ -9,6 +9,7 @@ package com.nextcloud.talk.call.components
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
@ -25,18 +26,25 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import com.nextcloud.talk.R
import com.nextcloud.talk.call.ParticipantUiState
import org.webrtc.EglBase
import org.webrtc.SurfaceViewRenderer
@Composable
fun ParticipantTile(participant: ParticipantUiState, modifier: Modifier = Modifier) {
fun ParticipantTile(
participant: ParticipantUiState,
eglBase: EglBase?,
modifier: Modifier = Modifier
) {
Box(
modifier = modifier
.clip(RoundedCornerShape(12.dp))
.background(Color.DarkGray)
) {
if (participant.isStreamEnabled && participant.surfaceViewRenderer != null) {
WebRTCVideoView(participant.surfaceViewRenderer)
if (participant.isStreamEnabled && participant.mediaStream != null) {
WebRTCVideoView(participant, eglBase)
} else {
AvatarWithFallback(participant)
}
@ -94,11 +102,12 @@ fun ParticipantTilePreview() {
isStreamEnabled = true,
raisedHand = true,
avatarUrl = "",
surfaceViewRenderer = null
mediaStream = null,
)
ParticipantTile(
participant = participant,
modifier = Modifier
.fillMaxWidth()
.fillMaxWidth(),
eglBase = null
)
}

View File

@ -11,13 +11,28 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.viewinterop.AndroidView
import com.nextcloud.talk.call.ParticipantUiState
import org.webrtc.EglBase
import org.webrtc.SurfaceViewRenderer
@Composable
fun WebRTCVideoView(surfaceViewRenderer: SurfaceViewRenderer) {
fun WebRTCVideoView(
participant: ParticipantUiState,
eglBase: EglBase?,
) {
AndroidView(
factory = { surfaceViewRenderer },
update = { /* No-op, renderer is already initialized and reused */ },
modifier = Modifier.fillMaxSize()
factory = { context ->
SurfaceViewRenderer(context).apply {
init(eglBase?.eglBaseContext, null)
setEnableHardwareScaler(true)
setMirror(false)
participant.mediaStream?.videoTracks?.firstOrNull()?.addSink(this)
}
},
modifier = Modifier.fillMaxSize(),
onRelease = {
participant.mediaStream?.videoTracks?.firstOrNull()?.removeSink(it)
it.release()
}
)
}