文档章节

(七)用JAVA编写MP3解码器——解码帧边信息

暗之幻影
 暗之幻影
发布于 2015/01/04 15:29
字数 1321
阅读 38
收藏 0

解码一帧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版帧数据的不同之处:

  • 标准立体声编码的MPEG 1.0的粒度组为2,解码得到的PCM样本值个数为2304;而MPEG 2.0/2.5的粒度组为1,解码得到的PCM样本值个数为1157。这将导致解码一帧不同版的MPEG Audio向音频硬件写入的PCM数据长度不同。
  • MPEG 1.0的两个粒度组(每个粒度组内1或2个声道)的增益因子可能共用,如果两个粒度组的增益因子值是相同的,就不用分别存储两份,这样可以提高帧数据的存储效率。是否共用由帧边信息的第6..9比特(2个声道)或4..7比特(单声道)每一比特的值是否为1给出,这个值暂存在SideInfo的scfsi变量中;MPEG 2.0/2.5只有一个粒度组,就不存在共用的问题了。“共用”导致解码增益因子的代码比较复杂。
  • 正是由于MPEG1.0和2.0/2.5粒度组的不同,为SideInfo中与增益因子相关的变量(取名为scalefacxxx)赋值时从位流中取得的比特数不同。
  • 为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=====================================================

 

 

上一篇:(六)用JAVA编写MP3解码器——帧数据结构

下一篇:(八)用JAVA编写MP3解码器——解码增益因子

 

【本程序下载地址】http://jmp123.sourceforge.net/

本文转载自:http://lfp001.iteye.com/blog/742913

暗之幻影
粉丝 20
博文 377
码字总数 71245
作品 0
南京
高级程序员
私信 提问
(五)用JAVA编写MP3解码器——解析文件信息

前文提到解析MP3标签,程序源码中也已经出现了调用解析MP3标签、打印MP3文件信息的功能,这儿先说说MP3文件信息的解析。 解析MP3的文件信息对MP3解码器来说只是一个附加功能,如果不加入这部...

暗之幻影
2015/01/04
61
0
疯狂Spring Cloud连载(11)——Feign的编码器与解码器

本文节选自《疯狂Spring Cloud微服务架构实战》 京东购买地址:https://item.jd.com/12256011.html 当当网购买地址:http://product.dangdang.com/25201393.html Spring Cloud教学视频:htt...

杨大仙的程序空间
2017/10/23
3.1K
5
直播,音视频编码器和解码器(EasyDarwin)-Android

使用摄像头采集视频数据,并通过MediaCodec进行H264编码,之后打包成RTSP格式并上传的。 TextuewView也提供了一个setTransform方法,该方法接收一个matrix参数,使用该参数对当前的渲染内容进...

shareus
2018/05/18
0
0
Java8 BASE64编解码

Java8 BASE64编解码 Base64是一种用64个字符来表示任意二进制数据的方法。 Base64是一种最常见的二进制编码方法。 Java一直缺少BASE64编码 API,以至于通常在项目开发中会选用第三方的API...

秋风醉了
2015/04/02
1K
0
android ijkplayer c层分析-prepare过程与读取线程

ijkplayer现在比较流行,因为工作关系,接触了他,现在做个简单的分析记录吧。我这里直接跳过java层代码,进入c层,因为大多数的工作都是通过jni调用到c层来完成的,java层的内容并不是主体功...

机械面条
09/29
0
0

没有更多内容

加载失败,请刷新页面

加载更多

爬虫可以采集哪些数据?爬虫借用什么代理可以提高效率

学习爬虫的门槛非常低,特别是通过Python学习爬虫,即使是网上也能找到许多学习爬虫的方法,而且爬虫在数据采集方面效果比较好,比如可以采集几万、上百万网页数据进行分析,带来极有价值的数...

xiaotaomi
32分钟前
4
0
redis自建笔记

自建redis笔记 最近在linux安装了一下redis,特做一些笔记! 本文先单节点启动redis,然后再进行持久化配置,在次基础上,再分享搭建主从模式的配置以及Sentinel 哨兵模式及集群的搭建 单节点...

北极之北
33分钟前
4
0
没想到Spring Boot居然这么耗内存,有点惊讶

Spring Boot总体来说,搭建还是比较容易的,特别是Spring Cloud全家桶,简称亲民微服务,但在发展趋势中,容器化技术已经成熟,面对巨耗内存的Spring Boot,小公司表示用不起。如今,很多刚诞...

程序员修BUG
37分钟前
5
0
Spring Security 实战干货:Spring Boot 中的 Spring Security 自动配置初探

1. 前言 我们在前几篇对 Spring Security 的用户信息管理机制,密码机制进行了探讨。我们发现 Spring Security Starter相关的 Servlet 自动配置都在spring-boot-autoconfigure-2.1.9.RELEASE...

码农小胖哥
39分钟前
4
0
Docker 容器时区时间不一致 问题解决

解决方案: 1,最傻瓜也最方便的处理方式,运行新的容器前设置本机时区和时间文件与容器的映射 docker run -v /etc/timezone:/etc/timezone -v /etc/localtime:/etc/localtime ...1 -v /etc/...

突突突酱
40分钟前
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部