зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
8c0fe7a521
Коммит
f3eb2d75ba
|
@ -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);
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче