Android 音视频编解码

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 帧,则顺序不一样。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容