MediaCodec音视频编码解码

前言

MediaCodecAPI 16之后Google推出的用于音视频编解码的一套可访问底层的媒体的API,可以直接利用硬件加速进行视频的编解码。调用的时候需要先初始化MediaCodec作为视频的编码器,然后只需要不停传入原始的YUV数据进入编码器就可以直接输出编码好的h264流。

通常与 MediaExtractor, MediaSync, MediaMuxer, MediaCrypto, MediaDrm, Image, Surface, and AudioTrack 一起结合使用

Alt text

相关资源

一些示例代码和常见问题的答案。
https://bigflake.com/mediacodec/

MediaCodec类官网介绍
https://developer.android.com/reference/android/media/MediaCodec.html

编解码的过程

图来自官网
Alt text

一、创建MediaCodec对象

编码MediaCodec.createByCodecName(name)
解码MediaCodec.createDecoderByType(type)

二、配置状态configure(…)
三、进入执行状态 start()

执行状态中进行数据处理编码

  1. dequeueInputBuffer获取输入队列空闲数组下标
  2. getInputBuffer获取输入队列,
  3. queueInputBuffer把原始YUV数据送入编码器
  4. dequeueOutputBuffer获取输出队列空闲数组下标
  5. getOutputBuffer获取输出的h264流
  6. releaseOutputBuffer把输出buffer还给系统,重新放到输出队列中
四、退出进入未初始化状态 stop()
五、释放对象及相关资源 release()

ByteBuffer编码例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
MediaCodec codec = MediaCodec.createByCodecName(name);
codec.configure(format, …);
MediaFormat outputFormat = codec.getOutputFormat(); // option B
codec.start();
for (;;) {
int inputBufferId = codec.dequeueInputBuffer(timeoutUs);
if (inputBufferId >= 0) {
ByteBuffer inputBuffer = codec.getInputBuffer(…);
// fill inputBuffer with valid data

codec.queueInputBuffer(inputBufferId, …);
}
int outputBufferId = codec.dequeueOutputBuffer(…);
if (outputBufferId >= 0) {
ByteBuffer outputBuffer = codec.getOutputBuffer(outputBufferId);
MediaFormat bufferFormat = codec.getOutputFormat(outputBufferId); // option A
// bufferFormat is identical to outputFormat
// outputBuffer is ready to be processed or rendered.

codec.releaseOutputBuffer(outputBufferId, …);
} else if (outputBufferId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
// Subsequent data will conform to new format.
// Can ignore if using getOutputFormat(outputBufferId)
outputFormat = codec.getOutputFormat(); // option B
}
}
codec.stop();
codec.release();

Buffer数组编码例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
MediaCodec codec = MediaCodec.createByCodecName(name);
codec.configure(format, …);
codec.start();
ByteBuffer[] inputBuffers = codec.getInputBuffers();
ByteBuffer[] outputBuffers = codec.getOutputBuffers();
for (;;) {
int inputBufferId = codec.dequeueInputBuffer(…);
if (inputBufferId >= 0) {
// fill inputBuffers[inputBufferId] with valid data

codec.queueInputBuffer(inputBufferId, …);
}
int outputBufferId = codec.dequeueOutputBuffer(…);
if (outputBufferId >= 0) {
// outputBuffers[outputBufferId] is ready to be processed or rendered.

codec.releaseOutputBuffer(outputBufferId, …);
} else if (outputBufferId == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
outputBuffers = codec.getOutputBuffers();
} else if (outputBufferId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
// Subsequent data will conform to new format.
MediaFormat format = codec.getOutputFormat();
}
}
codec.stop();
codec.release();