fix: SurfaceView rather than TextureView
This commit is contained in:
@@ -29,9 +29,9 @@ class StreamActivity : ComponentActivity() {
|
|||||||
.setEnabled(false)
|
.setEnabled(false)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
// 是否处于 PiP。
|
// 是否处于 PiP
|
||||||
// 这台设备上进入 PiP 时,动画事件比 onPictureInPictureModeChanged() 更稳定,
|
// MIUI 上进入 PiP 时,动画事件比 onPictureInPictureModeChanged() 更稳定,
|
||||||
// 所以进入 PiP 直接由动画事件置为 true。
|
// 所以进入 PiP 直接由动画事件置为 true
|
||||||
private val _pipModeState = MutableStateFlow(false)
|
private val _pipModeState = MutableStateFlow(false)
|
||||||
val pipModeState: StateFlow<Boolean> = _pipModeState
|
val pipModeState: StateFlow<Boolean> = _pipModeState
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
package io.github.miuzarte.scrcpyforandroid.widgets
|
package io.github.miuzarte.scrcpyforandroid.widgets
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.graphics.SurfaceTexture
|
|
||||||
import android.view.Surface
|
import android.view.Surface
|
||||||
import android.view.TextureView
|
import android.view.SurfaceHolder
|
||||||
|
import android.view.SurfaceView
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.animation.core.animateFloatAsState
|
import androidx.compose.animation.core.animateFloatAsState
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
@@ -742,16 +742,16 @@ private fun PairingDialog(
|
|||||||
* ScrcpyVideoSurface
|
* ScrcpyVideoSurface
|
||||||
*
|
*
|
||||||
* Purpose:
|
* Purpose:
|
||||||
* - Hosts a `TextureView` and bridges its `Surface` to `nativeCore` for video rendering.
|
* - Hosts a `SurfaceView` and bridges its `Surface` to `nativeCore` for video rendering.
|
||||||
* - Ensures only a single `Surface` instance is registered at any time under the
|
* - Ensures only a single `Surface` instance is registered at any time under the
|
||||||
* stable `surfaceTag` ("video-main"). This reduces surface recreation bugs seen
|
* stable `surfaceTag` ("video-main"). This reduces surface recreation bugs seen
|
||||||
* when preview/fullscreen used separate tags.
|
* when preview/fullscreen used separate tags.
|
||||||
*
|
*
|
||||||
* Concurrency / lifecycle:
|
* Concurrency / lifecycle:
|
||||||
* - `currentSurface` is only mutated on the UI thread via TextureView callbacks.
|
* - `currentSurface` is only mutated on the UI thread via SurfaceHolder callbacks.
|
||||||
* - Registration to `nativeCore` is triggered from a [LaunchedEffect] when both
|
* - Registration to `nativeCore` is triggered from a [LaunchedEffect] when both
|
||||||
* `session` and `currentSurface` are available. Unregistration happens in
|
* `session` and `currentSurface` are available. Unregistration happens in
|
||||||
* `onSurfaceTextureDestroyed` and `DisposableEffect.onDispose` to guarantee
|
* `surfaceDestroyed` and `DisposableEffect.onDispose` to guarantee
|
||||||
* cleanup even if the composable leaves composition.
|
* cleanup even if the composable leaves composition.
|
||||||
*
|
*
|
||||||
* Reliability notes:
|
* Reliability notes:
|
||||||
@@ -765,10 +765,21 @@ fun ScrcpyVideoSurface(
|
|||||||
target: VideoOutputTarget,
|
target: VideoOutputTarget,
|
||||||
) {
|
) {
|
||||||
var currentSurface by remember { mutableStateOf<Surface?>(null) }
|
var currentSurface by remember { mutableStateOf<Surface?>(null) }
|
||||||
|
var currentSurfaceView by remember { mutableStateOf<SurfaceView?>(null) }
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val taskScope = remember { CoroutineScope(SupervisorJob() + Dispatchers.IO) }
|
val taskScope = remember { CoroutineScope(SupervisorJob() + Dispatchers.IO) }
|
||||||
val lifecycleOwner = LocalLifecycleOwner.current
|
val lifecycleOwner = LocalLifecycleOwner.current
|
||||||
val currentTarget by VideoOutputTargetState.current.collectAsState()
|
val currentTarget by VideoOutputTargetState.current.collectAsState()
|
||||||
|
val latestSession by rememberUpdatedState(session)
|
||||||
|
val latestCurrentTarget by rememberUpdatedState(currentTarget)
|
||||||
|
|
||||||
|
LaunchedEffect(session?.width, session?.height, currentSurfaceView) {
|
||||||
|
val surfaceView = currentSurfaceView ?: return@LaunchedEffect
|
||||||
|
val currentSession = session ?: return@LaunchedEffect
|
||||||
|
if (currentSession.width > 0 && currentSession.height > 0) {
|
||||||
|
surfaceView.holder.setFixedSize(currentSession.width, currentSession.height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
LaunchedEffect(session, currentSurface, currentTarget, target) {
|
LaunchedEffect(session, currentSurface, currentTarget, target) {
|
||||||
val surface = currentSurface
|
val surface = currentSurface
|
||||||
@@ -811,44 +822,43 @@ fun ScrcpyVideoSurface(
|
|||||||
AndroidView(
|
AndroidView(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
factory = { context ->
|
factory = { context ->
|
||||||
TextureView(context).apply {
|
SurfaceView(context).apply {
|
||||||
surfaceTextureListener = object : TextureView.SurfaceTextureListener {
|
currentSurfaceView = this
|
||||||
override fun onSurfaceTextureAvailable(
|
holder.addCallback(object : SurfaceHolder.Callback {
|
||||||
surfaceTexture: SurfaceTexture,
|
override fun surfaceCreated(holder: SurfaceHolder) {
|
||||||
width: Int,
|
val newSurface = holder.surface
|
||||||
height: Int
|
if (!newSurface.isValid) return
|
||||||
) {
|
|
||||||
@SuppressLint("Recycle")
|
|
||||||
val newSurface = Surface(surfaceTexture)
|
|
||||||
currentSurface = newSurface
|
currentSurface = newSurface
|
||||||
// Register immediately when surface becomes available
|
// Register immediately when surface becomes available
|
||||||
if (currentTarget == target && session != null) {
|
if (latestCurrentTarget == target && latestSession != null) {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
NativeCoreFacade.attachVideoSurface(newSurface)
|
NativeCoreFacade.attachVideoSurface(newSurface)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSurfaceTextureSizeChanged(
|
override fun surfaceChanged(
|
||||||
surfaceTexture: SurfaceTexture,
|
holder: SurfaceHolder,
|
||||||
|
format: Int,
|
||||||
width: Int,
|
width: Int,
|
||||||
height: Int
|
height: Int,
|
||||||
) = Unit
|
) {
|
||||||
|
if (width <= 0 || height <= 0) return
|
||||||
|
val surface = holder.surface
|
||||||
|
if (!surface.isValid) return
|
||||||
|
currentSurface = surface
|
||||||
|
}
|
||||||
|
|
||||||
override fun onSurfaceTextureDestroyed(surfaceTexture: SurfaceTexture): Boolean {
|
override fun surfaceDestroyed(holder: SurfaceHolder) {
|
||||||
val surface = currentSurface
|
val surface = currentSurface
|
||||||
if (surface != null) {
|
if (surface != null) {
|
||||||
taskScope.launch {
|
taskScope.launch {
|
||||||
NativeCoreFacade.detachVideoSurface(surface, releaseDecoder = false)
|
NativeCoreFacade.detachVideoSurface(surface, releaseDecoder = false)
|
||||||
}
|
}
|
||||||
surface.release()
|
|
||||||
currentSurface = null
|
currentSurface = null
|
||||||
}
|
}
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onSurfaceTextureUpdated(surfaceTexture: SurfaceTexture) = Unit
|
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
update = {},
|
update = {},
|
||||||
|
|||||||
Reference in New Issue
Block a user