#![cfg(test)]
#![allow(
clippy::unwrap_used,
clippy::expect_used,
clippy::panic,
reason = "unit tests"
)]
use super::{parse_sample_entry, write_stsd};
use crate::isobmff::parse_header;
use crate::types::{Bytes, Codec, Rational, Track};
fn vp9_track(extra: Bytes) -> Track {
Track {
id: 0,
codec: Codec::Vp9,
time_base: Rational::new(1, 1000),
width: 640,
height: 480,
extra_data: extra,
}
}
fn h264_track() -> Track {
Track {
id: 0,
codec: Codec::H264,
time_base: Rational::new(1, 1000),
width: 320,
height: 240,
extra_data: Bytes::new(),
}
}
fn hevc_track(extra: Bytes) -> Track {
Track {
id: 0,
codec: Codec::Hevc,
time_base: Rational::new(1, 1000),
width: 1920,
height: 1080,
extra_data: extra,
}
}
fn av1_track(extra: Bytes) -> Track {
Track {
id: 0,
codec: Codec::Av1,
time_base: Rational::new(1, 1000),
width: 3840,
height: 2160,
extra_data: extra,
}
}
fn parse_stsd_entry(stsd: &[u8]) -> (u32, u32, Codec, Bytes) {
let hdr = parse_header(stsd).expect("stsd header");
let body = &stsd[hdr.header_len..hdr.size];
let mut width = 0;
let mut height = 0;
let mut codec = Codec::H264;
let mut extra = Bytes::new();
let mut encryption = None;
parse_sample_entry(
&body[8..],
&mut width,
&mut height,
&mut codec,
&mut extra,
&mut encryption,
);
assert!(encryption.is_none());
(width, height, codec, extra)
}
#[test]
fn vp9_sample_entry_writes_vp09_not_avc1() {
let mut buf = Vec::new();
write_stsd(&mut buf, &vp9_track(Bytes::new()));
assert!(buf.windows(4).any(|w| w == b"vp09"));
assert!(!buf.windows(4).any(|w| w == b"avc1"));
}
#[test]
fn h264_sample_entry_still_writes_avc1() {
let mut buf = Vec::new();
write_stsd(&mut buf, &h264_track());
assert!(buf.windows(4).any(|w| w == b"avc1"));
assert!(!buf.windows(4).any(|w| w == b"vp09"));
}
#[test]
fn vp9_sample_entry_roundtrips_dimensions_and_codec() {
let mut buf = Vec::new();
write_stsd(&mut buf, &vp9_track(Bytes::new()));
let (width, height, codec, extra) = parse_stsd_entry(&buf);
assert_eq!(codec, Codec::Vp9);
assert_eq!(width, 640);
assert_eq!(height, 480);
assert!(!extra.is_empty(), "placeholder vpcC should round-trip");
}
#[test]
fn vp9_sample_entry_reuses_demuxed_vpcc_payload() {
let demuxed_vpcc = Bytes::from_static(&[1, 0, 0, 0, 2, 62, 0x8a, 1, 1, 1, 0, 0]);
let mut buf = Vec::new();
write_stsd(&mut buf, &vp9_track(demuxed_vpcc.clone()));
let (_, _, codec, extra) = parse_stsd_entry(&buf);
assert_eq!(codec, Codec::Vp9);
assert_eq!(extra, demuxed_vpcc);
}
#[test]
fn hevc_sample_entry_writes_hvc1_not_avc1() {
let mut buf = Vec::new();
write_stsd(&mut buf, &hevc_track(Bytes::new()));
assert!(buf.windows(4).any(|w| w == b"hvc1"));
assert!(!buf.windows(4).any(|w| w == b"avc1"));
}
#[test]
fn hevc_sample_entry_roundtrips_dimensions_and_codec() {
let mut buf = Vec::new();
write_stsd(&mut buf, &hevc_track(Bytes::new()));
let (width, height, codec, extra) = parse_stsd_entry(&buf);
assert_eq!(codec, Codec::Hevc);
assert_eq!(width, 1920);
assert_eq!(height, 1080);
assert!(!extra.is_empty(), "placeholder hvcC should round-trip");
}
#[test]
fn hevc_sample_entry_reuses_demuxed_hvcc_payload() {
let demuxed_hvcc = Bytes::from_static(&[
1, 1, 0x60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x5A, 0xF0, 0, 0xFC, 0xFD, 0xF8, 0xF8, 0, 0, 0x03, 0,
]);
let mut buf = Vec::new();
write_stsd(&mut buf, &hevc_track(demuxed_hvcc.clone()));
let (_, _, codec, extra) = parse_stsd_entry(&buf);
assert_eq!(codec, Codec::Hevc);
assert_eq!(extra, demuxed_hvcc);
}
#[test]
fn av1_sample_entry_writes_av01_not_avc1() {
let mut buf = Vec::new();
write_stsd(&mut buf, &av1_track(Bytes::new()));
assert!(buf.windows(4).any(|w| w == b"av01"));
assert!(!buf.windows(4).any(|w| w == b"avc1"));
}
#[test]
fn av1_sample_entry_roundtrips_dimensions_and_codec() {
let mut buf = Vec::new();
write_stsd(&mut buf, &av1_track(Bytes::new()));
let (width, height, codec, extra) = parse_stsd_entry(&buf);
assert_eq!(codec, Codec::Av1);
assert_eq!(width, 3840);
assert_eq!(height, 2160);
assert!(!extra.is_empty(), "placeholder av1C should round-trip");
}
#[test]
fn av1_sample_entry_reuses_demuxed_av1c_payload() {
let demuxed_av1c = Bytes::from_static(&[0x81, 0x08, 0x0C, 0]);
let mut buf = Vec::new();
write_stsd(&mut buf, &av1_track(demuxed_av1c.clone()));
let (_, _, codec, extra) = parse_stsd_entry(&buf);
assert_eq!(codec, Codec::Av1);
assert_eq!(extra, demuxed_av1c);
}
mod opus {
use super::{parse_stsd_entry, write_stsd};
use crate::types::{Bytes, Codec, Rational, Track};
const OPUS_HEAD: &[u8] = &[
b'O', b'p', b'u', b's', b'H', b'e', b'a', b'd', 1, 2, 0x38, 0x01, 0x80, 0xbb, 0x00, 0x00, 0x00, 0x00, 0, ];
fn opus_track(extra: Bytes) -> Track {
Track {
id: 0,
codec: Codec::Opus,
time_base: Rational::new(1, 48_000),
width: 0,
height: 0,
extra_data: extra,
}
}
#[test]
fn opus_sample_entry_writes_opus_dops_and_never_mp4a_esds() {
let mut buf = Vec::new();
write_stsd(&mut buf, &opus_track(Bytes::from(OPUS_HEAD.to_vec())));
assert!(
buf.windows(4).any(|w| w == b"Opus"),
"expected an Opus sample entry"
);
assert!(buf.windows(4).any(|w| w == b"dOps"), "expected a dOps box");
assert!(
!buf.windows(4).any(|w| w == b"mp4a"),
"Opus must not be written as an mp4a sample entry"
);
assert!(
!buf.windows(4).any(|w| w == b"esds"),
"the Opus spec forbids esds here; dOps replaces it"
);
}
#[test]
fn an_opus_track_no_longer_claims_to_be_aac() {
let mut buf = Vec::new();
write_stsd(&mut buf, &opus_track(Bytes::from(OPUS_HEAD.to_vec())));
assert!(
!buf.windows(2).any(|w| w == [0x40, 0x15]),
"found an AAC objectTypeIndication in an Opus sample entry"
);
}
#[test]
fn opus_head_round_trips_through_dops_with_byte_order_flipped() {
let mut buf = Vec::new();
write_stsd(&mut buf, &opus_track(Bytes::from(OPUS_HEAD.to_vec())));
let dops = buf
.windows(4)
.position(|w| w == b"dOps")
.map(|i| &buf[i + 4..])
.expect("dOps payload");
assert_eq!(
&dops[..11],
&[
0x01, 0x02, 0x01, 0x38, 0x00, 0x00, 0xbb, 0x80, 0x00, 0x00, 0x00
]
);
let (_, _, codec, extra) = parse_stsd_entry(&buf);
assert_eq!(codec, Codec::Opus);
assert_eq!(
&extra[..],
OPUS_HEAD,
"demux must hand back an OpusHead, matching what webm stores as CodecPrivate"
);
}
#[test]
fn a_track_with_no_opus_head_gets_a_valid_placeholder() {
let mut buf = Vec::new();
write_stsd(&mut buf, &opus_track(Bytes::new()));
let (_, _, codec, extra) = parse_stsd_entry(&buf);
assert_eq!(codec, Codec::Opus);
assert_eq!(&extra[..8], b"OpusHead");
assert_eq!(extra[9], 2, "placeholder is stereo");
assert_eq!(
u16::from_le_bytes([extra[10], extra[11]]),
3840,
"placeholder pre-skip is RFC 7845's recommended 80 ms at 48 kHz"
);
}
#[test]
fn a_multichannel_opus_head_carries_its_channel_mapping_table() {
let mut head = OPUS_HEAD.to_vec();
head[9] = 3; head[18] = 1; head.extend_from_slice(&[2, 1, 0, 1, 2]);
let mut buf = Vec::new();
write_stsd(&mut buf, &opus_track(Bytes::from(head.clone())));
let (_, _, codec, extra) = parse_stsd_entry(&buf);
assert_eq!(codec, Codec::Opus);
assert_eq!(
&extra[..],
&head[..],
"the mapping table must survive both directions"
);
}
#[test]
fn a_truncated_opus_head_falls_back_instead_of_writing_a_short_box() {
let mut buf = Vec::new();
write_stsd(&mut buf, &opus_track(Bytes::from(OPUS_HEAD[..12].to_vec())));
let (_, _, codec, extra) = parse_stsd_entry(&buf);
assert_eq!(codec, Codec::Opus);
assert_eq!(extra[9], 2, "fell back to the stereo placeholder");
}
}