关于 Android 的音视频,也可以叫做多媒体,分成图像、声音和视频。我们先从最基本的图像入手,图像分成 2D 和 3D,Android 自身也提供了很多 API 来实现图像的功能。对于 Android 的图像内存优化,可以看我之前的这篇文章:Android应用篇 - 最全图片相关的优化。
YUV 简介
1. YUV 简介
YUV 是一种颜色编码方法,常使用在各个视频处理组件中。 YUV 在对照片或视频编码时,考虑到人类的感知能力,允许降低色度的带宽。
YUV 是编译 true-color 颜色空间 (colorspace) 的种类,Y'UV, YUV, YCbCr, YPbPr 等专有名词都可以称为 YUV,彼此有重叠。"Y" 表示明亮度 (Luminance、Luma),"U" 和 "V" 则是色度、浓度 (Chrominance、Chroma)。
彩色图像记录的格式,常见的有 RGB、YUV、CMYK 等。彩色电视最早的构想是使用 RGB 三原色来同时传输,这种设计方式是原来黑白带宽的 3 倍,在当时并不是很好的设计。RGB 诉求于人眼对色彩的感应,YUV 则着重于视觉对于亮度的敏感程度,Y 代表的是亮度,UV 代表的是彩度 (因此黑白电影可省略 UV,相近于 RGB),分别用 Cr 和 Cb 来表示,因此 YUV 的记录通常以Y:UV 的格式呈现。
2. 常用的 YUV 格式
为节省带宽起见,大多数 YUV 格式平均使用的每像素位数都少于 24 位。主要的抽样 (subsample) 格式有 YCbCr4:2:0、YCbCr4:2:2、YCbCr4:1:1 和 YCbCr4:4:4。YUV 的表示法称为 A:B:C 表示法:
- 4:4:4 表示完全取样。
- 4:2:2 表示 2:1 的水平取样,垂直完全采样。
- 4:2:0 表示 2:1 的水平取样,垂直 2:1 采样。
- 4:1:1 表示 4:1 的水平取样,垂直完全采样。
最常用 Y:UV 记录的比重通常 1:1 或 2:1,DVD-Video 是以 YUV4:2:0 的方式记录,也就是我们俗称的 I420,YUV4:2:0 并不是说只有 U (即 Cb) , V(即 Cr) 一定为 0,而是指 U:V 互相援引,时见时隐,也就是说对于每一个行,只有一个 U 或者 V 分量,如果一行是 4:2:0 的话,下一行就是 4:0:2,再下一行是 4:2:0...以此类推。
至于其他常见的 YUV 格式有 YUY2、YUYV、YVYU、UYVY、AYUV、Y41P、Y411、Y211、IF09、IYUV、YV12、YVU9、YUV411、YUV420 等。
3. YUV 操作
比如在做直播或者美颜相机的时候,因为需要添加美白,滤镜,AR 贴图等效果。所以不能简单的使用 SufaceView 加 Camera 的方式进行数据的采集,而是需要对 Camera 采集到的 YUV 数据进行相关的处理之后然后再进行推流的操作,YUV 数据的返回接口。
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
}
Android 摄像头采集的数据都是有一定的旋转的。一般前置摄像头有 270 度的旋转,后置摄像头有 90 的旋转。所以要对 YUV 数据进行一定旋转操作,同时对于前置摄像头的数据还要进行镜像翻转的操作。网上一般比较多的算法是关于旋转的:
private byte[] rotateYUVDegree90(byte[] data, int imageWidth, int imageHeight) {
byte[] yuv = new byte[imageWidth * imageHeight * 3 / 2];
// Rotate the Y luma
int i = 0;
for (int x = 0; x < imageWidth; x++) {
for (int y = imageHeight - 1; y >= 0; y--) {
yuv[i] = data[y * imageWidth + x];
i++;
}
}
// Rotate the U and V color components
i = imageWidth * imageHeight * 3 / 2 - 1;
for (int x = imageWidth - 1; x > 0; x = x - 2) {
for (int y = 0; y < imageHeight / 2; y++) {
yuv[i] = data[(imageWidth * imageHeight) + (y * imageWidth) + x];
i--;
yuv[i] = data[(imageWidth * imageHeight) + (y * imageWidth) + (x - 1)];
i--;
}
}
return yuv;
}
private byte[] rotateYUVDegree270(byte[] data, int imageWidth, int imageHeight) {
byte[] yuv = new byte[imageWidth * imageHeight * 3 / 2];
// Rotate the Y luma
int i = 0;
for (int x = imageWidth - 1; x >= 0; x--) {
for (int y = 0; y < imageHeight; y++) {
yuv[i] = data[y * imageWidth + x];
i++;
}
}// Rotate the U and V color compon