###1. YUV
YUV是编译true-color颜色空间(color space)的种类,Y’UV, YUV, YCbCr,YPbPr等专有名词都可以称为YUV,彼此有重叠。“Y”表示明亮度(Luminance、Luma),“U”和“V”则是色度、浓度(Chrominance、Chroma)
YUV 格式有两个格式:平面格式(planar formats) 和 紧缩格式(packed formats)。
- planar 格式,先连续存储所有像素点的 Y,接着存储像素点 U,最后存储像素点 V。
- packed 格式,每个像素点的 Y,U,V 是连续交叉存储的。
YUV码流的存储格式其实与其采样的方式密切相关,主流的采样方式有三种,YUV4:4:4,YUV4:2:2,YUV4:2:0;他们之间的区别:
- YUV 4:4:4采样,每一个Y对应一组UV分量。
- YUV 4:2:2采样,每两个Y对应一组UV分量。
- YUV 4:2:0采样,每四个Y对应一组UV分量。
###2. YUV420 存储方式
- YU12和YV12属于YUV420格式,也是一种Plane模式,将Y、U、V分量分别打包,依次存储。其每一个像素点的YUV数据提取遵循YUV420格式的提取方式,即4个Y分量共用一组UV。
- NV12和NV21属于YUV420格式,是一种two-plane模式,即Y和UV分为两个Plane,但是UV(CbCr)为交错存储,而不是分为三个plane。
假设一个分辨率为8X4的YUV图像,它们的格式如下图:
YUV420SP 格式
YUV420P 格式
YUV420P,Y,U,V三个分量都是平面格式,分为I420和YV12。I420格式和YV12格式的不同处在U平面和V平面的位置不同。在I420格式中,U平面紧跟在Y平面之后,然后才是V平面(即:YUV);但YV12则是相反(即:YVU)。
YUV420SP, Y分量平面格式,UV打包格式, 即NV12。 NV12与NV21类似,U 和 V 交错排列,不同在于UV顺序。
- I420: YYYYYYYY UU VV =>YUV420P
- YV12: YYYYYYYY VV UU =>YUV420P
- NV12: YYYYYYYY UVUV =>YUV420SP
- NV21: YYYYYYYY VUVU =>YUV420SP
###3.转换(Java层)
####3.1 YV12toNV21
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public void YV12toNV21(final byte[] input, final byte[] output, final int width, final int height) { final int size = width * height; final int quarter = size / 4; final int vPosition = size; // This is where V starts final int uPosition = size + quarter; // This is where U starts System.arraycopy(input, 0, output, 0, size); // Y is same for (int i = 0; i < quarter; i++) { output[size + i*2 ] = input[vPosition + i]; // For NV21, V first output[size + i*2 + 1] = input[uPosition + i]; // For Nv21, U second } }
|
###3.2 YV12toI420
1 2 3 4 5
| public void YV12toI420(byte[] yv12bytes, byte[] i420bytes, int width, int height) { System.arraycopy(yv12bytes, 0, i420bytes, 0, width * height); System.arraycopy(yv12bytes, width * height + width * height / 4, i420bytes, width * height, width * height / 4); System.arraycopy(yv12bytes, width * height, i420bytes, width * height + width * height / 4, width * height / 4); }
|
###3.3 YV12toNV12
1 2 3 4 5 6 7 8 9
| void YV12toNV12(byte[] yv12bytes, byte[] nv12bytes,int width,int height){ int nLenY = width * height; int nLenU = nLenY /4; System.arraycopy(yv12bytes,0, nv12bytes,0, width *height); for(inti = 0; i < nLenU; i++) { nv12bytes[nLenY +2* i + 1] = yv12bytes[nLenY + i]; nv12bytes[nLenY +2* i] = yv12bytes[nLenY + nLenU + i]; } }
|
###3.4 NV21toI420
1 2 3 4 5 6 7 8 9 10
| public void Nv21ToI420(byte[] data, byte[] dstData, int w, int h) { int size = w * h; // Y System.arraycopy(data, 0, dstData, 0, size); for (int i = 0; i < size / 4; i++) { dstData[size + i] = data[size + i * 2 + 1]; //U dstData[size + size / 4 + i] = data[size + i * 2]; //V } }
|
###3.5 NV21toNV12
1 2 3 4 5 6 7 8 9 10
| public void NV21toNV12(byte[] data, byte[] dstData, int w, int h) { int size = w * h; // Y System.arraycopy(data, 0, dstData, 0, size); for (int i = 0; i < size / 4; i++) { dstData[size + i * 2] = data[size + i * 2 + 1]; //U dstData[size + i * 2 + 1] = data[size + i * 2]; //V } }
|
由于在 Java 层进行转换效率较低且耗费内存,可以将转换放在 jni 层进行。