///|
/// Incremental Ogg FLAC demultiplexer. Output owns its bytes. Chained streams
/// emit a fresh fLaC signature, allowing callers to finish/start decoders.
/// After an error, discard this demultiplexer rather than resuming it.
struct OggDemux {
page : Array[Byte]
packet : Array[Byte]
mut serial : UInt32?
mut sequence : UInt32
mut offset : Int64
mut first : Bool
mut headers : Bool
mut header_count : Int32
mut expected_headers : Int32
mut ended : Bool
mut saw_stream : Bool
mut peak : Int32
mut page_size : Int32
mut granule : UInt64?
mut info : StreamInfo?
max_packet_bytes : Int32
}
///|
pub fn OggDemux::new(max_packet_bytes~ : Int32 = 16777219) -> OggDemux {
{
page: [],
packet: [],
serial: None,
sequence: 0,
offset: 0,
first: true,
headers: true,
header_count: 0,
expected_headers: 0,
ended: false,
saw_stream: false,
peak: 0,
page_size: 27,
granule: None,
info: None,
max_packet_bytes,
}
}
///|
pub fn OggDemux::buffered_bytes(self : OggDemux) -> Int32 {
self.page.length() + self.packet.length()
}
///|
pub fn OggDemux::peak_buffered_bytes(self : OggDemux) -> Int32 {
self.peak
}
///|
fn ogg_le(page : Array[Byte], start : Int32, n : Int32) -> UInt64 {
let mut value = 0UL
for i = 0; i < n; i = i + 1 {
value = value | (page[start + i].to_uint64() << (i * 8))
}
value
}
///|
fn OggDemux::bad(self : OggDemux, message : String) -> Unit raise FlacError {
raise Invalid(self.offset, message)
}
///|
fn ogg_crc(page : Array[Byte]) -> UInt32 {
let mut crc = 0U
for i = 0; i < page.length(); i = i + 1 {
let b = if i >= 22 && i < 26 { 0U } else { page[i].to_uint() }
crc = crc ^ (b << 24)
for _j = 0; _j < 8; _j = _j + 1 {
crc = if (crc & 0x80000000U) != 0U {
(crc << 1) ^ 0x04c11db7U
} else {
crc << 1
}
}
}
crc
}
///|
fn OggDemux::process(
self : OggDemux,
output : Array[Bytes],
) -> Unit raise FlacError {
let p = self.page
if ogg_crc(p) != ogg_le(p, 22, 4).to_uint() {
self.bad("Ogg CRC mismatch")
}
let flags = p[5].to_int()
let serial = ogg_le(p, 14, 4).to_uint()
let seq = ogg_le(p, 18, 4).to_uint()
let segments = p[26].to_int()
if self.serial is None {
// Select a FLAC BOS; unrelated logical streams may be multiplexed.
if (flags & 2) == 0 ||
segments == 0 ||
p.length() < 33 ||
p[27 + segments] != 127 {
return
}
let b = 27 + segments
if p.length() < b + 5 ||
p[b + 1] != 70 ||
p[b + 2] != 76 ||
p[b + 3] != 65 ||
p[b + 4] != 67 {
return
}
self.serial = Some(serial)
self.sequence = seq
self.first = true
self.headers = true
self.header_count = 0
self.ended = false
self.granule = None
self.saw_stream = true
}
if self.serial != Some(serial) {
return
}
if seq != self.sequence {
self.bad("Ogg page sequence discontinuity")
}
self.sequence += 1U
if ((flags & 1) != 0) != !self.packet.is_empty() {
self.bad("Ogg continuation flag mismatch")
}
if self.first &&
(
seq != 0U ||
flags != 2 ||
segments != 1 ||
p[27] != 51 ||
p.length() != 79
) {
self.bad("invalid Ogg FLAC BOS page")
}
if !self.first && (flags & 2) != 0 {
self.bad("unexpected Ogg BOS")
}
let granule = ogg_le(p, 6, 8)
let was_headers = self.headers
if self.headers && granule != 0UL {
self.bad("nonzero Ogg header granule")
}
let mut cursor = 27 + segments
let mut completed = false
let mut page_samples = 0UL
for i = 0; i < segments; i = i + 1 {
let size = p[27 + i].to_int()
if size > self.max_packet_bytes - self.packet.length() {
raise Limit("Ogg packet byte limit")
}
for j = 0; j < size; j = j + 1 {
self.packet.push(p[cursor + j])
}
cursor += size
if self.buffered_bytes() > self.peak {
self.peak = self.buffered_bytes()
}
if size < 255 {
completed = true
let packet = Bytes::from_array(self.packet)
self.packet.clear()
if self.first {
if packet.length() != 51 ||
packet[5] != 1 ||
packet[6] != 0 ||
packet[9] != 102 ||
packet[10] != 76 ||
packet[11] != 97 ||
packet[12] != 67 ||
packet[13] != 0 ||
packet[14] != 0 ||
packet[15] != 0 ||
packet[16] != 34 {
self.bad("invalid Ogg FLAC mapping header")
}
self.expected_headers = packet[7].to_int() * 256 + packet[8].to_int()
let r = MetaReader::{ data: packet, base: 0, pos: 9, }
let native = r.take(42)
let standalone = native.to_array()
standalone[4] = 128
let (metadata, _) = parse_metadata(Bytes::from_array(standalone))
self.info = Some(metadata.info)
output.push(native)
self.first = false
} else if self.headers {
if packet.length() < 4 {
self.bad("short Ogg metadata packet")
}
let n = packet[1].to_int() * 65536 +
packet[2].to_int() * 256 +
packet[3].to_int()
if packet.length() != n + 4 || (packet[0].to_int() & 127) == 0 {
self.bad("invalid Ogg metadata packet")
}
self.header_count += 1
output.push(packet)
if (packet[0].to_int() & 128) != 0 {
if i != segments - 1 {
self.bad("Ogg audio shares final metadata page")
}
if self.expected_headers != 0 &&
self.expected_headers != self.header_count {
self.bad("Ogg header packet count mismatch")
}
self.headers = false
}
} else {
if packet.length() < 2 ||
packet[0] != 255 ||
(packet[1].to_int() & 254) != 248 {
self.bad("Ogg audio packet lacks FLAC frame")
}
let info = self.info.unwrap()
let (frame, consumed) = parse_frame(packet, 0, info, self.offset) catch {
NeedMore =>
raise Invalid(self.offset, "truncated FLAC frame in Ogg packet")
error => raise error
}
if consumed != packet.length() {
self.bad("Ogg audio packet contains more than one FLAC frame")
}
page_samples += frame.block_size.to_uint64()
output.push(packet)
}
}
}
if !self.headers && !completed && granule != 0xffffffffffffffffUL {
self.bad("Ogg unfinished page granule")
}
if !was_headers && completed {
if granule == 0xffffffffffffffffUL {
self.bad("missing Ogg completed-page granule")
}
if self.granule is Some(previous) {
if granule < previous || granule - previous != page_samples {
self.bad("Ogg granule does not match decoded sample count")
}
} else if granule < page_samples {
self.bad("Ogg first granule precedes decoded samples")
}
self.granule = Some(granule)
}
if (flags & 4) != 0 {
if self.headers || !self.packet.is_empty() {
self.bad("Ogg EOS before packet completion")
}
self.ended = true
self.serial = None
}
}
///|
/// Consumes arbitrarily large caller input a byte at a time without retaining it.
pub fn OggDemux::push(
self : OggDemux,
data : Bytes,
) -> Array[Bytes] raise FlacError {
let output = []
for byte in data {
self.page.push(byte)
let p = self.page
if p.length() == 27 {
if p[0] != 79 ||
p[1] != 103 ||
p[2] != 103 ||
p[3] != 83 ||
p[4] != 0 ||
(p[5].to_int() & 248) != 0 {
self.bad("invalid Ogg page header")
}
}
if p.length() >= 27 && p.length() == 27 + p[26].to_int() {
let mut total = 27 + p[26].to_int()
for i = 0; i < p[26].to_int(); i = i + 1 {
total += p[27 + i].to_int()
}
self.page_size = total
} else if p.length() == 27 {
self.page_size = 27 + p[26].to_int()
}
if p.length() >= 27 && p.length() == self.page_size {
self.process(output)
self.offset += self.page_size.to_int64()
self.page.clear()
self.page_size = 27
}
if self.buffered_bytes() > self.peak {
self.peak = self.buffered_bytes()
}
}
output
}
///|
pub fn OggDemux::finish(self : OggDemux) -> Unit raise FlacError {
if !self.page.is_empty() ||
!self.packet.is_empty() ||
!self.saw_stream ||
!self.ended {
self.bad("truncated Ogg FLAC stream")
}
}