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