以下为音视频基础数据的图像化展示,方便大家理解
RGB24
RGB交替排列,RGBRGBRGB
占用空间Width*Height*3
YUV420P
YU12(I420)
每4个Y分量,共一个UV分量
Y是连续的,U也是连续的,V也是连续的
占用空间 Width*Height + Width*Height/4 + Width*Height/4
每4个Y分量,共一个UV分量
Y是连续的,U也是连续的,V也是连续的,只是V在U前面
占用空间 Width*Height + Width*Height/4 + Width*Height/4
YUV420SP
NV12
每4个Y分量,共一个UV分量
Y是连续的,UV是交替存放的,先U后V
占用空间 Width*Height + Width*Height/4 + Width*Height/4
NV21
每4个Y分量,共一个UV分量
Y是连续的,UV是交替存放的,先V后U
占用空间 Width*Height + Width*Height/4 + Width*Height/4
YUV422P
每两个Y分量,共一个UV分量
Y是连续的,U是连续的,V也是连续的
占用空间Width *Height + Width*Height/2 +Width*Height/2
YUYV422
每两个Y分量,共一个UV分量
YUV是间隔排列,按照 YUYV依次排列
占用空间Width *Height + Width*Height/2 +Width*Height/2
YUV444P
每一个Y分量对应一个U分量,对应一个V分量
占用空间Width *Height*3
结论:1. YUV各种格式体现的是其Y:U:V的排列方式和占比。
2.理解这些就很容易看懂,比如以下代码:
YUV422:
YUV是间隔排列,按照 YUYV依次排列
大小为 2 * w * h
I420:
Y是连续的,U也是连续的,V也是连续的
大小为 1.5 * w * h
1.Y分量0,2,4,6,8 这样隔一个取,I420连续存放
2.U分量1,5,9,13隔4个取一个
3.V分量3,7,13,17隔4个取一个
解析: yuv422[i * 2 * width + 4 * j + 1] 即 每行坐标 = 每行起始位置(i * 2 * width)+ 每行偏移(4 * j + 1)
解析: yuv420[ynum + k * 2 * width / 4 + j] 即 每行坐标 =Y偏移(ynum)+ 行高(K)* 行宽(2 * width) /4 + 偏移(j)
为何除以4?
*2 /4
0,2,4,6,8 ==》0,4,8,12,16==》0,1,2,3,4
即yuv420得到的是连续的角标
当然我们用其他的方式遍历,搞懂原理,大家各显神通
int YUV422To420(unsigned char yuv422[], unsigned char yuv420[], int width, int height)
{
int ynum = width * height;
int i, j, k = 0;
//得到Y分量
for (i = 0; i < ynum; i++) {
yuv420[i] = yuv422[i * 2];
}
//得到U分量
for (i = 0; i < height; i++) {
if ((i % 2) != 0)continue;
for (j = 0; j < (width / 2); j++) {
if ((4 * j + 1) > (2 * width))break;
yuv420[ynum + k * 2 * width / 4 + j] = yuv422[i * 2 * width + 4 * j + 1];
}
k++;
}
k = 0;
//得到V分量
for (i = 0; i < height; i++) {
if ((i % 2) == 0)continue;
for (j = 0; j < (width / 2); j++) {
if ((4 * j + 3) > (2 * width))break;
yuv420[ynum + ynum / 4 + k * 2 * width / 4 + j] = yuv422[i * 2 * width + 4 * j + 3];
}
k++;
}
return 1;
}
了解基本的YUV基础后,推荐一款libyuv的工具库,作为参考,方便我们后期功能的实现
GitHub - Yangandmore/libyuv_original: 补充libyuv功能的库,使libyuv更加完善
以下为其部分API