Android下如何降低MediaCodec编码帧率?兼容旧版本设备
解决MediaCodec编码帧率无法限制的问题(兼容旧Android版本)
MediaCodec的KEY_FRAME_RATE和KEY_MAX_FPS_TO_ENCODER多数情况下只是编码器的参考值,不会主动丢弃输入帧——尤其是Surface输入场景,编码器会处理所有送到Surface的帧。要稳定限制帧率并兼容旧版本,核心是主动控制输入到编码器的帧数量,配合参数优化实现目标。
一、主动控制帧输入频率(全版本兼容,最可靠)
直接从源头控制向编码器提交帧的频率,比如目标10fps就每100ms提交一帧,超过频率的帧直接丢弃。
实现逻辑:
- 计算目标帧率对应的时间间隔(比如10fps对应
1_000_000_000 / 10 = 100_000_000纳秒)。 - 记录上一次提交帧的时间戳,每次获取到新帧时,判断当前时间与上一次的间隔是否达到阈值,达到才提交,否则跳过。
示例代码:
// 目标帧率,可根据需求设置5-10之间的值 val targetFps = 10 val frameIntervalNs = 1_000_000_000 / targetFps var lastFrameSubmitTimeNs = 0L // 初始化编码器格式 val format = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_VP8, size.width, size.height).apply { setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface) setInteger(MediaFormat.KEY_BIT_RATE, 1000000) // 固定1Mbps带宽 setInteger(MediaFormat.KEY_BITRATE_MODE, MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_CBR_FD) setInteger(MediaFormat.KEY_FRAME_RATE, targetFps) // 给编码器提供参考 setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 10) // 每10帧生成一个I帧(对应1秒) setInteger(MediaFormat.KEY_MAX_FPS_TO_ENCODER, targetFps) // 部分设备支持的硬限制 } encoderInput = mEncoder!!.createInputSurface() // Surface帧率控制仅在API 24+生效,旧版本忽略 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { encoderInput.setFrameRate(targetFps.toFloat(), Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, Surface.CHANGE_FRAME_RATE_ALWAYS) } // 帧回调中的处理逻辑(以SurfaceTexture为例) fun onFrameAvailable(surfaceTexture: SurfaceTexture) { val currentTimeNs = System.nanoTime() surfaceTexture.updateTexImage() // 必须调用,避免后续帧阻塞 if (currentTimeNs - lastFrameSubmitTimeNs >= frameIntervalNs) { lastFrameSubmitTimeNs = currentTimeNs // 将帧渲染到编码器的InputSurface(需自行实现渲染逻辑,比如用OpenGL ES) renderFrameToEncoderSurface(surfaceTexture, encoderInput) } // 跳过的帧无需渲染到编码器 }
二、辅助优化建议
- Camera预览场景:优先设置Camera的预览帧率为目标帧率(比如
Camera.Parameters.setPreviewFpsRange(10000, 10000)对应10fps),减少Camera输出的帧数量,降低后续处理压力。若Camera不支持低帧率,再用主动丢弃的方法。 - 编码器参数配合:保持
BITRATE_MODE_CBR_FD模式,在固定带宽下,编码器会更合理地分配每帧的比特率,低帧率下单帧画质会更优。 - 旧版本兼容:API 24以下的系统完全依赖主动帧控制,不要依赖
Surface.setFrameRate()或编码器的帧率参数。
内容的提问来源于stack exchange,提问作者aljammali
相关产品推荐
相关产品推荐

