Fit fullscreen call video along the screen without rotating controls
This commit is contained in:
1 parent
bea8f1f901
commit
8b438f26a5
3 files changed
+33
-7
No files matched your search
@@ -16,6 +16,7 @@ import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.geometry.Rect
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.unit.IntOffset
|
||||
@@ -179,9 +180,12 @@ internal fun CallVideoStage(video: CallVideoEngine, expanded: CallVideoSource?,
|
||||
CallVideoPlayback(video, click, frameModifier, description, large)
|
||||
} else BoxWithConstraints(frameModifier, contentAlignment = Alignment.Center) {
|
||||
val aspect = local.format?.aspect(displayDegrees) ?: 1f
|
||||
val width = minOf(maxWidth, maxHeight * aspect)
|
||||
val fit = fitVideoFrame(maxWidth.value, maxHeight.value, aspect, large)
|
||||
LaunchedEffect(fit, large) {
|
||||
LogManager.addLog("DEBUG", "call", "video local fit expanded=$large aspect=$aspect fit=$fit")
|
||||
}
|
||||
AndroidView(factory = { CameraPreviewView(it, video.preview, keepAwake = false) },
|
||||
modifier = Modifier.requiredSize(width, width / aspect), update = { view ->
|
||||
modifier = Modifier.requiredSize(fit.width.dp, fit.height.dp).graphicsLayer { rotationZ = fit.rotation }, update = { view ->
|
||||
view.update(local.format, displayDegrees); view.alpha = if (local.active) 1f else 0f
|
||||
view.contentDescription = description
|
||||
view.setOnClickListener { if (local.active) click() }; view.isClickable = local.active
|
||||
@@ -200,9 +204,17 @@ internal fun CallVideoStage(video: CallVideoEngine, expanded: CallVideoSource?,
|
||||
@Composable
|
||||
private fun CallVideoPlayback(video: CallVideoEngine, onTap: () -> Unit, modifier: Modifier, description: String, large: Boolean) {
|
||||
val format by video.display.collectAsState()
|
||||
Box(modifier) {
|
||||
BoxWithConstraints(modifier) {
|
||||
val rotated = format?.rotation?.rem(180) != 0 && format != null
|
||||
val aspect = format?.let { if (rotated) it.height.toFloat() / it.width else it.width.toFloat() / it.height } ?: 1f
|
||||
val fit = fitVideoFrame(maxWidth.value, maxHeight.value, aspect, large)
|
||||
LaunchedEffect(fit, format, large) {
|
||||
LogManager.addLog("INFO", "call", "video remote fit expanded=$large viewport=${maxWidth.value}x${maxHeight.value} " +
|
||||
"format=$format fit=$fit; interface orientation retained")
|
||||
}
|
||||
key(video) {
|
||||
AndroidView(factory = { CallVideoTexture(it, video) }, modifier = Modifier.matchParentSize(),
|
||||
AndroidView(factory = { CallVideoTexture(it, video) }, modifier = Modifier.requiredSize(fit.width.dp, fit.height.dp)
|
||||
.align(Alignment.Center).graphicsLayer { rotationZ = fit.rotation },
|
||||
update = { view -> view.update(format, large); view.alpha = if (format == null) 0f else 1f
|
||||
view.contentDescription = description
|
||||
view.setOnClickListener { if (format != null) onTap() }; view.isClickable = format != null }, onRelease = { it.close() })
|
||||
|
||||
+3
-3
@@ -100,9 +100,9 @@ internal fun StreamViewerScreen(group: Long, source: Long, epoch: Long, title: S
|
||||
|
||||
BoxWithConstraints(Modifier.fillMaxSize().background(Color.Black).clipToBounds()) {
|
||||
val ratio = aspect.takeIf { it > 0f } ?: 16f / 9f
|
||||
val turn = aspect > 0f && minOf(maxHeight, maxWidth * ratio) > minOf(maxWidth, maxHeight * ratio)
|
||||
val width = if (turn) minOf(maxHeight, maxWidth * ratio) else minOf(maxWidth, maxHeight * ratio)
|
||||
val height = width / ratio
|
||||
val fit = fitVideoFrame(maxWidth.value, maxHeight.value, ratio, aspect > 0f)
|
||||
val turn = fit.rotation != 0f
|
||||
val width = fit.width.dp; val height = fit.height.dp
|
||||
val density = LocalDensity.current
|
||||
// Жесты работают в координатах окна, поэтому учитывают размеры после поворота видео.
|
||||
val videoSize = with(density) { if (turn) Size(height.toPx(), width.toPx()) else Size(width.toPx(), height.toPx()) }
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.utun.chat.ui.screens
|
||||
|
||||
/** Размер слоя ДО rotation; поворот относится только к видео, не к окну/кнопкам. */
|
||||
internal data class VideoFrameFit(val width: Float, val height: Float, val rotation: Float)
|
||||
|
||||
/** Вписать целый кадр без растяжения; fullscreen выбирает 0/90° по максимальной площади. */
|
||||
internal fun fitVideoFrame(width: Float, height: Float, aspect: Float, allowTurn: Boolean): VideoFrameFit {
|
||||
val ratio = aspect.takeIf { it.isFinite() && it > 0f } ?: 1f
|
||||
val normal = minOf(width, height * ratio)
|
||||
val rotated = minOf(height, width * ratio)
|
||||
val turn = allowTurn && rotated > normal
|
||||
val fittedWidth = if (turn) rotated else normal
|
||||
return VideoFrameFit(fittedWidth, fittedWidth / ratio, if (turn) 90f else 0f)
|
||||
}
|
||||
Reference in new issue
Block a user