MediaCodec的总结

参考:android MediaCodec

MediaCodec的api支持三种数据类型

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

所有三种类型的数据都可以使用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)
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MediaCodec是Android平台上的一个多媒体编解码器类,它提供了硬件加速的音视频编解码功能。通过使用MediaCodec,开发者可以在Android设备上高效地进行音视频编解码操作。 MediaCodec可以用于解码和编码各种音视频格式,包括但不限于H.264、H.265、AAC、MP3等。它可以直接与底层硬件交互,利用硬件加速来提高音视频处理的性能和效率。 使用MediaCodec进行音视频编解码的基本流程如下: 1. 创建MediaCodec对象:通过调用createDecoderByType()或createEncoderByType()方法创建一个指定类型的解码器或编码器。 2. 配置MediaCodec:设置解码器或编码器的参数,如输入数据格式、输出数据格式、码率等。 3. 启动MediaCodec:调用start()方法启动解码器或编码器。 4. 处理输入数据:将待解码或待编码的数据传递给MediaCodec进行处理,可以通过configure()方法设置输入缓冲区和输出缓冲区。 5. 处理输出数据:从MediaCodec获取解码或编码后的数据,可以通过dequeueInputBuffer()和dequeueOutputBuffer()方法获取输入缓冲区和输出缓冲区的索引,然后通过getInputBuffer()和getOutputBuffer()方法获取具体的输入数据和输出数据。 6. 释放资源:完成音视频编解码后,调用stop()和release()方法释放MediaCodec对象。 MediaCodec的使用可以实现高效的音视频处理,尤其在需要处理大量音视频数据的场景下,能够提供更好的性能和用户体验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值