ffmpeg之內存對齊簡要說明

ffmpeg之內存對齊簡要說明

  • 在ffmpeg的使用過程中有時會發現align這個參數,那麼這個參數代表什麼意思,不同的值會產生什麼影響呢

行字節數的計算

  • 理解內存對齊之前首先要理解行的概念,視頻有寬和高兩個概念,這裏的寬通常就是指行,但他們的大小並不是一一對應的

  • 例如720P(1280*720)的寬爲1280,那麼它的行是多少呢,如果有人直接告訴你是1280,那麼這個人可能也沒太明白行的概念,因爲他漏了一個很重要的前提條件,那就是對齊數align是多少,如果align是1,那麼行確實是1280,如果align是2,那麼行也是1280,如果align是4,那麼行還是1280

  • 到這裏可能很多人會倉促得出結論,行和寬是相等的,但實際上只是因爲1280這個數字比較特殊,它剛好是2的整數倍,也是4的整數倍,類似這樣的分辨率還有1080P(1920*1080),4K(4096*2160)等,標準的分辨率基本是對齊的,因此處理視頻一定要留意那些非標準分辨率的

  • 那麼當我們知道寬高後,怎麼計算行數呢?其實有一個公式的,如下

    r = ceil( w*1.0 / a ) * a

  • 其中,r是行數,w是寬,a是對齊數,ceil這個函數表示向上取整,即4.1我們會取值5

  • 其實上面的說法還是不嚴謹,我們都知道一個像素點有rgb三個通道,每個通道佔1字節的話,那麼一個像素點就會佔3個字節,那麼1280*720在實際存儲時每一行的字節數就不是1280了,而是1280*3=3840,那麼計算行數時就會變成

    r = ceil( w*3*1.0 / a ) * a

  • 因此對齊是根據實際存儲字節大小來計算的,如果存儲一個像素點不是佔3字節,那麼我們先要計算出這一行實際佔用的字節數,再根據計算出的字節數來計算對齊

  • 例如3*10的圖片,每個像素點佔2個字節,對齊數align爲4,那麼行數是多少呢,從條件可知,每行3個像素點,每個像素點佔2字節,那麼每行就是6字節,而對齊數是4,6不是4的整數倍,因此6需要補2個字節湊成8,8就是4的整數倍了,那麼我們就知道每行在內存中實際佔用了8個字節,後兩個字節是爲了對齊補上的,總共有10行,那麼這張圖片在實際內存中就佔用了8*10=80個字節,而不是60個字節了

  • 本來這張圖片只需要60個字節,爲何要用80個字節來存儲呢,這是因爲cpu並不能從任意地址開始讀取數據,如果不對齊,那麼可能需要多次讀取才能讀到完整數據,因此對齊主要是爲了提升性能,典型的空間換取時間

ffmpeg中的align

  • 有了上面的基礎後,現在你應該能理解ffmpeg中align參數了,一般我們設爲1,那就是按實際的大小進行存儲,不會對齊

  • ffmpeg之所以給了這個參數讓人設置,應該就是爲了兼容各個硬件平臺,因爲不是所有的硬件平臺都能訪問任意地址上的任意數據的,某些硬件平臺只能在某些地址處取某些特定類型的數據,否則拋出硬件異常

  • 以ffmpeg的av_image_get_buffer_size爲例,你能準確說出下面的結果嗎,如果可以,那麼證明你確實理解了ffmpeg中的對齊了

    值得注意的是yuv的計算,以w*h的yuv420p爲例,他是分三個平面存儲三個分量的,而u和v的計算是一致的,也就是說計算出了u即可得到v;對於y來說,它有w行h列,因此需要計算w行的對齊後字節數再乘以h;對於u來說,它有w/2行h/2列(這是因爲每4個y共享一組uv),因此需要計算w/2行的對齊後字節數再乘以h/2;v的計算與u的一模一樣,最後將這個三個數相加即可

