xref: /webrtc/media/src/io/ivf_reader/mod.rs (revision ffe74184)
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