Android MediaCodec

一、MediaCodec介绍

MediaCodec是Android音视频中相当重要的一个API。MediaCodec类可以用于使用一些基本的多媒体编解码器(音视频编解码组件),它是Android基本的多媒体支持基础架构的一部分通常和 MediaExtractor, MediaSync, MediaMuxer, MediaCrypto, MediaDrm, Image, Surface, and AudioTrack 一起使用。

广义而言,编解码器处理输入数据以生成输出数据。 它异步处理数据并使用一组输入和输出缓冲区。 在简单的层面上,您请求(或接收)一个空输入缓冲区,填充数据并将其发送到编解码器进行处理。 编解码器使用数据并将其转换为其空的输出缓冲区之一。 最后,您请求(或接收)一个填充的输出缓冲区,消耗其内容并将其释放回编解码器。

一个编解码器可以处理输入的数据来产生输出的数据,编解码器使用一组输入和输出缓冲器来异步处理数据。你可以创建一个空的输入缓冲区,填充数据后发送到编解码器进行处理。编解码器使用输入的数据进行转换,然后输出到一个空的输出缓冲区。最后你获取到输出缓冲区的数据,消耗掉里面的数据,释放回编解码器。如果后续还有数据需要继续处理,编解码器就会重复这些操作。

上图是一张MediaCodec编码器的数据流程图,1)对于提供数据的Client来说,首先是向Codec申请一组空buffers(图中的inputbuffers),然后将数据放入buffers中,最后将装满数据的buffers还给Codec;2)对于消耗数据的Client来说,先向Codec申请一组带处理好的数据的buffers(图中的outputbuffers),然后消费buffers中的数据,最后将buffers还给Codec。

MediaCodec的编解方式

MediaCodec可以通过同步和异步两种方式进行编解码。同步编解码是指在主线程中进行解码,这种方式简单易懂,但是会导致主线程阻塞,影响用户体验。而异步编解码则是在子线程中进行编解码,不会阻塞主线程,可以提高应用的响应速度和流畅度。在异步编解码中,需要开辟两个线程,分别用于视频和音频的编解码,通过线程间的通信来实现音视频的同步播放。

在Android 5.0 之后,google 建议使用异步解码的方式去使用 MediaCodec。

MediaCodec生命周期

MediaCodec 有三种状态,分别是执行(Executing)、停止(Stopped)和释放(Released),其中执行和停止分别有三个子状态,执行的三个字状态分别是 Flushed、Running 和 Stream-of-Stream,停止的三个子状态分别是 Uninitialized、Configured 和 Error,MediaCodec 生命周期示意图如下:

上图是Codec的生命周期示意图,从图中可以看出:

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本身并不具备Codec能力,通过调动底层编解码组件获得了Codec的能力,底层的编解码组件包括ACodec(采用 OMX 框架的解码器实现)和CCodec(采用 Codec 2 框架的解码器实现),如下是MediaCodec与ACodec组件的关系图:

MediaCodec 使用异步消息处理机制(AMessage/ALooper/AHandler)

编解码器支持的数据类型

编解码器对三种数据进行操作:压缩数据,原始音频数据和原始视频数据。 所有三种数据都可以使用ByteBuffers进行处理,但您应该使用Surface作为原始视频数据以提高编解码器的性能。 Surface使用本地视频缓冲区而不映射或将它们复制到ByteBuffers; 因此,它更有效率。 使用Surface时通常无法访问原始视频数据,但可以使用ImageReader类来访问不安全的已解码(原始)视频帧。 这可能仍然比使用ByteBuffers更高效,因为某些本地缓冲区可能映射到direct ByteBuffers中。 当使用ByteBuffer的模式,您可以使用访问原始视频帧Image

### 关于 Android MediaCodec 的使用教程 #### 创建并配置 MediaCodec 实例 创建 `MediaCodec` 对象可以通过指定编解码器名称来实现。这通常用于特定硬件加速的需求。 ```java String name = "OMX.google.h264.decoder"; // 示例编解码器名称 MediaCodec codec = MediaCodec.createByCodecName(name); ``` 对于更通用的情况,可以利用 `MediaCodecList` 来查找适合给定媒体格式的编码器或解码器[^3]: ```java MediaCodecInfo info; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { MediaCodecList mcl = new MediaCodecList(MediaCodecList.ALL_CODECS); info = selectCodec(mcl.getCodecInfos(), MIME_TYPE); } else { int numCodecs = MediaCodecList.getCodecCount(); for (int i = 0; i < numCodecs; i++) { MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i); if (!codecInfo.isEncoder()) continue; String[] types = codecInfo.getSupportedTypes(); for (String type : types) { if (type.equalsIgnoreCase(MIME_TYPE)) { info = codecInfo; break; } } } } ``` #### 配置输入输出缓冲区 一旦获得了合适的 `MediaCodec` 实例,则需对其进行初始化和配置。此过程涉及设置参数如比特率、分辨率等,并通过 `configure()` 方法完成实际设定。 ```java MediaFormat format = MediaFormat.createVideoFormat(MIME_TYPE, width, height); // 设置其他必要属性... format.setInteger(MediaFormat.KEY_BIT_RATE, bitRate); format.setInteger(MediaFormat.KEY_FRAME_RATE, frameRate); format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface); format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, IFRAME_INTERVAL); // 如果是在 Lollipop 或更高版本上运行,请确保移除帧速率键 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { format.setString(MediaFormat.KEY_FRAME_RATE, null); } codec.configure(format, surface, null, MODE); ``` #### 编解码流程管理 启动编解码操作之前调用 `start()` 启动组件;当不再需要时记得释放资源以防止内存泄漏。 ```java try { codec.start(); while (isRunning && !Thread.interrupted()) { processInput(); // 处理输入数据 processOutput(); // 获取处理后的输出数据 } } finally { codec.stop(); codec.release(); } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值