Android平台使用MediaCodec进行H264格式的视频编解码

Android平台使用MediaCodec进行H264格式的视频编解码

  • Android多媒体简介

  • MediaCodec

  • MediaExtractor

  • MediaMux

  • H264关键词


Android多媒体简介

提起android中的多媒体,就不得不提两个类,那就是MediaPlayer和MediaRecorder,这两个是分别用来进行音视频播放和录制的类。这两个类都可以在java中调用的到,他们都最终访问到android系统提供的多媒体服务。

android中的多媒体服务是有mediaplayerservice来提供的,它是一个本地服务,也就是常说到的native service。由C++语言实现,服务的业务主体是由C++类MediaPlayerService来实现,而该类是在服务进程mediaserver中调用的。

frameworks/av/media/mediaserver

以上是mediaserver的实现源码目录所在,主体就是一个简单的main函数,在此不再详细解读,感兴趣的读者可以自行研究。

MediaPlayerService是android多媒体框架的核心,其中涉及的很多类和复杂的处理,它运行在一个进程中,那么其他进程如何来获取它提供的功能的,这就涉及到android里面的又一个重量级人物,那就是binder。binder是android中用来进行进程间通信的,作为一种IPC手段来使用。除了binder,android也使用了一些其他的IPC手段,比如说UNIX socket等。为什么android大部分的主体实现都是用binder做为IPC手段,也许是效率,也许是其他原因,最终才促使google做了这个决定。本篇主要介绍多媒体,binder内容不做深入研究。

上面提到MediaPlayerService运行于服务进程中,那么客户进程如何来访问到他提供的功能呢,就是利用binder,binder由Bn端和Bp端,Bn端一般作为服务端,Bp端作为客户端,由此就把进程的概念模糊化了,上层开发者不必深入研究中间的通信过程,只知道利用Bp端去使用Bn端的功能就可以了。

那么接下来就要讲到MediaPlayer了,它在C++层有具体实现,其中就会封装到一个BpMediaPlayer,而MediaPlayerService中封装了一个BnMediaPlayer,由此两者实现了通信。注意:此处并不是通过BpMediaPlayerService来进行通信。为什么不这么做呢,也许是因为MeidaPlayerService承担的任务太重了,内部把录制和播放两个实例分开,模块化了,它不仅仅与MediaPlayer通信,也与Media Recorder通信,所以不要被它的名字所迷惑。

与MediaPlayer类似,C++层也实现了一个MediaRecorder类。有了这两个类的native实现,那么java层调用的MediaPlayer和MediaRecorder就都在native端对应上了,此时只需要通过jni把java和C++的实现对应起来,我们就可以在上层使用MediaPlayer和MediaRecorder了。

MediaCodec

通过上一节的介绍,我们应该对android平台的多媒体框架有一个大概的认识了。

我们知道,可以利用MediaPlayer和MediaRecorder来使用android实现的多媒体服务功能。但是这两个类并不是万能的。

MediaPlayer可以用来播放多媒体文件,但是它却无法操作视频解码的细节,比如采用哪种解码器,软解还是硬解,我们无法进行控制。只能按照平台上的实现来进行播放。

MediaRecorder的局限性就更大了,它只能录制camera的源(也许不正确,本人做系统开发,不太了解上层的一些接口),至少目前我遇到的都是把camera作为source的录制源的。那么我如果想要调用编码器编码一段原始的yuv420格式的视频源,就不能使用该接口了。

由此MediaCodec类应运而生,它是android平台上操作编解码器的一个类,同样实现了java层和C++层。在C++层的实现中,是通过一个OMXClinet来与Bn端的OMX来进行通信的,OMX服务也是在MediaPlayerService中实现的。OMX是对OMXMaster类的一个封装,它是负责对平台上的所有编解码器进行管理的一个类,包括软解和硬解等。

这样通过MediaCodec类,我们就可以直接操作android上的软硬件编解码器。这种操作更加的简单粗暴,对程序员的要求当然会更高。

MediaExtractor

对多媒体编解码比较熟悉的人都会知道,文件的格式并不代表编码格式,其实,文件的不同格式只是相当于对一种容器的区分,这种容器中装的就是通过编码器编码后的音频数据和视频数据。

MediaExtractor的作用就是把容器中音频数据和视频数据分开,分开后才能针对音频和视频进行不同的解码。

比如一个.mp4文件中,视频数据是按照H264格式编码的,音频是按照AAC格式编码的。那么通过MediaExtractor进行分流以后,就可以把视频数据送给H264解码器,而把音频数据送给AAC解码器。MediaExtractor就是起这么一个分流作用的。

MediaMuxer

与MediaExtractor相反,MediaMuxer是用在把不同的音频数据和视频数据组合在一起的类,java层和native c++都有实现。相当于是一个封装器,主要在编码流程中使用,比如在编码的最后通过MediaMuxer组合封装成一个MP4文件。

H264关键词

CSD: Codec-Specific Data,Codec特定数据,是一坨原始数据,包含诸如 Sequence Parameter Set 和Picture Parameter Set之类的数据。它是由MediaCodec编码器生成,并且MediaCodec解码器在解码时一定需要的数据。从编码器读数据时BUFFER_FLAG_CODEC_CONFIG就标志着CSD data的到来。在解码时必须要把此数据首先传给解码器,它会做一些初始配置工作。

SPS:Sequence Parameter Set 序列参数集,H.264码流第一个 NALU

PPS:Picture Parameter Set图像参数集,H.264码流第二个 NALU

IDR帧:IDR帧属于I 帧。解码器收到IDR frame时,将所有的参考帧队列丢弃 ,这点是所有I 帧共有的特性,但是收到IDR帧时,解码器另外需要做的工作就是:把所有的PPS和SPS参数进行更新。由此可见,在编码器端,每发一个 IDR,就相应地发一个 PPS&SPS_nal_unit

I帧:帧内编码帧是一种自带全部信息的独立帧,无需参考其它图像便可独立进行解码,视频序列中的第一个帧始终都是I帧。

P帧:前向预测编码帧

B帧:双向预测内插编码帧


你可能感兴趣的:(android)