解码一帧Layer3第1步:帧边信息解码 -- getSideInfo()方法
帧边信息用于描述一帧内的主信息(增益因子和主数据)特征,而后文讲到的解码的增益因子、主数据是描述一个声道的,换一种说法,帧边信息每帧连续存储在一处,增益因子和主数据(可能多达各4个)分散存储在每帧数据中的2处(MPEG 1.0的单声道/MPEG 2.0/2.5的2声道)或4处(MPEG 1.0的2个声道)。弄明白了这一点区别,就很容易弄明白后文中讲到的Layer3的decodeFrame方法的流程。
解码帧边信息先实例化一个BitStream对象bsSI,bsSI已经在Layer3的构造方法中初始化。从上一讲的帧数据结构示意图可以看出,对某一帧的帧头、帧边信息解码后,这些位流数据在后续过程中就不再被使用,而当前帧的主信息有可能是下一帧的,即解码下一帧才用到到,所以需要暂存起来。要将帧边信息读入到36字节长度的缓冲区内;主信息暂存在长度不低于1732+512字节的另一缓冲区内,这里的512是何来历?请看源代码中的objSI.main_data_begin = bsSI.getBits9(9)这一行就明白了,main_data_begin值的单位为字节。
帧边信息的解码结果暂存到SideInfo类型变量objSI中,供后续步骤使用。在前面的帧子中,先后有好几人说到我的代码中变量命名不合JAVA规范,代码中(比如class SideInfo的成员)变量名基本上采用了手册描述MP3解码规范里的名称或简写,尽量做到“见名知义”,比如part2_3_length就是指一个声道内增益因子(part2)和主数据(part3)的比特数;part1在哪?你说呢~~
class SideInfo内定义的成员变量表示的具体含义在手册中查得到,不再赘述,解码帧边信息的流程特简单,从源码很容易看懂帧边信息的结构。要把这些成员变量的含义、作用阐述清楚并非易事,没几千字下不来,其中个别变量在后续的过程中使用到这些变量的值时会讲解到。从这儿的源代码也可以看出MPEG 1.0版和2.0/2.5版帧数据的不同之处:
class Layer3内的getSideInfo()方法的源码如下:
//1. //>>>>SIDE INFORMATION===================================================== private static SideInfo objSI; private BitStream bsSI; // 读帧边信息位流 private boolean getSideInfo() throws Exception { int ch, gr, i; bsSI.resetIndex(); i = objHeader.getSideInfoSize(); if(bsSI.append(i) < i) return false; Layer3.GRInfo gri; if (isMPEG1) { objSI.main_data_begin = bsSI.getBits9(9); if (intChannels == 1) bsSI.getBits9(5); //private_bits else bsSI.getBits9(3); //private_bits int[] scfsi; for (ch = 0; ch < intChannels; ch++) { scfsi = objSI.ch[ch].scfsi; scfsi[0] = bsSI.get1Bit(); scfsi[1] = bsSI.get1Bit(); scfsi[2] = bsSI.get1Bit(); scfsi[3] = bsSI.get1Bit(); } for (gr = 0; gr < 2; gr++) { for (ch = 0; ch < intChannels; ch++) { gri = objSI.ch[ch].gr[gr]; gri.part2_3_length = bsSI.getBits17(12); gri.big_values = bsSI.getBits9(9); gri.global_gain = bsSI.getBits9(8); gri.scalefac_compress = bsSI.getBits9(4); gri.window_switching_flag = bsSI.get1Bit(); if ((gri.window_switching_flag) != 0) { gri.block_type = bsSI.getBits9(2); gri.mixed_block_flag = bsSI.get1Bit(); gri.table_select[0] = bsSI.getBits9(5); gri.table_select[1] = bsSI.getBits9(5); gri.subblock_gain[0] = bsSI.getBits9(3); gri.subblock_gain[1] = bsSI.getBits9(3); gri.subblock_gain[2] = bsSI.getBits9(3); if (gri.block_type == 0) return false; else if (gri.block_type == 2 && gri.mixed_block_flag == 0) gri.region0_count = 8; else gri.region0_count = 7; gri.region1_count = 20 - gri.region0_count; } else { gri.table_select[0] = bsSI.getBits9(5); gri.table_select[1] = bsSI.getBits9(5); gri.table_select[2] = bsSI.getBits9(5); gri.region0_count = bsSI.getBits9(4); gri.region1_count = bsSI.getBits9(3); gri.block_type = 0; } gri.preflag = bsSI.get1Bit(); gri.scalefac_scale = bsSI.get1Bit(); gri.count1table_select = bsSI.get1Bit(); } } } else { // MPEG 2.0/2.5 objSI.main_data_begin = bsSI.getBits9(8); if (intChannels == 1) bsSI.get1Bit(); else bsSI.getBits9(2); for (ch = 0; ch < intChannels; ch++) { gri = objSI.ch[ch].gr[0]; gri.part2_3_length = bsSI.getBits17(12); gri.big_values = bsSI.getBits9(9); gri.global_gain = bsSI.getBits9(8); gri.scalefac_compress = bsSI.getBits9(9); gri.window_switching_flag = bsSI.get1Bit(); if ((gri.window_switching_flag) != 0) { gri.block_type = bsSI.getBits9(2); gri.mixed_block_flag = bsSI.get1Bit(); gri.table_select[0] = bsSI.getBits9(5); gri.table_select[1] = bsSI.getBits9(5); gri.subblock_gain[0] = bsSI.getBits9(3); gri.subblock_gain[1] = bsSI.getBits9(3); gri.subblock_gain[2] = bsSI.getBits9(3); if (gri.block_type == 0) return false; else if (gri.block_type == 2 && gri.mixed_block_flag == 0) gri.region0_count = 8; else { gri.region0_count = 7; gri.region1_count = 20 - gri.region0_count; } } else { gri.table_select[0] = bsSI.getBits9(5); gri.table_select[1] = bsSI.getBits9(5); gri.table_select[2] = bsSI.getBits9(5); gri.region0_count = bsSI.getBits9(4); gri.region1_count = bsSI.getBits9(3); gri.block_type = 0; gri.mixed_block_flag = 0; } gri.scalefac_scale = bsSI.get1Bit(); gri.count1table_select = bsSI.get1Bit(); } } return true; } //<<<<SIDE INFORMATION=====================================================