在Android 4.3系统之后,用MediaCodec编码视频成为了主流的使用场景,尽管Android的碎片化比较严重,会导致一些兼容性问题,但是硬件编码器的性能以及速度是非常可观的,并且在4.3系统之后可以通过Surface来配置编码器的输入,大大降低了显存到内存的交换过程所使用的时间,从而使得整个应用的体验得到大大提升。由于输入和输出已经确定,因此接下来将直接编写MediaCodec编码视频帧的过程。
构造一个MediaCodec实例,通过该类的静态函数来实现。构造一个AVC的Codec,其代码如下:
MediaCodec mMediaCodec = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_AVC);
构造出该实例之后,就需要配置该编码器了,配置编码器最重要的是需要传递一个MediaFormat类型的对象,该对象中配置的是码率、帧率、关键帧间隔以及颜色格式,此外代码如下:
MediaFormat mediaFormat = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, mWidth, mHeight);
// 1500kbs码率
mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, 1500_000);
//帧率
mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 20);
//关键帧间隔
mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 20);
//颜色格式(RGB\YUV),从surface当中取到
mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
createVideoFormat参数:
MediaFormat.MIMETYPE_VIDEO_AVC:AVC高级编码,要将视频编码为h.264
mWidth:视频的宽
mHeight:视频的高
将MediaFormat该对象配置到编码器内部:
mMediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
最后一个参数
MediaCodec.CONFIGURE_FLAG_ENCODE
代表需要配置一个编码器,而非解码器(如果是解码器则传递为0)。
调用start方法,代表开启该编码器。
mMediaCodec.start();
至此,编码器已经完全配置好了,打开编码器,可以从MediaCodec实例中取出两个buffer,一个是inputBuffer,用于存放输入的PCM数据(类似于FFmpeg编码的AVFrame);另外一个是outputBuffer,用于存放编码之后的原始AAC的数据(类似于FFmpeg编码的AVPacket),代码如下:
ByteBuffer[] inputBuffers = mediaCodec.getInputBuffers();
ByteBuffer[] outputBuffers = mediaCodec.getOutputBuffers();
到此,初始化方法已实现完毕,下面来看一下编码方法,MediaCodec的工作原理如图所示,左边的Client元素代表要将PCM放到inputBuffer中的某个具体的buffer中去;右边的Client元素代表要将编码之后的原始AAC数据从outputBuffer中的某个具体的buffer中取出来,左边的小方块代表各个inputBuffer元素,右边的小方块则代表各个outputBuffer元素。
除了直接操作输入缓冲区之外,还有另一种方式来告知MediaCodec需要编码的数据,那就是通过createInputSurface这个方法:
Surface mInputSurface = mMediaCodec.createInputSurface();
使用此接口创建一个Surface,然后我们在这个Surface中"作画",MediaCodec就能够自动的编码Surface中的“画作”,我们只需要从输出缓冲区取出编码完成之后的数据即可。
最后来看一下销毁方法,使用完了MediaCodec编码器之后,就需要停止运行并释放编码器,代码如下:
mMediaCodec.stop();
mMediaCodec.release();
mMediaCodec = null;
mInputSurface = null;