#[derive(Default)]
pub struct AnnexBStreamState {
pub previous_frame_was_idr: bool,
}
pub const ANNEXB_NAL_START_CODE: &[u8] = &[0x00, 0x00, 0x00, 0x01];
#[derive(thiserror::Error, Debug)]
pub enum AnnexBStreamWriteError {
#[error("Bad video data: {0}")]
BadVideoData(String),
#[error("Failed to write to stream: {0}")]
FailedToWriteToStream(#[from] std::io::Error),
}
pub fn write_length_prefixed_nalus_to_annexb_stream(
nalu_stream: &mut dyn std::io::Write,
data: &[u8],
length_prefix_size: usize,
) -> Result<(), AnnexBStreamWriteError> {
let mut buffer_offset: usize = 0;
let sample_end = data.len();
while buffer_offset < sample_end {
re_tracing::profile_scope!("write_nalu");
if sample_end < buffer_offset + length_prefix_size {
return Err(AnnexBStreamWriteError::BadVideoData(
"Not enough bytes to fit the length prefix".to_owned(),
));
}
let nal_unit_size = match length_prefix_size {
1 => data[buffer_offset] as usize,
2 => u16::from_be_bytes(
#[expect(clippy::unwrap_used)] data[buffer_offset..(buffer_offset + 2)].try_into().unwrap(),
) as usize,
4 => u32::from_be_bytes(
#[expect(clippy::unwrap_used)] data[buffer_offset..(buffer_offset + 4)].try_into().unwrap(),
) as usize,
_ => {
return Err(AnnexBStreamWriteError::BadVideoData(format!(
"Bad length prefix size: {length_prefix_size}"
)));
}
};
let data_start = buffer_offset + length_prefix_size; let data_end = buffer_offset + nal_unit_size + length_prefix_size;
if data.len() < data_end {
return Err(AnnexBStreamWriteError::BadVideoData(
"Video sample data ends with incomplete NAL unit.".to_owned(),
));
}
let data = &data[data_start..data_end];
nalu_stream.write_all(ANNEXB_NAL_START_CODE)?;
re_tracing::profile_scope!("write_bytes", data.len().to_string());
nalu_stream.write_all(data)?;
buffer_offset = data_end;
}
Ok(())
}