use std::num::NonZero;
use byteorder::{BigEndian, ReadBytesExt};
use crate::error::{Error, Result};
use crate::icc::read_varint_from_reader;
use crate::util::NewWithCapacity;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FrameIndexEntry {
pub codestream_offset: u64,
pub duration_ticks: u64,
pub frame_count: u64,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FrameIndexBox {
pub tnum: u32,
pub tden: NonZero<u32>,
pub entries: Vec<FrameIndexEntry>,
}
impl FrameIndexBox {
pub fn num_frames(&self) -> usize {
self.entries.len()
}
pub fn tick_duration_secs(&self) -> f64 {
self.tnum as f64 / self.tden.get() as f64
}
pub fn entry_for_offset(&self, offset: u64) -> Option<&FrameIndexEntry> {
match self
.entries
.binary_search_by_key(&offset, |e| e.codestream_offset)
{
Ok(i) => Some(&self.entries[i]),
Err(0) => None,
Err(i) => Some(&self.entries[i - 1]),
}
}
pub fn parse(data: &[u8]) -> Result<Self> {
let mut reader = data;
let nf = read_varint_from_reader(&mut reader)?;
if nf > u32::MAX as u64 {
return Err(Error::InvalidBox);
}
let nf = nf as usize;
let tnum = reader
.read_u32::<BigEndian>()
.map_err(|_| Error::InvalidBox)?;
let tden = NonZero::new(
reader
.read_u32::<BigEndian>()
.map_err(|_| Error::InvalidBox)?,
)
.ok_or(Error::InvalidBox)?;
let mut entries = Vec::new_with_capacity(nf.min(reader.len() / 3))?;
let mut absolute_offset: u64 = 0;
for _ in 0..nf {
let off_delta = read_varint_from_reader(&mut reader)?;
let duration_ticks = read_varint_from_reader(&mut reader)?;
let frame_count = read_varint_from_reader(&mut reader)?;
absolute_offset = absolute_offset
.checked_add(off_delta)
.ok_or(Error::InvalidBox)?;
entries.push(FrameIndexEntry {
codestream_offset: absolute_offset,
duration_ticks,
frame_count,
});
}
Ok(FrameIndexBox {
tnum,
tden,
entries,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::util::test::{build_frame_index_content, encode_varint};
fn build_frame_index(tnum: u32, tden: u32, entries: &[(u64, u64, u64)]) -> Vec<u8> {
build_frame_index_content(tnum, tden, entries)
}
#[test]
fn test_parse_empty_index() {
let data = build_frame_index(1, 1000, &[]);
let index = FrameIndexBox::parse(&data).unwrap();
assert_eq!(index.num_frames(), 0);
assert_eq!(index.tnum, 1);
assert_eq!(index.tden.get(), 1000);
}
#[test]
fn test_parse_single_entry() {
let data = build_frame_index(1, 1000, &[(0, 100, 1)]);
let index = FrameIndexBox::parse(&data).unwrap();
assert_eq!(index.num_frames(), 1);
assert_eq!(
index.entries[0],
FrameIndexEntry {
codestream_offset: 0,
duration_ticks: 100,
frame_count: 1,
}
);
}
#[test]
fn test_parse_multiple_entries_delta_coding() {
let data = build_frame_index(1, 1000, &[(100, 50, 2), (200, 50, 2), (150, 30, 1)]);
let index = FrameIndexBox::parse(&data).unwrap();
assert_eq!(index.num_frames(), 3);
assert_eq!(index.entries[0].codestream_offset, 100);
assert_eq!(index.entries[1].codestream_offset, 300);
assert_eq!(index.entries[2].codestream_offset, 450);
assert_eq!(index.entries[0].duration_ticks, 50);
assert_eq!(index.entries[1].duration_ticks, 50);
assert_eq!(index.entries[2].duration_ticks, 30);
}
#[test]
fn test_parse_large_varint() {
let mut data = Vec::new();
data.extend(encode_varint(1)); data.extend(1u32.to_be_bytes()); data.extend(1000u32.to_be_bytes()); data.extend(encode_varint(0x1234_5678_9ABC)); data.extend(encode_varint(42));
data.extend(encode_varint(1));
let index = FrameIndexBox::parse(&data).unwrap();
assert_eq!(index.entries[0].codestream_offset, 0x1234_5678_9ABC);
}
#[test]
fn test_entry_for_offset() {
let data = build_frame_index(1, 1000, &[(100, 50, 2), (200, 50, 2), (150, 30, 1)]);
let index = FrameIndexBox::parse(&data).unwrap();
assert!(index.entry_for_offset(50).is_none());
assert_eq!(index.entry_for_offset(100).unwrap().codestream_offset, 100);
assert_eq!(index.entry_for_offset(200).unwrap().codestream_offset, 100);
assert_eq!(index.entry_for_offset(350).unwrap().codestream_offset, 300);
assert_eq!(index.entry_for_offset(450).unwrap().codestream_offset, 450);
assert_eq!(index.entry_for_offset(999).unwrap().codestream_offset, 450);
}
#[test]
fn test_zero_tden_rejected() {
let data = build_frame_index(1, 0, &[]);
assert!(FrameIndexBox::parse(&data).is_err());
}
#[test]
fn test_truncated_data() {
let data = encode_varint(1);
assert!(FrameIndexBox::parse(&data).is_err());
}
#[test]
fn test_huge_nf_no_oom() {
let mut data = Vec::new();
data.extend(encode_varint(u32::MAX as u64)); data.extend(1u32.to_be_bytes()); data.extend(1000u32.to_be_bytes()); assert!(FrameIndexBox::parse(&data).is_err());
}
#[test]
fn test_tick_duration() {
let data = build_frame_index(1, 1000, &[]);
let index = FrameIndexBox::parse(&data).unwrap();
assert!((index.tick_duration_secs() - 0.001).abs() < 1e-9);
}
}