diff options
Diffstat (limited to 'media/libstagefright/binding/mp4parse_capi/src/lib.rs')
-rw-r--r-- | media/libstagefright/binding/mp4parse_capi/src/lib.rs | 870 |
1 files changed, 0 insertions, 870 deletions
diff --git a/media/libstagefright/binding/mp4parse_capi/src/lib.rs b/media/libstagefright/binding/mp4parse_capi/src/lib.rs deleted file mode 100644 index f52d8b169..000000000 --- a/media/libstagefright/binding/mp4parse_capi/src/lib.rs +++ /dev/null @@ -1,870 +0,0 @@ -//! C API for mp4parse module. -//! -//! Parses ISO Base Media Format aka video/mp4 streams. -//! -//! # Examples -//! -//! ```rust -//! extern crate mp4parse_capi; -//! 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 mut file = std::fs::File::open("../mp4parse/tests/minimal.mp4").unwrap(); -//! let io = mp4parse_capi::mp4parse_io { -//! read: buf_read, -//! userdata: &mut file as *mut _ as *mut std::os::raw::c_void -//! }; -//! unsafe { -//! let parser = mp4parse_capi::mp4parse_new(&io); -//! let rv = mp4parse_capi::mp4parse_read(parser); -//! assert_eq!(rv, mp4parse_capi::mp4parse_error::MP4PARSE_OK); -//! 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/. - -extern crate mp4parse; - -use std::io::Read; -use std::collections::HashMap; - -// Symbols we need from our rust api. -use mp4parse::MediaContext; -use mp4parse::TrackType; -use mp4parse::read_mp4; -use mp4parse::Error; -use mp4parse::SampleEntry; -use mp4parse::AudioCodecSpecific; -use mp4parse::VideoCodecSpecific; -use mp4parse::MediaTimeScale; -use mp4parse::MediaScaledTime; -use mp4parse::TrackTimeScale; -use mp4parse::TrackScaledTime; -use mp4parse::serialize_opus_header; -use mp4parse::CodecType; - -// rusty-cheddar's C enum generation doesn't namespace enum members by -// prefixing them, so we're forced to do it in our member names until -// https://github.com/Sean1708/rusty-cheddar/pull/35 is fixed. Importing -// the members into the module namespace avoids doubling up on the -// namespacing on the Rust side. -use mp4parse_error::*; -use mp4parse_track_type::*; - -#[repr(C)] -#[derive(PartialEq, Debug)] -pub enum mp4parse_error { - MP4PARSE_OK = 0, - MP4PARSE_ERROR_BADARG = 1, - MP4PARSE_ERROR_INVALID = 2, - MP4PARSE_ERROR_UNSUPPORTED = 3, - MP4PARSE_ERROR_EOF = 4, - MP4PARSE_ERROR_IO = 5, -} - -#[repr(C)] -#[derive(PartialEq, Debug)] -pub enum mp4parse_track_type { - MP4PARSE_TRACK_TYPE_VIDEO = 0, - MP4PARSE_TRACK_TYPE_AUDIO = 1, -} - -#[repr(C)] -#[derive(PartialEq, Debug)] -pub enum mp4parse_codec { - MP4PARSE_CODEC_UNKNOWN, - MP4PARSE_CODEC_AAC, - MP4PARSE_CODEC_FLAC, - MP4PARSE_CODEC_OPUS, - MP4PARSE_CODEC_AVC, - MP4PARSE_CODEC_VP9, - MP4PARSE_CODEC_MP3, -} - -#[repr(C)] -pub struct mp4parse_track_info { - pub track_type: mp4parse_track_type, - pub codec: mp4parse_codec, - pub track_id: u32, - pub duration: u64, - pub media_time: i64, // wants to be u64? understand how elst adjustment works - // TODO(kinetik): include crypto guff -} - -#[repr(C)] -pub struct mp4parse_codec_specific_config { - pub length: u32, - pub data: *const u8, -} - -impl Default for mp4parse_codec_specific_config { - fn default() -> Self { - mp4parse_codec_specific_config { - length: 0, - data: std::ptr::null_mut(), - } - } -} - -#[derive(Default)] -#[repr(C)] -pub struct mp4parse_track_audio_info { - pub channels: u16, - pub bit_depth: u16, - pub sample_rate: u32, - // TODO(kinetik): - // int32_t profile; - // int32_t extended_profile; // check types - codec_specific_config: mp4parse_codec_specific_config, -} - -#[repr(C)] -pub struct mp4parse_track_video_info { - pub display_width: u32, - pub display_height: u32, - pub image_width: u16, - pub image_height: u16, - // TODO(kinetik): - // extra_data - // codec_specific_config -} - -#[repr(C)] -pub struct mp4parse_fragment_info { - pub fragment_duration: u64, - // TODO: - // info in trex box. -} - -// Even though mp4parse_parser is opaque to C, rusty-cheddar won't let us -// use more than one member, so we introduce *another* wrapper. -struct Wrap { - context: MediaContext, - io: mp4parse_io, - poisoned: bool, - opus_header: HashMap<u32, Vec<u8>>, -} - -#[repr(C)] -#[allow(non_camel_case_types)] -pub struct mp4parse_parser(Wrap); - -impl mp4parse_parser { - fn context(&self) -> &MediaContext { - &self.0.context - } - - fn context_mut(&mut self) -> &mut MediaContext { - &mut self.0.context - } - - fn io_mut(&mut self) -> &mut mp4parse_io { - &mut self.0.io - } - - fn poisoned(&self) -> bool { - self.0.poisoned - } - - fn set_poisoned(&mut self, poisoned: bool) { - self.0.poisoned = poisoned; - } - - fn opus_header_mut(&mut self) -> &mut HashMap<u32, Vec<u8>> { - &mut self.0.opus_header - } -} - -#[repr(C)] -#[derive(Clone)] -pub struct mp4parse_io { - pub read: extern fn(buffer: *mut u8, size: usize, userdata: *mut std::os::raw::c_void) -> isize, - pub userdata: *mut std::os::raw::c_void, -} - -impl Read for mp4parse_io { - fn read(&mut self, 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 mp4parse_io Read impl")); - } - let rv = (self.read)(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 mp4parse_io Read impl")) - } - } -} - -// C API wrapper functions. - -/// Allocate an `mp4parse_parser*` to read from the supplied `mp4parse_io`. -#[no_mangle] -pub unsafe extern fn mp4parse_new(io: *const mp4parse_io) -> *mut mp4parse_parser { - if io.is_null() || (*io).userdata.is_null() { - return std::ptr::null_mut(); - } - // is_null() isn't available on a fn type because it can't be null (in - // Rust) by definition. But since this value is coming from the C API, - // it *could* be null. Ideally, we'd wrap it in an Option to represent - // reality, but this causes rusty-cheddar to emit the wrong type - // (https://github.com/Sean1708/rusty-cheddar/issues/30). - if ((*io).read as *mut std::os::raw::c_void).is_null() { - return std::ptr::null_mut(); - } - let parser = Box::new(mp4parse_parser(Wrap { - context: MediaContext::new(), - io: (*io).clone(), - poisoned: false, - opus_header: HashMap::new(), - })); - Box::into_raw(parser) -} - -/// Free an `mp4parse_parser*` allocated by `mp4parse_new()`. -#[no_mangle] -pub unsafe extern fn mp4parse_free(parser: *mut mp4parse_parser) { - assert!(!parser.is_null()); - let _ = Box::from_raw(parser); -} - -/// Run the `mp4parse_parser*` allocated by `mp4parse_new()` until EOF or error. -#[no_mangle] -pub unsafe extern fn mp4parse_read(parser: *mut mp4parse_parser) -> mp4parse_error { - // Validate arguments from C. - if parser.is_null() || (*parser).poisoned() { - return MP4PARSE_ERROR_BADARG; - } - - let mut context = (*parser).context_mut(); - let mut io = (*parser).io_mut(); - - let r = read_mp4(io, context); - match r { - Ok(_) => MP4PARSE_OK, - Err(Error::NoMoov) | Err(Error::InvalidData(_)) => { - // Block further calls. We've probable lost sync. - (*parser).set_poisoned(true); - MP4PARSE_ERROR_INVALID - } - Err(Error::Unsupported(_)) => MP4PARSE_ERROR_UNSUPPORTED, - Err(Error::UnexpectedEOF) => MP4PARSE_ERROR_EOF, - Err(Error::Io(_)) => { - // Block further calls after a read failure. - // Getting std::io::ErrorKind::UnexpectedEof is normal - // but our From trait implementation should have converted - // those to our Error::UnexpectedEOF variant. - (*parser).set_poisoned(true); - MP4PARSE_ERROR_IO - } - } -} - -/// Return the number of tracks parsed by previous `mp4parse_read()` call. -#[no_mangle] -pub unsafe extern fn mp4parse_get_track_count(parser: *const mp4parse_parser, count: *mut u32) -> mp4parse_error { - // Validate arguments from C. - if parser.is_null() || count.is_null() || (*parser).poisoned() { - return MP4PARSE_ERROR_BADARG; - } - let context = (*parser).context(); - - // Make sure the track count fits in a u32. - if context.tracks.len() > u32::max_value() as usize { - return MP4PARSE_ERROR_INVALID; - } - *count = context.tracks.len() as u32; - MP4PARSE_OK -} - -/// Calculate numerator * scale / denominator, if possible. -/// -/// Applying the associativity of integer arithmetic, we divide first -/// and add the remainder after multiplying each term separately -/// to preserve precision while leaving more headroom. That is, -/// (n * s) / d is split into floor(n / d) * s + (n % d) * s / d. -/// -/// Return None on overflow or if the denominator is zero. -fn rational_scale(numerator: u64, denominator: u64, scale: u64) -> Option<u64> { - if denominator == 0 { - return None; - } - let integer = numerator / denominator; - let remainder = numerator % denominator; - match integer.checked_mul(scale) { - Some(integer) => remainder.checked_mul(scale) - .and_then(|remainder| (remainder/denominator).checked_add(integer)), - None => None, - } -} - -fn media_time_to_us(time: MediaScaledTime, scale: MediaTimeScale) -> Option<u64> { - let microseconds_per_second = 1000000; - rational_scale(time.0, scale.0, microseconds_per_second) -} - -fn track_time_to_us(time: TrackScaledTime, scale: TrackTimeScale) -> Option<u64> { - assert!(time.1 == scale.1); - let microseconds_per_second = 1000000; - rational_scale(time.0, scale.0, microseconds_per_second) -} - -/// Fill the supplied `mp4parse_track_info` with metadata for `track`. -#[no_mangle] -pub unsafe extern fn mp4parse_get_track_info(parser: *mut mp4parse_parser, track_index: u32, info: *mut mp4parse_track_info) -> mp4parse_error { - if parser.is_null() || info.is_null() || (*parser).poisoned() { - return MP4PARSE_ERROR_BADARG; - } - - let context = (*parser).context_mut(); - let track_index: usize = track_index as usize; - let info: &mut mp4parse_track_info = &mut *info; - - if track_index >= context.tracks.len() { - return MP4PARSE_ERROR_BADARG; - } - - info.track_type = match context.tracks[track_index].track_type { - TrackType::Video => MP4PARSE_TRACK_TYPE_VIDEO, - TrackType::Audio => MP4PARSE_TRACK_TYPE_AUDIO, - TrackType::Unknown => return MP4PARSE_ERROR_UNSUPPORTED, - }; - - info.codec = match context.tracks[track_index].data { - Some(SampleEntry::Audio(ref audio)) => match audio.codec_specific { - AudioCodecSpecific::OpusSpecificBox(_) => - mp4parse_codec::MP4PARSE_CODEC_OPUS, - AudioCodecSpecific::FLACSpecificBox(_) => - mp4parse_codec::MP4PARSE_CODEC_FLAC, - AudioCodecSpecific::ES_Descriptor(ref esds) if esds.audio_codec == CodecType::AAC => - mp4parse_codec::MP4PARSE_CODEC_AAC, - AudioCodecSpecific::ES_Descriptor(ref esds) if esds.audio_codec == CodecType::MP3 => - mp4parse_codec::MP4PARSE_CODEC_MP3, - AudioCodecSpecific::ES_Descriptor(_) => - mp4parse_codec::MP4PARSE_CODEC_UNKNOWN, - }, - Some(SampleEntry::Video(ref video)) => match video.codec_specific { - VideoCodecSpecific::VPxConfig(_) => - mp4parse_codec::MP4PARSE_CODEC_VP9, - VideoCodecSpecific::AVCConfig(_) => - mp4parse_codec::MP4PARSE_CODEC_AVC, - }, - _ => mp4parse_codec::MP4PARSE_CODEC_UNKNOWN, - }; - - let track = &context.tracks[track_index]; - - if let (Some(track_timescale), - Some(context_timescale)) = (track.timescale, - context.timescale) { - let media_time = - match track.media_time.map_or(Some(0), |media_time| { - track_time_to_us(media_time, track_timescale) }) { - Some(time) => time as i64, - None => return MP4PARSE_ERROR_INVALID, - }; - let empty_duration = - match track.empty_duration.map_or(Some(0), |empty_duration| { - media_time_to_us(empty_duration, context_timescale) }) { - Some(time) => time as i64, - None => return MP4PARSE_ERROR_INVALID, - }; - info.media_time = media_time - empty_duration; - - if let Some(track_duration) = track.duration { - match track_time_to_us(track_duration, track_timescale) { - Some(duration) => info.duration = duration, - None => return MP4PARSE_ERROR_INVALID, - } - } else { - // Duration unknown; stagefright returns 0 for this. - info.duration = 0 - } - } else { - return MP4PARSE_ERROR_INVALID - } - - info.track_id = match track.track_id { - Some(track_id) => track_id, - None => return MP4PARSE_ERROR_INVALID, - }; - - MP4PARSE_OK -} - -/// Fill the supplied `mp4parse_track_audio_info` with metadata for `track`. -#[no_mangle] -pub unsafe extern fn mp4parse_get_track_audio_info(parser: *mut mp4parse_parser, track_index: u32, info: *mut mp4parse_track_audio_info) -> mp4parse_error { - if parser.is_null() || info.is_null() || (*parser).poisoned() { - return MP4PARSE_ERROR_BADARG; - } - - let context = (*parser).context_mut(); - - if track_index as usize >= context.tracks.len() { - return MP4PARSE_ERROR_BADARG; - } - - let track = &context.tracks[track_index as usize]; - - match track.track_type { - TrackType::Audio => {} - _ => return MP4PARSE_ERROR_INVALID, - }; - - let audio = match track.data { - Some(ref data) => data, - None => return MP4PARSE_ERROR_INVALID, - }; - - let audio = match *audio { - SampleEntry::Audio(ref x) => x, - _ => return MP4PARSE_ERROR_INVALID, - }; - - (*info).channels = audio.channelcount; - (*info).bit_depth = audio.samplesize; - (*info).sample_rate = audio.samplerate >> 16; // 16.16 fixed point - - match audio.codec_specific { - AudioCodecSpecific::ES_Descriptor(ref v) => { - if v.codec_specific_config.len() > std::u32::MAX as usize { - return MP4PARSE_ERROR_INVALID; - } - (*info).codec_specific_config.length = v.codec_specific_config.len() as u32; - (*info).codec_specific_config.data = v.codec_specific_config.as_ptr(); - if let Some(rate) = v.audio_sample_rate { - (*info).sample_rate = rate; - } - if let Some(channels) = v.audio_channel_count { - (*info).channels = channels; - } - } - AudioCodecSpecific::FLACSpecificBox(ref flac) => { - // Return the STREAMINFO metadata block in the codec_specific. - let streaminfo = &flac.blocks[0]; - if streaminfo.block_type != 0 || streaminfo.data.len() != 34 { - return MP4PARSE_ERROR_INVALID; - } - (*info).codec_specific_config.length = streaminfo.data.len() as u32; - (*info).codec_specific_config.data = streaminfo.data.as_ptr(); - } - AudioCodecSpecific::OpusSpecificBox(ref opus) => { - let mut v = Vec::new(); - match serialize_opus_header(opus, &mut v) { - Err(_) => { - return MP4PARSE_ERROR_INVALID; - } - Ok(_) => { - let header = (*parser).opus_header_mut(); - header.insert(track_index, v); - match header.get(&track_index) { - None => {} - Some(v) => { - if v.len() > std::u32::MAX as usize { - return MP4PARSE_ERROR_INVALID; - } - (*info).codec_specific_config.length = v.len() as u32; - (*info).codec_specific_config.data = v.as_ptr(); - } - } - } - } - } - } - - MP4PARSE_OK -} - -/// Fill the supplied `mp4parse_track_video_info` with metadata for `track`. -#[no_mangle] -pub unsafe extern fn mp4parse_get_track_video_info(parser: *mut mp4parse_parser, track_index: u32, info: *mut mp4parse_track_video_info) -> mp4parse_error { - if parser.is_null() || info.is_null() || (*parser).poisoned() { - return MP4PARSE_ERROR_BADARG; - } - - let context = (*parser).context_mut(); - - if track_index as usize >= context.tracks.len() { - return MP4PARSE_ERROR_BADARG; - } - - let track = &context.tracks[track_index as usize]; - - match track.track_type { - TrackType::Video => {} - _ => return MP4PARSE_ERROR_INVALID, - }; - - let video = match track.data { - Some(ref data) => data, - None => return MP4PARSE_ERROR_INVALID, - }; - - let video = match *video { - SampleEntry::Video(ref x) => x, - _ => return MP4PARSE_ERROR_INVALID, - }; - - if let Some(ref tkhd) = track.tkhd { - (*info).display_width = tkhd.width >> 16; // 16.16 fixed point - (*info).display_height = tkhd.height >> 16; // 16.16 fixed point - } else { - return MP4PARSE_ERROR_INVALID; - } - (*info).image_width = video.width; - (*info).image_height = video.height; - - MP4PARSE_OK -} - -#[no_mangle] -pub unsafe extern fn mp4parse_get_fragment_info(parser: *mut mp4parse_parser, info: *mut mp4parse_fragment_info) -> mp4parse_error { - if parser.is_null() || info.is_null() || (*parser).poisoned() { - return MP4PARSE_ERROR_BADARG; - } - - let context = (*parser).context(); - let info: &mut mp4parse_fragment_info = &mut *info; - - info.fragment_duration = 0; - - let duration = match context.mvex { - Some(ref mvex) => mvex.fragment_duration, - None => return MP4PARSE_ERROR_INVALID, - }; - - if let (Some(time), Some(scale)) = (duration, context.timescale) { - info.fragment_duration = match media_time_to_us(time, scale) { - Some(time_us) => time_us as u64, - None => return MP4PARSE_ERROR_INVALID, - } - } - - MP4PARSE_OK -} - -// A fragmented file needs mvex table and contains no data in stts, stsc, and stco boxes. -#[no_mangle] -pub unsafe extern fn mp4parse_is_fragmented(parser: *mut mp4parse_parser, track_id: u32, fragmented: *mut u8) -> mp4parse_error { - if parser.is_null() || (*parser).poisoned() { - return MP4PARSE_ERROR_BADARG; - } - - let context = (*parser).context_mut(); - let tracks = &context.tracks; - (*fragmented) = false as u8; - - if context.mvex.is_none() { - return MP4PARSE_OK; - } - - // check sample tables. - let mut iter = tracks.iter(); - match iter.find(|track| track.track_id == Some(track_id)) { - Some(track) if track.empty_sample_boxes.all_empty() => (*fragmented) = true as u8, - Some(_) => {}, - None => return MP4PARSE_ERROR_BADARG, - } - - MP4PARSE_OK -} - -#[cfg(test)] -extern fn panic_read(_: *mut u8, _: usize, _: *mut std::os::raw::c_void) -> isize { - panic!("panic_read shouldn't be called in these tests"); -} - -#[cfg(test)] -extern fn error_read(_: *mut u8, _: usize, _: *mut std::os::raw::c_void) -> isize { - -1 -} - -#[cfg(test)] -extern fn valid_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, - } -} - -#[test] -fn new_parser() { - let mut dummy_value: u32 = 42; - let io = mp4parse_io { - read: panic_read, - userdata: &mut dummy_value as *mut _ as *mut std::os::raw::c_void, - }; - unsafe { - let parser = mp4parse_new(&io); - assert!(!parser.is_null()); - mp4parse_free(parser); - } -} - -#[test] -#[should_panic(expected = "assertion failed")] -fn free_null_parser() { - unsafe { - mp4parse_free(std::ptr::null_mut()); - } -} - -#[test] -fn get_track_count_null_parser() { - unsafe { - let mut count: u32 = 0; - let rv = mp4parse_get_track_count(std::ptr::null(), std::ptr::null_mut()); - assert!(rv == MP4PARSE_ERROR_BADARG); - let rv = mp4parse_get_track_count(std::ptr::null(), &mut count); - assert!(rv == MP4PARSE_ERROR_BADARG); - } -} - -#[test] -fn arg_validation() { - unsafe { - // Passing a null mp4parse_io is an error. - let parser = mp4parse_new(std::ptr::null()); - assert!(parser.is_null()); - - let null_mut: *mut std::os::raw::c_void = std::ptr::null_mut(); - - // Passing an mp4parse_io with null members is an error. - let io = mp4parse_io { read: std::mem::transmute(null_mut), - userdata: null_mut }; - let parser = mp4parse_new(&io); - assert!(parser.is_null()); - - let io = mp4parse_io { read: panic_read, - userdata: null_mut }; - let parser = mp4parse_new(&io); - assert!(parser.is_null()); - - let mut dummy_value = 42; - let io = mp4parse_io { - read: std::mem::transmute(null_mut), - userdata: &mut dummy_value as *mut _ as *mut std::os::raw::c_void, - }; - let parser = mp4parse_new(&io); - assert!(parser.is_null()); - - // Passing a null mp4parse_parser is an error. - assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_read(std::ptr::null_mut())); - - let mut dummy_info = mp4parse_track_info { - track_type: MP4PARSE_TRACK_TYPE_VIDEO, - codec: mp4parse_codec::MP4PARSE_CODEC_UNKNOWN, - track_id: 0, - duration: 0, - media_time: 0, - }; - assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_info(std::ptr::null_mut(), 0, &mut dummy_info)); - - let mut dummy_video = mp4parse_track_video_info { - display_width: 0, - display_height: 0, - image_width: 0, - image_height: 0, - }; - assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_video_info(std::ptr::null_mut(), 0, &mut dummy_video)); - - let mut dummy_audio = Default::default(); - assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_audio_info(std::ptr::null_mut(), 0, &mut dummy_audio)); - } -} - -#[test] -fn arg_validation_with_parser() { - unsafe { - let mut dummy_value = 42; - let io = mp4parse_io { - read: error_read, - userdata: &mut dummy_value as *mut _ as *mut std::os::raw::c_void, - }; - let parser = mp4parse_new(&io); - assert!(!parser.is_null()); - - // Our mp4parse_io read should simply fail with an error. - assert_eq!(MP4PARSE_ERROR_IO, mp4parse_read(parser)); - - // The parser is now poisoned and unusable. - assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_read(parser)); - - // Null info pointers are an error. - assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_info(parser, 0, std::ptr::null_mut())); - assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_video_info(parser, 0, std::ptr::null_mut())); - assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_audio_info(parser, 0, std::ptr::null_mut())); - - let mut dummy_info = mp4parse_track_info { - track_type: MP4PARSE_TRACK_TYPE_VIDEO, - codec: mp4parse_codec::MP4PARSE_CODEC_UNKNOWN, - track_id: 0, - duration: 0, - media_time: 0, - }; - assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_info(parser, 0, &mut dummy_info)); - - let mut dummy_video = mp4parse_track_video_info { - display_width: 0, - display_height: 0, - image_width: 0, - image_height: 0, - }; - assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_video_info(parser, 0, &mut dummy_video)); - - let mut dummy_audio = Default::default(); - assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_audio_info(parser, 0, &mut dummy_audio)); - - mp4parse_free(parser); - } -} - -#[test] -fn get_track_count_poisoned_parser() { - unsafe { - let mut dummy_value = 42; - let io = mp4parse_io { - read: error_read, - userdata: &mut dummy_value as *mut _ as *mut std::os::raw::c_void, - }; - let parser = mp4parse_new(&io); - assert!(!parser.is_null()); - - // Our mp4parse_io read should simply fail with an error. - assert_eq!(MP4PARSE_ERROR_IO, mp4parse_read(parser)); - - let mut count: u32 = 0; - let rv = mp4parse_get_track_count(parser, &mut count); - assert!(rv == MP4PARSE_ERROR_BADARG); - } -} - -#[test] -fn arg_validation_with_data() { - unsafe { - let mut file = std::fs::File::open("../mp4parse/tests/minimal.mp4").unwrap(); - let io = mp4parse_io { read: valid_read, - userdata: &mut file as *mut _ as *mut std::os::raw::c_void }; - let parser = mp4parse_new(&io); - assert!(!parser.is_null()); - - assert_eq!(MP4PARSE_OK, mp4parse_read(parser)); - - let mut count: u32 = 0; - assert_eq!(MP4PARSE_OK, mp4parse_get_track_count(parser, &mut count)); - assert_eq!(2, count); - - let mut info = mp4parse_track_info { - track_type: MP4PARSE_TRACK_TYPE_VIDEO, - codec: mp4parse_codec::MP4PARSE_CODEC_UNKNOWN, - track_id: 0, - duration: 0, - media_time: 0, - }; - assert_eq!(MP4PARSE_OK, mp4parse_get_track_info(parser, 0, &mut info)); - assert_eq!(info.track_type, MP4PARSE_TRACK_TYPE_VIDEO); - assert_eq!(info.codec, mp4parse_codec::MP4PARSE_CODEC_AVC); - assert_eq!(info.track_id, 1); - assert_eq!(info.duration, 40000); - assert_eq!(info.media_time, 0); - - assert_eq!(MP4PARSE_OK, mp4parse_get_track_info(parser, 1, &mut info)); - assert_eq!(info.track_type, MP4PARSE_TRACK_TYPE_AUDIO); - assert_eq!(info.codec, mp4parse_codec::MP4PARSE_CODEC_AAC); - assert_eq!(info.track_id, 2); - assert_eq!(info.duration, 61333); - assert_eq!(info.media_time, 21333); - - let mut video = mp4parse_track_video_info { - display_width: 0, - display_height: 0, - image_width: 0, - image_height: 0, - }; - assert_eq!(MP4PARSE_OK, mp4parse_get_track_video_info(parser, 0, &mut video)); - assert_eq!(video.display_width, 320); - assert_eq!(video.display_height, 240); - assert_eq!(video.image_width, 320); - assert_eq!(video.image_height, 240); - - let mut audio = Default::default(); - assert_eq!(MP4PARSE_OK, mp4parse_get_track_audio_info(parser, 1, &mut audio)); - assert_eq!(audio.channels, 1); - assert_eq!(audio.bit_depth, 16); - assert_eq!(audio.sample_rate, 48000); - - // Test with an invalid track number. - let mut info = mp4parse_track_info { - track_type: MP4PARSE_TRACK_TYPE_VIDEO, - codec: mp4parse_codec::MP4PARSE_CODEC_UNKNOWN, - track_id: 0, - duration: 0, - media_time: 0, - }; - assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_info(parser, 3, &mut info)); - assert_eq!(info.track_type, MP4PARSE_TRACK_TYPE_VIDEO); - assert_eq!(info.codec, mp4parse_codec::MP4PARSE_CODEC_UNKNOWN); - assert_eq!(info.track_id, 0); - assert_eq!(info.duration, 0); - assert_eq!(info.media_time, 0); - - let mut video = mp4parse_track_video_info { display_width: 0, - display_height: 0, - image_width: 0, - image_height: 0 }; - assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_video_info(parser, 3, &mut video)); - assert_eq!(video.display_width, 0); - assert_eq!(video.display_height, 0); - assert_eq!(video.image_width, 0); - assert_eq!(video.image_height, 0); - - let mut audio = Default::default(); - assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_audio_info(parser, 3, &mut audio)); - assert_eq!(audio.channels, 0); - assert_eq!(audio.bit_depth, 0); - assert_eq!(audio.sample_rate, 0); - - mp4parse_free(parser); - } -} - -#[test] -fn rational_scale_overflow() { - assert_eq!(rational_scale(17, 3, 1000), Some(5666)); - let large = 0x4000_0000_0000_0000; - assert_eq!(rational_scale(large, 2, 2), Some(large)); - assert_eq!(rational_scale(large, 4, 4), Some(large)); - assert_eq!(rational_scale(large, 2, 8), None); - assert_eq!(rational_scale(large, 8, 4), Some(large/2)); - assert_eq!(rational_scale(large + 1, 4, 4), Some(large+1)); - assert_eq!(rational_scale(large, 40, 1000), None); -} - -#[test] -fn media_time_overflow() { - let scale = MediaTimeScale(90000); - let duration = MediaScaledTime(9007199254710000); - assert_eq!(media_time_to_us(duration, scale), Some(100079991719000000)); -} - -#[test] -fn track_time_overflow() { - let scale = TrackTimeScale(44100, 0); - let duration = TrackScaledTime(4413527634807900, 0); - assert_eq!(track_time_to_us(duration, scale), Some(100079991719000000)); -} |