1. 程式人生 > >多個視訊畫面拼接技術

多個視訊畫面拼接技術

____YUV主要取樣格式理解

 

主要的取樣格式有YCbCr 4:2:0、YCbCr 4:2:2、YCbCr 4:1:1和 YCbCr 4:4:4。其中YCbCr 4:1:1 比較常用,其含義為:每個點儲存一個 8bit 的亮度值(也就是Y值), 每 2x2 個點儲存一個 Cr 和Cb 值, 影象在肉眼中的感覺不會起太大的變化。所以, 原來用 RGB(R,G,B 都是 8bit unsigned) 模型, 4 個點需要 8x3=24 bites(如下圖第一個圖). 而現在僅需要 8+(8/4)+(8/4)=12bites, 平均每個點佔12bites(如下圖第二個圖)。這樣就把影象的資料壓縮了一半。

 

上邊僅給出了理論上的示例,在實際資料儲存中是有可能是不同的,下面給出幾種具體的儲存形式:

 

(1) YUV 4:4:4

 

YUV三個通道的抽樣率相同,因此在生成的影象裡,每個象素的三個分量資訊完整(每個分量通常8位元),經過8位元量化之後,未經壓縮的每個畫素佔用3個位元組。

 

下面的四個畫素為: [Y0 U0 V0] [Y1 U1 V1] [Y2 U2 V2] [Y3 U3 V3]

 

存放的碼流為: Y0 U0 V0 Y1 U1 V1 Y2 U2 V2 Y3 U3 V3

 

(2) YUV 4:2:2

 

每個色差通道的抽樣率是亮度通道的一半,所以水平方向的色度抽樣率只是4:4:4的一半。對非壓縮的8位元量化的影象來說,每個由兩個水平方向相鄰的畫素組成的巨集畫素需要佔用4位元組記憶體(亮度2個位元組,兩個色度各1個位元組)。

 

下面的四個畫素為: [Y0 U0 V0] [Y1 U1 V1] [Y2 U2 V2] [Y3 U3 V3]

 

存放的碼流為: Y0 U0 Y1 V1 Y2 U2 Y3 V3

 

映射出畫素點為:[Y0 U0 V1] [Y1 U0 V1] [Y2 U2 V3] [Y3 U2 V3]

 

 

(3) YUV 4:1:1

 

4:1:1的色度抽樣,是在水平方向上對色度進行4:1抽樣。對於低端使用者和消費類產品這仍然是可以接受的。對非壓縮的8位元量化的視訊來說,每個由4個水平方向相鄰的畫素組成的巨集畫素需要佔用6位元組記憶體(亮度4個位元組,兩個色度各1個位元組)。

 

下面的四個畫素為: [Y0 U0 V0] [Y1 U1 V1] [Y2 U2 V2] [Y3 U3 V3]

 

存放的碼流為: Y0 U0 Y1 Y2 V2 Y3

 

映射出畫素點為:[Y0 U0 V2] [Y1 U0 V2] [Y2 U0 V2] [Y3 U0 V2]

 

(4)YUV4:2:0

 

4:2:0並不意味著只有Y,Cb而沒有Cr分量。它指得是對每行掃描線來說,只有一種色度分量以2:1的抽樣率儲存。相鄰的掃描行儲存不同的色度分量, 也就是說,如果一行是4:2:0的話,下一行就是4:0:2,再下一行是4:2:0...以此類推。對每個色度分量來說,水平方向和豎直方向的抽樣率都是 2:1,所以可以說色度的抽樣率是4:1。對非壓縮的8位元量化的視訊來說,每個由2x2個2行2列相鄰的畫素組成的巨集畫素需要佔用6位元組記憶體(亮度4個位元組,兩個色度各1個位元組)。

 

下面八個畫素為:[Y0 U0 V0] [Y1 U1 V1] [Y2 U2 V2] [Y3 U3 V3]

 

[Y5 U5 V5] [Y6 U6 V6] [Y7U7 V7] [Y8 U8 V8]

 

