Android MediaCodec 硬编解码实战:从Camera预览到H264流与回环渲染

一、核心概览

MediaCodec是Android提供的底层音视频编解码接口,其设计基于生产者-消费者模型 ,通过缓冲区队列进行异步数据处理。这对于高性能、低延迟的视频处理至关重要。

  • 核心组件

    • Input Buffers:存放待处理(编码或解码)的原始数据。

    • Output Buffers:存放处理后的结果数据。

    • MediaFormat:描述数据格式的关键配置(如视频的宽、高、比特率、颜色格式等)。

二、工作流程:一场数据的"奇幻漂流"

数据在MediaCodec中的旅程可以概括为以下流程图,它清晰地展示了编解码的异步处理过程:

三、关键步骤与要点

1. 视频编码(Camera YUV -> H264)

  • 创建与配置

    java

    复制代码
    // 1. 创建编码器实例
    mediaCodec = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_AVC);
    
    // 2. 配置MediaFormat
    MediaFormat format = MediaFormat.createVideoFormat(MIME_TYPE, width, height);
    format.setInteger(MediaFormat.KEY_BIT_RATE, bitrate); // 比特率,决定画质和体积
    format.setInteger(MediaFormat.KEY_FRAME_RATE, framerate); // 帧率
    format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, iFrameInterval); // 关键帧间隔(秒)
    format.setInteger(MediaFormat.KEY_COLOR_FORMAT, colorFormat); // !!!关键:颜色空间
    
    // 3. 配置编码器
    mediaCodec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);

    要点

    • 颜色格式 :Camera预览数据通常为YV12NV21,而编码器普遍支持的是YUV420Flexible或具体的YUV420Planar格式不匹配是导致绿屏、花屏的首要原因,必须进行转换。
  • 数据流转(参考流程图)

    • 输入(生产) :从Camera获得YUV数据后,dequeueInputBuffer获取一个输入缓冲区,填入数据后queueInputBuffer将其交还给编解码器。

    • 输出(消费) :循环调用dequeueOutputBuffer,当拿到有效索引后,从对应的Output Buffer中读取H264裸流数据,最后务必releaseOutputBuffer释放缓冲区。

2. 视频解码(H264 -> SurfaceView渲染)

  • 创建与配置

    java

    复制代码
    // 1. 创建解码器
    mediaCodec = MediaCodec.createDecoderByType(MediaFormat.MIMETYPE_VIDEO_AVC);
    
    // 2. 配置MediaFormat (关键:传递SPS & PPS)
    MediaFormat format = MediaFormat.createVideoFormat(MIME_TYPE, width, height);
    // CSD(Codec-Specific Data)是解码器的“说明书”
    format.setByteBuffer("csd-0", ByteBuffer.wrap(sps));
    format.setByteBuffer("csd-1", ByteBuffer.wrap(pps));
    format.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, width * height);
    
    // 3. 配置解码器并绑定Surface
    mediaCodec.configure(format, surface, null, 0); // 传入Surface用于渲染

    要点

    • SPS/PPS :这是H264的序列参数集和图像参数集,包含了解码整个视频流所需的关键信息(如分辨率、profile等)。通常附着在第一个关键帧之前,必须正确提取并配置给解码器。

    • Surface输出 :解码时配置一个Surface,解码后的YUV数据会直接渲染到其上,避免了手动处理YUV数据,极大提升了效率。

  • 数据流转

    • 输入H264数据到解码器。

    • 从解码器输出时,调用releaseOutputBuffer(outputBufferIndex, true),其中第二个参数render设置为true,通知编解码器将解码后的图像渲染到配置的Surface上。

四、实战架构与性能心法

上述实例实现了一个"编解码回环"(Camera -> 编码 -> 解码 -> SurfaceView),其核心架构如下:

  1. Camera预览管理 :使用带Buffer的预览回调setPreviewCallbackWithBuffer,避免GC,保证帧率稳定。

  2. 双缓冲队列

    • mPreviewBuffers_dirty/clean:管理从Camera来的原始YUV数据。

    • mDecodeBuffers_dirty/clean:管理编码后的H264数据。

    • 目的:解耦数据生产(Camera/编码器)和消费(编码器/解码器)线程,实现异步流水线处理。

  3. 编解码线程 :在独立的CodecThread中处理所有MediaCodec操作,防止阻塞UI线程。

总结与心法

  • 异步是灵魂 :绝不在主线程调用dequeueInput/OutputBuffer,尤其是使用timeoutUs = -1(无限等待)。

  • 颜色空间是基石:确保Camera输出格式与编码器输入格式匹配,否则必然失败。

  • 缓冲区生命周期queueInputBufferreleaseOutputBuffer的调用必须准确,否则会迅速导致队列阻塞或编解码器停止工作。

  • 善待MediaCodec State Machine :理解configure -> start -> (processing) -> flush/stop -> release的状态流转, improper state calls will throw IllegalStateException.

  • 追求零拷贝:在解码时,利用Surface直接渲染,是性能最高的方式。在编码时,合理复用ByteBuffer,减少内存分配。

通过理解上述核心概念、数据流模型和实战心法,你就能系统地驾驭MediaCodec,构建出高性能的Android视频处理应用。

转载请注明出处https://mp.csdn.net/mp_blog/creation/editor/154801674,谢谢合作!

相关推荐
峰哥的Android进阶之路1 小时前
Android常见的内存性能优化场景解决方案
android·性能优化
清空mega1 小时前
第三章 Android常见界面控件
android·gitee
吴名氏.2 小时前
电子书《21天学通Java(第5版)》
java·开发语言·21天学通java
星释2 小时前
Rust 练习册 :深入探索XOR加密与流密码
开发语言·网络·rust
郝学胜-神的一滴2 小时前
Effective STL 第9条:C++容器元素删除技巧详解
开发语言·c++·程序人生·stl
提娜米苏2 小时前
Bash Shell脚本学习——唇读数据集格式修复脚本
开发语言·学习·bash
larance2 小时前
python中的鸭子类型
开发语言·python
JoyCong19982 小时前
智能手机市场再次洗牌,远控何以成为数码生活新“连接器”?
android·智能手机·电脑·生活·远程工作·远程操作
丙寅3 小时前
微信小程序反编译遇到 TypeError: _typeof3 is not a function
开发语言·javascript·ecmascript