1 //! C API for mp4parse module.
2 //!
3 //! Parses ISO Base Media Format aka video/mp4 streams.
4 //!
5 //! # Examples
6 //!
7 //! ```rust
8 //! extern crate mp4parse_capi;
9 //! use std::io::Read;
10 //!
11 //! extern fn buf_read(buf: *mut u8, size: usize, userdata: *mut std::os::raw::c_void) -> isize {
12 //!    let mut input: &mut std::fs::File = unsafe { &mut *(userdata as *mut _) };
13 //!    let mut buf = unsafe { std::slice::from_raw_parts_mut(buf, size) };
14 //!    match input.read(&mut buf) {
15 //!        Ok(n) => n as isize,
16 //!        Err(_) => -1,
17 //!    }
18 //! }
19 //!
20 //! let mut file = std::fs::File::open("../mp4parse/tests/minimal.mp4").unwrap();
21 //! let io = mp4parse_capi::Mp4parseIo {
22 //!     read: Some(buf_read),
23 //!     userdata: &mut file as *mut _ as *mut std::os::raw::c_void
24 //! };
25 //! unsafe {
26 //!     let parser = mp4parse_capi::mp4parse_new(&io);
27 //!     let rv = mp4parse_capi::mp4parse_read(parser);
28 //!     assert_eq!(rv, mp4parse_capi::Mp4parseStatus::Ok);
29 //!     mp4parse_capi::mp4parse_free(parser);
30 //! }
31 //! ```
32 
33 // This Source Code Form is subject to the terms of the Mozilla Public
34 // License, v. 2.0. If a copy of the MPL was not distributed with this
35 // file, You can obtain one at https://mozilla.org/MPL/2.0/.
36 
37 extern crate mp4parse;
38 extern crate byteorder;
39 extern crate num_traits;
40 
41 use std::io::Read;
42 use std::collections::HashMap;
43 use byteorder::WriteBytesExt;
44 use num_traits::{PrimInt, Zero};
45 
46 // Symbols we need from our rust api.
47 use mp4parse::MediaContext;
48 use mp4parse::TrackType;
49 use mp4parse::read_mp4;
50 use mp4parse::Error;
51 use mp4parse::SampleEntry;
52 use mp4parse::AudioCodecSpecific;
53 use mp4parse::VideoCodecSpecific;
54 use mp4parse::MediaTimeScale;
55 use mp4parse::MediaScaledTime;
56 use mp4parse::TrackTimeScale;
57 use mp4parse::TrackScaledTime;
58 use mp4parse::serialize_opus_header;
59 use mp4parse::CodecType;
60 use mp4parse::Track;
61 use mp4parse::vec_push;
62 
63 #[repr(C)]
64 #[derive(PartialEq, Debug)]
65 pub enum Mp4parseStatus {
66     Ok = 0,
67     BadArg = 1,
68     Invalid = 2,
69     Unsupported = 3,
70     Eof = 4,
71     Io = 5,
72     Oom = 6,
73 }
74 
75 #[repr(C)]
76 #[derive(PartialEq, Debug)]
77 pub enum Mp4parseTrackType {
78     Video = 0,
79     Audio = 1,
80 }
81 
82 impl Default for Mp4parseTrackType {
default() -> Self83     fn default() -> Self { Mp4parseTrackType::Video }
84 }
85 
86 #[allow(non_camel_case_types)]
87 #[repr(C)]
88 #[derive(PartialEq, Debug)]
89 pub enum Mp4parseCodec {
90     Unknown,
91     Aac,
92     Flac,
93     Opus,
94     Avc,
95     Vp9,
96     Mp3,
97     Mp4v,
98     Jpeg,   // for QT JPEG atom in video track
99     Ac3,
100     Ec3,
101     Alac,
102 }
103 
104 impl Default for Mp4parseCodec {
default() -> Self105     fn default() -> Self { Mp4parseCodec::Unknown }
106 }
107 
108 #[repr(C)]
109 #[derive(Default, Debug)]
110 pub struct Mp4parseTrackInfo {
111     pub track_type: Mp4parseTrackType,
112     pub codec: Mp4parseCodec,
113     pub track_id: u32,
114     pub duration: u64,
115     pub media_time: i64, // wants to be u64? understand how elst adjustment works
116     // TODO(kinetik): include crypto guff
117 }
118 
119 #[repr(C)]
120 #[derive(Default, Debug, PartialEq)]
121 pub struct Mp4parseIndice {
122     pub start_offset: u64,
123     pub end_offset: u64,
124     pub start_composition: i64,
125     pub end_composition: i64,
126     pub start_decode: i64,
127     pub sync: bool,
128 }
129 
130 #[repr(C)]
131 #[derive(Debug)]
132 pub struct Mp4parseByteData {
133     pub length: u32,
134     // cheddar can't handle generic type, so it needs to be multiple data types here.
135     pub data: *const u8,
136     pub indices: *const Mp4parseIndice,
137 }
138 
139 impl Default for Mp4parseByteData {
default() -> Self140     fn default() -> Self {
141         Self {
142             length: 0,
143             data: std::ptr::null(),
144             indices: std::ptr::null(),
145         }
146     }
147 }
148 
149 impl Mp4parseByteData {
set_data(&mut self, data: &[u8])150     fn set_data(&mut self, data: &[u8]) {
151         self.length = data.len() as u32;
152         self.data = data.as_ptr();
153     }
154 
set_indices(&mut self, data: &[Mp4parseIndice])155     fn set_indices(&mut self, data: &[Mp4parseIndice]) {
156         self.length = data.len() as u32;
157         self.indices = data.as_ptr();
158     }
159 }
160 
161 #[repr(C)]
162 #[derive(Default)]
163 pub struct Mp4parsePsshInfo {
164     pub data: Mp4parseByteData,
165 }
166 
167 #[repr(C)]
168 #[derive(Default, Debug)]
169 pub struct Mp4parseSinfInfo {
170     pub is_encrypted: u32,
171     pub iv_size: u8,
172     pub kid: Mp4parseByteData,
173 }
174 
175 #[repr(C)]
176 #[derive(Default, Debug)]
177 pub struct Mp4parseTrackAudioInfo {
178     pub channels: u16,
179     pub bit_depth: u16,
180     pub sample_rate: u32,
181     pub profile: u16,
182     pub codec_specific_config: Mp4parseByteData,
183     pub extra_data: Mp4parseByteData,
184     pub protected_data: Mp4parseSinfInfo,
185 }
186 
187 #[repr(C)]
188 #[derive(Default, Debug)]
189 pub struct Mp4parseTrackVideoInfo {
190     pub display_width: u32,
191     pub display_height: u32,
192     pub image_width: u16,
193     pub image_height: u16,
194     pub rotation: u16,
195     pub extra_data: Mp4parseByteData,
196     pub protected_data: Mp4parseSinfInfo,
197 }
198 
199 #[repr(C)]
200 #[derive(Default, Debug)]
201 pub struct Mp4parseFragmentInfo {
202     pub fragment_duration: u64,
203     // TODO:
204     // info in trex box.
205 }
206 
207 pub struct Mp4parseParser {
208     context: MediaContext,
209     io: Mp4parseIo,
210     poisoned: bool,
211     opus_header: HashMap<u32, Vec<u8>>,
212     pssh_data: Vec<u8>,
213     sample_table: HashMap<u32, Vec<Mp4parseIndice>>,
214 }
215 
216 impl Mp4parseParser {
context(&self) -> &MediaContext217     fn context(&self) -> &MediaContext {
218         &self.context
219     }
220 
context_mut(&mut self) -> &mut MediaContext221     fn context_mut(&mut self) -> &mut MediaContext {
222         &mut self.context
223     }
224 
io_mut(&mut self) -> &mut Mp4parseIo225     fn io_mut(&mut self) -> &mut Mp4parseIo {
226         &mut self.io
227     }
228 
poisoned(&self) -> bool229     fn poisoned(&self) -> bool {
230         self.poisoned
231     }
232 
set_poisoned(&mut self, poisoned: bool)233     fn set_poisoned(&mut self, poisoned: bool) {
234         self.poisoned = poisoned;
235     }
236 
opus_header_mut(&mut self) -> &mut HashMap<u32, Vec<u8>>237     fn opus_header_mut(&mut self) -> &mut HashMap<u32, Vec<u8>> {
238         &mut self.opus_header
239     }
240 
pssh_data_mut(&mut self) -> &mut Vec<u8>241     fn pssh_data_mut(&mut self) -> &mut Vec<u8> {
242         &mut self.pssh_data
243     }
244 
sample_table_mut(&mut self) -> &mut HashMap<u32, Vec<Mp4parseIndice>>245     fn sample_table_mut(&mut self) -> &mut HashMap<u32, Vec<Mp4parseIndice>> {
246         &mut self.sample_table
247     }
248 }
249 
250 #[repr(C)]
251 #[derive(Clone)]
252 pub struct Mp4parseIo {
253     pub read: Option<extern fn(buffer: *mut u8, size: usize, userdata: *mut std::os::raw::c_void) -> isize>,
254     pub userdata: *mut std::os::raw::c_void,
255 }
256 
257 impl Read for Mp4parseIo {
read(&mut self, buf: &mut [u8]) -> std::io::Result<usize>258     fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
259         if buf.len() > isize::max_value() as usize {
260             return Err(std::io::Error::new(std::io::ErrorKind::Other, "buf length overflow in Mp4parseIo Read impl"));
261         }
262         let rv = self.read.unwrap()(buf.as_mut_ptr(), buf.len(), self.userdata);
263         if rv >= 0 {
264             Ok(rv as usize)
265         } else {
266             Err(std::io::Error::new(std::io::ErrorKind::Other, "I/O error in Mp4parseIo Read impl"))
267         }
268     }
269 }
270 
271 // C API wrapper functions.
272 
273 /// Allocate an `Mp4parseParser*` to read from the supplied `Mp4parseIo`.
274 #[no_mangle]
mp4parse_new(io: *const Mp4parseIo) -> *mut Mp4parseParser275 pub unsafe extern fn mp4parse_new(io: *const Mp4parseIo) -> *mut Mp4parseParser {
276     if io.is_null() || (*io).userdata.is_null() {
277         return std::ptr::null_mut();
278     }
279     if (*io).read.is_none() {
280         return std::ptr::null_mut();
281     }
282     let parser = Box::new(Mp4parseParser {
283         context: MediaContext::new(),
284         io: (*io).clone(),
285         poisoned: false,
286         opus_header: HashMap::new(),
287         pssh_data: Vec::new(),
288         sample_table: HashMap::new(),
289     });
290 
291     Box::into_raw(parser)
292 }
293 
294 /// Free an `Mp4parseParser*` allocated by `mp4parse_new()`.
295 #[no_mangle]
mp4parse_free(parser: *mut Mp4parseParser)296 pub unsafe extern fn mp4parse_free(parser: *mut Mp4parseParser) {
297     assert!(!parser.is_null());
298     let _ = Box::from_raw(parser);
299 }
300 
301 /// Run the `Mp4parseParser*` allocated by `mp4parse_new()` until EOF or error.
302 #[no_mangle]
mp4parse_read(parser: *mut Mp4parseParser) -> Mp4parseStatus303 pub unsafe extern fn mp4parse_read(parser: *mut Mp4parseParser) -> Mp4parseStatus {
304     // Validate arguments from C.
305     if parser.is_null() || (*parser).poisoned() {
306         return Mp4parseStatus::BadArg;
307     }
308 
309     let context = (*parser).context_mut();
310     let io = (*parser).io_mut();
311 
312     let r = read_mp4(io, context);
313     match r {
314         Ok(_) => Mp4parseStatus::Ok,
315         Err(Error::NoMoov) | Err(Error::InvalidData(_)) => {
316             // Block further calls. We've probable lost sync.
317             (*parser).set_poisoned(true);
318             Mp4parseStatus::Invalid
319         }
320         Err(Error::Unsupported(_)) => Mp4parseStatus::Unsupported,
321         Err(Error::UnexpectedEOF) => Mp4parseStatus::Eof,
322         Err(Error::Io(_)) => {
323             // Block further calls after a read failure.
324             // Getting std::io::ErrorKind::UnexpectedEof is normal
325             // but our From trait implementation should have converted
326             // those to our Error::UnexpectedEOF variant.
327             (*parser).set_poisoned(true);
328             Mp4parseStatus::Io
329         },
330         Err(Error::OutOfMemory) => Mp4parseStatus::Oom,
331     }
332 }
333 
334 /// Return the number of tracks parsed by previous `mp4parse_read()` call.
335 #[no_mangle]
mp4parse_get_track_count(parser: *const Mp4parseParser, count: *mut u32) -> Mp4parseStatus336 pub unsafe extern fn mp4parse_get_track_count(parser: *const Mp4parseParser, count: *mut u32) -> Mp4parseStatus {
337     // Validate arguments from C.
338     if parser.is_null() || count.is_null() || (*parser).poisoned() {
339         return Mp4parseStatus::BadArg;
340     }
341     let context = (*parser).context();
342 
343     // Make sure the track count fits in a u32.
344     if context.tracks.len() > u32::max_value() as usize {
345         return Mp4parseStatus::Invalid;
346     }
347     *count = context.tracks.len() as u32;
348     Mp4parseStatus::Ok
349 }
350 
351 /// Calculate numerator * scale / denominator, if possible.
352 ///
353 /// Applying the associativity of integer arithmetic, we divide first
354 /// and add the remainder after multiplying each term separately
355 /// to preserve precision while leaving more headroom. That is,
356 /// (n * s) / d is split into floor(n / d) * s + (n % d) * s / d.
357 ///
358 /// Return None on overflow or if the denominator is zero.
rational_scale<T, S>(numerator: T, denominator: T, scale2: S) -> Option<T> where T: PrimInt + Zero, S: PrimInt359 fn rational_scale<T, S>(numerator: T, denominator: T, scale2: S) -> Option<T>
360     where T: PrimInt + Zero, S: PrimInt {
361     if denominator.is_zero() {
362         return None;
363     }
364 
365     let integer = numerator / denominator;
366     let remainder = numerator % denominator;
367     num_traits::cast(scale2).and_then(|s| {
368         match integer.checked_mul(&s) {
369             Some(integer) => remainder.checked_mul(&s)
370                 .and_then(|remainder| (remainder/denominator).checked_add(&integer)),
371             None => None,
372         }
373     })
374 }
375 
media_time_to_us(time: MediaScaledTime, scale: MediaTimeScale) -> Option<u64>376 fn media_time_to_us(time: MediaScaledTime, scale: MediaTimeScale) -> Option<u64> {
377     let microseconds_per_second = 1000000;
378     rational_scale::<u64, u64>(time.0, scale.0, microseconds_per_second)
379 }
380 
track_time_to_us<T>(time: TrackScaledTime<T>, scale: TrackTimeScale<T>) -> Option<T> where T: PrimInt + Zero381 fn track_time_to_us<T>(time: TrackScaledTime<T>, scale: TrackTimeScale<T>) -> Option<T>
382     where T: PrimInt + Zero {
383     assert_eq!(time.1, scale.1);
384     let microseconds_per_second = 1000000;
385     rational_scale::<T, u64>(time.0, scale.0, microseconds_per_second)
386 }
387 
388 /// Fill the supplied `Mp4parseTrackInfo` with metadata for `track`.
389 #[no_mangle]
mp4parse_get_track_info(parser: *mut Mp4parseParser, track_index: u32, info: *mut Mp4parseTrackInfo) -> Mp4parseStatus390 pub unsafe extern fn mp4parse_get_track_info(parser: *mut Mp4parseParser, track_index: u32, info: *mut Mp4parseTrackInfo) -> Mp4parseStatus {
391     if parser.is_null() || info.is_null() || (*parser).poisoned() {
392         return Mp4parseStatus::BadArg;
393     }
394 
395     // Initialize fields to default values to ensure all fields are always valid.
396     *info = Default::default();
397 
398     let context = (*parser).context_mut();
399     let track_index: usize = track_index as usize;
400     let info: &mut Mp4parseTrackInfo = &mut *info;
401 
402     if track_index >= context.tracks.len() {
403         return Mp4parseStatus::BadArg;
404     }
405 
406     info.track_type = match context.tracks[track_index].track_type {
407         TrackType::Video => Mp4parseTrackType::Video,
408         TrackType::Audio => Mp4parseTrackType::Audio,
409         TrackType::Unknown => return Mp4parseStatus::Unsupported,
410     };
411 
412     // Return UNKNOWN for unsupported format.
413     info.codec = match context.tracks[track_index].data {
414         Some(SampleEntry::Audio(ref audio)) => match audio.codec_specific {
415             AudioCodecSpecific::OpusSpecificBox(_) =>
416                 Mp4parseCodec::Opus,
417             AudioCodecSpecific::FLACSpecificBox(_) =>
418                 Mp4parseCodec::Flac,
419             AudioCodecSpecific::ES_Descriptor(ref esds) if esds.audio_codec == CodecType::AAC =>
420                 Mp4parseCodec::Aac,
421             AudioCodecSpecific::ES_Descriptor(ref esds) if esds.audio_codec == CodecType::MP3 =>
422                 Mp4parseCodec::Mp3,
423             AudioCodecSpecific::ES_Descriptor(_) | AudioCodecSpecific::LPCM =>
424                 Mp4parseCodec::Unknown,
425             AudioCodecSpecific::MP3 =>
426                 Mp4parseCodec::Mp3,
427             AudioCodecSpecific::ALACSpecificBox(_) =>
428                 Mp4parseCodec::Alac,
429         },
430         Some(SampleEntry::Video(ref video)) => match video.codec_specific {
431             VideoCodecSpecific::VPxConfig(_) =>
432                 Mp4parseCodec::Vp9,
433             VideoCodecSpecific::AVCConfig(_) =>
434                 Mp4parseCodec::Avc,
435             VideoCodecSpecific::ESDSConfig(_) => // MP4V (14496-2) video is unsupported.
436                 Mp4parseCodec::Unknown,
437         },
438         _ => Mp4parseCodec::Unknown,
439     };
440 
441     let track = &context.tracks[track_index];
442 
443     if let (Some(track_timescale),
444             Some(context_timescale)) = (track.timescale,
445                                         context.timescale) {
446         let media_time =
447             match track.media_time.map_or(Some(0), |media_time| {
448                     track_time_to_us(media_time, track_timescale) }) {
449                 Some(time) => time as i64,
450                 None => return Mp4parseStatus::Invalid,
451             };
452         let empty_duration =
453             match track.empty_duration.map_or(Some(0), |empty_duration| {
454                     media_time_to_us(empty_duration, context_timescale) }) {
455                 Some(time) => time as i64,
456                 None => return Mp4parseStatus::Invalid,
457             };
458         info.media_time = media_time - empty_duration;
459 
460         if let Some(track_duration) = track.duration {
461             match track_time_to_us(track_duration, track_timescale) {
462                 Some(duration) => info.duration = duration,
463                 None => return Mp4parseStatus::Invalid,
464             }
465         } else {
466             // Duration unknown; stagefright returns 0 for this.
467             info.duration = 0
468         }
469     } else {
470         return Mp4parseStatus::Invalid
471     }
472 
473     info.track_id = match track.track_id {
474         Some(track_id) => track_id,
475         None => return Mp4parseStatus::Invalid,
476     };
477 
478     Mp4parseStatus::Ok
479 }
480 
481 /// Fill the supplied `Mp4parseTrackAudioInfo` with metadata for `track`.
482 #[no_mangle]
mp4parse_get_track_audio_info(parser: *mut Mp4parseParser, track_index: u32, info: *mut Mp4parseTrackAudioInfo) -> Mp4parseStatus483 pub unsafe extern fn mp4parse_get_track_audio_info(parser: *mut Mp4parseParser, track_index: u32, info: *mut Mp4parseTrackAudioInfo) -> Mp4parseStatus {
484     if parser.is_null() || info.is_null() || (*parser).poisoned() {
485         return Mp4parseStatus::BadArg;
486     }
487 
488     // Initialize fields to default values to ensure all fields are always valid.
489     *info = Default::default();
490 
491     let context = (*parser).context_mut();
492 
493     if track_index as usize >= context.tracks.len() {
494         return Mp4parseStatus::BadArg;
495     }
496 
497     let track = &context.tracks[track_index as usize];
498 
499     match track.track_type {
500         TrackType::Audio => {}
501         _ => return Mp4parseStatus::Invalid,
502     };
503 
504     let audio = match track.data {
505         Some(ref data) => data,
506         None => return Mp4parseStatus::Invalid,
507     };
508 
509     let audio = match *audio {
510         SampleEntry::Audio(ref x) => x,
511         _ => return Mp4parseStatus::Invalid,
512     };
513 
514     (*info).channels = audio.channelcount as u16;
515     (*info).bit_depth = audio.samplesize;
516     (*info).sample_rate = audio.samplerate as u32;
517 
518     match audio.codec_specific {
519         AudioCodecSpecific::ES_Descriptor(ref v) => {
520             if v.codec_esds.len() > std::u32::MAX as usize {
521                 return Mp4parseStatus::Invalid;
522             }
523             (*info).extra_data.length = v.codec_esds.len() as u32;
524             (*info).extra_data.data = v.codec_esds.as_ptr();
525             (*info).codec_specific_config.length = v.decoder_specific_data.len() as u32;
526             (*info).codec_specific_config.data = v.decoder_specific_data.as_ptr();
527             if let Some(rate) = v.audio_sample_rate {
528                 (*info).sample_rate = rate;
529             }
530             if let Some(channels) = v.audio_channel_count {
531                 (*info).channels = channels;
532             }
533             if let Some(profile) = v.audio_object_type {
534                 (*info).profile = profile;
535             }
536         }
537         AudioCodecSpecific::FLACSpecificBox(ref flac) => {
538             // Return the STREAMINFO metadata block in the codec_specific.
539             let streaminfo = &flac.blocks[0];
540             if streaminfo.block_type != 0 || streaminfo.data.len() != 34 {
541                 return Mp4parseStatus::Invalid;
542             }
543             (*info).extra_data.length = streaminfo.data.len() as u32;
544             (*info).extra_data.data = streaminfo.data.as_ptr();
545         }
546         AudioCodecSpecific::OpusSpecificBox(ref opus) => {
547             let mut v = Vec::new();
548             match serialize_opus_header(opus, &mut v) {
549                 Err(_) => {
550                     return Mp4parseStatus::Invalid;
551                 }
552                 Ok(_) => {
553                     let header = (*parser).opus_header_mut();
554                     header.insert(track_index, v);
555                     if let Some(v) = header.get(&track_index) {
556                         if v.len() > std::u32::MAX as usize {
557                             return Mp4parseStatus::Invalid;
558                         }
559                         (*info).extra_data.length = v.len() as u32;
560                         (*info).extra_data.data = v.as_ptr();
561                     }
562                 }
563             }
564         }
565         AudioCodecSpecific::ALACSpecificBox(ref alac) => {
566             (*info).extra_data.length = alac.data.len() as u32;
567             (*info).extra_data.data = alac.data.as_ptr();
568         }
569         AudioCodecSpecific::MP3 | AudioCodecSpecific::LPCM => (),
570     }
571 
572     if let Some(p) = audio.protection_info.iter().find(|sinf| sinf.tenc.is_some()) {
573         if let Some(ref tenc) = p.tenc {
574             (*info).protected_data.is_encrypted = tenc.is_encrypted;
575             (*info).protected_data.iv_size = tenc.iv_size;
576             (*info).protected_data.kid.set_data(&(tenc.kid));
577         }
578     }
579 
580     Mp4parseStatus::Ok
581 }
582 
583 /// Fill the supplied `Mp4parseTrackVideoInfo` with metadata for `track`.
584 #[no_mangle]
mp4parse_get_track_video_info(parser: *mut Mp4parseParser, track_index: u32, info: *mut Mp4parseTrackVideoInfo) -> Mp4parseStatus585 pub unsafe extern fn mp4parse_get_track_video_info(parser: *mut Mp4parseParser, track_index: u32, info: *mut Mp4parseTrackVideoInfo) -> Mp4parseStatus {
586     if parser.is_null() || info.is_null() || (*parser).poisoned() {
587         return Mp4parseStatus::BadArg;
588     }
589 
590     // Initialize fields to default values to ensure all fields are always valid.
591     *info = Default::default();
592 
593     let context = (*parser).context_mut();
594 
595     if track_index as usize >= context.tracks.len() {
596         return Mp4parseStatus::BadArg;
597     }
598 
599     let track = &context.tracks[track_index as usize];
600 
601     match track.track_type {
602         TrackType::Video => {}
603         _ => return Mp4parseStatus::Invalid,
604     };
605 
606     let video = match track.data {
607         Some(ref data) => data,
608         None => return Mp4parseStatus::Invalid,
609     };
610 
611     let video = match *video {
612         SampleEntry::Video(ref x) => x,
613         _ => return Mp4parseStatus::Invalid,
614     };
615 
616     if let Some(ref tkhd) = track.tkhd {
617         (*info).display_width = tkhd.width >> 16; // 16.16 fixed point
618         (*info).display_height = tkhd.height >> 16; // 16.16 fixed point
619         let matrix = (tkhd.matrix.a >> 16, tkhd.matrix.b >> 16,
620                       tkhd.matrix.c >> 16, tkhd.matrix.d >> 16);
621         (*info).rotation = match matrix {
622             ( 0,  1, -1,  0) => 90, // rotate 90 degrees
623             (-1,  0,  0, -1) => 180, // rotate 180 degrees
624             ( 0, -1,  1,  0) => 270, // rotate 270 degrees
625             _ => 0,
626         };
627     } else {
628         return Mp4parseStatus::Invalid;
629     }
630     (*info).image_width = video.width;
631     (*info).image_height = video.height;
632 
633     match video.codec_specific {
634         VideoCodecSpecific::AVCConfig(ref data) | VideoCodecSpecific::ESDSConfig(ref data) => {
635           (*info).extra_data.set_data(data);
636         },
637         _ => {}
638     }
639 
640     if let Some(p) = video.protection_info.iter().find(|sinf| sinf.tenc.is_some()) {
641         if let Some(ref tenc) = p.tenc {
642             (*info).protected_data.is_encrypted = tenc.is_encrypted;
643             (*info).protected_data.iv_size = tenc.iv_size;
644             (*info).protected_data.kid.set_data(&(tenc.kid));
645         }
646     }
647 
648     Mp4parseStatus::Ok
649 }
650 
651 #[no_mangle]
mp4parse_get_indice_table(parser: *mut Mp4parseParser, track_id: u32, indices: *mut Mp4parseByteData) -> Mp4parseStatus652 pub unsafe extern fn mp4parse_get_indice_table(parser: *mut Mp4parseParser, track_id: u32, indices: *mut Mp4parseByteData) -> Mp4parseStatus {
653     if parser.is_null() || (*parser).poisoned() {
654         return Mp4parseStatus::BadArg;
655     }
656 
657     // Initialize fields to default values to ensure all fields are always valid.
658     *indices = Default::default();
659 
660     let context = (*parser).context();
661     let tracks = &context.tracks;
662     let track = match tracks.iter().find(|track| track.track_id == Some(track_id)) {
663         Some(t) => t,
664         _ => return Mp4parseStatus::Invalid,
665     };
666 
667     let index_table = (*parser).sample_table_mut();
668     if let Some(v) = index_table.get(&track_id) {
669         (*indices).set_indices(v);
670         return Mp4parseStatus::Ok;
671     }
672 
673     let media_time = match (&track.media_time, &track.timescale) {
674         (&Some(t), &Some(s)) => {
675             track_time_to_us(t, s).map(|v| v as i64)
676         },
677         _ => None,
678     };
679 
680     let empty_duration = match (&track.empty_duration, &context.timescale) {
681         (&Some(e), &Some(s)) => {
682             media_time_to_us(e, s).map(|v| v as i64)
683         },
684         _ => None
685     };
686 
687     // Find the track start offset time from 'elst'.
688     // 'media_time' maps start time onward, 'empty_duration' adds time offset
689     // before first frame is displayed.
690     let offset_time = match (empty_duration, media_time) {
691         (Some(e), Some(m)) => e - m,
692         (Some(e), None) => e,
693         (None, Some(m)) => m,
694         _ => 0,
695     };
696 
697     if let Some(v) = create_sample_table(track, offset_time) {
698         (*indices).set_indices(&v);
699         index_table.insert(track_id, v);
700         return Mp4parseStatus::Ok;
701     }
702 
703     Mp4parseStatus::Invalid
704 }
705 
706 // Convert a 'ctts' compact table to full table by iterator,
707 // (sample_with_the_same_offset_count, offset) => (offset), (offset), (offset) ...
708 //
709 // For example:
710 // (2, 10), (4, 9) into (10, 10, 9, 9, 9, 9) by calling next_offset_time().
711 struct TimeOffsetIterator<'a> {
712     cur_sample_range: std::ops::Range<u32>,
713     cur_offset: i64,
714     ctts_iter: Option<std::slice::Iter<'a, mp4parse::TimeOffset>>,
715     track_id: usize,
716 }
717 
718 impl<'a> Iterator for TimeOffsetIterator<'a> {
719     type Item = i64;
720 
next(&mut self) -> Option<i64>721     fn next(&mut self) -> Option<i64> {
722         let has_sample = self.cur_sample_range.next()
723             .or_else(|| {
724                 // At end of current TimeOffset, find the next TimeOffset.
725                 let iter = match self.ctts_iter {
726                     Some(ref mut v) => v,
727                     _ => return None,
728                 };
729                 let offset_version;
730                 self.cur_sample_range = match iter.next() {
731                     Some(v) => {
732                         offset_version = v.time_offset;
733                         (0 .. v.sample_count)
734                     },
735                     _ => {
736                         offset_version = mp4parse::TimeOffsetVersion::Version0(0);
737                         (0 .. 0)
738                     },
739                 };
740 
741                 self.cur_offset = match offset_version {
742                     mp4parse::TimeOffsetVersion::Version0(i) => i as i64,
743                     mp4parse::TimeOffsetVersion::Version1(i) => i as i64,
744                 };
745 
746                 self.cur_sample_range.next()
747             });
748 
749         has_sample.and(Some(self.cur_offset))
750     }
751 }
752 
753 impl<'a> TimeOffsetIterator<'a> {
next_offset_time(&mut self) -> TrackScaledTime<i64>754     fn next_offset_time(&mut self) -> TrackScaledTime<i64> {
755         match self.next() {
756             Some(v) => TrackScaledTime::<i64>(v as i64, self.track_id),
757             _ => TrackScaledTime::<i64>(0, self.track_id),
758         }
759     }
760 }
761 
762 // Convert 'stts' compact table to full table by iterator,
763 // (sample_count_with_the_same_time, time) => (time, time, time) ... repeats
764 // sample_count_with_the_same_time.
765 //
766 // For example:
767 // (2, 3000), (1, 2999) to (3000, 3000, 2999).
768 struct TimeToSampleIterator<'a> {
769     cur_sample_count: std::ops::Range<u32>,
770     cur_sample_delta: u32,
771     stts_iter: std::slice::Iter<'a, mp4parse::Sample>,
772     track_id: usize,
773 }
774 
775 impl<'a> Iterator for TimeToSampleIterator<'a> {
776     type Item = u32;
777 
next(&mut self) -> Option<u32>778     fn next(&mut self) -> Option<u32> {
779         let has_sample = self.cur_sample_count.next()
780             .or_else(|| {
781                 self.cur_sample_count = match self.stts_iter.next() {
782                     Some(v) => {
783                         self.cur_sample_delta = v.sample_delta;
784                         (0 .. v.sample_count)
785                     },
786                     _ => (0 .. 0),
787                 };
788 
789                 self.cur_sample_count.next()
790             });
791 
792         has_sample.and(Some(self.cur_sample_delta))
793     }
794 }
795 
796 impl<'a> TimeToSampleIterator<'a> {
next_delta(&mut self) -> TrackScaledTime<i64>797     fn next_delta(&mut self) -> TrackScaledTime<i64> {
798         match self.next() {
799             Some(v) => TrackScaledTime::<i64>(v as i64, self.track_id),
800             _ => TrackScaledTime::<i64>(0, self.track_id),
801         }
802     }
803 }
804 
805 // Convert 'stco' compact table to full table by iterator.
806 // (start_chunk_num, sample_number) => (start_chunk_num, sample_number),
807 //                                     (start_chunk_num + 1, sample_number),
808 //                                     (start_chunk_num + 2, sample_number),
809 //                                     ...
810 //                                     (next start_chunk_num, next sample_number),
811 //                                     ...
812 //
813 // For example:
814 // (1, 5), (5, 10), (9, 2) => (1, 5), (2, 5), (3, 5), (4, 5), (5, 10), (6, 10),
815 // (7, 10), (8, 10), (9, 2)
816 struct SampleToChunkIterator<'a> {
817     chunks: std::ops::Range<u32>,
818     sample_count: u32,
819     stsc_peek_iter: std::iter::Peekable<std::slice::Iter<'a, mp4parse::SampleToChunk>>,
820     remain_chunk_count: u32, // total chunk number from 'stco'.
821 }
822 
823 impl<'a> Iterator for SampleToChunkIterator<'a> {
824     type Item = (u32, u32);
825 
next(&mut self) -> Option<(u32, u32)>826     fn next(&mut self) -> Option<(u32, u32)> {
827         let has_chunk = self.chunks.next()
828             .or_else(|| {
829                 self.chunks = match (self.stsc_peek_iter.next(), self.stsc_peek_iter.peek()) {
830                     (Some(next), Some(peek)) if next.first_chunk > 0 && peek.first_chunk > 0 => {
831                         self.sample_count = next.samples_per_chunk;
832                         ((next.first_chunk - 1) .. (peek.first_chunk - 1))
833                     },
834                     (Some(next), None) if next.first_chunk > 0 => {
835                         self.sample_count = next.samples_per_chunk;
836                         // Total chunk number in 'stsc' could be different to 'stco',
837                         // there could be more chunks at the last 'stsc' record.
838                         match next.first_chunk.checked_add(self.remain_chunk_count) {
839                             Some(r) => ((next.first_chunk - 1) .. r - 1),
840                             _ => (0 .. 0),
841                         }
842                     },
843                     _ => (0 .. 0),
844                 };
845 
846                 self.remain_chunk_count.checked_sub(self.chunks.len() as u32).and_then(|res| {
847                     self.remain_chunk_count = res;
848                     self.chunks.next()
849                 })
850             });
851 
852         has_chunk.map_or(None, |id| { Some((id, self.sample_count)) })
853     }
854 }
855 
create_sample_table(track: &Track, track_offset_time: i64) -> Option<Vec<Mp4parseIndice>>856 fn create_sample_table(track: &Track, track_offset_time: i64) -> Option<Vec<Mp4parseIndice>> {
857     let timescale = match track.timescale {
858         Some(ref t) => TrackTimeScale::<i64>(t.0 as i64, t.1),
859         _ => TrackTimeScale::<i64>(0, 0),
860     };
861 
862     let (stsc, stco, stsz, stts) =
863         match (&track.stsc, &track.stco, &track.stsz, &track.stts) {
864             (&Some(ref a), &Some(ref b), &Some(ref c), &Some(ref d)) => (a, b, c, d),
865             _ => return None,
866         };
867 
868     // According to spec, no sync table means every sample is sync sample.
869     let has_sync_table = match track.stss {
870         Some(_) => true,
871         _ => false,
872     };
873 
874     let mut sample_table = Vec::new();
875     let mut sample_size_iter = stsz.sample_sizes.iter();
876 
877     // Get 'stsc' iterator for (chunk_id, chunk_sample_count) and calculate the sample
878     // offset address.
879     let stsc_iter = SampleToChunkIterator {
880         chunks: (0 .. 0),
881         sample_count: 0,
882         stsc_peek_iter: stsc.samples.as_slice().iter().peekable(),
883         remain_chunk_count: stco.offsets.len() as u32,
884     };
885 
886     for i in stsc_iter {
887         let chunk_id = i.0 as usize;
888         let sample_counts = i.1;
889         let mut cur_position = match stco.offsets.get(chunk_id) {
890             Some(&i) => i,
891             _ => return None,
892         };
893         for _ in 0 .. sample_counts {
894             let start_offset = cur_position;
895             let end_offset = match (stsz.sample_size, sample_size_iter.next()) {
896                 (_, Some(t)) => start_offset + *t as u64,
897                 (t, _) if t > 0 => start_offset + t as u64,
898                 _ => 0,
899             };
900             if end_offset == 0 {
901                 return None;
902             }
903             cur_position = end_offset;
904 
905             let res = vec_push(&mut sample_table, Mp4parseIndice {
906                 start_offset: start_offset,
907                 end_offset: end_offset,
908                 start_composition: 0,
909                 end_composition: 0,
910                 start_decode: 0,
911                 sync: !has_sync_table,
912             });
913             if res.is_err() {
914                 return None;
915             }
916         }
917     }
918 
919     // Mark the sync sample in sample_table according to 'stss'.
920     if let Some(ref v) = track.stss {
921         for iter in &v.samples {
922             match iter.checked_sub(1).and_then(|idx| { sample_table.get_mut(idx as usize) }) {
923                 Some(elem) => elem.sync = true,
924                 _ => return None,
925             }
926         }
927     }
928 
929     let ctts_iter = match track.ctts {
930         Some(ref v) => Some(v.samples.as_slice().iter()),
931         _ => None,
932     };
933 
934     let mut ctts_offset_iter = TimeOffsetIterator {
935         cur_sample_range: (0 .. 0),
936         cur_offset: 0,
937         ctts_iter: ctts_iter,
938         track_id: track.id,
939     };
940 
941     let mut stts_iter = TimeToSampleIterator {
942         cur_sample_count: (0 .. 0),
943         cur_sample_delta: 0,
944         stts_iter: stts.samples.as_slice().iter(),
945         track_id: track.id,
946     };
947 
948     // sum_delta is the sum of stts_iter delta.
949     // According to sepc:
950     //      decode time => DT(n) = DT(n-1) + STTS(n)
951     //      composition time => CT(n) = DT(n) + CTTS(n)
952     // Note:
953     //      composition time needs to add the track offset time from 'elst' table.
954     let mut sum_delta = TrackScaledTime::<i64>(0, track.id);
955     for sample in sample_table.as_mut_slice() {
956         let decode_time = sum_delta;
957         sum_delta = sum_delta + stts_iter.next_delta();
958 
959         // ctts_offset is the current sample offset time.
960         let ctts_offset = ctts_offset_iter.next_offset_time();
961 
962         let start_composition = track_time_to_us(decode_time + ctts_offset, timescale);
963 
964         let end_composition = track_time_to_us(sum_delta + ctts_offset, timescale);
965 
966         let start_decode = track_time_to_us(decode_time, timescale);
967 
968         match (start_composition, end_composition, start_decode) {
969             (Some(s_c), Some(e_c), Some(s_d)) => {
970                 sample.start_composition = s_c + track_offset_time;
971                 sample.end_composition = e_c + track_offset_time;
972                 sample.start_decode = s_d;
973             },
974             _ => return None,
975         }
976     }
977 
978     // Correct composition end time due to 'ctts' causes composition time re-ordering.
979     //
980     // Composition end time is not in specification. However, gecko needs it, so we need to
981     // calculate to correct the composition end time.
982     if sample_table.len() > 0 {
983         // Create an index table refers to sample_table and sorted by start_composisiton time.
984         let mut sort_table = Vec::new();
985         for i in 0 .. sample_table.len() {
986             if vec_push(&mut sort_table, i).is_err() {
987                 return None;
988             }
989         }
990 
991         sort_table.sort_by_key(|i| {
992             match sample_table.get(*i) {
993                 Some(v) => {
994                     v.start_composition
995                 },
996                 _ => 0,
997             }
998         });
999 
1000         let iter = sort_table.iter();
1001         for i in 0 .. (iter.len() - 1) {
1002             let current_index = sort_table[i];
1003             let peek_index = sort_table[i + 1];
1004             let next_start_composition_time = sample_table[peek_index].start_composition;
1005             let sample = &mut sample_table[current_index];
1006             sample.end_composition = next_start_composition_time;
1007         }
1008     }
1009 
1010     Some(sample_table)
1011 }
1012 
1013 /// Fill the supplied `Mp4parseFragmentInfo` with metadata from fragmented file.
1014 #[no_mangle]
mp4parse_get_fragment_info(parser: *mut Mp4parseParser, info: *mut Mp4parseFragmentInfo) -> Mp4parseStatus1015 pub unsafe extern fn mp4parse_get_fragment_info(parser: *mut Mp4parseParser, info: *mut Mp4parseFragmentInfo) -> Mp4parseStatus {
1016     if parser.is_null() || info.is_null() || (*parser).poisoned() {
1017         return Mp4parseStatus::BadArg;
1018     }
1019 
1020     // Initialize fields to default values to ensure all fields are always valid.
1021     *info = Default::default();
1022 
1023     let context = (*parser).context();
1024     let info: &mut Mp4parseFragmentInfo = &mut *info;
1025 
1026     info.fragment_duration = 0;
1027 
1028     let duration = match context.mvex {
1029         Some(ref mvex) => mvex.fragment_duration,
1030         None => return Mp4parseStatus::Invalid,
1031     };
1032 
1033     if let (Some(time), Some(scale)) = (duration, context.timescale) {
1034         info.fragment_duration = match media_time_to_us(time, scale) {
1035             Some(time_us) => time_us as u64,
1036             None => return Mp4parseStatus::Invalid,
1037         }
1038     }
1039 
1040     Mp4parseStatus::Ok
1041 }
1042 
1043 /// A fragmented file needs mvex table and contains no data in stts, stsc, and stco boxes.
1044 #[no_mangle]
mp4parse_is_fragmented(parser: *mut Mp4parseParser, track_id: u32, fragmented: *mut u8) -> Mp4parseStatus1045 pub unsafe extern fn mp4parse_is_fragmented(parser: *mut Mp4parseParser, track_id: u32, fragmented: *mut u8) -> Mp4parseStatus {
1046     if parser.is_null() || (*parser).poisoned() {
1047         return Mp4parseStatus::BadArg;
1048     }
1049 
1050     let context = (*parser).context_mut();
1051     let tracks = &context.tracks;
1052     (*fragmented) = false as u8;
1053 
1054     if context.mvex.is_none() {
1055         return Mp4parseStatus::Ok;
1056     }
1057 
1058     // check sample tables.
1059     let mut iter = tracks.iter();
1060     iter.find(|track| track.track_id == Some(track_id)).map_or(Mp4parseStatus::BadArg, |track| {
1061         match (&track.stsc, &track.stco, &track.stts) {
1062             (&Some(ref stsc), &Some(ref stco), &Some(ref stts))
1063                 if stsc.samples.is_empty() && stco.offsets.is_empty() && stts.samples.is_empty() => (*fragmented) = true as u8,
1064             _ => {},
1065         };
1066         Mp4parseStatus::Ok
1067     })
1068 }
1069 
1070 /// Get 'pssh' system id and 'pssh' box content for eme playback.
1071 ///
1072 /// The data format of the `info` struct passed to gecko is:
1073 ///
1074 /// - system id (16 byte uuid)
1075 /// - pssh box size (32-bit native endian)
1076 /// - pssh box content (including header)
1077 #[no_mangle]
mp4parse_get_pssh_info(parser: *mut Mp4parseParser, info: *mut Mp4parsePsshInfo) -> Mp4parseStatus1078 pub unsafe extern fn mp4parse_get_pssh_info(parser: *mut Mp4parseParser, info: *mut Mp4parsePsshInfo) -> Mp4parseStatus {
1079     if parser.is_null() || info.is_null() || (*parser).poisoned() {
1080         return Mp4parseStatus::BadArg;
1081     }
1082 
1083     // Initialize fields to default values to ensure all fields are always valid.
1084     *info = Default::default();
1085 
1086     let context = (*parser).context_mut();
1087     let pssh_data = (*parser).pssh_data_mut();
1088     let info: &mut Mp4parsePsshInfo = &mut *info;
1089 
1090     pssh_data.clear();
1091     for pssh in &context.psshs {
1092         let content_len = pssh.box_content.len();
1093         if content_len > std::u32::MAX as usize {
1094             return Mp4parseStatus::Invalid;
1095         }
1096         let mut data_len = Vec::new();
1097         if data_len.write_u32::<byteorder::NativeEndian>(content_len as u32).is_err() {
1098             return Mp4parseStatus::Io;
1099         }
1100         pssh_data.extend_from_slice(pssh.system_id.as_slice());
1101         pssh_data.extend_from_slice(data_len.as_slice());
1102         pssh_data.extend_from_slice(pssh.box_content.as_slice());
1103     }
1104 
1105     info.data.set_data(pssh_data);
1106 
1107     Mp4parseStatus::Ok
1108 }
1109 
1110 #[cfg(test)]
panic_read(_: *mut u8, _: usize, _: *mut std::os::raw::c_void) -> isize1111 extern fn panic_read(_: *mut u8, _: usize, _: *mut std::os::raw::c_void) -> isize {
1112     panic!("panic_read shouldn't be called in these tests");
1113 }
1114 
1115 #[cfg(test)]
error_read(_: *mut u8, _: usize, _: *mut std::os::raw::c_void) -> isize1116 extern fn error_read(_: *mut u8, _: usize, _: *mut std::os::raw::c_void) -> isize {
1117     -1
1118 }
1119 
1120 #[cfg(test)]
valid_read(buf: *mut u8, size: usize, userdata: *mut std::os::raw::c_void) -> isize1121 extern fn valid_read(buf: *mut u8, size: usize, userdata: *mut std::os::raw::c_void) -> isize {
1122     let input: &mut std::fs::File = unsafe { &mut *(userdata as *mut _) };
1123 
1124     let mut buf = unsafe { std::slice::from_raw_parts_mut(buf, size) };
1125     match input.read(&mut buf) {
1126         Ok(n) => n as isize,
1127         Err(_) => -1,
1128     }
1129 }
1130 
1131 #[test]
new_parser()1132 fn new_parser() {
1133     let mut dummy_value: u32 = 42;
1134     let io = Mp4parseIo {
1135         read: Some(panic_read),
1136         userdata: &mut dummy_value as *mut _ as *mut std::os::raw::c_void,
1137     };
1138     unsafe {
1139         let parser = mp4parse_new(&io);
1140         assert!(!parser.is_null());
1141         mp4parse_free(parser);
1142     }
1143 }
1144 
1145 #[test]
get_track_count_null_parser()1146 fn get_track_count_null_parser() {
1147     unsafe {
1148         let mut count: u32 = 0;
1149         let rv = mp4parse_get_track_count(std::ptr::null(), std::ptr::null_mut());
1150         assert_eq!(rv, Mp4parseStatus::BadArg);
1151         let rv = mp4parse_get_track_count(std::ptr::null(), &mut count);
1152         assert_eq!(rv, Mp4parseStatus::BadArg);
1153     }
1154 }
1155 
1156 #[test]
arg_validation()1157 fn arg_validation() {
1158     unsafe {
1159         // Passing a null Mp4parseIo is an error.
1160         let parser = mp4parse_new(std::ptr::null());
1161         assert!(parser.is_null());
1162 
1163         let null_mut: *mut std::os::raw::c_void = std::ptr::null_mut();
1164 
1165         // Passing an Mp4parseIo with null members is an error.
1166         let io = Mp4parseIo { read: None,
1167                                userdata: null_mut };
1168         let parser = mp4parse_new(&io);
1169         assert!(parser.is_null());
1170 
1171         let io = Mp4parseIo { read: Some(panic_read),
1172                                userdata: null_mut };
1173         let parser = mp4parse_new(&io);
1174         assert!(parser.is_null());
1175 
1176         let mut dummy_value = 42;
1177         let io = Mp4parseIo {
1178             read: None,
1179             userdata: &mut dummy_value as *mut _ as *mut std::os::raw::c_void,
1180         };
1181         let parser = mp4parse_new(&io);
1182         assert!(parser.is_null());
1183 
1184         // Passing a null Mp4parseParser is an error.
1185         assert_eq!(Mp4parseStatus::BadArg, mp4parse_read(std::ptr::null_mut()));
1186 
1187         let mut dummy_info = Mp4parseTrackInfo {
1188             track_type: Mp4parseTrackType::Video,
1189             codec: Mp4parseCodec::Unknown,
1190             track_id: 0,
1191             duration: 0,
1192             media_time: 0,
1193         };
1194         assert_eq!(Mp4parseStatus::BadArg, mp4parse_get_track_info(std::ptr::null_mut(), 0, &mut dummy_info));
1195 
1196         let mut dummy_video = Mp4parseTrackVideoInfo {
1197             display_width: 0,
1198             display_height: 0,
1199             image_width: 0,
1200             image_height: 0,
1201             rotation: 0,
1202             extra_data: Mp4parseByteData::default(),
1203             protected_data: Default::default(),
1204         };
1205         assert_eq!(Mp4parseStatus::BadArg, mp4parse_get_track_video_info(std::ptr::null_mut(), 0, &mut dummy_video));
1206 
1207         let mut dummy_audio = Default::default();
1208         assert_eq!(Mp4parseStatus::BadArg, mp4parse_get_track_audio_info(std::ptr::null_mut(), 0, &mut dummy_audio));
1209     }
1210 }
1211 
1212 #[test]
arg_validation_with_parser()1213 fn arg_validation_with_parser() {
1214     unsafe {
1215         let mut dummy_value = 42;
1216         let io = Mp4parseIo {
1217             read: Some(error_read),
1218             userdata: &mut dummy_value as *mut _ as *mut std::os::raw::c_void,
1219         };
1220         let parser = mp4parse_new(&io);
1221         assert!(!parser.is_null());
1222 
1223         // Our Mp4parseIo read should simply fail with an error.
1224         assert_eq!(Mp4parseStatus::Io, mp4parse_read(parser));
1225 
1226         // The parser is now poisoned and unusable.
1227         assert_eq!(Mp4parseStatus::BadArg,  mp4parse_read(parser));
1228 
1229         // Null info pointers are an error.
1230         assert_eq!(Mp4parseStatus::BadArg, mp4parse_get_track_info(parser, 0, std::ptr::null_mut()));
1231         assert_eq!(Mp4parseStatus::BadArg, mp4parse_get_track_video_info(parser, 0, std::ptr::null_mut()));
1232         assert_eq!(Mp4parseStatus::BadArg, mp4parse_get_track_audio_info(parser, 0, std::ptr::null_mut()));
1233 
1234         let mut dummy_info = Mp4parseTrackInfo {
1235             track_type: Mp4parseTrackType::Video,
1236             codec: Mp4parseCodec::Unknown,
1237             track_id: 0,
1238             duration: 0,
1239             media_time: 0,
1240         };
1241         assert_eq!(Mp4parseStatus::BadArg, mp4parse_get_track_info(parser, 0, &mut dummy_info));
1242 
1243         let mut dummy_video = Mp4parseTrackVideoInfo {
1244             display_width: 0,
1245             display_height: 0,
1246             image_width: 0,
1247             image_height: 0,
1248             rotation: 0,
1249             extra_data: Mp4parseByteData::default(),
1250             protected_data: Default::default(),
1251         };
1252         assert_eq!(Mp4parseStatus::BadArg, mp4parse_get_track_video_info(parser, 0, &mut dummy_video));
1253 
1254         let mut dummy_audio = Default::default();
1255         assert_eq!(Mp4parseStatus::BadArg, mp4parse_get_track_audio_info(parser, 0, &mut dummy_audio));
1256 
1257         mp4parse_free(parser);
1258     }
1259 }
1260 
1261 #[test]
get_track_count_poisoned_parser()1262 fn get_track_count_poisoned_parser() {
1263     unsafe {
1264         let mut dummy_value = 42;
1265         let io = Mp4parseIo {
1266             read: Some(error_read),
1267             userdata: &mut dummy_value as *mut _ as *mut std::os::raw::c_void,
1268         };
1269         let parser = mp4parse_new(&io);
1270         assert!(!parser.is_null());
1271 
1272         // Our Mp4parseIo read should simply fail with an error.
1273         assert_eq!(Mp4parseStatus::Io, mp4parse_read(parser));
1274 
1275         let mut count: u32 = 0;
1276         let rv = mp4parse_get_track_count(parser, &mut count);
1277         assert_eq!(rv, Mp4parseStatus::BadArg);
1278 
1279         mp4parse_free(parser);
1280     }
1281 }
1282 
1283 #[test]
arg_validation_with_data()1284 fn arg_validation_with_data() {
1285     unsafe {
1286         let mut file = std::fs::File::open("../mp4parse/tests/minimal.mp4").unwrap();
1287         let io = Mp4parseIo { read: Some(valid_read),
1288                                userdata: &mut file as *mut _ as *mut std::os::raw::c_void };
1289         let parser = mp4parse_new(&io);
1290         assert!(!parser.is_null());
1291 
1292         assert_eq!(Mp4parseStatus::Ok, mp4parse_read(parser));
1293 
1294         let mut count: u32 = 0;
1295         assert_eq!(Mp4parseStatus::Ok, mp4parse_get_track_count(parser, &mut count));
1296         assert_eq!(2, count);
1297 
1298         let mut info = Mp4parseTrackInfo {
1299             track_type: Mp4parseTrackType::Video,
1300             codec: Mp4parseCodec::Unknown,
1301             track_id: 0,
1302             duration: 0,
1303             media_time: 0,
1304         };
1305         assert_eq!(Mp4parseStatus::Ok, mp4parse_get_track_info(parser, 0, &mut info));
1306         assert_eq!(info.track_type, Mp4parseTrackType::Video);
1307         assert_eq!(info.codec, Mp4parseCodec::Avc);
1308         assert_eq!(info.track_id, 1);
1309         assert_eq!(info.duration, 40000);
1310         assert_eq!(info.media_time, 0);
1311 
1312         assert_eq!(Mp4parseStatus::Ok, mp4parse_get_track_info(parser, 1, &mut info));
1313         assert_eq!(info.track_type, Mp4parseTrackType::Audio);
1314         assert_eq!(info.codec, Mp4parseCodec::Aac);
1315         assert_eq!(info.track_id, 2);
1316         assert_eq!(info.duration, 61333);
1317         assert_eq!(info.media_time, 21333);
1318 
1319         let mut video = Mp4parseTrackVideoInfo {
1320             display_width: 0,
1321             display_height: 0,
1322             image_width: 0,
1323             image_height: 0,
1324             rotation: 0,
1325             extra_data: Mp4parseByteData::default(),
1326             protected_data: Default::default(),
1327         };
1328         assert_eq!(Mp4parseStatus::Ok, mp4parse_get_track_video_info(parser, 0, &mut video));
1329         assert_eq!(video.display_width, 320);
1330         assert_eq!(video.display_height, 240);
1331         assert_eq!(video.image_width, 320);
1332         assert_eq!(video.image_height, 240);
1333 
1334         let mut audio = Default::default();
1335         assert_eq!(Mp4parseStatus::Ok, mp4parse_get_track_audio_info(parser, 1, &mut audio));
1336         assert_eq!(audio.channels, 1);
1337         assert_eq!(audio.bit_depth, 16);
1338         assert_eq!(audio.sample_rate, 48000);
1339 
1340         // Test with an invalid track number.
1341         let mut info = Mp4parseTrackInfo {
1342             track_type: Mp4parseTrackType::Video,
1343             codec: Mp4parseCodec::Unknown,
1344             track_id: 0,
1345             duration: 0,
1346             media_time: 0,
1347         };
1348         assert_eq!(Mp4parseStatus::BadArg, mp4parse_get_track_info(parser, 3, &mut info));
1349         assert_eq!(info.track_type, Mp4parseTrackType::Video);
1350         assert_eq!(info.codec, Mp4parseCodec::Unknown);
1351         assert_eq!(info.track_id, 0);
1352         assert_eq!(info.duration, 0);
1353         assert_eq!(info.media_time, 0);
1354 
1355         let mut video = Mp4parseTrackVideoInfo {
1356             display_width: 0,
1357             display_height: 0,
1358             image_width: 0,
1359             image_height: 0,
1360             rotation: 0,
1361             extra_data: Mp4parseByteData::default(),
1362             protected_data: Default::default(),
1363         };
1364         assert_eq!(Mp4parseStatus::BadArg, mp4parse_get_track_video_info(parser, 3, &mut video));
1365         assert_eq!(video.display_width, 0);
1366         assert_eq!(video.display_height, 0);
1367         assert_eq!(video.image_width, 0);
1368         assert_eq!(video.image_height, 0);
1369 
1370         let mut audio = Default::default();
1371         assert_eq!(Mp4parseStatus::BadArg, mp4parse_get_track_audio_info(parser, 3, &mut audio));
1372         assert_eq!(audio.channels, 0);
1373         assert_eq!(audio.bit_depth, 0);
1374         assert_eq!(audio.sample_rate, 0);
1375 
1376         mp4parse_free(parser);
1377     }
1378 }
1379 
1380 #[test]
rational_scale_overflow()1381 fn rational_scale_overflow() {
1382     assert_eq!(rational_scale::<u64, u64>(17, 3, 1000), Some(5666));
1383     let large = 0x4000_0000_0000_0000;
1384     assert_eq!(rational_scale::<u64, u64>(large, 2, 2), Some(large));
1385     assert_eq!(rational_scale::<u64, u64>(large, 4, 4), Some(large));
1386     assert_eq!(rational_scale::<u64, u64>(large, 2, 8), None);
1387     assert_eq!(rational_scale::<u64, u64>(large, 8, 4), Some(large/2));
1388     assert_eq!(rational_scale::<u64, u64>(large + 1, 4, 4), Some(large+1));
1389     assert_eq!(rational_scale::<u64, u64>(large, 40, 1000), None);
1390 }
1391 
1392 #[test]
media_time_overflow()1393 fn media_time_overflow() {
1394   let scale = MediaTimeScale(90000);
1395   let duration = MediaScaledTime(9007199254710000);
1396   assert_eq!(media_time_to_us(duration, scale), Some(100079991719000000));
1397 }
1398 
1399 #[test]
track_time_overflow()1400 fn track_time_overflow() {
1401   let scale = TrackTimeScale(44100u64, 0);
1402   let duration = TrackScaledTime(4413527634807900u64, 0);
1403   assert_eq!(track_time_to_us(duration, scale), Some(100079991719000000));
1404 }
1405