优化视频资源释放逻辑,避免主线程阻塞,添加追帧状态更新日志
This commit is contained in:
Binary file not shown.
@@ -151,16 +151,27 @@ class InteractiveLiveActivity : AppCompatActivity() {
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
rtcEngine?.setCaptureVideoFrameInterceptor(null)
|
||||
leaveChannel()
|
||||
InteractiveRtcEngine.destroy(rtcEngine)
|
||||
rtcEngine = null
|
||||
localRenderer?.let { releaseRenderer(it) }
|
||||
remoteRendererMap.values.forEach { releaseRenderer(it) }
|
||||
remoteRendererMap.clear()
|
||||
fuFrameInterceptor = null
|
||||
try { beautyRenderer?.release() } catch (_: Exception) {}
|
||||
beautyRenderer = null
|
||||
remoteMediaState.clear()
|
||||
|
||||
// 捕获需要释放的引用,避免主线程阻塞导致 ANR
|
||||
val engine = rtcEngine
|
||||
val local = localRenderer
|
||||
val remotes = remoteRendererMap.values.toList()
|
||||
val beauty = beautyRenderer
|
||||
rtcEngine = null
|
||||
localRenderer = null
|
||||
remoteRendererMap.clear()
|
||||
beautyRenderer = null
|
||||
|
||||
// 重量级资源释放移到后台线程
|
||||
Thread {
|
||||
try { engine?.leaveChannel() } catch (_: Exception) {}
|
||||
try { InteractiveRtcEngine.destroy(engine) } catch (_: Exception) {}
|
||||
try { local?.release() } catch (_: Exception) {}
|
||||
remotes.forEach { try { it.release() } catch (_: Exception) {} }
|
||||
try { beauty?.release() } catch (_: Exception) {}
|
||||
}.start()
|
||||
}
|
||||
|
||||
override fun onSupportNavigateUp(): Boolean {
|
||||
@@ -685,19 +696,29 @@ class InteractiveLiveActivity : AppCompatActivity() {
|
||||
slot.layout.detachRenderer()
|
||||
updateSlotOverlay(slot)
|
||||
}
|
||||
rtcEngine?.clearRemoteVideo(userId)
|
||||
remoteRendererMap.remove(userId)?.let { releaseRenderer(it) }
|
||||
val engine = rtcEngine
|
||||
val renderer = remoteRendererMap.remove(userId)
|
||||
remoteStats.remove(userId)
|
||||
// SurfaceViewRenderer.release() 会死锁主线程,移到后台
|
||||
Thread {
|
||||
try { engine?.clearRemoteVideo(userId) } catch (_: Exception) {}
|
||||
try { renderer?.release() } catch (_: Exception) {}
|
||||
}.start()
|
||||
}
|
||||
|
||||
private fun resetVideoSlots(releaseRemotes: Boolean = true) {
|
||||
if (releaseRemotes) {
|
||||
val engine = rtcEngine
|
||||
val remoteIds = remoteRendererMap.keys.toList()
|
||||
remoteIds.forEach { userId ->
|
||||
rtcEngine?.clearRemoteVideo(userId)
|
||||
remoteRendererMap.remove(userId)?.let { releaseRenderer(it) }
|
||||
}
|
||||
val renderersToRelease = remoteIds.mapNotNull { remoteRendererMap.remove(it) }
|
||||
remoteStats.clear()
|
||||
// SurfaceViewRenderer.release() 会死锁主线程,移到后台
|
||||
Thread {
|
||||
remoteIds.forEach { userId ->
|
||||
try { engine?.clearRemoteVideo(userId) } catch (_: Exception) {}
|
||||
}
|
||||
renderersToRelease.forEach { try { it.release() } catch (_: Exception) {} }
|
||||
}.start()
|
||||
}
|
||||
remoteSlots.forEach { slot ->
|
||||
slot.userId = null
|
||||
@@ -722,7 +743,9 @@ class InteractiveLiveActivity : AppCompatActivity() {
|
||||
private fun displayId(userId: String): String = userId
|
||||
|
||||
private fun leaveChannel() {
|
||||
rtcEngine?.leaveChannel()
|
||||
// SDK 的 leaveChannel() 会同步停止 Whip/Whep 客户端,阻塞主线程
|
||||
val engine = rtcEngine
|
||||
Thread { try { engine?.leaveChannel() } catch (_: Exception) {} }.start()
|
||||
resetUiAfterLeave()
|
||||
}
|
||||
|
||||
|
||||
@@ -81,6 +81,7 @@ class LivePlayActivity : AppCompatActivity() {
|
||||
private var firstAudioFrameCostMs: Long? = null
|
||||
private var isLatencyChasingActive: Boolean = false
|
||||
private var lastLatencyChasingSpeed: Float? = null
|
||||
private var lastLatencyChasingUpdate: SellyLatencyChasingUpdate? = null
|
||||
private var hasReleasedPlayer: Boolean = false
|
||||
|
||||
private val logLines: ArrayDeque<String> = ArrayDeque()
|
||||
@@ -157,29 +158,35 @@ class LivePlayActivity : AppCompatActivity() {
|
||||
override fun onLatencyChasingUpdate(update: SellyLatencyChasingUpdate) {
|
||||
runOnUiThread {
|
||||
val speedRounded = kotlin.math.round(update.speed * 10f) / 10f
|
||||
val speedText = formatLatencyChasingSpeed(speedRounded)
|
||||
val chasingDetail = buildLatencyChasingDetail(update)
|
||||
lastLatencyChasingUpdate = update
|
||||
val isChasing = speedRounded > 1.0f
|
||||
if (isChasing && !isLatencyChasingActive) {
|
||||
isLatencyChasingActive = true
|
||||
val speedText = String.format(Locale.US, "%.1f", speedRounded)
|
||||
logEvent("追帧开始: 速度=${speedText}x")
|
||||
logEvent("追帧开始: 速度=${speedText}x, $chasingDetail")
|
||||
lastLatencyChasingSpeed = speedRounded
|
||||
} else if (isChasing && isLatencyChasingActive) {
|
||||
if (lastLatencyChasingSpeed == null || lastLatencyChasingSpeed != speedRounded) {
|
||||
val speedText = String.format(Locale.US, "%.1f", speedRounded)
|
||||
logEvent("追帧速率变化: 速度=${speedText}x")
|
||||
logEvent("追帧速率变化: 速度=${speedText}x, $chasingDetail")
|
||||
lastLatencyChasingSpeed = speedRounded
|
||||
}
|
||||
} else if (!isChasing && isLatencyChasingActive) {
|
||||
isLatencyChasingActive = false
|
||||
logEvent("追帧结束: 速度=1.0x")
|
||||
logEvent("追帧结束: 速度=1.0x, $chasingDetail")
|
||||
lastLatencyChasingSpeed = null
|
||||
} else {
|
||||
logEvent("追帧状态更新: 速度=${speedText}x, $chasingDetail")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onLatencyChasingReloadRequired(latencyMs: Long) {
|
||||
runOnUiThread {
|
||||
logEvent("追帧触发重载: 延迟=${latencyMs}ms")
|
||||
val lastUpdateDetail = lastLatencyChasingUpdate
|
||||
?.let { ", 最近追帧: ${buildLatencyChasingDetail(it)}" }
|
||||
.orEmpty()
|
||||
logEvent("追帧触发重载: 延迟=${latencyMs}ms$lastUpdateDetail")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -709,9 +716,27 @@ class LivePlayActivity : AppCompatActivity() {
|
||||
firstAudioFrameCostMs = null
|
||||
isLatencyChasingActive = false
|
||||
lastLatencyChasingSpeed = null
|
||||
lastLatencyChasingUpdate = null
|
||||
logEvent("播放尝试开始")
|
||||
}
|
||||
|
||||
private fun formatLatencyChasingSpeed(speed: Float): String {
|
||||
return String.format(Locale.US, "%.1f", speed)
|
||||
}
|
||||
|
||||
private fun buildLatencyChasingDetail(update: SellyLatencyChasingUpdate): String {
|
||||
val cacheDeltaMs = update.audioCachedMs - update.videoCachedMs
|
||||
val deltaText = if (cacheDeltaMs > 0L) "+$cacheDeltaMs" else cacheDeltaMs.toString()
|
||||
val cacheSkewTag = when {
|
||||
cacheDeltaMs >= CACHE_SKEW_WARNING_MS -> "音频缓存领先"
|
||||
cacheDeltaMs <= -CACHE_SKEW_WARNING_MS -> "视频缓存领先"
|
||||
else -> "音视频缓存接近"
|
||||
}
|
||||
return "延迟=${update.latencyMs}ms, 档位=${update.tier}, 缓冲中=${if (update.buffering) "是" else "否"}, " +
|
||||
"首帧已出=${if (update.firstFrameRendered) "是" else "否"}, 音频缓存=${update.audioCachedMs}ms, " +
|
||||
"视频缓存=${update.videoCachedMs}ms, 缓存差(音-视)=${deltaText}ms($cacheSkewTag)"
|
||||
}
|
||||
|
||||
private fun formatState(state: SellyPlayerState): String {
|
||||
return "${stateLabel(state)}(${state.name})"
|
||||
}
|
||||
@@ -744,6 +769,7 @@ class LivePlayActivity : AppCompatActivity() {
|
||||
companion object {
|
||||
private const val TAG = "LivePlayActivity"
|
||||
private const val MAX_LOG_LINES = 200
|
||||
private const val CACHE_SKEW_WARNING_MS = 300L
|
||||
@Volatile
|
||||
private var pipActivityRef: WeakReference<LivePlayActivity>? = null
|
||||
const val EXTRA_PLAY_PROTOCOL = "play_protocol"
|
||||
|
||||
Reference in New Issue
Block a user