MPEG-1流比特層次結構分析總結
1.簡要介紹Mpeg
2.Mpeg-1數據流分析
2.1視頻序列層(VideoStream)
2.2畫麵組層(GOP)
2.3畫麵層(Pictures)
2.4片層(Slice)
2.5宏塊層(Macroblock)
2.6塊層(Block)
3.加密位置的思考
附錄
MPEG-1流比特層次結構分析總結
1.簡要介紹Mpeg
Mpeg是Motion Picture Expert Group的縮寫。活動圖像專家組是在1988年由ISO和IEC聯合成立的專家組,負責開發電視圖像數據和聲音數據的編碼,解碼和它們的同步等標準。到目前為止已經開發和正在開發的MPEG標準有很多,主要包括Mpeg-1,Mpeg-2,Mpeg-4,和Mpeg-7.
其中的Mpeg-1處理的是標準圖像交換格式(standard interchange format,SIF)或者稱為源輸入格式(Source Input Format,SIF)的電視,將模擬的圖像信息,通過編碼成為數字圖像信息,原始輸入可以是NTSC製式352pixels * 240lines * 30frames/second, PAL製352pixels * 288lines*25frames/second,壓縮後的數字圖像信息的速率為1.5Mb/s.這個標注是1992年正是的發布的,是針對當時具有這種數據傳輸速率的CD-ROM和網絡而開發的,用於在CD-ROM上存儲數字影視和在網絡上傳輸數字影視。
MPEG-1的標準號為ISO/IEC 11172,標準名:“信息技術——用於數據速率大約高達1.5Mb/s的數字存儲替的電視圖像和伴音編碼”
本文主要是對Mpeg-1Video數據流的結構進行分析,並將怎樣得到Mpeg-1流中的數據部分進行的闡述。ISO/IEC 11172-2
2.Mpeg-1數據流分析
編碼後的視頻序列是一個如同計算機網絡的OSI模型下的數據序列一樣,數據被分成很多層的概念。
視頻序列層-畫麵組層-畫麵層-片層-宏塊層-塊層
層次的關係很明顯,越往後越是底層,越接近實際的數據。
2.1視頻序列層(VideoStream)
視頻序列是以一個序列標題開始,之後可以跟著一個或者多個畫麵組。最後以Sequence_end_code結束。緊挨著每一個畫麵組之前可以有一個序列標題。也就是說每個畫麵組,都可以有一個自己的序列標題。
序列標題是一個以sequence_header_code開始,後跟著一係列數據元素的結構。是視頻流中用來解碼的重要的參數之一。其中定義了量化矩陣(load_intra_quantizer_matrix和 load_non_intra_quantizer_matrix以及可選的intra_quantizer_matrix和non_intra_quantizer_ matrix)以及其它的一些重要的數據元素,其中量化矩陣是可以在視頻流中重複的量化矩陣中變化的,並且在每次變化後,量化矩陣重新定義。其它的元素必須與第一個序列標題中的值相同。
整個視頻序列的結構可以用下麵的代碼表示:
Video_Stream{
unsigned int h_size; /* Horiz. size in pixels. */
unsigned int v_size; /* Vert. size in pixels. */
unsigned int mb_height; /* Vert. size in mblocks. */
unsigned int mb_width; /* Horiz. size in mblocks. */
unsigned char aspect_ratio; /* Code for aspect ratio. */
unsigned char picture_rate; /* Code for picture rate. */
unsigned int bit_rate; /* Bit rate. */
unsigned int vbv_buffer_size; /* Minimum buffer size. */
BOOLEAN const_param_flag; /* Contrained parameter flag. */
unsigned char intra_quant_matrix[8][8]; /* Quantization matrix for
intracoded frames. */
unsigned char non_intra_quant_matrix[8][8]; /* Quanitization matrix for
non intracoded frames. */
char *ext_data; /* Extension data. */
char *user_data; /* User data. */
GoP group; /* Current group of pict. */
Pict picture; /* Current picture. */
Slice slice; /* Current slice. */
Macroblock mblock; /* Current macroblock. */
Block block; /* Current block. */
int state; /* State of decoding. */
int bit_offset; /* Bit offset in stream. */
unsigned int *buffer; /* Pointer to next byte in
buffer. */
int buf_length; /* Length of remaining buffer.*/
unsigned int *buf_start; /* Pointer to buffer start. */
int max_buf_length; /* Max lenght of buffer. */
PictImage *past; /* Past predictive frame. */
PictImage *future; /* Future predictive frame. */
PictImage *current; /* Current frame. */
PictImage *ring[RING_BUF_SIZE]; /* Ring buffer of frames. */
} Video_Stream;
具體的序列標題的結構的部分是這樣的:
序列
sequence_header{
SEQ_START_CODE 0x000001b3; /* 常量 ,作用使用來定位視頻序列的序列頭 */
unsigned int h_size; /* Horiz. size in pixels. */
unsigned int v_size; /* Vert. size in pixels. */
unsigned int mb_height; /* Vert. size in mblocks. */
unsigned int mb_width; /* Horiz. size in mblocks. */
unsigned char aspect_ratio; /* Code for aspect ratio. */
unsigned char picture_rate; /* Code for picture rate. */
unsigned int bit_rate; /* Bit rate. */
unsigned int vbv_buffer_size; /* Minimum buffer size. */
BOOLEAN const_param_flag; /* Contrained parameter flag. */
unsigned char load_intra_quantizer_matrix;
unsigned char intra_quant_matrix[8][8]; /* Quantization matrix for intracoded frames. 這個結構是可選的,要看 load_intra_quantizer_matrix的值,為真則有這個部分,否則沒有,因為
intra_quant_matrix是量化表的值,而Sequence_header結構在視頻序列中是可重複的,即在每個畫麵組之前都有可能再次給出一個sequence_header,並且可以在新的sequence_header 中重新定義量化表*/
unsigned char load_non_intra_quantizer_matrix;
unsigned char non_intra_quant_matrix[8][8]; /* Quanitization matrix for non intracoded frames. 也是可選。願意於intra_quant_matrix可選的原因相同。當load_non_intra_quant_matrix的值為真的時候需要定義。 */
char *ext_data; /* Extension data. */
char *user_data; /* User data. */
}
由上麵的分析,可以看出來的是:
video_sequence()
{
next_start_code()
do
{
sequence_header();
do
{
group_of_pictures() ; //畫麵組
}while (nextbits()==GROUP_START_CODE)
}while(nextbits()==SEQUENCE_HEADER_CODE)
SEQUENCE_END_CODE
};
正是由於視頻序列中存在很多開始碼,或者稱之為定位碼、同步碼。用來告訴解碼器目前數據的區域信息,所以解碼器才可以正確的處理各個數據區的數據,下麵就是視頻序列中的開始碼的羅列:
#define SEQ_END_CODE 0x000001b7
#define SEQ_START_CODE 0x000001b3
#define GOP_START_CODE 0x000001b8
#define PICTURE_START_CODE 0x00000100
#define SLICE_MIN_START_CODE 0x00000101
#define SLICE_MAX_START_CODE 0x000001af
#define EXT_START_CODE 0x000001b5
#define USER_START_CODE 0x000001b2
這些開始碼都是一些特殊的32bits的比特序列,在視頻碼流中不會出現的。他們的起著標誌的作用,具體可以從名稱上麵看出來。
其中EXT_START_CODE和USER_START_CODE在每個層裏麵都會出現,用來標誌擴展數據區和用戶數據區,用來添加任意的數據,直到下一個開始碼結束。
2.2畫麵組層(GOP)
在軟件xmplay1.1中的定義
typedef struct GoP {
BOOLEAN drop_flag; /* Flag indicating dropped frame. */
unsigned int tc_hours; /* Hour component of time code. */
unsigned int tc_minutes; /* Minute component of time code. */
unsigned int tc_seconds; /* Second component of time code. */
unsigned int tc_pictures; /* Picture counter of time code. */
BOOLEAN closed_gop; /* Indicates no pred. vectors to
previous group of pictures. */
BOOLEAN broken_link; /* B frame unable to be decoded. */
char *ext_data; /* Extension data. */
char *user_data; /* User data. */
} GoP;
當然每個畫麵組層都是開始與標誌碼:GOP_START_CODE
該層次語法上的定義是
group_of_pictures
{
GOP_START_CODE
Time_code; tc_hours,tc_minutes,tc_seconds,tc_pictures
Closed_gop;
Broken_link;
Next_start_code;
If(nextbits==extension_start_code)
{
Extension_start_code;
While(nextbits()==”0000 0000 0000 0000 0000 0001”)
{
Group_extension_data;
}
next_start_code()
}
if(nextbits==user_data_start_code)
{
user_data_start_code
while(nextbits()!=’0000 0000 0000 0000 0000 0001’)
{
user_data;
}
next_start_code()
}
do
{
picture()
}while(nextbits==picture_start_code)
}
Mpeg流最終顯示出來是一係列的畫麵,而畫麵組是mpeg流中可以獨立編碼的最小的單位,每個畫麵組由一個標題和一係列畫麵組成。GOP標題包含了時間和編輯的信息。
Mpeg畫麵組中必須至少有一個I幀畫麵,可以有數目可變的B幀和P幀畫麵,也可以沒有P和B幀。畫麵組的第一幅編碼畫麵是I畫麵,該畫麵之後跟隨著任意數目的I或P畫麵,每對I、P畫麵之間可以插入任意數目的B畫麵。
畫麵組是畫麵的集合,每幅畫麵按照顯示的順序相鄰。
畫麵組中的畫麵有兩種排列順序:
1.按比特流順序 必須以I幀開頭,後麵可按任何的次序,跟上任意數目的I,P或B畫麵。
2.按顯示順序必須以I或B畫麵打頭,且以I或P畫麵結束,最小的畫麵組由一個I畫麵組成。
從編碼角度,可以精確的陳述的是,畫麵組以一個畫麵組標題開始,以最先出現的下一個畫麵組標題或者下一個序列標題或者序列結束碼結束。
Mepg流中的標誌碼也就是開始碼,對正確的分割和識別碼流的成分起到了至關重要的作用。
2.3畫麵層(Pictures)
畫麵組層中的一幅幅畫麵就是畫麵層的數據了。包含了一幅畫麵的所有編碼信息。一幅畫麵同樣始於畫麵的標題。標題以畫麵開始碼(PICTURE_START_CODE 0x00000100)打頭。
解析畫麵單元的語法結構:
picture()
{
picture_start_code
temprol_reference /*時序編號,通常一組畫麵的編號都在1024以內,如果超過那麼在1025幅畫麵出複位為0,重新計數。 */
picture_coding_type
vbv_delay/*對於固定比特率的視頻流,vbv_delay用與解碼過程開始和隨機存取之後,以保證在第一幅畫麵被顯示之前,解碼器 已經讀到正確數目的比特數。*/
if((picture_coding_type==2) || picture_coding_type==3)
{
full_pel_foward_vector /*全象素前向矢量,給定前向矢量的精度,在P和B畫麵的標題中出現*/
forward_f_code
}
if(picture_coding_type==3)
{
full_pel_backward_vector
back_f_code
}
while(nextbits()==’1’)
{
extra_bit_picture
extra_information_picture
}
extra_bit_picture
next_start_code
if(nextbits()==extension_start_code)
{
extension_start_code
while(nextbits()!=’0000 0000 0000 0000 0000 0001’)
{
picture_extension_data
}
next_start_code()
}
if(nextbits()==user_data_start_code)
{
user_data_start_code
while(nextbits()!=’0000 0000 0000 0000 0000 0001’)
{
user_data
}
next_start_code()
}
do
{
slice()
}while(nextbits()==slice_start_code)
}
整個畫麵單元結構是這樣的:
typedef struct pict {
unsigned int temp_ref; /* Temporal reference. */
unsigned int code_type; /* Frame type: P, B, I */
unsigned int vbv_delay; /* Buffer delay. */
BOOLEAN full_pel_forw_vector; /* Forw. vectors specified in full
pixel values flag. */
unsigned int forw_r_size; /* Used for vector decoding. */
unsigned int forw_f; /* Used for vector decoding. */
BOOLEAN full_pel_back_vector; /* Back vectors specified in full
pixel values flag. */
unsigned int back_r_size; /* Used in decoding. */
unsigned int back_f; /* Used in decoding. */
char *extra_info; /* Extra bit picture info. */
char *ext_data; /* Extension data. */
char *user_data; /* User data. */
} Pict;
可以看出整個pictures層的bit流結構中由標題和pictures數據組成。
標題中提供了必要的畫麵信息數據和運動矢量的信息。
2.4片層(Slice)
片是任意數目宏塊組成的序列,其中宏塊必須從畫麵的左上位置開始,按照光柵掃描的方向從左到右,從上到下排列。片中至少包涵一個宏塊,片與片之間沒有重疊,也沒有間隙。
片層的解析語法:
首先給出識別出Slice層數據的頭標slice_start_code
『
#define SLICE_MIN_START_CODE 0x00000101
#define SLICE_MAX_START_CODE 0x000001af
』
slice
{
slice_start_code /*從中可以計算出slice_vertical_position 片中第一個宏塊,以宏塊為單位的垂直位置*/
quantizer_scale /*設置量化步長尺寸。1-31*/
while(nextbits()==’1’)
{
extra_bit_slice ‘1’
extra_information_slice
}
extra_bit_scale ‘0’
do
{
macroblock()
}while(nextbits()!=’0000 0000 0000 0000 0000 0000’)
next_start_code()
}
typedef struct slice {
unsigned int vert_pos; /* Vertical position of slice. */
unsigned int quant_scale; /* Quantization scale. */
char *extra_info; /* Extra bit slice info. */
} Slice;
每個片由一個開始碼開始,開始後DC係數和矢量解碼的預測值都被複位,片開始部位的位置的水平位置由片中第一個宏塊的宏塊地址決定。這些措施使得在一幅畫麵內任何一片都可以單獨編碼而不需要前一片的信息。當解碼是出現錯誤,即可以從後繼的片重新開始。
所以,當數據在無錯的環境中,可以一幅畫麵就作為一片,但是如果是有錯的環境,則每行宏塊作為一片會更加合理。
表2 256×192畫麵內的片劃分(每行宏塊作為一個片,每個片的高度都是16pixels)
1開始 1結束
2開始 2結束
3開始 3結束
4開始 4結束
5開始 5結束
6開始 6結束
7開始 7結束
8開始 8結束
9開始 9結束
10開始 10結束
11開始 11結束
12開始 12結束
13開始 13結束
實際情況中片不宜太多,因為片標題,以及新片所需要盡心重新編碼花費的開銷很大。
片始於片標題,片標題又始於片開始碼,片開始碼是可以在一個範圍中取得得,這個範圍就是
#define SLICE_MIN_START_CODE 0x00000101
#define SLICE_MAX_START_CODE 0x000001af
片開始碼得最後8為可以給出片得垂直位置,即以宏塊為單位從畫麵頂部位置為1開始算起,片中第一個宏塊的垂直位置。宏塊有一個行號可以作為它得定位數據,這個行號的計算方法是:片垂直位置-1
宏塊的垂直位置最大為175。片中第一個宏塊的水平位置,可以由該宏塊的地址偏移計算出來,所以不需要依賴畫麵內的任何其他的宏塊的信息。
2.5宏塊層(Macroblock)
宏塊是包含16pixels*16lines的亮度分量部分,以及在空間位置上對應的兩個8pixels*8lines的色度分量部分,一個宏塊有4個亮度塊和2個色度塊。宏塊可以指源圖像或者重構圖像的數據,或者是量化後的DCT係數。
宏塊中塊的順序如下:
表1 宏塊中塊的排列
01
23
4
5
Y分量Cb分量Cr分量
宏塊的數據分析語法描述:
macroblock()
{
while(nextbits()==’0000 0001 111’)
{
macroblock_stuffing /*宏塊填料,為了防止下溢出,由編碼器填入的數據,有它固定的11位bit格式就是’0000 0001 111’,當然 解決下溢出的方法還有很多,編碼器可以在標題之前就加入填料位,或者可以減小quant_scale獲得更多的編碼係數等等*/
}
while(nextbits()==’0000 0001 000’)
{
macroblock_escape /*固定模式的bit串,當macroblock_address與previous_macroblock_address的差大於33時將用到該碼。 使得後繼的macroblock_increment所表示的值加33。
}
macroblock_address_increment /*用於表示macroblock_address和previous_macorblock_ address之間的差值。 最大值為33,當前兩者差大於33時用macroblock_escape補充。 Macroblock_address表示的是宏塊在畫麵中的絕對位置,最左上角的宏塊的macroblock_address為0,previous_macroblock_address指示片中最後一個非跳空宏塊的位置。*/
macroblock_type
if(macroblock_motion_forward)
{
motion_horizontal_forward_code
if((forward_f!=1) && (motion_horizontal_forward_code!=0))
motion_horizontal_forward_r
motion_vertical_forward_code
if((forward_f!=1) && (motion_vertical_forward_code!=0))
motion_vertical_forward_r
}
if(macroblock_motion_backward)
{
motion_horizontal_backward_code
if((backward_f!=1) && (motion_horizontal_backward_code!=0))
motion_horizontal_backward_r
motion_vertical_backward_code
if((backward_f!=1) && (motion_vertical_backward_code!=0))
motion_vertical_backward_r
}
if(macroblock_pattern)
coded_block_pattern /*可以得到宏塊宏塊的pattern_code[i](i=0:5),從而確定該宏塊接收到的塊的種類有哪些。*/
for(i=0;i<6;i++)
block(i)
if(picture_coding_type==4)
end_of_marcoblock
}
片被分為16pixels*16lines的象素宏塊。每個宏塊都有它的標題。包含了宏塊的地址、類型、量化器標尺信息等等。標題之後是該宏塊的6個塊的數據。
在Xmplay代碼中給出的macrblock的定義:
typedef struct macroblock {
int mb_address; /* Macroblock address. */
int past_mb_addr; /* Previous mblock address. */
int motion_h_forw_code; /* Forw. horiz. motion vector code. */
unsigned int motion_h_forw_r; /* Used in decoding vectors. */
int motion_v_forw_code; /* Forw. vert. motion vector code. */
unsigned int motion_v_forw_r; /* Used in decdoinge vectors. */
int motion_h_back_code; /* Back horiz. motion vector code. */
unsigned int motion_h_back_r; /* Used in decoding vectors. */
int motion_v_back_code; /* Back vert. motion vector code. */
unsigned int motion_v_back_r; /* Used in decoding vectors. */
unsigned int cbp; /* Coded block pattern. */
BOOLEAN mb_intra; /* Intracoded mblock flag. */
BOOLEAN bpict_past_forw; /* Past B frame forw. vector flag. */
BOOLEAN bpict_past_back; /* Past B frame back vector flag. */
int past_intra_addr; /* Addr of last intracoded mblock. */
int recon_right_for_prev; /* Past right forw. vector. */
int recon_down_for_prev; /* Past down forw. vector. */
int recon_right_back_prev; /* Past right back vector. */
int recon_down_back_prev; /* Past down back vector. */
} Macroblock;
2.6塊層(Block)
塊是一個正交的8pixels*8lines的亮度或者色度分量,塊可以指源畫麵數據或者相應的編碼數據元素。
8*8單位象素的源畫麵數據經過DCT變換後的成為了相應的DCT係數塊。
塊的具體結構為(xmplay源碼中的結構定義):
typedef struct block
{
short int dct_recon[8][8]; /* Reconstructed dct coeff matrix. */
short int dct_dc_y_past; /* Past lum. dc dct coefficient. */
short int dct_dc_cr_past; /* Past cr dc dct coefficient. */
short int dct_dc_cb_past; /* Past cb dc dct coefficient. */
} Block;
解析塊的語法結構是:
block(i)
{
if(pattern_code[i])
{
if(macroblock_intra)
{
if(i<4)
{
dct_dc_size_luminance
if(dc_size_luminance!=0)
dct_dc_differential
}
else
{
dct_dc_size_chrominance
if(dc_size_chrominance!=0)
dct_dc_differential
}
}
else
{
dct_coeff_first
}
if(picture_coding_type!=4)
{
while(nextbits()!=’10’)
dct_coeff_next
end_of_block
}
}
}
附錄
文中部分代碼中的數據類型與標準C中數據類型對應關係:
typedef int INT32;
typedef short INT16;
typedef char INT8;
#endif
typedef unsigned int UINT32;
typedef unsigned short UINT16;
typedef unsigned char UINT8;
xmplay-1.1這個播放器的源碼,對幫助理解mpeg1結構很有幫助,同時這也是一個編碼和解碼器,以及數據分析器,在internet上麵可以搜所到
|