in mp4parse_capi/src/lib.rs [1082:1233]
fn mp4parse_avif_get_info_safe(context: &AvifContext) -> mp4parse::Result<Mp4parseAvifInfo> {
let info = Mp4parseAvifInfo {
premultiplied_alpha: context.premultiplied_alpha,
major_brand: context.major_brand.value,
unsupported_features_bitfield: context.unsupported_features.into_bitfield(),
spatial_extents: context.spatial_extents_ptr()?,
nclx_colour_information: context
.nclx_colour_information_ptr()
.unwrap_or(Ok(std::ptr::null()))?,
icc_colour_information: Mp4parseByteData::with_data(
context.icc_colour_information().unwrap_or(Ok(&[]))?,
),
image_rotation: context.image_rotation()?,
image_mirror: context.image_mirror_ptr()?,
pixel_aspect_ratio: context.pixel_aspect_ratio_ptr()?,
has_primary_item: context.primary_item_is_present(),
primary_item_bit_depth: 0,
has_alpha_item: context.alpha_item_is_present(),
alpha_item_bit_depth: 0,
has_sequence: false,
loop_mode: Mp4parseAvifLoopMode::NoEdits,
loop_count: 0,
color_track_id: 0,
color_track_bit_depth: 0,
alpha_track_id: 0,
alpha_track_bit_depth: 0,
};
fn get_bit_depth(data: &[u8]) -> u8 {
if !data.is_empty() && data.iter().all(|v| *v == data[0]) {
data[0]
} else {
0
}
}
let primary_item_bit_depth =
get_bit_depth(context.primary_item_bits_per_channel().unwrap_or(Ok(&[]))?);
let alpha_item_bit_depth =
get_bit_depth(context.alpha_item_bits_per_channel().unwrap_or(Ok(&[]))?);
if let Some(sequence) = &context.sequence {
// Tracks must have track_id and samples
fn get_track<T>(tracks: &TryVec<Track>, pred: T) -> Option<&Track>
where
T: Fn(&Track) -> bool,
{
tracks.iter().find(|track| {
if track.track_id.is_none() {
return false;
}
match &track.stsc {
Some(stsc) => {
if stsc.samples.is_empty() {
return false;
}
if !pred(track) {
return false;
}
stsc.samples.iter().any(|chunk| chunk.samples_per_chunk > 0)
}
_ => false,
}
})
}
// Color track will be the first track found
let color_track = match get_track(&sequence.tracks, |_| true) {
Some(v) => v,
_ => return Ok(info),
};
// Alpha track will be the first track found with auxl.aux_for_track_id set to color_track's id
let alpha_track = get_track(&sequence.tracks, |track| match &track.tref {
Some(tref) => tref.has_auxl_reference(color_track.track_id.unwrap()),
_ => false,
});
fn get_av1c(track: &Track) -> Option<&AV1ConfigBox> {
if let Some(stsd) = &track.stsd {
for entry in &stsd.descriptions {
if let SampleEntry::Video(video_entry) = entry {
if let VideoCodecSpecific::AV1Config(av1c) = &video_entry.codec_specific {
return Some(av1c);
}
}
}
}
None
}
let color_track_id = color_track.track_id.unwrap();
let color_track_bit_depth = match get_av1c(color_track) {
Some(av1c) => av1c.bit_depth,
_ => return Ok(info),
};
let (alpha_track_id, alpha_track_bit_depth) = match alpha_track {
Some(track) => (
track.track_id.unwrap(),
match get_av1c(track) {
Some(av1c) => av1c.bit_depth,
_ => return Ok(info),
},
),
_ => (0, 0),
};
let (loop_mode, loop_count) = match color_track.tkhd.as_ref().map(|tkhd| tkhd.duration) {
Some(movie_duration) if movie_duration == u64::MAX => {
(Mp4parseAvifLoopMode::LoopInfinitely, 0)
}
Some(movie_duration) => match color_track.looped {
Some(true) => match color_track.edited_duration.map(|v| v.0) {
Some(segment_duration) => {
match movie_duration.checked_div(segment_duration).and_then(|n| {
match movie_duration.checked_rem(segment_duration) {
Some(0) => Some(n.saturating_sub(1)),
Some(_) => Some(n),
None => None,
}
}) {
Some(n) => (Mp4parseAvifLoopMode::LoopByCount, n),
None => (Mp4parseAvifLoopMode::LoopInfinitely, 0),
}
}
None => (Mp4parseAvifLoopMode::NoEdits, 0),
},
Some(false) => (Mp4parseAvifLoopMode::LoopByCount, 0),
None => (Mp4parseAvifLoopMode::NoEdits, 0),
},
None => (Mp4parseAvifLoopMode::LoopInfinitely, 0),
};
return Ok(Mp4parseAvifInfo {
primary_item_bit_depth,
alpha_item_bit_depth,
has_sequence: true,
loop_mode,
loop_count,
color_track_id,
color_track_bit_depth,
alpha_track_id,
alpha_track_bit_depth,
..info
});
}
Ok(info)
}