Android录屏开发实战:基于MediaProjection和MediaCodec的高效音视频录制方案
·
在开发需要录屏功能的Android应用时,我们常常会遇到音频视频不同步、录制卡顿、兼容性差等问题。今天就来分享一套经过实战检验的解决方案,使用MediaProjection+MediaCodec+MediaMuxer技术栈实现高效录屏。

为什么选择MediaCodec方案
Android提供了两种主要的录屏方案:
- MediaRecorder方案:
- 优点:API简单,集成快速
-
缺点:灵活性差,无法精细控制编码参数,难以处理特殊需求
-
MediaCodec+MediaMuxer方案:
- 优点:支持硬件编码,性能更好
- 可精确控制每一帧数据
- 支持自定义处理流程
- 能实现更低的延迟
对于需要高质量录屏的场景,我们显然应该选择后者。
核心实现步骤
1. 初始化MediaProjection
首先需要获取录屏权限,这是整个流程的起点:
// 在Activity中请求录屏权限
val mediaProjectionManager = getSystemService(MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
startActivityForResult(
mediaProjectionManager.createScreenCaptureIntent(),
REQUEST_CODE_SCREEN_CAPTURE
)
// 处理授权结果
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == REQUEST_CODE_SCREEN_CAPTURE) {
val mediaProjection = mediaProjectionManager.getMediaProjection(resultCode, data!!)
// 保存mediaProjection实例后续使用
}
}
2. 配置AudioRecord采集音频
音频采集需要特别注意参数配置和时间戳处理:
val audioSource = MediaRecorder.AudioSource.MIC
val sampleRate = 44100 // 采样率
val channelConfig = AudioFormat.CHANNEL_IN_MONO
val audioFormat = AudioFormat.ENCODING_PCM_16BIT
val bufferSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat)
val audioRecord = AudioRecord(
audioSource,
sampleRate,
channelConfig,
audioFormat,
bufferSize * 2 // 适当放大缓冲区
)
3. 配置MediaCodec进行编码
视频编码建议使用硬件加速的H.264编码器:
val mediaCodec = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_AVC)
val format = MediaFormat.createVideoFormat(
MediaFormat.MIMETYPE_VIDEO_AVC,
width,
height
).apply {
setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface)
setInteger(MediaFormat.KEY_BIT_RATE, bitRate)
setInteger(MediaFormat.KEY_FRAME_RATE, frameRate)
setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, iFrameInterval)
}
mediaCodec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE)
val inputSurface = mediaCodec.createInputSurface()
mediaCodec.start()
时间戳同步处理
音视频同步是录屏的核心难点,这里展示关键同步代码:
// 视频帧时间戳处理
fun onFrameAvailable(surfaceTexture: SurfaceTexture) {
surfaceTexture.updateTexImage()
val pts = System.nanoTime() / 1000 // 微秒单位
// 将pts传递给编码器...
}
// 音频时间戳处理
val audioBuffer = ByteArray(bufferSize)
var audioPts = 0L
val readSize = audioRecord.read(audioBuffer, 0, bufferSize)
if (readSize > 0) {
audioPts += 1000000L * readSize / (2 * sampleRate)
// 将音频数据和pts传递给编码器...
}

性能优化技巧
- 使用双缓冲队列:避免直接在回调线程处理数据
- 调整关键编码参数:
- 适当降低帧率(如24fps)
- 使用可变比特率(VBR)
- 调整关键帧间隔
- 及时释放资源:在Activity生命周期结束时确保释放所有资源
常见问题解决
- Android版本兼容性问题:
- 不同版本对MediaCodec的支持有差异
-
解决方案:做好版本判断和fallback处理
-
虚拟屏适配问题:
- 部分设备使用虚拟屏技术
-
解决方案:检查display.isValid()并做相应处理
-
内存泄漏问题:
- 忘记释放MediaCodec和MediaProjection
- 解决方案:实现完整的生命周期管理
进阶方向
掌握了基础录屏功能后,可以考虑:
- 使用MediaCodec的异步模式提升性能
- 集成OpenGL ES实现实时滤镜
- 支持多路音视频流混合
这套方案已经在我们多个产品中稳定运行,希望对你有所帮助。如果有任何问题,欢迎在评论区交流讨论。
更多推荐


所有评论(0)