fix:调整渲染任务调度和渲染间隔
This commit is contained in:
parent
6e7adbbc2f
commit
c8a4e5d28e
@ -49,7 +49,7 @@ class VideoDecoder(
|
|||||||
companion object {
|
companion object {
|
||||||
private const val TAG = "VideoDecoder"
|
private const val TAG = "VideoDecoder"
|
||||||
private const val TIMEOUT_US = 10000L
|
private const val TIMEOUT_US = 10000L
|
||||||
private const val INPUT_BUFFER_QUEUE_CAPACITY = 50 // 输入缓冲区容量
|
private const val INPUT_BUFFER_QUEUE_CAPACITY = 100 // 增大输入缓冲区容量
|
||||||
}
|
}
|
||||||
|
|
||||||
// region 成员变量定义
|
// region 成员变量定义
|
||||||
@ -64,19 +64,23 @@ class VideoDecoder(
|
|||||||
private var running = true // 解码器运行状态
|
private var running = true // 解码器运行状态
|
||||||
private val frameSeqSet = Collections.newSetFromMap(ConcurrentHashMap<Int, Boolean>()) // 防止重复帧入队
|
private val frameSeqSet = Collections.newSetFromMap(ConcurrentHashMap<Int, Boolean>()) // 防止重复帧入队
|
||||||
|
|
||||||
// 解码输出缓冲区,容量为100帧
|
// 解码输出缓冲区,增大容量
|
||||||
private val outputFrameQueue = LinkedBlockingQueue<DecodedFrame>(50)
|
private val outputFrameQueue = LinkedBlockingQueue<DecodedFrame>(100)
|
||||||
|
|
||||||
// 渲染线程控制
|
// 渲染线程控制
|
||||||
// 定时渲染调度器
|
|
||||||
private var scheduler = Executors.newSingleThreadScheduledExecutor()
|
private var scheduler = Executors.newSingleThreadScheduledExecutor()
|
||||||
|
private var lastRenderTimeMs = 0L // 记录上次渲染时间
|
||||||
|
|
||||||
|
// 这些变量移到init块中,因为它们依赖renderFps
|
||||||
|
private var renderIntervalMs: Long = 0
|
||||||
|
private val renderJitterMs = 2L // 允许的渲染时间抖动范围
|
||||||
|
|
||||||
// 主线程Handler,用于安全切换onFrameRendered到主线程
|
// 主线程Handler,用于安全切换onFrameRendered到主线程
|
||||||
private val mainHandler = Handler(Looper.getMainLooper())
|
private val mainHandler = Handler(Looper.getMainLooper())
|
||||||
|
|
||||||
// 渲染帧率(fps),可由外部控制,默认30
|
// 渲染帧率(fps),可由外部控制,默认30
|
||||||
@Volatile
|
@Volatile
|
||||||
var renderFps: Int = 30
|
var renderFps: Int = 20
|
||||||
|
|
||||||
// 兜底:记录最近一次I帧的frameSeq,P/B帧依赖校验
|
// 兜底:记录最近一次I帧的frameSeq,P/B帧依赖校验
|
||||||
@Volatile
|
@Volatile
|
||||||
@ -128,6 +132,9 @@ class VideoDecoder(
|
|||||||
|
|
||||||
// region 初始化与解码器配置
|
// region 初始化与解码器配置
|
||||||
init {
|
init {
|
||||||
|
// 初始化渲染相关参数
|
||||||
|
renderIntervalMs = (1000.0 / renderFps).toLong()
|
||||||
|
|
||||||
// 配置Surface尺寸
|
// 配置Surface尺寸
|
||||||
surfaceTexture.setDefaultBufferSize(width, height)
|
surfaceTexture.setDefaultBufferSize(width, height)
|
||||||
// 选择MIME类型
|
// 选择MIME类型
|
||||||
@ -139,11 +146,19 @@ class VideoDecoder(
|
|||||||
// 创建并配置MediaFormat
|
// 创建并配置MediaFormat
|
||||||
val format = MediaFormat.createVideoFormat(mime, width, height)
|
val format = MediaFormat.createVideoFormat(mime, width, height)
|
||||||
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)
|
// 移除可能导致flush的配置
|
||||||
format.setInteger(MediaFormat.KEY_LOW_LATENCY, 1);
|
// format.setInteger(MediaFormat.KEY_FRAME_RATE, renderFps)
|
||||||
format.setInteger(MediaFormat.KEY_PRIORITY, 0) // 最高优先级
|
// format.setInteger(MediaFormat.KEY_OPERATING_RATE, renderFps)
|
||||||
format.setInteger(MediaFormat.KEY_OPERATING_RATE, renderFps * 2) // 解码速率
|
format.setInteger(MediaFormat.KEY_LOW_LATENCY, 1)
|
||||||
format.setInteger(MediaFormat.KEY_MAX_B_FRAMES, 0) // 禁用B帧
|
format.setInteger(MediaFormat.KEY_PRIORITY, 0)
|
||||||
|
format.setInteger(MediaFormat.KEY_MAX_B_FRAMES, 0)
|
||||||
|
|
||||||
|
// 高通解码器特定配置
|
||||||
|
format.setInteger("vendor.qti-ext-dec-low-latency.enable", 1)
|
||||||
|
format.setInteger("vendor.qti-ext-dec-picture-order.enable", 0)
|
||||||
|
format.setInteger("vendor.qti-ext-dec-timestamp-mode.value", 0) // 使用原始时间戳
|
||||||
|
format.setInteger("vendor.qti-ext-dec-drv-flush.disable", 1) // 禁用驱动层flush
|
||||||
|
|
||||||
// 创建解码器
|
// 创建解码器
|
||||||
val decoder = MediaCodec.createDecoderByType(mime)
|
val decoder = MediaCodec.createDecoderByType(mime)
|
||||||
// 设置解码回调
|
// 设置解码回调
|
||||||
@ -210,16 +225,23 @@ class VideoDecoder(
|
|||||||
decoder.start()
|
decoder.start()
|
||||||
mediaCodec = decoder
|
mediaCodec = decoder
|
||||||
|
|
||||||
// 启动定时渲染任务,实现完全线性调度
|
// 优化渲染任务调度
|
||||||
// 说明:本方案通过ScheduledExecutorService定时驱动渲染,每帧间隔严格等距,不依赖阻塞或sleep
|
|
||||||
var hasNotifiedFlutter = false
|
var hasNotifiedFlutter = false
|
||||||
var renderedFrameCount = 0 // 渲染帧计数器
|
var renderedFrameCount = 0
|
||||||
val renderTask = Runnable {
|
val renderTask = Runnable {
|
||||||
try {
|
try {
|
||||||
|
val now = System.currentTimeMillis()
|
||||||
|
// 控制渲染间隔,避免过快渲染
|
||||||
|
val timeSinceLastRender = now - lastRenderTimeMs
|
||||||
|
if (timeSinceLastRender < renderIntervalMs - renderJitterMs) {
|
||||||
|
return@Runnable
|
||||||
|
}
|
||||||
|
|
||||||
val frame = outputFrameQueue.poll()
|
val frame = outputFrameQueue.poll()
|
||||||
if (frame != null) {
|
if (frame != null) {
|
||||||
frame.codec.releaseOutputBuffer(frame.bufferIndex, true)
|
frame.codec.releaseOutputBuffer(frame.bufferIndex, true)
|
||||||
latestRenderedTimestampMs = System.currentTimeMillis()
|
lastRenderTimeMs = now
|
||||||
|
latestRenderedTimestampMs = now
|
||||||
renderedFrameCount++
|
renderedFrameCount++
|
||||||
if (!hasNotifiedFlutter) {
|
if (!hasNotifiedFlutter) {
|
||||||
mainHandler.post { onFrameRendered() }
|
mainHandler.post { onFrameRendered() }
|
||||||
@ -229,13 +251,13 @@ class VideoDecoder(
|
|||||||
} else {
|
} else {
|
||||||
Log.w(TAG, "[Render] 渲染空转,无帧可渲染,当前outputFrameQueue=${outputFrameQueue.size}")
|
Log.w(TAG, "[Render] 渲染空转,无帧可渲染,当前outputFrameQueue=${outputFrameQueue.size}")
|
||||||
}
|
}
|
||||||
// 若outputFrameQueue为空,跳过本次渲染,实现线性调度
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e(TAG, "[RenderTask] Exception", e)
|
Log.e(TAG, "[RenderTask] Exception", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 固定20fps渲染(50ms间隔)
|
|
||||||
scheduler.scheduleAtFixedRate(renderTask, 0, 50, java.util.concurrent.TimeUnit.MILLISECONDS)
|
// 使用更短的调度间隔,但在任务中控制实际渲染间隔
|
||||||
|
scheduler.scheduleAtFixedRate(renderTask, 0, renderIntervalMs/2, TimeUnit.MILLISECONDS)
|
||||||
}
|
}
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user