Android录屏实战:AudioRecord+MediaProjection+MediaCodec+MediaMuxer全链路实现与避坑指南
·
背景痛点
开发Android录屏功能时,开发者常遇到三个致命问题:
- 音视频不同步:音频采集和视频帧时间戳对不齐,导致录制的视频出现声音延迟或提前
- 权限限制:Android 10+对MediaProjection增加了前台服务限制,后台录屏直接被系统终止
- 性能问题:高分辨率录屏时容易出现OOM崩溃,低端设备帧率波动剧烈

技术方案对比
MediaRecorder方案
- 优点:API简单,三行代码实现基础录屏
- 缺点:
- 无法自定义视频质量参数
- 音频采样率固定为48kHz
- Android 9以下存在兼容性问题
MediaCodec+MediaMuxer方案
- 优点:
- 可精确控制每一帧的编码参数
- 支持动态调整比特率
- 音频视频可分别处理
- 缺点:
- 需要手动处理同步逻辑
- 内存管理更复杂
核心实现细节
1. AudioRecord配置
// 创建环形缓冲区避免音频丢帧
val bufferSize = AudioRecord.getMinBufferSize(
44100, // 采样率
AudioFormat.CHANNEL_IN_MONO,
AudioFormat.ENCODING_PCM_16BIT
) * 2 // 双倍缓冲
val audioRecord = AudioRecord(
MediaRecorder.AudioSource.MIC,
44100,
AudioFormat.CHANNEL_IN_MONO,
AudioFormat.ENCODING_PCM_16BIT,
bufferSize
)
2. MediaProjection虚拟显示
// 注意:必须在前台Service中调用
val virtualDisplay = mediaProjection.createVirtualDisplay(
"ScreenCapture",
width, // 建议使用1080p
height,
displayMetrics.densityDpi, // 关键!错误的DPI会导致卡顿
DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
surface, // MediaCodec的输入surface
null,
handler
)
3. 双线程同步模型

- 视频采集线程通过MediaCodec获取编码后的H.264数据
- 音频采集线程通过AudioRecord获取PCM数据并编码为AAC
- 使用AtomicLong保存基准时间戳,两个线程根据相对时间写入Muxer
性能优化技巧
- 内存复用:预先分配ByteBuffer池,避免频繁GC
- 动态码率:根据CPU使用率调整编码比特率
fun adjustBitrate(codec: MediaCodec, newBitrate: Int) { val params = Bundle() params.putInt(MediaCodec.PARAMETER_KEY_VIDEO_BITRATE, newBitrate) codec.setParameters(params) } - 低端设备适配:检测到SDK<26时自动降级到720p
避坑指南
- Android 10+限制:必须使用前台服务并在通知栏显示录屏状态
- DPI设置:虚拟显示DPI建议与物理屏幕一致,过高会导致性能下降
- 编解码回退:先检查MediaCodecList.findEncoderForFormat(),找不到时切换H.265→H.264→VP8
完整代码结构
class ScreenRecorder {
// 1. 初始化MediaCodec
private fun initVideoCodec() { /*...*/ }
// 2. 配置音频采集
private fun setupAudioPipeline() { /*...*/ }
// 3. 启动混合器
private fun startMuxer() { /*...*/ }
// 4. 释放资源
fun release() { /*...*/ }
}
进阶思考
如何实现60fps无损录屏?考虑以下方向: - 使用SurfaceTexture接收帧数据 - 配置MediaFormat.KEY_COLOR_FORMAT为COLOR_FormatSurface - OpenGL ES离屏渲染优化
希望本文能帮你避开Android录屏开发的深坑,如果有更好的实现方案,欢迎交流讨论!
更多推荐


所有评论(0)