Skip to content
2 changes: 2 additions & 0 deletions mp4parse/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3815,8 +3815,10 @@ fn read_colr<T: Read>(
/// Rotation in the positive (that is, anticlockwise) direction
/// Visualized in terms of starting with (⥠) UPWARDS HARPOON WITH BARB LEFT FROM BAR
/// similar to a DIGIT ONE (1)
#[derive(Default)]
pub enum ImageRotation {
/// ⥠ UPWARDS HARPOON WITH BARB LEFT FROM BAR
#[default]
D0,
/// ⥞ LEFTWARDS HARPOON WITH BARB DOWN FROM BAR
D90,
Expand Down
41 changes: 34 additions & 7 deletions mp4parse_capi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,11 @@ pub struct Mp4parseFragmentInfo {

/// Parser state for MP4 files, exposed to C callers via raw pointer.
///
/// # Thread safety
///
/// A parser instance must not be accessed from multiple threads
/// concurrently. The caller is responsible for serializing all access.
///
/// # Pointer stability
///
/// Several C API functions return raw pointers into data cached on this
Expand Down Expand Up @@ -339,7 +344,7 @@ pub enum Mp4parseAvifLoopMode {
}

#[repr(C)]
#[derive(Debug)]
#[derive(Debug, Default)]
pub struct Mp4parseAvifInfo {
pub premultiplied_alpha: bool,
pub major_brand: [u8; 4],
Expand Down Expand Up @@ -381,7 +386,7 @@ pub struct Mp4parseAvifInfo {
}

#[repr(C)]
#[derive(Debug)]
#[derive(Debug, Default)]
pub struct Mp4parseAvifImage {
pub primary_image: Mp4parseByteData,
/// If no alpha item exists, members' `.length` will be 0 and `.data` will be null
Expand Down Expand Up @@ -429,6 +434,12 @@ impl ContextParser for Mp4parseParser {
}
}

/// Parser state for AVIF files, exposed to C callers via raw pointer.
///
/// # Thread safety
///
/// A parser instance must not be accessed from multiple threads
/// concurrently. The caller is responsible for serializing all access.
#[derive(Default)]
pub struct Mp4parseAvifParser {
context: AvifContext,
Expand Down Expand Up @@ -479,6 +490,7 @@ impl Read for Mp4parseIo {
}
let rv = self.read.unwrap()(buf.as_mut_ptr(), buf.len(), self.userdata);
if rv >= 0 {
assert!(rv as usize <= buf.len(), "read callback returned more bytes than buffer size");
Ok(rv as usize)
} else {
Err(std::io::Error::other("I/O error in Mp4parseIo Read impl"))
Expand Down Expand Up @@ -660,7 +672,10 @@ pub unsafe extern "C" fn mp4parse_get_track_info(
let track = &context.tracks[track_index];

if let (Some(timescale), Some(context_timescale)) = (track.timescale, context.timescale) {
info.time_scale = timescale.0 as u32;
info.time_scale = match timescale.0.try_into() {
Ok(v) => v,
Err(_) => return Mp4parseStatus::Invalid,
};
let media_time: CheckedInteger<u64> = track
.media_time
.map_or(0.into(), |media_time| media_time.0.into());
Expand Down Expand Up @@ -811,8 +826,12 @@ fn get_track_audio_info(
}
}
};
sample_info.channels = audio.channelcount as u16;
sample_info.channels =
u16::try_from(audio.channelcount).map_err(|_| Mp4parseStatus::Invalid)?;
sample_info.bit_depth = audio.samplesize;
if audio.samplerate < 0.0 || audio.samplerate > u32::MAX as f64 {
return Err(Mp4parseStatus::Invalid);
}
sample_info.sample_rate = audio.samplerate as u32;
// sample_info.profile is handled below on a per case basis

Expand Down Expand Up @@ -1146,6 +1165,9 @@ pub unsafe extern "C" fn mp4parse_avif_get_info(
return Mp4parseStatus::BadArg;
}

// Initialize fields to default values to ensure all fields are always valid.
*avif_info = Default::default();

if let Ok(info) = mp4parse_avif_get_info_safe((*parser).context()) {
*avif_info = info;
Mp4parseStatus::Ok
Expand Down Expand Up @@ -1328,6 +1350,9 @@ pub unsafe extern "C" fn mp4parse_avif_get_image(
return Mp4parseStatus::BadArg;
}

// Initialize fields to default values to ensure all fields are always valid.
*avif_image = Default::default();

if let Ok(image) = mp4parse_avif_get_image_safe(&*parser) {
*avif_image = image;
Mp4parseStatus::Ok
Expand Down Expand Up @@ -1362,7 +1387,7 @@ pub unsafe extern "C" fn mp4parse_get_indice_table(
track_id: u32,
indices: *mut Mp4parseByteData,
) -> Mp4parseStatus {
if parser.is_null() {
if parser.is_null() || indices.is_null() {
return Mp4parseStatus::BadArg;
}

Expand Down Expand Up @@ -1480,8 +1505,10 @@ fn get_indice_table(
};

if let Some(v) = create_sample_table(track, offset_time) {
indices.set_indices(&v);
sample_table_cache.insert(track_id, v)?;
if let Some(cached) = sample_table_cache.get(&track_id) {
indices.set_indices(cached);
}
return Ok(());
}

Expand Down Expand Up @@ -1541,7 +1568,7 @@ pub unsafe extern "C" fn mp4parse_is_fragmented(
track_id: u32,
fragmented: *mut u8,
) -> Mp4parseStatus {
if parser.is_null() {
if parser.is_null() || fragmented.is_null() {
return Mp4parseStatus::BadArg;
}

Expand Down