Bug 1267887 - Update source for mp4parse v0.4.0. r=kinetik

Result of running the update script.

MozReview-Commit-ID: AE6jXz8IMU7
This commit is contained in:
Ralph Giles 2016-06-28 13:43:08 -07:00
Родитель 8c0fe7a521
Коммит f3eb2d75ba
7 изменённых файлов: 1567 добавлений и 832 удалений

Просмотреть файл

@ -1,68 +1,103 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
#ifndef _MP4PARSE_RUST_H
#define _MP4PARSE_RUST_H
#ifndef cheddar_generated_mp4parse_h
#define cheddar_generated_mp4parse_h
#ifdef __cplusplus
extern "C" {
#endif
struct mp4parse_state;
#include <stdint.h>
#include <stdbool.h>
#define MP4PARSE_OK 0
#define MP4PARSE_ERROR_BADARG 1 // Argument validation failure
#define MP4PARSE_ERROR_INVALID 2 // Error::InvalidData
#define MP4PARSE_ERROR_UNSUPPORTED 3 // Error::Unsupported
#define MP4PARSE_ERROR_EOF 4 // Error::UnexpectedEOF
#define MP4PARSE_ASSERT 5 // Error::AssertCaught
#define MP4PARSE_ERROR_IO 6 // Error::Io(_)
// THIS FILE IS AUTOGENERATED BY mp4parse-rust/build.rs - DO NOT EDIT
#define MP4PARSE_TRACK_TYPE_H264 0 // "video/avc"
#define MP4PARSE_TRACK_TYPE_AAC 1 // "audio/mp4a-latm"
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
#define MP4PARSE_TRACK_CODEC_UNKNOWN 0
#define MP4PARSE_TRACK_CODEC_AAC 1
#define MP4PARSE_TRACK_CODEC_OPUS 2
#define MP4PARSE_TRACK_CODEC_H264 3
#define MP4PARSE_TRACK_CODEC_VP9 4
typedef enum mp4parse_error {
MP4PARSE_OK = 0,
MP4PARSE_ERROR_BADARG = 1,
MP4PARSE_ERROR_INVALID = 2,
MP4PARSE_ERROR_UNSUPPORTED = 3,
MP4PARSE_ERROR_EOF = 4,
MP4PARSE_ERROR_IO = 5,
} mp4parse_error;
struct mp4parse_track_audio_info {
uint16_t channels;
uint16_t bit_depth;
uint32_t sample_rate;
};
typedef enum mp4parse_track_type {
MP4PARSE_TRACK_TYPE_VIDEO = 0,
MP4PARSE_TRACK_TYPE_AUDIO = 1,
} mp4parse_track_type;
struct mp4parse_track_video_info {
uint32_t display_width;
uint32_t display_height;
uint16_t image_width;
uint16_t image_height;
};
typedef enum mp4parse_codec {
MP4PARSE_CODEC_UNKNOWN,
MP4PARSE_CODEC_AAC,
MP4PARSE_CODEC_OPUS,
MP4PARSE_CODEC_AVC,
MP4PARSE_CODEC_VP9,
} mp4parse_codec;
struct mp4parse_track_info {
uint32_t track_type;
uint32_t track_id;
uint64_t duration;
int64_t media_time;
};
typedef struct mp4parse_track_info {
mp4parse_track_type track_type;
mp4parse_codec codec;
uint32_t track_id;
uint64_t duration;
int64_t media_time;
} mp4parse_track_info;
struct mp4parse_state* mp4parse_new(void);
void mp4parse_free(struct mp4parse_state* state);
typedef struct mp4parse_codec_specific_config {
uint32_t length;
uint8_t const* data;
} mp4parse_codec_specific_config;
int32_t mp4parse_read(struct mp4parse_state* state, uint8_t *buffer, size_t size);
typedef struct mp4parse_track_audio_info {
uint16_t channels;
uint16_t bit_depth;
uint32_t sample_rate;
mp4parse_codec_specific_config codec_specific_config;
} mp4parse_track_audio_info;
uint32_t mp4parse_get_track_count(struct mp4parse_state* state);
typedef struct mp4parse_track_video_info {
uint32_t display_width;
uint32_t display_height;
uint16_t image_width;
uint16_t image_height;
} mp4parse_track_video_info;
int32_t mp4parse_get_track_info(struct mp4parse_state* state, uint32_t track, struct mp4parse_track_info* track_info);
typedef struct mp4parse_parser mp4parse_parser;
typedef struct mp4parse_io {
intptr_t (*read)(uint8_t* buffer, uintptr_t size, void* userdata);
void* userdata;
} mp4parse_io;
/// Allocate an `mp4parse_parser*` to read from the supplied `mp4parse_io`.
mp4parse_parser* mp4parse_new(mp4parse_io const* io);
/// Free an `mp4parse_parser*` allocated by `mp4parse_new()`.
void mp4parse_free(mp4parse_parser* parser);
/// Run the `mp4parse_parser*` allocated by `mp4parse_new()` until EOF or error.
mp4parse_error mp4parse_read(mp4parse_parser* parser);
/// Return the number of tracks parsed by previous `mp4parse_read()` call.
mp4parse_error mp4parse_get_track_count(mp4parse_parser const* parser, uint32_t* count);
/// Fill the supplied `mp4parse_track_info` with metadata for `track`.
mp4parse_error mp4parse_get_track_info(mp4parse_parser* parser, uint32_t track_index, mp4parse_track_info* info);
/// Fill the supplied `mp4parse_track_audio_info` with metadata for `track`.
mp4parse_error mp4parse_get_track_audio_info(mp4parse_parser* parser, uint32_t track_index, mp4parse_track_audio_info* info);
/// Fill the supplied `mp4parse_track_video_info` with metadata for `track`.
mp4parse_error mp4parse_get_track_video_info(mp4parse_parser* parser, uint32_t track_index, mp4parse_track_video_info* info);
int32_t mp4parse_get_track_audio_info(struct mp4parse_state* state, uint32_t track, struct mp4parse_track_audio_info* track_info);
int32_t mp4parse_get_track_video_info(struct mp4parse_state* state, uint32_t track, struct mp4parse_track_video_info* track_info);
#ifdef __cplusplus
}
#endif
#endif

Просмотреть файл

@ -0,0 +1,57 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
macro_rules! box_database {
($($boxenum:ident $boxtype:expr),*,) => {
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum BoxType {
$($boxenum),*,
UnknownBox(u32),
}
impl From<u32> for BoxType {
fn from(t: u32) -> BoxType {
use self::BoxType::*;
match t {
$($boxtype => $boxenum),*,
_ => UnknownBox(t),
}
}
}
}
}
box_database!(
FileTypeBox 0x66747970, // "ftyp"
MovieBox 0x6d6f6f76, // "moov"
MovieHeaderBox 0x6d766864, // "mvhd"
TrackBox 0x7472616b, // "trak"
TrackHeaderBox 0x746b6864, // "tkhd"
EditBox 0x65647473, // "edts"
MediaBox 0x6d646961, // "mdia"
EditListBox 0x656c7374, // "elst"
MediaHeaderBox 0x6d646864, // "mdhd"
HandlerBox 0x68646c72, // "hdlr"
MediaInformationBox 0x6d696e66, // "minf"
SampleTableBox 0x7374626c, // "stbl"
SampleDescriptionBox 0x73747364, // "stsd"
TimeToSampleBox 0x73747473, // "stts"
SampleToChunkBox 0x73747363, // "stsc"
SampleSizeBox 0x7374737a, // "stsz"
ChunkOffsetBox 0x7374636f, // "stco"
ChunkLargeOffsetBox 0x636f3634, // "co64"
SyncSampleBox 0x73747373, // "stss"
AVCSampleEntry 0x61766331, // "avc1"
AVC3SampleEntry 0x61766333, // "avc3" - Need to check official name in spec.
AVCConfigurationBox 0x61766343, // "avcC"
MP4AudioSampleEntry 0x6d703461, // "mp4a"
ESDBox 0x65736473, // "esds"
VP8SampleEntry 0x76703038, // "vp08"
VP9SampleEntry 0x76703039, // "vp09"
VPCodecConfigurationBox 0x76706343, // "vpcC"
OpusSampleEntry 0x4f707573, // "Opus"
OpusSpecificBox 0x644f7073, // "dOps"
ProtectedVisualSampleEntry 0x656e6376, // "encv" - Need to check official name in spec.
ProtectedAudioSampleEntry 0x656e6361, // "enca" - Need to check official name in spec.
);

Просмотреть файл