存放的碼流為:Y0 U0 Y1 Y2 U2 Y3

 

Y5 V5 Y6 Y7 V7 Y8

 

映射出的畫素點為:[Y0 U0 V5] [Y1 U0 V5] [Y2 U2 V7] [Y3 U2 V7]

 

[Y5 U0 V5] [Y6 U0 V5] [Y7U2 V7] [Y8 U2 V7]

 

 

YUV420拼接

在視訊會議/視訊監控/視訊排程等場景中,存在多分屏的應用, 典型如4分屏,/5+1/畫中畫等, 如下圖所示:

這就需要用到視訊拼接 需要將多路decode後的資料, 按每路的對應位置, 拼接到一路畫面中,再encoder送出去.

本段描述如何做視訊拼接.

YUV420在記憶體中的存放格式如下圖所示:

 

主要的拼接思路如下:

  1. 獲取必要的資料:
    • 合成畫面的大小, mix_width, mix_height
    • 需要拼接畫面的原始大小, src_width, src_height
    • 拼接過後畫面的大小和位置, region_width, region_height, region_top_x, region_top_y. 這裡面表示的是具體region的畫面長寬, 位置用左上角那個點的座標表示,x/y的值是相對位置,用百分比表示.比如,
      • 1分屏的位置region_top_x和region_top_y分別為0,0,
      • 2分屏的位置為1/2,0
      • 3分屏的位置為0,1/2
      • 4分屏的位置為1/2,1/2
  2. 申請mix的記憶體, 大小為mix_width*mix_height*3/2
  3. 開始拼接一路畫面,
    • scale拼接畫面, 將src_width/src_height大小reszie為region_width/region_height, 這個過程可以使用ffmpeg的sws_scale
    • 假設mix的畫面記憶體首地址為mix_buffer, scale之後的拼接畫面的記憶體首地址為region_src_buffer.
    • 獲取源資料(region_src_buffer)的yuv資料的記憶體首地址

region_src_y = region_src_buffer;

region_src_u = region_ src_buffer +region_width * region_height ;

region_src_v = region_ src_buffer + region_width * region_height * 5 / 4

    • 獲取region在合成畫面的YUV資料的記憶體首地址

region_dst_y = mix_buffer + mix_width * mix_height * region_top_y + mix_width * region_top_x

region_dst_u = mix_buffer + mix_width * mix_height + mix_width * region_top_x / 2 + mix_width * mix_height * region_top_y / 4

region_dst_v = mix_buffer + mix_width * mix_height * 5 / 4 + mix_width * mix_height + mix_width * region_top_x / 2 + mix_width * mix_height * region_top_y / 4

    • 複製Y資料

uint32_t tmp_dst_pos = 0;

uint32_t tmp_src_pos = 0;

for(uint32_t i = 0; i < region_height; i++)

{

    memcpy(region_dst_y + tmp_dst_pos, region_src_y + tmp_src_pos, region_width);

    tmp_dst_pos += mix_width;

    tmp_src_pos += region_width;

}

    • 複製UV資料

uint32_t tmp_dst_pos = 0;

uint32_t tmp_src_pos = 0;

for(uint32_t i = 0; i < region_height / 2; i++)

{

    memcpy(region_dst_u + tmp_dst_pos, region_src_u + tmp_src_pos, region_width / 2);

    memcpy(region_dst_v + tmp_dst_pos, region_src_v + tmp_src_pos, region_width / 2);

    tmp_dst_pos += mix_width / 2;

    tmp_src_pos += region_width / 2;

}

  1.  繼續拼接其他region畫面
  2. mix_buffer做後續處理scale/encoder等

 

     技術交流有興趣請加:

     音視訊技術交流群:308601278

     無線投屏技術交流群:582349005

     

     我司有成熟的無線投屏盒子銷售,也可做音視訊及無線投屏相關產品和技術的定製化開發

     可訪問我司官網瞭解詳細情況 必捷網路無線投屏SDK

     商務合作可發郵件[email protected]