avirus 0.2.5

A Rust library for manipulating AVI files for purposes such as glitch art.
Documentation
use byteorder::{BigEndian, ByteOrder, LittleEndian};

const AVIIF_KEYFRAME: u32 = 0x0000_0010;

/// The `Frame` type. This is the lowest level type
/// in the AVI file, ignoring the codec level.
#[derive(Clone, Copy, Debug)]
pub struct Frame {
    /// Specifies a four-character code corresponding to the chunk ID of a data chunk in the file. See [stream data ('movi' list)](https://docs.microsoft.com/en-us/windows/desktop/directshow/avi-riff-file-reference#stream-data-movi-list) for more information.
    pub id: u32,
    /// The following flags are defined:
    /// * `AVIIF_KEYFRAME`: The chunk the entry refers to is a keyframe
    /// * `AVIIF_LIST`: The entry points to a list, not a chunk
    /// * `AVIIF_FIRSTPART`: Indicates this chunk needs the frames following it to be used; it cannot stand alone
    /// * `AVIIF_LASTPART`: Indicates this chunk needs the frames preceding it to be used; it cannot stand alone
    /// * `AVIIF_NOTIME`: The duration which is applied to the corresponding chunk is 0
    pub flag: u32,
    /// Contains the position of the header of the corresponding chunk
    pub offset: u32,
    /// Contains the size of the corresponding chunk in bytes
    pub length: u32,
}

impl Frame {
    /// This function reads a `&[u8]`, reads four chunks of 4 and returns a `Frame`.
    /// It is expected that `id`, `flag`, `offset`, and `length` are in the correct order
    /// in the input file above.
    #[must_use]
    pub fn new(bytes: &[u8]) -> Self {
        let mut iter = bytes.chunks(4);
        Self {
            id: BigEndian::read_u32(iter.next().unwrap_or_default()),
            flag: LittleEndian::read_u32(iter.next().unwrap_or_default()),
            offset: LittleEndian::read_u32(iter.next().unwrap_or_default()),
            length: LittleEndian::read_u32(iter.next().unwrap_or_default()),
        }
    }

    /// This function outputs the `Frame` as a `[u8; 16]`.
    #[must_use]
    pub fn as_bytes(&self) -> [u8; 16] {
        let mut buf = [0u8; 16];
        BigEndian::write_u32_into(&[self.id], &mut buf[..4]);
        LittleEndian::write_u32_into(&[self.flag, self.offset, self.length], &mut buf[4..]);
        buf
    }

    /// This function returns a boolean which indicates that this frame is a video frame.
    #[must_use]
    pub fn is_videoframe(&self) -> bool {
        let id = self.id_as_u8_array();
        &id[2..4] == b"db" || &id[2..4] == b"dc"
    }

    /// This function returns a boolean which indicates that this frame is an audio frame.
    #[must_use]
    pub fn is_audioframe(&self) -> bool {
        let id = self.id_as_u8_array();
        &id[2..4] == b"wb"
    }

    /// This function returns a boolean which indicates that this frame is a key frame
    /// (hereby known as an iframe).
    #[must_use]
    pub fn is_iframe(&self) -> bool {
        if self.is_videoframe() {
            return self.flag & AVIIF_KEYFRAME != 0;
        }
        false
    }

    /// This function returns a boolean which indicates that this frame is a delta frame
    /// (hereby known as a pframe).
    #[must_use]
    pub fn is_pframe(&self) -> bool {
        if self.is_videoframe() {
            return self.flag & AVIIF_KEYFRAME == 0;
        }
        false
    }

    /// This is a private function to cast a `u32` to a `[u8; 4]`.
    fn id_as_u8_array(&self) -> [u8; 4] {
        let mut buf = [0u8; 4];
        BigEndian::write_u32(&mut buf, self.id);
        buf
    }
}