From 6e7adbbc2feb3a4b39ce79e6f646619197d62cb2 Mon Sep 17 00:00:00 2001 From: liyi Date: Wed, 18 Jun 2025 14:38:05 +0800 Subject: [PATCH] =?UTF-8?q?fix:v2=E7=89=88=E6=9C=AC=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../video_decode_plugin/VideoDecoder.kt | 115 +++++++++--------- 1 file changed, 58 insertions(+), 57 deletions(-) diff --git a/android/src/main/kotlin/top/skychip/video_decode_plugin/VideoDecoder.kt b/android/src/main/kotlin/top/skychip/video_decode_plugin/VideoDecoder.kt index 7357f7b..b0765f0 100644 --- a/android/src/main/kotlin/top/skychip/video_decode_plugin/VideoDecoder.kt +++ b/android/src/main/kotlin/top/skychip/video_decode_plugin/VideoDecoder.kt @@ -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(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(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 } \ No newline at end of file