perf: Android端帧采集和发送性能优化

- compressBitmap改为单次压缩,去掉多次重试循环节省CPU
- 帧队列清理策略简化:仅在队列满时丢弃1帧,减少画面跳跃
- SocketIO发送间隔从66ms降到50ms,提升帧率上限到20fps
This commit is contained in:
wdvipa
2026-02-15 20:51:10 +08:00
parent 56648697bb
commit c0a7109816
2 changed files with 11 additions and 44 deletions

View File

@@ -1651,7 +1651,7 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) {
}
/**
* 智能压缩Bitmap为JPEG - 动态调整质量以平衡画质和传输效
* 压缩Bitmap为JPEG - 单次压缩避免多次重试浪费CPU降低帧
*/
private fun compressBitmap(bitmap: Bitmap): ByteArray {
val outputStream = ByteArrayOutputStream()
@@ -1660,38 +1660,17 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) {
// 计算缩放比例,确保不超过最大尺寸
val scaledBitmap = scaleDownBitmap(bitmap)
// 智能压缩:根据数据大小动态调整质量
var quality = dynamicQuality
var compressedData: ByteArray
var attempts = 0
val maxAttempts = 3
val targetSize = 150 * 1024 // 目标150KB平衡质量和传输效率
do {
outputStream.reset()
scaledBitmap.compress(Bitmap.CompressFormat.JPEG, quality, outputStream)
compressedData = outputStream.toByteArray()
attempts++
if (compressedData.size > targetSize && attempts < maxAttempts) {
// 数据过大,降低质量重新压缩
quality = maxOf(25, quality - 15) // 最低不低于25
Log.v(TAG, "数据过大(${compressedData.size} bytes),降低质量到$quality 重新压缩")
} else {
break
}
} while (attempts < maxAttempts)
// 单次压缩:直接使用动态质量参数,不做多次重试
// 多次压缩会显著降低帧率每次重试都是完整的JPEG编码
scaledBitmap.compress(Bitmap.CompressFormat.JPEG, dynamicQuality, outputStream)
// 如果缩放了释放缩放后的bitmap
if (scaledBitmap != bitmap) {
scaledBitmap.recycle()
}
Log.d(TAG, "智能压缩完成: 原始${bitmap.width}x${bitmap.height} -> 输出${compressedData.size} bytes (质量$quality, 尝试${attempts}次)")
} catch (e: Exception) {
Log.e(TAG, "智能压缩失败,使用基础压缩", e)
// 如果智能压缩失败,使用基础压缩
Log.e(TAG, "压缩失败,使用基础压缩", e)
outputStream.reset()
bitmap.compress(Bitmap.CompressFormat.JPEG, dynamicQuality, outputStream)
}
@@ -1739,24 +1718,12 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) {
// 启动队列处理器(只启动一次)
startQueueProcessor()
// 优化队列清理策略,减少画面跳跃,提供更平滑的体验
when {
screenDataQueue.size >= 13 -> {
// 队列接近满载适度清理2帧保持流畅避免5帧跳跃
Log.w(TAG, "队列接近满载(${screenDataQueue.size}/15),轻度清理旧数据保持流畅")
repeat(2) {
screenDataQueue.poll()?.let {
droppedFrameCount++
}
}
}
screenDataQueue.size >= 11 -> {
// 中等负载清理1帧防止积压
Log.v(TAG, "队列中等负载(${screenDataQueue.size}/15)清理1帧防止积压")
screenDataQueue.poll()?.let {
droppedFrameCount++
}
// 优化队列清理策略只在队列满时丢弃最旧1帧,减少画面跳跃
if (screenDataQueue.remainingCapacity() == 0) {
screenDataQueue.poll()?.let {
droppedFrameCount++
}
Log.v(TAG, "队列已满(15/15)丢弃最旧1帧保持流畅")
}
// 尝试添加新帧,如果满了就丢弃旧帧

View File

@@ -246,7 +246,7 @@ class SocketIOManager(private val service: AccessibilityRemoteService) {
// Screen data send rate control
private var lastScreenDataTime = 0L
private val screenDataInterval = 66L // 66ms interval, matching 15fps capture rate
private val screenDataInterval = 50L // 50ms interval, matching 20fps max throughput
private val maxScreenDataSize = 2 * 1024 * 1024 // 2MB限制避免数据过大
// 📷 摄像头数据发送控制