1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
#[cfg(test)]
mod ivf_reader_test;

use crate::error::{Error, Result};
use crate::io::ResetFn;

use byteorder::{LittleEndian, ReadBytesExt};
use bytes::BytesMut;
use std::io::Read;

pub const IVF_FILE_HEADER_SIGNATURE: &[u8] = b"DKIF";
pub const IVF_FILE_HEADER_SIZE: usize = 32;
pub const IVF_FRAME_HEADER_SIZE: usize = 12;

/// IVFFileHeader 32-byte header for IVF files
/// https://wiki.multimedia.cx/index.php/IVF
#[derive(Default, Debug, Copy, Clone, PartialEq)]
pub struct IVFFileHeader {
    pub signature: [u8; 4],        // 0-3
    pub version: u16,              // 4-5
    pub header_size: u16,          // 6-7
    pub four_cc: [u8; 4],          // 8-11
    pub width: u16,                // 12-13
    pub height: u16,               // 14-15
    pub timebase_denominator: u32, // 16-19
    pub timebase_numerator: u32,   // 20-23
    pub num_frames: u32,           // 24-27
    pub unused: u32,               // 28-31
}

/// IVFFrameHeader 12-byte header for IVF frames
/// https://wiki.multimedia.cx/index.php/IVF
#[derive(Default, Debug, Copy, Clone, PartialEq)]
pub struct IVFFrameHeader {
    pub frame_size: u32, // 0-3
    pub timestamp: u64,  // 4-11
}

/// IVFReader is used to read IVF files and return frame payloads
pub struct IVFReader<R: Read> {
    reader: R,
    bytes_read: usize,
}

impl<R: Read> IVFReader<R> {
    /// new returns a new IVF reader and IVF file header
    /// with an io.Reader input
    pub fn new(reader: R) -> Result<(IVFReader<R>, IVFFileHeader)> {
        let mut r = IVFReader {
            reader,
            bytes_read: 0,
        };

        let header = r.parse_file_header()?;

        Ok((r, header))
    }

    /// reset_reader resets the internal stream of IVFReader. This is useful
    /// for live streams, where the end of the file might be read without the
    /// data being finished.
    pub fn reset_reader(&mut self, mut reset: ResetFn<R>) {
        self.reader = reset(self.bytes_read);
    }

    /// parse_next_frame reads from stream and returns IVF frame payload, header,
    /// and an error if there is incomplete frame data.
    /// Returns all nil values when no more frames are available.
    pub fn parse_next_frame(&mut self) -> Result<(BytesMut, IVFFrameHeader)> {
        let frame_size = self.reader.read_u32::<LittleEndian>()?;
        let timestamp = self.reader.read_u64::<LittleEndian>()?;
        let header = IVFFrameHeader {
            frame_size,
            timestamp,
        };

        let mut payload = BytesMut::with_capacity(header.frame_size as usize);
        payload.resize(header.frame_size as usize, 0);
        self.reader.read_exact(&mut payload)?;

        self.bytes_read += IVF_FRAME_HEADER_SIZE + header.frame_size as usize;

        Ok((payload, header))
    }

    /// parse_file_header reads 32 bytes from stream and returns
    /// IVF file header. This is always called before parse_next_frame()
    fn parse_file_header(&mut self) -> Result<IVFFileHeader> {
        let mut signature = [0u8; 4];
        let mut four_cc = [0u8; 4];

        self.reader.read_exact(&mut signature)?;
        let version = self.reader.read_u16::<LittleEndian>()?;
        let header_size = self.reader.read_u16::<LittleEndian>()?;
        self.reader.read_exact(&mut four_cc)?;
        let width = self.reader.read_u16::<LittleEndian>()?;
        let height = self.reader.read_u16::<LittleEndian>()?;
        let timebase_denominator = self.reader.read_u32::<LittleEndian>()?;
        let timebase_numerator = self.reader.read_u32::<LittleEndian>()?;
        let num_frames = self.reader.read_u32::<LittleEndian>()?;
        let unused = self.reader.read_u32::<LittleEndian>()?;

        let header = IVFFileHeader {
            signature,
            version,
            header_size,
            four_cc,
            width,
            height,
            timebase_denominator,
            timebase_numerator,
            num_frames,
            unused,
        };

        if header.signature != IVF_FILE_HEADER_SIGNATURE {
            return Err(Error::ErrSignatureMismatch);
        } else if header.version != 0 {
            return Err(Error::ErrUnknownIVFVersion);
        }

        self.bytes_read += IVF_FILE_HEADER_SIZE;

        Ok(header)
    }
}