安卓视频编解码之mediacodec

编码:相当于压缩数据,把这些原始YUV格式数据编码为.h264或者.h265等类型数据,然后利用mediamuxer把音频和视频数据最终合成mp4等视频类型。
解码就是把.h264的数据解码为YUV等原始格式数据。
硬编码和软编码:
硬编码:用设备gpu去实现编解码,这样可以减轻cpu压力
软编码:让cpu来进行编解码,在c层代码来进行编解码。
软硬编码对比:硬编速度快,且系统自带不需要引入外部的库,硬编的压缩率比较低。对于软编码来说,虽然速度慢,但压缩率比较高。
1 MediaCodec简介
安卓提供的底层api接口,可以访问更底层的多媒体编解码器组件。是Android提供的用于对音视频进行编解码的类,它通过访问底层的codec来实现编解码的功能。是android media基础框架的一部分。

  1. 工作模型

     

    work model

编解码器工作过程为处理输入数据产生输出数据,mediacodec采用异步方式处理数据,并且使用了一组输入输出缓存。
首先请求或接收到一个空的输入缓存,向其中填充满数据并将它传递给编解码器处理。
编解码器处理完这些数据并将处理结果输出至一个空的输出缓存中。
请求或接收到一个填充了的结果数据的输出缓存,使用完其中的数据,并将其释放给编解码器再次使用。

  1. 数据类型(data types)
    编解码器可以处理三种类型的数据:
    1)压缩数据(经过.h264/.h265等编码的视频数据或AAC等编码的音频数据)
    2)原始音频数据
    3)原始视频数据
    压缩缓存(Compressed Buffers)
    压缩数据需要指定数据格式,这样编码/解码器才能知道如何处理这些压缩数据。
    对于视频一般是包含完整的一帧数据,也就是要告诉编码器从哪儿到哪儿是一帧完整的数据或者从编码器得到一帧完整的数据。常用的就是让mediacodec解码h264数据,必须将分隔符和nalu单元作为一个完整的数据帧传给解码器才能正确解码。
    对于音频数据通常是单个可访问单元(一个编码的音频片段,通常包含几毫秒的遵循特定格式类型的音频数据)。
    1) 数据通过bytebuffer类来表示
    2)可以设置Surface来获取/呈现原始的视频数据,surface 使用的本地buffer,不需要进行bytebuffers拷贝。可以让编解码器的效率更高
    3) 通常在使用surface的时候,无法访问原始的视频数据,但是可以使用imagereader访问解码后的原始视频帧。在使用bytebuffer的模式下,可以使用image类和getinput/outputimage(int)获取原始视频帧。

  2. mediacodec的状态转换:
    在编解码的生命周期内有三种理论状态:停止态-stopped、执行态-executing、释放态-released。
    利用代码创建了一个编解码器,此时编解码器处于未初始化状态(Uninitialized)。首先,需要使用configure(…)方法对编解码器进行配置,这将使编解码器转为配置状态(Configured)。然后调用start()方法使其转入执行状态(Executing)。在这种状态下可以通过上述的缓存队列操作处理数据。
    执行状态(Executing)包含三个子状态: 刷新(Flushed)、运行( Running) 以及流结束(End-of-Stream)。在调用start()方法后编解码器立即进入刷新子状态(Flushed),此时编解码器会拥有所有的缓存。一旦第一个输入缓存(input buffer)被移出队列,编解码器就转入运行子状态(Running),编解码器的大部分生命周期会在此状态下度过。当你将一个带有end-of-stream 标记的输入缓存入队列时,编解码器将转入流结束子状态(End-of-Stream)。在这种状态下,编解码器不再接收新的输入缓存,但它仍然产生输出缓存(output buffers)直到end-of- stream标记到达输出端。你可以在执行状态(Executing)下的任何时候通过调用flush()方法使编解码器重新返回到刷新子状态(Flushed)。
    通过调用stop()方法使编解码器返回到未初始化状态(Uninitialized),此时这个编解码器可以再次重新配置 。当你使用完编解码器后,你必须调用release()方法释放其资源。
    在极少情况下编解码器会遇到错误并进入错误状态(Error)。这个错误可能是在队列操作时返回一个错误的值或者有时候产生了一个异常导致的。通过调用 reset()方法使编解码器再次可用。你可以在任何状态调用reset()方法使编解码器返回到未初始化状态(Uninitialized)。否则,调用 release()方法进入最终的Released状态。

  3. NDK中使用MediaCodec编解码视频
    NDK中使用 MediaCodec 编解码视频 - 知乎 (zhihu.com)

    NDK(Native Development Kit缩写)一种基于原生程序接口的软件开发工具包,可以让您在 Android 应用中利用 C 和 C++ 代码的工具。

MediaCodec的两个Buffer和三板斧
MediaCodec内部包含InputBuffer和OutputBuffer,内部有一个自启线程,不断去查询两个Buffer,是一个生产者消费者模型。

进行数据处理时主要靠三板斧。

第一步:取buffer地址
AMediaCodec_dequeueInputBuffer
第二步:获取buffer数据
AMediaCodec_getInputBuffer
第三步:buffer入队
AMediaCodec_queueInputBuffer
InputBuffer和OutputBuffer基本是对称的:

第一步:取buffer地址
AMediaCodec_dequeueOutputBuffer
第二步:获取buffer数据
AMediaCodec_getOutputBuffer
第三步:buffer释放
AMediaCodec_releaseOutputBuffer
只有第三步不同,AMediaCodec_queueInputBuffer是数据入队等待消费,AMediaCodec_releaseOutputBuffer是释放数据。

编码和解码过程,InputBuffer和OutputBuffer就互相置换下。

解码:原始数据(视频流)-> 提取器AMediaExtractor->InputBuffer->OutputBuffer->帧数据(YUV420sp,PCM)

编码:帧数据(视频流)->InputBuffer->OutputBuffer->合成器AMediaMuxer

猜你喜欢

转载自blog.csdn.net/chanimei_11/article/details/124820532