use crate::bit_reader::BitReader;
use crate::BitstreamError;
pub const START_CODE_PICTURE: u8 = 0x00;
pub const START_CODE_SLICE_FIRST: u8 = 0x01;
pub const START_CODE_SLICE_LAST: u8 = 0xAF;
pub const START_CODE_SEQUENCE_HEADER: u8 = 0xB3;
pub const START_CODE_EXTENSION: u8 = 0xB5;
pub const START_CODE_SEQUENCE_END: u8 = 0xB7;
pub const START_CODE_GROUP_OF_PICTURES: u8 = 0xB8;
pub const EXTENSION_ID_SEQUENCE: u8 = 0x1;
pub const EXTENSION_ID_PICTURE_CODING: u8 = 0x8;
pub const DEFAULT_INTRA_QUANT: [u8; 64] = [
8, 16, 19, 22, 26, 27, 29, 34, 16, 16, 22, 24, 27, 29, 34, 37, 19, 22, 26, 27, 29, 34, 34, 38,
22, 22, 26, 27, 29, 34, 37, 40, 22, 26, 27, 29, 32, 35, 40, 48, 26, 27, 29, 32, 35, 40, 48, 58,
26, 27, 29, 34, 38, 46, 56, 69, 27, 29, 35, 38, 46, 56, 69, 83,
];
pub const DEFAULT_NON_INTRA_QUANT: [u8; 64] = [16; 64];
const ZIGZAG_TO_RASTER: [u8; 64] = [
0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26, 33, 40, 48, 41, 34, 27, 20,
13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59,
52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63,
];
pub fn zigzag_to_raster_quant(zz: &[u8; 64]) -> [u8; 64] {
let mut out = [0u8; 64];
for (zz_idx, &raster_idx) in ZIGZAG_TO_RASTER.iter().enumerate() {
out[raster_idx as usize] = zz[zz_idx];
}
out
}
pub fn find_start_codes(buf: &[u8]) -> Vec<(u8, usize)> {
let mut out = Vec::new();
let mut i = 0;
let n = buf.len();
while i + 3 < n {
if buf[i] == 0 && buf[i + 1] == 0 && buf[i + 2] == 1 {
out.push((buf[i + 3], i + 4));
i += 4;
} else {
i += 1;
}
}
out
}
pub fn find_first_slice(buf: &[u8]) -> Option<usize> {
let n = buf.len();
let mut i = 0;
while i + 3 < n {
if buf[i] == 0 && buf[i + 1] == 0 && buf[i + 2] == 1 {
let code = buf[i + 3];
if (START_CODE_SLICE_FIRST..=START_CODE_SLICE_LAST).contains(&code) {
return Some(i);
}
i += 4;
} else {
i += 1;
}
}
None
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct Mpeg2SequenceHeader {
pub horizontal_size: u32,
pub vertical_size: u32,
pub aspect_ratio_information: u8,
pub frame_rate_code: u8,
pub bit_rate: u32,
pub vbv_buffer_size: u32,
pub constrained_parameters_flag: bool,
pub intra_quantiser_matrix: Option<[u8; 64]>,
pub non_intra_quantiser_matrix: Option<[u8; 64]>,
}
impl Mpeg2SequenceHeader {
pub fn intra_quantizer_matrix_raster(&self) -> [u8; 64] {
zigzag_to_raster_quant(
self.intra_quantiser_matrix
.as_ref()
.unwrap_or(&DEFAULT_INTRA_QUANT),
)
}
pub fn non_intra_quantizer_matrix_raster(&self) -> [u8; 64] {
zigzag_to_raster_quant(
self.non_intra_quantiser_matrix
.as_ref()
.unwrap_or(&DEFAULT_NON_INTRA_QUANT),
)
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct Mpeg2PictureHeader {
pub temporal_reference: u32,
pub picture_coding_type: u8,
pub vbv_delay: u32,
pub full_pel_forward_vector: u8,
pub full_pel_backward_vector: u8,
pub f_code: [[u8; 2]; 2],
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct Mpeg2PictureCodingExtension {
pub f_code: [[u8; 2]; 2],
pub intra_dc_precision: u8,
pub picture_structure: u8,
pub top_field_first: u8,
pub frame_pred_frame_dct: u8,
pub concealment_motion_vectors: u8,
pub q_scale_type: u8,
pub intra_vlc_format: u8,
pub alternate_scan: u8,
pub repeat_first_field: u8,
pub chroma_420_type: u8,
pub progressive_frame: u8,
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct Mpeg2SequenceExtension {
pub profile_and_level_indication: u8,
pub progressive_sequence: bool,
pub chroma_format: u8,
pub horizontal_size_extension: u8,
pub vertical_size_extension: u8,
pub bit_rate_extension: u32,
pub vbv_buffer_size_extension: u8,
pub low_delay: bool,
pub frame_rate_extension_n: u8,
pub frame_rate_extension_d: u8,
}
pub fn parse_sequence_header(bytes: &[u8]) -> Result<Mpeg2SequenceHeader, BitstreamError> {
if bytes.len() < 8 {
return Err(BitstreamError::unexpected_end(
"MPEG-2 sequence_header shorter than 8 bytes",
));
}
let mut r = BitReader::new(bytes);
let mut s = Mpeg2SequenceHeader {
horizontal_size: r.u(12),
vertical_size: r.u(12),
aspect_ratio_information: r.u(4) as u8,
frame_rate_code: r.u(4) as u8,
bit_rate: r.u(18),
..Mpeg2SequenceHeader::default()
};
let _marker_bit = r.u(1);
s.vbv_buffer_size = r.u(10);
s.constrained_parameters_flag = r.u(1) != 0;
let load_intra = r.u(1) != 0;
if load_intra {
let mut m = [0u8; 64];
for q in m.iter_mut() {
*q = r.u(8) as u8;
}
s.intra_quantiser_matrix = Some(m);
}
let load_non_intra = r.u(1) != 0;
if load_non_intra {
let mut m = [0u8; 64];
for q in m.iter_mut() {
*q = r.u(8) as u8;
}
s.non_intra_quantiser_matrix = Some(m);
}
Ok(s)
}
pub fn parse_picture_header(bytes: &[u8]) -> Result<Mpeg2PictureHeader, BitstreamError> {
if bytes.len() < 4 {
return Err(BitstreamError::unexpected_end(
"MPEG-2 picture_header shorter than 4 bytes",
));
}
let mut r = BitReader::new(bytes);
let mut p = Mpeg2PictureHeader {
temporal_reference: r.u(10),
picture_coding_type: r.u(3) as u8,
vbv_delay: r.u(16),
..Mpeg2PictureHeader::default()
};
if p.picture_coding_type == 2 || p.picture_coding_type == 3 {
p.full_pel_forward_vector = r.u(1) as u8;
let forward_f_code = r.u(3) as u8;
p.f_code[0][0] = forward_f_code;
p.f_code[0][1] = forward_f_code;
}
if p.picture_coding_type == 3 {
p.full_pel_backward_vector = r.u(1) as u8;
let backward_f_code = r.u(3) as u8;
p.f_code[1][0] = backward_f_code;
p.f_code[1][1] = backward_f_code;
}
Ok(p)
}
pub fn parse_picture_coding_extension(
bytes: &[u8],
) -> Result<Mpeg2PictureCodingExtension, BitstreamError> {
if bytes.len() < 4 {
return Err(BitstreamError::unexpected_end(
"MPEG-2 picture_coding_extension shorter than 4 bytes",
));
}
let mut r = BitReader::new(bytes);
let ext_id = r.u(4) as u8;
if ext_id != EXTENSION_ID_PICTURE_CODING {
return Err(BitstreamError::invalid(format!(
"MPEG-2 picture_coding_extension: ext_id={ext_id} != 8"
)));
}
let mut e = Mpeg2PictureCodingExtension::default();
e.f_code[0][0] = r.u(4) as u8;
e.f_code[0][1] = r.u(4) as u8;
e.f_code[1][0] = r.u(4) as u8;
e.f_code[1][1] = r.u(4) as u8;
e.intra_dc_precision = r.u(2) as u8;
e.picture_structure = r.u(2) as u8;
e.top_field_first = r.u(1) as u8;
e.frame_pred_frame_dct = r.u(1) as u8;
e.concealment_motion_vectors = r.u(1) as u8;
e.q_scale_type = r.u(1) as u8;
e.intra_vlc_format = r.u(1) as u8;
e.alternate_scan = r.u(1) as u8;
e.repeat_first_field = r.u(1) as u8;
e.chroma_420_type = r.u(1) as u8;
e.progressive_frame = r.u(1) as u8;
Ok(e)
}
pub fn parse_sequence_extension(bytes: &[u8]) -> Result<Mpeg2SequenceExtension, BitstreamError> {
if bytes.len() < 6 {
return Err(BitstreamError::unexpected_end(
"MPEG-2 sequence_extension shorter than 6 bytes",
));
}
let mut r = BitReader::new(bytes);
let ext_id = r.u(4) as u8;
if ext_id != EXTENSION_ID_SEQUENCE {
return Err(BitstreamError::invalid(format!(
"MPEG-2 sequence_extension: ext_id={ext_id} != 1"
)));
}
let mut e = Mpeg2SequenceExtension {
profile_and_level_indication: r.u(8) as u8,
progressive_sequence: r.u(1) != 0,
chroma_format: r.u(2) as u8,
horizontal_size_extension: r.u(2) as u8,
vertical_size_extension: r.u(2) as u8,
bit_rate_extension: r.u(12),
..Mpeg2SequenceExtension::default()
};
let _marker_bit = r.u(1);
e.vbv_buffer_size_extension = r.u(8) as u8;
e.low_delay = r.u(1) != 0;
e.frame_rate_extension_n = r.u(2) as u8;
e.frame_rate_extension_d = r.u(5) as u8;
Ok(e)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn find_start_codes_locates_known_codes() {
let buf = [
0, 0, 1, 0xb3, 0xaa, 0xbb, 0, 0, 1, 0xb5, 0xcc, 0, 0, 1, 0x00, 0xdd, 0xee, 0, 0, 1, 0x01, 0xff, ];
let codes = find_start_codes(&buf);
assert_eq!(codes.len(), 4);
assert_eq!(codes[0].0, 0xb3);
assert_eq!(codes[1].0, 0xb5);
assert_eq!(codes[2].0, 0x00);
assert_eq!(codes[3].0, 0x01);
}
#[test]
fn find_first_slice_returns_offset_of_leading_zeros() {
let buf = [
0, 0, 1, 0xb3, 0xaa, 0, 0, 1, 0x00, 0xbb, 0, 0, 1, 0x01, 0xcc, ];
let off = find_first_slice(&buf).expect("slice present");
assert_eq!(off, 10);
}
#[test]
fn zigzag_inverse_roundtrip() {
let zz: [u8; 64] = std::array::from_fn(|i| (i + 1) as u8);
let raster = zigzag_to_raster_quant(&zz);
assert_eq!(raster[0], 1); assert_eq!(raster[1], 2);
assert_eq!(raster[8], 3);
}
#[test]
fn parse_sequence_header_too_short() {
assert!(matches!(
parse_sequence_header(&[0u8; 4]),
Err(BitstreamError::UnexpectedEnd(_))
));
}
#[test]
fn parse_picture_coding_extension_rejects_wrong_ext_id() {
let bytes = [0x10, 0x00, 0x00, 0x00];
assert!(matches!(
parse_picture_coding_extension(&bytes),
Err(BitstreamError::InvalidData(_))
));
}
}