限时福利领取


在移动端开发中,将图片序列转换为视频是一个常见需求,比如生成动态相册、制作短视频等场景。传统方案使用FFmpeg虽然功能强大,但在Android平台上存在包体积大、性能开销高的缺点。而Android原生的MediaCodec API则提供了更高效的硬件加速方案,下面分享我的实战经验。

图片转视频应用场景

一、为什么选择MediaCodec?

  • 性能优势:直接调用硬件编码器(如高通/MTK芯片组的H.264编码模块)
  • 体积优势:无需集成第三方库,APK体积可减少5-10MB
  • 功耗控制:比软件编码节省30%以上电量(实测红米Note11 Pro编码1080P视频)

但要注意几个常见坑点: 1. 帧率不稳定导致视频卡顿 2. 内存泄漏风险(特别是Android 9以下版本) 3. 不同厂商设备编码器支持差异

二、核心实现四步走

  1. 初始化编码器(关键参数示例)

    val format = MediaFormat.createVideoFormat("video/avc", width, height).apply {
        setInteger(MediaFormat.KEY_COLOR_FORMAT, 
            MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface)
        setInteger(MediaFormat.KEY_BIT_RATE, 2500_000) // 2.5Mbps
        setInteger(MediaFormat.KEY_FRAME_RATE, 30)     
        setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 2) // 关键帧间隔
    }
    val encoder = MediaCodec.createEncoderByType("video/avc")
    encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE)
    val surface = encoder.createInputSurface() // 用于接收图像数据
  2. 帧数据输入技巧

  3. 使用OpenGL ES将Bitmap渲染到Surface(避免直接内存拷贝)
  4. 时间戳计算公式:presentationTimeUs = 1000000L * frameIndex / frameRate

  5. 输出缓冲区处理

    val bufferInfo = MediaCodec.BufferInfo()
    when (val status = encoder.dequeueOutputBuffer(bufferInfo, 10000)) {
        >= 0 -> {
            val outputBuffer = encoder.getOutputBuffer(status)
            // 写入MP4容器(建议用MediaMuxer)
            muxer.writeSampleData(trackIndex, outputBuffer, bufferInfo)
            encoder.releaseOutputBuffer(status, false)
        }
        MediaCodec.INFO_OUTPUT_FORMAT_CHANGED -> 
            trackIndex = muxer.addTrack(encoder.outputFormat)
    }

编码流程示意图

三、性能优化实战

内存优化三原则: 1. 复用Texture和Surface(避免每帧创建/销毁) 2. 使用BITMAP_RECYCLE及时回收Bitmap 3. 限制待编码帧队列长度(建议<5帧)

编码速度提升技巧: - 预生成所有Bitmap的时间戳(减少实时计算开销) - 对于720P视频,设置KEY_PROFILECodecProfileLevel.AVCProfileBaseline - 启用异步模式(API 21+)

四、避坑指南

高频异常处理

try {
    // 编码操作
} catch (e: IllegalStateException) {
    // 常见于编码器状态错误
    encoder.reset()
} catch (e: MediaCodec.CodecException) {
    // 检查设备支持的color format
    val caps = encoder.codecInfo.getCapabilitiesForType("video/avc")
    caps.colorFormats.forEach { Log.d("SupportedFormat", "$it") }
}

版本兼容方案: - Android 4.3以下:降级到H.263编码 - Android 5.0+:优先使用createPersistentInputSurface() - Android 12+:注意HardwareBuffer的使用限制

五、完整代码示例

GitHub代码片段 包含: 1. Bitmap到YUV的转换工具类 2. 带进度回调的编码封装 3. 帧率自适应算法

最后建议结合JobScheduler实现后台编码任务管理,避免ANR。通过合理配置MediaCodec参数,在我的Redmi K40上实现1080P@30fps视频编码仅需原图序列处理时间的1.2倍,内存占用稳定在50MB以内。

Logo

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

更多推荐