Android之MediaCodec

MediaCodec介绍

MediaCodec类可以用于使用一些基本的多媒体编解码器(音视频编解码组件),它主要是用来编码和解码音视频数据。

  • 编解码器能处理的数据类型为:压缩数据、原始音频数据和原始视频数据。
  • 主要的生命周期为:Stopped、Executing、Released。
    • Stopped的状态下也分为三种子状态:Uninitialized、Configured、Error。
    • Executing的状态下也分为三种子状态:Flushed, Running、End-of-Stream。

流程:

  1. 当创建编解码器的时候处于未初始化状态。首先你需要调用configure(…)方法让它处于Configured状态,然后调用start()方法让其处于Executing状态。在Executing状态下,你就可以使用缓冲区来处理数据。
  2. Executing的状态下也分为三种子状态:Flushed, Running、End-of-Stream。在start() 调用后,编解码器处于Flushed状态,这个状态下它保存着所有的缓冲区。一旦第一个输入buffer出现了,编解码器就会自动运行到Running的状态。当带有end-of-stream标志的buffer进去后,编解码器会进入End-of-Stream状态,这种状态下编解码器不在接受输入buffer,但是仍然在产生输出的buffer。此时你可以调用flush()方法,将编解码器重置于Flushed状态。
  3. 调用stop()将编解码器返回到未初始化状态,然后可以重新配置。 完成使用编解码器后,您必须通过调用release()来释放它。
  4. 在极少数情况下,编解码器可能会遇到错误并转到错误状态。 这是使用来自排队操作的无效返回值或有时通过异常来传达的。 调用reset()使编解码器再次可用。 您可以从任何状态调用它来将编解码器移回未初始化状态。 否则,调用 release()动到终端释放状态。

MediaCodec编码视频步骤

1. 初始化MediaCodec

初始化MediaCodec,方法有两种,分别是通过名称和类型来创建,对应的方法为:

MediaCodec createByCodecName (String name);
MediaCodec createEncoderByType (String type);

##2. 配置MediaCodec
这个类包含了比特率、帧率、关键帧间隔时间等,其中比特率如果太低就会造成类似马赛克的现象。

mMF = MediaFormat.createVideoFormat(MIME_TYPE, width, height);
mMF.setInteger(MediaFormat.KEY_BIT_RATE, bitrate);  
mMF.setInteger(MediaFormat.KEY_FRAME_RATE, framerate);  
if (mPrimeColorFormat != 0){
    mMF.setInteger(MediaFormat.KEY_COLOR_FORMAT, mPrimeColorFormat);  
}
mMF.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1); //关键帧间隔时间 单位s
mMC.configure(mMF, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);

3. 打开编码器,获取输入输出缓冲区

mMC.start();
mInputBuffers = mMC.getInputBuffers();
mOutputBuffers = mMC.getOutputBuffers();

4. 输入数据

1)获取可使用缓冲区位置得到索引

int inputbufferindex = mMC.dequeueInputBuffer(BUFFER_TIMEOUT);

如果存在可用的缓冲区,此方法会返回其位置索引,否则返回-1,参数为超时时间,单位是毫秒,如果此参数是0,则立即返回,如果参数小于0,则无限等待直到有可使用的缓冲区,如果参数大于0,则等待时间为传入的毫秒值。

2)传入原始数据

ByteBuffer inputBuffer = mInputBuffers[inputbufferindex];
inputBuffer.clear();//清除原来的内容以接收新的内容
inputBuffer.put(bytes, 0, len);//len是传进来的有效数据长度
mMC.queueInputBuffer(inputbufferindex, 0, len, timestamp, 0);

此缓冲区一旦使用,只有在dequeueInputBuffer返回其索引位置才代表它可以再次使用。

5. 获取输出数据

获取输入原始数据和获取输出数据最好是异步进行,因为输入一帧数据不代表编码器马上就会输出对应的编码数据,可能输入好几帧才会输出一帧。获取输出数据的步骤与输入数据的步骤相似:

1)获取可用的输出缓冲区

int outputbufferindex = mMC.dequeueOutputBuffer(mBI, BUFFER_TIMEOUT);

其中参数一是一个BufferInfo类型的实例,参数二为超时时间,负数代表无限等待(可见,不要在主线程进行操作)。

2)获取输出数据

mOutputBuffers[outputbufferindex].get(bytes, 0, mBI.size);

3)释放缓冲区

mMC.releaseOutputBuffer(outputbufferindex, false);

MediaCodec解码视频步骤

1. 实例化解码器

mMC = MediaCodec.createDecoderByType(MIME_TYPE);

2. 配置解码器

此处需要配置用于显示图像的Surface、MediaFormat包含视频的pps和sps(包含在编码出来的第一帧数据)

int[] width = new int[1];
int[] height = new int[1];  
AvcUtils.parseSPS(sps, width, height);//从sps中解析出视频宽高
mMF = MediaFormat.createVideoFormat(MIME_TYPE, width[0], height[0]);
mMF.setByteBuffer("csd-0", ByteBuffer.wrap(sps));
mMF.setByteBuffer("csd-1", ByteBuffer.wrap(pps));
mMF.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, width[0] * height[0]);
mMC.configure(mMF, surface, null, 0);

3. 开启解码器并获取输入输出缓冲区

mMC.start();
mInputBuffers = mMC.getInputBuffers();
mOutputBuffers = mMC.getOutputBuffers();

4. 输入数据

1)获取可用的输入缓冲区

int inputbufferindex = mMC.dequeueInputBuffer(BUFFER_TIMEOUT);

返回值为可用缓冲区的索引

ByteBuffer inputBuffer = mInputBuffers[inputbufferindex];
inputBuffer.clear();

2)输入数据

inputBuffer.put(bytes, 0, len);
mMC.queueInputBuffer(inputbufferindex, 0, len, timestamp, 0);

5. 获取输出数据

这一步与4同样应该异步进行,其具体步骤与上面解码的基本相同,在释放缓冲区的时候需要注意第二个参数设置为true,表示解码显示在Surface上。

mMC.dequeueOutputBuffer(mBI, BUFFER_TIMEOUT);
mOutputBuffers[i].get(data, 0, mBI.size);
mMC.releaseOutputBuffer(outputbufferindex, true);

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×