///|
pub struct ProgramRef {
number : Int
pmt_pid : Int
} derive(Eq, Debug)
///|
pub struct Pat {
section : LongSection
programs : Array[ProgramRef]
network_pid : Int?
} derive(Eq, Debug)
///|
pub struct Descriptor {
tag : Int
data : Bytes
} derive(Eq, Debug)
///|
pub struct ElementaryStream {
stream_type : Int
pid : Int
descriptors : Array[Descriptor]
} derive(Eq, Debug)
///|
pub struct Pmt {
section : LongSection
pcr_pid : Int
descriptors : Array[Descriptor]
streams : Array[ElementaryStream]
} derive(Eq, Debug)
///|
fn pid13(b : Bytes, i : Int) -> Int {
((b[i].to_int() & 31) << 8) | b[i + 1].to_int()
}
///|
pub fn parse_pat(section : LongSection) -> Result[Pat, Diagnostic] {
let bad = fault("pat_syntax", section.offset, Some(0))
if section.table_id != 0 ||
(section.bytes[1].to_int() & 0x40) != 0 ||
section.body.length() % 4 != 0 {
return Err(bad)
}
let programs : Array[ProgramRef] = []
let mut network_pid = None
let b = section.body
for i = 0; i < b.length(); i = i + 4 {
let number = (b[i].to_int() << 8) | b[i + 1].to_int()
let pid = pid13(b, i + 2)
if (b[i + 2].to_int() & 0xe0) != 0xe0 || pid < 16 || pid == 8191 {
return Err(bad)
}
if number == 0 {
if network_pid is Some(_) {
return Err(fault("pat_duplicate_program", section.offset, Some(0)))
}
network_pid = Some(pid)
} else {
for p in programs {
if p.number == number {
return Err(fault("pat_duplicate_program", section.offset, Some(0)))
}
}
programs.push({ number, pmt_pid: pid, })
}
}
Ok({ section, programs, network_pid, })
}
///|
fn descriptors(
bytes : Bytes,
locate : (Int) -> Int64,
) -> Result[Array[Descriptor], Diagnostic] {
let result = []
let mut pos = 0
while pos < bytes.length() {
if pos + 2 > bytes.length() {
return Err(fault("descriptor_length", locate(pos), None))
}
let end = pos + 2 + bytes[pos + 1].to_int()
if end > bytes.length() {
return Err(fault("descriptor_length", locate(pos), None))
}
result.push({
tag: bytes[pos].to_int(),
data: bytes.view(start=pos + 2, end~).to_owned(),
})
pos = end
}
Ok(result)
}
///|
pub fn parse_pmt(section : LongSection) -> Result[Pmt, Diagnostic] {
let bad = fault("pmt_syntax", section.offset, None)
let b = section.body
if section.table_id != 2 ||
(section.bytes[1].to_int() & 0x40) != 0 ||
section.extension == 0 ||
section.number != 0 ||
section.last_number != 0 ||
b.length() < 4 {
return Err(bad)
}
if (b[0].to_int() & 0xe0) != 0xe0 || (b[2].to_int() & 0xf0) != 0xf0 {
return Err(bad)
}
let pcr_pid = pid13(b, 0)
if pcr_pid < 16 {
return Err(bad)
}
let info_end = 4 + (((b[2].to_int() & 15) << 8) | b[3].to_int())
if info_end > b.length() {
return Err(bad)
}
let program_descriptors = match
descriptors(b.view(start=4, end=info_end).to_owned(), fn(index) {
section.at(12 + index)
}) {
Ok(x) => x
Err(e) => return Err(e)
}
let streams : Array[ElementaryStream] = []
let mut pos = info_end
while pos < b.length() {
if pos + 5 > b.length() {
return Err(bad)
}
let pid = pid13(b, pos + 1)
if (b[pos + 1].to_int() & 0xe0) != 0xe0 ||
(b[pos + 3].to_int() & 0xf0) != 0xf0 ||
pid < 16 ||
pid == 8191 {
return Err(bad)
}
let end = pos +
5 +
(((b[pos + 3].to_int() & 15) << 8) | b[pos + 4].to_int())
if end > b.length() {
return Err(bad)
}
for s in streams {
if s.pid == pid {
return Err(fault("pmt_duplicate_pid", section.offset, Some(pid)))
}
}
let desc = match
descriptors(b.view(start=pos + 5, end~).to_owned(), fn(index) {
section.at(13 + pos + index)
}) {
Ok(x) => x
Err(e) => return Err(e)
}
streams.push({ stream_type: b[pos].to_int(), pid, descriptors: desc, })
pos = end
}
Ok({ section, pcr_pid, descriptors: program_descriptors, streams, })
}