use crate::bitstream::TsStreamBuffer;
use crate::primitives::Pid;
use crate::stream::TsVideoStream;
struct MasteringDisplayColorVolumeValue {
code: u8,
values: [u16; 8],
}
const MASTERING_DISPLAY_COLOR_VOLUME_VALUES: [MasteringDisplayColorVolumeValue; 4] = [
MasteringDisplayColorVolumeValue {
code: 1,
values: [15000, 30000, 7500, 3000, 32000, 16500, 15635, 16450],
}, MasteringDisplayColorVolumeValue {
code: 9,
values: [8500, 39850, 6550, 2300, 35400, 14600, 15635, 16450],
}, MasteringDisplayColorVolumeValue {
code: 11,
values: [13250, 34500, 7500, 3000, 34000, 16000, 15700, 17550],
}, MasteringDisplayColorVolumeValue {
code: 12,
values: [13250, 34500, 7500, 3000, 34000, 16000, 15635, 16450],
}, ];
#[derive(Debug, Clone, PartialEq, Eq, Default)]
struct VuiColour {
video_signal_type_present_flag: bool,
colour_description_present_flag: bool,
video_full_range_flag: u8,
colour_primaries: u8,
transfer_characteristics: u8,
matrix_coefficients: u8,
}
impl VuiColour {
const fn unspecified() -> Self {
Self {
video_signal_type_present_flag: false,
colour_description_present_flag: false,
video_full_range_flag: 0,
colour_primaries: 2,
transfer_characteristics: 2,
matrix_coefficients: 2,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
struct SeqParameterSet {
profile_space: u32,
tier_flag: bool,
profile_idc: u16,
level_idc: u16,
chroma_format_idc: u32,
bit_depth_luma_minus8: u8,
bit_depth_chroma_minus8: u8,
vui: VuiColour,
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
struct PicParameterSet {
num_extra_slice_header_bits: u8,
dependent_slice_segments_enabled_flag: bool,
}
#[derive(Debug, Clone, Copy, Default)]
struct ProfileTierLevel {
profile_space: u32,
tier_flag: bool,
profile_idc: u16,
level_idc: u16,
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct HevcExtendedData {
video_param_set_count: usize,
seq_parameter_sets: Vec<SeqParameterSet>,
pic_parameter_sets: Vec<PicParameterSet>,
mastering_display_color_primaries: String,
mastering_display_luminance: String,
maximum_content_light_level: u32,
maximum_frame_average_light_level: u32,
light_level_available: bool,
is_hdr10_plus: bool,
mastering_display_luminance_exact: String,
extended_format_info: Vec<String>,
}
impl HevcExtendedData {
#[must_use]
pub(crate) fn extended_format_info(&self) -> &[String] {
&self.extended_format_info
}
#[must_use]
pub(crate) fn extended_format_info_exact(&self) -> Vec<String> {
self.extended_format_info
.iter()
.map(|entry| {
if entry.starts_with("Mastering display luminance: ")
&& !self.mastering_display_luminance_exact.is_empty()
{
format!(
"Mastering display luminance: {}",
self.mastering_display_luminance_exact
)
} else {
entry.clone()
}
})
.collect()
}
}
const fn colour_primaries(colour_primaries: u8) -> &'static str {
match colour_primaries {
1 => "BT.709",
4 => "BT.470 System M",
5 => "BT.601 PAL",
6 => "BT.601 NTSC",
7 => "SMPTE 240M",
8 => "Generic film",
9 => "BT.2020",
10 => "XYZ",
11 => "DCI P3",
12 => "Display P3",
22 => "EBU Tech 3213",
_ => "",
}
}
const fn transfer_characteristics(transfer_characteristics: u8) -> &'static str {
match transfer_characteristics {
1 => "BT.709",
4 => "BT.470 System M",
5 => "BT.470 System B/G",
6 => "BT.601",
7 => "SMPTE 240M",
8 => "Linear",
9 => "Logarithmic (100:1)",
10 => "Logarithmic (316.22777:1)",
11 => "xvYCC",
12 => "BT.1361",
13 => "sRGB/sYCC",
14 => "BT.2020 (10-bit)",
15 => "BT.2020 (12-bit)",
16 => "PQ",
17 => "SMPTE 428M",
18 => "HLG",
_ => "",
}
}
const fn matrix_coefficients(matrix_coefficients: u8) -> &'static str {
match matrix_coefficients {
0 => "Identity",
1 => "BT.709",
4 => "FCC 73.682",
5 => "BT.470 System B/G",
6 => "BT.601",
7 => "SMPTE 240M",
8 => "YCgCo",
9 => "BT.2020 non-constant",
10 => "BT.2020 constant",
11 => "Y'D'zD'x",
12 => "Chromaticity-derived non-constant",
13 => "Chromaticity-derived constant",
14 => "ICtCp",
_ => "",
}
}
const MAX_SHORT_TERM_REF_PIC_SETS: u32 = 64; const MAX_DELTA_POCS_PER_SET: u32 = 16; const MAX_LONG_TERM_REF_PICS: u32 = 32;
fn delta_bytes(a: u64, b: u64) -> i32 {
let a = i64::try_from(a).unwrap_or(i64::MAX);
let b = i64::try_from(b).unwrap_or(i64::MAX);
i32::try_from(a.wrapping_sub(b)).unwrap_or(i32::MAX)
}
pub fn scan(stream: &mut TsVideoStream, buffer: &mut TsStreamBuffer, tag: &mut Option<String>) {
let is_initialized = stream.base.is_initialized;
let mut ext = stream.extended_data.take().unwrap_or_default();
Hevc { ext: &mut ext, buffer, is_initialized }.run(tag);
finalize(stream, &mut ext, is_initialized);
stream.extended_data = Some(ext);
}
struct Hevc<'a, 'b> {
ext: &'a mut HevcExtendedData,
buffer: &'b mut TsStreamBuffer,
is_initialized: bool,
}
enum StartCode {
Found,
NotFound,
}
impl Hevc<'_, '_> {
fn run(&mut self, tag: &mut Option<String>) {
let mut frame_type_read = false;
while self.scan_should_continue(frame_type_read) {
let StartCode::Found = self.find_start_code(frame_type_read) else {
break;
};
if self.buffer.position() >= self.buffer.length() {
break;
}
let last_stream_pos = self.buffer.position();
self.buffer.bs_skip_bits(1, true); let nal_unit_type = self.buffer.read_bits2(6, true);
self.buffer.bs_skip_bits(9, true);
match nal_unit_type {
0..=9 | 16..=21 => {
*tag = self.slice_segment_layer(nal_unit_type);
frame_type_read = tag.is_some();
}
32 => self.video_parameter_set(),
33 => self.seq_parameter_set(),
34 => self.pic_parameter_set(),
39 | 40 => self.sei(),
_ => {}
}
self.buffer.bs_skip_next_byte();
let back = delta_bytes(last_stream_pos, self.buffer.position());
self.buffer.bs_skip_bytes(back, true);
}
}
fn scan_should_continue(&self, frame_type_read: bool) -> bool {
self.buffer.position() < self.buffer.length().saturating_sub(3)
&& (!self.is_initialized || !frame_type_read)
}
fn probe_start_code(&mut self) -> Option<u64> {
let stream_pos = self.buffer.position();
if self.buffer.read_byte(false) == 0x0
&& self.buffer.read_byte(false) == 0x0
&& self.buffer.read_byte(false) == 0x0
&& self.buffer.read_byte(false) == 0x1
{
return Some(4);
}
self.buffer.bs_skip_bytes(delta_bytes(stream_pos, self.buffer.position()), false);
if self.buffer.read_byte(false) == 0x0
&& self.buffer.read_byte(false) == 0x0
&& self.buffer.read_byte(false) == 0x1
{
return Some(3);
}
let resync = delta_bytes(stream_pos, self.buffer.position()).wrapping_add(1);
self.buffer.bs_skip_bytes(resync, false);
None
}
fn find_start_code(&mut self, frame_type_read: bool) -> StartCode {
loop {
if self.probe_start_code().is_some() {
return StartCode::Found;
}
if !self.scan_should_continue(frame_type_read) {
return StartCode::NotFound;
}
}
}
fn slice_segment_layer(&mut self, nal_unit_type: u16) -> Option<String> {
let first_slice_segment_in_pic_flag = self.buffer.read_bool(false);
if (16..=23).contains(&nal_unit_type) {
let _ = self.buffer.read_bool(false); }
let slice_pic_parameter_set_id = self.buffer.read_exp(true);
let pps = usize::try_from(slice_pic_parameter_set_id)
.ok()
.and_then(|i| self.ext.pic_parameter_sets.get(i))?;
let (dependent_enabled, num_extra_bits) =
(pps.dependent_slice_segments_enabled_flag, pps.num_extra_slice_header_bits);
if !first_slice_segment_in_pic_flag {
if dependent_enabled {
let _ = self.buffer.read_bool(true); }
return None;
}
self.buffer.bs_skip_bits(usize::from(num_extra_bits), false);
match self.buffer.read_exp(true) {
0 => Some("P".to_owned()),
1 => Some("B".to_owned()),
2 => Some("I".to_owned()),
_ => None,
}
}
fn video_parameter_set(&mut self) {
if self.is_initialized {
return;
}
let vps_id = usize::from(self.buffer.read_bits2(4, true));
self.ext.video_param_set_count =
self.ext.video_param_set_count.max(vps_id.saturating_add(1));
}
fn seq_parameter_set(&mut self) {
if self.is_initialized {
return;
}
let video_parameter_set_id = usize::from(self.buffer.read_bits2(4, true));
if video_parameter_set_id >= self.ext.video_param_set_count {
return;
}
let max_sub_layers_minus1 = self.buffer.read_bits2(3, true);
self.buffer.bs_skip_bits(1, true); let ptl = self.profile_tier_level(max_sub_layers_minus1);
let sps_seq_parameter_set_id = self.buffer.read_exp(true);
let chroma_format_idc = self.buffer.read_exp(true);
if chroma_format_idc >= 4 {
return;
}
if chroma_format_idc == 3 {
self.buffer.bs_skip_bits(1, true); }
self.buffer.skip_exp(true); self.buffer.skip_exp(true); if self.buffer.read_bool(true) {
self.buffer.skip_exp_multi(4, true); }
let bit_depth_luma_minus8 = self.buffer.read_exp(true);
if bit_depth_luma_minus8 > 6 {
return;
}
let bit_depth_chroma_minus8 = self.buffer.read_exp(true);
if bit_depth_chroma_minus8 > 6 {
return;
}
let log2_max_pic_order_cnt_lsb_minus4 = self.buffer.read_exp(true);
if log2_max_pic_order_cnt_lsb_minus4 > 12 {
return;
}
let sps_sub_layer_ordering_info_present_flag = self.buffer.read_bool(true);
let first =
if sps_sub_layer_ordering_info_present_flag { 0 } else { max_sub_layers_minus1 };
let mut sub_layer_pos = first;
while sub_layer_pos <= max_sub_layers_minus1 {
self.buffer.skip_exp_multi(3, true); sub_layer_pos = sub_layer_pos.wrapping_add(1);
}
self.buffer.skip_exp_multi(6, true);
if self.buffer.read_bool(true) && self.buffer.read_bool(true) {
self.scaling_list_data();
}
self.buffer.bs_skip_bits(2, true); if self.buffer.read_bool(true) {
self.buffer.bs_skip_bits(8, true); self.buffer.skip_exp_multi(2, true);
self.buffer.bs_skip_bits(1, true); }
let num_short_term_ref_pic_sets =
self.buffer.read_exp(true).min(MAX_SHORT_TERM_REF_PIC_SETS);
self.short_term_ref_pic_sets(num_short_term_ref_pic_sets);
if self.buffer.read_bool(true) {
let num_long_term_ref_pics_sps = self.buffer.read_exp(true).min(MAX_LONG_TERM_REF_PICS);
let mut i = 0;
while i < num_long_term_ref_pics_sps {
self.buffer.bs_skip_bits(
usize::try_from(log2_max_pic_order_cnt_lsb_minus4.wrapping_add(4))
.unwrap_or(usize::MAX),
true,
); self.buffer.bs_skip_bits(1, true); i = i.wrapping_add(1);
}
}
self.buffer.bs_skip_bits(2, true); let vui = if self.buffer.read_bool(true) {
self.vui_parameters()
} else {
VuiColour::unspecified()
};
let sps = SeqParameterSet {
profile_space: ptl.profile_space,
tier_flag: ptl.tier_flag,
profile_idc: ptl.profile_idc,
level_idc: ptl.level_idc,
chroma_format_idc,
bit_depth_luma_minus8: bit_depth_luma_minus8.to_le_bytes()[0],
bit_depth_chroma_minus8: bit_depth_chroma_minus8.to_le_bytes()[0],
vui,
};
register(&mut self.ext.seq_parameter_sets, sps_seq_parameter_set_id, sps);
}
fn pic_parameter_set(&mut self) {
if self.is_initialized {
return;
}
let pps_pic_parameter_set_id = self.buffer.read_exp(true);
if pps_pic_parameter_set_id >= 64 {
return;
}
let pps_seq_parameter_set_id = self.buffer.read_exp(true);
if pps_seq_parameter_set_id >= 16 {
return;
}
let valid = usize::try_from(pps_seq_parameter_set_id)
.is_ok_and(|i| i < self.ext.seq_parameter_sets.len());
if !valid {
return;
}
let dependent_slice_segments_enabled_flag = self.buffer.read_bool(true);
self.buffer.bs_skip_bits(1, true); let num_extra_slice_header_bits = self.buffer.read_bits2(3, true).to_le_bytes()[0];
let pps =
PicParameterSet { num_extra_slice_header_bits, dependent_slice_segments_enabled_flag };
register(&mut self.ext.pic_parameter_sets, pps_pic_parameter_set_id, pps);
}
fn profile_tier_level(&mut self, sub_layer_count: u16) -> ProfileTierLevel {
let profile_space = u32::from(self.buffer.read_bits2(2, true));
let tier_flag = self.buffer.read_bool(true);
let mut profile_idc = self.buffer.read_bits2(5, true);
let compatibility_flags = self.buffer.read_bits4(32, true);
let mut compat_idx: u16 = 0;
while compat_idx < 32 {
let bit = 31_u32.wrapping_sub(u32::from(compat_idx));
let flag = compatibility_flags.wrapping_shr(bit) & 1 != 0;
if profile_idc == 0 && flag {
profile_idc = compat_idx;
}
compat_idx = compat_idx.wrapping_add(1);
}
self.buffer.bs_skip_bits(4, true);
self.buffer.bs_skip_bits(44, true);
let level_idc = self.buffer.read_bits2(8, true);
let mut sub_layer_flags: Vec<(bool, bool)> = Vec::new();
let mut pos: u16 = 0;
while pos < sub_layer_count {
let profile_present = self.buffer.read_bool(true);
let level_present = self.buffer.read_bool(true);
sub_layer_flags.push((profile_present, level_present));
pos = pos.wrapping_add(1);
}
if sub_layer_count > 0 {
let mut i = sub_layer_count;
while i < 8 {
self.buffer.bs_skip_bits(2, true);
i = i.wrapping_add(1);
}
}
for (profile_present, level_present) in sub_layer_flags {
if profile_present {
self.buffer.bs_skip_bits(88, true); }
if level_present {
self.buffer.bs_skip_bits(8, true); }
}
ProfileTierLevel { profile_space, tier_flag, profile_idc, level_idc }
}
fn short_term_ref_pic_sets(&mut self, num_short_term_ref_pic_sets: u32) {
let mut num_pics: u32 = 0;
let mut st_rps_idx: u32 = 0;
while st_rps_idx < num_short_term_ref_pic_sets {
let inter_ref_pic_set_prediction_flag = st_rps_idx > 0 && self.buffer.read_bool(true);
if inter_ref_pic_set_prediction_flag {
self.buffer.bs_skip_bits(1, true); self.buffer.skip_exp(true); let mut num_pics_new: u32 = 0;
let mut pic_pos: u32 = 0;
while pic_pos <= num_pics {
let used_by_curr_pic = self.buffer.read_bool(true);
let use_delta = !used_by_curr_pic && self.buffer.read_bool(true);
if used_by_curr_pic || use_delta {
num_pics_new = num_pics_new.wrapping_add(1);
}
pic_pos = pic_pos.wrapping_add(1);
}
num_pics = num_pics_new;
} else {
let num_negative_pics = self.buffer.read_exp(true).min(MAX_DELTA_POCS_PER_SET);
let num_positive_pics = self.buffer.read_exp(true).min(MAX_DELTA_POCS_PER_SET);
num_pics = num_negative_pics.wrapping_add(num_positive_pics);
let mut i: u32 = 0;
while i < num_negative_pics {
self.buffer.skip_exp(true); self.buffer.bs_skip_bits(1, true); i = i.wrapping_add(1);
}
let mut i: u32 = 0;
while i < num_positive_pics {
self.buffer.skip_exp(true); self.buffer.bs_skip_bits(1, true); i = i.wrapping_add(1);
}
}
st_rps_idx = st_rps_idx.wrapping_add(1);
}
}
fn scaling_list_data(&mut self) {
let mut size_id: u32 = 0;
while size_id < 4 {
let matrix_count: i32 = if size_id == 3 { 2 } else { 6 };
let mut matrix_id: i32 = 0;
while matrix_id < matrix_count {
if self.buffer.read_bool(true) {
let shift = 4_u32.wrapping_add(size_id.wrapping_shl(1));
let coef_num = 64_i32.min(1_i32.wrapping_shl(shift));
if size_id > 1 {
self.buffer.skip_exp(true); }
let mut i: i32 = 0;
while i < coef_num {
self.buffer.skip_exp(true); i = i.wrapping_add(1);
}
} else {
self.buffer.skip_exp(true); }
matrix_id = matrix_id.wrapping_add(1);
}
size_id = size_id.wrapping_add(1);
}
}
fn vui_parameters(&mut self) -> VuiColour {
let mut vui = VuiColour::unspecified();
if self.buffer.read_bool(true) {
let aspect_ratio_idc = self.buffer.read_bits2(8, true);
if aspect_ratio_idc == 0xFF {
self.buffer.bs_skip_bits(32, true); }
}
if self.buffer.read_bool(true) {
self.buffer.bs_skip_bits(1, true); }
vui.video_signal_type_present_flag = self.buffer.read_bool(true);
if vui.video_signal_type_present_flag {
self.buffer.bs_skip_bits(3, true); vui.video_full_range_flag = self.buffer.read_bits2(1, true).to_le_bytes()[0];
vui.colour_description_present_flag = self.buffer.read_bool(true);
if vui.colour_description_present_flag {
vui.colour_primaries = self.buffer.read_bits2(8, true).to_le_bytes()[0];
vui.transfer_characteristics = self.buffer.read_bits2(8, true).to_le_bytes()[0];
vui.matrix_coefficients = self.buffer.read_bits2(8, true).to_le_bytes()[0];
}
}
vui
}
fn sei(&mut self) {
let element_start = self.buffer.position();
let mut num_bytes: u64 = 0;
loop {
if let Some(n) = self.probe_start_code() {
num_bytes = n;
break;
}
if self.buffer.position() >= self.buffer.length() {
break;
}
}
let element_size = self.buffer.position().wrapping_sub(element_start);
let rewind = delta_bytes(0, element_size); self.buffer.bs_skip_bytes(rewind, false);
let element_size = element_size.wrapping_sub(num_bytes.wrapping_add(1));
let element_end = element_start.wrapping_add(element_size);
loop {
let payload_type = self.read_sei_extended();
let payload_size = self.read_sei_extended();
let saved_pos = self.buffer.position().wrapping_add(payload_size);
if saved_pos > self.buffer.length() {
return;
}
match payload_type {
137 => self.sei_mastering_display_colour_volume(),
144 => self.sei_light_level(),
4 => self.sei_user_data_registered_itu_t_t35(payload_size),
_ => self.buffer.bs_skip_bytes(sei_bytes(payload_size), true),
}
let remainder = saved_pos.saturating_sub(self.buffer.position());
self.buffer.bs_skip_bytes(sei_bytes(remainder), true);
if self.buffer.position() >= element_end {
break;
}
}
}
fn read_sei_extended(&mut self) -> u64 {
let mut value: u64 = 0;
loop {
let byte = self.buffer.read_byte(true);
value = value.wrapping_add(u64::from(byte));
if byte != 0xFF {
return value;
}
}
}
fn sei_mastering_display_colour_volume(&mut self) {
self.buffer.bs_reset_bits();
let mut primaries = [0_u16; 8];
for slot in &mut primaries {
*slot = self.buffer.read_bits2(16, true); }
let luminance_max = self.buffer.read_bits4(32, true); let luminance_min = self.buffer.read_bits4(32, true);
let (red, green, blue) = (2_usize, 0_usize, 1_usize);
let not_valid = primaries.contains(&u16::MAX);
let matched =
if not_valid { None } else { match_color_volume(&primaries, green, blue, red) };
self.ext.mastering_display_color_primaries = matched.map_or_else(
|| format_raw_primaries(&primaries, red, green, blue),
|code| colour_primaries(code).to_owned(),
);
self.ext.mastering_display_luminance = format_luminance(luminance_min, luminance_max);
self.ext.mastering_display_luminance_exact =
format_luminance_exact(luminance_min, luminance_max);
}
fn sei_light_level(&mut self) {
self.ext.maximum_content_light_level = u32::from(self.buffer.read_bits2(16, true));
self.ext.maximum_frame_average_light_level = u32::from(self.buffer.read_bits2(16, true));
self.ext.light_level_available = true;
}
fn sei_user_data_registered_itu_t_t35(&mut self, payload_size: u64) {
let country_code = self.buffer.read_bits2(8, true);
let terminal_provider_code = self.buffer.read_bits2(16, true);
let terminal_provider_oriented_code = self.buffer.read_bits2(16, true);
let application_id = self.buffer.read_bits4(8, true);
self.buffer.bs_skip_bits(8, true); let num_windows = self.buffer.read_bits4(2, true);
self.buffer.bs_skip_bits(6, true);
if country_code == 0xB5
&& terminal_provider_code == 0x003C
&& terminal_provider_oriented_code == 0x0001
&& application_id == 4
&& (1..=3).contains(&num_windows)
{
self.ext.is_hdr10_plus = true;
}
let rest = u32::try_from(payload_size).unwrap_or(u32::MAX).wrapping_sub(8);
self.buffer.bs_skip_bytes(rest.cast_signed(), true);
}
}
fn sei_bytes(bytes: u64) -> i32 {
i32::try_from(bytes).unwrap_or(i32::MAX)
}
fn register<T: Clone + Default>(sets: &mut Vec<T>, id: u32, value: T) {
if id >= 64 {
return;
}
let id = usize::try_from(id).unwrap_or(usize::MAX);
while sets.len() <= id {
sets.push(T::default());
}
#[expect(clippy::indexing_slicing, reason = "the loop above grows `sets` to id < len")]
{
sets[id] = value;
}
}
fn match_color_volume(primaries: &[u16; 8], green: usize, blue: usize, red: usize) -> Option<u8> {
for row in &MASTERING_DISPLAY_COLOR_VOLUME_VALUES {
let mut code = row.code;
let mut j = 0_usize;
while j < 2 {
if !within(primaries, green, j, &row.values, 0) {
code = 0;
}
if !within(primaries, blue, j, &row.values, 1) {
code = 0;
}
if !within(primaries, red, j, &row.values, 2) {
code = 0;
}
if !within_white(primaries, j, &row.values) {
code = 0;
}
j = j.wrapping_add(1);
}
if code > 0 {
return Some(code);
}
}
None
}
fn within(
primaries: &[u16; 8],
channel: usize,
j: usize,
reference: &[u16; 8],
ref_channel: usize,
) -> bool {
let actual = primaries.get(channel.wrapping_mul(2).wrapping_add(j)).copied().unwrap_or(0);
let expected = reference.get(ref_channel.wrapping_mul(2).wrapping_add(j)).copied().unwrap_or(0);
let actual = i32::from(actual);
let expected = i32::from(expected);
actual >= expected.wrapping_sub(25) && actual < expected.wrapping_add(25)
}
fn within_white(primaries: &[u16; 8], j: usize, reference: &[u16; 8]) -> bool {
let actual = i32::from(primaries.get(6_usize.wrapping_add(j)).copied().unwrap_or(0));
let expected = i32::from(reference.get(6_usize.wrapping_add(j)).copied().unwrap_or(0));
actual >= expected.wrapping_sub(2) && actual < expected.wrapping_add(3)
}
fn format_raw_primaries(primaries: &[u16; 8], red: usize, green: usize, blue: usize) -> String {
let coord = |channel: usize, j: usize| -> f64 {
f64::from(primaries.get(channel.wrapping_mul(2).wrapping_add(j)).copied().unwrap_or(0))
/ 50000.0
};
let white = |j: usize| -> f64 {
f64::from(primaries.get(6_usize.wrapping_add(j)).copied().unwrap_or(0)) / 50000.0
};
format!(
"R: x={:.6} y={:.6}, G: x={:.6} y={:.6}, B: x={:.6} y={:.6}, White point: x={:.6} y={:.6}",
coord(red, 0),
coord(red, 1),
coord(green, 0),
coord(green, 1),
coord(blue, 0),
coord(blue, 1),
white(0),
white(1),
)
}
fn format_luminance(luminance_min: u32, luminance_max: u32) -> String {
let min = f64::from(luminance_min) / 10000.0;
let max = f64::from(luminance_max) / 10000.0;
let whole = i64::from(luminance_max).wrapping_sub(i64::from(luminance_max.cast_signed())) == 0;
if whole {
format!("min: {min:.4} cd/m2, max: {max:.0} cd/m2")
} else {
format!("min: {min:.4} cd/m2, max: {max:.4} cd/m2")
}
}
fn format_luminance_exact(luminance_min: u32, luminance_max: u32) -> String {
let min = f64::from(luminance_min) / 10000.0;
let max = f64::from(luminance_max) / 10000.0;
format!("min: {min:.4} cd/m2, max: {max:.4} cd/m2")
}
fn finalize(stream: &mut TsVideoStream, ext: &mut HevcExtendedData, is_initialized: bool) {
if let Some(sps) = ext.seq_parameter_sets.first().cloned()
&& sps.profile_space == 0
{
if !is_initialized {
stream.encoding_profile = Some(encoding_profile(&sps));
}
build_extended_format_info(ext, &sps, stream.base.pid);
}
if !ext.seq_parameter_sets.is_empty() {
stream.base.is_initialized = true;
}
stream.base.is_vbr = true;
}
fn encoding_profile(sps: &SeqParameterSet) -> String {
let mut profile = String::new();
profile.push_str(match sps.profile_idc {
1 => "Main",
2 => "Main 10",
3 => "Main Still",
_ => "",
});
if sps.level_idc > 0 {
let calc_level = f32::from(sps.level_idc) / 30.0;
let dec = sps.level_idc.wrapping_rem(10);
let level = if dec >= 1 { format!("{calc_level:.1}") } else { format!("{calc_level:.0}") };
profile.push_str(" @ Level ");
profile.push_str(&level);
profile.push_str(" @ ");
profile.push_str(if sps.tier_flag { "High" } else { "Main" });
}
profile
}
fn build_extended_format_info(ext: &mut HevcExtendedData, sps: &SeqParameterSet, pid: Pid) {
ext.extended_format_info.clear();
let info = &mut ext.extended_format_info;
let chroma = match sps.chroma_format_idc {
1 => "4:2:0",
2 => "4:2:2",
3 => "4:4:4",
_ => "",
};
if !chroma.is_empty() {
info.push(chroma.to_owned());
}
if sps.bit_depth_luma_minus8 == sps.bit_depth_chroma_minus8 {
info.push(format!("{} bits", u16::from(sps.bit_depth_luma_minus8).wrapping_add(8)));
}
if u16::from(sps.bit_depth_luma_minus8).wrapping_add(8) == 10
&& sps.chroma_format_idc == 1
&& sps.vui.video_signal_type_present_flag
&& sps.vui.colour_description_present_flag
&& sps.vui.colour_primaries == 9
&& sps.vui.transfer_characteristics == 16
&& (sps.vui.matrix_coefficients == 9 || sps.vui.matrix_coefficients == 10)
&& (!ext.mastering_display_color_primaries.is_empty() || ext.is_hdr10_plus)
{
info.push(
if pid.get() >= 4117 {
"Dolby Vision"
} else if ext.is_hdr10_plus {
"HDR10+"
} else {
"HDR10"
}
.to_owned(),
);
}
if sps.vui.video_signal_type_present_flag {
info.push(
if sps.vui.video_full_range_flag == 1 { "Full Range" } else { "Limited Range" }
.to_owned(),
);
if sps.vui.colour_description_present_flag {
info.push(colour_primaries(sps.vui.colour_primaries).to_owned());
info.push(transfer_characteristics(sps.vui.transfer_characteristics).to_owned());
info.push(matrix_coefficients(sps.vui.matrix_coefficients).to_owned());
}
}
if !ext.mastering_display_color_primaries.is_empty() {
info.push(format!(
"Mastering display color primaries: {}",
ext.mastering_display_color_primaries
));
}
if !ext.mastering_display_luminance.is_empty() {
info.push(format!("Mastering display luminance: {}", ext.mastering_display_luminance));
}
if ext.light_level_available && ext.maximum_content_light_level > 0 {
info.push(format!(
"Maximum Content Light Level: {} cd/m2",
ext.maximum_content_light_level
));
info.push(format!(
"Maximum Frame-Average Light Level: {} cd/m2",
ext.maximum_frame_average_light_level
));
}
}
#[cfg(test)]
mod tests {
use proptest::prelude::{any, proptest};
use super::{
Hevc, HevcExtendedData, StartCode, format_luminance_exact, match_color_volume, scan,
sei_bytes, within, within_white,
};
use crate::bitstream::TsStreamBuffer;
use crate::primitives::Pid;
use crate::stream::{TsFrameRate, TsStreamType, TsVideoFormat, TsVideoStream};
#[test]
fn the_exact_luminance_spelling_keeps_four_decimals_on_both_bounds() {
assert_eq!(
format_luminance_exact(10, 100_000_000),
"min: 0.0010 cd/m2, max: 10000.0000 cd/m2"
);
}
#[test]
fn the_exact_format_info_respells_only_the_luminance_entry() {
let ext = HevcExtendedData {
mastering_display_luminance_exact: "min: 0.0010 cd/m2, max: 1000.0000 cd/m2".to_owned(),
extended_format_info: vec![
"HDR10".to_owned(),
"Mastering display luminance: min: 0.0010 cd/m2, max: 1000 cd/m2".to_owned(),
],
..HevcExtendedData::default()
};
assert_eq!(
ext.extended_format_info_exact(),
["HDR10", "Mastering display luminance: min: 0.0010 cd/m2, max: 1000.0000 cd/m2"]
);
let plain = HevcExtendedData {
extended_format_info: vec!["Mastering display luminance: raw".to_owned()],
..HevcExtendedData::default()
};
assert_eq!(plain.extended_format_info_exact(), ["Mastering display luminance: raw"]);
}
#[test]
fn full_description_joins_the_exact_format_info() {
let mut stream = TsVideoStream::default();
stream.base.stream_type = TsStreamType::HevcVideo;
stream.extended_data = Some(HevcExtendedData {
mastering_display_luminance_exact: "min: 0.0010 cd/m2, max: 1000.0000 cd/m2".to_owned(),
extended_format_info: vec![
"Mastering display luminance: min: 0.0010 cd/m2, max: 1000 cd/m2".to_owned(),
],
..HevcExtendedData::default()
});
assert_eq!(
stream.description(),
"Mastering display luminance: min: 0.0010 cd/m2, max: 1000 cd/m2"
);
assert_eq!(
stream.full_description(),
"Mastering display luminance: min: 0.0010 cd/m2, max: 1000.0000 cd/m2"
);
}
#[derive(Default)]
struct Writer {
bits: Vec<bool>,
}
impl Writer {
fn new() -> Self {
Self::default()
}
fn bit(&mut self, value: bool) {
self.bits.push(value);
}
fn u(&mut self, value: u64, n: u32) {
let mut i = n;
while i > 0 {
i = i.wrapping_sub(1);
self.bit((value.wrapping_shr(i) & 1) == 1);
}
}
fn ue(&mut self, v: u32) {
let code = v.wrapping_add(1);
let len = 32_u32.wrapping_sub(code.leading_zeros());
let mut zeros = len.wrapping_sub(1);
while zeros > 0 {
self.bit(false);
zeros = zeros.wrapping_sub(1);
}
self.u(u64::from(code), len);
}
fn byte(&mut self, value: u8) {
self.u(u64::from(value), 8);
}
fn append(&mut self, other: &Self) {
for &b in &other.bits {
self.bit(b);
}
}
fn bytes(&self) -> Vec<u8> {
let mut out = Vec::new();
let mut cur: u8 = 0;
let mut n: u32 = 0;
for &b in &self.bits {
cur = cur.wrapping_shl(1).wrapping_add(u8::from(b));
n = n.wrapping_add(1);
if n == 8 {
out.push(cur);
cur = 0;
n = 0;
}
}
if n > 0 {
out.push(cur.wrapping_shl(8_u32.wrapping_sub(n)));
}
out
}
}
fn emulation_encode(rbsp: &[u8]) -> Vec<u8> {
let mut out = Vec::new();
let mut zeros: u32 = 0;
for &b in rbsp {
if zeros >= 2 && b <= 3 {
out.push(0x03);
zeros = 0;
}
out.push(b);
zeros = if b == 0 { zeros.wrapping_add(1) } else { 0 };
}
out
}
fn nal(nal_unit_type: u8, rbsp: &Writer) -> Vec<u8> {
let mut w = Writer::new();
w.bit(false); w.u(u64::from(nal_unit_type), 6); w.u(0, 6); w.u(1, 3); w.append(rbsp);
let mut out = vec![0x00, 0x00, 0x01];
out.extend_from_slice(&emulation_encode(&w.bytes()));
out
}
fn sei_message(payload_type: u8, payload: &[u8]) -> Vec<u8> {
let mut m = vec![payload_type, u8::try_from(payload.len()).unwrap()];
m.extend_from_slice(payload);
m
}
fn sei_nal(messages: &[Vec<u8>]) -> Vec<u8> {
let mut w = Writer::new();
for message in messages {
for &b in message {
w.byte(b);
}
}
w.byte(0x80); nal(39, &w)
}
fn mastering_payload(coords: [u16; 8], max_lum: u32, min_lum: u32) -> Vec<u8> {
let mut p = Vec::new();
for c in coords {
p.extend_from_slice(&c.to_be_bytes());
}
p.extend_from_slice(&max_lum.to_be_bytes());
p.extend_from_slice(&min_lum.to_be_bytes());
p
}
const DISPLAY_P3_COORDS: [u16; 8] = [13250, 34500, 7500, 3000, 34000, 16000, 15635, 16450];
fn light_level_payload(max_cll: u16, max_fall: u16) -> Vec<u8> {
let mut p = Vec::new();
p.extend_from_slice(&max_cll.to_be_bytes());
p.extend_from_slice(&max_fall.to_be_bytes());
p
}
fn hdr10_plus_payload() -> Vec<u8> {
vec![0xB5, 0x00, 0x3C, 0x00, 0x01, 0x04, 0x00, 0x40]
}
fn vps(id: u64) -> Vec<u8> {
let mut w = Writer::new();
w.u(id, 4); w.byte(0x80); nal(32, &w)
}
#[derive(Clone, Copy)]
#[expect(clippy::struct_excessive_bools, reason = "each bool is a distinct VUI bitstream flag")]
struct VuiConfig {
aspect_ratio_present: bool,
aspect_ratio_idc: u64,
overscan_present: bool,
video_signal_type_present: bool,
full_range: u64,
colour_description_present: bool,
colour_primaries: u64,
transfer_characteristics: u64,
matrix_coefficients: u64,
}
impl Default for VuiConfig {
fn default() -> Self {
Self {
aspect_ratio_present: false,
aspect_ratio_idc: 1,
overscan_present: false,
video_signal_type_present: true,
full_range: 0,
colour_description_present: true,
colour_primaries: 9,
transfer_characteristics: 16,
matrix_coefficients: 9,
}
}
}
#[derive(Clone, Copy)]
enum MalformedRps {
OuterCount,
InnerPicCounts,
LongTermCount,
}
#[derive(Clone)]
#[expect(clippy::struct_excessive_bools, reason = "each bool toggles a distinct SPS field")]
struct SpsConfig {
vps_id: u64,
max_sub_layers_minus1: u16,
profile_space: u64,
tier_flag: bool,
profile_idc: u64,
level_idc: u64,
sub_layer_flags: Vec<(bool, bool)>,
sps_id: u32,
chroma_format_idc: u32,
separate_colour_plane: bool,
conformance_window: bool,
bit_depth_luma_minus8: u32,
bit_depth_chroma_minus8: u32,
log2_max_poc_minus4: u32,
sub_layer_ordering_present: bool,
scaling_list_enabled: bool,
scaling_list_present: bool,
scaling_list_pred_mode: bool,
pcm_enabled: bool,
short_term_ref_pic_sets: u32,
inter_pred_rps: bool,
long_term_present: bool,
num_long_term: u32,
vui_present: bool,
vui: VuiConfig,
malformed_rps: Option<MalformedRps>,
}
impl Default for SpsConfig {
fn default() -> Self {
Self {
vps_id: 0,
max_sub_layers_minus1: 0,
profile_space: 0,
tier_flag: true,
profile_idc: 2,
level_idc: 153,
sub_layer_flags: Vec::new(),
sps_id: 0,
chroma_format_idc: 1,
separate_colour_plane: false,
conformance_window: false,
bit_depth_luma_minus8: 2,
bit_depth_chroma_minus8: 2,
log2_max_poc_minus4: 4,
sub_layer_ordering_present: true,
scaling_list_enabled: false,
scaling_list_present: false,
scaling_list_pred_mode: false,
pcm_enabled: false,
short_term_ref_pic_sets: 0,
inter_pred_rps: false,
long_term_present: false,
num_long_term: 0,
vui_present: true,
vui: VuiConfig::default(),
malformed_rps: None,
}
}
}
fn write_profile_tier_level(w: &mut Writer, cfg: &SpsConfig) {
w.u(cfg.profile_space, 2); w.bit(cfg.tier_flag); w.u(cfg.profile_idc, 5); w.u(0, 32); w.u(0, 4); w.u(0, 32); w.u(0, 12); w.u(cfg.level_idc, 8); for &(profile_present, level_present) in &cfg.sub_layer_flags {
w.bit(profile_present);
w.bit(level_present);
}
if cfg.max_sub_layers_minus1 > 0 {
let mut i = cfg.max_sub_layers_minus1;
while i < 8 {
w.u(0, 2);
i = i.wrapping_add(1);
}
}
for &(profile_present, level_present) in &cfg.sub_layer_flags {
if profile_present {
w.u(0, 32); w.u(0, 32);
w.u(0, 24);
}
if level_present {
w.u(0, 8); }
}
}
fn sps(cfg: &SpsConfig) -> Vec<u8> {
let mut w = Writer::new();
w.u(cfg.vps_id, 4); w.u(u64::from(cfg.max_sub_layers_minus1), 3); w.bit(false); write_profile_tier_level(&mut w, cfg);
w.ue(cfg.sps_id); w.ue(cfg.chroma_format_idc); if cfg.chroma_format_idc == 3 {
w.bit(cfg.separate_colour_plane); }
w.ue(0); w.ue(0); w.bit(cfg.conformance_window); if cfg.conformance_window {
w.ue(0); w.ue(0); w.ue(0); w.ue(0); }
w.ue(cfg.bit_depth_luma_minus8); w.ue(cfg.bit_depth_chroma_minus8); w.ue(cfg.log2_max_poc_minus4); w.bit(cfg.sub_layer_ordering_present); let iterations = if cfg.sub_layer_ordering_present {
cfg.max_sub_layers_minus1.wrapping_add(1)
} else {
1
};
for _ in 0..iterations {
w.ue(0); w.ue(0); w.ue(0); }
for _ in 0..6 {
w.ue(0); }
w.bit(cfg.scaling_list_enabled); if cfg.scaling_list_enabled {
w.bit(cfg.scaling_list_present); if cfg.scaling_list_present {
if cfg.scaling_list_pred_mode {
write_scaling_list_pred(&mut w);
} else {
write_scaling_list_data(&mut w);
}
}
}
w.u(0, 2); w.bit(cfg.pcm_enabled); if cfg.pcm_enabled {
w.u(0, 8); w.ue(0);
w.ue(0);
w.bit(false); }
if matches!(cfg.malformed_rps, Some(MalformedRps::OuterCount)) {
w.ue(0x7FFF_FFFF); } else if matches!(cfg.malformed_rps, Some(MalformedRps::InnerPicCounts)) {
w.ue(1); w.ue(0x7FFF_FFFF); w.ue(0x7FFF_FFFF); } else if cfg.inter_pred_rps {
w.ue(3); write_inter_pred_rps(&mut w);
} else {
w.ue(cfg.short_term_ref_pic_sets); write_short_term_ref_pic_sets(&mut w, cfg.short_term_ref_pic_sets);
}
if matches!(cfg.malformed_rps, Some(MalformedRps::LongTermCount)) {
w.bit(true); w.ue(0x7FFF_FFFF); } else {
w.bit(cfg.long_term_present); if cfg.long_term_present {
w.ue(cfg.num_long_term); for _ in 0..cfg.num_long_term {
w.u(0, cfg.log2_max_poc_minus4.wrapping_add(4)); w.bit(false); }
}
}
w.u(0, 2); w.bit(cfg.vui_present); if cfg.vui_present {
write_vui(&mut w, &cfg.vui);
}
w.byte(0x80); nal(33, &w)
}
fn write_scaling_list_data(w: &mut Writer) {
for size_id in 0..4 {
let matrices = if size_id == 3 { 2 } else { 6 };
for _ in 0..matrices {
w.bit(false); w.ue(0); }
}
}
fn write_scaling_list_pred(w: &mut Writer) {
for size_id in 0..4_u32 {
let matrices = if size_id == 3 { 2 } else { 6 };
let coef_num =
64_u32.min(1_u32.wrapping_shl(4_u32.wrapping_add(size_id.wrapping_mul(2))));
for _ in 0..matrices {
w.bit(true); if size_id > 1 {
w.ue(0); }
for _ in 0..coef_num {
w.ue(0); }
}
}
}
fn nal4(nal_unit_type: u8, rbsp: &Writer) -> Vec<u8> {
let mut out = vec![0x00];
out.extend_from_slice(&nal(nal_unit_type, rbsp));
out
}
fn write_short_term_ref_pic_sets(w: &mut Writer, num: u32) {
for idx in 0..num {
if idx > 0 {
w.bit(false); }
w.ue(1); w.ue(0); w.ue(0); w.bit(true); }
}
fn write_inter_pred_rps(w: &mut Writer) {
w.ue(2); w.ue(1); for _ in 0..3 {
w.ue(0); w.bit(true); }
w.bit(true); w.bit(false); w.ue(0); w.bit(true); w.bit(true); w.bit(false); w.bit(false); w.bit(false); w.bit(false); w.bit(true); w.bit(false); w.ue(0); w.bit(true); w.bit(true); w.bit(true); }
fn write_vui(w: &mut Writer, vui: &VuiConfig) {
w.bit(vui.aspect_ratio_present); if vui.aspect_ratio_present {
w.u(vui.aspect_ratio_idc, 8); if vui.aspect_ratio_idc == 0xFF {
w.u(0, 16); w.u(0, 16); }
}
w.bit(vui.overscan_present); if vui.overscan_present {
w.bit(false); }
w.bit(vui.video_signal_type_present); if vui.video_signal_type_present {
w.u(0, 3); w.u(vui.full_range, 1); w.bit(vui.colour_description_present); if vui.colour_description_present {
w.u(vui.colour_primaries, 8); w.u(vui.transfer_characteristics, 8); w.u(vui.matrix_coefficients, 8); }
}
w.byte(0x80); }
fn pps(pps_id: u32, sps_id: u32, dependent: bool, num_extra_bits: u64) -> Vec<u8> {
let mut w = Writer::new();
w.ue(pps_id); w.ue(sps_id); w.bit(dependent); w.bit(false); w.u(num_extra_bits, 3); w.byte(0x80); nal(34, &w)
}
fn slice(nal_unit_type: u8, pps_id: u32, slice_type: u32) -> Vec<u8> {
slice_seg(nal_unit_type, true, pps_id, 0, slice_type)
}
fn slice_seg(
nal_unit_type: u8,
first: bool,
pps_id: u32,
num_extra: u32,
slice_type: u32,
) -> Vec<u8> {
let mut w = Writer::new();
w.bit(first); if (16..=23).contains(&nal_unit_type) {
w.bit(false); }
w.ue(pps_id); if first {
w.u(0, num_extra); w.ue(slice_type); } else {
w.bit(false); }
w.byte(0x80); nal(nal_unit_type, &w)
}
fn sei_message_raw(payload_type: u8, declared_size: usize, payload: &[u8]) -> Vec<u8> {
let mut m = vec![payload_type];
let mut remaining = declared_size;
while remaining >= 255 {
m.push(0xFF);
remaining = remaining.wrapping_sub(255);
}
m.push(u8::try_from(remaining).unwrap());
m.extend_from_slice(payload);
m
}
fn buffer_of(nals: &[Vec<u8>]) -> TsStreamBuffer {
let mut data = Vec::new();
for n in nals {
data.extend_from_slice(n);
}
data.extend_from_slice(&[0xFF; 8]); let mut b = TsStreamBuffer::new();
b.add(&data, 0, data.len());
b.begin_read();
b
}
fn run(pid: u16, nals: &[Vec<u8>]) -> (TsVideoStream, Option<String>) {
let mut s = TsVideoStream::default();
s.base.stream_type = TsStreamType::HevcVideo;
s.base.pid = Pid::new(pid);
let mut b = buffer_of(nals);
let mut tag = None;
scan(&mut s, &mut b, &mut tag);
(s, tag)
}
fn hdr_unit(pid: u16, messages: &[Vec<u8>]) -> (TsVideoStream, Option<String>) {
run(
pid,
&[
vps(0),
sps(&SpsConfig::default()),
pps(0, 0, false, 0),
sei_nal(messages),
slice(19, 0, 2),
],
)
}
fn hdr_messages() -> Vec<Vec<u8>> {
vec![
sei_message(137, &mastering_payload(DISPLAY_P3_COORDS, 10_000_000, 1)),
sei_message(144, &light_level_payload(1033, 311)),
]
}
#[test]
fn hdr10_unit_reproduces_the_expected_desc() {
let (mut s, tag) = hdr_unit(4113, &hdr_messages());
s.set_video_format(TsVideoFormat::Videoformat2160p);
s.set_frame_rate(TsFrameRate::Framerate23_976);
s.aspect_ratio = crate::stream::TsAspectRatio::Aspect16_9;
assert_eq!(tag.as_deref(), Some("I"));
assert_eq!(s.codec_short_name(), "HEVC");
assert_eq!(s.codec_name(), "MPEG-H HEVC Video");
assert_eq!(
s.description(),
"2160p / 23.976 fps / 16:9 / Main 10 @ Level 5.1 @ High / 4:2:0 / 10 bits / HDR10 / \
Limited Range / BT.2020 / PQ / BT.2020 non-constant / Mastering display color \
primaries: Display P3 / Mastering display luminance: min: 0.0001 cd/m2, max: 1000 \
cd/m2 / Maximum Content Light Level: 1033 cd/m2 / Maximum Frame-Average Light \
Level: 311 cd/m2"
);
}
#[test]
fn dolby_vision_unit_uses_the_pid_gated_label() {
let (mut s, _) = hdr_unit(
4117,
&[sei_message(137, &mastering_payload(DISPLAY_P3_COORDS, 10_000_000, 1))],
);
s.set_video_format(TsVideoFormat::Videoformat1080p);
s.set_frame_rate(TsFrameRate::Framerate23_976);
s.aspect_ratio = crate::stream::TsAspectRatio::Aspect16_9;
assert_eq!(
s.description(),
"1080p / 23.976 fps / 16:9 / Main 10 @ Level 5.1 @ High / 4:2:0 / 10 bits / Dolby \
Vision / Limited Range / BT.2020 / PQ / BT.2020 non-constant / Mastering display \
color primaries: Display P3 / Mastering display luminance: min: 0.0001 cd/m2, max: \
1000 cd/m2"
);
}
#[test]
fn hdr10_plus_unit_uses_the_t35_gated_label() {
let (mut s, _) = hdr_unit(4113, &hdr_messages_hdr10_plus());
s.set_video_format(TsVideoFormat::Videoformat2160p);
s.set_frame_rate(TsFrameRate::Framerate23_976);
s.aspect_ratio = crate::stream::TsAspectRatio::Aspect16_9;
assert_eq!(
s.description(),
"2160p / 23.976 fps / 16:9 / Main 10 @ Level 5.1 @ High / 4:2:0 / 10 bits / HDR10+ / \
Limited Range / BT.2020 / PQ / BT.2020 non-constant / Mastering display color \
primaries: Display P3 / Mastering display luminance: min: 0.0010 cd/m2, max: 1000 \
cd/m2 / Maximum Content Light Level: 969 cd/m2 / Maximum Frame-Average Light \
Level: 230 cd/m2"
);
}
fn hdr_messages_hdr10_plus() -> Vec<Vec<u8>> {
vec![
sei_message(137, &mastering_payload(DISPLAY_P3_COORDS, 10_000_000, 10)),
sei_message(144, &light_level_payload(969, 230)),
sei_message(4, &hdr10_plus_payload()),
]
}
#[test]
fn no_start_codes_leaves_the_stream_uninitialized() {
let (s, tag) = run(4113, &[]);
assert!(s.encoding_profile.is_none());
assert!(!s.base.is_initialized);
assert_eq!(tag, None);
assert!(s.base.is_vbr);
}
fn sps_stream(cfg: &SpsConfig) -> TsVideoStream {
run(4113, &[vps(0), sps(cfg)]).0
}
fn sps_desc(cfg: &SpsConfig) -> String {
sps_stream(cfg).description()
}
#[test]
fn colour_primaries_table_covers_every_arm() {
let cases = [
(1_u8, "BT.709"),
(4, "BT.470 System M"),
(5, "BT.601 PAL"),
(6, "BT.601 NTSC"),
(7, "SMPTE 240M"),
(8, "Generic film"),
(9, "BT.2020"),
(10, "XYZ"),
(11, "DCI P3"),
(12, "Display P3"),
(22, "EBU Tech 3213"),
(0, ""),
(2, ""),
(255, ""),
];
for (code, name) in cases {
assert_eq!(super::colour_primaries(code), name, "primaries {code}");
}
}
#[test]
fn transfer_characteristics_table_covers_every_arm() {
let cases = [
(1_u8, "BT.709"),
(4, "BT.470 System M"),
(5, "BT.470 System B/G"),
(6, "BT.601"),
(7, "SMPTE 240M"),
(8, "Linear"),
(9, "Logarithmic (100:1)"),
(10, "Logarithmic (316.22777:1)"),
(11, "xvYCC"),
(12, "BT.1361"),
(13, "sRGB/sYCC"),
(14, "BT.2020 (10-bit)"),
(15, "BT.2020 (12-bit)"),
(16, "PQ"),
(17, "SMPTE 428M"),
(18, "HLG"),
(0, ""),
(255, ""),
];
for (code, name) in cases {
assert_eq!(super::transfer_characteristics(code), name, "transfer {code}");
}
}
#[test]
fn matrix_coefficients_table_covers_every_arm() {
let cases = [
(0_u8, "Identity"),
(1, "BT.709"),
(4, "FCC 73.682"),
(5, "BT.470 System B/G"),
(6, "BT.601"),
(7, "SMPTE 240M"),
(8, "YCgCo"),
(9, "BT.2020 non-constant"),
(10, "BT.2020 constant"),
(11, "Y'D'zD'x"),
(12, "Chromaticity-derived non-constant"),
(13, "Chromaticity-derived constant"),
(14, "ICtCp"),
(2, ""),
(255, ""),
];
for (code, name) in cases {
assert_eq!(super::matrix_coefficients(code), name, "matrix {code}");
}
}
#[test]
fn default_sps_desc_has_no_hdr_label_without_mastering() {
assert_eq!(
sps_desc(&SpsConfig::default()),
"Main 10 @ Level 5.1 @ High / 4:2:0 / 10 bits / Limited Range / BT.2020 / PQ / \
BT.2020 non-constant"
);
}
#[test]
fn profile_idc_variants_map_to_their_names() {
let profile = |idc: u64| {
let cfg = SpsConfig { profile_idc: idc, ..SpsConfig::default() };
sps_stream(&cfg).encoding_profile
};
assert_eq!(profile(1).as_deref(), Some("Main @ Level 5.1 @ High"));
assert_eq!(profile(2).as_deref(), Some("Main 10 @ Level 5.1 @ High"));
assert_eq!(profile(3).as_deref(), Some("Main Still @ Level 5.1 @ High"));
assert_eq!(profile(5).as_deref(), Some(" @ Level 5.1 @ High"));
assert_eq!(profile(0).as_deref(), Some(" @ Level 5.1 @ High"));
}
fn parse_profile_tier_level(profile_idc: u8, flags: u32, level_idc: u8) -> (u16, u16) {
let mut bytes = vec![profile_idc & 0x1F];
bytes.extend_from_slice(&flags.to_be_bytes());
bytes.extend_from_slice(&[0_u8; 6]); bytes.push(level_idc);
bytes.push(0x80); let mut ext = HevcExtendedData::default();
let mut b = TsStreamBuffer::new();
let len = bytes.len();
b.add(&bytes, 0, len);
b.begin_read();
let mut h = Hevc { ext: &mut ext, buffer: &mut b, is_initialized: false };
let ptl = h.profile_tier_level(0);
(ptl.profile_idc, ptl.level_idc)
}
#[test]
fn profile_idc_zero_recovers_from_the_compatibility_flags() {
let recover = |flags: u32| parse_profile_tier_level(0, flags, 153);
assert_eq!(recover(0x2000_0000), (2, 153));
assert_eq!(recover(0x4000_0000), (1, 153));
assert_eq!(recover(0x6000_0000), (1, 153));
assert_eq!(recover(0x0000_0001), (31, 153));
assert_eq!(recover(0x8000_0000), (0, 153));
assert_eq!(recover(0x0000_0000), (0, 153));
assert_eq!(parse_profile_tier_level(2, 0x4000_0000, 153), (2, 153));
}
#[test]
fn level_and_tier_variants() {
let profile = |level: u64, tier: bool| {
let cfg = SpsConfig { level_idc: level, tier_flag: tier, ..SpsConfig::default() };
sps_stream(&cfg).encoding_profile
};
assert_eq!(profile(90, false).as_deref(), Some("Main 10 @ Level 3 @ Main"));
assert_eq!(profile(93, true).as_deref(), Some("Main 10 @ Level 3.1 @ High"));
assert_eq!(profile(0, true).as_deref(), Some("Main 10"));
}
#[test]
fn profile_space_nonzero_skips_the_profile_but_still_initializes() {
let cfg = SpsConfig { profile_space: 1, ..SpsConfig::default() };
let s = sps_stream(&cfg);
assert!(s.encoding_profile.is_none());
assert!(s.base.is_initialized); }
#[test]
fn sps_early_returns_register_no_parameter_set() {
let none = |cfg: SpsConfig| {
let s = sps_stream(&cfg);
assert!(s.encoding_profile.is_none(), "expected no profile");
assert!(!s.base.is_initialized, "expected uninitialized");
};
none(SpsConfig { vps_id: 1, ..SpsConfig::default() }); none(SpsConfig { chroma_format_idc: 4, ..SpsConfig::default() }); none(SpsConfig { bit_depth_luma_minus8: 7, ..SpsConfig::default() }); none(SpsConfig { bit_depth_chroma_minus8: 7, ..SpsConfig::default() }); none(SpsConfig { log2_max_poc_minus4: 13, ..SpsConfig::default() }); none(SpsConfig { sps_id: 64, ..SpsConfig::default() }); }
#[test]
fn an_undersized_sei_message_is_realigned_to_its_payload_size() {
let light = [0x04, 0x09, 0x01, 0x37, 0x55, 0x66, 0x77, 0x88]; let (s, _) = hdr_unit_with(
4113,
&[
sei_message_raw(144, light.len(), &light),
sei_message(137, &mastering_payload(DISPLAY_P3_COORDS, 10_000_000, 1)),
],
);
let desc = s.description();
assert!(desc.contains("Maximum Content Light Level: 1033"), "{desc}");
assert!(desc.contains("Display P3"), "{desc}");
}
#[test]
fn a_type4_sei_smaller_than_eight_bytes_realigns_backward_not_a_huge_skip() {
let (s, _) = hdr_unit_with(
4113,
&[
sei_message_raw(4, 2, &[0xB5, 0x00]), sei_message(144, &light_level_payload(1033, 311)),
sei_message(137, &mastering_payload(DISPLAY_P3_COORDS, 10_000_000, 1)),
],
);
let desc = s.description();
assert!(desc.contains("Maximum Content Light Level: 1033"), "{desc}");
assert!(desc.contains("Display P3"), "{desc}");
}
#[test]
fn chroma_format_strings_and_bit_depth() {
let chroma = |idc: u32, sep: bool| {
let cfg = SpsConfig {
chroma_format_idc: idc,
separate_colour_plane: sep,
..SpsConfig::default()
};
sps_desc(&cfg)
};
assert!(chroma(2, false).contains(" / 4:2:2 / "), "4:2:2");
assert!(chroma(3, false).contains(" / 4:4:4 / "), "4:4:4");
assert!(chroma(3, true).contains(" / 4:4:4 / "), "4:4:4 separate plane");
let mono = chroma(0, false);
assert!(!mono.contains("4:2:0"), "no chroma label");
assert!(mono.contains(" 10 bits "), "bit depth still shown");
}
#[test]
fn bit_depth_mismatch_omits_the_bits_fragment() {
let cfg = SpsConfig { bit_depth_chroma_minus8: 0, ..SpsConfig::default() };
let desc = sps_desc(&cfg);
assert!(!desc.contains("bits"), "luma != chroma depth → no bits fragment: {desc}");
}
const DEFAULT_DESC: &str = "Main 10 @ Level 5.1 @ High / 4:2:0 / 10 bits / Limited Range / \
BT.2020 / PQ / BT.2020 non-constant";
#[test]
fn optional_sps_blocks_preserve_the_colour_position() {
let same = |cfg: SpsConfig| assert_eq!(sps_desc(&cfg), DEFAULT_DESC);
same(SpsConfig { conformance_window: true, ..SpsConfig::default() });
same(SpsConfig {
scaling_list_enabled: true,
scaling_list_present: true,
..SpsConfig::default()
});
same(SpsConfig {
scaling_list_enabled: true,
scaling_list_present: true,
scaling_list_pred_mode: true,
..SpsConfig::default()
});
same(SpsConfig { scaling_list_enabled: true, ..SpsConfig::default() });
same(SpsConfig { pcm_enabled: true, ..SpsConfig::default() });
same(SpsConfig { short_term_ref_pic_sets: 2, ..SpsConfig::default() });
same(SpsConfig { inter_pred_rps: true, ..SpsConfig::default() });
same(SpsConfig { long_term_present: true, num_long_term: 2, ..SpsConfig::default() });
same(SpsConfig { sub_layer_ordering_present: false, ..SpsConfig::default() });
}
#[test]
fn sub_layers_in_profile_tier_level_preserve_position() {
let with_sub_layers = |n: u16, flags: Vec<(bool, bool)>| {
let cfg = SpsConfig {
max_sub_layers_minus1: n,
sub_layer_flags: flags,
..SpsConfig::default()
};
assert_eq!(sps_desc(&cfg), DEFAULT_DESC);
};
with_sub_layers(1, vec![(true, true)]); with_sub_layers(1, vec![(false, false)]); with_sub_layers(2, vec![(true, false), (false, true)]); with_sub_layers(7, vec![(false, false); 7]); }
#[test]
fn vui_variants_preserve_the_colour_position() {
let vui = |v: VuiConfig| {
let cfg = SpsConfig { vui: v, ..SpsConfig::default() };
assert_eq!(sps_desc(&cfg), DEFAULT_DESC);
};
vui(VuiConfig { aspect_ratio_present: true, aspect_ratio_idc: 1, ..VuiConfig::default() });
vui(VuiConfig {
aspect_ratio_present: true,
aspect_ratio_idc: 0xFF,
..VuiConfig::default()
});
vui(VuiConfig { overscan_present: true, ..VuiConfig::default() });
}
#[test]
fn vui_full_range_and_missing_colour_description() {
let full = SpsConfig {
vui: VuiConfig { full_range: 1, ..VuiConfig::default() },
..SpsConfig::default()
};
assert!(sps_desc(&full).contains(" / Full Range / "), "full range");
let no_colour = SpsConfig {
vui: VuiConfig { colour_description_present: false, ..VuiConfig::default() },
..SpsConfig::default()
};
assert_eq!(
sps_desc(&no_colour),
"Main 10 @ Level 5.1 @ High / 4:2:0 / 10 bits / Limited Range"
);
let no_signal = SpsConfig {
vui: VuiConfig { video_signal_type_present: false, ..VuiConfig::default() },
..SpsConfig::default()
};
assert_eq!(sps_desc(&no_signal), "Main 10 @ Level 5.1 @ High / 4:2:0 / 10 bits");
}
#[test]
fn bt2020_constant_matrix_still_gates_hdr() {
let cfg = SpsConfig {
vui: VuiConfig { matrix_coefficients: 10, ..VuiConfig::default() },
..SpsConfig::default()
};
let (s, _) = run(
4113,
&[
vps(0),
sps(&cfg),
pps(0, 0, false, 0),
sei_nal(&[sei_message(137, &mastering_payload(DISPLAY_P3_COORDS, 10_000_000, 1))]),
slice(19, 0, 2),
],
);
let desc = s.description();
assert!(desc.contains(" / HDR10 / "), "{desc}");
assert!(desc.contains(" / BT.2020 constant"), "{desc}");
}
#[test]
fn no_vui_present_still_initializes() {
let cfg = SpsConfig { vui_present: false, ..SpsConfig::default() };
assert_eq!(sps_desc(&cfg), "Main 10 @ Level 5.1 @ High / 4:2:0 / 10 bits");
}
#[test]
fn four_byte_start_codes_are_recognized() {
let (s, tag) = run(
4113,
&[
nal4(32, &{
let mut w = Writer::new();
w.u(0, 4);
w.byte(0x80);
w
}),
sps(&SpsConfig::default()),
pps(0, 0, false, 0),
sei_nal(&[sei_message(137, &mastering_payload(DISPLAY_P3_COORDS, 10_000_000, 1))]),
nal4(19, &{
let mut w = Writer::new();
w.bit(true); w.bit(false); w.ue(0); w.ue(2); w.byte(0x80);
w
}),
],
);
assert_eq!(tag.as_deref(), Some("I"));
let desc = s.description();
assert!(desc.contains("HDR10"), "{desc}");
}
fn slice_unit(pps_dependent: bool, slices: &[Vec<u8>]) -> Option<String> {
let mut nals = vec![vps(0), sps(&SpsConfig::default()), pps(0, 0, pps_dependent, 0)];
nals.extend_from_slice(slices);
run(4113, &nals).1
}
#[test]
fn slice_type_maps_to_the_picture_tag() {
assert_eq!(slice_unit(false, &[slice(19, 0, 0)]), Some("P".to_owned()));
assert_eq!(slice_unit(false, &[slice(19, 0, 1)]), Some("B".to_owned()));
assert_eq!(slice_unit(false, &[slice(19, 0, 2)]), Some("I".to_owned()));
assert_eq!(slice_unit(false, &[slice(19, 0, 3)]), None);
}
#[test]
fn non_irap_slice_skips_the_no_output_flag() {
assert_eq!(slice_unit(false, &[slice(1, 0, 2)]), Some("I".to_owned()));
}
#[test]
fn num_extra_slice_header_bits_are_skipped() {
let unit = |dependent_pps_extra: u64| {
let nals = vec![
vps(0),
sps(&SpsConfig::default()),
pps(0, 0, false, dependent_pps_extra),
slice_seg(19, true, 0, u32::try_from(dependent_pps_extra).unwrap(), 2),
];
run(4113, &nals).1
};
assert_eq!(unit(3), Some("I".to_owned()));
}
#[test]
fn dependent_and_out_of_range_slices_yield_no_tag() {
assert_eq!(slice_unit(true, &[slice_seg(19, false, 0, 0, 2)]), None);
assert_eq!(slice_unit(false, &[slice_seg(19, false, 0, 0, 2)]), None);
assert_eq!(slice_unit(false, &[slice(19, 7, 2)]), None);
}
#[test]
fn pic_parameter_set_id_ranges_are_validated() {
let unit = |pps_id: u32, seq_id: u32| {
let nals = vec![
vps(0),
sps(&SpsConfig::default()),
pps(pps_id, seq_id, false, 0),
slice(19, 0, 2),
];
run(4113, &nals).1
};
assert_eq!(unit(64, 0), None); assert_eq!(unit(0, 16), None); assert_eq!(unit(0, 5), None); assert_eq!(unit(0, 1), None); }
#[test]
fn unknown_nal_types_and_unknown_sei_are_skipped() {
let (s, _) = run(
4113,
&[
vps(0),
sps(&SpsConfig::default()),
pps(0, 0, false, 0),
nal(35, &{
let mut w = Writer::new();
w.byte(0x80);
w
}),
sei_nal(&[
sei_message(100, &[1, 2, 3]), sei_message(137, &mastering_payload(DISPLAY_P3_COORDS, 10_000_000, 1)),
sei_message(144, &light_level_payload(1033, 311)),
]),
slice(19, 0, 2),
],
);
let desc = s.description();
assert!(desc.contains(" / HDR10 / "), "{desc}");
assert!(desc.contains("Maximum Content Light Level: 1033"));
}
#[test]
fn multi_byte_sei_payload_type_is_summed() {
let unknown = vec![0xFF, 0x05, 0x03, 0x11, 0x22, 0x33];
let (s, _) = hdr_unit_with(
4113,
&[unknown, sei_message(137, &mastering_payload(DISPLAY_P3_COORDS, 10_000_000, 1))],
);
let desc = s.description();
assert!(desc.contains("Display P3"), "{desc}");
}
#[test]
fn an_oversized_sei_payload_size_aborts_the_element() {
let nals = vec![
vps(0),
sps(&SpsConfig::default()),
pps(0, 0, false, 0),
sei_nal(&[sei_message_raw(137, 4096, &[0x00; 4])]),
slice(19, 0, 2),
];
let (s, _) = run(4113, &nals);
let desc = s.description();
assert!(!desc.contains("HDR10"), "{desc}");
}
#[test]
fn an_sei_at_the_buffer_end_terminates_the_scan() {
let (s, _) = run(
4113,
&[
vps(0),
sps(&SpsConfig::default()),
sei_nal(&[sei_message(137, &mastering_payload(DISPLAY_P3_COORDS, 10_000_000, 1))]),
],
);
let desc = s.description();
assert!(desc.contains("Display P3"), "{desc}");
}
#[test]
fn a_trailing_start_code_with_no_nal_body_ends_the_loop() {
let mut b = TsStreamBuffer::new();
let data = [0xFF, 0xFF, 0x00, 0x00, 0x00, 0x01];
b.add(&data, 0, data.len());
b.begin_read();
let mut s = TsVideoStream::default();
s.base.stream_type = TsStreamType::HevcVideo;
let mut tag = None;
scan(&mut s, &mut b, &mut tag);
assert!(!s.base.is_initialized);
}
#[test]
fn post_init_skips_parameter_sets_but_still_collects_hdr_sei() {
let mut s = TsVideoStream::default();
s.base.stream_type = TsStreamType::HevcVideo;
s.base.pid = Pid::new(4113);
let mut b1 =
buffer_of(&[vps(0), sps(&SpsConfig::default()), pps(0, 0, false, 0), slice(19, 0, 2)]);
scan(&mut s, &mut b1, &mut None);
assert!(s.base.is_initialized);
assert_eq!(s.encoding_profile.as_deref(), Some("Main 10 @ Level 5.1 @ High"));
let desc_before = s.description();
assert!(!desc_before.contains("HDR10"), "no HDR label yet: {desc_before}");
let mut b2 = buffer_of(&[
vps(0),
sps(&SpsConfig { profile_idc: 1, ..SpsConfig::default() }),
pps(0, 0, false, 0),
sei_nal(&hdr_messages_hdr10_plus()),
slice(19, 0, 0),
]);
let mut tag = None;
scan(&mut s, &mut b2, &mut tag);
assert_eq!(tag.as_deref(), Some("P"));
assert_eq!(s.encoding_profile.as_deref(), Some("Main 10 @ Level 5.1 @ High"));
let desc = s.description();
assert!(desc.contains("HDR10+"), "{desc}");
assert!(desc.contains("Mastering display color primaries: Display P3"), "{desc}");
assert!(desc.contains("Maximum Content Light Level: 969 cd/m2"), "{desc}");
}
#[test]
fn a_repeated_post_init_hdr_sei_is_not_duplicated() {
let mut s = TsVideoStream::default();
s.base.stream_type = TsStreamType::HevcVideo;
s.base.pid = Pid::new(4113);
let unit = || {
vec![
vps(0),
sps(&SpsConfig::default()),
pps(0, 0, false, 0),
sei_nal(&[sei_message(137, &mastering_payload(DISPLAY_P3_COORDS, 10_000_000, 1))]),
slice(19, 0, 2),
]
};
let mut b1 = buffer_of(&unit());
scan(&mut s, &mut b1, &mut None);
let mut b2 = buffer_of(&unit());
scan(&mut s, &mut b2, &mut None);
let desc = s.description();
assert_eq!(
desc.matches("Mastering display color primaries").count(),
1,
"mastering fragment must appear once: {desc}"
);
assert_eq!(desc.matches("4:2:0").count(), 1, "no fragment duplicated: {desc}");
}
#[test]
fn an_hdr10_plus_stream_without_a_mastering_display_is_labelled_hdr10_plus() {
let (s, _) = hdr_unit_with(4113, &[sei_message(4, &hdr10_plus_payload())]);
let desc = s.description();
assert!(desc.contains("HDR10+"), "{desc}");
assert!(!desc.contains("Mastering display"), "no mastering line exists: {desc}");
assert!(desc.contains("10 bits / HDR10+ / Limited Range"), "{desc}");
}
fn hdr_unit_with(pid: u16, messages: &[Vec<u8>]) -> (TsVideoStream, Option<String>) {
run(
pid,
&[
vps(0),
sps(&SpsConfig::default()),
pps(0, 0, false, 0),
sei_nal(messages),
slice(19, 0, 2),
],
)
}
fn mastering_desc(coords: [u16; 8], max_lum: u32, min_lum: u32) -> String {
hdr_unit_with(4113, &[sei_message(137, &mastering_payload(coords, max_lum, min_lum))])
.0
.description()
}
#[test]
fn mastering_display_matches_each_known_colour_volume() {
let primaries = |coords: [u16; 8]| {
let d = mastering_desc(coords, 10_000_000, 1);
d.split("Mastering display color primaries: ")
.nth(1)
.and_then(|s| s.split(" / ").next())
.unwrap()
.to_owned()
};
assert_eq!(primaries([15000, 30000, 7500, 3000, 32000, 16500, 15635, 16450]), "BT.709");
assert_eq!(primaries([8500, 39850, 6550, 2300, 35400, 14600, 15635, 16450]), "BT.2020");
assert_eq!(primaries([13250, 34500, 7500, 3000, 34000, 16000, 15700, 17550]), "DCI P3");
}
#[test]
fn unrecognized_primaries_format_as_raw_coordinates() {
let desc = mastering_desc([1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000], 10_000_000, 1);
assert!(
desc.contains(
"Mastering display color primaries: R: x=0.100000 y=0.120000, G: x=0.020000 \
y=0.040000, B: x=0.060000 y=0.080000, White point: x=0.140000 y=0.160000"
),
"{desc}"
);
}
#[test]
fn a_max_value_primary_is_invalid_and_formats_raw() {
let desc =
mastering_desc([13250, 34500, 7500, 3000, 34000, 16000, 15635, 0xFFFF], 10_000_000, 1);
assert!(desc.contains("White point: x=0.312700 y=1.310700"), "{desc}");
}
#[test]
fn mastering_luminance_uses_fractional_max_above_the_int_range() {
let desc = mastering_desc(DISPLAY_P3_COORDS, 0x8000_0000, 1);
assert!(desc.contains("max: 214748.3648 cd/m2"), "{desc}");
}
#[test]
fn hdr10_plus_registration_requires_the_prefix_and_a_window_in_range() {
let label = |t35: Vec<u8>| {
let (s, _) = hdr_unit_with(
4113,
&[
sei_message(137, &mastering_payload(DISPLAY_P3_COORDS, 10_000_000, 1)),
sei_message(4, &t35),
],
);
if s.description().contains("HDR10+") { "HDR10+" } else { "HDR10" }
};
assert_eq!(label(vec![0xB5, 0x00, 0x3C, 0x00, 0x01, 0x04, 0x00, 0x40]), "HDR10+"); assert_eq!(label(vec![0xB5, 0x00, 0x3C, 0x00, 0x01, 0x04, 0x01, 0x40]), "HDR10+"); assert_eq!(label(vec![0xB5, 0x00, 0x3C, 0x00, 0x01, 0x04, 0x02, 0x40]), "HDR10+"); assert_eq!(label(vec![0xB5, 0x00, 0x3C, 0x00, 0x01, 0x04, 0x00, 0x80]), "HDR10+"); assert_eq!(label(vec![0xB5, 0x00, 0x3C, 0x00, 0x01, 0x04, 0x00, 0xC0]), "HDR10+"); assert_eq!(label(vec![0xB6, 0x00, 0x3C, 0x00, 0x01, 0x04, 0x00, 0x40]), "HDR10"); assert_eq!(label(vec![0xB5, 0x00, 0x3D, 0x00, 0x01, 0x04, 0x00, 0x40]), "HDR10"); assert_eq!(label(vec![0xB5, 0x00, 0x3C, 0x00, 0x02, 0x04, 0x00, 0x40]), "HDR10"); assert_eq!(label(vec![0xB5, 0x00, 0x3C, 0x00, 0x01, 0x05, 0x00, 0x40]), "HDR10"); assert_eq!(label(vec![0xB5, 0x00, 0x3C, 0x00, 0x01, 0x04, 0x00, 0x00]), "HDR10"); }
fn should_continue(len: usize, pos: i64, is_initialized: bool, frame_type_read: bool) -> bool {
let mut ext = HevcExtendedData::default();
let mut b = TsStreamBuffer::new();
b.add(&vec![0_u8; len], 0, len);
b.begin_read();
b.seek(pos, crate::bitstream::SeekOrigin::Begin);
let h = Hevc { ext: &mut ext, buffer: &mut b, is_initialized };
h.scan_should_continue(frame_type_read)
}
#[test]
fn scan_should_continue_guards_position_and_init() {
assert!(should_continue(10, 0, false, false));
assert!(!should_continue(10, 7, false, false));
assert!(should_continue(10, 0, false, true));
assert!(!should_continue(10, 0, true, true));
assert!(should_continue(10, 0, true, false));
}
fn find_start(data: &[u8]) -> (bool, u64) {
let mut ext = HevcExtendedData::default();
let mut b = TsStreamBuffer::new();
b.add(data, 0, data.len());
b.begin_read();
let mut h = Hevc { ext: &mut ext, buffer: &mut b, is_initialized: false };
let found = matches!(h.find_start_code(false), StartCode::Found);
(found, h.buffer.position())
}
#[test]
fn find_start_code_locates_three_and_four_byte_codes() {
assert_eq!(find_start(&[0x00, 0x00, 0x01, 0xAA, 0xBB, 0xCC, 0xDD]), (true, 3));
assert_eq!(find_start(&[0x00, 0x00, 0x00, 0x01, 0xAA, 0xBB, 0xCC, 0xDD]), (true, 4));
assert_eq!(find_start(&[0xFF, 0x00, 0x00, 0x01, 0xAA, 0xBB, 0xCC]), (true, 4));
}
#[test]
fn find_start_code_rejects_near_miss_byte_patterns() {
assert!(!find_start(&[0x00, 0x00, 0x00, 0x02, 0xFF, 0xFF, 0xFF, 0xFF]).0); assert!(!find_start(&[0x00, 0x00, 0x02, 0x01, 0xFF, 0xFF, 0xFF, 0xFF]).0); assert!(!find_start(&[0x00, 0x02, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF]).0); }
fn probe(data: &[u8]) -> (Option<u64>, u64) {
let mut ext = HevcExtendedData::default();
let mut b = TsStreamBuffer::new();
b.add(data, 0, data.len());
b.begin_read();
let mut h = Hevc { ext: &mut ext, buffer: &mut b, is_initialized: false };
let result = h.probe_start_code();
(result, h.buffer.position())
}
#[test]
fn probe_start_code_classifies_each_byte() {
assert_eq!(probe(&[0x00, 0x00, 0x00, 0x01, 0xAA]), (Some(4), 4));
assert_eq!(probe(&[0x00, 0x00, 0x01, 0xAA]), (Some(3), 3));
assert_eq!(probe(&[0xFF, 0x00, 0x00, 0x01, 0xAA]).0, None); assert_eq!(probe(&[0x00, 0xFF, 0x00, 0x01, 0xAA]).0, None); assert_eq!(probe(&[0x00, 0x00, 0xFF, 0x01, 0xAA]).0, None); assert_eq!(probe(&[0x00, 0x00, 0x00, 0xFF, 0xAA]).0, None); assert_eq!(probe(&[0xFF, 0x00, 0x01, 0xAA]).0, None); assert_eq!(probe(&[0x00, 0xFF, 0x01, 0xAA]).0, None); assert_eq!(probe(&[0x00, 0x00, 0xFF, 0xAA]).0, None); }
#[test]
fn sei_bytes_converts_byte_counts() {
assert_eq!(sei_bytes(0), 0);
assert_eq!(sei_bytes(24), 24);
assert_eq!(sei_bytes(4096), 4096);
}
#[test]
fn match_color_volume_requires_every_coordinate_aligned() {
assert_eq!(
match_color_volume(&[13250, 34500, 7500, 3000, 34000, 16000, 15635, 16450], 0, 1, 2),
Some(12)
);
assert_eq!(
match_color_volume(&[7500, 3000, 34000, 16000, 13250, 34500, 15635, 16450], 2, 0, 1),
Some(12)
);
assert_eq!(match_color_volume(&[0; 8], 0, 1, 2), None);
}
#[test]
fn primary_and_white_point_tolerances_are_half_open() {
let reference = [100, 0, 0, 0, 0, 0, 0, 0];
assert!(within(&[124, 0, 0, 0, 0, 0, 0, 0], 0, 0, &reference, 0));
assert!(!within(&[125, 0, 0, 0, 0, 0, 0, 0], 0, 0, &reference, 0));
let white = [0, 0, 0, 0, 0, 0, 100, 0];
assert!(within_white(&[0, 0, 0, 0, 0, 0, 102, 0], 0, &white));
assert!(!within_white(&[0, 0, 0, 0, 0, 0, 103, 0], 0, &white));
}
#[test]
fn an_sei_message_ending_exactly_at_the_buffer_end_is_parsed() {
let coords = [0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0x7777, 0x8888];
let payload = mastering_payload(coords, 0x1122_3344, 0x5566_7788);
let mut data = vec![137_u8, 24];
data.extend_from_slice(&payload);
assert_eq!(data.len(), 26);
let mut ext = HevcExtendedData::default();
let mut b = TsStreamBuffer::new();
b.add(&data, 0, data.len());
b.begin_read();
Hevc { ext: &mut ext, buffer: &mut b, is_initialized: false }.sei();
assert!(!ext.mastering_display_color_primaries.is_empty());
}
#[test]
fn bit_depth_and_log2_at_their_maxima_still_register() {
let cfg = SpsConfig {
bit_depth_luma_minus8: 6,
bit_depth_chroma_minus8: 6,
log2_max_poc_minus4: 12,
..SpsConfig::default()
};
let desc = sps_desc(&cfg);
assert!(desc.contains(" / 14 bits"), "{desc}");
}
#[test]
fn zero_max_content_light_level_omits_the_light_level_lines() {
let (s, _) = hdr_unit_with(
4113,
&[
sei_message(137, &mastering_payload(DISPLAY_P3_COORDS, 10_000_000, 1)),
sei_message(144, &light_level_payload(0, 0)),
],
);
let desc = s.description();
assert!(!desc.contains("Maximum Content Light Level"), "{desc}");
assert!(desc.contains("Display P3"), "{desc}"); }
#[test]
fn a_malformed_sps_ref_pic_set_count_terminates_quickly() {
let unit: &[u8] = &[
0, 0, 1, 64, 1, 8, 0, 0, 0, 1, 66, 1, 0, 34, 0, 0, 3, 0, 0, 3, 0, 0, 3, 1, 0, 0, 0, 0,
0, 0, 1, 127, 240, 137, 248, 183, 127, 174, 0, 0, 0, 1, 68, 1, 193, 0, 0, 0, 1, 78, 1,
137, 24, 51, 194, 134, 196, 29, 76, 11, 126, 132, 208, 62, 128, 61, 19, 64, 66, 0, 152,
150, 128, 0, 0, 3, 0, 1, 144, 4, 4, 9, 1, 55, 128, 0, 0, 1, 38, 1, 174, 0,
];
let mut s = TsVideoStream::default();
s.base.stream_type = TsStreamType::HevcVideo;
let mut b = TsStreamBuffer::new();
b.add(unit, 0, unit.len());
b.begin_read();
let mut tag = None;
scan(&mut s, &mut b, &mut tag);
}
#[test]
fn a_malformed_sps_outer_ref_pic_set_count_terminates() {
let cfg =
SpsConfig { malformed_rps: Some(MalformedRps::OuterCount), ..SpsConfig::default() };
let (stream, _) = run(0, &[vps(0), sps(&cfg), slice(19, 0, 2)]);
assert_eq!(stream.base.stream_type, TsStreamType::HevcVideo); }
#[test]
fn a_malformed_sps_inner_pic_counts_terminate() {
let cfg =
SpsConfig { malformed_rps: Some(MalformedRps::InnerPicCounts), ..SpsConfig::default() };
let (stream, _) = run(0, &[vps(0), sps(&cfg), slice(19, 0, 2)]);
assert_eq!(stream.base.stream_type, TsStreamType::HevcVideo); }
#[test]
fn a_malformed_sps_long_term_count_terminates() {
let cfg =
SpsConfig { malformed_rps: Some(MalformedRps::LongTermCount), ..SpsConfig::default() };
let (stream, _) = run(0, &[vps(0), sps(&cfg), slice(19, 0, 2)]);
assert_eq!(stream.base.stream_type, TsStreamType::HevcVideo); }
proptest! {
#[test]
fn scan_never_panics_on_arbitrary_bytes(data in any::<Vec<u8>>()) {
let mut s = TsVideoStream::default();
s.base.stream_type = TsStreamType::HevcVideo;
let mut b = TsStreamBuffer::new();
b.add(&data, 0, data.len());
b.begin_read();
let mut tag = None;
scan(&mut s, &mut b, &mut tag);
}
}
}