限时福利领取


背景痛点

开发Android录屏功能时,开发者常遇到三个致命问题:

  1. 音视频不同步:音频采集和视频帧时间戳对不齐,导致录制的视频出现声音延迟或提前
  2. 权限限制:Android 10+对MediaProjection增加了前台服务限制,后台录屏直接被系统终止
  3. 性能问题:高分辨率录屏时容易出现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. 双线程同步模型

同步流程图

  1. 视频采集线程通过MediaCodec获取编码后的H.264数据
  2. 音频采集线程通过AudioRecord获取PCM数据并编码为AAC
  3. 使用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

避坑指南

  1. Android 10+限制:必须使用前台服务并在通知栏显示录屏状态
  2. DPI设置:虚拟显示DPI建议与物理屏幕一致,过高会导致性能下降
  3. 编解码回退:先检查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录屏开发的深坑,如果有更好的实现方案,欢迎交流讨论!

Logo

音视频技术社区,一个全球开发者共同探讨、分享、学习音视频技术的平台,加入我们,与全球开发者一起创造更加优秀的音视频产品!

更多推荐