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