掌上游戏新活动平台

mpeg文件格式分析

2026-08-10 16:44:21

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上面能够搜所到