use crate::annexb::{AvcConfig, HevcConfig, parse_avcc, parse_hvcc};
pub(crate) fn has_av01_sample_entry(data: &[u8]) -> bool {
let path: &[&[u8; 4]] = &[b"moov", b"trak", b"mdia", b"minf", b"stbl", b"stsd"];
let Some(stsd_body) = super::super::find_box_body(data, path) else {
return false;
};
if stsd_body.len() < 16 {
return false;
}
let mut pos = 8; while pos + 8 <= stsd_body.len() {
let entry_size = u32::from_be_bytes([
stsd_body[pos],
stsd_body[pos + 1],
stsd_body[pos + 2],
stsd_body[pos + 3],
]) as usize;
if entry_size == 0 {
break;
}
if pos + 4 < stsd_body.len() && &stsd_body[pos + 4..pos + 8] == b"av01" {
return true;
}
pos = pos.saturating_add(entry_size);
}
false
}
pub(super) fn hevc_sample_entry_fourcc(data: &[u8]) -> Option<[u8; 4]> {
let path: &[&[u8; 4]] = &[b"moov", b"trak", b"mdia", b"minf", b"stbl", b"stsd"];
let stsd_body = super::super::find_box_body(data, path)?;
if stsd_body.len() < 16 {
return None;
}
let mut pos = 8; while pos + 8 <= stsd_body.len() {
let entry_size = u32::from_be_bytes([
stsd_body[pos],
stsd_body[pos + 1],
stsd_body[pos + 2],
stsd_body[pos + 3],
]) as usize;
let entry_type: [u8; 4] = stsd_body[pos + 4..pos + 8].try_into().ok()?;
match &entry_type {
b"hvc1" | b"hev1" | b"hvc2" | b"hev2" | b"dvh1" | b"dvhe" => {
return Some(entry_type);
}
_ => {}
}
if entry_size == 0 {
break;
}
pos = pos.saturating_add(entry_size);
}
None
}
pub(crate) fn prores_sample_entry_fourcc(data: &[u8]) -> Option<[u8; 4]> {
let path: &[&[u8; 4]] = &[b"moov", b"trak", b"mdia", b"minf", b"stbl", b"stsd"];
let stsd_body = super::super::find_box_body(data, path)?;
if stsd_body.len() < 16 {
return None;
}
let mut pos = 8;
while pos + 8 <= stsd_body.len() {
let entry_size = u32::from_be_bytes([
stsd_body[pos],
stsd_body[pos + 1],
stsd_body[pos + 2],
stsd_body[pos + 3],
]) as usize;
let entry_type: [u8; 4] = stsd_body[pos + 4..pos + 8].try_into().ok()?;
match &entry_type {
b"apcn" | b"apch" | b"apcs" | b"apco" | b"ap4h" | b"ap4x" => {
return Some(entry_type);
}
_ => {}
}
if entry_size == 0 {
break;
}
pos = pos.saturating_add(entry_size);
}
None
}
pub(super) fn extract_avc_config(data: &[u8]) -> Option<AvcConfig> {
let path: &[&[u8; 4]] = &[b"moov", b"trak", b"mdia", b"minf", b"stbl", b"stsd"];
let stsd_body = super::super::find_box_body(data, path)?;
if stsd_body.len() < 16 {
return None;
}
let mut pos = 8;
while pos + 8 <= stsd_body.len() {
let entry_size = u32::from_be_bytes([
stsd_body[pos],
stsd_body[pos + 1],
stsd_body[pos + 2],
stsd_body[pos + 3],
]) as usize;
let entry_type = &stsd_body[pos + 4..pos + 8];
let is_avc = matches!(entry_type, b"avc1" | b"avc3");
if !is_avc {
if entry_size == 0 {
break;
}
pos = pos.saturating_add(entry_size);
continue;
}
let end = pos.saturating_add(entry_size);
if end > stsd_body.len() {
return None;
}
let child_start = pos + 8 + 78; if child_start >= end {
return None;
}
let avcc = super::super::find_direct_child(&stsd_body[child_start..end], b"avcC")?;
return parse_avcc(avcc);
}
None
}
pub(super) fn extract_hevc_config(data: &[u8]) -> Option<HevcConfig> {
let path: &[&[u8; 4]] = &[b"moov", b"trak", b"mdia", b"minf", b"stbl", b"stsd"];
let stsd_body = super::super::find_box_body(data, path)?;
if stsd_body.len() < 16 {
return None;
}
let mut pos = 8;
while pos + 8 <= stsd_body.len() {
let entry_size = u32::from_be_bytes([
stsd_body[pos],
stsd_body[pos + 1],
stsd_body[pos + 2],
stsd_body[pos + 3],
]) as usize;
let entry_type = &stsd_body[pos + 4..pos + 8];
let is_hevc = matches!(
entry_type,
b"hvc1" | b"hev1" | b"hvc2" | b"hev2" | b"dvh1" | b"dvhe"
);
if !is_hevc {
if entry_size == 0 {
break;
}
pos = pos.saturating_add(entry_size);
continue;
}
let end = pos.saturating_add(entry_size);
if end > stsd_body.len() {
return None;
}
let child_start = pos + 8 + 78; if child_start >= end {
return None;
}
let hvcc = super::super::find_direct_child(&stsd_body[child_start..end], b"hvcC")?;
return parse_hvcc(hvcc);
}
None
}
#[allow(dead_code)]
pub(super) fn extract_hevc_parameter_sets(data: &[u8]) -> Vec<Vec<u8>> {
extract_hevc_config(data)
.map(|cfg| cfg.parameter_sets)
.unwrap_or_default()
}
#[allow(dead_code)]
pub(crate) fn parse_avcc_param_sets(avcc: &[u8]) -> Vec<Vec<u8>> {
parse_avcc(avcc)
.map(|cfg| cfg.parameter_sets)
.unwrap_or_default()
}
#[allow(dead_code)]
pub(super) fn parse_hvcc_param_sets(hvcc: &[u8]) -> Vec<Vec<u8>> {
parse_hvcc(hvcc)
.map(|cfg| cfg.parameter_sets)
.unwrap_or_default()
}