#![forbid(unsafe_code)]
use super::cenc_box::{TrackEncryption, parse_tenc};
use super::{parse_header, tag, write_box};
use crate::types::{Bytes, Codec, Track};
pub(crate) fn write_stsd(buf: &mut Vec<u8>, track: &Track) {
let audio = matches!(track.codec, Codec::Aac | Codec::Opus);
write_box(buf, tag::STSD, |st| {
st.extend_from_slice(&0u32.to_be_bytes());
st.extend_from_slice(&1u32.to_be_bytes());
if audio {
write_mp4a(st, track);
} else if track.codec == Codec::Vp9 {
write_vp09(st, track);
} else if track.codec == Codec::Hevc {
write_hvc1(st, track);
} else if track.codec == Codec::Av1 {
write_av01(st, track);
} else {
write_avc1(st, track);
}
});
}
pub(crate) fn parse_sample_entry(
data: &[u8],
width: &mut u32,
height: &mut u32,
codec: &mut Codec,
extra: &mut Bytes,
encryption: &mut Option<TrackEncryption>,
) {
let Some(hdr) = parse_header(data) else {
return;
};
if hdr.size > data.len() {
return;
}
let body = &data[hdr.header_len..hdr.size];
match &hdr.typ.0 {
b"avc1" | b"avc3" => parse_visual_avc(body, width, height, codec, extra),
b"vp09" => parse_visual_vp9(body, width, height, codec, extra),
b"hvc1" | b"hev1" => parse_visual_hevc(body, width, height, codec, extra),
b"av01" => parse_visual_av1(body, width, height, codec, extra),
b"mp4a" => parse_audio_mp4a(body, codec, extra),
b"encv" => {
parse_visual_avc(body, width, height, codec, extra);
if let Some(tenc) = find_tenc(body) {
*encryption = Some(tenc);
}
if let Some(frma) = find_nested_payload(body, *b"frma") {
if frma.len() >= 4 {
match &frma[..4] {
b"avc1" | b"avc3" => *codec = Codec::H264,
_ => {}
}
}
}
}
b"enca" => {
parse_audio_mp4a(body, codec, extra);
if let Some(tenc) = find_tenc(body) {
*encryption = Some(tenc);
}
}
_ => {}
}
}
fn parse_visual_avc(
body: &[u8],
width: &mut u32,
height: &mut u32,
codec: &mut Codec,
extra: &mut Bytes,
) {
*codec = Codec::H264;
if body.len() >= 28 {
*width = u32::from(u16::from_be_bytes([body[24], body[25]]));
*height = u32::from(u16::from_be_bytes([body[26], body[27]]));
}
if body.len() > 78 {
if let Some(avcc) = find_child_payload(&body[78..], *b"avcC") {
*extra = Bytes::copy_from_slice(avcc);
}
}
}
fn parse_visual_vp9(
body: &[u8],
width: &mut u32,
height: &mut u32,
codec: &mut Codec,
extra: &mut Bytes,
) {
*codec = Codec::Vp9;
if body.len() >= 28 {
*width = u32::from(u16::from_be_bytes([body[24], body[25]]));
*height = u32::from(u16::from_be_bytes([body[26], body[27]]));
}
if body.len() > 78 {
if let Some(vpcc) = find_child_payload(&body[78..], *b"vpcC") {
*extra = Bytes::copy_from_slice(vpcc);
}
}
}
fn parse_visual_hevc(
body: &[u8],
width: &mut u32,
height: &mut u32,
codec: &mut Codec,
extra: &mut Bytes,
) {
*codec = Codec::Hevc;
if body.len() >= 28 {
*width = u32::from(u16::from_be_bytes([body[24], body[25]]));
*height = u32::from(u16::from_be_bytes([body[26], body[27]]));
}
if body.len() > 78 {
if let Some(hvcc) = find_child_payload(&body[78..], *b"hvcC") {
*extra = Bytes::copy_from_slice(hvcc);
}
}
}
fn parse_visual_av1(
body: &[u8],
width: &mut u32,
height: &mut u32,
codec: &mut Codec,
extra: &mut Bytes,
) {
*codec = Codec::Av1;
if body.len() >= 28 {
*width = u32::from(u16::from_be_bytes([body[24], body[25]]));
*height = u32::from(u16::from_be_bytes([body[26], body[27]]));
}
if body.len() > 78 {
if let Some(av1c) = find_child_payload(&body[78..], *b"av1C") {
*extra = Bytes::copy_from_slice(av1c);
}
}
}
fn parse_audio_mp4a(body: &[u8], codec: &mut Codec, extra: &mut Bytes) {
*codec = Codec::Aac;
if body.len() > 28 {
if let Some(esds) = find_child_payload(&body[28..], *b"esds") {
if let Some(asc) = find_asc(esds) {
*extra = asc;
}
}
}
}
fn find_tenc(body: &[u8]) -> Option<TrackEncryption> {
let tenc = find_nested_payload(body, *b"tenc")?;
parse_tenc(tenc)
}
fn write_avc1(buf: &mut Vec<u8>, track: &Track) {
write_box(buf, tag::AVC1, |a| {
a.extend_from_slice(&[0u8; 6]);
a.extend_from_slice(&1u16.to_be_bytes());
a.extend_from_slice(&0u16.to_be_bytes());
a.extend_from_slice(&0u16.to_be_bytes());
a.extend_from_slice(&[0u8; 12]);
a.extend_from_slice(&(track.width.min(u32::from(u16::MAX)) as u16).to_be_bytes());
a.extend_from_slice(&(track.height.min(u32::from(u16::MAX)) as u16).to_be_bytes());
a.extend_from_slice(&0x0048_0000u32.to_be_bytes());
a.extend_from_slice(&0x0048_0000u32.to_be_bytes());
a.extend_from_slice(&0u32.to_be_bytes());
a.extend_from_slice(&1u16.to_be_bytes());
a.extend_from_slice(&[0u8; 32]);
a.extend_from_slice(&0x0018u16.to_be_bytes());
a.extend_from_slice(&0xffffu16.to_be_bytes());
write_box(a, tag::AVCC, |c| {
if track.extra_data.is_empty() {
c.extend_from_slice(&[1, 0x42, 0x00, 0x1e, 0xff, 0xe1, 0, 0, 1, 0, 0]);
} else {
c.extend_from_slice(&track.extra_data);
}
});
});
}
const VPCC_PLACEHOLDER: &[u8] = &[1, 0, 0, 0, 0, 10, 0x82, 2, 2, 2, 0, 0];
fn write_vp09(buf: &mut Vec<u8>, track: &Track) {
write_box(buf, tag::VP09, |a| {
a.extend_from_slice(&[0u8; 6]);
a.extend_from_slice(&1u16.to_be_bytes());
a.extend_from_slice(&0u16.to_be_bytes());
a.extend_from_slice(&0u16.to_be_bytes());
a.extend_from_slice(&[0u8; 12]);
a.extend_from_slice(&(track.width.min(u32::from(u16::MAX)) as u16).to_be_bytes());
a.extend_from_slice(&(track.height.min(u32::from(u16::MAX)) as u16).to_be_bytes());
a.extend_from_slice(&0x0048_0000u32.to_be_bytes());
a.extend_from_slice(&0x0048_0000u32.to_be_bytes());
a.extend_from_slice(&0u32.to_be_bytes());
a.extend_from_slice(&1u16.to_be_bytes());
a.extend_from_slice(&[0u8; 32]);
a.extend_from_slice(&0x0018u16.to_be_bytes());
a.extend_from_slice(&0xffffu16.to_be_bytes());
write_box(a, tag::VPCC, |c| {
if track.extra_data.is_empty() {
c.extend_from_slice(VPCC_PLACEHOLDER);
} else {
c.extend_from_slice(&track.extra_data);
}
});
});
}
const HVCC_PLACEHOLDER: &[u8] = &[
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xF0, 0x00, 0xFC, 0xFC, 0xF8, 0xF8, 0, 0, 0x03, 0, ];
fn write_hvc1(buf: &mut Vec<u8>, track: &Track) {
write_box(buf, tag::HVC1, |a| {
a.extend_from_slice(&[0u8; 6]);
a.extend_from_slice(&1u16.to_be_bytes());
a.extend_from_slice(&0u16.to_be_bytes());
a.extend_from_slice(&0u16.to_be_bytes());
a.extend_from_slice(&[0u8; 12]);
a.extend_from_slice(&(track.width.min(u32::from(u16::MAX)) as u16).to_be_bytes());
a.extend_from_slice(&(track.height.min(u32::from(u16::MAX)) as u16).to_be_bytes());
a.extend_from_slice(&0x0048_0000u32.to_be_bytes());
a.extend_from_slice(&0x0048_0000u32.to_be_bytes());
a.extend_from_slice(&0u32.to_be_bytes());
a.extend_from_slice(&1u16.to_be_bytes());
a.extend_from_slice(&[0u8; 32]);
a.extend_from_slice(&0x0018u16.to_be_bytes());
a.extend_from_slice(&0xffffu16.to_be_bytes());
write_box(a, tag::HVCC, |c| {
if track.extra_data.is_empty() {
c.extend_from_slice(HVCC_PLACEHOLDER);
} else {
c.extend_from_slice(&track.extra_data);
}
});
});
}
const AV1C_PLACEHOLDER: &[u8] = &[0x81, 0, 0, 0];
fn write_av01(buf: &mut Vec<u8>, track: &Track) {
write_box(buf, tag::AV01, |a| {
a.extend_from_slice(&[0u8; 6]);
a.extend_from_slice(&1u16.to_be_bytes());
a.extend_from_slice(&0u16.to_be_bytes());
a.extend_from_slice(&0u16.to_be_bytes());
a.extend_from_slice(&[0u8; 12]);
a.extend_from_slice(&(track.width.min(u32::from(u16::MAX)) as u16).to_be_bytes());
a.extend_from_slice(&(track.height.min(u32::from(u16::MAX)) as u16).to_be_bytes());
a.extend_from_slice(&0x0048_0000u32.to_be_bytes());
a.extend_from_slice(&0x0048_0000u32.to_be_bytes());
a.extend_from_slice(&0u32.to_be_bytes());
a.extend_from_slice(&1u16.to_be_bytes());
a.extend_from_slice(&[0u8; 32]);
a.extend_from_slice(&0x0018u16.to_be_bytes());
a.extend_from_slice(&0xffffu16.to_be_bytes());
write_box(a, tag::AV1C, |c| {
if track.extra_data.is_empty() {
c.extend_from_slice(AV1C_PLACEHOLDER);
} else {
c.extend_from_slice(&track.extra_data);
}
});
});
}
fn write_mp4a(buf: &mut Vec<u8>, track: &Track) {
write_box(buf, tag::MP4A, |a| {
a.extend_from_slice(&[0u8; 6]);
a.extend_from_slice(&1u16.to_be_bytes());
a.extend_from_slice(&[0u8; 8]);
a.extend_from_slice(&2u16.to_be_bytes());
a.extend_from_slice(&16u16.to_be_bytes());
a.extend_from_slice(&0u32.to_be_bytes());
a.extend_from_slice(&(track.time_base.den << 16).to_be_bytes());
write_box(a, tag::ESDS, |e| {
e.extend_from_slice(&0u32.to_be_bytes());
let asc: &[u8] = if track.extra_data.is_empty() {
&[0x12, 0x10]
} else {
&track.extra_data
};
e.push(0x03);
e.push(u8::try_from(asc.len() + 23).unwrap_or(u8::MAX));
e.extend_from_slice(&(track.id as u16 + 1).to_be_bytes());
e.push(0);
e.push(0x04);
e.push(u8::try_from(asc.len() + 15).unwrap_or(u8::MAX));
e.push(0x40);
e.push(0x15);
e.extend_from_slice(&[0u8; 3]);
e.extend_from_slice(&128_000u32.to_be_bytes());
e.extend_from_slice(&128_000u32.to_be_bytes());
e.push(0x05);
e.push(u8::try_from(asc.len()).unwrap_or(u8::MAX));
e.extend_from_slice(asc);
});
});
}
fn find_child_payload(data: &[u8], box_tag: [u8; 4]) -> Option<&[u8]> {
let mut pos = 0;
while pos + 8 <= data.len() {
let hdr = parse_header(&data[pos..])?;
if pos + hdr.size > data.len() {
return None;
}
if hdr.typ.0 == box_tag {
return Some(&data[pos + hdr.header_len..pos + hdr.size]);
}
pos += hdr.size;
}
None
}
fn find_nested_payload(data: &[u8], box_tag: [u8; 4]) -> Option<&[u8]> {
let mut pos = 0;
while pos + 8 <= data.len() {
let hdr = parse_header(&data[pos..])?;
if pos + hdr.size > data.len() {
return None;
}
let payload = &data[pos + hdr.header_len..pos + hdr.size];
if hdr.typ.0 == box_tag {
return Some(payload);
}
if let Some(found) = find_nested_payload(payload, box_tag) {
return Some(found);
}
pos += hdr.size;
}
None
}
fn find_asc(esds: &[u8]) -> Option<Bytes> {
let start = if esds.len() >= 6 { 4 } else { 0 };
let mut i = start;
while i + 1 < esds.len() {
if esds[i] == 0x05 {
let len = usize::from(esds[i + 1]);
let s = i + 2;
let e = s.saturating_add(len).min(esds.len());
if s < e {
return Some(Bytes::copy_from_slice(&esds[s..e]));
}
return None;
}
i += 1;
}
None
}
#[cfg(test)]
#[path = "sample_entry_tests.rs"]
mod tests;