废气处理设施运行台账:MP3详解-程序中子函数代码详析(1)

来源:百度文库 编辑:偶看新闻 时间:2024/04/29 17:44:42

比特流分解函数分析

1:函数open_bit_stream_r分析

void open_bit_stream_r(Bit_stream_struc *bs, char *bs_filenam, int size)

{

函数的操作对象是(1)用户提供的mp3文件名filenam2)用来映像MP3文件的帧结构体bs3)缓冲区的大小size

       register unsigned char flag = 1;

定义变量前加个register是因为这个参数flag将频繁使用,为了引用的速度够块,前加register从而建议处理器分配寄存器来存放这个参数。

       if ((bs->pt = fopen(bs_filenam, "rb")) == NULL) {

以二进制读出的方式打开用户指定的mp3文件,并用pt指向这个文件,如果打开失败则执行下面的出错信息。

              printf("Could not find \"%s\".\n", bs_filenam);

              exit(1);

       }

       bs->format = BINARY;

       alloc_buffer(bs, size);

       bs->buf_byte_idx=0;

       bs->buf_bit_idx=0;

       bs->totbit=0;

       bs->mode = READ_MODE;

       bs->eob = FALSE;

       bs->eobs = FALSE;

为了更好的宏观上用结构体bs映像该mp3文件,要根据文件的一些特点来对结构体中的一些参数进行赋值。比如用bs->pt指向文件,bs->format设为二进制,bs->buf_byte_idx=0;bs->buf_bit_idx=0;bs->totbit=0;表示这个mp3文件现在还没有被操作过,如果从这个文件中取出了9位比特,那么相应的bs->buf_byte_idx bs->buf_bit_idx bs->totbit=0会被从新设定为bs->buf_byte_idx=1表示当前是第2个字节,bs->buf_bit_idx=1表示当前等待操作的是第2个字节的第2位,bs->totbit表示当前等待操作的是第10位比特。

}

2:函数end_bs分析

int end_bs(Bit_stream_struc *bs)

{

       return(bs->eobs);

返回结束标志,一开始初始化位false表示当前帧还没有操作结束,一旦对当前帧的所有数据都操作过后,bs->eobstrue

}

3:函数seek_sync分析

int seek_sync(Bit_stream_struc *bs, unsigned long sync, int N)

{

该函数的操作对象是结构体bs指向的文件,查找目标是变量sync定义的0xfff,查找目标的长度有变量N定义这里N12

       unsigned long aligning;

       unsigned long val;

       long maxi = (int)pow(2.0, (double)N) - 1;

       定义maxi=[2N次方)-1],在主程序中N=12,所以maxi实际上就是1111 1111 1111B

       aligning = sstell(bs)%ALIGNING;

