//! C API for mp4parse module. //! //! Parses ISO Base Media Format aka video/mp4 streams. //! //! # Examples //! //! ```rust //! use std::io::Read; //! //! extern fn buf_read(buf: *mut u8, size: usize, userdata: *mut std::os::raw::c_void) -> isize { //! let mut input: &mut std::fs::File = unsafe { &mut *(userdata as *mut _) }; //! let mut buf = unsafe { std::slice::from_raw_parts_mut(buf, size) }; //! match input.read(&mut buf) { //! Ok(n) => n as isize, //! Err(_) => -1, //! } //! } //! let capi_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap(); //! let mut file = std::fs::File::open(capi_dir + "/../mp4parse/tests/minimal.mp4").unwrap(); //! let io = mp4parse_capi::Mp4parseIo { //! read: Some(buf_read), //! userdata: &mut file as *mut _ as *mut std::os::raw::c_void //! }; //! let mut parser = std::ptr::null_mut(); //! unsafe { //! let rv = mp4parse_capi::mp4parse_new(&io, &mut parser); //! assert_eq!(rv, mp4parse_capi::Mp4parseStatus::Ok); //! assert!(!parser.is_null()); //! mp4parse_capi::mp4parse_free(parser); //! } //! ```
// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/.
use byteorder::WriteBytesExt; use mp4parse::unstable::rational_scale; use std::convert::TryFrom; use std::convert::TryInto;
use std::io::Read;
// Symbols we need from our rust api. use mp4parse::serialize_opus_header; use mp4parse::unstable::{create_sample_table, CheckedInteger, Indice}; use mp4parse::AV1ConfigBox; use mp4parse::AudioCodecSpecific; use mp4parse::AvifContext; use mp4parse::CodecType; use mp4parse::MediaContext; // Re-exported so consumers don't have to depend on mp4parse as well pubuse mp4parse::ParseStrictness; use mp4parse::SampleEntry; pubuse mp4parse::Status as Mp4parseStatus; use mp4parse::Track; use mp4parse::TrackType; use mp4parse::TryBox; use mp4parse::TryHashMap; use mp4parse::TryVec; use mp4parse::VideoCodecSpecific;
// To ensure we don't use stdlib allocating types by accident #[allow(dead_code)] struct Vec; #[allow(dead_code)] structBox; #[allow(dead_code)] struct HashMap; #[allow(dead_code)] struct String;
#[repr(C)] #[derive(PartialEq, Eq, Debug, Default)] pubenum Mp4ParseEncryptionSchemeType { #[default]
None,
Cenc,
Cbc1,
Cens,
Cbcs, // Schemes also have a version component. At the time of writing, this does // not impact handling, so we do not expose it. Note that this may need to // be exposed in future, should the spec change.
}
#[repr(C)] #[derive(Default, Debug)] pubstruct Mp4parseSinfInfo { pub original_format: OptionalFourCc, pub scheme_type: Mp4ParseEncryptionSchemeType, pub is_encrypted: u8, pub iv_size: u8, pub kid: Mp4parseByteData, // Members for pattern encryption schemes, may be 0 (u8) or empty // (Mp4parseByteData) if pattern encryption is not in use pub crypt_byte_block: u8, pub skip_byte_block: u8, pub constant_iv: Mp4parseByteData, // End pattern encryption scheme members
}
#[repr(C)] #[derive(Default, Debug)] pubstruct Mp4parseFragmentInfo { pub fragment_duration: u64, // in ticks pub time_scale: u64, // TODO: // info in trex box.
}
#[derive(Default)] pubstruct Mp4parseParser {
context: MediaContext,
opus_header: TryHashMap<u32, TryVec<u8>>,
pssh_data: TryVec<u8>,
sample_table: TryHashMap<u32, TryVec<Indice>>, // Store a mapping from track index (not id) to associated sample // descriptions. Because each track has a variable number of sample // descriptions, and because we need the data to live long enough to be // copied out by callers, we store these on the parser struct.
audio_track_sample_descriptions: TryHashMap<u32, TryVec<Mp4parseTrackAudioSampleInfo>>,
video_track_sample_descriptions: TryHashMap<u32, TryVec<Mp4parseTrackVideoSampleInfo>>,
}
#[repr(C)] #[derive(Debug)] pubstruct 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, pub icc_colour_information: Mp4parseByteData, pub image_rotation: mp4parse::ImageRotation, pub image_mirror: *const mp4parse::ImageMirror, pub pixel_aspect_ratio: *const mp4parse::PixelAspectRatio,
/// 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, /// Indicates whether the EditListBox requests that the image be looped. pub loop_mode: Mp4parseAvifLoopMode, /// Number of times to loop the animation during playback. /// /// The duration of the animation specified in `elst` must be looped to fill the /// duration of the color track. If the resulting loop count is not an integer, /// then it will be ceiled to play past and fill the entire track's duration. pub loop_count: u64, /// 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)] pubstruct 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 /// share the same pattern of construction. This allows unification of /// argument validation from C and minimizes the surface of unsafe code. trait ContextParser where Self: Sized,
{ type Context;
impl Read for Mp4parseIo { fn read(&mutself, buf: &mut [u8]) -> std::io::Result<usize> { if buf.len() > isize::max_value() as usize { return Err(std::io::Error::new(
std::io::ErrorKind::Other, "buf length overflow in Mp4parseIo Read impl",
));
} let rv = self.read.unwrap()(buf.as_mut_ptr(), buf.len(), self.userdata); if rv >= 0 {
Ok(rv as usize)
} else {
Err(std::io::Error::new(
std::io::ErrorKind::Other, "I/O error in Mp4parseIo Read impl",
))
}
}
}
// C API wrapper functions.
/// Allocate an `Mp4parseParser*` to read from the supplied `Mp4parseIo` and /// parse the content from the `Mp4parseIo` argument until EOF or error. /// /// # Safety /// /// This function is unsafe because it dereferences the `io` and `parser_out` /// pointers given to it. The caller should ensure that the `Mp4ParseIo` /// struct passed in is a valid pointer. The caller should also ensure the /// members of io are valid: the `read` function should be sanely implemented, /// and the `userdata` pointer should be valid. The `parser_out` should be a /// valid pointer to a location containing a null pointer. Upon successful /// return (`Mp4parseStatus::Ok`), that location will contain the address of /// an `Mp4parseParser` allocated by this function. /// /// To avoid leaking memory, any successful return of this function must be /// paired with a call to `mp4parse_free`. In the event of error, no memory /// will be allocated and `mp4parse_free` must *not* be called. #[no_mangle] pubunsafeextern"C"fn mp4parse_new(
io: *const Mp4parseIo,
parser_out: *mut *mut Mp4parseParser,
) -> Mp4parseStatus {
mp4parse_new_common(io, ParseStrictness::Normal, parser_out)
}
/// Allocate an `Mp4parseAvifParser*` to read from the supplied `Mp4parseIo`. /// /// See mp4parse_new; this function is identical except that it allocates an /// `Mp4parseAvifParser`, which (when successful) must be paired with a call /// to mp4parse_avif_free. /// /// # Safety /// /// Same as mp4parse_new. #[no_mangle] pubunsafeextern"C"fn mp4parse_avif_new(
io: *const Mp4parseIo,
strictness: ParseStrictness,
parser_out: *mut *mut Mp4parseAvifParser,
) -> Mp4parseStatus {
mp4parse_new_common(io, strictness, parser_out)
}
/// Free an `Mp4parseParser*` allocated by `mp4parse_new()`. /// /// # Safety /// /// This function is unsafe because it creates a box from a raw pointer. /// Callers should ensure that the parser pointer points to a valid /// `Mp4parseParser` created by `mp4parse_new`. #[no_mangle] pubunsafeextern"C"fn mp4parse_free(parser: *mut Mp4parseParser) {
assert!(!parser.is_null()); let _ = TryBox::from_raw(parser);
}
/// Free an `Mp4parseAvifParser*` allocated by `mp4parse_avif_new()`. /// /// # Safety /// /// This function is unsafe because it creates a box from a raw pointer. /// Callers should ensure that the parser pointer points to a valid /// `Mp4parseAvifParser` created by `mp4parse_avif_new`. #[no_mangle] pubunsafeextern"C"fn mp4parse_avif_free(parser: *mut Mp4parseAvifParser) {
assert!(!parser.is_null()); let _ = TryBox::from_raw(parser);
}
/// Return the number of tracks parsed by previous `mp4parse_read()` call. /// /// # Safety /// /// This function is unsafe because it dereferences both the parser and count /// raw pointers passed into it. Callers should ensure the parser pointer /// points to a valid `Mp4parseParser`, and that the count pointer points an /// appropriate memory location to have a `u32` written to. #[no_mangle] pubunsafeextern"C"fn mp4parse_get_track_count(
parser: *const Mp4parseParser,
count: *mut u32,
) -> Mp4parseStatus { // Validate arguments from C. if parser.is_null() || count.is_null() { return Mp4parseStatus::BadArg;
} let context = (*parser).context();
// Make sure the track count fits in a u32. if context.tracks.len() > u32::max_value() as usize { return Mp4parseStatus::Invalid;
}
*count = context.tracks.len() as u32;
Mp4parseStatus::Ok
}
/// Fill the supplied `Mp4parseTrackInfo` with metadata for `track`. /// /// # Safety /// /// This function is unsafe because it dereferences the the parser and info raw /// pointers passed to it. Callers should ensure the parser pointer points to a /// valid `Mp4parseParser` and that the info pointer points to a valid /// `Mp4parseTrackInfo`. #[no_mangle] pubunsafeextern"C"fn mp4parse_get_track_info(
parser: *mut Mp4parseParser,
track_index: u32,
info: *mut Mp4parseTrackInfo,
) -> Mp4parseStatus { if parser.is_null() || info.is_null() { return Mp4parseStatus::BadArg;
}
// Initialize fields to default values to ensure all fields are always valid.
*info = Default::default();
let context = (*parser).context_mut(); let track_index: usize = track_index as usize; let info: &mut Mp4parseTrackInfo = &mut *info;
if track_index >= context.tracks.len() { return Mp4parseStatus::BadArg;
}
// Empty duration is in the context's timescale, convert it and return it in the track's // timescale let empty_duration: CheckedInteger<u64> = match track.empty_duration.map_or(Some(0), |empty_duration| {
rational_scale(empty_duration.0, context_timescale.0, timescale.0)
}) {
Some(time) => mp4parse::unstable::CheckedInteger(time),
None => return Mp4parseStatus::Invalid,
};
/// Fill the supplied `Mp4parseTrackAudioInfo` with metadata for `track`. /// /// # Safety /// /// This function is unsafe because it dereferences the the parser and info raw /// pointers passed to it. Callers should ensure the parser pointer points to a /// valid `Mp4parseParser` and that the info pointer points to a valid /// `Mp4parseTrackAudioInfo`. #[no_mangle] pubunsafeextern"C"fn mp4parse_get_track_audio_info(
parser: *mut Mp4parseParser,
track_index: u32,
info: *mut Mp4parseTrackAudioInfo,
) -> Mp4parseStatus { if parser.is_null() || info.is_null() { return Mp4parseStatus::BadArg;
}
// Initialize fields to default values to ensure all fields are always valid.
*info = Default::default();
iflet Some(p) = audio
.protection_info
.iter()
.find(|sinf| sinf.tenc.is_some())
{
sample_info.protected_data.original_format =
OptionalFourCc::Some(p.original_format.value);
sample_info.protected_data.scheme_type = match p.scheme_type {
Some(ref scheme_type_box) => { match scheme_type_box.scheme_type.value.as_ref() {
b"cenc" => Mp4ParseEncryptionSchemeType::Cenc,
b"cbcs" => Mp4ParseEncryptionSchemeType::Cbcs, // We don't support other schemes, and shouldn't reach // this case. Try to gracefully handle by treating as // no encryption case.
_ => Mp4ParseEncryptionSchemeType::None,
}
}
None => Mp4ParseEncryptionSchemeType::None,
}; iflet Some(ref tenc) = p.tenc {
sample_info.protected_data.is_encrypted = tenc.is_encrypted;
sample_info.protected_data.iv_size = tenc.iv_size;
sample_info.protected_data.kid.set_data(&(tenc.kid));
sample_info.protected_data.crypt_byte_block =
tenc.crypt_byte_block_count.unwrap_or(0);
sample_info.protected_data.skip_byte_block =
tenc.skip_byte_block_count.unwrap_or(0); iflet Some(ref iv_vec) = tenc.constant_iv { if iv_vec.len() > std::u32::MAX as usize { return Err(Mp4parseStatus::Invalid);
}
sample_info.protected_data.constant_iv.set_data(iv_vec);
};
}
}
audio_sample_infos.push(sample_info)?;
}
parser
.audio_track_sample_descriptions
.insert(track_index, audio_sample_infos)?; match parser.audio_track_sample_descriptions.get(&track_index) {
Some(sample_info) => { if sample_info.len() > std::u32::MAX as usize { // Should never happen due to upper limits on number of sample // descriptions a track can have, but lets be safe. return Err(Mp4parseStatus::Invalid);
}
info.sample_info_count = sample_info.len() as u32;
info.sample_info = sample_info.as_ptr();
}
None => return Err(Mp4parseStatus::Invalid), // Shouldn't happen, we just inserted the info!
}
Ok(())
}
/// Fill the supplied `Mp4parseTrackVideoInfo` with metadata for `track`. /// /// # Safety /// /// This function is unsafe because it dereferences the the parser and info raw /// pointers passed to it. Callers should ensure the parser pointer points to a /// valid `Mp4parseParser` and that the info pointer points to a valid /// `Mp4parseTrackVideoInfo`. #[no_mangle] pubunsafeextern"C"fn mp4parse_get_track_video_info(
parser: *mut Mp4parseParser,
track_index: u32,
info: *mut Mp4parseTrackVideoInfo,
) -> Mp4parseStatus { if parser.is_null() || info.is_null() { return Mp4parseStatus::BadArg;
}
// Initialize fields to default values to ensure all fields are always valid.
*info = Default::default();
iflet Some(p) = video
.protection_info
.iter()
.find(|sinf| sinf.tenc.is_some())
{
sample_info.protected_data.original_format =
OptionalFourCc::Some(p.original_format.value);
sample_info.protected_data.scheme_type = match p.scheme_type {
Some(ref scheme_type_box) => { match scheme_type_box.scheme_type.value.as_ref() {
b"cenc" => Mp4ParseEncryptionSchemeType::Cenc,
b"cbcs" => Mp4ParseEncryptionSchemeType::Cbcs, // We don't support other schemes, and shouldn't reach // this case. Try to gracefully handle by treating as // no encryption case.
_ => Mp4ParseEncryptionSchemeType::None,
}
}
None => Mp4ParseEncryptionSchemeType::None,
}; iflet Some(ref tenc) = p.tenc {
sample_info.protected_data.is_encrypted = tenc.is_encrypted;
sample_info.protected_data.iv_size = tenc.iv_size;
sample_info.protected_data.kid.set_data(&(tenc.kid));
sample_info.protected_data.crypt_byte_block =
tenc.crypt_byte_block_count.unwrap_or(0);
sample_info.protected_data.skip_byte_block =
tenc.skip_byte_block_count.unwrap_or(0); iflet Some(ref iv_vec) = tenc.constant_iv { if iv_vec.len() > std::u32::MAX as usize { return Err(Mp4parseStatus::Invalid);
}
sample_info.protected_data.constant_iv.set_data(iv_vec);
};
}
}
video_sample_infos.push(sample_info)?;
}
parser
.video_track_sample_descriptions
.insert(track_index, video_sample_infos)?; match parser.video_track_sample_descriptions.get(&track_index) {
Some(sample_info) => { if sample_info.len() > std::u32::MAX as usize { // Should never happen due to upper limits on number of sample // descriptions a track can have, but lets be safe. return Err(Mp4parseStatus::Invalid);
}
info.sample_info_count = sample_info.len() as u32;
info.sample_info = sample_info.as_ptr();
}
None => return Err(Mp4parseStatus::Invalid), // Shouldn't happen, we just inserted the info!
}
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] pubunsafeextern"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;
}
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(&[]))?);
iflet 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() { returnfalse;
} match &track.stsc {
Some(stsc) => { if stsc.samples.is_empty() { returnfalse;
} if !pred(track) { returnfalse;
}
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,
});
/// Return a pointer to the primary item parsed by previous `mp4parse_avif_new()` call. /// /// # Safety /// /// This function is unsafe because it dereferences both the parser and /// avif_image raw pointers passed into it. Callers should ensure the parser /// pointer points to a valid `Mp4parseAvifParser`, and that the avif_image /// pointer points to a valid `Mp4parseAvifImage`. If there was not a previous /// successful call to `mp4parse_avif_read()`, no guarantees are made as to /// the state of `avif_image`. If `avif_image.alpha_image.coded_data` is set to /// a positive `length` and non-null `data`, then the `avif_image` contains a /// valid alpha channel data. Otherwise, the image is opaque. #[no_mangle] pubunsafeextern"C"fn mp4parse_avif_get_image(
parser: *const Mp4parseAvifParser,
avif_image: *mut Mp4parseAvifImage,
) -> Mp4parseStatus { if parser.is_null() || avif_image.is_null() { return Mp4parseStatus::BadArg;
}
/// Fill the supplied `Mp4parseByteData` with index information from `track`. /// /// # Safety /// /// This function is unsafe because it dereferences the the parser and indices /// raw pointers passed to it. Callers should ensure the parser pointer points /// to a valid `Mp4parseParser` and that the indices pointer points to a valid /// `Mp4parseByteData`. #[no_mangle] pubunsafeextern"C"fn mp4parse_get_indice_table(
parser: *mut Mp4parseParser,
track_id: u32,
indices: *mut Mp4parseByteData,
) -> Mp4parseStatus { if parser.is_null() { return Mp4parseStatus::BadArg;
}
// Initialize fields to default values to ensure all fields are always valid.
*indices = Default::default();
/// 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] pubunsafeextern"C"fn mp4parse_avif_get_indice_table(
parser: *mut Mp4parseAvifParser,
track_id: u32,
indices: *mut Mp4parseByteData,
timescale: *mut u64,
) -> Mp4parseStatus { if parser.is_null() { return Mp4parseStatus::BadArg;
}
if indices.is_null() { return Mp4parseStatus::BadArg;
}
if timescale.is_null() { return Mp4parseStatus::BadArg;
}
// Initialize fields to default values to ensure all fields are always valid.
*indices = Default::default();
iflet Some(sequence) = &(*parser).context.sequence { // Use the top level timescale, and the track timescale if present. letmut found_timescale = false; iflet Some(context_timescale) = sequence.timescale {
*timescale = context_timescale.0;
found_timescale = true;
} let maybe_track_timescale = match sequence
.tracks
.iter()
.find(|track| track.track_id == Some(track_id))
{
Some(track) => track.timescale,
_ => None,
}; iflet Some(track_timescale) = maybe_track_timescale {
found_timescale = true;
*timescale = track_timescale.0;
} if !found_timescale { return Mp4parseStatus::Invalid;
} return get_indice_table(
sequence,
&mut (*parser).sample_table,
track_id,
&mut *indices,
)
.into();
}
let media_time = match &track.media_time {
&Some(t) => i64::try_from(t.0).ok().map(Into::into),
_ => None,
};
let empty_duration: Option<CheckedInteger<_>> = match &track.empty_duration {
&Some(e) => i64::try_from(e.0).ok().map(Into::into),
_ => None,
};
// Find the track start offset time from 'elst'. // 'media_time' maps start time onward, 'empty_duration' adds time offset // before first frame is displayed. let offset_time = match (empty_duration, media_time) {
(Some(e), Some(m)) => (e - m).ok_or(Err(Mp4parseStatus::Invalid))?,
(Some(e), None) => e,
(None, Some(m)) => m,
_ => 0.into(),
};
/// Fill the supplied `Mp4parseFragmentInfo` with metadata from fragmented file. /// /// # Safety /// /// This function is unsafe because it dereferences the the parser and /// info raw pointers passed to it. Callers should ensure the parser /// pointer points to a valid `Mp4parseParser` and that the info pointer points /// to a valid `Mp4parseFragmentInfo`.
/// Determine if an mp4 file is fragmented. A fragmented file needs mvex table /// and contains no data in stts, stsc, and stco boxes. /// /// # Safety /// /// This function is unsafe because it dereferences the the parser and /// fragmented raw pointers passed to it. Callers should ensure the parser /// pointer points to a valid `Mp4parseParser` and that the fragmented pointer /// points to an appropriate memory location to have a `u8` written to. #[no_mangle] pubunsafeextern"C"fn mp4parse_is_fragmented(
parser: *mut Mp4parseParser,
track_id: u32,
fragmented: *mut u8,
) -> Mp4parseStatus { if parser.is_null() { return Mp4parseStatus::BadArg;
}
let context = (*parser).context_mut(); let tracks = &context.tracks;
(*fragmented) = falseas u8;
if context.mvex.is_none() { return Mp4parseStatus::Ok;
}
/// Get 'pssh' system id and 'pssh' box content for eme playback. /// /// The data format of the `info` struct passed to gecko is: /// /// - system id (16 byte uuid) /// - pssh box size (32-bit native endian) /// - pssh box content (including header) /// /// # Safety /// /// This function is unsafe because it dereferences the the parser and /// info raw pointers passed to it. Callers should ensure the parser /// pointer points to a valid `Mp4parseParser` and that the fragmented pointer /// points to a valid `Mp4parsePsshInfo`. #[no_mangle] pubunsafeextern"C"fn mp4parse_get_pssh_info(
parser: *mut Mp4parseParser,
info: *mut Mp4parsePsshInfo,
) -> Mp4parseStatus { if parser.is_null() || info.is_null() { return Mp4parseStatus::BadArg;
}
// Initialize fields to default values to ensure all fields are always valid.
*info = Default::default();
// Passing a null Mp4parseIo is an error. letmut parser = std::ptr::null_mut(); let rv = mp4parse_new(std::ptr::null(), &mut parser);
assert_eq!(rv, Mp4parseStatus::BadArg);
assert!(parser.is_null());
let null_mut: *mut std::os::raw::c_void = std::ptr::null_mut();
// Passing an Mp4parseIo with null members is an error. let io = Mp4parseIo {
read: None,
userdata: null_mut,
}; letmut parser = std::ptr::null_mut(); let rv = mp4parse_new(&io, &mut parser);
assert_eq!(rv, Mp4parseStatus::BadArg);
assert!(parser.is_null());
letmut dummy_value = 42; let io = Mp4parseIo {
read: None,
userdata: &mut dummy_value as *mut _ as *mut std::os::raw::c_void,
}; letmut parser = std::ptr::null_mut(); let rv = mp4parse_new(&io, &mut parser);
assert_eq!(rv, Mp4parseStatus::BadArg);
assert!(parser.is_null());
// The file has a video track, but the track has a timescale of 0, so. letmut track_info = Mp4parseTrackInfo::default();
rv = mp4parse_get_track_info(parser, 0, &mut track_info);
assert_eq!(rv, Mp4parseStatus::Invalid);
};
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.27 Sekunden
(vorverarbeitet am 2026-06-18)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.