fix:v2版本优化
This commit is contained in:
parent
1015321979
commit
6e7adbbc2f
@ -74,30 +74,28 @@ class VideoDecoder(
|
||||
// 主线程Handler,用于安全切换onFrameRendered到主线程
|
||||
private val mainHandler = Handler(Looper.getMainLooper())
|
||||
|
||||
// 渲染帧率(fps),可由外部控制,默认18
|
||||
// 渲染帧率(fps),可由外部控制,默认30
|
||||
@Volatile
|
||||
var renderFps: Int = 20
|
||||
var renderFps: Int = 30
|
||||
|
||||
// 兜底:记录最近一次I帧的frameSeq,P/B帧依赖校验
|
||||
@Volatile
|
||||
private var lastIFrameSeq: Int? = null
|
||||
|
||||
// 解码输出帧时间戳队列(用于动态帧率统计和平滑)
|
||||
private val decodeTimestampQueue = ArrayDeque<Long>(20) // 最多保存20帧时间戳
|
||||
private val decodeTimestampLock = ReentrantLock() // 线程安全保护
|
||||
|
||||
// EMA平滑参数
|
||||
@Volatile
|
||||
private var smoothedFps: Double = 25.0 // 平滑后的渲染帧率
|
||||
private val alpha = 0.2 // EMA平滑系数,越大响应越快
|
||||
private val minFps = 8 // 渲染帧率下限,防止过低
|
||||
private val maxFps = 30 // 渲染帧率上限,防止过高
|
||||
private val maxStep = 2.0 // 单次最大调整幅度,防止突变
|
||||
// ====== 动态帧率统计与平滑相关内容已废弃,以下变量保留注释以便后续扩展 ======
|
||||
// private val decodeTimestampQueue = ArrayDeque<Long>(20) // 最多保存20帧时间戳
|
||||
// private val decodeTimestampLock = ReentrantLock() // 线程安全保护
|
||||
// @Volatile
|
||||
// private var smoothedFps: Double = 25.0 // 平滑后的渲染帧率
|
||||
// private val alpha = 0.2 // EMA平滑系数,越大响应越快
|
||||
// private val minFps = 8 // 渲染帧率下限,防止过低
|
||||
// private val maxFps = 30 // 渲染帧率上限,防止过高
|
||||
// private val maxStep = 2.0 // 单次最大调整幅度,防止突变
|
||||
|
||||
// 1. 新增成员变量
|
||||
@Volatile
|
||||
private var latestRenderedTimestampMs: Long? = null
|
||||
private val MAX_ALLOWED_DELAY_MS = 550 // 最大允许延迟,单位毫秒
|
||||
private val MAX_ALLOWED_DELAY_MS = 650 // 最大允许延迟,单位毫秒
|
||||
@Volatile
|
||||
private var timestampBaseMs: Long? = null
|
||||
@Volatile
|
||||
@ -143,7 +141,9 @@ class VideoDecoder(
|
||||
format.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, width * height)
|
||||
format.setInteger(MediaFormat.KEY_FRAME_RATE, renderFps)
|
||||
format.setInteger(MediaFormat.KEY_LOW_LATENCY, 1);
|
||||
|
||||
format.setInteger(MediaFormat.KEY_PRIORITY, 0) // 最高优先级
|
||||
format.setInteger(MediaFormat.KEY_OPERATING_RATE, renderFps * 2) // 解码速率
|
||||
format.setInteger(MediaFormat.KEY_MAX_B_FRAMES, 0) // 禁用B帧
|
||||
// 创建解码器
|
||||
val decoder = MediaCodec.createDecoderByType(mime)
|
||||
// 设置解码回调
|
||||
@ -159,7 +159,7 @@ class VideoDecoder(
|
||||
val absTimestamp = base + (frame.timestamp - firstRel)
|
||||
val now = System.currentTimeMillis()
|
||||
if (absTimestamp < now - MAX_ALLOWED_DELAY_MS) {
|
||||
Log.w(TAG, "[onInputBufferAvailable] Drop frame due to delay: absFrameTs=$absTimestamp, now=$now, maxDelay=$MAX_ALLOWED_DELAY_MS")
|
||||
Log.w(TAG, "[onInputBufferAvailable] 丢弃延迟帧: absFrameTs=$absTimestamp, now=$now, maxDelay=$MAX_ALLOWED_DELAY_MS, seq=${frame.frameSeq}")
|
||||
frameSeqSet.remove(frame.frameSeq)
|
||||
codec.queueInputBuffer(index, 0, 0, 0, 0)
|
||||
return
|
||||
@ -171,6 +171,7 @@ class VideoDecoder(
|
||||
inputBuffer.put(frame.data)
|
||||
val start = System.nanoTime()
|
||||
val ptsUs = absTimestamp * 1000L // 6. 送入解码器用绝对时间戳
|
||||
// Log.d(TAG, "[MediaCodec] 入解码: type=${frame.frameType}, seq=${frame.frameSeq}, pts=$ptsUs, bufferIdx=$index")
|
||||
codec.queueInputBuffer(
|
||||
index,
|
||||
0,
|
||||
@ -187,14 +188,7 @@ class VideoDecoder(
|
||||
}
|
||||
override fun onOutputBufferAvailable(codec: MediaCodec, index: Int, info: MediaCodec.BufferInfo) {
|
||||
if (!running) return
|
||||
// 记录解码输出时间戳
|
||||
val now = SystemClock.elapsedRealtime()
|
||||
decodeTimestampLock.withLock {
|
||||
if (decodeTimestampQueue.size >= 20) {
|
||||
decodeTimestampQueue.removeFirst()
|
||||
}
|
||||
decodeTimestampQueue.addLast(now)
|
||||
}
|
||||
// ====== 动态帧率统计相关代码已注释,如需帧率统计可恢复 ======
|
||||
// 解码后帧入输出缓冲区,由渲染线程处理
|
||||
val frame = DecodedFrame(codec, index, MediaCodec.BufferInfo().apply {
|
||||
set(0, info.size, info.presentationTimeUs, info.flags)
|
||||
@ -203,7 +197,9 @@ class VideoDecoder(
|
||||
// 缓冲区满,丢弃最旧帧再插入
|
||||
outputFrameQueue.poll()
|
||||
outputFrameQueue.offer(frame)
|
||||
Log.w(TAG, "[MediaCodec] outputFrameQueue溢出,丢弃最旧帧,当前队列=${outputFrameQueue.size}")
|
||||
}
|
||||
// Log.d(TAG, "[MediaCodec] 输出: bufferIdx=$index, pts=${info.presentationTimeUs}, 当前outputFrameQueue=${outputFrameQueue.size}")
|
||||
}
|
||||
override fun onError(codec: MediaCodec, e: MediaCodec.CodecException) {
|
||||
Log.e(TAG, "MediaCodec error", e)
|
||||
@ -229,6 +225,9 @@ class VideoDecoder(
|
||||
mainHandler.post { onFrameRendered() }
|
||||
hasNotifiedFlutter = true
|
||||
}
|
||||
Log.d(TAG, "[Render] 渲染: bufferIdx=${frame.bufferIndex}, pts=${frame.timestampUs}, 当前outputFrameQueue=${outputFrameQueue.size}")
|
||||
} else {
|
||||
Log.w(TAG, "[Render] 渲染空转,无帧可渲染,当前outputFrameQueue=${outputFrameQueue.size}")
|
||||
}
|
||||
// 若outputFrameQueue为空,跳过本次渲染,实现线性调度
|
||||
} catch (e: Exception) {
|
||||
@ -253,7 +252,10 @@ class VideoDecoder(
|
||||
refIFrameSeq: Int?
|
||||
): Boolean {
|
||||
if (!running || mediaCodec == null) return false
|
||||
if (!frameSeqSet.add(frameSeq)) return false // 防止重复帧
|
||||
if (!frameSeqSet.add(frameSeq)) {
|
||||
Log.w(TAG, "[decodeFrame] 丢弃重复帧: type=$frameType, seq=$frameSeq, refI=$refIFrameSeq, ts=$timestamp")
|
||||
return false // 防止重复帧
|
||||
}
|
||||
// 2. 初始化起点
|
||||
if (timestampBaseMs == null) {
|
||||
synchronized(this) {
|
||||
@ -270,7 +272,7 @@ class VideoDecoder(
|
||||
// 3. decodeFrame延迟丢弃判断(用系统时间)
|
||||
val now = System.currentTimeMillis()
|
||||
if (absTimestamp < now - MAX_ALLOWED_DELAY_MS) {
|
||||
Log.w(TAG, "[decodeFrame] Drop frame due to delay: absFrameTs=$absTimestamp, now=$now, maxDelay=$MAX_ALLOWED_DELAY_MS")
|
||||
Log.w(TAG, "[decodeFrame] 丢弃延迟帧: type=$frameType, seq=$frameSeq, absTs=$absTimestamp, now=$now, maxDelay=$MAX_ALLOWED_DELAY_MS")
|
||||
return false
|
||||
}
|
||||
// ===== 帧重排序缓冲区机制 =====
|
||||
@ -278,36 +280,43 @@ class VideoDecoder(
|
||||
if (frameType == 0) { // I帧
|
||||
receivedIFrames.add(frameSeq)
|
||||
lastIFrameSeq = frameSeq
|
||||
// Log.d(TAG, "[reorder] I帧到达: seq=$frameSeq, 当前缓存P帧数=${reorderBuffer.size}")
|
||||
// I帧直接入解码队列
|
||||
inputFrameQueue.offer(FrameData(frameData, frameType, timestamp, frameSeq, refIFrameSeq), 50, TimeUnit.MILLISECONDS)
|
||||
inputFrameQueue.offer(FrameData(frameData, frameType, timestamp, frameSeq, refIFrameSeq), 150, TimeUnit.MILLISECONDS)
|
||||
// 检查缓冲区,入队所有依赖于该I帧的P帧
|
||||
val readyPFrames = reorderBuffer.values.filter { it.refIFrameSeq == frameSeq }
|
||||
.sortedBy { it.frameSeq }
|
||||
// Log.d(TAG, "[reorder] I帧释放P帧: 依赖seq=$frameSeq, 释放P帧数=${readyPFrames.size}")
|
||||
for (pFrame in readyPFrames) {
|
||||
inputFrameQueue.offer(pFrame, 50, TimeUnit.MILLISECONDS)
|
||||
inputFrameQueue.offer(pFrame, 150, TimeUnit.MILLISECONDS)
|
||||
reorderBuffer.remove(pFrame.frameSeq)
|
||||
}
|
||||
// 清理过期P帧(如缓冲区过大)
|
||||
if (reorderBuffer.size > MAX_REORDER_BUFFER_SIZE) {
|
||||
val toRemove = reorderBuffer.keys.sorted().take(reorderBuffer.size - MAX_REORDER_BUFFER_SIZE)
|
||||
Log.w(TAG, "[reorder] 缓冲区溢出,清理P帧: 清理数=${toRemove.size}")
|
||||
toRemove.forEach { reorderBuffer.remove(it) }
|
||||
}
|
||||
// Log.d(TAG, "[decodeFrame] 入队I帧: seq=$frameSeq, ts=$timestamp, 当前inputFrameQueue=${inputFrameQueue.size}")
|
||||
return true
|
||||
} else { // P帧
|
||||
val lastI = lastIFrameSeq
|
||||
// 只有依赖的I帧已收到,才允许入队,否则暂存
|
||||
if (refIFrameSeq != null && receivedIFrames.contains(refIFrameSeq)) {
|
||||
inputFrameQueue.offer(FrameData(frameData, frameType, timestamp, frameSeq, refIFrameSeq), 50, TimeUnit.MILLISECONDS)
|
||||
inputFrameQueue.offer(FrameData(frameData, frameType, timestamp, frameSeq, refIFrameSeq), 150, TimeUnit.MILLISECONDS)
|
||||
// Log.d(TAG, "[decodeFrame] 入队P帧: seq=$frameSeq, refI=$refIFrameSeq, ts=$timestamp, 当前inputFrameQueue=${inputFrameQueue.size}")
|
||||
return true
|
||||
} else {
|
||||
// 暂存到重排序缓冲区
|
||||
reorderBuffer[frameSeq] = FrameData(frameData, frameType, timestamp, frameSeq, refIFrameSeq)
|
||||
Log.d(TAG, "[reorder] P帧缓存: seq=$frameSeq, refI=$refIFrameSeq, 当前缓存=${reorderBuffer.size}")
|
||||
// 控制缓冲区大小
|
||||
if (reorderBuffer.size > MAX_REORDER_BUFFER_SIZE) {
|
||||
val toRemove = reorderBuffer.keys.sorted().take(reorderBuffer.size - MAX_REORDER_BUFFER_SIZE)
|
||||
Log.w(TAG, "[reorder] 缓冲区溢出,清理P帧: 清理数=${toRemove.size}")
|
||||
toRemove.forEach { reorderBuffer.remove(it) }
|
||||
}
|
||||
Log.w(TAG, "[decodeFrame] P-frame cached: frameSeq=$frameSeq, refIFrameSeq=$refIFrameSeq, waiting for I-frame.")
|
||||
Log.w(TAG, "[decodeFrame] P帧暂存: seq=$frameSeq, refI=$refIFrameSeq, 等待I帧")
|
||||
return false
|
||||
}
|
||||
}
|
||||
@ -334,33 +343,25 @@ class VideoDecoder(
|
||||
} catch (_: Exception) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算最近N帧的平均解码帧率(fps)
|
||||
*/
|
||||
private fun calculateDecodeFps(): Double {
|
||||
decodeTimestampLock.withLock {
|
||||
if (decodeTimestampQueue.size < 2) return renderFps.toDouble()
|
||||
val first = decodeTimestampQueue.first()
|
||||
val last = decodeTimestampQueue.last()
|
||||
val frameCount = decodeTimestampQueue.size - 1
|
||||
val durationMs = (last - first).coerceAtLeast(1L)
|
||||
return frameCount * 1000.0 / durationMs
|
||||
}
|
||||
}
|
||||
// private fun calculateDecodeFps(): Double {
|
||||
// decodeTimestampLock.withLock {
|
||||
// if (decodeTimestampQueue.size < 2) return renderFps.toDouble()
|
||||
// val first = decodeTimestampQueue.first()
|
||||
// val last = decodeTimestampQueue.last()
|
||||
// val frameCount = decodeTimestampQueue.size - 1
|
||||
// val durationMs = (last - first).coerceAtLeast(1L)
|
||||
// return frameCount * 1000.0 / durationMs
|
||||
// }
|
||||
// }
|
||||
|
||||
/**
|
||||
* EMA平滑更新渲染帧率
|
||||
* @param measuredFps 当前测得的解码帧率
|
||||
* @return 平滑后的渲染帧率(取整)
|
||||
*/
|
||||
private fun updateSmoothedFps(measuredFps: Double): Int {
|
||||
// measuredFps边界保护
|
||||
val safeFps = measuredFps.coerceIn(minFps.toDouble(), maxFps.toDouble())
|
||||
val targetFps = alpha * safeFps + (1 - alpha) * smoothedFps
|
||||
val delta = targetFps - smoothedFps
|
||||
val step = delta.coerceIn(-maxStep, maxStep)
|
||||
smoothedFps = (smoothedFps + step).coerceIn(minFps.toDouble(), maxFps.toDouble())
|
||||
return smoothedFps.toInt()
|
||||
}
|
||||
// private fun updateSmoothedFps(measuredFps: Double): Int {
|
||||
// // measuredFps边界保护
|
||||
// val safeFps = measuredFps.coerceIn(minFps.toDouble(), maxFps.toDouble())
|
||||
// val targetFps = alpha * safeFps + (1 - alpha) * smoothedFps
|
||||
// val delta = targetFps - smoothedFps
|
||||
// val step = delta.coerceIn(-maxStep, maxStep)
|
||||
// smoothedFps = (smoothedFps + step).coerceIn(minFps.toDouble(), maxFps.toDouble())
|
||||
// return smoothedFps.toInt()
|
||||
// }
|
||||
// endregion
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user