rtc-media 0.9.0

RTC Media in Rust
Documentation
use std::io::BufReader;

use bytes::Bytes;

use super::*;

/// build_ivf_container takes frames and prepends valid IVF file header
fn build_ivf_container(frames: &[Bytes]) -> Bytes {
    // Valid IVF file header taken from: https://github.com/webmproject/...
    // vp8-test-vectors/blob/master/vp80-00-comprehensive-001.ivf
    // Video Image Width      	- 176
    // Video Image Height    	- 144
    // Frame Rate Rate        	- 30000
    // Frame Rate Scale       	- 1000
    // Video Length in Frames	- 29
    // BitRate: 		 64.01 kb/s
    let header = Bytes::from_static(&[
        0x44, 0x4b, 0x49, 0x46, 0x00, 0x00, 0x20, 0x00, 0x56, 0x50, 0x38, 0x30, 0xb0, 0x00, 0x90,
        0x00, 0x30, 0x75, 0x00, 0x00, 0xe8, 0x03, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00,
    ]);

    let mut ivf = BytesMut::new();
    ivf.extend(header);

    for frame in frames {
        ivf.extend(frame);
    }

    ivf.freeze()
}

#[test]
fn test_ivf_reader_parse_valid_file_header() -> Result<()> {
    let ivf = build_ivf_container(&[]);

    let r = BufReader::new(&ivf[..]);
    let (_, header) = IVFReader::new(r)?;

    assert_eq!(&header.signature, b"DKIF", "signature is 'DKIF'");
    assert_eq!(header.version, 0, "version should be 0");
    assert_eq!(&header.four_cc, b"VP80", "FourCC should be 'VP80'");
    assert_eq!(header.width, 176, "width should be 176");
    assert_eq!(header.height, 144, "height should be 144");
    assert_eq!(
        header.timebase_denominator, 30000,
        "timebase denominator should be 30000"
    );
    assert_eq!(
        header.timebase_numerator, 1000,
        "timebase numerator should be 1000"
    );
    assert_eq!(header.num_frames, 29, "number of frames should be 29");
    assert_eq!(header.unused, 0, "bytes should be unused");

    Ok(())
}

#[test]
fn test_ivf_reader_parse_valid_frames() -> Result<()> {
    // Frame Length - 4
    // Timestamp - None
    // Frame Payload - 0xDEADBEEF
    let valid_frame1 = Bytes::from_static(&[
        0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDE, 0xAD, 0xBE,
        0xEF,
    ]);

    // Frame Length - 12
    // Timestamp - None
    // Frame Payload - 0xDEADBEEFDEADBEEF
    let valid_frame2 = Bytes::from_static(&[
        0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDE, 0xAD, 0xBE,
        0xEF, 0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE, 0xEF,
    ]);

    let ivf = build_ivf_container(&[valid_frame1, valid_frame2]);
    let r = BufReader::new(&ivf[..]);
    let (mut reader, _) = IVFReader::new(r)?;

    // Parse Frame #1
    let (payload, header) = reader.parse_next_frame()?;

    assert_eq!(header.frame_size, 4, "Frame header frameSize should be 4");
    assert_eq!(payload.len(), 4, "Payload should be length 4");
    assert_eq!(
        payload,
        Bytes::from_static(&[0xDE, 0xAD, 0xBE, 0xEF,]),
        "Payload value should be 0xDEADBEEF"
    );
    assert_eq!(
        reader.bytes_read,
        IVF_FILE_HEADER_SIZE + IVF_FRAME_HEADER_SIZE + header.frame_size as usize
    );
    let previous_bytes_read = reader.bytes_read;

    // Parse Frame #2
    let (payload, header) = reader.parse_next_frame()?;

    assert_eq!(header.frame_size, 12, "Frame header frameSize should be 4");
    assert_eq!(payload.len(), 12, "Payload should be length 12");
    assert_eq!(
        payload,
        Bytes::from_static(&[
            0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE, 0xEF,
        ]),
        "Payload value should be 0xDEADBEEFDEADBEEF"
    );
    assert_eq!(
        reader.bytes_read,
        previous_bytes_read + IVF_FRAME_HEADER_SIZE + header.frame_size as usize,
    );

    Ok(())
}

#[test]
fn test_ivf_reader_parse_incomplete_frame_header() -> Result<()> {
    // frame with 11-byte header (missing 1 byte)
    let incomplete_frame = Bytes::from_static(&[
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    ]);

    let ivf = build_ivf_container(&[incomplete_frame]);
    let r = BufReader::new(&ivf[..]);
    let (mut reader, _) = IVFReader::new(r)?;

    // Parse Frame #1
    let result = reader.parse_next_frame();
    assert!(result.is_err(), "Expected Error but got Ok");

    Ok(())
}

#[test]
fn test_ivf_reader_parse_incomplete_frame_payload() -> Result<()> {
    // frame with header defining frameSize of 4
    // but only 2 bytes available (missing 2 bytes)
    let incomplete_frame = Bytes::from_static(&[
        0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDE, 0xAD,
    ]);

    let ivf = build_ivf_container(&[incomplete_frame]);
    let r = BufReader::new(&ivf[..]);
    let (mut reader, _) = IVFReader::new(r)?;

    // Parse Frame #1
    let result = reader.parse_next_frame();
    assert!(result.is_err(), "Expected Error but got Ok");

    Ok(())
}

#[test]
fn test_ivf_reader_eof_when_no_frames_left() -> Result<()> {
    let ivf = build_ivf_container(&[]);
    let r = BufReader::new(&ivf[..]);
    let (mut reader, _) = IVFReader::new(r)?;

    let result = reader.parse_next_frame();
    assert!(result.is_err(), "Expected Error but got Ok");

    Ok(())
}