use oxideav_core::bits::BitReader;
use oxideav_core::{Error, Result};
#[derive(Clone, Debug)]
pub struct GobHeader {
pub gn: u8,
pub gquant: u8,
}
pub fn parse_gob_header(br: &mut BitReader<'_>) -> Result<GobHeader> {
let gbsc = br.read_u32(16)?;
if gbsc != 0x0001 {
return Err(Error::invalid(format!(
"h261 GOB: bad GBSC 0x{gbsc:04x} (want 0x0001)"
)));
}
let gn = br.read_u32(4)? as u8;
if gn == 0 {
return Err(Error::invalid(
"h261 GOB: GN==0 indicates a PSC, not a GBSC",
));
}
if gn >= 13 {
return Err(Error::invalid(format!("h261 GOB: GN={gn} is reserved")));
}
let gquant = br.read_u32(5)? as u8;
if gquant == 0 {
return Err(Error::invalid("h261 GOB: GQUANT == 0"));
}
loop {
let gei = br.read_u1()?;
if gei == 0 {
break;
}
let _gspare = br.read_u32(8)?;
}
Ok(GobHeader { gn, gquant })
}
pub fn mba_to_mb_rc(mba: u8) -> (usize, usize) {
debug_assert!((1..=33).contains(&mba));
let idx = (mba - 1) as usize;
(idx / 11, idx % 11)
}
pub fn gn_to_gob_rc(gn: u8) -> (usize, usize) {
debug_assert!((1..=12).contains(&gn));
let idx = (gn - 1) as usize;
(idx / 2, idx % 2)
}
pub fn cif_gob_origin_luma(gn: u8) -> (usize, usize) {
let (r, c) = gn_to_gob_rc(gn);
(c * 176, r * 48)
}
pub fn qcif_gob_origin_luma(gn: u8) -> (usize, usize) {
let row = match gn {
1 => 0,
3 => 1,
5 => 2,
_ => panic!("qcif GOB number must be 1, 3, or 5, got {gn}"),
};
(0, row * 48)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn mba_layout() {
assert_eq!(mba_to_mb_rc(1), (0, 0));
assert_eq!(mba_to_mb_rc(11), (0, 10));
assert_eq!(mba_to_mb_rc(12), (1, 0));
assert_eq!(mba_to_mb_rc(33), (2, 10));
}
#[test]
fn gn_layout() {
assert_eq!(gn_to_gob_rc(1), (0, 0));
assert_eq!(gn_to_gob_rc(2), (0, 1));
assert_eq!(gn_to_gob_rc(3), (1, 0));
assert_eq!(gn_to_gob_rc(12), (5, 1));
}
}