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 //! let capi_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap();
20 //! let mut file = std::fs::File::open(capi_dir + "/../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 //! let mut parser = std::ptr::null_mut();
26 //! unsafe {
27 //!     let rv = mp4parse_capi::mp4parse_new(&io, &mut parser);
28 //!     assert_eq!(rv, mp4parse_capi::Mp4parseStatus::Ok);
29 //!     assert!(!parser.is_null());
30 //!     mp4parse_capi::mp4parse_free(parser);
31 //! }
32 //! ```
33 
34 // This Source Code Form is subject to the terms of the Mozilla Public
35 // License, v. 2.0. If a copy of the MPL was not distributed with this
36 // file, You can obtain one at https://mozilla.org/MPL/2.0/.
37 
38 extern crate byteorder;
39 extern crate log;
40 extern crate mp4parse;
41 extern crate num_traits;
42 
43 use byteorder::WriteBytesExt;
44 use std::convert::TryFrom;
45 use std::convert::TryInto;
46 
47 use std::io::Read;
48 
49 // Symbols we need from our rust api.
50 use mp4parse::serialize_opus_header;
51 use mp4parse::unstable::{
52     create_sample_table, media_time_to_us, track_time_to_us, CheckedInteger, Indice, Microseconds,
53 };
54 use mp4parse::AudioCodecSpecific;
55 use mp4parse::AvifContext;
56 use mp4parse::CodecType;
57 use mp4parse::Error;
58 use mp4parse::MediaContext;
59 // Re-exported so consumers don't have to depend on mp4parse as well
60 pub use mp4parse::ParseStrictness;
61 use mp4parse::SampleEntry;
62 use mp4parse::TrackType;
63 use mp4parse::TryBox;
64 use mp4parse::TryHashMap;
65 use mp4parse::TryVec;
66 use mp4parse::VideoCodecSpecific;
67 
68 // To ensure we don't use stdlib allocating types by accident
69 #[allow(dead_code)]
70 struct Vec;
71 #[allow(dead_code)]
72 struct Box;
73 #[allow(dead_code)]
74 struct HashMap;
75 #[allow(dead_code)]
76 struct String;
77 
78 #[repr(C)]
79 #[derive(PartialEq, Debug)]
80 pub enum Mp4parseStatus {
81     Ok = 0,
82     BadArg = 1,
83     Invalid = 2,
84     Unsupported = 3,
85     Eof = 4,
86     Io = 5,
87     Oom = 6,
88 }
89 
90 #[repr(C)]
91 #[derive(PartialEq, Debug)]
92 pub enum Mp4parseTrackType {
93     Video = 0,
94     Audio = 1,
95     Metadata = 2,
96 }
97 
98 impl Default for Mp4parseTrackType {
default() -> Self99     fn default() -> Self {
100         Mp4parseTrackType::Video
101     }
102 }
103 
104 #[allow(non_camel_case_types)]
105 #[repr(C)]
106 #[derive(PartialEq, Debug)]
107 pub enum Mp4parseCodec {
108     Unknown,
109     Aac,
110     Flac,
111     Opus,
112     Avc,
113     Vp9,
114     Av1,
115     Mp3,
116     Mp4v,
117     Jpeg, // for QT JPEG atom in video track
118     Ac3,
119     Ec3,
120     Alac,
121     H263,
122     #[cfg(feature = "3gpp")]
123     AMRNB,
124     #[cfg(feature = "3gpp")]
125     AMRWB,
126 }
127 
128 impl Default for Mp4parseCodec {
default() -> Self129     fn default() -> Self {
130         Mp4parseCodec::Unknown
131     }
132 }
133 
134 #[repr(C)]
135 #[derive(PartialEq, Debug)]
136 pub enum Mp4ParseEncryptionSchemeType {
137     None,
138     Cenc,
139     Cbc1,
140     Cens,
141     Cbcs,
142     // Schemes also have a version component. At the time of writing, this does
143     // not impact handling, so we do not expose it. Note that this may need to
144     // be exposed in future, should the spec change.
145 }
146 
147 impl Default for Mp4ParseEncryptionSchemeType {
default() -> Self148     fn default() -> Self {
149         Mp4ParseEncryptionSchemeType::None
150     }
151 }
152 
153 #[repr(C)]
154 #[derive(Default, Debug)]
155 pub struct Mp4parseTrackInfo {
156     pub track_type: Mp4parseTrackType,
157     pub track_id: u32,
158     pub duration: u64,
159     pub media_time: CheckedInteger<i64>, // wants to be u64? understand how elst adjustment works
160                                          // TODO(kinetik): include crypto guff
161                                          // If this changes to u64, we can get rid of the strange
162                                          // impl Sub for CheckedInteger<u64>
163 }
164 
165 #[repr(C)]
166 #[derive(Debug)]
167 pub struct Mp4parseByteData {
168     pub length: usize,
169     // cheddar can't handle generic type, so it needs to be multiple data types here.
170     pub data: *const u8,
171     pub indices: *const Indice,
172 }
173 
174 impl Mp4parseByteData {
with_data(slice: &[u8]) -> Self175     fn with_data(slice: &[u8]) -> Self {
176         Self {
177             length: slice.len(),
178             data: slice.as_ptr(),
179             indices: std::ptr::null(),
180         }
181     }
182 }
183 
184 impl Default for Mp4parseByteData {
default() -> Self185     fn default() -> Self {
186         Self {
187             length: 0,
188             data: std::ptr::null(),
189             indices: std::ptr::null(),
190         }
191     }
192 }
193 
194 impl Mp4parseByteData {
set_data(&mut self, data: &[u8])195     fn set_data(&mut self, data: &[u8]) {
196         self.length = data.len();
197         self.data = data.as_ptr();
198     }
199 
set_indices(&mut self, data: &[Indice])200     fn set_indices(&mut self, data: &[Indice]) {
201         self.length = data.len();
202         self.indices = data.as_ptr();
203     }
204 }
205 
206 #[repr(C)]
207 #[derive(Default)]
208 pub struct Mp4parsePsshInfo {
209     pub data: Mp4parseByteData,
210 }
211 
212 #[repr(u8)]
213 #[derive(Debug, PartialEq)]
214 pub enum OptionalFourCc {
215     None,
216     Some([u8; 4]),
217 }
218 
219 impl Default for OptionalFourCc {
default() -> Self220     fn default() -> Self {
221         Self::None
222     }
223 }
224 
225 #[repr(C)]
226 #[derive(Default, Debug)]
227 pub struct Mp4parseSinfInfo {
228     pub original_format: OptionalFourCc,
229     pub scheme_type: Mp4ParseEncryptionSchemeType,
230     pub is_encrypted: u8,
231     pub iv_size: u8,
232     pub kid: Mp4parseByteData,
233     // Members for pattern encryption schemes, may be 0 (u8) or empty
234     // (Mp4parseByteData) if pattern encryption is not in use
235     pub crypt_byte_block: u8,
236     pub skip_byte_block: u8,
237     pub constant_iv: Mp4parseByteData,
238     // End pattern encryption scheme members
239 }
240 
241 #[repr(C)]
242 #[derive(Default, Debug)]
243 pub struct Mp4parseTrackAudioSampleInfo {
244     pub codec_type: Mp4parseCodec,
245     pub channels: u16,
246     pub bit_depth: u16,
247     pub sample_rate: u32,
248     pub profile: u16,
249     pub extended_profile: u16,
250     pub codec_specific_config: Mp4parseByteData,
251     pub extra_data: Mp4parseByteData,
252     pub protected_data: Mp4parseSinfInfo,
253 }
254 
255 #[repr(C)]
256 #[derive(Debug)]
257 pub struct Mp4parseTrackAudioInfo {
258     pub sample_info_count: u32,
259     pub sample_info: *const Mp4parseTrackAudioSampleInfo,
260 }
261 
262 impl Default for Mp4parseTrackAudioInfo {
default() -> Self263     fn default() -> Self {
264         Self {
265             sample_info_count: 0,
266             sample_info: std::ptr::null(),
267         }
268     }
269 }
270 
271 #[repr(C)]
272 #[derive(Default, Debug)]
273 pub struct Mp4parseTrackVideoSampleInfo {
274     pub codec_type: Mp4parseCodec,
275     pub image_width: u16,
276     pub image_height: u16,
277     pub extra_data: Mp4parseByteData,
278     pub protected_data: Mp4parseSinfInfo,
279 }
280 
281 #[repr(C)]
282 #[derive(Debug)]
283 pub struct Mp4parseTrackVideoInfo {
284     pub display_width: u32,
285     pub display_height: u32,
286     pub rotation: u16,
287     pub sample_info_count: u32,
288     pub sample_info: *const Mp4parseTrackVideoSampleInfo,
289 }
290 
291 impl Default for Mp4parseTrackVideoInfo {
default() -> Self292     fn default() -> Self {
293         Self {
294             display_width: 0,
295             display_height: 0,
296             rotation: 0,
297             sample_info_count: 0,
298             sample_info: std::ptr::null(),
299         }
300     }
301 }
302 
303 #[repr(C)]
304 #[derive(Default, Debug)]
305 pub struct Mp4parseFragmentInfo {
306     pub fragment_duration: u64,
307     // TODO:
308     // info in trex box.
309 }
310 
311 #[derive(Default)]
312 pub struct Mp4parseParser {
313     context: MediaContext,
314     opus_header: TryHashMap<u32, TryVec<u8>>,
315     pssh_data: TryVec<u8>,
316     sample_table: TryHashMap<u32, TryVec<Indice>>,
317     // Store a mapping from track index (not id) to associated sample
318     // descriptions. Because each track has a variable number of sample
319     // descriptions, and because we need the data to live long enough to be
320     // copied out by callers, we store these on the parser struct.
321     audio_track_sample_descriptions: TryHashMap<u32, TryVec<Mp4parseTrackAudioSampleInfo>>,
322     video_track_sample_descriptions: TryHashMap<u32, TryVec<Mp4parseTrackVideoSampleInfo>>,
323 }
324 
325 #[repr(C)]
326 #[derive(Debug)]
327 pub struct Mp4parseAvifImage {
328     pub primary_item: Mp4parseByteData,
329     /// The size of the image; should never be null unless using permissive parsing
330     pub spatial_extents: *const mp4parse::ImageSpatialExtentsProperty,
331     pub image_rotation: mp4parse::ImageRotation,
332     pub image_mirror: *const mp4parse::ImageMirror,
333     /// If no alpha item exists, `.length` will be 0 and `.data` will be null
334     pub alpha_item: Mp4parseByteData,
335     pub premultiplied_alpha: bool,
336 }
337 
338 /// A unified interface for the parsers which have different contexts, but
339 /// share the same pattern of construction. This allows unification of
340 /// argument validation from C and minimizes the surface of unsafe code.
341 trait ContextParser
342 where
343     Self: Sized,
344 {
345     type Context;
346 
with_context(context: Self::Context) -> Self347     fn with_context(context: Self::Context) -> Self;
348 
read<T: Read>(io: &mut T, strictness: ParseStrictness) -> mp4parse::Result<Self::Context>349     fn read<T: Read>(io: &mut T, strictness: ParseStrictness) -> mp4parse::Result<Self::Context>;
350 }
351 
352 impl Mp4parseParser {
context(&self) -> &MediaContext353     fn context(&self) -> &MediaContext {
354         &self.context
355     }
356 
context_mut(&mut self) -> &mut MediaContext357     fn context_mut(&mut self) -> &mut MediaContext {
358         &mut self.context
359     }
360 }
361 
362 impl ContextParser for Mp4parseParser {
363     type Context = MediaContext;
364 
with_context(context: Self::Context) -> Self365     fn with_context(context: Self::Context) -> Self {
366         Self {
367             context,
368             ..Default::default()
369         }
370     }
371 
read<T: Read>(io: &mut T, _strictness: ParseStrictness) -> mp4parse::Result<Self::Context>372     fn read<T: Read>(io: &mut T, _strictness: ParseStrictness) -> mp4parse::Result<Self::Context> {
373         let r = mp4parse::read_mp4(io);
374         log::debug!("mp4parse::read_mp4 -> {:?}", r);
375         r
376     }
377 }
378 
379 pub struct Mp4parseAvifParser {
380     context: AvifContext,
381 }
382 
383 impl Mp4parseAvifParser {
context(&self) -> &AvifContext384     fn context(&self) -> &AvifContext {
385         &self.context
386     }
387 }
388 
389 impl ContextParser for Mp4parseAvifParser {
390     type Context = AvifContext;
391 
with_context(context: Self::Context) -> Self392     fn with_context(context: Self::Context) -> Self {
393         Self { context }
394     }
395 
read<T: Read>(io: &mut T, strictness: ParseStrictness) -> mp4parse::Result<Self::Context>396     fn read<T: Read>(io: &mut T, strictness: ParseStrictness) -> mp4parse::Result<Self::Context> {
397         let r = mp4parse::read_avif(io, strictness);
398         if r.is_err() {
399             log::debug!("{:?}", r);
400         }
401         log::trace!("mp4parse::read_avif -> {:?}", r);
402         r
403     }
404 }
405 
406 #[repr(C)]
407 #[derive(Clone)]
408 pub struct Mp4parseIo {
409     pub read: Option<
410         extern "C" fn(buffer: *mut u8, size: usize, userdata: *mut std::os::raw::c_void) -> isize,
411     >,
412     pub userdata: *mut std::os::raw::c_void,
413 }
414 
415 impl Read for Mp4parseIo {
read(&mut self, buf: &mut [u8]) -> std::io::Result<usize>416     fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
417         if buf.len() > isize::max_value() as usize {
418             return Err(std::io::Error::new(
419                 std::io::ErrorKind::Other,
420                 "buf length overflow in Mp4parseIo Read impl",
421             ));
422         }
423         let rv = self.read.unwrap()(buf.as_mut_ptr(), buf.len(), self.userdata);
424         if rv >= 0 {
425             Ok(rv as usize)
426         } else {
427             Err(std::io::Error::new(
428                 std::io::ErrorKind::Other,
429                 "I/O error in Mp4parseIo Read impl",
430             ))
431         }
432     }
433 }
434 
435 // C API wrapper functions.
436 
437 /// Allocate an `Mp4parseParser*` to read from the supplied `Mp4parseIo` and
438 /// parse the content from the `Mp4parseIo` argument until EOF or error.
439 ///
440 /// # Safety
441 ///
442 /// This function is unsafe because it dereferences the `io` and `parser_out`
443 /// pointers given to it. The caller should ensure that the `Mp4ParseIo`
444 /// struct passed in is a valid pointer. The caller should also ensure the
445 /// members of io are valid: the `read` function should be sanely implemented,
446 /// and the `userdata` pointer should be valid. The `parser_out` should be a
447 /// valid pointer to a location containing a null pointer. Upon successful
448 /// return (`Mp4parseStatus::Ok`), that location will contain the address of
449 /// an `Mp4parseParser` allocated by this function.
450 ///
451 /// To avoid leaking memory, any successful return of this function must be
452 /// paired with a call to `mp4parse_free`. In the event of error, no memory
453 /// will be allocated and `mp4parse_free` must *not* be called.
454 #[no_mangle]
mp4parse_new( io: *const Mp4parseIo, parser_out: *mut *mut Mp4parseParser, ) -> Mp4parseStatus455 pub unsafe extern "C" fn mp4parse_new(
456     io: *const Mp4parseIo,
457     parser_out: *mut *mut Mp4parseParser,
458 ) -> Mp4parseStatus {
459     mp4parse_new_common(io, ParseStrictness::Normal, parser_out)
460 }
461 
462 /// Allocate an `Mp4parseAvifParser*` to read from the supplied `Mp4parseIo`.
463 ///
464 /// See mp4parse_new; this function is identical except that it allocates an
465 /// `Mp4parseAvifParser`, which (when successful) must be paired with a call
466 /// to mp4parse_avif_free.
467 ///
468 /// # Safety
469 ///
470 /// Same as mp4parse_new.
471 #[no_mangle]
mp4parse_avif_new( io: *const Mp4parseIo, strictness: ParseStrictness, parser_out: *mut *mut Mp4parseAvifParser, ) -> Mp4parseStatus472 pub unsafe extern "C" fn mp4parse_avif_new(
473     io: *const Mp4parseIo,
474     strictness: ParseStrictness,
475     parser_out: *mut *mut Mp4parseAvifParser,
476 ) -> Mp4parseStatus {
477     mp4parse_new_common(io, strictness, parser_out)
478 }
479 
mp4parse_new_common<P: ContextParser>( io: *const Mp4parseIo, strictness: ParseStrictness, parser_out: *mut *mut P, ) -> Mp4parseStatus480 unsafe fn mp4parse_new_common<P: ContextParser>(
481     io: *const Mp4parseIo,
482     strictness: ParseStrictness,
483     parser_out: *mut *mut P,
484 ) -> Mp4parseStatus {
485     // Validate arguments from C.
486     if io.is_null()
487         || (*io).userdata.is_null()
488         || (*io).read.is_none()
489         || parser_out.is_null()
490         || !(*parser_out).is_null()
491     {
492         Mp4parseStatus::BadArg
493     } else {
494         match mp4parse_new_common_safe(&mut (*io).clone(), strictness) {
495             Ok(parser) => {
496                 *parser_out = parser;
497                 Mp4parseStatus::Ok
498             }
499             Err(status) => status,
500         }
501     }
502 }
503 
mp4parse_new_common_safe<T: Read, P: ContextParser>( io: &mut T, strictness: ParseStrictness, ) -> Result<*mut P, Mp4parseStatus>504 fn mp4parse_new_common_safe<T: Read, P: ContextParser>(
505     io: &mut T,
506     strictness: ParseStrictness,
507 ) -> Result<*mut P, Mp4parseStatus> {
508     P::read(io, strictness)
509         .map(P::with_context)
510         .and_then(|x| TryBox::try_new(x).map_err(mp4parse::Error::from))
511         .map(TryBox::into_raw)
512         .map_err(Mp4parseStatus::from)
513 }
514 
515 impl From<mp4parse::Error> for Mp4parseStatus {
from(error: mp4parse::Error) -> Self516     fn from(error: mp4parse::Error) -> Self {
517         match error {
518             Error::NoMoov | Error::InvalidData(_) => Mp4parseStatus::Invalid,
519             Error::Unsupported(_) => Mp4parseStatus::Unsupported,
520             Error::UnexpectedEOF => Mp4parseStatus::Eof,
521             Error::Io(_) => {
522                 // Getting std::io::ErrorKind::UnexpectedEof is normal
523                 // but our From trait implementation should have converted
524                 // those to our Error::UnexpectedEOF variant.
525                 Mp4parseStatus::Io
526             }
527             Error::OutOfMemory => Mp4parseStatus::Oom,
528         }
529     }
530 }
531 
532 impl From<Result<(), Mp4parseStatus>> for Mp4parseStatus {
from(result: Result<(), Mp4parseStatus>) -> Self533     fn from(result: Result<(), Mp4parseStatus>) -> Self {
534         match result {
535             Ok(()) => Mp4parseStatus::Ok,
536             Err(Mp4parseStatus::Ok) => unreachable!(),
537             Err(e) => e,
538         }
539     }
540 }
541 
542 impl From<fallible_collections::TryReserveError> for Mp4parseStatus {
from(_: fallible_collections::TryReserveError) -> Self543     fn from(_: fallible_collections::TryReserveError) -> Self {
544         Mp4parseStatus::Oom
545     }
546 }
547 
548 /// Free an `Mp4parseParser*` allocated by `mp4parse_new()`.
549 ///
550 /// # Safety
551 ///
552 /// This function is unsafe because it creates a box from a raw pointer.
553 /// Callers should ensure that the parser pointer points to a valid
554 /// `Mp4parseParser` created by `mp4parse_new`.
555 #[no_mangle]
mp4parse_free(parser: *mut Mp4parseParser)556 pub unsafe extern "C" fn mp4parse_free(parser: *mut Mp4parseParser) {
557     assert!(!parser.is_null());
558     let _ = TryBox::from_raw(parser);
559 }
560 
561 /// Free an `Mp4parseAvifParser*` allocated by `mp4parse_avif_new()`.
562 ///
563 /// # Safety
564 ///
565 /// This function is unsafe because it creates a box from a raw pointer.
566 /// Callers should ensure that the parser pointer points to a valid
567 /// `Mp4parseAvifParser` created by `mp4parse_avif_new`.
568 #[no_mangle]
mp4parse_avif_free(parser: *mut Mp4parseAvifParser)569 pub unsafe extern "C" fn mp4parse_avif_free(parser: *mut Mp4parseAvifParser) {
570     assert!(!parser.is_null());
571     let _ = TryBox::from_raw(parser);
572 }
573 
574 /// Return the number of tracks parsed by previous `mp4parse_read()` call.
575 ///
576 /// # Safety
577 ///
578 /// This function is unsafe because it dereferences both the parser and count
579 /// raw pointers passed into it. Callers should ensure the parser pointer
580 /// points to a valid `Mp4parseParser`, and that the count pointer points an
581 /// appropriate memory location to have a `u32` written to.
582 #[no_mangle]
mp4parse_get_track_count( parser: *const Mp4parseParser, count: *mut u32, ) -> Mp4parseStatus583 pub unsafe extern "C" fn mp4parse_get_track_count(
584     parser: *const Mp4parseParser,
585     count: *mut u32,
586 ) -> Mp4parseStatus {
587     // Validate arguments from C.
588     if parser.is_null() || count.is_null() {
589         return Mp4parseStatus::BadArg;
590     }
591     let context = (*parser).context();
592 
593     // Make sure the track count fits in a u32.
594     if context.tracks.len() > u32::max_value() as usize {
595         return Mp4parseStatus::Invalid;
596     }
597     *count = context.tracks.len() as u32;
598     Mp4parseStatus::Ok
599 }
600 
601 /// Fill the supplied `Mp4parseTrackInfo` with metadata for `track`.
602 ///
603 /// # Safety
604 ///
605 /// This function is unsafe because it dereferences the the parser and info raw
606 /// pointers passed to it. Callers should ensure the parser pointer points to a
607 /// valid `Mp4parseParser` and that the info pointer points to a valid
608 /// `Mp4parseTrackInfo`.
609 #[no_mangle]
mp4parse_get_track_info( parser: *mut Mp4parseParser, track_index: u32, info: *mut Mp4parseTrackInfo, ) -> Mp4parseStatus610 pub unsafe extern "C" fn mp4parse_get_track_info(
611     parser: *mut Mp4parseParser,
612     track_index: u32,
613     info: *mut Mp4parseTrackInfo,
614 ) -> Mp4parseStatus {
615     if parser.is_null() || info.is_null() {
616         return Mp4parseStatus::BadArg;
617     }
618 
619     // Initialize fields to default values to ensure all fields are always valid.
620     *info = Default::default();
621 
622     let context = (*parser).context_mut();
623     let track_index: usize = track_index as usize;
624     let info: &mut Mp4parseTrackInfo = &mut *info;
625 
626     if track_index >= context.tracks.len() {
627         return Mp4parseStatus::BadArg;
628     }
629 
630     info.track_type = match context.tracks[track_index].track_type {
631         TrackType::Video => Mp4parseTrackType::Video,
632         TrackType::Audio => Mp4parseTrackType::Audio,
633         TrackType::Metadata => Mp4parseTrackType::Metadata,
634         TrackType::Unknown => return Mp4parseStatus::Unsupported,
635     };
636 
637     let track = &context.tracks[track_index];
638 
639     if let (Some(track_timescale), Some(context_timescale)) = (track.timescale, context.timescale) {
640         let media_time: CheckedInteger<_> = match track
641             .media_time
642             .map_or(Some(Microseconds(0)), |media_time| {
643                 track_time_to_us(media_time, track_timescale)
644             }) {
645             Some(time) => time.0.into(),
646             None => return Mp4parseStatus::Invalid,
647         };
648         let empty_duration: CheckedInteger<_> = match track
649             .empty_duration
650             .map_or(Some(Microseconds(0)), |empty_duration| {
651                 media_time_to_us(empty_duration, context_timescale)
652             }) {
653             Some(time) => time.0.into(),
654             None => return Mp4parseStatus::Invalid,
655         };
656         info.media_time = match media_time - empty_duration {
657             Some(difference) => difference,
658             None => return Mp4parseStatus::Invalid,
659         };
660 
661         if let Some(track_duration) = track.duration {
662             match track_time_to_us(track_duration, track_timescale) {
663                 Some(duration) => info.duration = duration.0,
664                 None => return Mp4parseStatus::Invalid,
665             }
666         } else {
667             // Duration unknown; stagefright returns 0 for this.
668             info.duration = 0
669         }
670     } else {
671         return Mp4parseStatus::Invalid;
672     }
673 
674     info.track_id = match track.track_id {
675         Some(track_id) => track_id,
676         None => return Mp4parseStatus::Invalid,
677     };
678 
679     Mp4parseStatus::Ok
680 }
681 
682 /// Fill the supplied `Mp4parseTrackAudioInfo` with metadata for `track`.
683 ///
684 /// # Safety
685 ///
686 /// This function is unsafe because it dereferences the the parser and info raw
687 /// pointers passed to it. Callers should ensure the parser pointer points to a
688 /// valid `Mp4parseParser` and that the info pointer points to a valid
689 /// `Mp4parseTrackAudioInfo`.
690 #[no_mangle]
mp4parse_get_track_audio_info( parser: *mut Mp4parseParser, track_index: u32, info: *mut Mp4parseTrackAudioInfo, ) -> Mp4parseStatus691 pub unsafe extern "C" fn mp4parse_get_track_audio_info(
692     parser: *mut Mp4parseParser,
693     track_index: u32,
694     info: *mut Mp4parseTrackAudioInfo,
695 ) -> Mp4parseStatus {
696     if parser.is_null() || info.is_null() {
697         return Mp4parseStatus::BadArg;
698     }
699 
700     // Initialize fields to default values to ensure all fields are always valid.
701     *info = Default::default();
702 
703     get_track_audio_info(&mut *parser, track_index, &mut *info).into()
704 }
705 
get_track_audio_info( parser: &mut Mp4parseParser, track_index: u32, info: &mut Mp4parseTrackAudioInfo, ) -> Result<(), Mp4parseStatus>706 fn get_track_audio_info(
707     parser: &mut Mp4parseParser,
708     track_index: u32,
709     info: &mut Mp4parseTrackAudioInfo,
710 ) -> Result<(), Mp4parseStatus> {
711     let Mp4parseParser {
712         context,
713         opus_header,
714         ..
715     } = parser;
716 
717     if track_index as usize >= context.tracks.len() {
718         return Err(Mp4parseStatus::BadArg);
719     }
720 
721     let track = &context.tracks[track_index as usize];
722 
723     if track.track_type != TrackType::Audio {
724         return Err(Mp4parseStatus::Invalid);
725     }
726 
727     // Handle track.stsd
728     let stsd = match track.stsd {
729         Some(ref stsd) => stsd,
730         None => return Err(Mp4parseStatus::Invalid), // Stsd should be present
731     };
732 
733     if stsd.descriptions.is_empty() {
734         return Err(Mp4parseStatus::Invalid); // Should have at least 1 description
735     }
736 
737     let mut audio_sample_infos = TryVec::with_capacity(stsd.descriptions.len())?;
738     for description in stsd.descriptions.iter() {
739         let mut sample_info = Mp4parseTrackAudioSampleInfo::default();
740         let audio = match description {
741             SampleEntry::Audio(a) => a,
742             _ => return Err(Mp4parseStatus::Invalid),
743         };
744 
745         // UNKNOWN for unsupported format.
746         sample_info.codec_type = match audio.codec_specific {
747             AudioCodecSpecific::OpusSpecificBox(_) => Mp4parseCodec::Opus,
748             AudioCodecSpecific::FLACSpecificBox(_) => Mp4parseCodec::Flac,
749             AudioCodecSpecific::ES_Descriptor(ref esds) if esds.audio_codec == CodecType::AAC => {
750                 Mp4parseCodec::Aac
751             }
752             AudioCodecSpecific::ES_Descriptor(ref esds) if esds.audio_codec == CodecType::MP3 => {
753                 Mp4parseCodec::Mp3
754             }
755             AudioCodecSpecific::ES_Descriptor(_) | AudioCodecSpecific::LPCM => {
756                 Mp4parseCodec::Unknown
757             }
758             AudioCodecSpecific::MP3 => Mp4parseCodec::Mp3,
759             AudioCodecSpecific::ALACSpecificBox(_) => Mp4parseCodec::Alac,
760             #[cfg(feature = "3gpp")]
761             AudioCodecSpecific::AMRSpecificBox(_) => {
762                 if audio.codec_type == CodecType::AMRNB {
763                     Mp4parseCodec::AMRNB
764                 } else {
765                     Mp4parseCodec::AMRWB
766                 }
767             }
768         };
769         sample_info.channels = audio.channelcount as u16;
770         sample_info.bit_depth = audio.samplesize;
771         sample_info.sample_rate = audio.samplerate as u32;
772         // sample_info.profile is handled below on a per case basis
773 
774         match audio.codec_specific {
775             AudioCodecSpecific::ES_Descriptor(ref esds) => {
776                 if esds.codec_esds.len() > std::u32::MAX as usize {
777                     return Err(Mp4parseStatus::Invalid);
778                 }
779                 sample_info.extra_data.length = esds.codec_esds.len();
780                 sample_info.extra_data.data = esds.codec_esds.as_ptr();
781                 sample_info.codec_specific_config.length = esds.decoder_specific_data.len();
782                 sample_info.codec_specific_config.data = esds.decoder_specific_data.as_ptr();
783                 if let Some(rate) = esds.audio_sample_rate {
784                     sample_info.sample_rate = rate;
785                 }
786                 if let Some(channels) = esds.audio_channel_count {
787                     sample_info.channels = channels;
788                 }
789                 if let Some(profile) = esds.audio_object_type {
790                     sample_info.profile = profile;
791                 }
792                 sample_info.extended_profile = match esds.extended_audio_object_type {
793                     Some(extended_profile) => extended_profile,
794                     _ => sample_info.profile,
795                 };
796             }
797             AudioCodecSpecific::FLACSpecificBox(ref flac) => {
798                 // Return the STREAMINFO metadata block in the codec_specific.
799                 let streaminfo = &flac.blocks[0];
800                 if streaminfo.block_type != 0 || streaminfo.data.len() != 34 {
801                     return Err(Mp4parseStatus::Invalid);
802                 }
803                 sample_info.codec_specific_config.length = streaminfo.data.len();
804                 sample_info.codec_specific_config.data = streaminfo.data.as_ptr();
805             }
806             AudioCodecSpecific::OpusSpecificBox(ref opus) => {
807                 let mut v = TryVec::new();
808                 match serialize_opus_header(opus, &mut v) {
809                     Err(_) => {
810                         return Err(Mp4parseStatus::Invalid);
811                     }
812                     Ok(_) => {
813                         opus_header.insert(track_index, v)?;
814                         if let Some(v) = opus_header.get(&track_index) {
815                             if v.len() > std::u32::MAX as usize {
816                                 return Err(Mp4parseStatus::Invalid);
817                             }
818                             sample_info.codec_specific_config.length = v.len();
819                             sample_info.codec_specific_config.data = v.as_ptr();
820                         }
821                     }
822                 }
823             }
824             AudioCodecSpecific::ALACSpecificBox(ref alac) => {
825                 sample_info.codec_specific_config.length = alac.data.len();
826                 sample_info.codec_specific_config.data = alac.data.as_ptr();
827             }
828             AudioCodecSpecific::MP3 | AudioCodecSpecific::LPCM => (),
829             #[cfg(feature = "3gpp")]
830             AudioCodecSpecific::AMRSpecificBox(_) => (),
831         }
832 
833         if let Some(p) = audio
834             .protection_info
835             .iter()
836             .find(|sinf| sinf.tenc.is_some())
837         {
838             sample_info.protected_data.original_format =
839                 OptionalFourCc::Some(p.original_format.value);
840             sample_info.protected_data.scheme_type = match p.scheme_type {
841                 Some(ref scheme_type_box) => {
842                     match scheme_type_box.scheme_type.value.as_ref() {
843                         b"cenc" => Mp4ParseEncryptionSchemeType::Cenc,
844                         b"cbcs" => Mp4ParseEncryptionSchemeType::Cbcs,
845                         // We don't support other schemes, and shouldn't reach
846                         // this case. Try to gracefully handle by treating as
847                         // no encryption case.
848                         _ => Mp4ParseEncryptionSchemeType::None,
849                     }
850                 }
851                 None => Mp4ParseEncryptionSchemeType::None,
852             };
853             if let Some(ref tenc) = p.tenc {
854                 sample_info.protected_data.is_encrypted = tenc.is_encrypted;
855                 sample_info.protected_data.iv_size = tenc.iv_size;
856                 sample_info.protected_data.kid.set_data(&(tenc.kid));
857                 sample_info.protected_data.crypt_byte_block =
858                     tenc.crypt_byte_block_count.unwrap_or(0);
859                 sample_info.protected_data.skip_byte_block =
860                     tenc.skip_byte_block_count.unwrap_or(0);
861                 if let Some(ref iv_vec) = tenc.constant_iv {
862                     if iv_vec.len() > std::u32::MAX as usize {
863                         return Err(Mp4parseStatus::Invalid);
864                     }
865                     sample_info.protected_data.constant_iv.set_data(iv_vec);
866                 };
867             }
868         }
869         audio_sample_infos.push(sample_info)?;
870     }
871 
872     parser
873         .audio_track_sample_descriptions
874         .insert(track_index, audio_sample_infos)?;
875     match parser.audio_track_sample_descriptions.get(&track_index) {
876         Some(sample_info) => {
877             if sample_info.len() > std::u32::MAX as usize {
878                 // Should never happen due to upper limits on number of sample
879                 // descriptions a track can have, but lets be safe.
880                 return Err(Mp4parseStatus::Invalid);
881             }
882             info.sample_info_count = sample_info.len() as u32;
883             info.sample_info = sample_info.as_ptr();
884         }
885         None => return Err(Mp4parseStatus::Invalid), // Shouldn't happen, we just inserted the info!
886     }
887 
888     Ok(())
889 }
890 
891 /// Fill the supplied `Mp4parseTrackVideoInfo` with metadata for `track`.
892 ///
893 /// # Safety
894 ///
895 /// This function is unsafe because it dereferences the the parser and info raw
896 /// pointers passed to it. Callers should ensure the parser pointer points to a
897 /// valid `Mp4parseParser` and that the info pointer points to a valid
898 /// `Mp4parseTrackVideoInfo`.
899 #[no_mangle]
mp4parse_get_track_video_info( parser: *mut Mp4parseParser, track_index: u32, info: *mut Mp4parseTrackVideoInfo, ) -> Mp4parseStatus900 pub unsafe extern "C" fn mp4parse_get_track_video_info(
901     parser: *mut Mp4parseParser,
902     track_index: u32,
903     info: *mut Mp4parseTrackVideoInfo,
904 ) -> Mp4parseStatus {
905     if parser.is_null() || info.is_null() {
906         return Mp4parseStatus::BadArg;
907     }
908 
909     // Initialize fields to default values to ensure all fields are always valid.
910     *info = Default::default();
911 
912     mp4parse_get_track_video_info_safe(&mut *parser, track_index, &mut *info).into()
913 }
914 
mp4parse_get_track_video_info_safe( parser: &mut Mp4parseParser, track_index: u32, info: &mut Mp4parseTrackVideoInfo, ) -> Result<(), Mp4parseStatus>915 fn mp4parse_get_track_video_info_safe(
916     parser: &mut Mp4parseParser,
917     track_index: u32,
918     info: &mut Mp4parseTrackVideoInfo,
919 ) -> Result<(), Mp4parseStatus> {
920     let context = parser.context();
921 
922     if track_index as usize >= context.tracks.len() {
923         return Err(Mp4parseStatus::BadArg);
924     }
925 
926     let track = &context.tracks[track_index as usize];
927 
928     if track.track_type != TrackType::Video {
929         return Err(Mp4parseStatus::Invalid);
930     }
931 
932     // Handle track.tkhd
933     if let Some(ref tkhd) = track.tkhd {
934         info.display_width = tkhd.width >> 16; // 16.16 fixed point
935         info.display_height = tkhd.height >> 16; // 16.16 fixed point
936         let matrix = (
937             tkhd.matrix.a >> 16,
938             tkhd.matrix.b >> 16,
939             tkhd.matrix.c >> 16,
940             tkhd.matrix.d >> 16,
941         );
942         info.rotation = match matrix {
943             (0, 1, -1, 0) => 90,   // rotate 90 degrees
944             (-1, 0, 0, -1) => 180, // rotate 180 degrees
945             (0, -1, 1, 0) => 270,  // rotate 270 degrees
946             _ => 0,
947         };
948     } else {
949         return Err(Mp4parseStatus::Invalid);
950     }
951 
952     // Handle track.stsd
953     let stsd = match track.stsd {
954         Some(ref stsd) => stsd,
955         None => return Err(Mp4parseStatus::Invalid), // Stsd should be present
956     };
957 
958     if stsd.descriptions.is_empty() {
959         return Err(Mp4parseStatus::Invalid); // Should have at least 1 description
960     }
961 
962     let mut video_sample_infos = TryVec::with_capacity(stsd.descriptions.len())?;
963     for description in stsd.descriptions.iter() {
964         let mut sample_info = Mp4parseTrackVideoSampleInfo::default();
965         let video = match description {
966             SampleEntry::Video(v) => v,
967             _ => return Err(Mp4parseStatus::Invalid),
968         };
969 
970         // UNKNOWN for unsupported format.
971         sample_info.codec_type = match video.codec_specific {
972             VideoCodecSpecific::VPxConfig(_) => Mp4parseCodec::Vp9,
973             VideoCodecSpecific::AV1Config(_) => Mp4parseCodec::Av1,
974             VideoCodecSpecific::AVCConfig(_) => Mp4parseCodec::Avc,
975             VideoCodecSpecific::H263Config(_) => Mp4parseCodec::H263,
976             #[cfg(feature = "mp4v")]
977             VideoCodecSpecific::ESDSConfig(_) => Mp4parseCodec::Mp4v,
978             #[cfg(not(feature = "mp4v"))]
979             VideoCodecSpecific::ESDSConfig(_) =>
980             // MP4V (14496-2) video is unsupported.
981             {
982                 Mp4parseCodec::Unknown
983             }
984         };
985         sample_info.image_width = video.width;
986         sample_info.image_height = video.height;
987 
988         match video.codec_specific {
989             VideoCodecSpecific::AV1Config(ref config) => {
990                 sample_info.extra_data.set_data(&config.raw_config);
991             }
992             VideoCodecSpecific::AVCConfig(ref data) | VideoCodecSpecific::ESDSConfig(ref data) => {
993                 sample_info.extra_data.set_data(data);
994             }
995             _ => {}
996         }
997 
998         if let Some(p) = video
999             .protection_info
1000             .iter()
1001             .find(|sinf| sinf.tenc.is_some())
1002         {
1003             sample_info.protected_data.original_format =
1004                 OptionalFourCc::Some(p.original_format.value);
1005             sample_info.protected_data.scheme_type = match p.scheme_type {
1006                 Some(ref scheme_type_box) => {
1007                     match scheme_type_box.scheme_type.value.as_ref() {
1008                         b"cenc" => Mp4ParseEncryptionSchemeType::Cenc,
1009                         b"cbcs" => Mp4ParseEncryptionSchemeType::Cbcs,
1010                         // We don't support other schemes, and shouldn't reach
1011                         // this case. Try to gracefully handle by treating as
1012                         // no encryption case.
1013                         _ => Mp4ParseEncryptionSchemeType::None,
1014                     }
1015                 }
1016                 None => Mp4ParseEncryptionSchemeType::None,
1017             };
1018             if let Some(ref tenc) = p.tenc {
1019                 sample_info.protected_data.is_encrypted = tenc.is_encrypted;
1020                 sample_info.protected_data.iv_size = tenc.iv_size;
1021                 sample_info.protected_data.kid.set_data(&(tenc.kid));
1022                 sample_info.protected_data.crypt_byte_block =
1023                     tenc.crypt_byte_block_count.unwrap_or(0);
1024                 sample_info.protected_data.skip_byte_block =
1025                     tenc.skip_byte_block_count.unwrap_or(0);
1026                 if let Some(ref iv_vec) = tenc.constant_iv {
1027                     if iv_vec.len() > std::u32::MAX as usize {
1028                         return Err(Mp4parseStatus::Invalid);
1029                     }
1030                     sample_info.protected_data.constant_iv.set_data(iv_vec);
1031                 };
1032             }
1033         }
1034         video_sample_infos.push(sample_info)?;
1035     }
1036 
1037     parser
1038         .video_track_sample_descriptions
1039         .insert(track_index, video_sample_infos)?;
1040     match parser.video_track_sample_descriptions.get(&track_index) {
1041         Some(sample_info) => {
1042             if sample_info.len() > std::u32::MAX as usize {
1043                 // Should never happen due to upper limits on number of sample
1044                 // descriptions a track can have, but lets be safe.
1045                 return Err(Mp4parseStatus::Invalid);
1046             }
1047             info.sample_info_count = sample_info.len() as u32;
1048             info.sample_info = sample_info.as_ptr();
1049         }
1050         None => return Err(Mp4parseStatus::Invalid), // Shouldn't happen, we just inserted the info!
1051     }
1052     Ok(())
1053 }
1054 
1055 /// Return a pointer to the primary item parsed by previous `mp4parse_avif_new()` call.
1056 ///
1057 /// # Safety
1058 ///
1059 /// This function is unsafe because it dereferences both the parser and
1060 /// avif_image raw pointers passed into it. Callers should ensure the parser
1061 /// pointer points to a valid `Mp4parseAvifParser`, and that the avif_image
1062 /// pointer points to a valid `Mp4parseAvifImage`. If there was not a previous
1063 /// successful call to `mp4parse_avif_read()`, no guarantees are made as to
1064 /// the state of `avif_image`. If `avif_image.alpha_item` is set to a
1065 /// positive `length` and non-null `data`, then the `avif_image` contains an
1066 /// valid alpha channel data. Otherwise, the image is opaque.
1067 #[no_mangle]
mp4parse_avif_get_image( parser: *const Mp4parseAvifParser, avif_image: *mut Mp4parseAvifImage, ) -> Mp4parseStatus1068 pub unsafe extern "C" fn mp4parse_avif_get_image(
1069     parser: *const Mp4parseAvifParser,
1070     avif_image: *mut Mp4parseAvifImage,
1071 ) -> Mp4parseStatus {
1072     if parser.is_null() || avif_image.is_null() {
1073         return Mp4parseStatus::BadArg;
1074     }
1075 
1076     *avif_image = mp4parse_avif_get_image_safe(&*parser);
1077 
1078     Mp4parseStatus::Ok
1079 }
1080 
mp4parse_avif_get_image_safe(parser: &Mp4parseAvifParser) -> Mp4parseAvifImage1081 pub fn mp4parse_avif_get_image_safe(parser: &Mp4parseAvifParser) -> Mp4parseAvifImage {
1082     let context = parser.context();
1083 
1084     Mp4parseAvifImage {
1085         primary_item: Mp4parseByteData::with_data(context.primary_item()),
1086         spatial_extents: context.spatial_extents_ptr(),
1087         image_rotation: context.image_rotation(),
1088         image_mirror: context.image_mirror_ptr(),
1089         alpha_item: context
1090             .alpha_item()
1091             .map(Mp4parseByteData::with_data)
1092             .unwrap_or_default(),
1093         premultiplied_alpha: context.premultiplied_alpha,
1094     }
1095 }
1096 
1097 /// Fill the supplied `Mp4parseByteData` with index information from `track`.
1098 ///
1099 /// # Safety
1100 ///
1101 /// This function is unsafe because it dereferences the the parser and indices
1102 /// raw pointers passed to it. Callers should ensure the parser pointer points
1103 /// to a valid `Mp4parseParser` and that the indices pointer points to a valid
1104 /// `Mp4parseByteData`.
1105 #[no_mangle]
mp4parse_get_indice_table( parser: *mut Mp4parseParser, track_id: u32, indices: *mut Mp4parseByteData, ) -> Mp4parseStatus1106 pub unsafe extern "C" fn mp4parse_get_indice_table(
1107     parser: *mut Mp4parseParser,
1108     track_id: u32,
1109     indices: *mut Mp4parseByteData,
1110 ) -> Mp4parseStatus {
1111     if parser.is_null() {
1112         return Mp4parseStatus::BadArg;
1113     }
1114 
1115     // Initialize fields to default values to ensure all fields are always valid.
1116     *indices = Default::default();
1117 
1118     get_indice_table(&mut *parser, track_id, &mut *indices).into()
1119 }
1120 
get_indice_table( parser: &mut Mp4parseParser, track_id: u32, indices: &mut Mp4parseByteData, ) -> Result<(), Mp4parseStatus>1121 fn get_indice_table(
1122     parser: &mut Mp4parseParser,
1123     track_id: u32,
1124     indices: &mut Mp4parseByteData,
1125 ) -> Result<(), Mp4parseStatus> {
1126     let Mp4parseParser {
1127         context,
1128         sample_table: index_table,
1129         ..
1130     } = parser;
1131     let tracks = &context.tracks;
1132     let track = match tracks.iter().find(|track| track.track_id == Some(track_id)) {
1133         Some(t) => t,
1134         _ => return Err(Mp4parseStatus::Invalid),
1135     };
1136 
1137     if let Some(v) = index_table.get(&track_id) {
1138         indices.set_indices(v);
1139         return Ok(());
1140     }
1141 
1142     let media_time = match (&track.media_time, &track.timescale) {
1143         (&Some(t), &Some(s)) => track_time_to_us(t, s)
1144             .and_then(|v| i64::try_from(v.0).ok())
1145             .map(Into::into),
1146         _ => None,
1147     };
1148 
1149     let empty_duration: Option<CheckedInteger<_>> =
1150         match (&track.empty_duration, &context.timescale) {
1151             (&Some(e), &Some(s)) => media_time_to_us(e, s)
1152                 .and_then(|v| i64::try_from(v.0).ok())
1153                 .map(Into::into),
1154             _ => None,
1155         };
1156 
1157     // Find the track start offset time from 'elst'.
1158     // 'media_time' maps start time onward, 'empty_duration' adds time offset
1159     // before first frame is displayed.
1160     let offset_time = match (empty_duration, media_time) {
1161         (Some(e), Some(m)) => (e - m).ok_or(Err(Mp4parseStatus::Invalid))?,
1162         (Some(e), None) => e,
1163         (None, Some(m)) => m,
1164         _ => 0.into(),
1165     };
1166 
1167     if let Some(v) = create_sample_table(track, offset_time) {
1168         indices.set_indices(&v);
1169         index_table.insert(track_id, v)?;
1170         return Ok(());
1171     }
1172 
1173     Err(Mp4parseStatus::Invalid)
1174 }
1175 
1176 /// Fill the supplied `Mp4parseFragmentInfo` with metadata from fragmented file.
1177 ///
1178 /// # Safety
1179 ///
1180 /// This function is unsafe because it dereferences the the parser and
1181 /// info raw pointers passed to it. Callers should ensure the parser
1182 /// pointer points to a valid `Mp4parseParser` and that the info pointer points
1183 /// to a valid `Mp4parseFragmentInfo`.
1184 
1185 #[no_mangle]
mp4parse_get_fragment_info( parser: *mut Mp4parseParser, info: *mut Mp4parseFragmentInfo, ) -> Mp4parseStatus1186 pub unsafe extern "C" fn mp4parse_get_fragment_info(
1187     parser: *mut Mp4parseParser,
1188     info: *mut Mp4parseFragmentInfo,
1189 ) -> Mp4parseStatus {
1190     if parser.is_null() || info.is_null() {
1191         return Mp4parseStatus::BadArg;
1192     }
1193 
1194     // Initialize fields to default values to ensure all fields are always valid.
1195     *info = Default::default();
1196 
1197     let context = (*parser).context();
1198     let info: &mut Mp4parseFragmentInfo = &mut *info;
1199 
1200     info.fragment_duration = 0;
1201 
1202     let duration = match context.mvex {
1203         Some(ref mvex) => mvex.fragment_duration,
1204         None => return Mp4parseStatus::Invalid,
1205     };
1206 
1207     if let (Some(time), Some(scale)) = (duration, context.timescale) {
1208         info.fragment_duration = match media_time_to_us(time, scale) {
1209             Some(time_us) => time_us.0 as u64,
1210             None => return Mp4parseStatus::Invalid,
1211         }
1212     }
1213 
1214     Mp4parseStatus::Ok
1215 }
1216 
1217 /// Determine if an mp4 file is fragmented. A fragmented file needs mvex table
1218 /// and contains no data in stts, stsc, and stco boxes.
1219 ///
1220 /// # Safety
1221 ///
1222 /// This function is unsafe because it dereferences the the parser and
1223 /// fragmented raw pointers passed to it. Callers should ensure the parser
1224 /// pointer points to a valid `Mp4parseParser` and that the fragmented pointer
1225 /// points to an appropriate memory location to have a `u8` written to.
1226 #[no_mangle]
mp4parse_is_fragmented( parser: *mut Mp4parseParser, track_id: u32, fragmented: *mut u8, ) -> Mp4parseStatus1227 pub unsafe extern "C" fn mp4parse_is_fragmented(
1228     parser: *mut Mp4parseParser,
1229     track_id: u32,
1230     fragmented: *mut u8,
1231 ) -> Mp4parseStatus {
1232     if parser.is_null() {
1233         return Mp4parseStatus::BadArg;
1234     }
1235 
1236     let context = (*parser).context_mut();
1237     let tracks = &context.tracks;
1238     (*fragmented) = false as u8;
1239 
1240     if context.mvex.is_none() {
1241         return Mp4parseStatus::Ok;
1242     }
1243 
1244     // check sample tables.
1245     let mut iter = tracks.iter();
1246     iter.find(|track| track.track_id == Some(track_id))
1247         .map_or(Mp4parseStatus::BadArg, |track| {
1248             match (&track.stsc, &track.stco, &track.stts) {
1249                 (&Some(ref stsc), &Some(ref stco), &Some(ref stts))
1250                     if stsc.samples.is_empty()
1251                         && stco.offsets.is_empty()
1252                         && stts.samples.is_empty() =>
1253                 {
1254                     (*fragmented) = true as u8
1255                 }
1256                 _ => {}
1257             };
1258             Mp4parseStatus::Ok
1259         })
1260 }
1261 
1262 /// Get 'pssh' system id and 'pssh' box content for eme playback.
1263 ///
1264 /// The data format of the `info` struct passed to gecko is:
1265 ///
1266 /// - system id (16 byte uuid)
1267 /// - pssh box size (32-bit native endian)
1268 /// - pssh box content (including header)
1269 ///
1270 /// # Safety
1271 ///
1272 /// This function is unsafe because it dereferences the the parser and
1273 /// info raw pointers passed to it. Callers should ensure the parser
1274 /// pointer points to a valid `Mp4parseParser` and that the fragmented pointer
1275 /// points to a valid `Mp4parsePsshInfo`.
1276 #[no_mangle]
mp4parse_get_pssh_info( parser: *mut Mp4parseParser, info: *mut Mp4parsePsshInfo, ) -> Mp4parseStatus1277 pub unsafe extern "C" fn mp4parse_get_pssh_info(
1278     parser: *mut Mp4parseParser,
1279     info: *mut Mp4parsePsshInfo,
1280 ) -> Mp4parseStatus {
1281     if parser.is_null() || info.is_null() {
1282         return Mp4parseStatus::BadArg;
1283     }
1284 
1285     // Initialize fields to default values to ensure all fields are always valid.
1286     *info = Default::default();
1287 
1288     get_pssh_info(&mut *parser, &mut *info).into()
1289 }
1290 
get_pssh_info( parser: &mut Mp4parseParser, info: &mut Mp4parsePsshInfo, ) -> Result<(), Mp4parseStatus>1291 fn get_pssh_info(
1292     parser: &mut Mp4parseParser,
1293     info: &mut Mp4parsePsshInfo,
1294 ) -> Result<(), Mp4parseStatus> {
1295     let Mp4parseParser {
1296         context, pssh_data, ..
1297     } = parser;
1298 
1299     pssh_data.clear();
1300     for pssh in &context.psshs {
1301         let content_len = pssh
1302             .box_content
1303             .len()
1304             .try_into()
1305             .map_err(|_| Mp4parseStatus::Invalid)?;
1306         let mut data_len = TryVec::new();
1307         if data_len
1308             .write_u32::<byteorder::NativeEndian>(content_len)
1309             .is_err()
1310         {
1311             return Err(Mp4parseStatus::Io);
1312         }
1313         pssh_data.extend_from_slice(pssh.system_id.as_slice())?;
1314         pssh_data.extend_from_slice(data_len.as_slice())?;
1315         pssh_data.extend_from_slice(pssh.box_content.as_slice())?;
1316     }
1317 
1318     info.data.set_data(pssh_data);
1319 
1320     Ok(())
1321 }
1322 
1323 #[cfg(test)]
error_read(_: *mut u8, _: usize, _: *mut std::os::raw::c_void) -> isize1324 extern "C" fn error_read(_: *mut u8, _: usize, _: *mut std::os::raw::c_void) -> isize {
1325     -1
1326 }
1327 
1328 #[cfg(test)]
valid_read(buf: *mut u8, size: usize, userdata: *mut std::os::raw::c_void) -> isize1329 extern "C" fn valid_read(buf: *mut u8, size: usize, userdata: *mut std::os::raw::c_void) -> isize {
1330     let input: &mut std::fs::File = unsafe { &mut *(userdata as *mut _) };
1331 
1332     let mut buf = unsafe { std::slice::from_raw_parts_mut(buf, size) };
1333     match input.read(&mut buf) {
1334         Ok(n) => n as isize,
1335         Err(_) => -1,
1336     }
1337 }
1338 
1339 #[test]
get_track_count_null_parser()1340 fn get_track_count_null_parser() {
1341     unsafe {
1342         let mut count: u32 = 0;
1343         let rv = mp4parse_get_track_count(std::ptr::null(), std::ptr::null_mut());
1344         assert_eq!(rv, Mp4parseStatus::BadArg);
1345         let rv = mp4parse_get_track_count(std::ptr::null(), &mut count);
1346         assert_eq!(rv, Mp4parseStatus::BadArg);
1347     }
1348 }
1349 
1350 #[test]
arg_validation()1351 fn arg_validation() {
1352     unsafe {
1353         let rv = mp4parse_new(std::ptr::null(), std::ptr::null_mut());
1354         assert_eq!(rv, Mp4parseStatus::BadArg);
1355 
1356         // Passing a null Mp4parseIo is an error.
1357         let mut parser = std::ptr::null_mut();
1358         let rv = mp4parse_new(std::ptr::null(), &mut parser);
1359         assert_eq!(rv, Mp4parseStatus::BadArg);
1360         assert!(parser.is_null());
1361 
1362         let null_mut: *mut std::os::raw::c_void = std::ptr::null_mut();
1363 
1364         // Passing an Mp4parseIo with null members is an error.
1365         let io = Mp4parseIo {
1366             read: None,
1367             userdata: null_mut,
1368         };
1369         let mut parser = std::ptr::null_mut();
1370         let rv = mp4parse_new(&io, &mut parser);
1371         assert_eq!(rv, Mp4parseStatus::BadArg);
1372         assert!(parser.is_null());
1373 
1374         let mut dummy_value = 42;
1375         let io = Mp4parseIo {
1376             read: None,
1377             userdata: &mut dummy_value as *mut _ as *mut std::os::raw::c_void,
1378         };
1379         let mut parser = std::ptr::null_mut();
1380         let rv = mp4parse_new(&io, &mut parser);
1381         assert_eq!(rv, Mp4parseStatus::BadArg);
1382         assert!(parser.is_null());
1383 
1384         let mut dummy_info = Mp4parseTrackInfo {
1385             track_type: Mp4parseTrackType::Video,
1386             ..Default::default()
1387         };
1388         assert_eq!(
1389             Mp4parseStatus::BadArg,
1390             mp4parse_get_track_info(std::ptr::null_mut(), 0, &mut dummy_info)
1391         );
1392 
1393         let mut dummy_video = Mp4parseTrackVideoInfo {
1394             display_width: 0,
1395             display_height: 0,
1396             rotation: 0,
1397             sample_info_count: 0,
1398             sample_info: std::ptr::null(),
1399         };
1400         assert_eq!(
1401             Mp4parseStatus::BadArg,
1402             mp4parse_get_track_video_info(std::ptr::null_mut(), 0, &mut dummy_video)
1403         );
1404 
1405         let mut dummy_audio = Default::default();
1406         assert_eq!(
1407             Mp4parseStatus::BadArg,
1408             mp4parse_get_track_audio_info(std::ptr::null_mut(), 0, &mut dummy_audio)
1409         );
1410     }
1411 }
1412 
1413 #[test]
parser_input_must_be_null()1414 fn parser_input_must_be_null() {
1415     let mut dummy_value = 42;
1416     let io = Mp4parseIo {
1417         read: Some(error_read),
1418         userdata: &mut dummy_value as *mut _ as *mut std::os::raw::c_void,
1419     };
1420     let mut parser = 0xDEAD_BEEF as *mut _;
1421     let rv = unsafe { mp4parse_new(&io, &mut parser) };
1422     assert_eq!(rv, Mp4parseStatus::BadArg);
1423 }
1424 
1425 #[test]
arg_validation_with_parser()1426 fn arg_validation_with_parser() {
1427     unsafe {
1428         let mut dummy_value = 42;
1429         let io = Mp4parseIo {
1430             read: Some(error_read),
1431             userdata: &mut dummy_value as *mut _ as *mut std::os::raw::c_void,
1432         };
1433         let mut parser = std::ptr::null_mut();
1434         let rv = mp4parse_new(&io, &mut parser);
1435         assert_eq!(rv, Mp4parseStatus::Io);
1436         assert!(parser.is_null());
1437 
1438         // Null info pointers are an error.
1439         assert_eq!(
1440             Mp4parseStatus::BadArg,
1441             mp4parse_get_track_info(parser, 0, std::ptr::null_mut())
1442         );
1443         assert_eq!(
1444             Mp4parseStatus::BadArg,
1445             mp4parse_get_track_video_info(parser, 0, std::ptr::null_mut())
1446         );
1447         assert_eq!(
1448             Mp4parseStatus::BadArg,
1449             mp4parse_get_track_audio_info(parser, 0, std::ptr::null_mut())
1450         );
1451 
1452         let mut dummy_info = Mp4parseTrackInfo {
1453             track_type: Mp4parseTrackType::Video,
1454             ..Default::default()
1455         };
1456         assert_eq!(
1457             Mp4parseStatus::BadArg,
1458             mp4parse_get_track_info(parser, 0, &mut dummy_info)
1459         );
1460 
1461         let mut dummy_video = Mp4parseTrackVideoInfo {
1462             display_width: 0,
1463             display_height: 0,
1464             rotation: 0,
1465             sample_info_count: 0,
1466             sample_info: std::ptr::null(),
1467         };
1468         assert_eq!(
1469             Mp4parseStatus::BadArg,
1470             mp4parse_get_track_video_info(parser, 0, &mut dummy_video)
1471         );
1472 
1473         let mut dummy_audio = Default::default();
1474         assert_eq!(
1475             Mp4parseStatus::BadArg,
1476             mp4parse_get_track_audio_info(parser, 0, &mut dummy_audio)
1477         );
1478     }
1479 }
1480 
1481 #[cfg(test)]
parse_minimal_mp4() -> *mut Mp4parseParser1482 fn parse_minimal_mp4() -> *mut Mp4parseParser {
1483     let mut file = std::fs::File::open("../mp4parse/tests/minimal.mp4").unwrap();
1484     let io = Mp4parseIo {
1485         read: Some(valid_read),
1486         userdata: &mut file as *mut _ as *mut std::os::raw::c_void,
1487     };
1488     let mut parser = std::ptr::null_mut();
1489     let rv = unsafe { mp4parse_new(&io, &mut parser) };
1490     assert_eq!(Mp4parseStatus::Ok, rv);
1491     parser
1492 }
1493 
1494 #[test]
minimal_mp4_parse_ok()1495 fn minimal_mp4_parse_ok() {
1496     let parser = parse_minimal_mp4();
1497 
1498     assert!(!parser.is_null());
1499 
1500     unsafe {
1501         mp4parse_free(parser);
1502     }
1503 }
1504 
1505 #[test]
minimal_mp4_get_track_cout()1506 fn minimal_mp4_get_track_cout() {
1507     let parser = parse_minimal_mp4();
1508 
1509     let mut count: u32 = 0;
1510     assert_eq!(Mp4parseStatus::Ok, unsafe {
1511         mp4parse_get_track_count(parser, &mut count)
1512     });
1513     assert_eq!(2, count);
1514 
1515     unsafe {
1516         mp4parse_free(parser);
1517     }
1518 }
1519 
1520 #[test]
minimal_mp4_get_track_info()1521 fn minimal_mp4_get_track_info() {
1522     let parser = parse_minimal_mp4();
1523 
1524     let mut info = Mp4parseTrackInfo {
1525         track_type: Mp4parseTrackType::Video,
1526         ..Default::default()
1527     };
1528     assert_eq!(Mp4parseStatus::Ok, unsafe {
1529         mp4parse_get_track_info(parser, 0, &mut info)
1530     });
1531     assert_eq!(info.track_type, Mp4parseTrackType::Video);
1532     assert_eq!(info.track_id, 1);
1533     assert_eq!(info.duration, 40000);
1534     assert_eq!(info.media_time, 0);
1535 
1536     assert_eq!(Mp4parseStatus::Ok, unsafe {
1537         mp4parse_get_track_info(parser, 1, &mut info)
1538     });
1539     assert_eq!(info.track_type, Mp4parseTrackType::Audio);
1540     assert_eq!(info.track_id, 2);
1541     assert_eq!(info.duration, 61333);
1542     assert_eq!(info.media_time, 21333);
1543 
1544     unsafe {
1545         mp4parse_free(parser);
1546     }
1547 }
1548 
1549 #[test]
minimal_mp4_get_track_video_info()1550 fn minimal_mp4_get_track_video_info() {
1551     let parser = parse_minimal_mp4();
1552 
1553     let mut video = Mp4parseTrackVideoInfo::default();
1554     assert_eq!(Mp4parseStatus::Ok, unsafe {
1555         mp4parse_get_track_video_info(parser, 0, &mut video)
1556     });
1557     assert_eq!(video.display_width, 320);
1558     assert_eq!(video.display_height, 240);
1559     assert_eq!(video.sample_info_count, 1);
1560 
1561     unsafe {
1562         assert_eq!((*video.sample_info).image_width, 320);
1563         assert_eq!((*video.sample_info).image_height, 240);
1564     }
1565 
1566     unsafe {
1567         mp4parse_free(parser);
1568     }
1569 }
1570 
1571 #[test]
minimal_mp4_get_track_audio_info()1572 fn minimal_mp4_get_track_audio_info() {
1573     let parser = parse_minimal_mp4();
1574 
1575     let mut audio = Mp4parseTrackAudioInfo::default();
1576     assert_eq!(Mp4parseStatus::Ok, unsafe {
1577         mp4parse_get_track_audio_info(parser, 1, &mut audio)
1578     });
1579     assert_eq!(audio.sample_info_count, 1);
1580 
1581     unsafe {
1582         assert_eq!((*audio.sample_info).channels, 1);
1583         assert_eq!((*audio.sample_info).bit_depth, 16);
1584         assert_eq!((*audio.sample_info).sample_rate, 48000);
1585     }
1586 
1587     unsafe {
1588         mp4parse_free(parser);
1589     }
1590 }
1591 
1592 #[test]
minimal_mp4_get_track_info_invalid_track_number()1593 fn minimal_mp4_get_track_info_invalid_track_number() {
1594     let parser = parse_minimal_mp4();
1595 
1596     let mut info = Mp4parseTrackInfo {
1597         track_type: Mp4parseTrackType::Video,
1598         ..Default::default()
1599     };
1600     assert_eq!(Mp4parseStatus::BadArg, unsafe {
1601         mp4parse_get_track_info(parser, 3, &mut info)
1602     });
1603     assert_eq!(info.track_type, Mp4parseTrackType::Video);
1604     assert_eq!(info.track_id, 0);
1605     assert_eq!(info.duration, 0);
1606     assert_eq!(info.media_time, 0);
1607 
1608     let mut video = Mp4parseTrackVideoInfo::default();
1609     assert_eq!(Mp4parseStatus::BadArg, unsafe {
1610         mp4parse_get_track_video_info(parser, 3, &mut video)
1611     });
1612     assert_eq!(video.display_width, 0);
1613     assert_eq!(video.display_height, 0);
1614     assert_eq!(video.sample_info_count, 0);
1615 
1616     let mut audio = Default::default();
1617     assert_eq!(Mp4parseStatus::BadArg, unsafe {
1618         mp4parse_get_track_audio_info(parser, 3, &mut audio)
1619     });
1620     assert_eq!(audio.sample_info_count, 0);
1621 
1622     unsafe {
1623         mp4parse_free(parser);
1624     }
1625 }
1626