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预览数据通常为
YV12或NV21,而编码器普遍支持的是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),其核心架构如下:
-
Camera预览管理:使用带Buffer的预览回调
setPreviewCallbackWithBuffer,避免GC,保证帧率稳定。 -
双缓冲队列:
-
mPreviewBuffers_dirty/clean:管理从Camera来的原始YUV数据。 -
mDecodeBuffers_dirty/clean:管理编码后的H264数据。 -
目的:解耦数据生产(Camera/编码器)和消费(编码器/解码器)线程,实现异步流水线处理。
-
-
编解码线程:在独立的
CodecThread中处理所有MediaCodec操作,防止阻塞UI线程。
总结与心法:
-
异步是灵魂:绝不在主线程调用
dequeueInput/OutputBuffer,尤其是使用timeoutUs = -1(无限等待)。 -
颜色空间是基石:确保Camera输出格式与编码器输入格式匹配,否则必然失败。
-
缓冲区生命周期:
queueInputBuffer和releaseOutputBuffer的调用必须准确,否则会迅速导致队列阻塞或编解码器停止工作。 -
善待MediaCodec State Machine:理解
configure->start-> (processing) ->flush/stop->release的状态流转, improper state calls will throwIllegalStateException. -
追求零拷贝:在解码时,利用Surface直接渲染,是性能最高的方式。在编码时,合理复用ByteBuffer,减少内存分配。
通过理解上述核心概念、数据流模型和实战心法,你就能系统地驾驭MediaCodec,构建出高性能的Android视频处理应用。
转载请注明出处https://mp.csdn.net/mp_blog/creation/editor/154801674,谢谢合作!
