Android音视频开发(二)——Android编码实践

一、Android Camera API

1.1 图像采集

  • 构建预览布局——使用SurfaceView或者TextureView
  • 打开相机 ——Camera.open
  • 设置参数——Camera.Parameters
  • 设置预览数据回调——PreviewCallback
  • 设置预览画布并启动——setPreviewTexture/startPreview
  • 释放相机——stopPreview/release

二、MediaCodec 实践

2.1 MediaCodec (虽然常说是硬编码,其实也可以软编码)

        MediaCodec类可用于访问Android底层的多媒体编解码器,例如,编码器/解码器组件。它是Android底层多媒体支持基础架构的一部分。

        Android底层多媒体模块采用的是OpenMax框架,任何Android底层编解码模块的实现,都必须遵循OpenMax标准。Google官方默认提供了一系列的软件编解码器,而硬件编解码功能,则需要由芯片厂商依照OpenMax框架标准来完成,所以,一般采用不同芯片型号的手机,硬件编解码的实现和性能是不同的。

优点 缺点
软解码

1.兼容性强,对系统版本要求低,出错情况少;

2.解码方面,软解码的色彩一般比硬解码的柔和;

3.编码的可操作空间比较大,自由度高

1. CPU消耗较大;

2.机器容易发热;

3.功耗较高

硬解码 功耗低、执行效率低

1.不同型号的芯片对编解码的实现不同,并不能保证编解码效果与其他机型一样或不出错;

2.可控性差,依赖底层编解码实现

2.2  MediaCodec 流程

  1. 配置编码参数(MediaFormat.setInteger()):码率:数据传输单位时间传送的数据位数,1kbps即每秒有1000位数据;帧率:图像每秒的个数;关键帧间隔:两个I之间的间隔;色彩空间:yuv和rgb,等等。
  2. 创建编码器(MediaCodec)
  3. 创建混合器(MediaMuxer):把声音与图像压缩数据放入MP4盒子
  4. 开始编码

MediaCodec详细的流程:

1.Client从Codec拿到input缓冲区队列[getInputBuffers]
2.Client从input队列中申请一个empty buffer[dequeueInputBuffer]
3.Client将需要编解码的数据拷贝到empty buffer,然后将其放入input队列[queueInputBuffer]
4.Codec从input缓冲区取出一个buffer(一帧)数据,对其进行编解码处理
5.编解码处理结束后,Codec将原始数据的buffer置为empty后放入input缓冲区队列,并将编解码后的数据放到output缓冲区队列

//Client和Codec模块是并行工作的

6.Client从Codec拿到output缓冲区队列[getOutputBuffers]
7.Client从output队列申请一个有编解码数据的buffer[dequeueOutputBuffer]
8.Client对编解码的数据进行渲染/播放
9.渲染/播放完成后,Client再将这个buffer放回output缓冲区队列[releaseOutputBuffer]

MediCodec在架构上采用的是一种基于环形缓冲区生产者-消费者设计模式

2.3  MediaCodec 编码

过程类似一个生产者-消费者模式。

        从input队列当中添加YUN格式数据,从output队列中获取H.264格式数据。(因为目前使用的是混合器MediaMuxer)

大致说一下编码的流程:

(1)input队列

  1. 使用MediaCodec.dequeueInputBuffer(timeoutUS);获取input队列的一个下标,然后通过MediaCodec.getInputBuffer(index)下标获取一个ByteBuffer的一个容器。
  2. 清空容器(clear),然后将byte[]数据put到容器当中。
  3. 当数据填充好后,将这个下标通知给MediaCodec加入队列。

(2)output队列(在一个循环中,方便写入数据就可以输出)

  1. 使用dequeueOutputBuffer获取输出缓冲区的一个int状态值,并且传入一个MediaCodec.BufferInfo(每缓冲器元数据包括指定相关编解码器缓冲器中有效数据范围的偏移量和大小。)和timeoutUS
  2. 用MediaMuxer.addTract(MediaFormat)配置编码参数,并返回一个int下标,并start混合器。
  3. 根据BufferInfo设置输出缓冲区从哪里开始读,长度,写出格式

返回的int状态值有必要好好说一下:

  • 假如input队列的数据没有加载好,就会返回一个MedioCodec.INFO_TRY_AGAIN_LATER,此时我们break就行了,稍后重试。
  • MedioCodec.INFO_OUTPUT_FORMAT_CHANGED,表示的是输出格式发生变化,也就是我们最开始配置的编码参数发生了变化。当我们第一次dequeueOutputBuffer,总会回调一次这个,所以在这里开启混合器(获取MedioCodec的MedioFormat编码参数,传给MediaMuxer)。

三、RGB与YUV

3.1 RGB(只能单转灰度)

        牛顿利用三棱镜将太阳光分解成彩色的光带。各色光因其所形成的折射角不同而彼此分离,就像彩虹一样,所以白光能分解成多种色彩的光。经过不断地实验发现,红绿蓝(RGB)三种色光无法被分解,所以称它们为三原色光,等量的三原色光相加会变成白光。

        在计算机里,R、G、B也被称为"基色分量”。它们的取值,分别从0到255,一共256个等级(256是2的8次方)。所以,任何颜色,都可以用R、G、B三个值的组合表示。

3.2 YUV(彩色与黑白互转):是一组格式的称呼

        YUV主要应用于优化彩色视频信号的传输,与RGB相比,YUV只需要占用极少的频宽(RGB需要三个独立的视频信号同时传输)。YUV中Y代表明亮度,也称灰阶值;U与V表示的则是色度(色调和饱和度)也可以记作:YCbCr如果只有Y数据,那么表示的图像就是黑白的。

        使用YUV格式才能极大地去除冗余信息,人眼对亮点信息更敏感,对色度敏感度不高。也就是说,可以压缩UV数据,而人眼难以发现。所以压缩算法的第一步,往往先把RGB数据转换成YUV数据对Y少压缩一点,对UV多压缩一点,以平衡图像效果和压缩率。这也是为什么编码选择使用YUV而不是RGB。

        如何将RGB转化为Gray(灰度):使用305911公式

Gray=R*0.30+G*0.59+B*0.11

1. RGB是可以转换成Gray的,但是不能根据Gray倒推RGB

2. 但是RGB和YUV是可以相互转换的,而且其中Y就表示的是Gray

3.3 NV21 和 I420

        YUV因为采样和数据排列方式的不同,又分为不同的存储格式。

        一般的Android摄像头输出为:NV21格式,I420格式则是绝大多数编解码器默认输入输出格式

        这两者主要的异同是:首先都是将Y数据先排完,这都是相同的;不同的是NV21是V、U一组一排,I420是先排U再排V。

猜你喜欢

转载自blog.csdn.net/indeedes/article/details/124755102