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博客