Android利用stagefright进行音视频文件的解析,其中OggExtractor类继承MediaExtractor类。ogg是一种多媒体容器,官网(http://www.xiph.org/)提供免费开源的音视频格式。OGG每页之间相互独立,都包含了各自应有的信息,页的大小是可变的,通常为4-8KB,最大值不超过65307字节(27+255+255×255=65307)。页由页头部(pageheader)和页数据(pagedata)组成。OggExtractor.cpp中的readPage()函数会读取页头部,页头部总共27个字节。
页结构
struct Page {
uint64_t mGranulePosition;
int32_t mPrevPacketSize;
uint64_t mPrevPacketPos;
uint32_t mSerialNo;
uint32_t mPageNo;
uint8_t mFlags;
uint8_t mNumSegments;
uint8_t mLace[255];
};
ssize_t MyOggExtractor::readPage(off64_t offset, Page *page) {
uint8_t header[27];
ssize_t n;
if ((n = mSource->readAt(offset, header, sizeof(header)))
< (ssize_t)sizeof(header)) {
ALOGV("failed to read %zu bytes at offset %#016llx, got %zd bytes",
sizeof(header), (long long)offset, n);
if (n < 0) {
return n;
} else if (n == 0) {
return ERROR_END_OF_STREAM;
} else {
return ERROR_IO;
}
}
if (memcmp(header, "OggS", 4)) {
return ERROR_MALFORMED;
}
if (header[4] != 0) {
// Wrong version.
return ERROR_UNSUPPORTED;
}
page->mFlags = header[5];
if (page->mFlags & ~7) {
// Only bits 0-2 are defined in version 0.
return ERROR_MALFORMED;
}
page->mGranulePosition = U64LE_AT(&header[6]);
page->mSerialNo = U32LE_AT(&header[14]);
page->mPageNo = U32LE_AT(&header[18]);
page->mNumSegments = header[26];
if (mSource->readAt(
offset + sizeof(header), page->mLace, page->mNumSegments)
< (ssize_t)page->mNumSegments) {
return ERROR_IO;
}
size_t totalSize = 0;;
for (size_t i = 0; i < page->mNumSegments; ++i) {
totalSize += page->mLace[i];
}
return sizeof(header) + page->mNumSegments + totalSize;
}
OGG 页结构
--------------------------------------------------------------------------------
域名称 占用字节 描述
--------------------------------------------------------------------------------
capture_pattern 4 页标识,"OggS"的ASCII字符 4F 67 67 53
structure_version 1 版本ID,当前版本默认=0
Header_type_flag 1 页头部类型
Granule_position 8 区段位置
Serial_number 4 逻辑流的序列号
Page_seguence_number 4 本页在逻辑流的序号,OGG解码器据此识别有无页丢失。
CRC_cbecksum 4 循环冗余校验码校验和
Number_page_segments 1 本页的区段数量,指明区段表中有多少个区段长度,≤255
Segment_table ≤255 区段长度表,每个字节表示一个区段的长度
--------------------------------------------------------------------------------
说明:
①页标识:标识着一个页的开始。其作用是分离Ogg封装格式还原媒体编码时识别新页。
②头部类型1字节8位值前3位的意义:
第1位=1:本页的媒体编码数据与前一页属于同一个逻辑流的同一个包,=0:表示本页是新包。
第2位=1:本页为逻辑流的第一页bos;=0:不是第一页。
第3位=1:本页为逻辑流的最后一页eos;=0:不是最后一页。
③区段位置不是指区段在文件中的位置,而是指区段在逻辑流中的位置。它存储了媒体编码相关的参数信息,对于音频流来说,它存储着到本页为止逻辑流在PCM输出中采样码的数目,可以由它来算得时间戳。对于视频流来说,它存储着到本页为止视频帧编码的数目。若此值=0,表示截止到本页,逻辑流的包未结束。
④流序列号,即本页所在的流ID,它是区分本页所属逻辑流与其他逻辑流的序号,可以通过这个值来划分流。
⑤循环冗余校验码校验和包含页的32位CRC校验(头部零CRC校验、页数据校验)的产生多项式为:0x04c11db7。
⑥区段长度表记录着逻辑流中的每个包中每个区段的长度值,取值范围是0-255。包中的最后一个区段长度<255,其它区段长度都=255。这些值以区段出现的先后顺序排列。此域的字节数是区段数量域所表示的数字,即在0-255字节之间。从区段长度表中可以计算出每个包的长度,例如:区段表中的值为 4D FF 45 FF FF FF 40 FF FF 66,那么:
第一包有1个区段,总长度=4D
第二包有2个区段,总长度=FF+45
第三包有4个区段,总长度=FF+FF+FF+40
第四包有3个区段,总长度=FF+FF+66
⑦页头部的长度和整个页的长度计算:
页头部长度=27+区段数量
页长度=页头长度+区段长度表中每个区段的大小=页头部长度+所有区段长度之和。
⑧页头部后面紧接着页数据,页数据包括本页所有的区段数据。