fn mp4parse_avif_get_info_safe()

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)
}