use std::io::SeekFrom;
use crate::binary::{ReadBytes, WriteBytes};
use crate::error::{RLibError, Result};
use crate::utils::check_size_mismatch;
use super::*;
const HEADER_LENGTH_CAVP8_V0: u16 = 40;
const HEADER_LENGTH_CAVP8_V1: u16 = 40;
impl Video {
pub(crate) fn read_cavp8<R: ReadBytes>(data: &mut R) -> Result<Self> {
let format = SupportedFormats::CaVp8;
let version = data.read_u16()?;
let mut header_len = data.read_u16()?;
let codec_four_cc = data.read_string_u8(4)?;
let width = data.read_u16()?;
let height = data.read_u16()?;
let ms_per_frame = data.read_f32()?;
let _mystery_u32 = data.read_u32()?; let num_frames_minus_1 = data.read_u32()?; let offset_frame_table = data.read_u32()?;
let num_frames = data.read_u32()?;
let _largest_frame_size = data.read_u32()?;
let extra_data = if num_frames_minus_1 == num_frames {
let mystery_u8 = data.read_u8()?; let mystery_u32_1 = data.read_u32()?; let mystery_u32_2 = data.read_u32()?; Some((mystery_u8, mystery_u32_1, mystery_u32_2))
} else {
None
};
header_len += 8;
check_size_mismatch(data.stream_position()? as usize, header_len as usize)?;
let frame_data_len = offset_frame_table as u64 - data.stream_position()?;
let frame_data = data.read_slice(frame_data_len as usize, false)?;
let data_len = data.len()?;
let frame_table_len = data_len - data.stream_position()?;
let bells = frame_table_len / 13 == num_frames as u64 && frame_table_len % 13 == 0;
let mut frame_offset = 0;
let mut frame_table = Vec::with_capacity(num_frames as usize);
let mut frame_table_decoded = vec![];
for _ in 0..num_frames {
let frame_offset_real = data.read_u32()?;
let frame_size = data.read_u32()?;
if bells {
let _unknown_data = data.read_u32()?;
}
let frame_is_key_frame = data.read_bool()?;
let frame = Frame {
offset: frame_offset,
size: frame_size,
is_key_frame: frame_is_key_frame,
};
frame_offset += frame.size;
frame_table.push(frame);
let frame_offset_real_end = frame_offset_real + frame.size;
if frame_offset_real_end as u64 > data.len()? {
return Err(RLibError::DecodingCAVP8IncorrectOrUnknownFrameSize);
}
let x = data.stream_position()?;
data.seek(SeekFrom::Start(frame_offset_real as u64))?;
frame_table_decoded.extend_from_slice(&data.read_slice(frame.size as usize, false)?);
data.seek(SeekFrom::Start(x))?;
}
check_size_mismatch(data.stream_position()? as usize, data_len as usize)?;
Ok(Self {
format,
version,
codec_four_cc,
width,
height,
num_frames,
extra_data,
framerate: 1_000f32 / ms_per_frame,
frame_table,
frame_data,
})
}
pub(crate) fn save_cavp8<W: WriteBytes>(&self, buffer: &mut W) -> Result<()> {
let header_lenght = if self.version == 0 {
if self.extra_data.is_some() {
HEADER_LENGTH_CAVP8_V0 + 9
} else {
HEADER_LENGTH_CAVP8_V0
}
} else if self.extra_data.is_some() {
HEADER_LENGTH_CAVP8_V1 + 9
} else {
HEADER_LENGTH_CAVP8_V1
};
let header_lenght_broken = header_lenght - 8;
buffer.write_string_u8(SIGNATURE_CAVP8)?;
buffer.write_u16(self.version)?;
buffer.write_u16(header_lenght_broken)?;
buffer.write_string_u8(&self.codec_four_cc)?;
buffer.write_u16(self.width)?;
buffer.write_u16(self.height)?;
buffer.write_f32(1_000f32 / self.framerate)?;
buffer.write_u32(1)?;
if self.extra_data.is_some() || self.num_frames == 0 {
buffer.write_u32(self.num_frames)?;
} else {
buffer.write_u32(self.num_frames - 1)?;
}
buffer.write_u32(header_lenght as u32 + self.frame_data.len() as u32)?;
buffer.write_u32(self.num_frames)?;
buffer.write_u32(self.frame_table.iter().map(|x| x.size).max().unwrap())?;
if let Some(extra_data) = self.extra_data {
buffer.write_u8(extra_data.0)?;
buffer.write_u32(extra_data.1)?;
buffer.write_u32(extra_data.2)?;
}
buffer.write_all(&self.frame_data)?;
let mut offset = if self.extra_data.is_some() {
header_lenght_broken as u32
} else {
header_lenght as u32
};
for frame in &self.frame_table {
buffer.write_u32(offset)?;
buffer.write_u32(frame.size)?;
buffer.write_bool(frame.is_key_frame)?;
offset += frame.size;
}
Ok(())
}
}