use super::bitwriter::BitWriter;
use super::dequant::{DEFAULT_INTRA_MATRIX, DEFAULT_NON_INTRA_MATRIX};
use super::zigzag::SCAN_PROGRESSIVE;
const PICTURE_STRUCTURE_FRAME: u32 = 3;
const PICTURE_CODING_TYPE_I: u32 = 1;
pub const CHROMA_FORMAT_420: u32 = 1;
pub const CHROMA_FORMAT_422: u32 = 2;
pub const CHROMA_FORMAT_444: u32 = 3;
const SEQUENCE_EXTENSION_ID: u32 = 0b0001;
const PICTURE_CODING_EXTENSION_ID: u32 = 0b1000;
#[derive(Debug, Clone, Copy)]
pub struct SequenceHeaderParams {
pub width: u32,
pub height: u32,
pub aspect_ratio_information: u8,
pub frame_rate_code: u8,
pub bit_rate_value: u32,
pub vbv_buffer_size_value: u32,
pub load_default_matrices: bool,
}
fn write_quant_matrix_zigzag(writer: &mut BitWriter, matrix_raster: &[u8; 64]) {
for &raster_pos in SCAN_PROGRESSIVE.iter() {
writer.write_bits(u32::from(matrix_raster[raster_pos]), 8);
}
}
pub fn write_sequence_header(writer: &mut BitWriter, params: &SequenceHeaderParams) {
writer.write_bits(params.width & 0xFFF, 12);
writer.write_bits(params.height & 0xFFF, 12);
writer.write_bits(u32::from(params.aspect_ratio_information), 4);
writer.write_bits(u32::from(params.frame_rate_code), 4);
writer.write_bits(params.bit_rate_value & 0x3_FFFF, 18);
writer.write_bit(true); writer.write_bits(params.vbv_buffer_size_value & 0x3FF, 10);
writer.write_bit(false);
if params.load_default_matrices {
writer.write_bit(true); write_quant_matrix_zigzag(writer, &DEFAULT_INTRA_MATRIX);
writer.write_bit(true); write_quant_matrix_zigzag(writer, &DEFAULT_NON_INTRA_MATRIX);
} else {
writer.write_bit(false); writer.write_bit(false); }
}
#[derive(Debug, Clone, Copy)]
pub struct SequenceExtensionParams {
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 frame_rate_extension_n: u8,
pub frame_rate_extension_d: u8,
}
pub fn write_sequence_extension(writer: &mut BitWriter, params: &SequenceExtensionParams) {
writer.write_bits(SEQUENCE_EXTENSION_ID, 4);
writer.write_bits(u32::from(params.profile_and_level_indication), 8);
writer.write_bit(params.progressive_sequence);
writer.write_bits(u32::from(params.chroma_format) & 0x3, 2);
writer.write_bits(u32::from(params.horizontal_size_extension), 2);
writer.write_bits(u32::from(params.vertical_size_extension), 2);
writer.write_bits(params.bit_rate_extension & 0xFFF, 12);
writer.write_bit(true); writer.write_bits(0, 8); writer.write_bit(false); writer.write_bits(u32::from(params.frame_rate_extension_n), 2);
writer.write_bits(u32::from(params.frame_rate_extension_d), 5);
}
pub fn write_picture_header(writer: &mut BitWriter, temporal_reference: u32, vbv_delay: u32) {
writer.write_bits(temporal_reference & 0x3FF, 10);
writer.write_bits(PICTURE_CODING_TYPE_I, 3);
writer.write_bits(vbv_delay & 0xFFFF, 16);
}
#[derive(Debug, Clone, Copy)]
pub struct PictureCodingExtensionParams {
pub intra_dc_precision: u8,
pub q_scale_type: bool,
pub intra_vlc_format: bool,
pub alternate_scan: bool,
pub progressive_frame: bool,
}
pub fn write_picture_coding_extension(
writer: &mut BitWriter,
params: &PictureCodingExtensionParams,
) {
writer.write_bits(PICTURE_CODING_EXTENSION_ID, 4);
for _ in 0..4 {
writer.write_bits(0xF, 4);
}
writer.write_bits(u32::from(params.intra_dc_precision & 0x3), 2);
writer.write_bits(PICTURE_STRUCTURE_FRAME, 2);
writer.write_bit(false); writer.write_bit(true); writer.write_bit(false); writer.write_bit(params.q_scale_type);
writer.write_bit(params.intra_vlc_format);
writer.write_bit(params.alternate_scan);
writer.write_bit(false); writer.write_bit(true); writer.write_bit(params.progressive_frame);
writer.write_bit(false); }
pub fn write_slice_header(writer: &mut BitWriter, quantiser_scale_code: u8) {
writer.write_bits(u32::from(quantiser_scale_code & 0x1F), 5);
writer.write_bit(false); }
#[cfg(test)]
mod tests {
use super::*;
use crate::mpeg2::bitreader::BitReader;
use crate::mpeg2::headers::{
parse_picture_coding_extension, parse_picture_header, parse_sequence_extension,
parse_sequence_header, parse_slice_header,
};
#[test]
fn sequence_header_round_trips_defaults() {
let params = SequenceHeaderParams {
width: 32,
height: 16,
aspect_ratio_information: 1,
frame_rate_code: 3,
bit_rate_value: 0x3FFFF,
vbv_buffer_size_value: 112,
load_default_matrices: false,
};
let mut w = BitWriter::new();
write_sequence_header(&mut w, ¶ms);
let bytes = w.into_bytes();
let mut r = BitReader::new(&bytes);
let sh = parse_sequence_header(&mut r).expect("parse");
assert_eq!(sh.horizontal_size_value, 32);
assert_eq!(sh.vertical_size_value, 16);
assert_eq!(sh.aspect_ratio_information, 1);
assert_eq!(sh.frame_rate_code, 3);
assert_eq!(sh.intra_quantiser_matrix, DEFAULT_INTRA_MATRIX);
}
#[test]
fn sequence_header_round_trips_with_downloaded_matrices() {
let params = SequenceHeaderParams {
width: 64,
height: 48,
aspect_ratio_information: 2,
frame_rate_code: 4,
bit_rate_value: 1000,
vbv_buffer_size_value: 200,
load_default_matrices: true,
};
let mut w = BitWriter::new();
write_sequence_header(&mut w, ¶ms);
let bytes = w.into_bytes();
let mut r = BitReader::new(&bytes);
let sh = parse_sequence_header(&mut r).expect("parse");
assert_eq!(sh.intra_quantiser_matrix, DEFAULT_INTRA_MATRIX);
assert_eq!(sh.non_intra_quantiser_matrix, DEFAULT_NON_INTRA_MATRIX);
}
#[test]
fn sequence_extension_round_trips() {
let params = SequenceExtensionParams {
profile_and_level_indication: 0x44,
progressive_sequence: true,
chroma_format: 1,
horizontal_size_extension: 0,
vertical_size_extension: 0,
bit_rate_extension: 0,
frame_rate_extension_n: 0,
frame_rate_extension_d: 0,
};
let mut w = BitWriter::new();
write_sequence_extension(&mut w, ¶ms);
let bytes = w.into_bytes();
let mut r = BitReader::new(&bytes);
let se = parse_sequence_extension(&mut r).expect("parse");
assert_eq!(se.chroma_format, 1);
assert_eq!(se.profile_and_level_indication, 0x44);
assert!(se.progressive_sequence);
}
#[test]
fn sequence_extension_writes_all_chroma_formats() {
for cf in 1u8..=3u8 {
let params = SequenceExtensionParams {
profile_and_level_indication: 0x44,
progressive_sequence: true,
chroma_format: cf,
horizontal_size_extension: 0,
vertical_size_extension: 0,
bit_rate_extension: 0,
frame_rate_extension_n: 0,
frame_rate_extension_d: 0,
};
let mut w = BitWriter::new();
write_sequence_extension(&mut w, ¶ms);
let bytes = w.into_bytes();
let mut r = BitReader::new(&bytes);
let se = parse_sequence_extension(&mut r).expect("parse");
assert_eq!(se.chroma_format, cf);
}
}
#[test]
fn picture_header_round_trips() {
let mut w = BitWriter::new();
write_picture_header(&mut w, 0, 0xFFFF);
let bytes = w.into_bytes();
let mut r = BitReader::new(&bytes);
let ph = parse_picture_header(&mut r).expect("parse");
assert_eq!(ph.picture_coding_type, 1);
assert_eq!(ph.vbv_delay, 0xFFFF);
}
#[test]
fn picture_coding_extension_round_trips() {
let params = PictureCodingExtensionParams {
intra_dc_precision: 2,
q_scale_type: true,
intra_vlc_format: true,
alternate_scan: false,
progressive_frame: true,
};
let mut w = BitWriter::new();
write_picture_coding_extension(&mut w, ¶ms);
let bytes = w.into_bytes();
let mut r = BitReader::new(&bytes);
let pce = parse_picture_coding_extension(&mut r).expect("parse");
assert_eq!(pce.intra_dc_precision, 2);
assert_eq!(pce.picture_structure, 3);
assert!(pce.q_scale_type);
assert!(pce.intra_vlc_format);
assert!(!pce.alternate_scan);
assert!(pce.progressive_frame);
}
#[test]
fn slice_header_round_trips() {
let mut w = BitWriter::new();
write_slice_header(&mut w, 9);
let bytes = w.into_bytes();
let mut r = BitReader::new(&bytes);
let sh = parse_slice_header(&mut r, 1).expect("parse");
assert_eq!(sh.quantiser_scale_code, 9);
assert_eq!(sh.slice_vertical_position, 1);
}
}