#![forbid(unsafe_code)]
use crate::mp4::isobmff::header::{Header, parse_header};
#[derive(Debug, Clone)]
pub struct Mp4BoxNode {
pub header: ParsedBoxHeader,
pub payload_offset: usize,
pub children: Vec<Self>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ParsedBoxHeader {
pub tag: [u8; 4],
pub size: usize,
pub header_size: usize,
}
impl From<Header> for ParsedBoxHeader {
fn from(h: Header) -> Self {
Self {
tag: h.typ.0,
size: h.size,
header_size: h.header_len,
}
}
}
#[must_use]
pub fn parse_box_tree(buf: &[u8], base_offset: usize) -> Vec<Mp4BoxNode> {
let mut nodes = Vec::new();
let mut offset = 0;
while offset + 8 <= buf.len() {
let Some(h) = parse_header(&buf[offset..]) else {
break;
};
if offset + h.size > buf.len() || h.size == 0 {
break;
}
let payload_start = offset + h.header_len;
let box_end = offset + h.size;
let is_container = matches!(
&h.typ.0,
b"moov" | b"trak" | b"mdia" | b"minf" | b"dinf" | b"stbl" | b"mvex" | b"moof" | b"traf"
);
let children = if is_container && payload_start < box_end {
parse_box_tree(&buf[payload_start..box_end], base_offset + payload_start)
} else {
Vec::new()
};
nodes.push(Mp4BoxNode {
header: h.into(),
payload_offset: base_offset + payload_start,
children,
});
offset = box_end;
}
nodes
}