YUV笔记
- 做过视频编解码的对YUV肯定比较熟悉,而YUV有多种采样,如下图,在我们实际开发当中遇到最多的就是YUV420
YUV420P
- yuv420就是每4个y共享一组uv,也就是说每个像素点都有一个y与之对应,每4个像素点共用同一组uv,了解了这个就可以很容易计算出图像的yuv的大小为 size(yuv)=width*height*3/2,就是宽高大小的1.5倍
- 对图像的处理一般都是以行为单位,对y分量来说,行数等于高度(height),列数等于宽度(不存在内存对齐情况下)
- 对uv分量来说,行数等于高度的一半,列数等于宽度的一半(这个很好理解吧,由于4个y共享一组uv,因此相当于宽度和高度都减半了)
- 如果没有对齐的问题,那么存放yuv的大小就是上面的公式了 size(yuv)=width*height*3/2,但我们在开发时有可能遇到实际长度是大于size(yuv)=width*height*3/2这个公式的,为什么会这样呢
- 其实原因已经说了,内存对齐的问题,先看看下面的分析
- 假设图像分辨率为width*height=4*4(先不考虑对齐的情况),那么yuv在内存中就是这样存的
连续内存存储:
data[0]: yyyy yyyy yyyy yyyy uuuu vvvv
格式化一下:
data[0]: y y y y
y y y y
y y y y
y y y y
data[1]: u u
u u
data[2]: v v
v v
-
此时长度刚好等于size(yuv)=width*height*3/2=24,我们也可以很容易计算出
- data[1]=data[0]+width*height=data[0]+16,
- data[2]=data[1]+width/2*height/2=data[1]+width*height/4=data[1]+4
-
知道首地址和长度其实yuv各个分量已经能分隔开了
-
然后假设分辨率变为width*height=6*4,并假设内存是以4来对齐的,那么我们看看这次yuv数据在内存中会如何存储
连续内存存储:
data[0]: yyyyyy00 yyyyyy00 yyyyyy00 yyyyyy00 uuu0 uuu0 vvv0 vvv0
格式化一下:
data[0]: y y y y y y 0 0
y y y y y y 0 0
y y y y y y 0 0
y y y y y y 0 0
data[1]: u u u 0
u u u 0
data[2]: v v v 0
v v v 0
剔除对齐产生的多余空间后:
yyyyyy yyyyyy yyyyyy yyyyyy uuu uuu vvv vvv
- 我们看到后面会多出一些0(不一定是0,反正就是多出了一些字节,这些字节存是什么我们其实并不关心;这里为了方便说明,用0来表示),这些0是为了对齐而增加的,多了这些0虽然对cpu处理来说有性能的提升(可以减少数据读取的次数),但真实数据是没有这些0的,我们如果不管三七二十一,直接根据上面的公式去计算分离各分量数据,那么就会发生错误,从而产生花屏
- 于是就需要有一个变量来表示每行的行宽,这个变量在ffmpeg中就叫linesize
- 我们知道ffmpeg中对这帧数据的描述除了data外,还有linesize,这个data比较好理解,他是一个指针数组,data[0]存放的就是y分量数据的首地址,data[1]存放的就是u分量数据的首地址,data[2]存放的就是v分量数据的首地址
- 而linesize也是一个数组,linesize[0]就表示y分量每一行的长度,在我们的例子中就是y分量有4行(跟宽度相等),每行的长度为linesize[0];u分量有2行(是宽度的一半),每行的长度为linesize[1];v分量有2行(是宽度的一半),每行的长度为linesize[2];在yuv420中linesize[1]和linesize[2]是相等的
- 到这里也可以看出,在yuv420中,u与v的计算结果是一致的,因此在实际当中只要计算u的相关数值后v与u一致即可
- 从上图我们可以知道linesize[0]=8,linesize[1]=linesize[2]=4,有了这个linesize,我们就可以计算出任意一行的首地址,比如y分量第三行的首地址为(data[0]+linesize[0]*2),u分量第二行首地址为(data[1]+linesize[1]*1),知道首地址后就可以根据宽度获得对应的数据,从而把后面多余的0剔除,最后得到有效的数据
- 最后总结一下,在yuv420p中,假如图像分辨率为w*h,那么y分量有h行linesize[0]列,u和v分量一样,都是h/2行linesize[1]列,其中linesize[0]大于等于w,linesize[1]大于等于w/2
- 了解这些东西对于了解ffmpeg的相关api设计和参数有很大帮助作用
- 看到这里如果你也在使用ffmpeg,那么也可以看看这篇文章:FFmpeg简单分析系列----内存对齐简要说明_huweijian5的专栏-CSDN博客