@ -43,11 +43,9 @@ assert_eq!(wtr, vec![5, 2, 0, 3]);
use std::mem::transmute;
use std::ptr::copy_nonoverlapping;
#[cfg(not(feature = "no-std"))]
pub use byteorder::new::{ReadBytesExt, WriteBytesExt, Error, Result};
pub use byteorder::new::{ReadBytesExt, WriteBytesExt};
#[cfg(not(feature = "no-std"))]
// Re-export new so gecko can build us as a mod intead of a crate.
// Re-export new so gecko can build us as a mod instead of a crate.
pub mod new;
#[inline]
@ -267,6 +265,18 @@ pub trait ByteOrder {
/// type level.
#[allow(missing_copy_implementations)] pub enum LittleEndian {}
/// Defines network byte order serialization.
///
/// Network byte order is defined by [RFC 1700][1] to be big-endian, and is
/// referred to in several protocol specifications. This type is an alias of
/// BigEndian.
///
/// [1]: https://tools.ietf.org/html/rfc1700
///
/// Note that this type has no value constructor. It is used purely at the
/// type level.
pub type NetworkEndian = BigEndian;
/// Defines system native-endian serialization.
///
/// Note that this type has no value constructor. It is used purely at the
@ -283,10 +293,16 @@ pub type NativeEndian = BigEndian;
macro_rules! read_num_bytes {
($ty:ty, $size:expr, $src:expr, $which:ident) => ({
assert!($size == ::std::mem::size_of::<$ty>());
assert!($size <= $src.len());
let mut data: $ty = 0;
unsafe {
(*($src.as_ptr() as *const $ty)).$which()
copy_nonoverlapping(
$src.as_ptr(),
&mut data as *mut $ty as *mut u8,
$size);
}
data.$which()
});
}

Просмотреть файл

@ -1,70 +1,7 @@
use std::error;
use std::fmt;
use std::io;
use std::result;
use std::io::{self, Result};
use byteorder::ByteOrder;
/// A short-hand for `result::Result<T, byteorder::Error>`.
pub type Result<T> = result::Result<T, Error>;
/// An error type for reading bytes.
///
/// This is a thin wrapper over the standard `io::Error` type. Namely, it
/// adds one additional error case: an unexpected EOF.
///
/// Note that this error is also used for the `write` methods to keep things
/// consistent.
#[derive(Debug)]
pub enum Error {
/// An unexpected EOF.
///
/// This occurs when a call to the underlying reader returns `0` bytes,
/// but more bytes are required to decode a meaningful value.
UnexpectedEOF,
/// Any underlying IO error that occurs while reading bytes.
Io(io::Error),
}
impl From<io::Error> for Error {
fn from(err: io::Error) -> Error { Error::Io(err) }
}
impl From<Error> for io::Error {
fn from(err: Error) -> io::Error {
match err {
Error::Io(err) => err,
Error::UnexpectedEOF => io::Error::new(io::ErrorKind::Other,
"unexpected EOF")
}
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Error::UnexpectedEOF => write!(f, "Unexpected end of file."),
Error::Io(ref err) => err.fmt(f),
}
}
}
impl error::Error for Error {
fn description(&self) -> &str {
match *self {
Error::UnexpectedEOF => "Unexpected end of file.",
Error::Io(ref err) => error::Error::description(err),
}
}
fn cause(&self) -> Option<&error::Error> {
match *self {
Error::UnexpectedEOF => None,
Error::Io(ref err) => err.cause(),
}
}
}
/// Extends `Read` with methods for reading numbers. (For `std::io`.)
///
/// Most of the methods defined here have an unconstrained type parameter that
@ -91,7 +28,7 @@ pub trait ReadBytesExt: io::Read {
#[inline]
fn read_u8(&mut self) -> Result<u8> {
let mut buf = [0; 1];
try!(read_full(self, &mut buf));
try!(self.read_exact(&mut buf));
Ok(buf[0])
}
@ -102,7 +39,7 @@ pub trait ReadBytesExt: io::Read {
#[inline]
fn read_i8(&mut self) -> Result<i8> {
let mut buf = [0; 1];
try!(read_full(self, &mut buf));
try!(self.read_exact(&mut buf));
Ok(buf[0] as i8)
}
@ -110,7 +47,7 @@ pub trait ReadBytesExt: io::Read {
#[inline]
fn read_u16<T: ByteOrder>(&mut self) -> Result<u16> {
let mut buf = [0; 2];
try!(read_full(self, &mut buf));
try!(self.read_exact(&mut buf));
Ok(T::read_u16(&buf))
}
@ -118,7 +55,7 @@ pub trait ReadBytesExt: io::Read {
#[inline]
fn read_i16<T: ByteOrder>(&mut self) -> Result<i16> {
let mut buf = [0; 2];
try!(read_full(self, &mut buf));
try!(self.read_exact(&mut buf));
Ok(T::read_i16(&buf))
}
@ -126,7 +63,7 @@ pub trait ReadBytesExt: io::Read {
#[inline]
fn read_u32<T: ByteOrder>(&mut self) -> Result<u32> {
let mut buf = [0; 4];
try!(read_full(self, &mut buf));
try!(self.read_exact(&mut buf));
Ok(T::read_u32(&buf))
}
@ -134,7 +71,7 @@ pub trait ReadBytesExt: io::Read {
#[inline]
fn read_i32<T: ByteOrder>(&mut self) -> Result<i32> {
let mut buf = [0; 4];
try!(read_full(self, &mut buf));
try!(self.read_exact(&mut buf));
Ok(T::read_i32(&buf))
}
@ -142,7 +79,7 @@ pub trait ReadBytesExt: io::Read {
#[inline]
fn read_u64<T: ByteOrder>(&mut self) -> Result<u64> {
let mut buf = [0; 8];
try!(read_full(self, &mut buf));
try!(self.read_exact(&mut buf));
Ok(T::read_u64(&buf))
}
@ -150,7 +87,7 @@ pub trait ReadBytesExt: io::Read {
#[inline]
fn read_i64<T: ByteOrder>(&mut self) -> Result<i64> {
let mut buf = [0; 8];
try!(read_full(self, &mut buf));
try!(self.read_exact(&mut buf));
Ok(T::read_i64(&buf))
}
@ -158,7 +95,7 @@ pub trait ReadBytesExt: io::Read {
#[inline]
fn read_uint<T: ByteOrder>(&mut self, nbytes: usize) -> Result<u64> {
let mut buf = [0; 8];
try!(read_full(self, &mut buf[..nbytes]));
try!(self.read_exact(&mut buf[..nbytes]));
Ok(T::read_uint(&buf[..nbytes], nbytes))
}
@ -166,7 +103,7 @@ pub trait ReadBytesExt: io::Read {
#[inline]
fn read_int<T: ByteOrder>(&mut self, nbytes: usize) -> Result<i64> {
let mut buf = [0; 8];
try!(read_full(self, &mut buf[..nbytes]));
try!(self.read_exact(&mut buf[..nbytes]));
Ok(T::read_int(&buf[..nbytes], nbytes))
}
@ -175,7 +112,7 @@ pub trait ReadBytesExt: io::Read {
#[inline]
fn read_f32<T: ByteOrder>(&mut self) -> Result<f32> {
let mut buf = [0; 4];
try!(read_full(self, &mut buf));
try!(self.read_exact(&mut buf));
Ok(T::read_f32(&buf))
}
@ -184,7 +121,7 @@ pub trait ReadBytesExt: io::Read {
#[inline]
fn read_f64<T: ByteOrder>(&mut self) -> Result<f64> {
let mut buf = [0; 8];
try!(read_full(self, &mut buf));
try!(self.read_exact(&mut buf));
Ok(T::read_f64(&buf))
}
}
@ -193,23 +130,6 @@ pub trait ReadBytesExt: io::Read {
/// for free.
impl<R: io::Read + ?Sized> ReadBytesExt for R {}
fn read_full<R: io::Read + ?Sized>(rdr: &mut R, buf: &mut [u8]) -> Result<()> {
let mut nread = 0usize;
while nread < buf.len() {
match rdr.read(&mut buf[nread..]) {
Ok(0) => return Err(Error::UnexpectedEOF),
Ok(n) => nread += n,
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {},
Err(e) => return Err(From::from(e))
}
}
Ok(())
}
fn write_all<W: io::Write + ?Sized>(wtr: &mut W, buf: &[u8]) -> Result<()> {
wtr.write_all(buf).map_err(From::from)
}
/// Extends `Write` with methods for writing numbers. (For `std::io`.)
///
/// Most of the methods defined here have an unconstrained type parameter that
@ -235,7 +155,7 @@ pub trait WriteBytesExt: io::Write {
/// are used. It is included for completeness.
#[inline]
fn write_u8(&mut self, n: u8) -> Result<()> {
write_all(self, &[n])
self.write_all(&[n])
}
/// Writes a signed 8 bit integer to the underlying writer.
@ -244,7 +164,7 @@ pub trait WriteBytesExt: io::Write {
/// are used. It is included for completeness.
#[inline]
fn write_i8(&mut self, n: i8) -> Result<()> {
write_all(self, &[n as u8])
self.write_all(&[n as u8])
}
/// Writes an unsigned 16 bit integer to the underlying writer.
@ -252,7 +172,7 @@ pub trait WriteBytesExt: io::Write {
fn write_u16<T: ByteOrder>(&mut self, n: u16) -> Result<()> {
let mut buf = [0; 2];
T::write_u16(&mut buf, n);
write_all(self, &buf)
self.write_all(&buf)
}
/// Writes a signed 16 bit integer to the underlying writer.
@ -260,7 +180,7 @@ pub trait WriteBytesExt: io::Write {
fn write_i16<T: ByteOrder>(&mut self, n: i16) -> Result<()> {
let mut buf = [0; 2];
T::write_i16(&mut buf, n);
write_all(self, &buf)
self.write_all(&buf)
}
/// Writes an unsigned 32 bit integer to the underlying writer.
@ -268,7 +188,7 @@ pub trait WriteBytesExt: io::Write {
fn write_u32<T: ByteOrder>(&mut self, n: u32) -> Result<()> {
let mut buf = [0; 4];
T::write_u32(&mut buf, n);
write_all(self, &buf)
self.write_all(&buf)
}
/// Writes a signed 32 bit integer to the underlying writer.
@ -276,7 +196,7 @@ pub trait WriteBytesExt: io::Write {
fn write_i32<T: ByteOrder>(&mut self, n: i32) -> Result<()> {
let mut buf = [0; 4];
T::write_i32(&mut buf, n);
write_all(self, &buf)
self.write_all(&buf)
}
/// Writes an unsigned 64 bit integer to the underlying writer.
@ -284,7 +204,7 @@ pub trait WriteBytesExt: io::Write {
fn write_u64<T: ByteOrder>(&mut self, n: u64) -> Result<()> {
let mut buf = [0; 8];
T::write_u64(&mut buf, n);
write_all(self, &buf)
self.write_all(&buf)
}
/// Writes a signed 64 bit integer to the underlying writer.
@ -292,7 +212,7 @@ pub trait WriteBytesExt: io::Write {
fn write_i64<T: ByteOrder>(&mut self, n: i64) -> Result<()> {
let mut buf = [0; 8];
T::write_i64(&mut buf, n);
write_all(self, &buf)
self.write_all(&buf)
}
/// Writes an unsigned n-bytes integer to the underlying writer.
@ -307,7 +227,7 @@ pub trait WriteBytesExt: io::Write {
) -> Result<()> {
let mut buf = [0; 8];
T::write_uint(&mut buf, n, nbytes);
write_all(self, &buf[0..nbytes])
self.write_all(&buf[0..nbytes])
}
/// Writes a signed n-bytes integer to the underlying writer.
@ -322,7 +242,7 @@ pub trait WriteBytesExt: io::Write {
) -> Result<()> {
let mut buf = [0; 8];
T::write_int(&mut buf, n, nbytes);
write_all(self, &buf[0..nbytes])
self.write_all(&buf[0..nbytes])
}
/// Writes a IEEE754 single-precision (4 bytes) floating point number to
@ -331,7 +251,7 @@ pub trait WriteBytesExt: io::Write {
fn write_f32<T: ByteOrder>(&mut self, n: f32) -> Result<()> {
let mut buf = [0; 4];
T::write_f32(&mut buf, n);
write_all(self, &buf)
self.write_all(&buf)
}
/// Writes a IEEE754 double-precision (8 bytes) floating point number to
@ -340,7 +260,7 @@ pub trait WriteBytesExt: io::Write {
fn write_f64<T: ByteOrder>(&mut self, n: f64) -> Result<()> {
let mut buf = [0; 8];
T::write_f64(&mut buf, n);
write_all(self, &buf)
self.write_all(&buf)
}
}

Просмотреть файл

@ -6,15 +6,25 @@
//!
//! ```rust
//! extern crate mp4parse;
//! use std::io::Read;
//!
//! // Minimal valid mp4 containing no tracks.
//! let data = b"\0\0\0\x0cftypmp42";
//! extern fn buf_read(buf: *mut u8, size: usize, userdata: *mut std::os::raw::c_void) -> isize {
//! let mut input: &mut std::fs::File = unsafe { &mut *(userdata as *mut _) };
//! let mut buf = unsafe { std::slice::from_raw_parts_mut(buf, size) };
//! match input.read(&mut buf) {
//! Ok(n) => n as isize,
//! Err(_) => -1,
//! }
//! }
//!
//! let context = mp4parse::mp4parse_new();
//! let mut file = std::fs::File::open("examples/minimal.mp4").unwrap();
//! let io = mp4parse::mp4parse_io { read: buf_read,
//! userdata: &mut file as *mut _ as *mut std::os::raw::c_void };
//! unsafe {
//! let rv = mp4parse::mp4parse_read(context, data.as_ptr(), data.len());
//! assert_eq!(0, rv);
//! mp4parse::mp4parse_free(context);
//! let parser = mp4parse::mp4parse_new(&io);
//! let rv = mp4parse::mp4parse_read(parser);
//! assert_eq!(rv, mp4parse::mp4parse_error::MP4PARSE_OK);
//! mp4parse::mp4parse_free(parser);
//! }
//! ```
@ -23,7 +33,8 @@
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
use std;
use std::io::Cursor;
use std::io::Read;
use std::collections::HashMap;
// Symbols we need from our rust api.
use MediaContext;
@ -33,160 +44,270 @@ use Error;
use media_time_to_ms;
use track_time_to_ms;
use SampleEntry;
use AudioCodecSpecific;
use VideoCodecSpecific;
use serialize_opus_header;
// These constants *must* match those in include/mp4parse.h.
/// Map Error to int32 return codes.
const MP4PARSE_OK: i32 = 0;
const MP4PARSE_ERROR_BADARG: i32 = 1;
const MP4PARSE_ERROR_INVALID: i32 = 2;
const MP4PARSE_ERROR_UNSUPPORTED: i32 = 3;
const MP4PARSE_ERROR_EOF: i32 = 4;
const MP4PARSE_ASSERT: i32 = 5;
const MP4PARSE_ERROR_IO: i32 = 6;
/// Map TrackType to uint32 constants.
const TRACK_TYPE_H264: u32 = 0;
const TRACK_TYPE_AAC: u32 = 1;
/// Map Track mime_type to uint32 constants.
const TRACK_CODEC_UNKNOWN: u32 = 0;
const TRACK_CODEC_AAC: u32 = 1;
const TRACK_CODEC_OPUS: u32 = 2;
const TRACK_CODEC_H264: u32 = 3;
const TRACK_CODEC_VP9: u32 = 4;
// These structs *must* match those declared in include/mp4parse.h.
// rusty-cheddar's C enum generation doesn't namespace enum members by
// prefixing them, so we're forced to do it in our member names until
// https://github.com/Sean1708/rusty-cheddar/pull/35 is fixed. Importing
// the members into the module namespace avoids doubling up on the
// namespacing on the Rust side.
use mp4parse_error::*;
use mp4parse_track_type::*;
#[repr(C)]
pub struct TrackInfo {
track_type: u32,
track_id: u32,
codec: u32,
duration: u64,
media_time: i64, // wants to be u64? understand how elst adjustment works
#[derive(PartialEq, Debug)]
pub enum mp4parse_error {
MP4PARSE_OK = 0,
MP4PARSE_ERROR_BADARG = 1,
MP4PARSE_ERROR_INVALID = 2,
MP4PARSE_ERROR_UNSUPPORTED = 3,
MP4PARSE_ERROR_EOF = 4,
MP4PARSE_ERROR_IO = 5,
}
#[repr(C)]
#[derive(PartialEq, Debug)]
pub enum mp4parse_track_type {
MP4PARSE_TRACK_TYPE_VIDEO = 0,
MP4PARSE_TRACK_TYPE_AUDIO = 1,
}
#[repr(C)]
#[derive(PartialEq, Debug)]
pub enum mp4parse_codec {
MP4PARSE_CODEC_UNKNOWN,
MP4PARSE_CODEC_AAC,
MP4PARSE_CODEC_OPUS,
MP4PARSE_CODEC_AVC,
MP4PARSE_CODEC_VP9,
}
#[repr(C)]
pub struct mp4parse_track_info {
pub track_type: mp4parse_track_type,
pub codec: mp4parse_codec,
pub track_id: u32,
pub duration: u64,
pub media_time: i64, // wants to be u64? understand how elst adjustment works
// TODO(kinetik): include crypto guff
}
#[repr(C)]
pub struct TrackAudioInfo {
channels: u16,
bit_depth: u16,
sample_rate: u32,
pub struct mp4parse_codec_specific_config {
pub length: u32,
pub data: *const u8,
}
impl Default for mp4parse_codec_specific_config {
fn default() -> Self {
mp4parse_codec_specific_config {
length: 0,
data: std::ptr::null_mut(),
}
}
}
#[derive(Default)]
#[repr(C)]
pub struct mp4parse_track_audio_info {
pub channels: u16,
pub bit_depth: u16,
pub sample_rate: u32,
// TODO(kinetik):
// int32_t profile;
// int32_t extended_profile; // check types
codec_specific_config: mp4parse_codec_specific_config,
}
#[repr(C)]
pub struct mp4parse_track_video_info {
pub display_width: u32,
pub display_height: u32,
pub image_width: u16,
pub image_height: u16,
// TODO(kinetik):
// extra_data
// codec_specific_config
}
// Even though mp4parse_parser is opaque to C, rusty-cheddar won't let us
// use more than one member, so we introduce *another* wrapper.
struct Wrap {
context: MediaContext,
io: mp4parse_io,
poisoned: bool,
opus_header: HashMap<u32, Vec<u8>>,
}
#[repr(C)]
pub struct TrackVideoInfo {
display_width: u32,
display_height: u32,
image_width: u16,
image_height: u16,
// TODO(kinetik):
// extra_data
// codec_specific_config
#[allow(non_camel_case_types)]
pub struct mp4parse_parser(Wrap);
impl mp4parse_parser {
fn context(&self) -> &MediaContext {
&self.0.context
}
fn context_mut(&mut self) -> &mut MediaContext {
&mut self.0.context
}
fn io_mut(&mut self) -> &mut mp4parse_io {
&mut self.0.io
}
fn poisoned(&self) -> bool {
self.0.poisoned
}
fn set_poisoned(&mut self, poisoned: bool) {
self.0.poisoned = poisoned;
}
fn opus_header_mut(&mut self) -> &mut HashMap<u32, Vec<u8>> {
&mut self.0.opus_header
}
}
#[repr(C)]
#[derive(Clone)]
pub struct mp4parse_io {
pub read: extern fn(buffer: *mut u8, size: usize, userdata: *mut std::os::raw::c_void) -> isize,
pub userdata: *mut std::os::raw::c_void,
}
impl Read for mp4parse_io {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
if buf.len() > isize::max_value() as usize {
return Err(std::io::Error::new(std::io::ErrorKind::Other, "buf length overflow in mp4parse_io Read impl"));
}
let rv = (self.read)(buf.as_mut_ptr(), buf.len(), self.userdata);
if rv >= 0 {
Ok(rv as usize)
} else {
Err(std::io::Error::new(std::io::ErrorKind::Other, "I/O error in mp4parse_io Read impl"))
}
}
}
// C API wrapper functions.
/// Allocate an opaque rust-side parser context.
/// Allocate an `mp4parse_parser*` to read from the supplied `mp4parse_io`.
#[no_mangle]
pub extern "C" fn mp4parse_new() -> *mut MediaContext {
let context = Box::new(MediaContext::new());
Box::into_raw(context)
pub unsafe extern fn mp4parse_new(io: *const mp4parse_io) -> *mut mp4parse_parser {
if io.is_null() || (*io).userdata.is_null() {
return std::ptr::null_mut();
}
// is_null() isn't available on a fn type because it can't be null (in
// Rust) by definition. But since this value is coming from the C API,
// it *could* be null. Ideally, we'd wrap it in an Option to represent
// reality, but this causes rusty-cheddar to emit the wrong type
// (https://github.com/Sean1708/rusty-cheddar/issues/30).
if ((*io).read as *mut std::os::raw::c_void).is_null() {
return std::ptr::null_mut();
}
let parser = Box::new(mp4parse_parser(Wrap {
context: MediaContext::new(),
io: (*io).clone(),
poisoned: false,
opus_header: HashMap::new(),
}));
Box::into_raw(parser)
}
/// Free a rust-side parser context.
/// Free an `mp4parse_parser*` allocated by `mp4parse_new()`.
#[no_mangle]
pub unsafe extern "C" fn mp4parse_free(context: *mut MediaContext) {
assert!(!context.is_null());
let _ = Box::from_raw(context);
pub unsafe extern fn mp4parse_free(parser: *mut mp4parse_parser) {
assert!(!parser.is_null());
let _ = Box::from_raw(parser);
}
/// Feed a buffer through `read_mp4()` with the given rust-side
/// parser context, returning success or an error code.
///
/// This is safe to call with NULL arguments but will crash
/// if given invalid pointers, as is usual for C.
/// Run the `mp4parse_parser*` allocated by `mp4parse_new()` until EOF or error.
#[no_mangle]
pub unsafe extern "C" fn mp4parse_read(context: *mut MediaContext, buffer: *const u8, size: usize) -> i32 {
pub unsafe extern fn mp4parse_read(parser: *mut mp4parse_parser) -> mp4parse_error {
// Validate arguments from C.
if context.is_null() || buffer.is_null() || size < 8 {
if parser.is_null() || (*parser).poisoned() {
return MP4PARSE_ERROR_BADARG;
}
let mut context: &mut MediaContext = &mut *context;
let mut context = (*parser).context_mut();
let mut io = (*parser).io_mut();
// Wrap the buffer we've been give in a slice.
let b = std::slice::from_raw_parts(buffer, size);
let mut c = Cursor::new(b);
// Parse in a subthread to catch any panics.
// We must use the thread::Builder API to avoid spawn itself
// panicking if thread creation fails. See bug 1266309.
let task = match std::thread::Builder::new()
.name("mp4parse_read isolation".to_string())
.spawn(move || read_mp4(&mut c, &mut context)) {
Ok(task) => task,
Err(_) => return MP4PARSE_ASSERT,
};
// The task's JoinHandle will return an error result if the
// thread panicked, and will wrap the closure's return'd
// result in an Ok(..) otherwise, meaning we could see
// Ok(Err(Error::..)) here. So map thread failures back
// to an mp4parse::Error before converting to a C return value.
match task.join().unwrap_or(Err(Error::AssertCaught)) {
let r = read_mp4(io, context);
match r {
Ok(_) => MP4PARSE_OK,
Err(Error::InvalidData) => MP4PARSE_ERROR_INVALID,
Err(Error::Unsupported) => MP4PARSE_ERROR_UNSUPPORTED,
Err(Error::NoMoov) | Err(Error::InvalidData(_)) => {
// Block further calls. We've probable lost sync.
(*parser).set_poisoned(true);
MP4PARSE_ERROR_INVALID
}
Err(Error::Unsupported(_)) => MP4PARSE_ERROR_UNSUPPORTED,
Err(Error::UnexpectedEOF) => MP4PARSE_ERROR_EOF,
Err(Error::AssertCaught) => MP4PARSE_ASSERT,
Err(Error::Io(_)) => MP4PARSE_ERROR_IO,
Err(Error::Io(_)) => {
// Block further calls after a read failure.
// Getting std::io::ErrorKind::UnexpectedEof is normal
// but our From trait implementation should have converted
// those to our Error::UnexpectedEOF variant.
(*parser).set_poisoned(true);
MP4PARSE_ERROR_IO
}
}
}
/// Return the number of tracks parsed by previous `read_mp4()` calls.
/// Return the number of tracks parsed by previous `mp4parse_read()` call.
#[no_mangle]
pub unsafe extern "C" fn mp4parse_get_track_count(context: *const MediaContext) -> u32 {
// Validate argument from C.
assert!(!context.is_null());
let context = &*context;
pub unsafe extern fn mp4parse_get_track_count(parser: *const mp4parse_parser, count: *mut u32) -> mp4parse_error {
// Validate arguments from C.
if parser.is_null() || count.is_null() || (*parser).poisoned() {
return MP4PARSE_ERROR_BADARG;
}
let context = (*parser).context();
// Make sure the track count fits in a u32.
assert!(context.tracks.len() < u32::max_value() as usize);
context.tracks.len() as u32
if context.tracks.len() >= u32::max_value() as usize {
return MP4PARSE_ERROR_INVALID;
}
*count = context.tracks.len() as u32;
MP4PARSE_OK
}
/// Fill the supplied `mp4parse_track_info` with metadata for `track`.
#[no_mangle]
pub unsafe extern "C" fn mp4parse_get_track_info(context: *mut MediaContext, track: u32, info: *mut TrackInfo) -> i32 {
if context.is_null() || info.is_null() {
pub unsafe extern fn mp4parse_get_track_info(parser: *mut mp4parse_parser, track_index: u32, info: *mut mp4parse_track_info) -> mp4parse_error {
if parser.is_null() || info.is_null() || (*parser).poisoned() {
return MP4PARSE_ERROR_BADARG;
}
let context: &mut MediaContext = &mut *context;
let track_index: usize = track as usize;
let info: &mut TrackInfo = &mut *info;
let context = (*parser).context_mut();
let track_index: usize = track_index as usize;
let info: &mut mp4parse_track_info = &mut *info;
if track_index >= context.tracks.len() {
return MP4PARSE_ERROR_BADARG;
}
info.track_type = match context.tracks[track_index].track_type {
TrackType::Video => TRACK_TYPE_H264,
TrackType::Audio => TRACK_TYPE_AAC,
TrackType::Video => MP4PARSE_TRACK_TYPE_VIDEO,
TrackType::Audio => MP4PARSE_TRACK_TYPE_AUDIO,
TrackType::Unknown => return MP4PARSE_ERROR_UNSUPPORTED,
};
info.codec = match &*context.tracks[track_index].mime_type {
"audio/opus" => TRACK_CODEC_OPUS,
"video/vp9" => TRACK_CODEC_VP9,
"video/h264" => TRACK_CODEC_H264,
"audio/aac" => TRACK_CODEC_AAC,
_ => TRACK_CODEC_UNKNOWN,
info.codec = match context.tracks[track_index].data {
Some(SampleEntry::Audio(ref audio)) => match audio.codec_specific {
AudioCodecSpecific::OpusSpecificBox(_) =>
mp4parse_codec::MP4PARSE_CODEC_OPUS,
AudioCodecSpecific::ES_Descriptor(_) =>
mp4parse_codec::MP4PARSE_CODEC_AAC,
},
Some(SampleEntry::Video(ref video)) => match video.codec_specific {
VideoCodecSpecific::VPxConfig(_) =>
mp4parse_codec::MP4PARSE_CODEC_VP9,
VideoCodecSpecific::AVCConfig(_) =>
mp4parse_codec::MP4PARSE_CODEC_AVC,
},
_ => mp4parse_codec::MP4PARSE_CODEC_UNKNOWN,
};
// Maybe context & track should just have a single simple is_valid() instead?
@ -198,35 +319,31 @@ pub unsafe extern "C" fn mp4parse_get_track_info(context: *mut MediaContext, tra
}
let track = &context.tracks[track_index];
let empty_duration = if track.empty_duration.is_some() {
media_time_to_ms(track.empty_duration.unwrap(), context.timescale.unwrap())
} else {
0
};
info.media_time = if track.media_time.is_some() {
track_time_to_ms(track.media_time.unwrap(), track.timescale.unwrap()) as i64 - empty_duration as i64
} else {
0
};
info.media_time = track.media_time.map_or(0, |media_time| {
track_time_to_ms(media_time, track.timescale.unwrap()) as i64
}) - track.empty_duration.map_or(0, |empty_duration| {
media_time_to_ms(empty_duration, context.timescale.unwrap()) as i64
});
info.duration = track_time_to_ms(track.duration.unwrap(), track.timescale.unwrap());
info.track_id = track.track_id.unwrap();
MP4PARSE_OK
}
/// Fill the supplied `mp4parse_track_audio_info` with metadata for `track`.
#[no_mangle]
pub unsafe extern "C" fn mp4parse_get_track_audio_info(context: *mut MediaContext, track: u32, info: *mut TrackAudioInfo) -> i32 {
if context.is_null() || info.is_null() {
pub unsafe extern fn mp4parse_get_track_audio_info(parser: *mut mp4parse_parser, track_index: u32, info: *mut mp4parse_track_audio_info) -> mp4parse_error {
if parser.is_null() || info.is_null() || (*parser).poisoned() {
return MP4PARSE_ERROR_BADARG;
}
let context: &mut MediaContext = &mut *context;
let context = (*parser).context_mut();
if track as usize >= context.tracks.len() {
if track_index as usize >= context.tracks.len() {
return MP4PARSE_ERROR_BADARG;
}
let track = &context.tracks[track as usize];
let track = &context.tracks[track_index as usize];
match track.track_type {
TrackType::Audio => {}
@ -247,22 +364,52 @@ pub unsafe extern "C" fn mp4parse_get_track_audio_info(context: *mut MediaContex
(*info).bit_depth = audio.samplesize;
(*info).sample_rate = audio.samplerate >> 16; // 16.16 fixed point
match audio.codec_specific {
AudioCodecSpecific::ES_Descriptor(ref v) => {
if v.len() > std::u32::MAX as usize {
return MP4PARSE_ERROR_INVALID;
}
(*info).codec_specific_config.length = v.len() as u32;
(*info).codec_specific_config.data = v.as_ptr();
}
AudioCodecSpecific::OpusSpecificBox(ref opus) => {
let mut v = Vec::new();
match serialize_opus_header(opus, &mut v) {
Err(_) => {
return MP4PARSE_ERROR_INVALID;
}
Ok(_) => {
let header = (*parser).opus_header_mut();
header.insert(track_index, v);
match header.get(&track_index) {
None => {}
Some(v) => {
(*info).codec_specific_config.length = v.len() as u32;
(*info).codec_specific_config.data = v.as_ptr();
}
}
}
}
}
}
MP4PARSE_OK
}
/// Fill the supplied `mp4parse_track_video_info` with metadata for `track`.
#[no_mangle]
pub unsafe extern "C" fn mp4parse_get_track_video_info(context: *mut MediaContext, track: u32, info: *mut TrackVideoInfo) -> i32 {
if context.is_null() || info.is_null() {
pub unsafe extern fn mp4parse_get_track_video_info(parser: *mut mp4parse_parser, track_index: u32, info: *mut mp4parse_track_video_info) -> mp4parse_error {
if parser.is_null() || info.is_null() || (*parser).poisoned() {
return MP4PARSE_ERROR_BADARG;
}
let context: &mut MediaContext = &mut *context;
let context = (*parser).context_mut();
if track as usize >= context.tracks.len() {
if track_index as usize >= context.tracks.len() {
return MP4PARSE_ERROR_BADARG;
}
let track = &context.tracks[track as usize];
let track = &context.tracks[track_index as usize];
match track.track_type {
TrackType::Video => {}
@ -286,54 +433,269 @@ pub unsafe extern "C" fn mp4parse_get_track_video_info(context: *mut MediaContex
return MP4PARSE_ERROR_INVALID;
}
(*info).image_width = video.width;
(*info).image_width = video.height;
(*info).image_height = video.height;
MP4PARSE_OK
}
#[cfg(test)]
extern fn panic_read(_: *mut u8, _: usize, _: *mut std::os::raw::c_void) -> isize {
panic!("panic_read shouldn't be called in these tests");
}
#[cfg(test)]
extern fn error_read(_: *mut u8, _: usize, _: *mut std::os::raw::c_void) -> isize {
-1
}
#[cfg(test)]
extern fn valid_read(buf: *mut u8, size: usize, userdata: *mut std::os::raw::c_void) -> isize {
let mut input: &mut std::fs::File = unsafe { &mut *(userdata as *mut _) };
let mut buf = unsafe { std::slice::from_raw_parts_mut(buf, size) };
match input.read(&mut buf) {
Ok(n) => n as isize,
Err(_) => -1,
}
}
#[test]
fn new_context() {
let context = mp4parse_new();
assert!(!context.is_null());
fn new_parser() {
let mut dummy_value: u32 = 42;
let io = mp4parse_io {
read: panic_read,
userdata: &mut dummy_value as *mut _ as *mut std::os::raw::c_void,
};
unsafe {
mp4parse_free(context);
let parser = mp4parse_new(&io);
assert!(!parser.is_null());
mp4parse_free(parser);
}
}
#[test]
#[should_panic(expected = "assertion failed")]
fn free_null_context() {
fn free_null_parser() {
unsafe {
mp4parse_free(std::ptr::null_mut());
}
}
#[test]
fn arg_validation() {
let null_buffer = std::ptr::null();
let null_context = std::ptr::null_mut();
let context = mp4parse_new();
assert!(!context.is_null());
let buffer = vec![0u8; 8];
fn get_track_count_null_parser() {
unsafe {
assert_eq!(MP4PARSE_ERROR_BADARG,
mp4parse_read(null_context, null_buffer, 0));
assert_eq!(MP4PARSE_ERROR_BADARG,
mp4parse_read(context, null_buffer, 0));
}
for size in 0..buffer.len() {
println!("testing buffer length {}", size);
unsafe {
assert_eq!(MP4PARSE_ERROR_BADARG,
mp4parse_read(context, buffer.as_ptr(), size));
}
}
unsafe {
mp4parse_free(context);
let mut count: u32 = 0;
let rv = mp4parse_get_track_count(std::ptr::null(), std::ptr::null_mut());
assert!(rv == MP4PARSE_ERROR_BADARG);
let rv = mp4parse_get_track_count(std::ptr::null(), &mut count);
assert!(rv == MP4PARSE_ERROR_BADARG);
}
}
#[test]
fn arg_validation() {
unsafe {
// Passing a null mp4parse_io is an error.
let parser = mp4parse_new(std::ptr::null());
assert!(parser.is_null());
let null_mut: *mut std::os::raw::c_void = std::ptr::null_mut();
// Passing an mp4parse_io with null members is an error.
let io = mp4parse_io { read: std::mem::transmute(null_mut),
userdata: null_mut };
let parser = mp4parse_new(&io);
assert!(parser.is_null());
let io = mp4parse_io { read: panic_read,
userdata: null_mut };
let parser = mp4parse_new(&io);
assert!(parser.is_null());
let mut dummy_value = 42;
let io = mp4parse_io {
read: std::mem::transmute(null_mut),
userdata: &mut dummy_value as *mut _ as *mut std::os::raw::c_void,
};
let parser = mp4parse_new(&io);
assert!(parser.is_null());
// Passing a null mp4parse_parser is an error.
assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_read(std::ptr::null_mut()));
let mut dummy_info = mp4parse_track_info {
track_type: MP4PARSE_TRACK_TYPE_VIDEO,
codec: mp4parse_codec::MP4PARSE_CODEC_UNKNOWN,
track_id: 0,
duration: 0,
media_time: 0,
};
assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_info(std::ptr::null_mut(), 0, &mut dummy_info));
let mut dummy_video = mp4parse_track_video_info {
display_width: 0,
display_height: 0,
image_width: 0,
image_height: 0,
};
assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_video_info(std::ptr::null_mut(), 0, &mut dummy_video));
let mut dummy_audio = Default::default();
assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_audio_info(std::ptr::null_mut(), 0, &mut dummy_audio));
}
}
#[test]
fn arg_validation_with_parser() {
unsafe {
let mut dummy_value = 42;
let io = mp4parse_io {
read: error_read,
userdata: &mut dummy_value as *mut _ as *mut std::os::raw::c_void,
};
let parser = mp4parse_new(&io);
assert!(!parser.is_null());
// Our mp4parse_io read should simply fail with an error.
assert_eq!(MP4PARSE_ERROR_IO, mp4parse_read(parser));
// The parser is now poisoned and unusable.
assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_read(parser));
// Null info pointers are an error.
assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_info(parser, 0, std::ptr::null_mut()));
assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_video_info(parser, 0, std::ptr::null_mut()));
assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_audio_info(parser, 0, std::ptr::null_mut()));
let mut dummy_info = mp4parse_track_info {
track_type: MP4PARSE_TRACK_TYPE_VIDEO,
codec: mp4parse_codec::MP4PARSE_CODEC_UNKNOWN,
track_id: 0,
duration: 0,
media_time: 0,
};
assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_info(parser, 0, &mut dummy_info));
let mut dummy_video = mp4parse_track_video_info {
display_width: 0,
display_height: 0,
image_width: 0,
image_height: 0,
};
assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_video_info(parser, 0, &mut dummy_video));
let mut dummy_audio = Default::default();
assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_audio_info(parser, 0, &mut dummy_audio));
mp4parse_free(parser);
}
}
#[test]
fn get_track_count_poisoned_parser() {
unsafe {
let mut dummy_value = 42;
let io = mp4parse_io {
read: error_read,
userdata: &mut dummy_value as *mut _ as *mut std::os::raw::c_void,
};
let parser = mp4parse_new(&io);
assert!(!parser.is_null());
// Our mp4parse_io read should simply fail with an error.
assert_eq!(MP4PARSE_ERROR_IO, mp4parse_read(parser));
let mut count: u32 = 0;
let rv = mp4parse_get_track_count(parser, &mut count);
assert!(rv == MP4PARSE_ERROR_BADARG);
}
}
#[test]
fn arg_validation_with_data() {
unsafe {
let mut file = std::fs::File::open("examples/minimal.mp4").unwrap();
let io = mp4parse_io { read: valid_read,
userdata: &mut file as *mut _ as *mut std::os::raw::c_void };
let parser = mp4parse_new(&io);
assert!(!parser.is_null());
assert_eq!(MP4PARSE_OK, mp4parse_read(parser));
let mut count: u32 = 0;
assert_eq!(MP4PARSE_OK, mp4parse_get_track_count(parser, &mut count));
assert_eq!(2, count);
let mut info = mp4parse_track_info {
track_type: MP4PARSE_TRACK_TYPE_VIDEO,
codec: mp4parse_codec::MP4PARSE_CODEC_UNKNOWN,
track_id: 0,
duration: 0,
media_time: 0,
};
assert_eq!(MP4PARSE_OK, mp4parse_get_track_info(parser, 0, &mut info));
assert_eq!(info.track_type, MP4PARSE_TRACK_TYPE_VIDEO);
assert_eq!(info.codec, mp4parse_codec::MP4PARSE_CODEC_AVC);
assert_eq!(info.track_id, 1);
assert_eq!(info.duration, 40000);
assert_eq!(info.media_time, 0);
assert_eq!(MP4PARSE_OK, mp4parse_get_track_info(parser, 1, &mut info));
assert_eq!(info.track_type, MP4PARSE_TRACK_TYPE_AUDIO);
assert_eq!(info.codec, mp4parse_codec::MP4PARSE_CODEC_AAC);
assert_eq!(info.track_id, 2);
assert_eq!(info.duration, 61333);
assert_eq!(info.media_time, 21333);
let mut video = mp4parse_track_video_info {
display_width: 0,
display_height: 0,
image_width: 0,
image_height: 0,
};
assert_eq!(MP4PARSE_OK, mp4parse_get_track_video_info(parser, 0, &mut video));
assert_eq!(video.display_width, 320);
assert_eq!(video.display_height, 240);
assert_eq!(video.image_width, 320);
assert_eq!(video.image_height, 240);
let mut audio = Default::default();
assert_eq!(MP4PARSE_OK, mp4parse_get_track_audio_info(parser, 1, &mut audio));
assert_eq!(audio.channels, 2);
assert_eq!(audio.bit_depth, 16);
assert_eq!(audio.sample_rate, 48000);
// Test with an invalid track number.
let mut info = mp4parse_track_info {
track_type: MP4PARSE_TRACK_TYPE_VIDEO,
codec: mp4parse_codec::MP4PARSE_CODEC_UNKNOWN,
track_id: 0,
duration: 0,
media_time: 0,
};
assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_info(parser, 3, &mut info));
assert_eq!(info.track_type, MP4PARSE_TRACK_TYPE_VIDEO);
assert_eq!(info.codec, mp4parse_codec::MP4PARSE_CODEC_UNKNOWN);
assert_eq!(info.track_id, 0);
assert_eq!(info.duration, 0);
assert_eq!(info.media_time, 0);
let mut video = mp4parse_track_video_info { display_width: 0,
display_height: 0,
image_width: 0,
image_height: 0 };
assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_video_info(parser, 3, &mut video));
assert_eq!(video.display_width, 0);
assert_eq!(video.display_height, 0);
assert_eq!(video.image_width, 0);
assert_eq!(video.image_height, 0);
let mut audio = Default::default();
assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_audio_info(parser, 3, &mut audio));
assert_eq!(audio.channels, 0);
assert_eq!(audio.bit_depth, 0);
assert_eq!(audio.sample_rate, 0);
mp4parse_free(parser);
}
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -10,6 +10,8 @@ use super::*;
extern crate test_assembler;
use self::test_assembler::*;
use boxes::BoxType;
enum BoxSize {
Short(u32),
Long(u64),
@ -18,7 +20,7 @@ enum BoxSize {
Auto,
}
fn make_box_raw<F>(size: BoxSize, name: &[u8; 4], func: F) -> Cursor<Vec<u8>>
fn make_box<F>(size: BoxSize, name: &[u8; 4], func: F) -> Cursor<Vec<u8>>
where F: Fn(Section) -> Section
{
let mut section = Section::new();
@ -50,25 +52,19 @@ fn make_box_raw<F>(size: BoxSize, name: &[u8; 4], func: F) -> Cursor<Vec<u8>>
BoxSize::Long(size) => assert_eq!(size, section.size()),
BoxSize::Auto => {
assert!(section.size() <= u32::max_value() as u64,
"Tried to use a long box with BoxSize::Auto");
"Tried to use a long box with BoxSize::Auto");
box_size.set_const(section.size());
},
}
// Skip checking BoxSize::Unchecked* cases.
_ => (),
}
Cursor::new(section.get_contents().unwrap())
}
fn make_box<F>(size: u32, name: &[u8; 4], func: F) -> Cursor<Vec<u8>>
where F: Fn(Section) -> Section
{
make_box_raw(BoxSize::Short(size), name, func)
}
fn make_fullbox<F>(size: BoxSize, name: &[u8; 4], version: u8, func: F) -> Cursor<Vec<u8>>
where F: Fn(Section) -> Section
{
make_box_raw(size, name, |s| {
make_box(size, name, |s| {
func(s.B8(version)
.B8(0)
.B8(0)
@ -78,71 +74,71 @@ fn make_fullbox<F>(size: BoxSize, name: &[u8; 4], version: u8, func: F) -> Curso
#[test]
fn read_box_header_short() {
let mut stream = make_box_raw(BoxSize::Short(8), b"test", |s| s);
let parsed = read_box_header(&mut stream).unwrap();
assert_eq!(parsed.name, FourCC(*b"test"));
assert_eq!(parsed.size, 8);
let mut stream = make_box(BoxSize::Short(8), b"test", |s| s);
let header = super::read_box_header(&mut stream).unwrap();
assert_eq!(header.name, BoxType::UnknownBox(0x74657374)); // "test"
assert_eq!(header.size, 8);
}
#[test]
fn read_box_header_long() {
let mut stream = make_box_raw(BoxSize::Long(16), b"test", |s| s);
let parsed = read_box_header(&mut stream).unwrap();
assert_eq!(parsed.name, FourCC(*b"test"));
assert_eq!(parsed.size, 16);
let mut stream = make_box(BoxSize::Long(16), b"test", |s| s);
let header = super::read_box_header(&mut stream).unwrap();
assert_eq!(header.name, BoxType::UnknownBox(0x74657374)); // "test"
assert_eq!(header.size, 16);
}
#[test]
fn read_box_header_short_unknown_size() {
let mut stream = make_box_raw(BoxSize::Short(0), b"test", |s| s);
match read_box_header(&mut stream) {
Err(Error::Unsupported) => (),
let mut stream = make_box(BoxSize::Short(0), b"test", |s| s);
match super::read_box_header(&mut stream) {
Err(Error::Unsupported(s)) => assert_eq!(s, "unknown sized box"),
_ => panic!("unexpected result reading box with unknown size"),
};
}
#[test]
fn read_box_header_short_invalid_size() {
let mut stream = make_box_raw(BoxSize::UncheckedShort(2), b"test", |s| s);
match read_box_header(&mut stream) {
Err(Error::InvalidData) => (),
let mut stream = make_box(BoxSize::UncheckedShort(2), b"test", |s| s);
match super::read_box_header(&mut stream) {
Err(Error::InvalidData(s)) => assert_eq!(s, "malformed size"),
_ => panic!("unexpected result reading box with invalid size"),
};
}
#[test]
fn read_box_header_long_invalid_size() {
let mut stream = make_box_raw(BoxSize::UncheckedLong(2), b"test", |s| s);
match read_box_header(&mut stream) {
Err(Error::InvalidData) => (),
let mut stream = make_box(BoxSize::UncheckedLong(2), b"test", |s| s);
match super::read_box_header(&mut stream) {
Err(Error::InvalidData(s)) => assert_eq!(s, "malformed wide size"),
_ => panic!("unexpected result reading box with invalid size"),
};
}
#[test]
fn read_ftyp() {
let mut stream = make_box(24, b"ftyp", |s| {
let mut stream = make_box(BoxSize::Short(24), b"ftyp", |s| {
s.append_bytes(b"mp42")
.B32(0) // minor version
.append_bytes(b"isom")
.append_bytes(b"mp42")
});
let header = read_box_header(&mut stream).unwrap();
let parsed = super::read_ftyp(&mut stream, &header).unwrap();
assert_eq!(parsed.header.name, FourCC(*b"ftyp"));
assert_eq!(parsed.header.size, 24);
assert_eq!(parsed.major_brand, FourCC(*b"mp42"));
let mut iter = super::BoxIter::new(&mut stream);
let mut stream = iter.next_box().unwrap().unwrap();
assert_eq!(stream.head.name, BoxType::FileTypeBox);
assert_eq!(stream.head.size, 24);
let parsed = super::read_ftyp(&mut stream).unwrap();
assert_eq!(parsed.major_brand, 0x6d703432); // mp42
assert_eq!(parsed.minor_version, 0);
assert_eq!(parsed.compatible_brands.len(), 2);
assert_eq!(parsed.compatible_brands[0], FourCC(*b"isom"));
assert_eq!(parsed.compatible_brands[1], FourCC(*b"mp42"));
assert_eq!(parsed.compatible_brands[0], 0x69736f6d); // isom
assert_eq!(parsed.compatible_brands[1], 0x6d703432); // mp42
}
#[test]
#[should_panic(expected = "expected an error result")]
fn read_truncated_ftyp() {
// We declare a 24 byte box, but only write 20 bytes.
let mut stream = make_box_raw(BoxSize::UncheckedShort(24), b"ftyp", |s| {
let mut stream = make_box(BoxSize::UncheckedShort(24), b"ftyp", |s| {
s.append_bytes(b"mp42")
.B32(0) // minor version
.append_bytes(b"isom")
@ -155,6 +151,32 @@ fn read_truncated_ftyp() {
}
}
#[test]
fn read_ftyp_case() {
// Brands in BMFF are represented as a u32, so it would seem clear that
// 0x6d703432 ("mp42") is not equal to 0x4d503432 ("MP42"), but some
// demuxers treat these as case-insensitive strings, e.g. street.mp4's
// major brand is "MP42". I haven't seen case-insensitive
// compatible_brands (which we also test here), but it doesn't seem
// unlikely given the major_brand behaviour.
let mut stream = make_box(BoxSize::Auto, b"ftyp", |s| {
s.append_bytes(b"MP42")
.B32(0) // minor version
.append_bytes(b"ISOM")
.append_bytes(b"MP42")
});
let mut iter = super::BoxIter::new(&mut stream);
let mut stream = iter.next_box().unwrap().unwrap();
assert_eq!(stream.head.name, BoxType::FileTypeBox);
assert_eq!(stream.head.size, 24);
let parsed = super::read_ftyp(&mut stream).unwrap();
assert_eq!(parsed.major_brand, 0x4d503432);
assert_eq!(parsed.minor_version, 0);
assert_eq!(parsed.compatible_brands.len(), 2);
assert_eq!(parsed.compatible_brands[0], 0x49534f4d); // ISOM
assert_eq!(parsed.compatible_brands[1], 0x4d503432); // MP42
}
#[test]
fn read_elst_v0() {
let mut stream = make_fullbox(BoxSize::Short(28), b"elst", 0, |s| {
@ -165,10 +187,11 @@ fn read_elst_v0() {
.B16(12) // rate integer
.B16(34) // rate fraction
});
let header = read_box_header(&mut stream).unwrap();
let parsed = super::read_elst(&mut stream, &header).unwrap();
assert_eq!(parsed.header.name, FourCC(*b"elst"));
assert_eq!(parsed.header.size, 28);
let mut iter = super::BoxIter::new(&mut stream);
let mut stream = iter.next_box().unwrap().unwrap();
assert_eq!(stream.head.name, BoxType::EditListBox);
assert_eq!(stream.head.size, 28);
let parsed = super::read_elst(&mut stream).unwrap();
assert_eq!(parsed.edits.len(), 1);
assert_eq!(parsed.edits[0].segment_duration, 1234);
assert_eq!(parsed.edits[0].media_time, 5678);
@ -191,10 +214,11 @@ fn read_elst_v1() {
.B16(12) // rate integer
.B16(34) // rate fraction
});
let header = read_box_header(&mut stream).unwrap();
let parsed = super::read_elst(&mut stream, &header).unwrap();
assert_eq!(parsed.header.name, FourCC(*b"elst"));
assert_eq!(parsed.header.size, 56);
let mut iter = super::BoxIter::new(&mut stream);
let mut stream = iter.next_box().unwrap().unwrap();
assert_eq!(stream.head.name, BoxType::EditListBox);
assert_eq!(stream.head.size, 56);
let parsed = super::read_elst(&mut stream).unwrap();
assert_eq!(parsed.edits.len(), 2);
assert_eq!(parsed.edits[1].segment_duration, 1234);
assert_eq!(parsed.edits[1].media_time, 5678);
@ -211,10 +235,11 @@ fn read_mdhd_v0() {
.B32(5678) // duration
.B32(0)
});
let header = read_box_header(&mut stream).unwrap();
let parsed = super::read_mdhd(&mut stream, &header).unwrap();
assert_eq!(parsed.header.name, FourCC(*b"mdhd"));
assert_eq!(parsed.header.size, 32);
let mut iter = super::BoxIter::new(&mut stream);
let mut stream = iter.next_box().unwrap().unwrap();
assert_eq!(stream.head.name, BoxType::MediaHeaderBox);
assert_eq!(stream.head.size, 32);
let parsed = super::read_mdhd(&mut stream).unwrap();
assert_eq!(parsed.timescale, 1234);
assert_eq!(parsed.duration, 5678);
}
@ -228,10 +253,11 @@ fn read_mdhd_v1() {
.B64(5678) // duration
.B32(0)
});
let header = read_box_header(&mut stream).unwrap();
let parsed = super::read_mdhd(&mut stream, &header).unwrap();
assert_eq!(parsed.header.name, FourCC(*b"mdhd"));
assert_eq!(parsed.header.size, 44);
let mut iter = super::BoxIter::new(&mut stream);
let mut stream = iter.next_box().unwrap().unwrap();
assert_eq!(stream.head.name, BoxType::MediaHeaderBox);
assert_eq!(stream.head.size, 44);
let parsed = super::read_mdhd(&mut stream).unwrap();
assert_eq!(parsed.timescale, 1234);
assert_eq!(parsed.duration, 5678);
}
@ -245,10 +271,11 @@ fn read_mdhd_unknown_duration() {
.B32(::std::u32::MAX) // duration
.B32(0)
});
let header = read_box_header(&mut stream).unwrap();
let parsed = super::read_mdhd(&mut stream, &header).unwrap();
assert_eq!(parsed.header.name, FourCC(*b"mdhd"));
assert_eq!(parsed.header.size, 32);
let mut iter = super::BoxIter::new(&mut stream);
let mut stream = iter.next_box().unwrap().unwrap();
assert_eq!(stream.head.name, BoxType::MediaHeaderBox);
assert_eq!(stream.head.size, 32);
let parsed = super::read_mdhd(&mut stream).unwrap();
assert_eq!(parsed.timescale, 1234);
assert_eq!(parsed.duration, ::std::u64::MAX);
}
@ -262,8 +289,11 @@ fn read_mdhd_invalid_timescale() {
.B64(5678) // duration
.B32(0)
});
let header = read_box_header(&mut stream).unwrap();
let r = super::parse_mdhd(&mut stream, &header, &mut super::Track::new(0));
let mut iter = super::BoxIter::new(&mut stream);
let mut stream = iter.next_box().unwrap().unwrap();
assert_eq!(stream.head.name, BoxType::MediaHeaderBox);
assert_eq!(stream.head.size, 44);
let r = super::parse_mdhd(&mut stream, &mut super::Track::new(0));
assert_eq!(r.is_err(), true);
}
@ -276,10 +306,11 @@ fn read_mvhd_v0() {
.B32(5678)
.append_repeated(0, 80)
});
let header = read_box_header(&mut stream).unwrap();
let parsed = super::read_mvhd(&mut stream, &header).unwrap();
assert_eq!(parsed.header.name, FourCC(*b"mvhd"));
assert_eq!(parsed.header.size, 108);
let mut iter = super::BoxIter::new(&mut stream);
let mut stream = iter.next_box().unwrap().unwrap();
assert_eq!(stream.head.name, BoxType::MovieHeaderBox);
assert_eq!(stream.head.size, 108);
let parsed = super::read_mvhd(&mut stream).unwrap();
assert_eq!(parsed.timescale, 1234);
assert_eq!(parsed.duration, 5678);
}
@ -293,10 +324,11 @@ fn read_mvhd_v1() {
.B64(5678)
.append_repeated(0, 80)
});
let header = read_box_header(&mut stream).unwrap();
let parsed = super::read_mvhd(&mut stream, &header).unwrap();
assert_eq!(parsed.header.name, FourCC(*b"mvhd"));
assert_eq!(parsed.header.size, 120);
let mut iter = super::BoxIter::new(&mut stream);
let mut stream = iter.next_box().unwrap().unwrap();
assert_eq!(stream.head.name, BoxType::MovieHeaderBox);
assert_eq!(stream.head.size, 120);
let parsed = super::read_mvhd(&mut stream).unwrap();
assert_eq!(parsed.timescale, 1234);
assert_eq!(parsed.duration, 5678);
}
@ -310,8 +342,11 @@ fn read_mvhd_invalid_timescale() {
.B64(5678)
.append_repeated(0, 80)
});
let header = read_box_header(&mut stream).unwrap();
let r = super::parse_mvhd(&mut stream, &header);
let mut iter = super::BoxIter::new(&mut stream);
let mut stream = iter.next_box().unwrap().unwrap();
assert_eq!(stream.head.name, BoxType::MovieHeaderBox);
assert_eq!(stream.head.size, 120);
let r = super::parse_mvhd(&mut stream);
assert_eq!(r.is_err(), true);
}
@ -324,10 +359,11 @@ fn read_mvhd_unknown_duration() {
.B32(::std::u32::MAX)
.append_repeated(0, 80)
});
let header = read_box_header(&mut stream).unwrap();
let parsed = super::read_mvhd(&mut stream, &header).unwrap();
assert_eq!(parsed.header.name, FourCC(*b"mvhd"));
assert_eq!(parsed.header.size, 108);
let mut iter = super::BoxIter::new(&mut stream);
let mut stream = iter.next_box().unwrap().unwrap();
assert_eq!(stream.head.name, BoxType::MovieHeaderBox);
assert_eq!(stream.head.size, 108);
let parsed = super::read_mvhd(&mut stream).unwrap();
assert_eq!(parsed.timescale, 1234);
assert_eq!(parsed.duration, ::std::u64::MAX);
}
@ -343,15 +379,16 @@ fn read_vpcc() {
.B16(data_length)
.append_repeated(42, data_length as usize)
});
let header = read_box_header(&mut stream).unwrap();
assert_eq!(header.name.as_bytes(), b"vpcC");
let mut iter = super::BoxIter::new(&mut stream);
let mut stream = iter.next_box().unwrap().unwrap();
assert_eq!(stream.head.name, BoxType::VPCodecConfigurationBox);
let r = super::read_vpcc(&mut stream);
assert!(r.is_ok());
}
#[test]
fn read_hdlr() {
let mut stream = make_fullbox(BoxSize::Short(45), b"mvhd", 0, |s| {
let mut stream = make_fullbox(BoxSize::Short(45), b"hdlr", 0, |s| {
s.B32(0)
.append_bytes(b"vide")
.B32(0)
@ -360,16 +397,17 @@ fn read_hdlr() {
.append_bytes(b"VideoHandler")
.B8(0) // null-terminate string
});
let header = read_box_header(&mut stream).unwrap();
let parsed = super::read_hdlr(&mut stream, &header).unwrap();
assert_eq!(parsed.header.name, FourCC(*b"mvhd"));
assert_eq!(parsed.header.size, 45);
assert_eq!(parsed.handler_type, FourCC(*b"vide"));
let mut iter = super::BoxIter::new(&mut stream);
let mut stream = iter.next_box().unwrap().unwrap();
assert_eq!(stream.head.name, BoxType::HandlerBox);
assert_eq!(stream.head.size, 45);
let parsed = super::read_hdlr(&mut stream).unwrap();
assert_eq!(parsed.handler_type, 0x76696465); // vide
}
#[test]
fn read_hdlr_short_name() {
let mut stream = make_fullbox(BoxSize::Short(33), b"mvhd", 0, |s| {
let mut stream = make_fullbox(BoxSize::Short(33), b"hdlr", 0, |s| {
s.B32(0)
.append_bytes(b"vide")
.B32(0)
@ -377,25 +415,247 @@ fn read_hdlr_short_name() {
.B32(0)
.B8(0) // null-terminate string
});
let header = read_box_header(&mut stream).unwrap();
let parsed = super::read_hdlr(&mut stream, &header).unwrap();
assert_eq!(parsed.header.name, FourCC(*b"mvhd"));
assert_eq!(parsed.header.size, 33);
assert_eq!(parsed.handler_type, FourCC(*b"vide"));
let mut iter = super::BoxIter::new(&mut stream);
let mut stream = iter.next_box().unwrap().unwrap();
assert_eq!(stream.head.name, BoxType::HandlerBox);
assert_eq!(stream.head.size, 33);
let parsed = super::read_hdlr(&mut stream).unwrap();
assert_eq!(parsed.handler_type, 0x76696465); // vide
}
#[test]
fn read_hdlr_zero_length_name() {
let mut stream = make_fullbox(BoxSize::Short(32), b"mvhd", 0, |s| {
let mut stream = make_fullbox(BoxSize::Short(32), b"hdlr", 0, |s| {
s.B32(0)
.append_bytes(b"vide")
.B32(0)
.B32(0)
.B32(0)
});
let header = read_box_header(&mut stream).unwrap();
let parsed = super::read_hdlr(&mut stream, &header).unwrap();
assert_eq!(parsed.header.name, FourCC(*b"mvhd"));
assert_eq!(parsed.header.size, 32);
assert_eq!(parsed.handler_type, FourCC(*b"vide"));
let mut iter = super::BoxIter::new(&mut stream);
let mut stream = iter.next_box().unwrap().unwrap();
assert_eq!(stream.head.name, BoxType::HandlerBox);
assert_eq!(stream.head.size, 32);
let parsed = super::read_hdlr(&mut stream).unwrap();
assert_eq!(parsed.handler_type, 0x76696465); // vide
}
#[test]
fn read_opus() {
let mut stream = make_box(BoxSize::Auto, b"Opus", |s| {
s.append_repeated(0, 6)
.B16(1) // data reference index
.B32(0)
.B32(0)
.B16(2) // channel count
.B16(16) // bits per sample
.B16(0)
.B16(0)
.B32(48000 << 16) // Sample rate is always 48 kHz for Opus.
.append_bytes(&make_dops().into_inner())
});
let mut iter = super::BoxIter::new(&mut stream);
let mut stream = iter.next_box().unwrap().unwrap();
let mut track = super::Track::new(0);
let r = super::read_audio_desc(&mut stream, &mut track);
assert!(r.is_ok());
}
fn make_dops() -> Cursor<Vec<u8>> {
make_box(BoxSize::Auto, b"dOps", |s| {
s.B8(0) // version
.B8(2) // channel count
.B16(348) // pre-skip
.B32(44100) // original sample rate
.B16(0) // gain
.B8(0) // channel mapping
})
}
#[test]
fn read_dops() {
let mut stream = make_dops();
let mut iter = super::BoxIter::new(&mut stream);
let mut stream = iter.next_box().unwrap().unwrap();
assert_eq!(stream.head.name, BoxType::OpusSpecificBox);
let r = super::read_dops(&mut stream);
assert!(r.is_ok());
}
#[test]
fn serialize_opus_header() {
let opus = super::OpusSpecificBox {
version: 0,
output_channel_count: 1,
pre_skip: 342,
input_sample_rate: 24000,
output_gain: 0,
channel_mapping_family: 0,
channel_mapping_table: None,
};
let mut v = Vec::<u8>::new();
super::serialize_opus_header(&opus, &mut v).unwrap();
assert!(v.len() == 19);
assert!(v == vec![
0x4f, 0x70, 0x75, 0x73, 0x48,0x65, 0x61, 0x64,
0x01, 0x01, 0x56, 0x01,
0xc0, 0x5d, 0x00, 0x00,
0x00, 0x00, 0x00,
]);
let opus = super::OpusSpecificBox {
version: 0,
output_channel_count: 6,
pre_skip: 152,
input_sample_rate: 48000,
output_gain: 0,
channel_mapping_family: 1,
channel_mapping_table: Some(super::ChannelMappingTable {
stream_count: 4,
coupled_count: 2,
channel_mapping: vec![0, 4, 1, 2, 3, 5],
}),
};
let mut v = Vec::<u8>::new();
super::serialize_opus_header(&opus, &mut v).unwrap();
assert!(v.len() == 27);
assert!(v == vec![
0x4f, 0x70, 0x75, 0x73, 0x48,0x65, 0x61, 0x64,
0x01, 0x06, 0x98, 0x00,
0x80, 0xbb, 0x00, 0x00,
0x00, 0x00, 0x01, 0x04, 0x02,
0x00, 0x04, 0x01, 0x02, 0x03, 0x05,
]);
}
#[test]
fn avcc_limit() {
let mut stream = make_box(BoxSize::Auto, b"avc1", |s| {
s.append_repeated(0, 6)
.B16(1)
.append_repeated(0, 16)
.B16(320)
.B16(240)
.append_repeated(0, 14)
.append_repeated(0, 32)
.append_repeated(0, 4)
.B32(0xffffffff)
.append_bytes(b"avcC")
.append_repeated(0, 100)
});
let mut iter = super::BoxIter::new(&mut stream);
let mut stream = iter.next_box().unwrap().unwrap();
let mut track = super::Track::new(0);
match super::read_video_desc(&mut stream, &mut track) {
Err(Error::InvalidData(s)) => assert_eq!(s, "avcC box exceeds BUF_SIZE_LIMIT"),
Ok(_) => assert!(false, "expected an error result"),
_ => assert!(false, "expected a different error result"),
}
}
#[test]
fn esds_limit() {
let mut stream = make_box(BoxSize::Auto, b"mp4a", |s| {
s.append_repeated(0, 6)
.B16(1)
.B32(0)
.B32(0)
.B16(2)
.B16(16)
.B16(0)
.B16(0)
.B32(48000 << 16)
.B32(0xffffffff)
.append_bytes(b"esds")
.append_repeated(0, 100)
});
let mut iter = super::BoxIter::new(&mut stream);
let mut stream = iter.next_box().unwrap().unwrap();
let mut track = super::Track::new(0);
match super::read_audio_desc(&mut stream, &mut track) {
Err(Error::InvalidData(s)) => assert_eq!(s, "esds box exceeds BUF_SIZE_LIMIT"),
Ok(_) => assert!(false, "expected an error result"),
_ => assert!(false, "expected a different error result"),
}
}
#[test]
fn esds_limit_2() {
let mut stream = make_box(BoxSize::Auto, b"mp4a", |s| {
s.append_repeated(0, 6)
.B16(1)
.B32(0)
.B32(0)
.B16(2)
.B16(16)
.B16(0)
.B16(0)
.B32(48000 << 16)
.B32(8)
.append_bytes(b"esds")
.append_repeated(0, 4)
});
let mut iter = super::BoxIter::new(&mut stream);
let mut stream = iter.next_box().unwrap().unwrap();
let mut track = super::Track::new(0);
match super::read_audio_desc(&mut stream, &mut track) {
Err(Error::UnexpectedEOF) => (),
Ok(_) => assert!(false, "expected an error result"),
_ => assert!(false, "expected a different error result"),
}
}
#[test]
fn read_elst_zero_entries() {
let mut stream = make_fullbox(BoxSize::Auto, b"elst", 0, |s| {
s.B32(0)
.B16(12)
.B16(34)
});
let mut iter = super::BoxIter::new(&mut stream);
let mut stream = iter.next_box().unwrap().unwrap();
match super::read_elst(&mut stream) {
Err(Error::InvalidData(s)) => assert_eq!(s, "invalid edit count"),
Ok(_) => assert!(false, "expected an error result"),
_ => assert!(false, "expected a different error result"),
}
}
fn make_elst() -> Cursor<Vec<u8>> {
make_fullbox(BoxSize::Auto, b"elst", 1, |s| {
s.B32(1)
// first entry
.B64(1234) // duration
.B64(0xffffffffffffffff) // time
.B16(12) // rate integer
.B16(34) // rate fraction
})
}
#[test]
fn read_edts_bogus() {
// First edit list entry has a media_time of -1, so we expect a second
// edit list entry to be present to provide a valid media_time.
let mut stream = make_box(BoxSize::Auto, b"edts", |s| {
s.append_bytes(&make_elst().into_inner())
});
let mut iter = super::BoxIter::new(&mut stream);
let mut stream = iter.next_box().unwrap().unwrap();
let mut track = super::Track::new(0);
match super::read_edts(&mut stream, &mut track) {
Err(Error::InvalidData(s)) => assert_eq!(s, "expected additional edit"),
Ok(_) => assert!(false, "expected an error result"),
_ => assert!(false, "expected a different error result"),
}
}
#[test]
fn invalid_pascal_string() {
// String claims to be 32 bytes long (we provide 33 bytes to account for
// the 1 byte length prefix).
let pstr = "\x20xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
let mut stream = Cursor::new(pstr);
// Reader wants to limit the total read length to 32 bytes, so any
// returned string must be no longer than 31 bytes.
let s = super::read_fixed_length_pascal_string(&mut stream, 32).unwrap();
assert_eq!(s.len(), 31);
}