Android音视频---YUV格式深入浅出(一)

版权声明:本文为卫伟学习总结文章,转载请注明出处!
近期由于项目需要,开始接触视频像素格式,因此在这里做一个小结;
像素格式描述了像素数据存储所用的格式,定义了像素在内存中编码方式。RGB和YUV为两种经常使用的像素格式。

1.RGB格式

一般较为熟悉,RGB图像具有三个通道R、G、B,分别对应红、绿、蓝三个分量,由三个分量的值决定颜色;通常,会给RGB图像加一个通道alpha,即透明度,于是共有四个分量共同控制颜色。

2.YUV格式

YUV是一种颜色编码格式,可以说YUV流媒体是原始数据,大部分的视频领域都在使用。它与RGB类似,但RGB更多的用于渲染时,而YUV则用于数据传输,因为它占用更少的频宽。当然,实时通讯为了降低带宽都会采用H264/H265编码。从字面理解,YUV的含义:Y代表亮点信息(灰度),UV分别代表色彩信息。
YUV像素格式来源与RGB像素格式,通过公司运算,YUV三分量可以还原RGB,YUV转RGB的公式如下:

 R = Y + 1.403V
 G = Y - 0.344U - 0.714V
 B = Y + 1.770U

一般,将RGB和YUV的范围均限制在[0,255]间,则有如下转换公式:

 R = Y + 1.403(V - 128)
 G = Y - 0.344(U - 128) - 0.714(V - 128)
 B = Y + 1.770(U - 128)
2.1.YUV采样

YUV相比于RGB格式最大的好处是可以做到保持图像质量降低不明显的前提下,减小文件大小。YUV格式之所以能够做到,是因为进行了采样操作。
YUV码流的存储格式与其采样方式密切相关,主流的采样方式有三种:YUV 4:4:4(YUV444),YUV 4:2:2(YUV422),YUV 4:2:0(YUV420)
若以以黑点表示采样该像素点的Y分量,以空心圆圈表示采用该像素点的UV分量,则这三种采样方式如下:

Android音视频---YUV格式深入浅出(一)_第1张图片
即:

  • YUV 4:4:4采样,每一个Y对应一组UV
  • YUV 4:2:2采样,每两个Y共用一组UV分量
  • YUV 4:2:0采样,每四个Y公用一组UV分量
2.2.YUV存储格式

YUV存储可以分为两种:packd(打包) 和 planar(平面);

  • packed: Y、U、V分量穿插着排列,三个分量存在一个Byte型数组里;
  • planar: Y、U、V分量分别存在三个Byte型数组中;

常见的像素格式

1.YUV422:YUYV、YVYU、UYVY、V

这四种格式每一种又可以分为2类(packed和planar),以YUYV为例,一个6*4的图像的存储方式如下:
Y Y Y Y Y Y                   
Y Y Y Y Y Y                  
Y Y Y Y Y Y                   
Y Y Y Y Y Y                    
U U U U U U                  Y U Y V Y U Y V Y U Y V
U U U U U U                  Y U Y V Y U Y V Y U Y V
V V V V V V                  Y U Y V Y U Y V Y U Y V
V V V V V V                  Y U Y V Y U Y V Y U Y V
- Planar -                          - Packed - 

2.YUV420

YUV420p: I420、YV12
YUV420sp: NV12、NV21
同样,对于一个6*4的图像,这四种像素格式的存储方式如下:
Y Y Y Y Y Y      Y Y Y Y Y Y      Y Y Y Y Y Y      Y Y Y Y Y Y
Y Y Y Y Y Y      Y Y Y Y Y Y      Y Y Y Y Y Y      Y Y Y Y Y Y
Y Y Y Y Y Y      Y Y Y Y Y Y      Y Y Y Y Y Y      Y Y Y Y Y Y
Y Y Y Y Y Y      Y Y Y Y Y Y      Y Y Y Y Y Y      Y Y Y Y Y Y
U U U U U U      V V V V V V      U V U V U V      V U V U V U
V V V V V V      U U U U U U      U V U V U V      V U V U V U
 - I420 -          - YV12 -         - NV12 -         - NV21 -

