// ogg_page.mbt
//
// OGG 页面层的读写(为 Vorbis/Opus 的 comment packet 提供容器能力)。
//
// 页面结构(RFC 3533 / Ogg bitstream):
// 0..4 "OggS"
// 4 版本(恒 0)
// 5 header type:bit0=continued, bit1=BOS, bit2=EOS
// 6..14 granule position(8 字节小端)
// 14..18 serial number(4 字节小端)
// 18..22 页序号(4 字节小端)
// 22..26 CRC-32(Ogg 非反射算法,见下)
// 26 段数(lacing count)
// 27.. lacing 值表 + 段载荷
///|
/// OGG 页头长度(固定 27 字节,不含 lacing 表)。
pub const OGG_HEADER_SIZE : Int32 = 27
///|
/// 一个 OGG 页(位置描述)。
pub struct OggPage {
/// 页在文件中的起始偏移。
offset : Int32
/// 段数(lacing count)。
n_segments : Int32
/// 完整页长度(含头与全部段载荷)。
total_len : Int32
/// 段 0 的载荷起始偏移。
body_offset : Int32
/// 是否 BOS。
is_bos : Bool
/// 是否 EOS。
is_eos : Bool
/// 是否 continued。
is_continued : Bool
/// 流序列号。
serial : Int32
/// 页序号。
seq : Int32
/// 原始数据。
data : Bytes
}
///|
/// 从文件头开始解析 OGG 页。
pub fn parse_page(data : Bytes, offset : Int32) -> OggPage raise TagError {
require_range(data, offset, OGG_HEADER_SIZE)
if data.at(offset) != b'O' ||
data.at(offset + 1) != b'g' ||
data.at(offset + 2) != b'g' ||
data.at(offset + 3) != b'S' {
raise NotTagged(format="ogg")
}
let version = data.at(offset + 4).to_int()
if version != 0 {
raise BadHeader(reason="Ogg 版本 \{version} != 0")
}
let header_type = data.at(offset + 5).to_int()
let n_segments = data.at(offset + 26).to_int()
let body_offset = offset + OGG_HEADER_SIZE + n_segments
let mut total = 0
for i in 0.. Int32 {
self.data.at(self.offset + OGG_HEADER_SIZE + i).to_int()
}
///|
/// 把连续若干页的段重组成逻辑 packet(跨页未完成也处理)。
pub fn assemble_packets(data : Bytes, pages : Array[OggPage]) -> Array[Bytes] {
let out : Array[Bytes] = []
let buf = @buffer.Buffer()
for p in pages {
let mut cursor = p.body_offset
for i in 0.. Array[Int32] {
let n = payload.length()
if n == 0 {
return [0]
}
let out : Array[Int32] = []
let mut remaining = n
while remaining > 255 {
out.push(255)
remaining -= 255
}
out.push(remaining)
out
}
///|
/// 构造一个 OGG 页(含 CRC 计算)。
///
/// `header_type` 用 bit1=BOS;comment 页与普通页用 0。
pub fn make_page(
serial : Int32,
seq : Int32,
header_type : Int32,
granule : Int64,
payload : Bytes,
) -> Bytes {
make_page_multi(serial, seq, header_type, granule, [payload])
}
///|
/// 构造一个含**多个 packet**的 OGG 页。
///
/// 每个 packet 独立做 lacing(段结束标记 <255),packet 之间在页内
/// 顺序拼接。这是 Vorbis 头页的正确形态:comment 与 setup 是两个
/// packet,虽然同页,但必须各自成段。
pub fn make_page_multi(
serial : Int32,
seq : Int32,
header_type : Int32,
granule : Int64,
packets : Array[Bytes],
) -> Bytes {
// 合并 lacing 表与载荷
let lacing : Array[Int32] = []
let body = @buffer.Buffer()
for p in packets {
let l = lacing_values(p)
for v in l {
lacing.push(v)
}
body.write_bytes(p)
}
let n = lacing.length()
let payload = body.to_bytes()
// 页头:27 字节 + lacing 表;CRC 字段(22..26)先填 0
let mut head = Bytes::from_array([
0x4F,
0x67,
0x67,
0x53,
0x00,
header_type.to_byte(),
])
head = head.add(write_le64(granule))
head = head.add(write_le32_bytes(serial))
head = head.add(write_le32_bytes(seq))
head = head.add(Bytes::from_array([0x00, 0x00, 0x00, 0x00]))
head = head.add(Bytes::from_array([n.to_byte()]))
for l in lacing {
head = head.add(Bytes::from_array([l.to_byte()]))
}
let page_with_zero_crc = head.add(payload)
// 计算 CRC(把 CRC 字段当 0 参与计算,即整页直接算)
let crc = ogg_crc32(page_with_zero_crc)
// 把 CRC 写回 22..26(小端)
let mut final_bytes = page_with_zero_crc
final_bytes = patch_bytes(
final_bytes,
22,
write_le32_bytes(crc.reinterpret_as_int()),
)
final_bytes
}
///|
/// 把 `replacement` 写到 `data` 的 `offset` 处。
fn patch_bytes(data : Bytes, offset : Int32, replacement : Bytes) -> Bytes {
let out = @buffer.Buffer()
let mut i = 0
while i < offset {
out.write_byte(data.at(i))
i += 1
}
out.write_bytes(replacement)
let mut j = offset + replacement.length()
while j < data.length() {
out.write_byte(data.at(j))
j += 1
}
out.to_bytes()
}
///|
/// 写 8 字节小端。
fn write_le64(v : Int64) -> Bytes {
let out = @buffer.Buffer()
for shift in [0, 8, 16, 24, 32, 40, 48, 56] {
let byte = ((v >> shift) & 0xFFL).to_int().to_byte()
out.write_byte(byte)
}
out.to_bytes()
}
///|
/// 写 4 字节小端。
fn write_le32_bytes(v : Int32) -> Bytes {
Bytes::from_array([
(v & 0xFF).to_byte(),
((v >> 8) & 0xFF).to_byte(),
((v >> 16) & 0xFF).to_byte(),
((v >> 24) & 0xFF).to_byte(),
])
}
///|
/// Ogg CRC-32:非反射(MSB-first)算法,多项式 0x04C11DB7,
/// 初始 0、无最终异或。与 zlib/标准 CRC-32 不同,不能复用。
pub fn ogg_crc32(data : Bytes) -> UInt32 {
let table = ogg_crc_table()
let mut c : UInt32 = 0U
for i in 0..> 24) ^ byte).reinterpret_as_int()
c = (c << 8) ^ table[idx]
}
c
}
///|
/// Ogg CRC 查找表(惰性构造一次)。
fn ogg_crc_table() -> FixedArray[UInt32] {
let table = FixedArray::make(256, 0U)
for i in 0..<256 {
let mut r : UInt32 = i.reinterpret_as_uint() << 24
for _ in 0..<8 {
if (r & 0x80000000U) != 0U {
r = (r << 1) ^ 0x04C11DB7U
} else {
r = r << 1
}
}
table[i] = r
}
table
}