MediaCodec的数据类型和使用方式

三种数据类型

编解码器处理三种数据:压缩数据原始音频数据原始视频数据

所有三种类型的数据都可以使用ByteBuffer进行处理,但是您应该使用原始视频数据的surface来提高编解码器的性能。Surface使用native video buffer,而无需将它们映射或复制到ByteBuffers;因此,它的效率更高。

在使用Surface时,通常无法访问原始视频数据,但可以使用ImageReader类访问raw视频帧。这可能仍然比使用ByteBuffers更有效,因为一些native buffer可能映射到ByteBuffer#isDirect ByteBuffers。使用ByteBuffer模式时,可以使用Image类和getInput/OutputImage(int)访问原始视频帧。

现在的基本上api版本都是大于21的,所以用的较多的就后面两种:

  • Synchronous API using buffers
  • Asynchronous API using buffers
Processing ModeAPI version <= 20API version >= 21
Synchronous API using buffer arraysSupportedDeprecated
Synchronous API using buffersNot AvailableSupported
Asynchronous API using buffersNot AvailableSupported
Raw Video Buffers

在ByteBuffer模式下,video buffer由MediaFormat::KEY_COLOR_FORMAT来确定。video codec支持三种颜色格式:

  • native raw video format:这是由COLOR_FormatSurface指定,可以与输入或输出surface一起使用。
  • flexible YUV buffers: COLOR_FormatYUV420Flexible,这些可以用于输入/输出surface,也可以在ByteBuffer模式下使用getInput/OutputImage.
  • other, specific formats:通常仅在ByteBuffer模式下支持这些格式。某些颜色格式是特定于供应商的,可以使用getInput/OutputImage.

MediaCodec的同步方式

java实现
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();

MediaCodec的异步实现方式

java实现
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 EOS

当需要结束时,必须向编解码器发送EOS信号,可以通过queueInputBuffer调用,指定buffer的flag为BUFFER_FLAG_END_OF_STREAM,可以在最后一个有效的输入输出buffer上执行,也可以通过指定一个额外的空输入输出buffer设置EOS flag来执行此操作,如果使用空buffer,时间戳会被忽略。

编解码器将继续返回输出buffer,直到它通过在dequeueOutputBuffer中的BufferInfo中获得相同EOS标志或通过Callback#onOutputBufferAvailable返回,最终发出EOS信号。这可以在最后一个有效输出buffer上设置,也可以在最后一个有效输出buffer之后的空缓冲区上设置,空buffer的时间戳需要忽略掉。

在发出输入end-of-stream信号后,不要提交额外的输入缓冲区,除非编解码器已经被flushed, or或者stopped and restarted。

Codec-specific Data

一些格式,尤其是AAC音频和MPEG4、H.264和H.265视频格式,需要在实际数据前有一个Codec-specific Data。在处理这种压缩格式时,这些数据必须在start()之后和实际frame数据之前提交到编解码器。在对queueInputBuffer的调用中,必须使用标志BUFFER_FLAG_CODEC_CONFIG来标记这些数据。

Codec-specific data也可以包含在key“csd-0”“csd-1”等在ByteBuffer条目中配置的格式中。这些key始终包含在从MediaExtractor#getTrackFormat获得的track MediaFormat中。格式中的Codec-specific data在start()时自动提交到编解码器,您不能explicitly提交此数据。如果格式不包含Codec-specific数据,则可以根据格式要求选择使用指定数量的buffer按正确顺序提交。对于H.264 AVC,还可以连接所有Codec-specific data,并将其作为单个codec-config buffer提交给编解码器。


MediaCodec Surface


使用输入Surface

使用Input Surface时,没有可访问的输入buffer,因为buffer会自动从输入到Surface传递到编解码器。调用dequeueInputBuffer将引发IllegalStateException,getInputBuffers()返回一个不能写入的伪ByteBuffer[]数组。

调用signalEndOfInputStream()以发出流结束的信号。在此调用之后,输入Surface将立即停止向编解码器submit数据。

使用输出Surface

当使用输出Surface,数据处理与ByteBuffer模式几乎相同;但是,输出buffer将不可访问,并且表示为空值。getOutputBuffer/Image将返回null,getOutputBuffers()将返回仅包含null-s的数组。

使用输出surface,可以选择是否在surface上渲染每个输出buffer,您有三个选择:

  • Do not render the buffer: Call releaseOutputBuffer(bufferId, false)
  • Render the buffer with the default timestamp: Call releaseOutputBuffer(bufferId, true)
  • Render the buffer with a specific timestamp: Call releaseOutputBuffer(bufferId, timestamp)
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
MediaCodec的dequeueOutputBuffer方法用于从输出缓冲区中获取编解码后的数据。它的使用方法如下: 1. 调用dequeueOutputBuffer获取一个输出缓冲区的索引值: ```java int outputBufferIndex = codec.dequeueOutputBuffer(bufferInfo, timeoutUs); ``` 此方法会阻塞等待,直到有可用的输出缓冲区。其中,bufferInfo是一个MediaCodec.BufferInfo类型的对象,用于存储输出缓冲区的相关信息,timeoutUs是超时时间,单位为微秒。 2. 根据输出缓冲区的索引值获取输出缓冲区的数据: ```java if (outputBufferIndex >= 0) { ByteBuffer outputBuffer = codec.getOutputBuffer(outputBufferIndex); if (outputBuffer != null) { outputBuffer.position(bufferInfo.offset); outputBuffer.limit(bufferInfo.offset + bufferInfo.size); byte[] chunk = new byte[bufferInfo.size]; outputBuffer.get(chunk); // 处理获取到的数据 ... } } ``` 这里需要注意的是,获取到的ByteBuffer对象需要设置它的position和limit才能获取到正确的数据。此外,获取到的数据可能是多个NALU或多帧数据,需要根据具体情况进行处理。 3. 释放输出缓冲区: ```java codec.releaseOutputBuffer(outputBufferIndex, render); ``` 其中,render表示是否渲染此帧数据。如果设置为true,则会将数据渲染到Surface上。 需要注意的是,在使用MediaCodec的时候,应该及时释放输入输出缓冲区,以免造成内存泄漏。同时,也应该根据具体情况合理设置MediaCodec的参数,以达到最佳的编解码效果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值