int res = 0;
	res=av_image_get_buffer_size(AV_PIX_FMT_RGB24,1920,1080,1);
	log(AV_LOG_INFO, "av_image_get_buffer_size %d.", res);//3*1920*1080=6220800
	res=av_image_get_buffer_size(AV_PIX_FMT_RGB24, 1920, 1080, 2);
	log(AV_LOG_INFO, "av_image_get_buffer_size %d.", res);//3*1920*1080=6220800,這裏由於3*1920剛好是2的整數倍,因此不會產生多餘的對齊空間
	res = av_image_get_buffer_size(AV_PIX_FMT_RGB24, 1920, 1080, 4);
	log(AV_LOG_INFO, "av_image_get_buffer_size %d.", res);//3*1920*1080=6220800,這裏由於3*1920剛好是4的整數倍,因此不會產生多餘的對齊空間

	res = av_image_get_buffer_size(AV_PIX_FMT_RGB24, 1280, 720, 1);
	log(AV_LOG_INFO, "av_image_get_buffer_size %d.", res);//3*1280*720=2764800
	res = av_image_get_buffer_size(AV_PIX_FMT_RGB24, 1280, 720, 2);
	log(AV_LOG_INFO, "av_image_get_buffer_size %d.", res);//3*1280*720=2764800,這裏由於3*1280剛好是2的整數倍,因此不會產生多餘的對齊空間
	res = av_image_get_buffer_size(AV_PIX_FMT_RGB24, 1280, 720, 4);
	log(AV_LOG_INFO, "av_image_get_buffer_size %d.", res);//3*1280*720=2764800,這裏由於3*1280剛好是4的整數倍,因此不會產生多餘的對齊空間

	res = av_image_get_buffer_size(AV_PIX_FMT_RGB24, 6, 10, 1);
	log(AV_LOG_INFO, "av_image_get_buffer_size %d.", res);//3*6*10=180
	res = av_image_get_buffer_size(AV_PIX_FMT_RGB24, 6, 10, 2);
	log(AV_LOG_INFO, "av_image_get_buffer_size %d.", res);//3*6*10=180,這裏由於3*6剛好是2的整數倍,因此不會產生多餘的對齊空間
	res = av_image_get_buffer_size(AV_PIX_FMT_RGB24, 6, 10, 4);
	log(AV_LOG_INFO, "av_image_get_buffer_size %d.", res);//ceil(3*6/4.0)*4.0*10=200,這裏由於3*6不是4的整數倍,向上取整ceil(3*6/4.0)*4=20,因此每行產生了2個字節的對齊

	res = av_image_get_buffer_size(AV_PIX_FMT_RGB24, 5, 10, 1);
	log(AV_LOG_INFO, "av_image_get_buffer_size %d.", res);//3*5*10=150   
	res = av_image_get_buffer_size(AV_PIX_FMT_RGB24, 5, 10, 2);
	log(AV_LOG_INFO, "av_image_get_buffer_size %d.", res);//ceil(3*5/2.0)*2*10=160,這裏由於3*5不是2的整數倍,向上取整ceil(3*5/2.0)*2=16,因此每行產生了1個字節的對齊
	res = av_image_get_buffer_size(AV_PIX_FMT_RGB24, 5, 10, 4);
	log(AV_LOG_INFO, "av_image_get_buffer_size %d.", res);//ceil(3*5/4.0)*4*10=160,這裏由於3*5不是4的整數倍,向上取整ceil(3*5/4.0)*4=16,因此每行產生了1個字節的對齊

	res = av_image_get_buffer_size(AV_PIX_FMT_YUV420P, 8, 8, 1);
	log(AV_LOG_INFO, "av_image_get_buffer_size %d.", res);//8*8+4*4*2=96  
	res = av_image_get_buffer_size(AV_PIX_FMT_YUV420P, 8, 8, 2);
	log(AV_LOG_INFO, "av_image_get_buffer_size %d.", res);//8*8+4*4*2=96
	res = av_image_get_buffer_size(AV_PIX_FMT_YUV420P, 8, 8, 4);
	log(AV_LOG_INFO, "av_image_get_buffer_size %d.", res);//8*8+4*4*2=96

	res = av_image_get_buffer_size(AV_PIX_FMT_YUV420P, 6, 8, 1);
	log(AV_LOG_INFO, "av_image_get_buffer_size %d.", res);//6*8+3*4*2=72  
	res = av_image_get_buffer_size(AV_PIX_FMT_YUV420P, 6, 8, 2);
	log(AV_LOG_INFO, "av_image_get_buffer_size %d.", res);//6*8+4*4*2=80,這裏對於y來說,6剛好是2的整數倍,因此y剛好對齊;而對於u來說,每行只有3個u,爲了對齊必須補一個字節,變成4;v與u是一樣的
	res = av_image_get_buffer_size(AV_PIX_FMT_YUV420P, 6, 8, 4);
	log(AV_LOG_INFO, "av_image_get_buffer_size %d.", res);//8*8+4*4*2=96,這裏對於y來說,6不是4的整數倍,因此y必須補2個字節湊成8後纔對齊;而對於u來說,每行只有3個u,爲了對齊必須補一個字節,變成4;v與u是一樣的

ffmpeg的linesize

  • linesize其實就是我們上文提及到的行字節數,在我們解碼出數據後,經常會遇到這個linesize,既然我們知道了align的概念,就該明白這個linesize就是爲了讓你取出真實的數據的
  • 解碼後的數據中可能是經過對齊的,既然有對齊,那就是數據里加多了一些爲了對齊而多餘的字節,如果我們想最後顯示視頻數據,那麼這些多餘的數據勢必要進行剔除掉,那麼怎麼剔除呢,linesize就是來幫你幹這事的,有了它,你就可以一行一行比較,然後把每行最後爲了對齊而補的字節刪除,還原出視頻的真實數據

參考

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章