use super::{
AudioCodecKind, AudioStreamInfo, PatProgram, VideoStreamInfo, DESC_TAG_REGISTRATION, REG_AC3,
REG_EAC3, STREAM_TYPE_AAC_ADTS, STREAM_TYPE_AC3, STREAM_TYPE_EAC3, STREAM_TYPE_H264,
STREAM_TYPE_HEVC, STREAM_TYPE_MPEG2_VIDEO, STREAM_TYPE_PES_PRIVATE,
};
pub(super) fn parse_pat_all_programs(section: &[u8]) -> Vec<PatProgram> {
let mut out = Vec::new();
if section.len() < 12 {
return out;
}
if section[0] != 0x00 {
return out;
}
let section_length = (((section[1] & 0x0F) as usize) << 8) | section[2] as usize;
let total = 3 + section_length;
if total > section.len() {
return out;
}
let loop_start = 8;
let loop_end = total - 4;
let mut i = loop_start;
while i + 4 <= loop_end {
let program = u16::from_be_bytes([section[i], section[i + 1]]);
let pid = (((section[i + 2] & 0x1F) as u16) << 8) | section[i + 3] as u16;
if program != 0 {
out.push(PatProgram {
program_number: program,
pmt_pid: pid,
});
}
i += 4;
}
out
}
pub(super) fn parse_pat_first_pmt_pid(section: &[u8]) -> Option<u16> {
parse_pat_all_programs(section).first().map(|p| p.pmt_pid)
}
pub(super) fn parse_pmt_streams(
section: &[u8],
) -> Option<(Vec<VideoStreamInfo>, Vec<AudioStreamInfo>)> {
if section.len() < 12 {
return None;
}
if section[0] != 0x02 {
return None;
}
let section_length = (((section[1] & 0x0F) as usize) << 8) | section[2] as usize;
let total = 3 + section_length;
if total > section.len() {
return None;
}
if section.len() < 12 {
return None;
}
let pil = (((section[10] & 0x0F) as usize) << 8) | section[11] as usize;
let mut i = 12 + pil;
let loop_end = total - 4; let mut video: Vec<VideoStreamInfo> = Vec::new();
let mut audio: Vec<AudioStreamInfo> = Vec::new();
while i + 5 <= loop_end {
let stype = section[i];
let pid = (((section[i + 1] & 0x1F) as u16) << 8) | section[i + 2] as u16;
let esi_len = (((section[i + 3] & 0x0F) as usize) << 8) | section[i + 4] as usize;
let desc_start = i + 5;
let desc_end = (desc_start + esi_len).min(loop_end);
let descriptors = if desc_start <= desc_end {
§ion[desc_start..desc_end]
} else {
&[][..]
};
match stype {
STREAM_TYPE_MPEG2_VIDEO | STREAM_TYPE_H264 | STREAM_TYPE_HEVC => {
video.push(VideoStreamInfo {
pid,
stream_type: stype,
});
}
STREAM_TYPE_AAC_ADTS => {
audio.push(AudioStreamInfo {
pid,
stream_type: stype,
kind: AudioCodecKind::AacAdts,
});
}
STREAM_TYPE_AC3 => {
audio.push(AudioStreamInfo {
pid,
stream_type: stype,
kind: AudioCodecKind::Ac3,
});
}
STREAM_TYPE_EAC3 => {
audio.push(AudioStreamInfo {
pid,
stream_type: stype,
kind: AudioCodecKind::Eac3,
});
}
STREAM_TYPE_PES_PRIVATE => {
if let Some(reg) = find_registration(descriptors) {
match reg {
REG_AC3 => audio.push(AudioStreamInfo {
pid,
stream_type: stype,
kind: AudioCodecKind::Ac3,
}),
REG_EAC3 => audio.push(AudioStreamInfo {
pid,
stream_type: stype,
kind: AudioCodecKind::Eac3,
}),
_ => {}
}
}
}
_ => {}
}
i += 5 + esi_len;
}
Some((video, audio))
}
fn find_registration(descriptors: &[u8]) -> Option<u32> {
let mut i = 0usize;
while i + 2 <= descriptors.len() {
let tag = descriptors[i];
let len = descriptors[i + 1] as usize;
let body_start = i + 2;
let body_end = body_start + len;
if body_end > descriptors.len() {
break;
}
if tag == DESC_TAG_REGISTRATION && len >= 4 {
let id = u32::from_be_bytes([
descriptors[body_start],
descriptors[body_start + 1],
descriptors[body_start + 2],
descriptors[body_start + 3],
]);
return Some(id);
}
i = body_end;
}
None
}