本篇是JPEG解码系列的第二篇——读数据的底层依赖库。
本人所开发的MiniJpegDecoder项目,主要分为两层,一层是底层依赖库,另外一层是包含jpeg解码逻辑的应用层。
其实,分三层的话结构更为合理:底层库+解码库+应用层。但由于目前没时间维护这个库,等什么时候空闲了再优化一下结构吧。
今天介绍的主要是两层结构的底层库——libcodec_utils.so。
1. 源码组成
源码树结构如下:
utils$ tree
.
|-- ABitReader.cpp
|-- AString.cpp
|-- DataSource.cpp
|-- FileSource.cpp
|-- Makefile
`-- types_def.cpp
需要说明的是,这几个cpp文件都是来源于Android SDK,位于framesworks/av/media目录下。
2. 各模块说明
ABitReader——一个按二进制位读写/查询数据的工具,例如读取2bit二进制位,或者跳过3bit,或者查询当前读的bit位置的offset。
AString——一个用于字符操作的工具,例如追加/插入字符串,或查找某个子字符串。
DataSource——FileSource的父类,用于抽象读取数据的接口。
FileSource——读本地文件的工具,是对open/read系统调用的一层抽象,未使用fopen/fread的标准C库函数。
types_def——64位整形数据转换为网络字节序。
3. 为什么增加加这个底层库?
JpegDecoder模块作为高层模块,期望从本地文件中,按位读取数据再去解码,重点是处理解码逻辑,而不希望看到底层细节处理操作,例如
从文件中如何读若干Bytes数据,再一个个bit位的逻辑操作。因此使用了这些底层子模块,去实现按bit位读取、跳若干bit位、查询当前位在文件中
的offset等底层细节问题。
即高层模块只处理高层的业务逻辑,底层只处理底层的业务逻辑,避免眉毛鼻子一把抓。
底层只负责读文件数据,按bit位方式拿数据。
4. 有无替代方案?
在做这个h264_tools工具时,找到了x264实现的按bit位读取数据的模块——bs_read,这个应该是更简洁和高效一些吧。
5. bit位操作的常用接口
由于解码过程中,常常是对bit位的操作运算,如果用fread从文件中读取一个个字节再拆解和组合为需要的bit位,那是相当低效的。从模块解耦
角度上看,需要一个工具,把jpeg数据读取到一块内存中,按需要从这块内存拿取或跳过一定bit位。这个工具就是ABitReader,移植自Android。
下面是几个常用api及其介绍:
uint32_t getBits(size_t n); //从当前offset出取n bits,返回这n bits的值,之后内部的offset会后移n bits
void skipBits(size_t n); //从当前offset处跳过n bits,之后内部的offset会后移n bits
void putBits(uint32_t x, size_t n); //将n bits的值x,再返回给在读取的一块内存,那么offset会前移n bits
int32_t getOffset() const; //当前offset,其实是当前欲读bit位所在Byte值位置,距内存块首地址的偏移
const uint8_t *data() const; //当前读取bit位置所在的Byte的指针