文章目录

  • 资料
  • 用到的接口
  • 获取所有的能够解析的编/解码器
  • MediaCodec在异步模式下的用法如下:(使用Buffer异步处理)
  • MediaCodec在同步模式下的用法如下:(使用Buffers同步处理)
  • 使用Buffer Arrays同步处理(deprecated)
  • MediaExtractor API介绍
  • 使用MediaCodec的编解码流程


资料

MediaCodec的使用介绍MediaCodec基本原理及使用
Android MediaCodec实现多段音视频的截取与拼接
安卓解码器MediaCodec解析Android直播开发之旅(8):Android硬编解码接口MediaCodec原理剖析
Android MediaCodec编解码详解及demoAndroid 音视频开发(六): MediaCodec API 详解
Android 音视频开发(五):使用 MediaExtractor 和 MediaMuxer API 解析和封装 mp4 文件
Android硬编解码接口MediaCodec使用完全解析(一)
Android分离合成音视频(用MediaExtractor和MediaMuxer)
Android音视频开发-入门(四):使用 MediaExtractor 和 MediaMuxer API 解析和封装 mp4 文件android中MediaCodec类解析
Android MediaCodec stuffAndroid 原生 MediaPlayer 和 MediaCodec 的区别和联系(二)
MediaCodec 官方API

用到的接口

获取所有的能够解析的编/解码器

// 获取所有支持的编码器数量
val codecCount: Int = MediaCodecList.getCodecCount()
for (i in 0 until codecCount) {
    val codecInfoAt: MediaCodecInfo = MediaCodecList.getCodecInfoAt(i)
    // 判断是否为编码器
    if (!codecInfoAt.isEncoder) {
        continue
    }
    // 获取编码器支持的MIME类型,并进行匹配
    val types = codecInfoAt.supportedTypes
    types.forEach { type: String ->
        log("type = $type")
    }
}

MediaCodec在异步模式下的用法如下:(使用Buffer异步处理)

MediaCodec codec = MediaCodec.createByCodecName(name);
MediaFormat mOutputFormat; // member variable
codec.setCallback(new MediaCodec.Callback() {
  @Override
  void onInputBufferAvailable(MediaCodec mc, int inputBufferId) {
    ByteBuffer inputBuffer = codec.getInputBuffer(inputBufferId);
    // fill inputBuffer with valid data
    …
    codec.queueInputBuffer(inputBufferId, …);
  }
 
  @Override
  void onOutputBufferAvailable(MediaCodec mc, int outputBufferId, …) {
    ByteBuffer outputBuffer = codec.getOutputBuffer(outputBufferId);
    MediaFormat bufferFormat = codec.getOutputFormat(outputBufferId); // option A
    // bufferFormat is equivalent to mOutputFormat
    // outputBuffer is ready to be processed or rendered.
    …
    codec.releaseOutputBuffer(outputBufferId, …);
  }
 
  @Override
  void onOutputFormatChanged(MediaCodec mc, MediaFormat format) {
    // Subsequent data will conform to new format.
    // Can ignore if using getOutputFormat(outputBufferId)
    mOutputFormat = format; // option B
  }
 
  @Override
  void onError(…) {
    …
  }
});
codec.configure(format, …);
mOutputFormat = codec.getOutputFormat(); // option B
codec.start();
// wait for processing to complete
codec.stop();
codec.release();

MediaCodec在同步模式下的用法如下:(使用Buffers同步处理)

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 Arrays同步处理(deprecated)

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();

MediaExtractor API介绍

MediaExtractor的作用是把音频和视频的数据进行分离。

主要API介绍:

setDataSource(String path):即可以设置本地文件又可以设置网络文件
getTrackCount():得到源文件通道数 
getTrackFormat(int index):获取指定(index)的通道格式
getSampleTime():返回当前的时间戳 
readSampleData(ByteBuffer byteBuf, int offset):把指定通道中的数据按偏移量读取到ByteBuffer中;
advance():读取下一帧数据
release(): 读取结束后释放资源
MediaExtractor extractor = new MediaExtractor();
 extractor.setDataSource(...);
 int numTracks = extractor.getTrackCount();
 for (int i = 0; i < numTracks; ++i) {
   MediaFormat format = extractor.getTrackFormat(i);
   String mime = format.getString(MediaFormat.KEY_MIME);
   if (weAreInterestedInThisTrack) {
     extractor.selectTrack(i);
   }
 }
 ByteBuffer inputBuffer = ByteBuffer.allocate(...)
 while (extractor.readSampleData(inputBuffer, ...) >= 0) {
   int trackIndex = extractor.getSampleTrackIndex();
   long presentationTimeUs = extractor.getSampleTime();
   ...
   extractor.advance();
 }

 extractor.release();
 extractor = null;

使用MediaCodec的编解码流程

一些编解码器对于它们的buffer要求是比较特殊的,比如内存对齐或是有特定的最小最大限制,为了适应广泛的可能性,buffer分配是由编解码器实现的。

  • 这看起来和“零拷贝”原则是相悖的,但大部分情况发生拷贝的几率是比较小的,因为编解码器并不需要复制或调整这些数据来满足要求,而且大多数情况可以直接使用buffer,比如直接从磁盘或网络读取数据到buffer中,不需要复制。

MediaCodec采用异步方式处理数据,并且使用了一组输入输出的buffer(ByteBuffer)。

  1. 使用者从MediacCodec请求一个空的输入buffer(ByteBuffer),填充满数据后将它传递给MediaCodec处理。
  2. MediaCodec处理完这些数据并将处理结果输出至一个空的输出buffer(ByteBuffer)中。
  3. 使用者从MediaCodec获取输出buffer的数据,消耗掉里面的数据,使用完输出buffer的数据之后,将其释放回编解码器。

音视频之~MediaCodec的基本原理及使用_android