pub const GN_PICTURE: u8 = 0;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct StartCode {
pub byte_pos: usize,
pub bit_pos: u64,
pub gn: u8,
}
pub fn find_next_start_code_bits(data: &[u8], start_bit: u64) -> Option<StartCode> {
let total_bits = data.len() as u64 * 8;
if start_bit + 20 > total_bits {
return None;
}
let read_bit = |pos: u64| -> u8 {
let byte = (pos / 8) as usize;
let shift = 7 - (pos % 8) as u8;
(data[byte] >> shift) & 1
};
let mut window: u32 = 0;
for i in 0..16 {
window = (window << 1) | (read_bit(start_bit + i) as u32);
}
const TARGET: u32 = 0b0000_0000_0000_0001;
let mut pos = start_bit + 15; loop {
if (window & 0xFFFF) == TARGET {
let prefix_start = pos - 15;
if prefix_start + 20 > total_bits {
return None;
}
let mut gn = 0u8;
for i in 0..4 {
gn = (gn << 1) | read_bit(prefix_start + 16 + i);
}
return Some(StartCode {
byte_pos: (prefix_start / 8) as usize,
bit_pos: prefix_start,
gn,
});
}
pos += 1;
if pos >= total_bits {
return None;
}
window = (window << 1) | (read_bit(pos) as u32);
}
}
pub fn find_next_start_code(data: &[u8], pos: usize) -> Option<StartCode> {
find_next_start_code_bits(data, (pos as u64) * 8)
}
pub fn iter_start_codes(data: &[u8]) -> impl Iterator<Item = StartCode> + '_ {
let mut bit = 0u64;
std::iter::from_fn(move || {
let sc = find_next_start_code_bits(data, bit)?;
bit = sc.bit_pos + 20;
Some(sc)
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn finds_psc_byte_aligned() {
let data = [0x00, 0x01, 0x00, 0x16];
let v: Vec<_> = iter_start_codes(&data).collect();
assert_eq!(v.len(), 1);
assert_eq!(v[0].gn, 0);
assert_eq!(v[0].bit_pos, 0);
}
#[test]
fn finds_gbsc_gn_1_byte_aligned() {
let data = [0x00, 0x01, 0x1F, 0x00];
let v: Vec<_> = iter_start_codes(&data).collect();
assert_eq!(v.len(), 1);
assert_eq!(v[0].gn, 1);
assert_eq!(v[0].bit_pos, 0);
}
#[test]
fn finds_nonaligned_start_code() {
let data = [0xE0u8, 0x00, 0x26];
let sc = find_next_start_code_bits(&data, 0).unwrap();
assert_eq!(sc.bit_pos, 3);
assert_eq!(sc.byte_pos, 0);
assert_eq!(sc.gn, 3);
}
#[test]
fn stops_when_not_enough_bits() {
let data = [0x00, 0x01]; assert!(find_next_start_code_bits(&data, 0).is_none());
}
}