1、创建MediaCodec对象:
首先需要创建一个MediaCodec对象,指定要进行的编解码类型(如音频编码、音频解码、视频编码、视频解码)以及相应的参数。
2、配置MediaFormat:
针对要编解码的数据流,需要配置相应的MediaFormat,包括音视频数据的格式、编解码器的参数等。
3、配置输入缓冲区:
对于编码操作,需要配置输入缓冲区,将原始的音视频数据填充到输入缓冲区中;对于解码操作,需要配置输出缓冲区,用于接收解码后的音视频数据。
4、启动MediaCodec:
配置完成后,可以启动MediaCodec,开始进行编解码操作。
5、处理输入数据:
对于编码操作,将原始的音视频数据传入输入缓冲区;对于解码操作,将编码后的音视频数据传入输入缓冲区。
6、处理输出数据:
对于编码操作,从输出缓冲区获取编码后的音视频数据;对于解码操作,从输出缓冲区获取解码后的音视频数据。
7、停止和释放资源:
编解码操作完成后,需要停止MediaCodec,并释放相关资源。
MediaCodeC 比较重要的常用方法如下所示
方法 | 描述 |
---|---|
createDecoderByType(String type) | 创建解码器 |
createEncoderByType(String type) | 创建编码器 |
configure(MediaFormat format, Surface surface, MediaCrypto crypto, int flags) | 配置MediaCodec相关参数,format 是一个 MediaFormat 对象,包含了媒体数据的详细参数。surface 是一个 Surface 对象,用于显示解码的视频。crypto 是一个 MediaCrypto 对象,用于处理加密的媒体数据。flags 是一个标志位,用于指定是编码还是解码。 |
start() | 启动 MediaCodec |
stop() | 停止 MediaCodec |
release() | 释放 MediaCodec |
getInputBuffers() | 获取输入缓冲区。输入缓冲区是一个 ByteBuffer 数组,你可以将媒体数据填充到这些缓冲区。 |
getOutputBuffers() | 获取输出缓冲区。输出缓冲区是一个 ByteBuffer 数组,你可以从这些缓冲区读取编解码后的数据。 |
dequeueInputBuffer(long timeoutUs) | 获取一个可用的输入缓冲区的索引。timeoutUs 是等待输入缓冲区可用的超时时间(以微秒为单位)。 |
queueInputBuffer(int index, int offset, int size, long presentationTimeUs, int flags) | 将填充了数据的输入缓冲区提交给编解码器。index 是输入缓冲区的索引,offset 和 size 指定了有效数据在缓冲区中的位置,presentationTimeUs 是这个缓冲区的呈现时间戳,flags 是标志位。 |
dequeueOutputBuffer(MediaCodec.BufferInfo info, long timeoutUs) | 获取一个填充了编解码后数据的输出缓冲区的索引。info 是一个 BufferInfo 对象,用于接收关于输出缓冲区的信息,timeoutUs 是等待输出缓冲区可用的超时时间(以微秒为单位)。 |
releaseOutputBuffer(int index, boolean render) | 将输出缓冲区返回给编解码器。index 是输出缓冲区的索引,render 指定是否应该显示这个缓冲区的内容。 |
public int dequeueInputBuffer (long timeoutUs)
// 获取输入缓冲区
public ByteBuffer getInputBuffer(int index)
// 将填满数据的inputBuffer提交到编码队列
public final void queueInputBuffer(int index,int offset, int size, long presentationTimeUs, int flags)
// 获取已成功编解码的输出缓冲区的索引
public final int dequeueOutputBuffer(BufferInfo info, long timeoutUs)
// 获取输出缓冲区
public ByteBuffer getOutputBuffer(int index)
// 释放输出缓冲区
public final void releaseOutputBuffer(int index, boolean render)
MediaCodec 同步模式、异步模式
使用同步模式还是异步模式。同步模式流程简单,但效率更低;异步模式涉及更多线程,流程更加复杂但效率更高。
同步模式
MediaCodec codec = MediaCodec.createByCodecName(name);
codec.configure(format, …);
MediaFormat outputFormat = codec.getOutputFormat();
codec.start();
for (;;) {
int inputBufferId = codec.dequeueInputBuffer(timeoutUs);
if (inputBufferId >= 0) {
ByteBuffer inputBuffer = codec.getInputBuffer(…);
codec.queueInputBuffer(inputBufferId, …);
}
int outputBufferId = codec.dequeueOutputBuffer(…);
if (outputBufferId >= 0) {
ByteBuffer outputBuffer = codec.getOutputBuffer(outputBufferId);
MediaFormat bufferFormat = codec.getOutputFormat(outputBufferId);
}
异步模式
MediaCodec codec = MediaCodec.createByCodecName(name);
MediaFormat mOutputFormat; // member variable
codec.setCallback(new MediaCodec.Callback() {
@Override
void onInputBufferAvailable(MediaCodec mc, int inputBufferId) {}
@Override
void onOutputBufferAvailable(MediaCodec mc, int outputBufferId, …) {}
}
调用线程,调用 start() stop() 等方法的线程
回调线程, MediaCodec 调用回调函数的线程。
时间戳
DTS (Decoding Time Stamp) :
即解码时间戳,这个时间戳的意义在于告诉播放器该在什么时候解码这一帧的数据
PTS (Presentation Time Stamp) :
显示时间戳,这个时间戳告诉播放器,什么时候播放这一帧
需要注意的是,虽然 DTS 、PTS 是用于指导播放端的行为,但他们是在编码的时候,由编码器生成的。
在没有B帧的情况下,DTS和 PTS 的输出顺序是一样的,一旦存在 B 帧,则顺序不一样。