注:

  • I420、YV12三个分量均为平面格式,即分别存在三个Byte型数组中;
  • NV12、NV21的存储格式为Y平面,UV打包,即Y信息存储在一个数组中,UV信息存储在一个矩阵中。

3.Android I420转NV21,NV21转I420

/**
 * Nv21:
 * YYYYYYYY
 * YYYYYYYY
 * YYYYYYYY
 * YYYYYYYY`------`     ````
 * VUVU
 * VUVU
 * VUVU
 * VUVU
 * 

* * YUV420P * YYYYYYYY * YYYYYYYY * YYYYYYYY * YYYYYYYY`------` ```` * UVUV * UVUV * UVUV * UVUV *

* * I420: * YYYYYYYY * YYYYYYYY * YYYYYYYY * YYYYYYYY * UUUU * UUUU * VVVV * VVVV */ public class I420NV21Test { public static byte[] I420ToNV21(byte[] data, int width, int height) { byte[] ret = new byte[data.length]; int total = width * height; ByteBuffer bufferY = ByteBuffer.wrap(ret, 0, total); ByteBuffer bufferV = ByteBuffer.wrap(ret, total ,total/4); ByteBuffer bufferU = ByteBuffer.wrap(ret, total + total / 4, total /4); bufferY.put(data, 0, total); for(int i = 0; i < total /4; i+=1) { bufferV.put(data[total+i]); bufferU.put(data[i+total+total/4]); } return ret; } /** * nv21转I420 * @param data * @param width * @param height * @return */ public static byte[] nv21ToI420(byte[] data, int width, int height) { byte[] ret = new byte[data.length]; int total = width * height; ByteBuffer bufferY = ByteBuffer.wrap(ret, 0, total); ByteBuffer bufferU = ByteBuffer.wrap(ret, total, total / 4); ByteBuffer bufferV = ByteBuffer.wrap(ret, total + total / 4, total / 4); bufferY.put(data, 0, total); for (int i=total; i>2) + i] = data[size + (i<<1)]; //V } return dstData; } /** * 将Nv21数据转换为Yuv420SP数据 * @param data Nv21数据 * @param dstData Yuv420sp数据 * @param w 宽度 * @param h 高度 */ public static byte[] Nv21ToYuv420SP(byte[] data, int w, int h) { byte[] dstData = new byte[data.length]; 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 } return dstData; } public static void main(String[] args) { //模拟nv21 数据 [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2] //模拟I420 数据 [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3] byte[] data=new byte[30]; for(int i=0;i<20;i++){ data[i]=1; } for(int i=0;i<10;i++){ if(i%2==0){ data[i+20]=3; }else{ data[i+20]=2; } } System.out.println("NV21 数据 --> "+Arrays.toString(data)); byte[] i420data=nv21ToI420(data,5,4); System.out.println("I420 数据 --> "+Arrays.toString(i420data)); byte[] nv21data=I420ToNV21(data,5,4); System.out.println("NV21 数据 --> "+Arrays.toString(nv21data)); System.out.println("\n"); byte[] i420data1=Nv21ToI420(data,5,4); System.out.println("方式二 I420 数据 --> "+Arrays.toString(i420data1)); byte[] yuv420sp = Nv21ToYuv420SP(data,5,4); System.out.println("YUV420SP 数据 --> "+Arrays.toString(yuv420sp)); }

一张图片的大小为widthheight, YUV420图片数据的大小就是widthheight3/2
NV21数据格式
width
height个字节存的是每个像素的Y分量,后面的widthheight/2字节是VUVUVUVU…
如: YYYY…YYVUVUVUVU…
I420 是数据格式:
width
height个字节还是每个像素的Y分量,接下来的widthheight/4字节是U分量,最后的widthheight/4字节是V分量
如: YYYY…YYUUUUVVVV…
模拟数据测试结果如下:
其中: 1表示是Y, 2表示是U ,3表示是V

Android音视频---YUV格式深入浅出(一)_第2张图片

你可能感兴趣的:(Android音视频---YUV格式深入浅出(一))