1 #[cfg(test)] 2 mod ivf_reader_test; 3 4 use crate::error::{Error, Result}; 5 use crate::io::ResetFn; 6 7 use byteorder::{LittleEndian, ReadBytesExt}; 8 use bytes::BytesMut; 9 use std::io::Read; 10 11 pub const IVF_FILE_HEADER_SIGNATURE: &[u8] = b"DKIF"; 12 pub const IVF_FILE_HEADER_SIZE: usize = 32; 13 pub const IVF_FRAME_HEADER_SIZE: usize = 12; 14 15 /// IVFFileHeader 32-byte header for IVF files 16 /// https://wiki.multimedia.cx/index.php/IVF 17 #[derive(Default, Debug, Copy, Clone, PartialEq, Eq)] 18 pub struct IVFFileHeader { 19 pub signature: [u8; 4], // 0-3 20 pub version: u16, // 4-5 21 pub header_size: u16, // 6-7 22 pub four_cc: [u8; 4], // 8-11 23 pub width: u16, // 12-13 24 pub height: u16, // 14-15 25 pub timebase_denominator: u32, // 16-19 26 pub timebase_numerator: u32, // 20-23 27 pub num_frames: u32, // 24-27 28 pub unused: u32, // 28-31 29 } 30 31 /// IVFFrameHeader 12-byte header for IVF frames 32 /// https://wiki.multimedia.cx/index.php/IVF 33 #[derive(Default, Debug, Copy, Clone, PartialEq, Eq)] 34 pub struct IVFFrameHeader { 35 pub frame_size: u32, // 0-3 36 pub timestamp: u64, // 4-11 37 } 38 39 /// IVFReader is used to read IVF files and return frame payloads 40 pub struct IVFReader<R: Read> { 41 reader: R, 42 bytes_read: usize, 43 } 44 45 impl<R: Read> IVFReader<R> { 46 /// new returns a new IVF reader and IVF file header 47 /// with an io.Reader input new(reader: R) -> Result<(IVFReader<R>, IVFFileHeader)>48 pub fn new(reader: R) -> Result<(IVFReader<R>, IVFFileHeader)> { 49 let mut r = IVFReader { 50 reader, 51 bytes_read: 0, 52 }; 53 54 let header = r.parse_file_header()?; 55 56 Ok((r, header)) 57 } 58 59 /// reset_reader resets the internal stream of IVFReader. This is useful 60 /// for live streams, where the end of the file might be read without the 61 /// data being finished. reset_reader(&mut self, mut reset: ResetFn<R>)62 pub fn reset_reader(&mut self, mut reset: ResetFn<R>) { 63 self.reader = reset(self.bytes_read); 64 } 65 66 /// parse_next_frame reads from stream and returns IVF frame payload, header, 67 /// and an error if there is incomplete frame data. 68 /// Returns all nil values when no more frames are available. parse_next_frame(&mut self) -> Result<(BytesMut, IVFFrameHeader)>69 pub fn parse_next_frame(&mut self) -> Result<(BytesMut, IVFFrameHeader)> { 70 let frame_size = self.reader.read_u32::<LittleEndian>()?; 71 let timestamp = self.reader.read_u64::<LittleEndian>()?; 72 let header = IVFFrameHeader { 73 frame_size, 74 timestamp, 75 }; 76 77 let mut payload = BytesMut::with_capacity(header.frame_size as usize); 78 payload.resize(header.frame_size as usize, 0); 79 self.reader.read_exact(&mut payload)?; 80 81 self.bytes_read += IVF_FRAME_HEADER_SIZE + header.frame_size as usize; 82 83 Ok((payload, header)) 84 } 85 86 /// parse_file_header reads 32 bytes from stream and returns 87 /// IVF file header. This is always called before parse_next_frame() parse_file_header(&mut self) -> Result<IVFFileHeader>88 fn parse_file_header(&mut self) -> Result<IVFFileHeader> { 89 let mut signature = [0u8; 4]; 90 let mut four_cc = [0u8; 4]; 91 92 self.reader.read_exact(&mut signature)?; 93 let version = self.reader.read_u16::<LittleEndian>()?; 94 let header_size = self.reader.read_u16::<LittleEndian>()?; 95 self.reader.read_exact(&mut four_cc)?; 96 let width = self.reader.read_u16::<LittleEndian>()?; 97 let height = self.reader.read_u16::<LittleEndian>()?; 98 let timebase_denominator = self.reader.read_u32::<LittleEndian>()?; 99 let timebase_numerator = self.reader.read_u32::<LittleEndian>()?; 100 let num_frames = self.reader.read_u32::<LittleEndian>()?; 101 let unused = self.reader.read_u32::<LittleEndian>()?; 102 103 let header = IVFFileHeader { 104 signature, 105 version, 106 header_size, 107 four_cc, 108 width, 109 height, 110 timebase_denominator, 111 timebase_numerator, 112 num_frames, 113 unused, 114 }; 115 116 if header.signature != IVF_FILE_HEADER_SIGNATURE { 117 return Err(Error::ErrSignatureMismatch); 118 } else if header.version != 0 { 119 return Err(Error::ErrUnknownIVFVersion); 120 } 121 122 self.bytes_read += IVF_FILE_HEADER_SIZE; 123 124 Ok(header) 125 } 126 } 127