Android录屏性能优化实战:MediaCodec硬编码的避坑指南
在短视频和在线教育爆发的时代,Android屏幕录制功能成为许多应用的刚需。但当你真正用MediaCodec实现录屏时,会发现理想很丰满,现实很骨感——帧率波动、内存泄漏、厂商兼容性问题接踵而至。今天就用我们团队趟过的坑,手把手教你打造高性能的录屏方案。

一、那些年我们踩过的MediaCodec大坑
- SurfaceTexture丢帧:当主线程繁忙时,SurfaceTexture的updateTexImage()会丢弃未处理的帧,导致录制视频卡顿
- BITRATE_MODE_CQ陷阱:质量优先模式在小米机型上会导致编码器缓冲堆积,最终视频播放速度异常
- 内存泄漏重灾区:未释放MediaCodec的InputSurface会导致GL上下文泄漏,应用退出后GPU内存仍在增长
- 厂商格式兼容:华为麒麟芯片对COLOR_FormatYUV420Flexible支持不完善,直接使用会绿屏
二、硬编码VS软编码实战对比
通过OPPO Reno6 Pro实测数据:
| 指标 | MediaCodec(H.264) | FFmpeg(x264) | |------------|------------------|-------------| | 1080P延迟 | 120ms | 350ms | | 功耗 | 2.1W/h | 4.7W/h | | 兼容性 | 需处理厂商差异 | 全版本通用 |
结论:追求低延迟和低功耗必选MediaCodec,但需要做好兼容层封装。
三、从零搭建录制流水线(Kotlin/Java双实现)
关键代码片段(省略异常处理):
// 1. 创建虚拟显示器
val surfaceTexture = SurfaceTexture(0).apply {
setDefaultBufferSize(width, height)
}
val surface = Surface(surfaceTexture)
// 2. 配置MediaCodec(关键参数)
val format = MediaFormat.createVideoFormat(MIMETYPE, width, height).apply {
setInteger(MediaFormat.KEY_COLOR_FORMAT,
MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface) // 必须
setInteger(MediaFormat.KEY_BITRATE_MODE,
MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_VBR) // 避免CQ
setInteger(MediaFormat.KEY_FRAME_RATE, 30)
}
// 3. 启动编码器线程
val codec = MediaCodec.createEncoderByType(MIMETYPE).apply {
configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE)
createInputSurface().also { inputSurface ->
start()
virtualDisplay = mediaProjection.createVirtualDisplay(
"RECORDER", width, height, dpi,
DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
inputSurface, null, null)
}
}

四、让性能飞起来的黑科技
-
ByteBuffer对象池:复用ByteBuffer减少GC卡顿
private val bufferPool = SynchronizedPool<ByteBuffer>(5); fun getInputBuffer(index: Int): ByteBuffer { return bufferPool.acquire()?.also { it.clear() } ?: codec.getInputBuffer(index)!! } -
动态比特率算法:根据帧复杂度调整码率
fun calculateBitrate(frameTimeNanos: Long): Int { val fps = 1e9 / frameTimeNanos return when { fps < 15 -> originalBitrate * 1.5f // 降帧补偿 fps > 45 -> originalBitrate * 0.8f // 节省带宽 else -> originalBitrate }.toInt() }
五、血泪换来的兼容性方案
- Android 12受限模式:
- 在AndroidManifest添加
<uses-permission android:name="android.permission.RECORD_AUDIO"/> -
启动前检查
MediaProjectionManager.isProjectionEnabled() -
荣耀YUV异常处理:
// 检测到荣耀机型时强制使用NV12 if (Build.MANUFACTURER.equalsIgnoreCase("HONOR")) { format.setInteger(KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar); }
六、真机实测数据
| 机型 | 平均帧率 | CPU占用 | 内存增量 | |------------|---------|--------|---------| | OPPO FindX5| 29.8fps | 12% | +38MB | | 三星S22 Ultra | 28.3fps | 15% | +45MB | | 红米K50 | 26.7fps | 18% | +52MB |
思考题:4K@30fps实现密钥
提示方向: 1. 使用MediaCodec.createPersistentInputSurface() 2. 分片编码+多线程处理 3. 预加载GL Shader避免编译卡顿
优化无止境,欢迎在评论区分享你的高阶玩法。下期我们会揭秘如何用OpenGL实现实时美颜录屏,敬请期待!
更多推荐


所有评论(0)