变量aligning的作用在于确保同步头的识别是从字节边界开始的。比如说在二进制文件里面存在这么一段数据:0111 1111 1111 0101 0011 1100 1111 0010,在这段数据里面,前面连续的111不能算是同步头,因为第一个1开始的地方不是字节的边界。如果存在这么一段数据1111 1111 1111 0101 0011 1100 1111 0010,那么前面连续的111就可以确定是同步头了。(understand?:

函数sstell的作用是返回当前要操作的比特位置,比如说,我们已经提取并处理了文件的前50个比特,但还是没有找到同步头(mp3文件的开始往往有一段无用的乱码数据),虽然没有找到,但是结构体中的bs->totbit始终在累加计数,此时的它应该是50,通过调用函数sstell(&bs)就可以返回出bs->totbit的取值。如果让bs->totbit8求余,余数为0说明,当前要被操作的这个比特是一个新字节的起点。%是求余运算,ALIGNING是常量8

       if (aligning)

如果余数不为0,说明刚要被操作的比特不是以字节为边界的,比如bs->totbit14,而我们知道第0816个比特是以字节为边界的,为了查找同步头,我们希望一开始就把比特位置定位在字节边界。就像刚才比如的bs->totbit14,从这个位置开始查找11个连续的1,即使找到也没有意义。所以要先进行下面的操作:

              getbits(bs, (int)(ALIGNING-aligning));

我们要再提出两个比特让bs->totbit变成16(每提出一个比特,相应的bs->totbit会加1,这是提取比特函数完成的工作)。这里用到了一个新的函数getbits是比特提取函数,在下面介绍。

              val = getbits(bs, N);

定位到字节边界后,以后每次从文件中提取11个比特,只要这11个比特是同步头,就说明解码起点找到了。但是请注意J程序中val = getbits(bs, N)N实际上是12,程序每次提出了12个比特来进行比对,这主要是因为如果不这样,万一提出的11位数据不是同步头的时候,还得重复进行比特字节边界定位的操作,会变得麻烦。而如果一次提出12位,即使不是同步头,重新进行比特字节边界定位的操作也比较方便用如下的3句话完成(自己分析这3句话哈,不难的呵呵,提示maxi是上面定义好的0xfff

              while (((val&maxi) != sync) && (!end_bs(bs))) {

              val <<= ALIGNING;

              val |= getbits(bs, ALIGNING);

       }

 

       if (end_bs(bs))

              return(0);

       else

              return(1);

}

4:函数getbits分析

unsigned long getbits(Bit_stream_struc *bs, int N)

{

J该函数是比特分解的核心函数,静下心看还是看得懂的,加油

该函数的操作对象是结构体指向的文件,以及需要取出的比特个数N

       unsigned long val=0;

       register int i;

       register int j = N;         

       register int k, tmp;

       if (N > MAX_LENGTH)

程序设定最多一次只能取出32个比特,MAX_LENGTH是常量32

              printf("Cannot read or write more than %d bits at a time.\n", MAX_LENGTH);

       bs->totbit += N;

取出多少个比特,结构体中相应的比特位置参数就要相应的计数累加,事实上字节位置也要计数累加,就是每取出8个比特,字节位置变化1,但由于程序中往往取出的比特个数是不一定的,所以字节位置的计数比较困难,相对麻烦一些

       while (j > 0) {

如果要取出的比特个数不为0,那么进行如下的操作:比特位置的计数很方便上面已经实现,字节位置的计数要麻烦一些:要借助到变量bs->buf_bit_idx,具体实现看下面:(至于为什么要用到字节位置的计数,那是因为缓冲器是一字节位单位组织数据的)

              if (!bs->buf_bit_idx) {

注意bs->buf_bit_idx的取值是[0-7],比特位置一直累加,bs->buf_bit_idx就一直在[0-7]区间里面循环。如果bs->buf_bit_idx0,说明循环一次了,已经有一个字节长度被处理过了。

以下的代码循环相套可读性很差,得有点耐心分析。。。。。。

为方便分析,在这里加了编号:

                     1 bs->buf_bit_idx = 8;

                     2 bs->buf_byte_idx--;

                     3 if ((bs->buf_byte_idx < MINIMUM) || (bs->buf_byte_idx < bs->eob)) {

                     4     if (bs->eob)

                     5            bs->eobs = TRUE;

                     6     else {

                     7            for (i=bs->buf_byte_idx; i>=0;i--)

                     8                   bs->buf[bs->buf_size-1-bs->buf_byte_idx+i] = bs->buf[i];

                     9                   refill_buffer(bs);

                     10           bs->buf_byte_idx = bs->buf_size-1;

                            }

                     }

              }

刚开始的时候buf_byte_idx初始化为0,那么经过语句2,则它一定是个小于0的数,肯定通过3句的判断,继而执行第4句,以开始的时候,buffer里还没调入数据,所以eof(end of buffer)肯定是false所以不执行5句,而直接执行7句,有因为这次的buf_byte_idx也不符合7句的判断,所以不会执行8句,而开始9句的执行。(9句的目的是把数据调入buffer中,填满buffer,eof为真),然后执行10bs->buf_byte_idx = bs->buf_size-1;(显然bs->buf_byte_idx的计数方式与bs->totbit的计数方向正好相反。

等第二次循环到这里的时候

2句的执行说明,已经处理完了一个字节,bs->buf_byte_idx计数减1   如果bs->buf_byte_idx值已经小于MINIMUM(常量4)抑或已经是负值的时候,表明这一帧的数据已经处理完毕,令bs->eobs为真

              11 k = MIN(j, bs->buf_bit_idx);

              12 tmp = bs->buf[bs->buf_byte_idx]&putmask[bs->buf_bit_idx];

              13 tmp = tmp >> (bs->buf_bit_idx-k);

              14 val |= tmp << (j-k);

              15 bs->buf_bit_idx -= k;

              16 j -= k;

[11-16]的含义是:如果要取出的比特数小于8,比如是5,那么从缓冲区buffer中取出第buf_byte_idx个字节数据(注意缓冲区里的数据应该是从高位相低位开始填充的,否则没有办法解释,有待确定),通过逻辑与运算只保留前5为比特(在高位)。(这里putmask[bs->buf_bit_idx]是个数组,int putmask[9]={0x0, 0x1, 0x3, 0x7, 0xf, 0x1f, 0x3f, 0x7f, 0xff};)。保留后左移3位,存入32为的变量val中,因为值取了5位,没有超过8位,所以buf_byte_idx不变

如果取出的比特数大于8,比如25,那么。。。。。下面的分析你自己搞定哈,表达出来太麻烦了,呵呵,现在体会到什么是只可意会了呵呵。额外话:春节快乐!!!

       }

       return val;

}

 

5:函数refill_buffer 分析

void refill_buffer(Bit_stream_struc *bs)

{

       register int i=bs->buf_size-2-bs->buf_byte_idx;

       register unsigned long n=1;

 

       while ((i>=0) && (!bs->eob)) {

       在这里如果(i>=0) && (!bs->eob),说明buffer有空余的空间

             n=fread(&bs->buf[i--], sizeof(unsigned char), 1, bs->pt);

那么从文件中取出数据一次来填这个缓冲区buffer,通过反复执行达到填满buffer的目的

              if (!n)

              bs->eob= i+1;

直到填满后,将没办法再填入,返回值n开始变为0,于是bs->eob= 真,并跳出程序

       }

}

6:函数decode_info 分析

void decode_info(Bit_stream_struc *bs, frame_params *fr_ps)

{     该函数的操作对象是结构体bs所映像的文件,

将解码出来的信息存放在帧结构体fr_ps

layer *hdr = fr_ps->header;

这句话是辅助用的,他的使用让hdr->version = get1bit(bs);

等效于(fr_ps->header->version= get1bit(bs);

    hdr->version = get1bit(bs);(取出同步头后,接着取出的1个比特是版本信息

    hdr->lay = 4-getbits(bs,2); (再接着取出的2个比特是协议层数的信息)

    hdr->error_protection = !get1bit(bs); /* error protect. TRUE/FALSE */

    hdr->bitrate_index = getbits(bs,4);

    hdr->sampling_frequency = getbits(bs,2);

    hdr->padding = get1bit(bs);

    hdr->extension = get1bit(bs);

    hdr->mode = getbits(bs,2);

    hdr->mode_ext = getbits(bs,2);

    hdr->copyright = get1bit(bs);

    hdr->original = get1bit(bs);

    hdr->emphasis = getbits(bs,2);

注意这里有个新的函数get1bit(bs),其实功能等于getbits(bs,1),所有就不再额外分析

}

7:函数hdr_to_frps 分析

void hdr_to_frps(frame_params *fr_ps)

{

       layer *hdr = fr_ps->header;     /* (or pass in as arg?) */

 

       fr_ps->actual_mode = hdr->mode;

       fr_ps->stereo = (hdr->mode == MPG_MD_MONO) ? 1 : 2;

       fr_ps->sblimit = SBLIMIT;

       if(hdr->mode == MPG_MD_JOINT_STEREO)

              fr_ps->jsbound = js_bound(hdr->lay, hdr->mode_ext);

       else

              fr_ps->jsbound = fr_ps->sblimit;

}

8:函数buffer_CRC分析

void  buffer_CRC(Bit_stream_struc *bs, unsigned int *old_crc)

{

           *old_crc = getbits(bs, 16);

}

 

9:函数III_get_side_info分析

void III_get_side_info(Bit_stream_struc *bs, III_side_info_t *si, frame_params *fr_ps)

{

该函数的操作对象是文件结构体bs指向的文件,根据帧结构体fr_ps的相关信息,从文件中提取边信息存放到边信息结构体III_side_info_t *si中管理。

       int ch, gr, i;

       int stereo = fr_ps->stereo;

       si->main_data_begin = getbits(bs, 9);

边信息紧接在头信息之后,接在头信息后面的9位是第一个边信息即语音主信息开始位置,取出后存放在si->main_data_begin

       if (stereo == 1)

       说明:stereo在帧解码出来的头信息中,标志该帧是单声道,还是双声道

如果是单声道那么文件的数据组织形式是,标识语音主信息开始位置的9位比特之后,接着的5位是private_bits信息(解码中没用,呵呵摆设用的)。如果是双声道的,那么只有接着的3位是private_bits信息

              si->private_bits = getbits(bs,5);

       else

              si->private_bits = getbits(bs,3);

       for (ch=0; ch

              for (i=0; i<4; i++)

                     si->ch[ch].scfsi[i] = get1bit(bs);

紧接着取出比例因子解码需要的系数scfsi,每个声道有4个比例因子系数,每个比例因子是1位比特信息。注意此后的信息解码是以颗粒位单位来进行的。一帧中有一个或者两个声道,每个声道有1152个采样数据,协议把这1152个数据分为两个颗粒来封装编码。颗粒编码得到的相应的一些系数被放到边信息中的比例因子解码信息之后。对这一部分的系数的解码结果自然要对应相应的颗粒来存放。以下的程序是个循环体,这里取gr=0,ch=0,来简单说明这个循环体的作用:

       for (gr=0; gr<2; gr++) {

              for (ch=0; ch

                     si->ch[ch].gr[gr].part2_3_length = getbits(bs, 12);

从文件中在取出12个比特作为第1个声道的第一个颗粒的part2_3_length  系数,这个系数的作用是:表识1区的大小,上面已经介绍过了,1个颗粒里面有576个数据。实际上采样数据被分为32个子带,每个子带经过mdct变换后得到18个数据组成的32*18=576个系数。这576个系数经过霍夫曼编码后得到这576个数据。在编码的时候,为了方便解码这576个数据分为3个区,大值区(big_value,1区,0区。大值区又被细分位3个小区:0号区,1号区,2号区。分区的目的是为了霍夫曼解码的速度。具体机制,huhu俺还没彻底清楚,可能的话,呵呵有你来讲。

                     si->ch[ch].gr[gr].big_values = getbits(bs, 9);

              参数big_values用来标识从主数据开始的多少字节是属于大值区

                     si->ch[ch].gr[gr].global_gain = getbits(bs, 8);

              参数global_gain是全局增益因子,解码计算用到

                     si->ch[ch].gr[gr].scalefac_compress = getbits(bs, 4);

              参数scalefac_compress比例因子压缩系数,也是后面的计算用到

                     si->ch[ch].gr[gr].window_switching_flag = get1bit(bs);

参数window_switching_flag带窗标志,后面的解码常常根据这个标志作一些判断,是否带窗决定了不同的解码方法。窗的具体含义还没有彻底明白,应该跟子带分解,滤波,子带合成,信号处理等有关系。

                     if (si->ch[ch].gr[gr].window_switching_flag) {

                            si->ch[ch].gr[gr].block_type = getbits(bs, 2);

                            si->ch[ch].gr[gr].mixed_block_flag = get1bit(bs);

                            for (i=0; i<2; i++)

                                   si->ch[ch].gr[gr].table_select[i] = getbits(bs, 5);

                            for (i=0; i<3; i++)

                                   si->ch[ch].gr[gr].subblock_gain[i] = getbits(bs, 3);

如果该颗粒是带窗的那么进行上述的解码。table_select[i]是霍夫曼解码表的选择:协议提供了32个表来提供霍夫曼解码使用,具体用那个表,与该参数table_select[i]相关

                            }

                            if (si->ch[ch].gr[gr].block_type == 0) {

                                   printf("Side info bad: block_type == 0 in split block.\n");

                                   exit(0);

                            else if (si->ch[ch].gr[gr].block_type == 2

                                          && si->ch[ch].gr[gr].mixed_block_flag == 0)

                                   si->ch[ch].gr[gr].region0_count = 8; /* MI 9; */

                            else si->ch[ch].gr[gr].region0_count = 7; /* MI 8; */

si->ch[ch].gr[gr].region1_count = 20 - si->ch[ch].gr[gr].region0_count;

上面这些语句的含义是根据窗的类型,来确定大值区中0号,1号,2号区的起始与结束区间。

                     }

                     else {

如果颗粒是不带窗的,那么大值区中0号,1号,2号区的起始与结束区间又是一种新的分布如下:

                            for (i=0; i<3; i++)

                                   si->ch[ch].gr[gr].table_select[i] = getbits(bs, 5);

                            si->ch[ch].gr[gr].region0_count = getbits(bs, 4);

                            si->ch[ch].gr[gr].region1_count = getbits(bs, 3);

                            si->ch[ch].gr[gr].block_type = 0;

                     }

                     si->ch[ch].gr[gr].preflag = get1bit(bs);

                     si->ch[ch].gr[gr].scalefac_scale = get1bit(bs);

                     si->ch[ch].gr[gr].count1table_select = get1bit(bs);

         }

       }

}

10:函数main_data_slots分析

int main_data_slots(frame_params fr_ps)

{

       int nSlots;

       nSlots = (144 * bitrate[2][fr_ps.header->bitrate_index])

                     / s_freq[fr_ps.header->sampling_frequency];

       在协议中,帧的长度是固定的。用上述的计算公式可以计算出来

       在协议中,帧数据扣掉头信息和边信息后剩下的信息称为 (Audio Data)

       在解码过程中需要先将Audio Data另存到一个缓冲区中,以方便解码

       下面的操作是为了计算Audio Data的长度,通过帧长度减去头信息,边信息长度

       注意nSlots表征的是多少字节,而不是多少比特

       if (fr_ps.header->padding) nSlots++;

       nSlots -= 4;

       if (fr_ps.header->error_protection)

              nSlots -= 2;

       if (fr_ps.stereo == 1)

              nSlots -= 17;

       else

              nSlots -=32;

       return(nSlots);

}

11:函数hputbuf分析

void hputbuf(unsigned int val, int N)

{

       if (N != 8) {

              printf("Not Supported yet!!\n");

              exit(-3);

       }

       buf[offset % BUFSIZE] = val;

       offset++;

}

12:函数III_get_scale_factors分析

void III_get_scale_factors(III_scalefac_t *scalefac, III_side_info_t *si, int gr, int ch, frame_params *fr_ps)

{    

以下的代码理解难度不大,让人感觉纷乱的是mp3里面参数之间错综复杂的关系,这个函数的作用无非就是根据已经得到的一些信息(比如是窗类型,比例因子解码系数等),从全局buf中取出规定位数的比特作为相应的比例因子。更细节的描述,即使写出来呵呵,你也未必有耐心看。

       int sfb, i, window;

       struct gr_info_s *gr_info = &(si->ch[ch].gr[gr]);

 

       if (gr_info->window_switching_flag && (gr_info->block_type == 2)) {

              if (gr_info->mixed_block_flag) { /* MIXED */ /* NEW - ag 11/25 */

                     for (sfb = 0; sfb < 8; sfb++)

                            (*scalefac)[ch].l[sfb] = hgetbits(

                                   slen[0][gr_info->scalefac_compress]);

                     for (sfb = 3; sfb < 6; sfb++)

                            for (window=0; window<3; window++)

                                   (*scalefac)[ch].s[window][sfb] = hgetbits(

                                          slen[0][gr_info->scalefac_compress]);

                     for (sfb = 6; sfb < 12; sfb++)

                            for (window=0; window<3; window++)

                                   (*scalefac)[ch].s[window][sfb] = hgetbits(

                                          slen[1][gr_info->scalefac_compress]);

                     for (sfb=12,window=0; window<3; window++)

                            (*scalefac)[ch].s[window][sfb] = 0;

              }

              else {  /* SHORT*/

                     for (i=0; i<2; i++)

                            for (sfb = sfbtable.s[i]; sfb < sfbtable.s[i+1]; sfb++)

                                   for (window=0; window<3; window++)

                                          (*scalefac)[ch].s[window][sfb] = hgetbits(

                                                 slen[i][gr_info->scalefac_compress]);

                            for (sfb=12,window=0; window<3; window++)

                                   (*scalefac)[ch].s[window][sfb] = 0;

              }

       }

       else {   /* LONG types 0,1,3 */

              for (i=0; i<4; i++) {

                     if ((si->ch[ch].scfsi[i] == 0) || (gr == 0))

                            for (sfb = sfbtable.l[i]; sfb < sfbtable.l[i+1]; sfb++)

                                   (*scalefac)[ch].l[sfb] = hgetbits(

                                          slen[(i<2)?0:1][gr_info->scalefac_compress]);

              }

              (*scalefac)[ch].l[22] = 0;

       }

}

霍夫曼解码函数分析

1:函数III_hufman_decode分析

void III_hufman_decode(long int is[SBLIMIT][SSLIMIT], III_side_info_t *si, int ch, int gr, int part2_start, frame_params *fr_ps)

参量说明:ais 是一个32*18的二维数组用来存放解码后的576个数据,既是PCM数据经过频率变换得到的系数。

(b)*si是边信息结构体的入口指针:解码过程需要一些边信息中的系数

(c)ch是声道标示,gr是颗粒标示,解码过程是以没个声道的颗粒为单位进行的

(d)part2_start,解码的位置依据

(e)*fr_ps是帧参量的入口指针:解码过程需要诸如声道数,协议层次的信息来源

{

             int i, x, y;

             int v, w;

            struct huffcodetab *h;

这是一个结构体的定义:它将统一对若干个霍夫曼解码表进行管理。

            int region1Start;

            int region2Start;

int bt = (*si).ch[ch].gr[gr].window_switching_flag && ((*si).ch[ch].gr[gr].block_type == 2);

            initialize_huffman();

函数initialize_huffman的作用:

(1)                霍夫曼解码初始化:打开协议定义好的霍夫曼解码树表(该表中存在34各子表),在理解之后的内容时,请一定一边读程序,一边参考这个霍夫曼解码树表文件,该文件放在附录里

(2)                取出霍夫曼解码树表文件中34个子表的内容以便于解码使用

            /* Find region boundary for short block case. */

            if ( ((*si).ch[ch].gr[gr].window_switching_flag) &&

        ((*si).ch[ch].gr[gr].block_type == 2) ) {

如果相应颗粒的窗口标志为1并且块类型是2的话那么等于说明这个颗粒是短块,它的576个系数中前36个数属于0区,从36576的系数属于1区。并且不存在属于2区的系数。所在区不同,需要的解码表不同,要根据区从34个表中选出合适的表来解码。

        region1Start = 36;  /* sfb[9/3]*3=36 */

          region2Start = 576; /* No Region2 for short block case. */

                   }

   else {          /* Find region boundary for long block case. */

相反如果不符合上述的条件就是长块,长块的数据区分布情况是:

      region1Start = sfBandIndex[fr_ps->header->sampling_frequency]

                           .l[(*si).ch[ch].gr[gr].region0_count + 1]; /* MI */

      region2Start = sfBandIndex[fr_ps->header->sampling_frequency]

                              .l[(*si).ch[ch].gr[gr].region0_count +

                              (*si).ch[ch].gr[gr].region1_count + 2]; /* MI */

注意:

(1)                   首先要了解sfBandIndex这一特殊的数据结构。可以理解它是一个特殊的数组,在这个数组里面还包含了两个子数组l[n]s[n]

(2)                   根据头信息的采样率和边信息中的相关参数可以确定0区,1区,2区的范围

(3)                   576个系数大体上分为大值区,1号区和零区。大值区又分为012三个小区

(4)                   大值区解码一次得到两个数据,1号区解码一次得到4个数据。

 

      }

   /* Read bigvalues area. */

   for (i=0; i<(*si).ch[ch].gr[gr].big_values*2; i+=2) {

(1)                   循环执行big_values次以下的代码,big_values是边信息中的一个参数。

(2)                   大数值区是成对出现的

(3)                   上面已经介绍过h是结构体统一对若干个霍夫曼解码表进行管理。

(4)                   结果霍夫曼解码表的解读后,解码表数据已经存入了变量ht[n].val中,ht是个全局的管理霍夫曼解码表的结构体

      if      (i

      else if (i

           else                h = &ht[(*si).ch[ch].gr[gr].table_select[2]];

              先根据数据的位置选择相应的用来解码的霍夫曼表

      huffman_decoder(h, &x, &y, &v, &w);

函数huffman_decoder的作用就是霍夫曼解码核心程序,解码的数据通过下面的2句,保存到变量is

      is[i/SSLIMIT][i%SSLIMIT] = x;

      is[(i+1)/SSLIMIT][(i+1)%SSLIMIT] = y;

      }

大值区的霍夫曼解码到此为止,存放在is数组中

   /* Read count1 area. */

1号区的霍夫曼解码:

   h = &ht[(*si).ch[ch].gr[gr].count1table_select+32];

先选择适当的霍夫曼解码表

   while ((hsstell() < part2_start + (*si).ch[ch].gr[gr].part2_3_length ) &&

     ( i < SSLIMIT*SBLIMIT )) {

当位比特的位置在1号区范围的时候并且i < SSLIMIT*SBLIMIT的时候,始终执行如下的动作:

      huffman_decoder(h, &x, &y, &v, &w);

      is[i/SSLIMIT][i%SSLIMIT] = v;

      is[(i+1)/SSLIMIT][(i+1)%SSLIMIT] = w;

      is[(i+2)/SSLIMIT][(i+2)%SSLIMIT] = x;

      is[(i+3)/SSLIMIT][(i+3)%SSLIMIT] = y;

      i += 4;

      }

 

   if (hsstell() > part2_start + (*si).ch[ch].gr[gr].part2_3_length)

如果比特位置已经到了0值区,进行0值区的解码前重新组织一下缓冲区,以便于下一帧的解码。

   {  i -=4;

      rewindNbits(hsstell()-part2_start - (*si).ch[ch].gr[gr].part2_3_length);

   }

 

   /* Dismiss stuffing Bits */

   if ( hsstell() < part2_start + (*si).ch[ch].gr[gr].part2_3_length )

      hgetbits( part2_start + (*si).ch[ch].gr[gr].part2_3_length - hsstell());

 

   /* Zero out rest. */

   for (; i

      is[i/SSLIMIT][i%SSLIMIT] = 0;

}

2:函数initialize_huffman分析

       void initialize_huffman()

{

该函数的作用就是打开协议定义好的霍夫曼解码树表(该表中存在34各子表)MPEG标准通过大量的统计已经为霍夫曼编码作了规定,也为其解码制作了相应的解码表以供查表。在理解之后的内容时,请一定先看一下这个霍夫曼解码树表文件结构,该文件放在附录里

另一个作用读取霍夫曼解码树表中的数据

       FILE *fi;

       if (huffman_initialized) return;

       if (!(fi = OpenTableFile("huffdec.txt") )) {

       如果打开名为huffdec.txt霍夫曼解码树表文件出错则提示并推出

              printf("Please check huffman table 'huffdec.txt'\n");

              exit(1);

       }

       if (read_decoder_table(fi) != HTN) {

函数read_decoder_table的作用是读取文件指针fi指向的霍夫曼解码树表文件,从中读出相应的数据,如果读取失败则提示出错

              fprintf(stderr,"decoder table read error\n");

              exit(4);

       }

       huffman_initialized = TRUE;

}