webrtc_media/io/ivf_reader/
mod.rs

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