fix: PiP aspect ratio matches remote video when available
- Track remote video aspect ratio in CallController via VideoFrame rotatedWidth/rotatedHeight - PiP uses video's aspect ratio when remote video is active, falls back to 9:16 portrait for audio-only calls - Observe isRemoteVideoActive to update PiP params when video starts/stops during a call https://claude.ai/code/session_01Ak5tTkujpjNG1r5ASuPipZ
This commit is contained in:
@@ -82,11 +82,18 @@ class CallController(
|
||||
// Remote video frame monitoring — detects when peer stops sending video
|
||||
private val _isRemoteVideoActive = MutableStateFlow(false)
|
||||
val isRemoteVideoActive: StateFlow<Boolean> = _isRemoteVideoActive.asStateFlow()
|
||||
private val _remoteVideoAspectRatio = MutableStateFlow<Float?>(null)
|
||||
val remoteVideoAspectRatio: StateFlow<Float?> = _remoteVideoAspectRatio.asStateFlow()
|
||||
private val lastRemoteFrameTimeMs = AtomicLong(0L)
|
||||
private var remoteVideoMonitorJob: kotlinx.coroutines.Job? = null
|
||||
private val remoteFrameSink =
|
||||
VideoSink { _: VideoFrame ->
|
||||
VideoSink { frame: VideoFrame ->
|
||||
lastRemoteFrameTimeMs.set(System.currentTimeMillis())
|
||||
val w = frame.rotatedWidth
|
||||
val h = frame.rotatedHeight
|
||||
if (w > 0 && h > 0) {
|
||||
_remoteVideoAspectRatio.value = w.toFloat() / h.toFloat()
|
||||
}
|
||||
}
|
||||
|
||||
// Audio/video toggle state (UI concerns, not domain state)
|
||||
@@ -358,6 +365,7 @@ class CallController(
|
||||
_isAudioMuted.value = false
|
||||
_isVideoEnabled.value = false
|
||||
_isRemoteVideoActive.value = false
|
||||
_remoteVideoAspectRatio.value = null
|
||||
videoPausedByProximity = false
|
||||
webRtcSession?.dispose()
|
||||
webRtcSession = null
|
||||
|
||||
@@ -35,6 +35,7 @@ import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.commons.call.CallState
|
||||
import com.vitorpamplona.amethyst.ui.theme.AmethystTheme
|
||||
@@ -79,6 +80,7 @@ class CallActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
registerPipReceiver()
|
||||
observeVideoStateForPip(callController)
|
||||
|
||||
setContent {
|
||||
AmethystTheme {
|
||||
@@ -140,6 +142,15 @@ class CallActivity : AppCompatActivity() {
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
private fun observeVideoStateForPip(controller: com.vitorpamplona.amethyst.service.call.CallController?) {
|
||||
controller ?: return
|
||||
lifecycleScope.launch {
|
||||
controller.isRemoteVideoActive.collect {
|
||||
updatePipParams()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun enterPipIfActive() {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return
|
||||
val callManager = ActiveCallHolder.callManager ?: return
|
||||
@@ -168,10 +179,11 @@ class CallActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
private fun buildPipParams(): PictureInPictureParams {
|
||||
val aspectRatio = computePipAspectRatio()
|
||||
val builder =
|
||||
PictureInPictureParams
|
||||
.Builder()
|
||||
.setAspectRatio(Rational(9, 16))
|
||||
.setAspectRatio(aspectRatio)
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
builder.setActions(buildPipActions())
|
||||
@@ -180,6 +192,22 @@ class CallActivity : AppCompatActivity() {
|
||||
return builder.build()
|
||||
}
|
||||
|
||||
private fun computePipAspectRatio(): Rational {
|
||||
val controller = ActiveCallHolder.callController ?: return Rational(9, 16)
|
||||
val isVideoActive = controller.isRemoteVideoActive.value
|
||||
val videoRatio = controller.remoteVideoAspectRatio.value
|
||||
|
||||
if (isVideoActive && videoRatio != null && videoRatio > 0f) {
|
||||
// Clamp to Android's allowed PiP range (roughly 1:2.39 to 2.39:1)
|
||||
val clamped = videoRatio.coerceIn(0.42f, 2.39f)
|
||||
val num = (clamped * 1000).toInt()
|
||||
return Rational(num, 1000)
|
||||
}
|
||||
|
||||
// No active video — use portrait ratio
|
||||
return Rational(9, 16)
|
||||
}
|
||||
|
||||
private fun buildPipActions(): List<RemoteAction> {
|
||||
val actions = mutableListOf<RemoteAction>()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user