Android MediaMuxer实战:如何高效实现图片序列生成视频

1次阅读
没有评论

共计 1725 个字符,预计需要花费 5 分钟才能阅读完成。

image.webp

在移动开发中,经常遇到需要将图片序列合成视频的场景,比如监控录像回放、短视频制作等。前段时间我正好接手了一个类似需求,过程中踩了不少坑,也积累了一些经验,今天就来分享一下如何使用 MediaMuxer 高效实现这一功能。

Android MediaMuxer 实战:如何高效实现图片序列生成视频

需求背景与痛点

先说说为什么会有这个需求。我们项目需要将用户拍摄的多张照片自动合成为一段视频,类似手机相册的 ” 回忆 ” 功能。最开始尝试了几种方案:

  1. 服务器端合成:图片上传到服务器处理,但延迟高、流量消耗大
  2. FFmpeg 命令行:需要集成 so 库,增加包体积
  3. MediaCodec+MediaMuxer:原生 API,但实现复杂

经过对比,最终选择了第三种方案,因为:

  • 无需额外依赖
  • 性能更好
  • 可以精细控制每一帧

但在实际开发中遇到了几个典型问题:

  • 帧率不稳定,视频播放卡顿
  • 大分辨率图片导致内存溢出
  • 音视频轨道同步困难
  • 部分机型兼容性问题

技术实现详解

1. 整体流程设计

核心流程分为四个步骤:

  1. 初始化 MediaMuxer 和 MediaCodec
  2. 配置视频格式和编码参数
  3. 逐帧输入图片数据
  4. 释放资源完成合成

2. 关键代码实现

// 1. 初始化 MediaMuxer
val muxer = MediaMuxer(outputPath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4)

// 2. 配置 MediaCodec
val format = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, width, height)
format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface)
format.setInteger(MediaFormat.KEY_BIT_RATE, bitRate)
format.setInteger(MediaFormat.KEY_FRAME_RATE, frameRate)
format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, iFrameInterval)

val codec = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_AVC)
codec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE)
val surface = codec.createInputSurface()
codec.start()

3. 时间戳控制

精确控制帧率是关键,这里采用固定时间间隔:

val frameInterval = 1000000L / frameRate // 微秒单位

fun encodeFrame(bitmap: Bitmap, frameIndex: Int) {
    val presentationTime = frameIndex * frameInterval
    // ... 将 bitmap 绘制到 surface...
}

4. 内存优化技巧

处理大图时容易 OOM,解决方案:

  1. 采样压缩:

    val options = BitmapFactory.Options()
    options.inSampleSize = 2 // 降低采样率 

  2. 使用对象池复用 ByteBuffer

  3. 异步处理避免阻塞 UI 线程

常见问题解决

1. addTrack failed 错误

确保在收到 MediaCodec 的 INFO_OUTPUT_FORMAT_CHANGED 回调后再添加轨道

2. 视频不同步

  • 确保每帧时间戳计算正确
  • 使用固定帧率模式
  • 检查编码器是否丢帧

3. 资源释放

必须按顺序释放:

  1. 先停止 MediaCodec
  2. 再停止 MediaMuxer
  3. 最后释放 Surface

扩展思考

基础功能完成后,还可以考虑:

  1. 添加背景音乐
  2. 动态调整输出分辨率
  3. 支持滤镜效果
  4. 添加转场动画

完整实现代码已放在 GitHub(伪链接),欢迎参考。通过这次实践,我深刻体会到 ” 魔鬼在细节 ” 这句话的含义。特别是时间戳处理和资源释放顺序,稍有差错就会导致各种奇怪问题。希望这篇分享能帮你少走弯路。

如果有更好的实现方案,欢迎在评论区交流讨论。

正文完
 0
评论(没有评论)