BT.656視頻信號解碼
BT.656協議標准
ITU-R BT.601和ITU-R BT.656是ITU-R(國際電信聯盟)制定的標准。嚴格來說ITU-R BT.656是ITU-R BT.601的一個子協議。
兩種協議區別在於:
ITU-R BT.601 16位數據傳輸;Y、U、V信號同時傳輸,是並行數據,行場同步信號單獨輸出;
ITU-R BT.656 8/10位數據傳輸;不需要同步信號,串行數據傳輸,輸出速率是601的2倍,先傳Y,再傳UV。行場同步信號嵌入在數據流中。
BT.656
對於PAL制式分辨率為720*576來說,每一幀有576行,奇場288行,偶場288行;
去隔行采用的方法:
利用兩片存儲芯片,采用乒乓交替讀寫的方式來完成。實現步驟:首先將輸入的第一幀圖像奇場數據依次隔行存入存儲器A中,然后再將偶數場數據隔行插入到存儲器A中的奇場數據之間,這樣A中就存入了一幀完整的視頻幀。同樣的方式將第二幀圖像奇場和偶場數據依次存入到B中,在對B進行寫操作的同時依次逐行從A中讀出之前存入的第一幀圖像數據。依次類推,通過乒乓操作方式保證存儲器A和B分別工作在讀寫的狀態,並不斷切換即可完成隔行掃描到逐行掃描的轉換。
凡是做模擬信號采集的,很少不涉及BT.656標准的,因為常見的模擬視頻信號采集芯片都支持輸出BT.656的數字信號,那么,BT.656到底是何種格式呢?
本文將主要介紹 標准的 8bit BT656(4:2:2)YCbCr SDTV(標清)數字視頻信號格式,主要針對剛剛入門模擬視頻采集的初學者入門之用。
1. 幀的概念(Frame)
一個視頻序列是由N個幀組成的,采集圖像的時候一般有2種掃描方式,一種是逐行掃描(progressive scanning),一種是隔行掃描(interlaced scanning)。對於隔行掃描,每一幀一般有2個場(field),一個叫頂場(top field),一個叫底場(bottom field)。假設一幀圖像是720行,那么,頂場就包含其中所有的偶數行,而底場則包含其中所有的奇數行。
2. 場的概念(field)
注意,上面提到頂場和底場,用的是“包含”二字,而不是說完全由后者組成,因為在BT.656標准中,一個場是由三個部分組成的:
場 = 垂直消隱頂場(First Vertical Blanking) + 有效數據行(Active Video) + 垂直消隱底場(Second Vertical Blanking)
對於頂場,有效數據行就是一幀圖像的所有偶數行,而底場,有效數據行就是一幀圖像的所有奇數行。頂場和底場的空白行的個數也有所不同,那么,對於一個標准的 8bit BT656(4:2:2)SDTV(標清)的視頻而言,對於一幀圖像,其格式定義如下:
由上圖可以知道,對於PAL制式,每一幀有625行,其中,頂場有效數據288行,底場有效數據也是288行,其余行即為垂直消隱信號。為什么是288行?因為PAL制式的SDTV或者D1的分辨率為 720*576,即一幀有576行,故一場為288行。
由上圖我們還可以知道,頂場有效數據的起始行為第23行,底場有效數據的起始行為第335行。
另外,上圖中的 F 標記奇偶場,V標記 是否為垂直消隱信號。
3. 每一行的組成(Lines)
下面說明每一行的組成,一行是由4個部分組成:
行 = 結束碼(EAV) + 水平消隱(Horizontal Vertical Blanking) + 起始碼(SAV) + 有效數據(Active Video)
典型的一行數據組成如下圖所示:
起始碼(SAV)和結束碼(EAV),它是標志着一行的開始結束的重要標記,也包含了其他的一些重要的信息,后面將會講到。
為什么水平消隱 是280字節,這個我暫時還沒搞清楚,不知道是不是標准定義的。
為什么一行中的有效數據是 1440 字節? 因為PAL制式的SDTV或者D1的分辨率為 720*576,即一行有720個有效點,由於采集的是彩色圖像,那么一行就是由亮度信息(Y)和色差信息(CbCr)組成的,由於是 YCbCr422格式,故一行中有720列Y,720列CbCr,這樣,一行的有效字節數就自然為 720 x 2 = 1440 字節了。
4. EAV和SAV
EAV和SAV都是4個字節(Bytes),由上面的圖可以知道,SAV后面跟着的就是有效的視頻數據了。那么,EAV和SAV的格式是怎么樣的呢?
EAV和SAV的4個字節的格式規定如下(下面以16進制表示):FF 00 00 XY
其中,前三個字節為固定的,必須是FF 00 00,而第4個字節(XY)是根據場、消隱信息而定的,其8個bit含義如下: 1 F V H P3 P2 P1 P0
其中,F:標記場信息,傳輸頂場時為0,傳輸底場時為1
V:標記消隱信息,傳輸消隱數據時為1,傳輸有效視頻數據時為0
H:標記EAV還是SAV,SAV為0,EAV為1
而 P0~P3為保護比特,其值取決於F、H、V,起到校驗的作用,計算方法如下:
對8bit BT656(4:2:2)數字視頻信號數據進行解碼
(注:bt.656視頻信號的來源實際上是TW9912芯片輸出的視頻信號)
測試平台采用Alinx7020板卡,將轉換得到的RGB數據以HDMI編碼輸出顯示(參照板卡例程);測試結果圖像顯示起始有幾行是黑條,考慮可能是模塊中是從起始行就算有效數據,而bt.656逐行輸出可能是從23行之后才是有效視頻數據的,本人未做進一步測試;
創建RTL模塊如下:
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2020/05/09 11:09:41
// Design Name:
// Module Name: top
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module bt656_to_rgb
(
// system reset
input RST, // system reset
// BT656 input
input CLK_i, // clock
input [7:0] bt_data_i, // video data
input VSYNC_i, // bt656 vertical synchronization
input HSYNC_i, // bt656 horizontal synchronization
// output
output post_frame_clk_o, // pix_out_clock
output wire post_frame_href, // Hs /de
output wire post_frame_vsync, // Vs
output Video_active, // data enable
output wire[23:0] post_img_rgb_0 // video data output
);
//define
wire clk27,clk54;
reg[10:0] pixcnt_total;
reg[7:0] Yreg,Crreg,Cbreg;
reg hs_in_d;
reg [7:0] pix_data;
reg [15:0] pix_cnt;
reg active_video;
reg [15:0] row_cnt;
reg [2:0] state;
reg hs_, vs_, hs_reg, vs_reg;
wire [7:0] R_o1 ,G_o1 ,B_o1;
reg [7:0] R_o2 ,G_o2 ,B_o2;
always @(posedge CLK_i or posedge RST)
begin
if(RST)
hs_in_d <= 0;
else
begin
hs_in_d <= HSYNC_i;
end
end
reg active_video_de;
reg Video_active_reg;
//reg [2:0] state;
always @(posedge CLK_i or posedge RST)
begin
if(RST) begin
pixcnt_total <= 0;
pix_cnt <= 0;
active_video_de <= 0;
row_cnt <= 0;
hs_ <= 0;
vs_ <= 0;
end
else
case(state)
3'd0: begin
if(VSYNC_i) begin
state <= 3'd1;
end
end
3'd1: begin
if(HSYNC_i && !hs_in_d) begin
pixcnt_total <= 0;
end
else begin
pixcnt_total <= pixcnt_total + 1;
end
//720個像素 720個Y亮度分量,360個cb、cr色度分量 720*576
if((pixcnt_total >= 288) && (pixcnt_total <= 1728)) begin
pix_cnt <= pix_cnt + 1;
active_video_de <= 1;
end
else begin
pix_cnt <= 0;
active_video_de <= 0;
end
//行
if(pix_cnt == 1440) begin
hs_ <= 1;
row_cnt <= row_cnt + 1;
end
else begin
hs_ <= 0;
end
if(row_cnt == 576) begin
//if(row_cnt == 720) begin
//if(row_cnt == 582) begin
//if(row_cnt == 625) begin
state <= 3'd0;
row_cnt <= 0;
vs_ <= 1;
Video_active_reg <= 1;
end
else begin
vs_ <= 0;
Video_active_reg <= 0;
end
end
endcase
end
assign Video_active = Video_active_reg;
wire [7:0] post_img_red_0; //Processed Image Red output
wire [7:0] post_img_green_0; //Processed Image Green output
wire [7:0] post_img_blue_0; //Processed Image Blue output
Video_Image_Processor u_Video_Image_Processor_0
(
//global clock
.clk (CLK_i), //cmos video pixel clock
.rst_n (1), //global reset
.per_frame_vsync (vs_), //Prepared Image data vsync valid signal
.per_frame_href (active_video_de), //Prepared Image data href vaild signal
.per_frame_clken (active_video_de), //Prepared Image data output/capture enable clock
.per_frame_YCbCr (bt_data_i), //Prepared Image red data to be processed
//Image data has been processd
.post_frame_vsync (post_frame_vsync), //Processed Image data vsync valid signal
.post_frame_href (post_frame_href), //Processed Image data href vaild signal
.post_frame_clken (), //Processed Image data output/capture enable clock
.post_img_red (post_img_red_0), //Processed Image Red output
.post_img_green (post_img_green_0), //Processed Image Green output
.post_img_blue (post_img_blue_0) //Processed Image Blue output
);
assign post_frame_clk_o = CLK_i;
assign post_img_rgb_0 = {post_img_red_0,post_img_green_0,post_img_blue_0};
endmodule
子模塊1如下:
/*-------------------------------------------------------------------------
| Oooo |
+-------------------------------oooO--( )-----------------------------+
( ) ) /
\ ( (_/
\_)
-----------------------------------------------------------------------*/
`timescale 1ns/1ns
module Video_Image_Processor
(
//global clock
input clk, //cmos video pixel clock
input rst_n, //global reset
//Image data prepred to be processd
input per_frame_vsync, //Prepared Image data vsync valid signal
input per_frame_href, //Prepared Image data href vaild signal
input per_frame_clken, //Prepared Image data output/capture enable clock
input [7:0] per_frame_YCbCr, //Prepared Image data of YCbCr 4:2:2 {CbY} {CrY}
//Image data has been processd
output post_frame_vsync, //Processed Image data vsync valid signal
output post_frame_href, //Processed Image data href vaild signal
output post_frame_clken, //Processed Image data output/capture enable clock
output [7:0] post_img_red, //Processed Image Red output
output [7:0] post_img_green, //Processed Image Green output
output [7:0] post_img_blue //Processed Image Blue output
);
//-------------------------------------
//Convert the YCbCr4:2:2 format to YCbCr4:4:4 format.
//CMOS YCbCr444 data output
wire post1_frame_vsync; //Processed Image data vsync valid signal
wire post1_frame_href; //Processed Image data href vaild signal
wire post1_frame_clken; //Processed Image data output/capture enable clock
wire [7:0] post1_img_Y; //Processed Image data of YCbCr 4:4:4
wire [7:0] post1_img_Cb; //Processed Image data of YCbCr 4:4:4
wire [7:0] post1_img_Cr; //Processed Image data of YCbCr 4:4:4
VIP_YCbCr422_YCbCr444 u_VIP_YCbCr422_YCbCr444
(
//global clock
.clk (clk), //cmos video pixel clock
.rst_n (rst_n), //system reset
//Image data prepred to be processd
.per_frame_vsync (per_frame_vsync), //Prepared Image data vsync valid signal
.per_frame_href (per_frame_href), //Prepared Image data href vaild signal
.per_frame_clken (per_frame_clken), //Prepared Image data output/capture enable clock
.per_frame_YCbCr (per_frame_YCbCr), //Prepared Image red data to be processed
//Image data has been processd
.post_frame_vsync (post1_frame_vsync), //Processed Image data vsync valid signal
.post_frame_href (post1_frame_href), //Processed Image data href vaild signal
.post_frame_clken (post1_frame_clken), //Processed Image data output/capture enable clock
.post_img_Y (post1_img_Y), //Processed Image brightness output
.post_img_Cb (post1_img_Cb), //Processed Image blue shading output
.post_img_Cr (post1_img_Cr) //Processed Image red shading output
);
//-------------------------------------
//Convert the YCbCr444 format to RGB888 format.
VIP_YCbCr444_RGB888 u_VIP_YCbCr444_RGB888
(
//global clock
.clk (clk), //cmos video pixel clock
.rst_n (rst_n), //system reset
//Image data prepred to be processd
.per_frame_vsync (post1_frame_vsync), //Prepared Image data vsync valid signal
.per_frame_href (post1_frame_href), //Prepared Image data href vaild signal
.per_frame_clken (post1_frame_clken), //Prepared Image data output/capture enable clock
.per_img_Y (post1_img_Y), //Prepared Image data of Y
.per_img_Cb (post1_img_Cb), //Prepared Image data of Cb
.per_img_Cr (post1_img_Cr), //Prepared Image data of Cr
//Image data has been processd
.post_frame_vsync (post_frame_vsync), //Processed Image data vsync valid signal
.post_frame_href (post_frame_href), //Processed Image data href vaild signal
.post_frame_clken (post_frame_clken), //Processed Image data output/capture enable clock
.post_img_red (post_img_red), //Prepared Image green data to be processed
.post_img_green (post_img_green), //Prepared Image green data to be processed
.post_img_blue (post_img_blue) //Prepared Image blue data to be processed
);
Endmodule
子模塊2如下:
`timescale 1ns/1ns
module VIP_YCbCr422_YCbCr444
(
//global clock
input clk, //cmos video pixel clock
input rst_n, //global reset
//CMOS 16Bit YCbCr data input: {CbYCrYCbYCrY}
input per_frame_vsync, //Prepared Image data vsync valid signal
input per_frame_href, //Prepared Image data href vaild signal
input per_frame_clken, //Prepared Image data output/capture enable clock
input [7:0] per_frame_YCbCr, //Prepared Image data of YCbCr 4:2:2 CbYCrY
//CMOS YCbCr444 data output
output post_frame_vsync, //Processed Image data vsync valid signal
output post_frame_href, //Processed Image data href vaild signal
output post_frame_clken, //Processed Image data output/capture enable clock
output reg [7:0] post_img_Y, //Processed Image data of YCbCr 4:4:4
output reg [7:0] post_img_Cb, //Processed Image data of YCbCr 4:4:4
output reg [7:0] post_img_Cr //Processed Image data of YCbCr 4:4:4
);
//------------------------------------------
//lag n pixel clocks
reg [4:0] post_frame_vsync_r;
reg [4:0] post_frame_href_r;
reg [4:0] post_frame_clken_r;
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
post_frame_vsync_r <= 0;
post_frame_href_r <= 0;
post_frame_clken_r <= 0;
end
else
begin
post_frame_vsync_r <= {post_frame_vsync_r[3:0], per_frame_vsync};
post_frame_href_r <= {post_frame_href_r[3:0], per_frame_href};
post_frame_clken_r <= {post_frame_clken_r[3:0], per_frame_clken};
end
end
assign post_frame_vsync = post_frame_vsync_r[4];
assign post_frame_href = post_frame_href_r[4];
assign post_frame_clken = post_frame_clken_r[4];
wire yuv_process_href = per_frame_href || post_frame_href_r[3];
wire yuv_process_clken = per_frame_clken || post_frame_clken_r[3];
//-------------------------------------------
//convert YCbCr422 to YCbCr444
reg [3:0] yuv_state;
reg [7:0] mY0, mY1, mY2, mY3;
reg [7:0] mCb0, mCb1;
reg [7:0] mCr0, mCr1;
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
yuv_state <= 4'd0;
{mY0, mCb0, mCr0} <= {8'h0, 8'h0, 8'h0};
mY1 <= 8'h0;
{mY2, mCb1, mCr1} <= {8'h0, 8'h0, 8'h0};
mY3 <= 8'h0;
{post_img_Y, post_img_Cb, post_img_Cr} <= {8'h0, 8'h0, 8'h0};
end
else if(yuv_process_href) //lag 2 data enable clock and need 2 more clocks
begin
if(yuv_process_clken) //lag 2 data enable clock and need 2 more clocks
case(yuv_state) //---YCbCr
4'd0: begin //reg p0
yuv_state <= 4'd1;
{mCb0} <= per_frame_YCbCr;
end
4'd1: begin //reg p1
yuv_state <= 4'd2;
{mY0} <= per_frame_YCbCr;
end
4'd2: begin //p0; reg p2
yuv_state <= 4'd3;
{mCr0} <= per_frame_YCbCr;
end
4'd3: begin //p1; reg p4
yuv_state <= 4'd4;
{mY1} <= per_frame_YCbCr;
end
4'd4: begin //p2; reg p0
yuv_state <= 4'd5;
{mCb1} <= per_frame_YCbCr;
{post_img_Y, post_img_Cb, post_img_Cr} <= {mY0, mCb0, mCr0};
end //p4; reg p1
4'd5: begin
yuv_state <= 4'd6;
{mY2} <= per_frame_YCbCr;
{post_img_Y, post_img_Cb, post_img_Cr} <= {mY1, mCb0, mCr0};
end
4'd6: begin //p2; reg p0
yuv_state <= 4'd7;
{mCr1} <= per_frame_YCbCr;
end //p4; reg p1
4'd7: begin
yuv_state <= 4'd8;
{mY3} <= per_frame_YCbCr;
end
4'd8: begin //p2; reg p0
yuv_state <= 4'd9;
{mCb0} <= per_frame_YCbCr;
{post_img_Y, post_img_Cb, post_img_Cr} <= {mY2, mCb1, mCr1};
end //p4; reg p1
4'd9: begin
yuv_state <= 4'd10;
{mY0} <= per_frame_YCbCr;
{post_img_Y, post_img_Cb, post_img_Cr} <= {mY3, mCb1, mCr1};
end
4'd10: begin //p2; reg p0
yuv_state <= 4'd11;
{mCr0} <= per_frame_YCbCr;
end //p4; reg p1
4'd11: begin
yuv_state <= 4'd4;
{mY1} <= per_frame_YCbCr;
end
endcase
else
begin
yuv_state <= yuv_state;
{mY0, mCb0, mCr0} <= {mY0, mCb0, mCr0};
mY1 <= mY1;
{mY2, mCb1, mCr1} <= {mY2, mCb1, mCr1};
mY3 <= mY3;
{post_img_Y, post_img_Cb, post_img_Cr} <= {post_img_Y, post_img_Cb, post_img_Cr};
end
end
else
begin
yuv_state <= 3'd0;
{mY0, mCb0, mCr0} <= {8'h0, 8'h0, 8'h0};
{mY1, mCb1, mCr1} <= {8'h0, 8'h0, 8'h0};
{post_img_Y, post_img_Cb, post_img_Cr} <= {8'h0, 8'h0, 8'h0};
end
end
endmodule
子模塊3如下:
`timescale 1ns/1ns
module VIP_YCbCr444_RGB888
(
//global clock
input clk, //cmos video pixel clock
input rst_n, //global reset
//CMOS YCbCr444 data output
input per_frame_vsync, //Prepared Image data vsync valid signal
input per_frame_href, //Prepared Image data href vaild signal
input per_frame_clken, //Prepared Image data output/capture enable clock
input [7:0] per_img_Y, //Prepared Image data of Y
input [7:0] per_img_Cb, //Prepared Image data of Cb
input [7:0] per_img_Cr, //Prepared Image data of Cr
//CMOS RGB888 data output
output post_frame_vsync, //Processed Image data vsync valid signal
output post_frame_href, //Processed Image data href vaild signal
output post_frame_clken, //Processed Image data output/capture enable clock
output [7:0] post_img_red, //Prepared Image green data to be processed
output [7:0] post_img_green, //Prepared Image green data to be processed
output [7:0] post_img_blue //Prepared Image blue data to be processed
);
//--------------------------------------------
/*********************************************
R = 1.164(Y-16) + 1.596(Cr-128)
G = 1.164(Y-16) - 0.391(Cb-128) - 0.813(Cr-128)
B = 1.164(Y-16) + 2.018(Cb-128)
->
R = 1.164Y + 1.596Cr - 222.912
G = 1.164Y - 0.391Cb - 0.813Cr + 135.488
B = 1.164Y + 2.018Cb - 276.928
->
R << 9 = 596Y + 817Cr - 114131
G << 9 = 596Y - 200Cb - 416Cr + 69370
B << 9 = 596Y + 1033Cb - 141787
**********************************************/
reg [19:0] img_Y_r1; //8 + 9 + 1 = 18Bit
reg [19:0] img_Cb_r1, img_Cb_r2;
reg [19:0] img_Cr_r1, img_Cr_r2;
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
img_Y_r1 <= 0;
img_Cb_r1 <= 0; img_Cb_r2 <= 0;
img_Cr_r1 <= 0; img_Cr_r2 <= 0;
end
else
begin
img_Y_r1 <= per_img_Y * 18'd596;
img_Cb_r1 <= per_img_Cb * 18'd200;
img_Cb_r2 <= per_img_Cb * 18'd1033;
img_Cr_r1 <= per_img_Cr * 18'd817;
img_Cr_r2 <= per_img_Cr * 18'd416;
end
end
//--------------------------------------------
/**********************************************
R << 9 = 596Y + 817Cr - 114131
G << 9 = 596Y - 200Cb - 416Cr + 69370
B << 9 = 596Y + 1033Cb - 141787
**********************************************/
reg [19:0] XOUT;
reg [19:0] YOUT;
reg [19:0] ZOUT;
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
XOUT <= 0;
YOUT <= 0;
ZOUT <= 0;
end
else
begin
XOUT <= (img_Y_r1 + img_Cr_r1 - 20'd114131)>>9;
YOUT <= (img_Y_r1 - img_Cb_r1 - img_Cr_r2 + 20'd69370)>>9;
ZOUT <= (img_Y_r1 + img_Cb_r2 - 20'd141787)>>9;
end
end
//------------------------------------------
//Divide 512 and get the result
//{xx[19:11], xx[10:0]}
reg [7:0] R, G, B;
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
R <= 0;
G <= 0;
B <= 0;
end
else
begin
R <= XOUT[10] ? 8'd0 : (XOUT[9:0] > 9'd255) ? 8'd255 : XOUT[7:0];
G <= YOUT[10] ? 8'd0 : (YOUT[9:0] > 9'd255) ? 8'd255 : YOUT[7:0];
B <= ZOUT[10] ? 8'd0 : (ZOUT[9:0] > 9'd255) ? 8'd255 : ZOUT[7:0];
end
end
//------------------------------------------
//lag n clocks signal sync
reg [2:0] post_frame_vsync_r;
reg [2:0] post_frame_href_r;
reg [2:0] post_frame_clken_r;
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
post_frame_vsync_r <= 0;
post_frame_href_r <= 0;
post_frame_clken_r <= 0;
end
else
begin
post_frame_vsync_r <= {post_frame_vsync_r[1:0], per_frame_vsync};
post_frame_href_r <= {post_frame_href_r[1:0], per_frame_href};
post_frame_clken_r <= {post_frame_clken_r[1:0], per_frame_clken};
end
end
assign post_frame_vsync = post_frame_vsync_r[2];
assign post_frame_href = post_frame_href_r[2];
assign post_frame_clken = post_frame_clken_r[2];
assign post_img_red = post_frame_href ? R : 8'd0;
assign post_img_green = post_frame_href ? G : 8'd0;
assign post_img_blue = post_frame_href ? B : 8'd0;
endmodule