use super::{pts_to_ns, CodecParser, Frame, PesPacket};
pub struct Ac3Parser;
impl Default for Ac3Parser {
fn default() -> Self {
Self::new()
}
}
impl Ac3Parser {
pub fn new() -> Self {
Self
}
}
impl CodecParser for Ac3Parser {
fn parse(&mut self, pes: &PesPacket) -> Vec<Frame> {
if pes.data.len() < 2 {
return Vec::new();
}
let pts_ns = pes.pts.map(pts_to_ns).unwrap_or(0);
let data = &pes.data;
let mut frames = Vec::new();
let mut pos = 0;
while pos < data.len() {
let sync = find_ac3_sync(&data[pos..]);
let start = match sync {
Some(offset) => pos + offset,
None => break,
};
let remaining = &data[start..];
if remaining.len() < 6 {
frames.push(Frame {
pts_ns,
keyframe: true,
data: remaining.to_vec(),
});
break;
}
let bsid = get_bsid(remaining);
if bsid >= 11 {
let frame_size = eac3_frame_size(remaining);
let end = start + frame_size.min(data.len() - start);
frames.push(Frame {
pts_ns,
keyframe: true,
data: data[start..end].to_vec(),
});
pos = end;
} else {
let next_sync = find_ac3_sync(&data[start + 2..]).map(|o| start + 2 + o);
let end = next_sync.unwrap_or(data.len());
frames.push(Frame {
pts_ns,
keyframe: true,
data: data[start..end].to_vec(),
});
pos = end;
}
}
frames
}
fn codec_private(&self) -> Option<Vec<u8>> {
None
}
}
fn find_ac3_sync(data: &[u8]) -> Option<usize> {
(0..data.len().saturating_sub(1)).find(|&i| data[i] == 0x0B && data[i + 1] == 0x77)
}
pub fn get_bsid(data: &[u8]) -> u8 {
debug_assert!(data.len() >= 6);
(data[5] >> 3) & 0x1F
}
pub fn eac3_frame_size(data: &[u8]) -> usize {
debug_assert!(data.len() >= 4);
let frmsiz = ((data[2] as usize & 0x07) << 8) | (data[3] as usize);
(frmsiz + 1) * 2
}
#[cfg(test)]
mod tests {
use super::*;
use crate::mux::ts::PesPacket;
fn make_pes(data: Vec<u8>, pts: Option<i64>) -> PesPacket {
PesPacket {
pid: 0x1100,
pts,
dts: None,
data,
}
}
fn make_ac3_header(bsid: u8) -> Vec<u8> {
let byte5 = (bsid & 0x1F) << 3;
vec![0x0B, 0x77, 0x00, 0x00, 0x00, byte5, 0xAA, 0xBB]
}
fn make_eac3_header(bsid: u8, frmsiz: u16, payload_fill: u8) -> Vec<u8> {
let byte2 = (frmsiz >> 8) as u8 & 0x07;
let byte3 = (frmsiz & 0xFF) as u8;
let byte5 = (bsid & 0x1F) << 3;
let frame_size = (frmsiz as usize + 1) * 2;
let mut data = vec![0x0B, 0x77, byte2, byte3, 0x00, byte5];
while data.len() < frame_size {
data.push(payload_fill);
}
data.truncate(frame_size);
data
}
#[test]
fn find_ac3_sync_at_start() {
let data = [0x0B, 0x77, 0x01, 0x02, 0x03];
assert_eq!(find_ac3_sync(&data), Some(0));
}
#[test]
fn find_ac3_sync_with_garbage_prefix() {
let data = [0xFF, 0xFE, 0x0B, 0x77, 0x01, 0x02];
assert_eq!(find_ac3_sync(&data), Some(2));
}
#[test]
fn find_ac3_sync_none() {
let data = [0x0B, 0x78, 0x00, 0x00];
assert_eq!(find_ac3_sync(&data), None);
}
#[test]
fn find_ac3_sync_empty() {
let data: [u8; 0] = [];
assert_eq!(find_ac3_sync(&data), None);
}
#[test]
fn bsid_ac3() {
let header = make_ac3_header(8);
assert_eq!(get_bsid(&header), 8);
}
#[test]
fn bsid_eac3() {
let header = make_eac3_header(16, 99, 0x00);
assert_eq!(get_bsid(&header), 16);
}
#[test]
fn bsid_boundary_10() {
let header = make_ac3_header(10);
assert_eq!(get_bsid(&header), 10);
assert!(get_bsid(&header) <= 10);
}
#[test]
fn bsid_boundary_11() {
let header = make_eac3_header(11, 3, 0x00);
assert_eq!(get_bsid(&header), 11);
assert!(get_bsid(&header) >= 11);
}
#[test]
fn eac3_frame_size_basic() {
let header = make_eac3_header(16, 99, 0xDD);
assert_eq!(eac3_frame_size(&header), 200);
}
#[test]
fn eac3_frame_size_min() {
let data = [0x0B, 0x77, 0x00, 0x00, 0x00, 0x80];
assert_eq!(eac3_frame_size(&data), 2);
}
#[test]
fn eac3_frame_size_large() {
let data = [0x0B, 0x77, 0x07, 0xFF, 0x00, 0x80];
assert_eq!(eac3_frame_size(&data), 4096);
}
#[test]
fn parse_eac3_single_frame() {
let mut parser = Ac3Parser::new();
let data = make_eac3_header(16, 9, 0xCC);
assert_eq!(data.len(), 20);
let pes = make_pes(data.clone(), Some(90000));
let frames = parser.parse(&pes);
assert_eq!(frames.len(), 1);
assert_eq!(frames[0].data.len(), 20);
assert_eq!(frames[0].pts_ns, 1_000_000_000);
assert!(frames[0].keyframe);
}
#[test]
fn parse_eac3_frame_with_garbage_prefix() {
let mut parser = Ac3Parser::new();
let mut data = vec![0xFF, 0xFE]; data.extend_from_slice(&make_eac3_header(16, 4, 0xAA)); let pes = make_pes(data, Some(0));
let frames = parser.parse(&pes);
assert_eq!(frames.len(), 1);
assert_eq!(frames[0].data[0], 0x0B);
assert_eq!(frames[0].data[1], 0x77);
assert_eq!(frames[0].data.len(), 10);
}
#[test]
fn parse_syncword() {
let mut parser = Ac3Parser::new();
let data = make_ac3_header(8);
let pes = make_pes(data.clone(), Some(90000));
let frames = parser.parse(&pes);
assert_eq!(frames.len(), 1);
assert_eq!(frames[0].data, data);
assert_eq!(frames[0].pts_ns, 1_000_000_000);
}
#[test]
fn parse_syncword_with_garbage_prefix() {
let mut parser = Ac3Parser::new();
let mut data = vec![0xFF, 0xFE];
data.extend_from_slice(&make_ac3_header(8));
let pes = make_pes(data, Some(0));
let frames = parser.parse(&pes);
assert_eq!(frames.len(), 1);
assert_eq!(frames[0].data[0], 0x0B);
assert_eq!(frames[0].data[1], 0x77);
}
#[test]
fn all_keyframes() {
let mut parser = Ac3Parser::new();
for i in 0..5u8 {
let mut data = make_ac3_header(8);
data.push(i);
let pes = make_pes(data, Some(90000 * i as i64));
let frames = parser.parse(&pes);
assert_eq!(frames.len(), 1);
assert!(frames[0].keyframe, "AC3 frame {} should be a keyframe", i);
}
}
#[test]
fn codec_private_none() {
let parser = Ac3Parser::new();
assert!(parser.codec_private().is_none());
}
#[test]
fn parse_empty_pes() {
let mut parser = Ac3Parser::new();
let pes = make_pes(Vec::new(), Some(0));
let frames = parser.parse(&pes);
assert!(frames.is_empty());
}
#[test]
fn parse_single_byte_pes() {
let mut parser = Ac3Parser::new();
let pes = make_pes(vec![0x0B], Some(0));
let frames = parser.parse(&pes);
assert!(frames.is_empty());
}
#[test]
fn pts_conversion() {
let mut parser = Ac3Parser::new();
let data = make_ac3_header(8);
let pes = make_pes(data, Some(45000));
let frames = parser.parse(&pes);
assert_eq!(frames.len(), 1);
assert_eq!(frames[0].pts_ns, 500_000_000);
}
#[test]
fn no_pts() {
let mut parser = Ac3Parser::new();
let data = make_ac3_header(8);
let pes = make_pes(data, None);
let frames = parser.parse(&pes);
assert_eq!(frames.len(), 1);
assert_eq!(frames[0].pts_ns, 0);
}
}