perf: Android端帧采集和发送性能优化
- compressBitmap改为单次压缩,去掉多次重试循环节省CPU - 帧队列清理策略简化:仅在队列满时丢弃1帧,减少画面跳跃 - SocketIO发送间隔从66ms降到50ms,提升帧率上限到20fps
This commit is contained in:
@@ -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帧保持流畅")
|
||||
}
|
||||
|
||||
// 尝试添加新帧,如果满了就丢弃旧帧
|
||||
|
||||
@@ -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限制,避免数据过大
|
||||
|
||||
// 📷 摄像头数据发送控制
|
||||
|
||||
Reference in New Issue
Block a user