#[cfg(test)]
mod ivf_writer_test;
use std::io::{Seek, SeekFrom, Write};
use byteorder::{LittleEndian, WriteBytesExt};
use bytes::{BufMut, Bytes, BytesMut};
use rtp::codec::av1::Av1Depacketizer;
use rtp::packetizer::Depacketizer;
use crate::io::Writer;
use crate::io::ivf_reader::IVFFileHeader;
use shared::error::Result;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum IvfCodec {
#[default]
Vp8,
Vp9,
Av1,
}
impl IvfCodec {
pub fn from_fourcc(fourcc: &[u8; 4]) -> Self {
match fourcc {
b"VP80" => IvfCodec::Vp8,
b"VP90" => IvfCodec::Vp9,
b"AV01" => IvfCodec::Av1,
_ => IvfCodec::Vp8, }
}
pub fn fourcc(&self) -> [u8; 4] {
match self {
IvfCodec::Vp8 => *b"VP80",
IvfCodec::Vp9 => *b"VP90",
IvfCodec::Av1 => *b"AV01",
}
}
}
pub struct IVFWriter<W: Write + Seek> {
writer: W,
count: u64,
seen_key_frame: bool,
current_frame: Option<BytesMut>,
codec: IvfCodec,
av1_depacketizer: Option<Av1Depacketizer>,
}
impl<W: Write + Seek> IVFWriter<W> {
pub fn new(writer: W, header: &IVFFileHeader) -> Result<Self> {
let codec = IvfCodec::from_fourcc(&header.four_cc);
let mut w = IVFWriter {
writer,
count: 0,
seen_key_frame: false,
current_frame: None,
codec,
av1_depacketizer: None,
};
w.write_header(header)?;
Ok(w)
}
fn write_header(&mut self, header: &IVFFileHeader) -> Result<()> {
self.writer.write_all(&header.signature)?; self.writer.write_u16::<LittleEndian>(header.version)?; self.writer.write_u16::<LittleEndian>(header.header_size)?; self.writer.write_all(&header.four_cc)?; self.writer.write_u16::<LittleEndian>(header.width)?; self.writer.write_u16::<LittleEndian>(header.height)?; self.writer
.write_u32::<LittleEndian>(header.timebase_denominator)?; self.writer
.write_u32::<LittleEndian>(header.timebase_numerator)?; self.writer.write_u32::<LittleEndian>(header.num_frames)?; self.writer.write_u32::<LittleEndian>(header.unused)?;
Ok(())
}
fn write_vp8(&mut self, packet: &rtp::Packet) -> Result<()> {
let mut depacketizer = rtp::codec::vp8::Vp8Packet::default();
let payload = depacketizer.depacketize(&packet.payload)?;
let is_key_frame = (payload[0] & 0x01) == 0;
if !self.seen_key_frame && !is_key_frame {
return Ok(());
}
if self.current_frame.is_none() && !depacketizer.is_partition_head(&packet.payload) {
return Ok(());
}
self.seen_key_frame = true;
self.append_to_frame(payload);
if !packet.header.marker {
return Ok(());
}
self.write_current_frame()
}
fn write_vp9(&mut self, packet: &rtp::Packet) -> Result<()> {
let mut depacketizer = rtp::codec::vp9::Vp9Packet::default();
let payload = depacketizer.depacketize(&packet.payload)?;
let is_key_frame = !depacketizer.p;
if !self.seen_key_frame && !is_key_frame {
return Ok(());
}
if self.current_frame.is_none() && !depacketizer.b {
return Ok(());
}
self.seen_key_frame = true;
self.append_to_frame(payload);
if !packet.header.marker {
return Ok(());
}
self.write_current_frame()
}
fn write_av1(&mut self, packet: &rtp::Packet) -> Result<()> {
if self.av1_depacketizer.is_none() {
self.av1_depacketizer = Some(Av1Depacketizer::new());
}
let depacketizer = self.av1_depacketizer.as_mut().unwrap();
let payload = depacketizer.depacketize(&packet.payload)?;
if !self.seen_key_frame {
let is_key_frame =
depacketizer.n || (!payload.is_empty() && ((payload[0] & 0x78) >> 3) == 1);
if !is_key_frame {
return Ok(());
}
self.seen_key_frame = true;
}
self.append_to_frame(payload);
if !packet.header.marker {
return Ok(());
}
let mut frame_with_delimiter = BytesMut::with_capacity(2 + self.current_frame_len());
frame_with_delimiter.put_u8(0x12); frame_with_delimiter.put_u8(0x00);
if let Some(current_frame) = self.current_frame.take() {
frame_with_delimiter.extend_from_slice(¤t_frame);
}
self.write_frame(&frame_with_delimiter)
}
fn append_to_frame(&mut self, payload: Bytes) {
if let Some(current_frame) = &mut self.current_frame {
current_frame.extend(payload);
} else {
let mut current_frame = BytesMut::new();
current_frame.extend(payload);
self.current_frame = Some(current_frame);
}
}
fn current_frame_len(&self) -> usize {
self.current_frame.as_ref().map_or(0, |f| f.len())
}
fn write_current_frame(&mut self) -> Result<()> {
if let Some(current_frame) = &self.current_frame {
if current_frame.is_empty() {
return Ok(());
}
} else {
return Ok(());
}
let frame_content = self.current_frame.take().unwrap();
self.write_frame(&frame_content)
}
fn write_frame(&mut self, frame: &[u8]) -> Result<()> {
self.writer.write_u32::<LittleEndian>(frame.len() as u32)?; self.writer.write_u64::<LittleEndian>(self.count)?; self.count += 1;
self.writer.write_all(frame)?;
Ok(())
}
}
impl<W: Write + Seek> Writer for IVFWriter<W> {
fn write_rtp(&mut self, packet: &rtp::Packet) -> Result<()> {
if packet.payload.is_empty() {
return Ok(());
}
match self.codec {
IvfCodec::Vp8 => self.write_vp8(packet),
IvfCodec::Vp9 => self.write_vp9(packet),
IvfCodec::Av1 => self.write_av1(packet),
}
}
fn close(&mut self) -> Result<()> {
self.writer.seek(SeekFrom::Start(24))?;
self.writer.write_u32::<LittleEndian>(self.count as u32)?;
self.writer.flush()?;
Ok(())
}
}