diff --git a/mp4parse/Cargo.toml b/mp4parse/Cargo.toml index f2c437cb..d87f80cf 100644 --- a/mp4parse/Cargo.toml +++ b/mp4parse/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mp4parse" -version = "0.15.0" +version = "0.16.0" authors = [ "Ralph Giles ", "Matthew Gregan ", diff --git a/mp4parse/src/boxes.rs b/mp4parse/src/boxes.rs index 97da1a26..838df5db 100644 --- a/mp4parse/src/boxes.rs +++ b/mp4parse/src/boxes.rs @@ -110,6 +110,8 @@ box_database!( MovieHeaderBox 0x6d76_6864, // "mvhd" TrackBox 0x7472_616b, // "trak" TrackHeaderBox 0x746b_6864, // "tkhd" + TrackReferenceBox 0x7472_6566, // "tref" + AuxiliaryBox 0x6175_786C, // "auxl" EditBox 0x6564_7473, // "edts" MediaBox 0x6d64_6961, // "mdia" EditListBox 0x656c_7374, // "elst" diff --git a/mp4parse/src/lib.rs b/mp4parse/src/lib.rs index 35453e40..b749c672 100644 --- a/mp4parse/src/lib.rs +++ b/mp4parse/src/lib.rs @@ -299,19 +299,14 @@ impl Feature { match self { Self::Auxc | Self::Av1c + | Self::Avis | Self::Colr | Self::Imir | Self::Irot | Self::Ispe | Self::Pasp | Self::Pixi => true, - Self::A1lx - | Self::A1op - | Self::Clap - | Self::Grid - | Self::Ipro - | Self::Lsel - | Self::Avis => false, + Self::A1lx | Self::A1op | Self::Clap | Self::Grid | Self::Ipro | Self::Lsel => false, } } } @@ -1100,6 +1095,29 @@ pub enum SampleEntry { Unknown, } +#[derive(Debug)] +pub struct TrackReferenceBox { + pub references: TryVec, +} + +impl TrackReferenceBox { + pub fn has_auxl_reference(&self, track_id: u32) -> bool { + self.references.iter().any(|entry| match entry { + TrackReferenceEntry::Auxiliary(aux_entry) => aux_entry.track_ids.contains(&track_id), + }) + } +} + +#[derive(Debug)] +pub enum TrackReferenceEntry { + Auxiliary(TrackReference), +} + +#[derive(Debug)] +pub struct TrackReference { + pub track_ids: TryVec, +} + /// An Elementary Stream Descriptor /// See MPEG-4 Systems (ISO 14496-1:2010) ยง 7.2.6.5 #[allow(non_camel_case_types)] @@ -1550,7 +1568,7 @@ impl AvifItem { } } -#[derive(Debug)] +#[derive(Default, Debug)] pub struct AvifContext { /// Level of deviation from the specification before failing the parse strictness: ParseStrictness, @@ -1574,13 +1592,17 @@ pub struct AvifContext { /// Should probably only ever be [`AVIF_BRAND`] or [`AVIS_BRAND`], but other values /// are legal as long as one of the two is the `compatible_brand` list. pub major_brand: FourCC, - /// True if a `moov` box is present - pub has_sequence: bool, + /// Information on the sequence contained in the image, or None if not present + pub sequence: Option, /// A collection of unsupported features encountered during the parse pub unsupported_features: UnsupportedFeatures, } impl AvifContext { + pub fn primary_item_is_present(&self) -> bool { + self.primary_item.is_some() + } + pub fn primary_item_coded_data(&self) -> Option<&[u8]> { self.primary_item .as_ref() @@ -1593,6 +1615,10 @@ impl AvifContext { .map(|item| self.image_bits_per_channel(item.id)) } + pub fn alpha_item_is_present(&self) -> bool { + self.alpha_item.is_some() + } + pub fn alpha_item_coded_data(&self) -> Option<&[u8]> { self.alpha_item .as_ref() @@ -2122,6 +2148,8 @@ enum Extent { pub enum TrackType { Audio, Video, + Picture, + AuxiliaryVideo, Metadata, Unknown, } @@ -2142,6 +2170,12 @@ pub enum ParseStrictness { Strict, // Error on "should" directives } +impl Default for ParseStrictness { + fn default() -> Self { + ParseStrictness::Normal + } +} + fn fail_with_status_if(violation: bool, status: Status) -> Result<()> { let error = Error::from(status); if violation { @@ -2227,6 +2261,7 @@ pub struct Track { pub stco: Option, // It is for stco or co64. pub stss: Option, pub ctts: Option, + pub tref: Option, } impl Track { @@ -2496,10 +2531,6 @@ pub fn read_avif(f: &mut T, strictness: ParseStrictness) -> Result(f: &mut T, strictness: ParseStrictness) -> Result(f: &mut BMFFBox, track: &mut Track) -> Result<()> { } BoxType::EditBox => read_edts(&mut b, track)?, BoxType::MediaBox => read_mdia(&mut b, track)?, + BoxType::TrackReferenceBox => track.tref = Some(read_tref(&mut b)?), _ => skip_box_content(&mut b)?, }; check_parser_state!(b.content); @@ -4430,6 +4462,8 @@ fn read_mdia(f: &mut BMFFBox, track: &mut Track) -> Result<()> { match hdlr.handler_type.value.as_ref() { b"vide" => track.track_type = TrackType::Video, + b"pict" => track.track_type = TrackType::Picture, + b"auxv" => track.track_type = TrackType::AuxiliaryVideo, b"soun" => track.track_type = TrackType::Audio, b"meta" => track.track_type = TrackType::Metadata, _ => (), @@ -4444,6 +4478,32 @@ fn read_mdia(f: &mut BMFFBox, track: &mut Track) -> Result<()> { Ok(()) } +fn read_tref(f: &mut BMFFBox) -> Result { + // Will likely only see trefs with one auxl + let mut references = TryVec::with_capacity(1)?; + let mut iter = f.box_iter(); + while let Some(mut b) = iter.next_box()? { + match b.head.name { + BoxType::AuxiliaryBox => { + references.push(TrackReferenceEntry::Auxiliary(read_tref_auxl(&mut b)?))? + } + _ => skip_box_content(&mut b)?, + }; + check_parser_state!(b.content); + } + Ok(TrackReferenceBox { references }) +} + +fn read_tref_auxl(f: &mut BMFFBox) -> Result { + let num_track_ids = (f.bytes_left() / std::mem::size_of::().to_u64()).try_into()?; + let mut track_ids = TryVec::with_capacity(num_track_ids)?; + for _ in 0..num_track_ids { + track_ids.push(be_u32(f)?)?; + } + + Ok(TrackReference { track_ids }) +} + fn read_minf(f: &mut BMFFBox, track: &mut Track) -> Result<()> { let mut iter = f.box_iter(); while let Some(mut b) = iter.next_box()? { @@ -5830,6 +5890,8 @@ fn read_stsd(src: &mut BMFFBox, track: &mut Track) -> Result read_video_sample_entry(&mut b), + TrackType::Picture => read_video_sample_entry(&mut b), + TrackType::AuxiliaryVideo => read_video_sample_entry(&mut b), TrackType::Audio => read_audio_sample_entry(&mut b), TrackType::Metadata => Err(Error::Unsupported("metadata track")), TrackType::Unknown => Err(Error::Unsupported("unknown track type")), diff --git a/mp4parse/tests/public.rs b/mp4parse/tests/public.rs index bb3ed2ed..28ef1ba1 100644 --- a/mp4parse/tests/public.rs +++ b/mp4parse/tests/public.rs @@ -95,8 +95,6 @@ static AVIF_UNSUPPORTED_IMAGES: &[&str] = &[ AVIF_GRID, AVIF_GRID_A1LX, AVIF_LSEL, - AVIF_AVIS_MAJOR_NO_PITM, - AVIF_AVIS_MAJOR_WITH_PITM_AND_ALPHA, "av1-avif/testFiles/Apple/multilayer_examples/animals_00_multilayer_a1lx.avif", "av1-avif/testFiles/Apple/multilayer_examples/animals_00_multilayer_a1op.avif", "av1-avif/testFiles/Apple/multilayer_examples/animals_00_multilayer_a1op_lsel.avif", @@ -107,7 +105,6 @@ static AVIF_UNSUPPORTED_IMAGES: &[&str] = &[ "av1-avif/testFiles/Microsoft/Chimera_10bit_cropped_to_1920x1008.avif", "av1-avif/testFiles/Microsoft/Chimera_10bit_cropped_to_1920x1008_with_HDR_metadata.avif", "av1-avif/testFiles/Microsoft/Chimera_8bit_cropped_480x256.avif", - "av1-avif/testFiles/Netflix/avis/alpha_video.avif", "av1-avif/testFiles/Xiph/abandoned_filmgrain.avif", "av1-avif/testFiles/Xiph/fruits_2layer_thumbsize.avif", "av1-avif/testFiles/Xiph/quebec_3layer_op2.avif", @@ -115,12 +112,6 @@ static AVIF_UNSUPPORTED_IMAGES: &[&str] = &[ "av1-avif/testFiles/Xiph/tiger_3layer_3res.avif", "link-u-avif-sample-images/kimono.crop.avif", "link-u-avif-sample-images/kimono.mirror-vertical.rotate270.crop.avif", - "link-u-avif-sample-images/star-10bpc-with-alpha.avifs", - "link-u-avif-sample-images/star-10bpc.avifs", - "link-u-avif-sample-images/star-12bpc-with-alpha.avifs", - "link-u-avif-sample-images/star-12bpc.avifs", - "link-u-avif-sample-images/star-8bpc-with-alpha.avifs", - "link-u-avif-sample-images/star-8bpc.avifs", ]; /// See https://github.com/AOMediaCodec/av1-avif/issues/150 /// https://github.com/AOMediaCodec/av1-avif/issues/174 @@ -346,7 +337,7 @@ fn public_api() { assert!(a.samplesize > 0); assert!(a.samplerate > 0.0); } - mp4::TrackType::Metadata | mp4::TrackType::Unknown => {} + _ => {} } } } @@ -1201,8 +1192,8 @@ fn public_avis_major_no_pitm() { match mp4::read_avif(input, ParseStrictness::Normal) { Ok(context) => { assert_eq!(context.major_brand, mp4::AVIS_BRAND); - assert!(context.unsupported_features.contains(mp4::Feature::Avis)); assert!(context.primary_item_coded_data().is_none()); + assert!(context.sequence.is_some()); } Err(e) => panic!("Expected Ok(_), found {:?}", e), } @@ -1214,9 +1205,9 @@ fn public_avis_major_with_pitm_and_alpha() { match mp4::read_avif(input, ParseStrictness::Normal) { Ok(context) => { assert_eq!(context.major_brand, mp4::AVIS_BRAND); - assert!(context.unsupported_features.contains(mp4::Feature::Avis)); assert!(context.primary_item_coded_data().is_some()); assert!(context.alpha_item_coded_data().is_some()); + assert!(context.sequence.is_some()); } Err(e) => panic!("Expected Ok(_), found {:?}", e), } diff --git a/mp4parse_capi/Cargo.toml b/mp4parse_capi/Cargo.toml index bb27ae33..6b95a3ec 100644 --- a/mp4parse_capi/Cargo.toml +++ b/mp4parse_capi/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mp4parse_capi" -version = "0.15.0" +version = "0.16.0" authors = [ "Ralph Giles ", "Matthew Gregan ", @@ -27,7 +27,7 @@ travis-ci = { repository = "https://github.com/mozilla/mp4parse-rust" } byteorder = "1.2.1" fallible_collections = { version = "0.4", features = ["std_io"] } log = "0.4" -mp4parse = { version = "0.15.0", path = "../mp4parse", features = ["unstable-api"] } +mp4parse = { version = "0.16.0", path = "../mp4parse", features = ["unstable-api"] } num-traits = "0.2.14" [dev-dependencies] diff --git a/mp4parse_capi/examples/dump.rs b/mp4parse_capi/examples/dump.rs index 1c58a2ee..f61693b9 100644 --- a/mp4parse_capi/examples/dump.rs +++ b/mp4parse_capi/examples/dump.rs @@ -112,7 +112,9 @@ fn dump_file(filename: &str, strictness: ParseStrictness) { } } } - Mp4parseTrackType::Video => { + Mp4parseTrackType::Video + | Mp4parseTrackType::Picture + | Mp4parseTrackType::AuxiliaryVideo => { let mut video_info = Mp4parseTrackVideoInfo::default(); match mp4parse_get_track_video_info(parser, i, &mut video_info) { Mp4parseStatus::Ok => { diff --git a/mp4parse_capi/src/lib.rs b/mp4parse_capi/src/lib.rs index 43e208c6..90a71e93 100644 --- a/mp4parse_capi/src/lib.rs +++ b/mp4parse_capi/src/lib.rs @@ -45,6 +45,7 @@ use mp4parse::serialize_opus_header; use mp4parse::unstable::{ create_sample_table, media_time_to_us, track_time_to_us, CheckedInteger, Indice, Microseconds, }; +use mp4parse::AV1ConfigBox; use mp4parse::AudioCodecSpecific; use mp4parse::AvifContext; use mp4parse::CodecType; @@ -53,6 +54,7 @@ use mp4parse::MediaContext; pub use mp4parse::ParseStrictness; use mp4parse::SampleEntry; pub use mp4parse::Status as Mp4parseStatus; +use mp4parse::Track; use mp4parse::TrackType; use mp4parse::TryBox; use mp4parse::TryHashMap; @@ -73,8 +75,10 @@ struct String; #[derive(PartialEq, Eq, Debug)] pub enum Mp4parseTrackType { Video = 0, - Audio = 1, - Metadata = 2, + Picture = 1, + AuxiliaryVideo = 2, + Audio = 3, + Metadata = 4, } impl Default for Mp4parseTrackType { @@ -310,15 +314,10 @@ pub struct Mp4parseParser { #[repr(C)] #[derive(Debug)] -pub struct Mp4parseAvifImageItem { - pub coded_data: Mp4parseByteData, - pub bits_per_channel: Mp4parseByteData, -} - -#[repr(C)] -#[derive(Debug)] -pub struct Mp4parseAvifImage { - pub primary_image: Mp4parseAvifImageItem, +pub struct Mp4parseAvifInfo { + pub premultiplied_alpha: bool, + pub major_brand: [u8; 4], + pub unsupported_features_bitfield: u32, /// The size of the image; should never be null unless using permissive parsing pub spatial_extents: *const mp4parse::ImageSpatialExtentsProperty, pub nclx_colour_information: *const mp4parse::NclxColourInformation, @@ -326,12 +325,33 @@ pub struct Mp4parseAvifImage { pub image_rotation: mp4parse::ImageRotation, pub image_mirror: *const mp4parse::ImageMirror, pub pixel_aspect_ratio: *const mp4parse::PixelAspectRatio, - /// If no alpha item exists, members' `.length` will be 0 and `.data` will be null - pub alpha_image: Mp4parseAvifImageItem, - pub premultiplied_alpha: bool, - pub major_brand: [u8; 4], + + /// Whether there is a `pitm` reference to the color image present. + pub has_primary_item: bool, + /// Bit depth for the item referenced by `pitm`, or 0 if values are inconsistent. + pub primary_item_bit_depth: u8, + /// Whether there is an `auxl` reference to the `pitm`-accompanying + /// alpha image present. + pub has_alpha_item: bool, + /// Bit depth for the alpha item used by the `pitm`, or 0 if values are inconsistent. + pub alpha_item_bit_depth: u8, + + /// Whether there is a sequence. Can be true with no primary image. pub has_sequence: bool, - pub unsupported_features_bitfield: u32, + /// The color track's ID, which must be valid if has_sequence is true. + pub color_track_id: u32, + pub color_track_bit_depth: u8, + /// The track ID of the alpha track, will be 0 if no alpha track is present. + pub alpha_track_id: u32, + pub alpha_track_bit_depth: u8, +} + +#[repr(C)] +#[derive(Debug)] +pub struct Mp4parseAvifImage { + pub primary_image: Mp4parseByteData, + /// If no alpha item exists, members' `.length` will be 0 and `.data` will be null + pub alpha_image: Mp4parseByteData, } /// A unified interface for the parsers which have different contexts, but @@ -375,8 +395,10 @@ impl ContextParser for Mp4parseParser { } } +#[derive(Default)] pub struct Mp4parseAvifParser { context: AvifContext, + sample_table: TryHashMap>, } impl Mp4parseAvifParser { @@ -389,7 +411,10 @@ impl ContextParser for Mp4parseAvifParser { type Context = AvifContext; fn with_context(context: Self::Context) -> Self { - Self { context } + Self { + context, + ..Default::default() + } } fn read(io: &mut T, strictness: ParseStrictness) -> mp4parse::Result { @@ -595,6 +620,8 @@ pub unsafe extern "C" fn mp4parse_get_track_info( info.track_type = match context.tracks[track_index].track_type { TrackType::Video => Mp4parseTrackType::Video, + TrackType::Picture => Mp4parseTrackType::Picture, + TrackType::AuxiliaryVideo => Mp4parseTrackType::AuxiliaryVideo, TrackType::Audio => Mp4parseTrackType::Audio, TrackType::Metadata => Mp4parseTrackType::Metadata, TrackType::Unknown => return Mp4parseStatus::Unsupported, @@ -1018,6 +1045,158 @@ fn mp4parse_get_track_video_info_safe( Ok(()) } +/// Return a struct containing meta information read by previous +/// `mp4parse_avif_new()` call. +/// +/// `color_track_id`and `alpha_track_id` will be 0 if has_sequence is false. +/// `alpha_track_id` will be 0 if no alpha aux track is present. +/// +/// # Safety +/// +/// This function is unsafe because it dereferences both the parser and +/// avif_info raw pointers passed into it. Callers should ensure the parser +/// pointer points to a valid `Mp4parseAvifParser`, and that the avif_info +/// pointer points to a valid `Mp4parseAvifInfo`. +#[no_mangle] +pub unsafe extern "C" fn mp4parse_avif_get_info( + parser: *const Mp4parseAvifParser, + avif_info: *mut Mp4parseAvifInfo, +) -> Mp4parseStatus { + if parser.is_null() || avif_info.is_null() { + return Mp4parseStatus::BadArg; + } + + if let Ok(info) = mp4parse_avif_get_info_safe((*parser).context()) { + *avif_info = info; + Mp4parseStatus::Ok + } else { + Mp4parseStatus::Invalid + } +} + +fn mp4parse_avif_get_info_safe(context: &AvifContext) -> mp4parse::Result { + 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, + 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.primary_item_bits_per_channel().unwrap_or(Ok(&[]))?); + + if let Some(sequence) = &context.sequence { + // Tracks must have track_id and samples + fn get_track(tracks: &TryVec, 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), + }; + + return Ok(Mp4parseAvifInfo { + primary_item_bit_depth, + alpha_item_bit_depth, + has_sequence: true, + color_track_id, + color_track_bit_depth, + alpha_track_id, + alpha_track_bit_depth, + ..info + }); + } + + Ok(info) +} + /// Return a pointer to the primary item parsed by previous `mp4parse_avif_new()` call. /// /// # Safety @@ -1051,39 +1230,11 @@ pub fn mp4parse_avif_get_image_safe( parser: &Mp4parseAvifParser, ) -> mp4parse::Result { let context = parser.context(); - - let primary_image = Mp4parseAvifImageItem { - coded_data: Mp4parseByteData::with_data(context.primary_item_coded_data().unwrap_or(&[])), - bits_per_channel: Mp4parseByteData::with_data( - context.primary_item_bits_per_channel().unwrap_or(Ok(&[]))?, - ), - }; - - // If there is no alpha present, all the `Mp4parseByteData`s will be zero length - let alpha_image = Mp4parseAvifImageItem { - coded_data: Mp4parseByteData::with_data(context.alpha_item_coded_data().unwrap_or(&[])), - bits_per_channel: Mp4parseByteData::with_data( - context.alpha_item_bits_per_channel().unwrap_or(Ok(&[]))?, - ), - }; - Ok(Mp4parseAvifImage { - primary_image, - 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(&[]))?, + primary_image: Mp4parseByteData::with_data( + context.primary_item_coded_data().unwrap_or(&[]), ), - image_rotation: context.image_rotation()?, - image_mirror: context.image_mirror_ptr()?, - pixel_aspect_ratio: context.pixel_aspect_ratio_ptr()?, - alpha_image, - premultiplied_alpha: context.premultiplied_alpha, - major_brand: context.major_brand.value, - has_sequence: context.has_sequence, - unsupported_features_bitfield: context.unsupported_features.into_bitfield(), + alpha_image: Mp4parseByteData::with_data(context.alpha_item_coded_data().unwrap_or(&[])), }) } @@ -1108,26 +1259,66 @@ pub unsafe extern "C" fn mp4parse_get_indice_table( // Initialize fields to default values to ensure all fields are always valid. *indices = Default::default(); - get_indice_table(&mut *parser, track_id, &mut *indices).into() + get_indice_table( + &(*parser).context, + &mut (*parser).sample_table, + track_id, + &mut *indices, + ) + .into() +} + +/// Fill the supplied `Mp4parseByteData` with index information from `track`. +/// +/// # Safety +/// +/// This function is unsafe because it dereferences both the parser and +/// indices raw pointers passed to it. Callers should ensure the parser +/// points to a valid `Mp4parseAvifParser` and indices points to a valid +/// `Mp4parseByteData`. +#[no_mangle] +pub unsafe extern "C" fn mp4parse_avif_get_indice_table( + parser: *mut Mp4parseAvifParser, + track_id: u32, + indices: *mut Mp4parseByteData, +) -> Mp4parseStatus { + if parser.is_null() { + return Mp4parseStatus::BadArg; + } + + if indices.is_null() { + return Mp4parseStatus::BadArg; + } + + // Initialize fields to default values to ensure all fields are always valid. + *indices = Default::default(); + + if let Some(sequence) = &(*parser).context.sequence { + return get_indice_table( + sequence, + &mut (*parser).sample_table, + track_id, + &mut *indices, + ) + .into(); + } + + Mp4parseStatus::BadArg } fn get_indice_table( - parser: &mut Mp4parseParser, + context: &MediaContext, + sample_table_cache: &mut TryHashMap>, track_id: u32, indices: &mut Mp4parseByteData, ) -> Result<(), Mp4parseStatus> { - let Mp4parseParser { - context, - sample_table: index_table, - .. - } = parser; let tracks = &context.tracks; let track = match tracks.iter().find(|track| track.track_id == Some(track_id)) { Some(t) => t, _ => return Err(Mp4parseStatus::Invalid), }; - if let Some(v) = index_table.get(&track_id) { + if let Some(v) = sample_table_cache.get(&track_id) { indices.set_indices(v); return Ok(()); } @@ -1159,7 +1350,7 @@ fn get_indice_table( if let Some(v) = create_sample_table(track, offset_time) { indices.set_indices(&v); - index_table.insert(track_id, v)?; + sample_table_cache.insert(track_id, v)?; return Ok(()); }