зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1624056 - Properly vendor mp4parse-rust. r=kinetik
This requires --build-peers-said-large-imports-were-ok since third_party/rust/mp4parse/src/lib.rs is 113KB. This code is just moving from media/mp4parse-rust to third_party/rust, so it's not really adding to net code size. Differential Revision: https://phabricator.services.mozilla.com/D74488
This commit is contained in:
Родитель
e28d058a27
Коммит
ff79fde159
|
@ -17,6 +17,11 @@ git = "https://github.com/mozilla/neqo"
|
|||
replace-with = "vendored-sources"
|
||||
tag = "v0.2.4"
|
||||
|
||||
[source."https://github.com/mozilla/mp4parse-rust"]
|
||||
git = "https://github.com/mozilla/mp4parse-rust"
|
||||
replace-with = "vendored-sources"
|
||||
rev = "0dc3e6e7c5371fe21f69b847f61c65fe6d6dc317"
|
||||
|
||||
[source."https://github.com/mozilla/application-services"]
|
||||
git = "https://github.com/mozilla/application-services"
|
||||
replace-with = "vendored-sources"
|
||||
|
|
|
@ -2940,11 +2940,12 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "mp4parse"
|
||||
version = "0.11.4"
|
||||
source = "git+https://github.com/mozilla/mp4parse-rust?rev=0dc3e6e7c5371fe21f69b847f61c65fe6d6dc317#0dc3e6e7c5371fe21f69b847f61c65fe6d6dc317"
|
||||
dependencies = [
|
||||
"bitreader",
|
||||
"byteorder",
|
||||
"hashbrown",
|
||||
"log",
|
||||
"mp4parse_fallible",
|
||||
"num-traits",
|
||||
"static_assertions",
|
||||
]
|
||||
|
@ -2955,7 +2956,8 @@ version = "0.1.0"
|
|||
|
||||
[[package]]
|
||||
name = "mp4parse_capi"
|
||||
version = "0.11.2"
|
||||
version = "0.11.4"
|
||||
source = "git+https://github.com/mozilla/mp4parse-rust?rev=0dc3e6e7c5371fe21f69b847f61c65fe6d6dc317#0dc3e6e7c5371fe21f69b847f61c65fe6d6dc317"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"log",
|
||||
|
@ -2963,12 +2965,6 @@ dependencies = [
|
|||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mp4parse_fallible"
|
||||
version = "0.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "704f773471ac3e7110427b6bdf93184932b19319c9b7717688da5424e519b10a"
|
||||
|
||||
[[package]]
|
||||
name = "msdos_time"
|
||||
version = "0.1.6"
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
This directory exists to provide the C++ interface to the mp4parse-rust code.
|
||||
The code itself is hosted at https://github.com/mozilla/mp4parse-rust and
|
||||
vendored into the /third_party/rust directory, so the only things here are the
|
||||
moz.build file which specifies how the dynamically generated bindings header
|
||||
should be generated and mp4parse.h, the header which consumers should include.
|
||||
It includes the dynamically-generated bindings header as well as any
|
||||
additional support code for FFI.
|
||||
|
||||
# Updating the version from the github repository
|
||||
|
||||
1. In /toolkit/library/rust/shared/Cargo.toml, Set the `rev` attribute of the
|
||||
`mp4parse_capi` dependency to the revision you want to use.
|
||||
2. Run `mach vendor rust` (`--build-peers-said-large-imports-were-ok` may be
|
||||
necessary since the `mp4parse` crate's lib.rs is quite large).
|
||||
3. Verify the expected changes in /third_party/rust.
|
||||
4. Build, run try, etc.
|
||||
|
||||
NOTE: Git has no concept of checking out a subdirectory, so `cargo` will
|
||||
search the whole repository to find the crate. Because the `mp4parse_capi`
|
||||
depends on the `mp4parse` crate in the same repository via a relative path,
|
||||
both crates will be vendored into /third_party/rust and should be part of the
|
||||
same revision of mp4parse-rust.
|
||||
|
||||
# Developing changes to mp4parse-rust crates
|
||||
|
||||
Before committing changes to the github repository, it's a good idea to test
|
||||
them out in the context of mozilla-central. There are a number of ways to
|
||||
achieve this with various trade-offs, but here are the two I recommend. Both
|
||||
start the same way:
|
||||
|
||||
1. `git clone https://github.com/mozilla/mp4parse-rust` somewhere outside
|
||||
mozilla-central. For example: /Users/your_username/src/mp4parse-rust.
|
||||
|
||||
## For rapid iteration on local, uncommitted changes
|
||||
|
||||
2. In /toolkit/library/rust/shared/Cargo.toml, change the `mp4parse_capi`
|
||||
dependency to
|
||||
```
|
||||
mp4parse_capi = { path = "/Users/your_username/src/mp4parse-rust/mp4parse_capi" }
|
||||
```
|
||||
3. Run `mach vendor rust`; the code in third_party/rust/mp4parse_capi and
|
||||
third_party/rust/mp4parse will be removed, indicating the code in your
|
||||
local checkout is being used instead.
|
||||
4. In the moz.build in this directory, in `ffi_generated.inputs`, change
|
||||
'/third_party/rust/mp4parse_capi' to
|
||||
'//Users/your_username/src/mp4parse-rust/mp4parse_capi'. Note the
|
||||
double-slash, it's required to reference paths outside mozilla-central.
|
||||
5. Build and run the code or tests normally to exercise the code in
|
||||
/Users/your_username/src/mp4parse-rust.
|
||||
|
||||
This is a fast way to try experiment with the rust code, but because it exists
|
||||
outside the mozilla-central tree, it cannot be used with try.
|
||||
|
||||
## For validation of local, committed changes
|
||||
|
||||
2. In /toolkit/library/rust/shared/Cargo.toml, change the `mp4parse_capi`
|
||||
dependency to
|
||||
```
|
||||
mp4parse_capi = { git = "file:///Users/your_username/src/mp4parse-rust" }
|
||||
```
|
||||
3. Run `mach vendor rust`; the local, committed code will be copied into
|
||||
third_party/rust/mp4parse_capi and third_party/rust/mp4parse. Confirm the
|
||||
diff is what you expect.
|
||||
4. Unlike above, no changes to moz.build are necessary; if locally changed,
|
||||
make sure to revert.
|
||||
5. Build and run the code or tests normally to exercise the code in
|
||||
/Users/your_username/src/mp4parse-rust. This can include try runs, but if
|
||||
you make any additional changes, you must be sure to commit them in your
|
||||
local git checkout of mp4parse-rust and re-run `mach vendor rust`.
|
||||
|
||||
This is a more thorough way to test local changes to the rust code since try
|
||||
is available, but it's slower than the `path` dependency approach above
|
||||
because every change must be committed and copied into the mozilla-central
|
||||
tree with `mach vendor rust`.
|
|
@ -21,7 +21,7 @@ if CONFIG['COMPILE_ENVIRONMENT']:
|
|||
ffi_generated = GENERATED_FILES['mp4parse_ffi_generated.h']
|
||||
ffi_generated.script = '/build/RunCbindgen.py:generate'
|
||||
ffi_generated.inputs = [
|
||||
'/media/mp4parse-rust/mp4parse_capi',
|
||||
'/third_party/rust/mp4parse_capi',
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
diff --git a/media/mp4parse-rust/_upstream/mp4parse/mp4parse_capi/Cargo.toml b/media/mp4parse-rust/mp4parse_capi/Cargo.toml
|
||||
index 2c606b4..895739f 100644
|
||||
--- a/media/mp4parse-rust/_upstream/mp4parse/mp4parse_capi/Cargo.toml
|
||||
+++ b/media/mp4parse-rust/mp4parse_capi/Cargo.toml
|
||||
@@ -18,8 +18,7 @@ exclude = [
|
||||
"*.mp4",
|
||||
]
|
||||
|
||||
-[lib]
|
||||
-crate-type = ["lib", "cdylib"]
|
||||
+build = false # See bug 1611431 - Generate mp4parse-rust bindings as part of mach build
|
||||
|
||||
[badges]
|
||||
travis-ci = { repository = "https://github.com/mozilla/mp4parse-rust" }
|
||||
@@ -33,9 +32,6 @@ num-traits = "0.2.0"
|
||||
[dev-dependencies]
|
||||
env_logger = "0.7.1"
|
||||
|
||||
-[build-dependencies]
|
||||
-cbindgen = "0.5.2"
|
||||
-
|
||||
[features]
|
||||
# Enable mp4parse_fallible to use fallible memory allocation rather than
|
||||
# panicking on OOM. Note that this is only safe within Gecko where the system
|
|
@ -2,8 +2,8 @@
|
|||
// 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 mozilla_media_mp4parse_rust_mp4parse_h
|
||||
#define mozilla_media_mp4parse_rust_mp4parse_h
|
||||
#ifndef mp4parse_rust_mp4parse_h
|
||||
#define mp4parse_rust_mp4parse_h
|
||||
|
||||
#include "mp4parse_ffi_generated.h" // prepend mozilla/media when we namespace this
|
||||
|
||||
|
|
Двоичные данные
media/mp4parse-rust/mp4parse/tests/bipbop-cenc-audioinit.mp4
Двоичные данные
media/mp4parse-rust/mp4parse/tests/bipbop-cenc-audioinit.mp4
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичные данные
media/mp4parse-rust/mp4parse/tests/bipbop_cbcs_audio_init.mp4
Двоичные данные
media/mp4parse-rust/mp4parse/tests/bipbop_cbcs_audio_init.mp4
Двоичный файл не отображается.
Двоичные данные
media/mp4parse-rust/mp4parse/tests/bipbop_cbcs_video_init.mp4
Двоичные данные
media/mp4parse-rust/mp4parse/tests/bipbop_cbcs_video_init.mp4
Двоичный файл не отображается.
Двоичные данные
media/mp4parse-rust/mp4parse/tests/metadata.mp4
Двоичные данные
media/mp4parse-rust/mp4parse/tests/metadata.mp4
Двоичный файл не отображается.
Двоичные данные
media/mp4parse-rust/mp4parse/tests/metadata_gnre.mp4
Двоичные данные
media/mp4parse-rust/mp4parse/tests/metadata_gnre.mp4
Двоичный файл не отображается.
Двоичные данные
media/mp4parse-rust/mp4parse/tests/minimal.mp4
Двоичные данные
media/mp4parse-rust/mp4parse/tests/minimal.mp4
Двоичный файл не отображается.
Двоичные данные
media/mp4parse-rust/mp4parse/tests/tiny_av1.mp4
Двоичные данные
media/mp4parse-rust/mp4parse/tests/tiny_av1.mp4
Двоичный файл не отображается.
|
@ -1,51 +0,0 @@
|
|||
#!/bin/bash
|
||||
# Script to update mp4parse-rust sources to latest upstream
|
||||
|
||||
set -e
|
||||
|
||||
# Default version.
|
||||
VER="63ca8c6bde27b39dea87fb15e2922a23cb38c8df"
|
||||
|
||||
# Accept version or commit from the command line.
|
||||
if test -n "$1"; then
|
||||
VER=$1
|
||||
fi
|
||||
|
||||
echo "Fetching sources..."
|
||||
rm -rf _upstream
|
||||
git clone --recurse-submodules https://github.com/mozilla/mp4parse-rust _upstream/mp4parse
|
||||
|
||||
pushd _upstream/mp4parse
|
||||
git checkout ${VER}
|
||||
echo "Verifying sources..."
|
||||
cargo test
|
||||
popd
|
||||
rm -rf mp4parse
|
||||
mkdir -p mp4parse/src
|
||||
cp _upstream/mp4parse/mp4parse/Cargo.toml mp4parse/
|
||||
cp _upstream/mp4parse/mp4parse/src/*.rs mp4parse/src/
|
||||
mkdir -p mp4parse/tests
|
||||
cp _upstream/mp4parse/mp4parse/tests/*.rs mp4parse/tests/
|
||||
cp _upstream/mp4parse/mp4parse/tests/*.mp4 mp4parse/tests/
|
||||
# Remove everything but the cbindgen.toml, since it's needed to configure the
|
||||
# creation of the bindings as part of moz.build
|
||||
find mp4parse_capi -not -name cbindgen.toml -delete
|
||||
mkdir -p mp4parse_capi/src
|
||||
cp _upstream/mp4parse/mp4parse_capi/Cargo.toml mp4parse_capi/
|
||||
cp _upstream/mp4parse/mp4parse_capi/src/*.rs mp4parse_capi/src/
|
||||
|
||||
echo "Applying patches..."
|
||||
patch -p3 < mp4parse-cargo.patch
|
||||
|
||||
echo "Cleaning up..."
|
||||
rm -rf _upstream
|
||||
|
||||
echo "Updating gecko Cargo.lock..."
|
||||
pushd ../../toolkit/library/rust/
|
||||
cargo update --package mp4parse_capi
|
||||
popd
|
||||
pushd ../../toolkit/library/gtest/rust/
|
||||
cargo update --package mp4parse_capi
|
||||
popd
|
||||
|
||||
echo "Updated to ${VER}."
|
|
@ -0,0 +1 @@
|
|||
{"files":{"Cargo.toml":"107804fbf8f667fbad45e7dea9fa1bb32ce8ef5580b543a54455e678d7769708","src/boxes.rs":"623f948e69244db586d7cc1855e0a1828766cbe2d4998277d170047d0ae0fce0","src/fallible.rs":"836a36c2bc9803aead4bb24621e4fa6176c77b3752e69459a1f36555eb8bf2ec","src/lib.rs":"05d2e8523eab120bbc712adea9cd47979da3e95f54ff5816e6738ebeaf90afb2","src/macros.rs":"76c840f9299797527fe71aa5b378ffb01312767372b45cc62deddb19775400ae","src/tests.rs":"7c7f69051f8b8edcb994d3813bcca7277cc71bcfb0a6eaadbbdbb9a1f777ff88","tests/overflow.rs":"16b591d8def1a155b3b997622f6ea255536870d99c3d8f97c51755b77a50de3c","tests/public.rs":"eb02970aa69c31855936b4ec24dcf09b300fad274b620c879d9b6dfbcd89444c"},"package":null}
|
|
@ -18,6 +18,7 @@ repository = "https://github.com/mozilla/mp4parse-rust"
|
|||
# Avoid complaints about trying to package test files.
|
||||
exclude = [
|
||||
"*.mp4",
|
||||
"av1-avif/*"
|
||||
]
|
||||
|
||||
[badges]
|
||||
|
@ -26,11 +27,18 @@ travis-ci = { repository = "https://github.com/mozilla/mp4parse-rust" }
|
|||
[dependencies]
|
||||
byteorder = "1.2.1"
|
||||
bitreader = { version = "0.3.2" }
|
||||
hashbrown = "0.7.1"
|
||||
num-traits = "0.2.0"
|
||||
mp4parse_fallible = { version = "0.0.3", optional = true }
|
||||
log = "0.4"
|
||||
static_assertions = "1.1.0"
|
||||
|
||||
[dev-dependencies]
|
||||
test-assembler = "0.1.2"
|
||||
env_logger = "0.7.1"
|
||||
walkdir = "2.3.1"
|
||||
|
||||
[features]
|
||||
# Enable mp4parse_fallible to use fallible memory allocation rather than
|
||||
# panicking on OOM. Note that this is only safe within Gecko where the system
|
||||
# allocator has been globally overridden (see BMO 1457359).
|
||||
mp4parse_fallible = []
|
|
@ -3,6 +3,16 @@
|
|||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
use std::fmt;
|
||||
|
||||
// To ensure we don't use stdlib allocating types by accident
|
||||
#[allow(dead_code)]
|
||||
struct Vec;
|
||||
#[allow(dead_code)]
|
||||
struct Box;
|
||||
#[allow(dead_code)]
|
||||
struct HashMap;
|
||||
#[allow(dead_code)]
|
||||
struct String;
|
||||
|
||||
macro_rules! box_database {
|
||||
($($boxenum:ident $boxtype:expr),*,) => {
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
|
@ -42,24 +52,14 @@ macro_rules! box_database {
|
|||
|
||||
#[derive(Default, PartialEq, Clone)]
|
||||
pub struct FourCC {
|
||||
pub value: String,
|
||||
pub value: [u8; 4],
|
||||
}
|
||||
|
||||
impl From<u32> for FourCC {
|
||||
fn from(number: u32) -> FourCC {
|
||||
let mut box_chars = Vec::new();
|
||||
for x in 0..4 {
|
||||
let c = (number >> (x * 8) & 0x0000_00FF) as u8;
|
||||
box_chars.push(c);
|
||||
FourCC {
|
||||
value: number.to_be_bytes(),
|
||||
}
|
||||
box_chars.reverse();
|
||||
|
||||
let box_string = match String::from_utf8(box_chars) {
|
||||
Ok(t) => t,
|
||||
_ => String::from("null"), // error to retrieve fourcc
|
||||
};
|
||||
|
||||
FourCC { value: box_string }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -70,23 +70,27 @@ impl From<BoxType> for FourCC {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a str> for FourCC {
|
||||
fn from(v: &'a str) -> FourCC {
|
||||
FourCC {
|
||||
value: v.to_owned(),
|
||||
}
|
||||
impl From<[u8; 4]> for FourCC {
|
||||
fn from(v: [u8; 4]) -> FourCC {
|
||||
FourCC { value: v }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for FourCC {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.value)
|
||||
match std::str::from_utf8(&self.value) {
|
||||
Ok(s) => write!(f, "{}", s),
|
||||
Err(_) => self.value.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for FourCC {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.value)
|
||||
match std::str::from_utf8(&self.value) {
|
||||
Ok(s) => write!(f, "{}", s),
|
||||
Err(_) => write!(f, "null"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,520 @@
|
|||
//! The types in this module are thin wrappers around the stdlib types to add
|
||||
//! support for fallible allocation. Though the fallible allocator is only
|
||||
//! enabled with the mp4parse_fallible feature, the API differences from the
|
||||
//! stdlib types ensure that all operations which allocate return a Result.
|
||||
//! For the most part, this simply means adding a Result return value to
|
||||
//! functions which return nothing or a non-Result value. However, these types
|
||||
//! implement some traits whose API cannot communicate failure, but which do
|
||||
//! require allocation, so it is important that these wrapper types do not
|
||||
//! implement these traits.
|
||||
//!
|
||||
//! Specifically, do not implement any of the following traits:
|
||||
//! - Clone
|
||||
//! - Extend
|
||||
//! - From
|
||||
//! - FromIterator
|
||||
//!
|
||||
//! This list may not be exhaustive. Exercise caution when implementing
|
||||
//! any new traits to ensure they won't potentially allocate in a way that
|
||||
//! can't return a Result to indicate allocation failure.
|
||||
|
||||
#[cfg(feature = "mp4parse_fallible")]
|
||||
extern "C" {
|
||||
fn malloc(bytes: usize) -> *mut u8;
|
||||
fn realloc(ptr: *mut u8, bytes: usize) -> *mut u8;
|
||||
}
|
||||
|
||||
use std::convert::TryInto as _;
|
||||
use std::hash::Hash;
|
||||
use std::io::Read;
|
||||
use std::io::Take;
|
||||
use std::iter::IntoIterator;
|
||||
use BMFFBox;
|
||||
|
||||
type Result<T> = std::result::Result<T, super::Error>;
|
||||
|
||||
pub trait TryRead {
|
||||
fn try_read_to_end(&mut self, buf: &mut TryVec<u8>) -> Result<usize>;
|
||||
|
||||
fn read_into_try_vec(&mut self) -> Result<TryVec<u8>> {
|
||||
let mut buf = TryVec::new();
|
||||
let _ = self.try_read_to_end(&mut buf)?;
|
||||
Ok(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Read> TryRead for BMFFBox<'a, T> {
|
||||
fn try_read_to_end(&mut self, buf: &mut TryVec<u8>) -> Result<usize> {
|
||||
try_read_up_to(self, self.bytes_left(), buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Read> TryRead for Take<T> {
|
||||
/// With the `mp4parse_fallible` feature enabled, this function reserves the
|
||||
/// upper limit of what `src` can generate before reading all bytes until EOF
|
||||
/// in this source, placing them into buf. If the allocation is unsuccessful,
|
||||
/// or reading from the source generates an error before reaching EOF, this
|
||||
/// will return an error. Otherwise, it will return the number of bytes read.
|
||||
///
|
||||
/// Since `Take::limit()` may return a value greater than the number of bytes
|
||||
/// which can be read from the source, it's possible this function may fail
|
||||
/// in the allocation phase even though allocating the number of bytes available
|
||||
/// to read would have succeeded. In general, it is assumed that the callers
|
||||
/// have accurate knowledge of the number of bytes of interest and have created
|
||||
/// `src` accordingly.
|
||||
///
|
||||
/// With the `mp4parse_fallible` feature disabled, this is essentially a wrapper
|
||||
/// around `std::io::Read::read_to_end()`.
|
||||
fn try_read_to_end(&mut self, buf: &mut TryVec<u8>) -> Result<usize> {
|
||||
try_read_up_to(self, self.limit(), buf)
|
||||
}
|
||||
}
|
||||
|
||||
fn try_read_up_to<R: Read>(src: &mut R, limit: u64, buf: &mut TryVec<u8>) -> Result<usize> {
|
||||
let additional = limit.try_into()?;
|
||||
buf.reserve(additional)?;
|
||||
let bytes_read = src.read_to_end(&mut buf.inner)?;
|
||||
Ok(bytes_read)
|
||||
}
|
||||
|
||||
/// TryBox is a thin wrapper around std::boxed::Box to provide support for
|
||||
/// fallible allocation.
|
||||
///
|
||||
/// See the `fallible` module documentation for more.
|
||||
pub struct TryBox<T> {
|
||||
inner: std::boxed::Box<T>,
|
||||
}
|
||||
|
||||
impl<T> TryBox<T> {
|
||||
pub fn try_new(x: T) -> Result<Self> {
|
||||
let inner;
|
||||
|
||||
#[cfg(feature = "mp4parse_fallible")]
|
||||
{
|
||||
let size = std::mem::size_of::<T>();
|
||||
let new_ptr = unsafe { malloc(size) as *mut T };
|
||||
|
||||
if new_ptr.is_null() {
|
||||
return Err(super::Error::OutOfMemory);
|
||||
}
|
||||
|
||||
// If we did a simple assignment: *new_ptr = x, then the value
|
||||
// pointed to by new_ptr would immediately be dropped, but
|
||||
// that would cause an invalid memory access. Instead, we use
|
||||
// replace() to avoid the immediate drop and forget() to
|
||||
// ensure that drop never happens.
|
||||
inner = unsafe {
|
||||
std::mem::forget(std::mem::replace(&mut *new_ptr, x));
|
||||
Box::from_raw(new_ptr)
|
||||
};
|
||||
}
|
||||
#[cfg(not(feature = "mp4parse_fallible"))]
|
||||
{
|
||||
inner = Box::new(x);
|
||||
}
|
||||
|
||||
Ok(Self { inner })
|
||||
}
|
||||
|
||||
pub fn into_raw(b: TryBox<T>) -> *mut T {
|
||||
Box::into_raw(b.inner)
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// See std::boxed::from_raw
|
||||
pub unsafe fn from_raw(raw: *mut T) -> Self {
|
||||
Self {
|
||||
inner: Box::from_raw(raw),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// TryHashMap is a thin wrapper around the hashbrown HashMap to provide
|
||||
/// support for fallible allocation. This is the same library that stdlib
|
||||
/// currently uses, but we use it directly since it provides try_reserve().
|
||||
///
|
||||
/// See the `fallible` module documentation for more.
|
||||
#[derive(Default)]
|
||||
pub struct TryHashMap<K, V> {
|
||||
inner: hashbrown::hash_map::HashMap<K, V>,
|
||||
}
|
||||
|
||||
impl<K, V> TryHashMap<K, V>
|
||||
where
|
||||
K: Eq + Hash,
|
||||
{
|
||||
pub fn get<Q: ?Sized>(&self, k: &Q) -> Option<&V>
|
||||
where
|
||||
K: std::borrow::Borrow<Q>,
|
||||
Q: Hash + Eq,
|
||||
{
|
||||
self.inner.get(k)
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, k: K, v: V) -> Result<Option<V>> {
|
||||
self.reserve(if self.inner.capacity() == 0 { 4 } else { 1 })?;
|
||||
Ok(self.inner.insert(k, v))
|
||||
}
|
||||
|
||||
fn reserve(&mut self, additional: usize) -> Result<()> {
|
||||
#[cfg(feature = "mp4parse_fallible")]
|
||||
{
|
||||
self.inner
|
||||
.try_reserve(additional)
|
||||
.map_err(|_| super::Error::OutOfMemory)
|
||||
}
|
||||
#[cfg(not(feature = "mp4parse_fallible"))]
|
||||
{
|
||||
self.inner.reserve(additional);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "mp4parse_fallible")]
|
||||
fn tryhashmap_oom() {
|
||||
match TryHashMap::<char, char>::default().reserve(std::usize::MAX) {
|
||||
Ok(_) => panic!("it should be OOM"),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, PartialEq)]
|
||||
pub struct TryVec<T> {
|
||||
inner: std::vec::Vec<T>,
|
||||
}
|
||||
|
||||
impl<T: std::fmt::Debug> std::fmt::Debug for TryVec<T> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{:?}", self.inner)
|
||||
}
|
||||
}
|
||||
|
||||
/// TryVec is a thin wrapper around std::vec::Vec to provide support for
|
||||
/// fallible allocation.
|
||||
///
|
||||
/// See the `fallible` module documentation for more.
|
||||
impl<T> TryVec<T> {
|
||||
pub fn new() -> Self {
|
||||
Self { inner: Vec::new() }
|
||||
}
|
||||
|
||||
pub fn with_capacity(capacity: usize) -> Result<Self> {
|
||||
let mut v = Self::new();
|
||||
v.reserve(capacity)?;
|
||||
Ok(v)
|
||||
}
|
||||
|
||||
pub fn append(&mut self, other: &mut Self) -> Result<()> {
|
||||
self.reserve(other.inner.len())?;
|
||||
self.inner.append(&mut other.inner);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn as_mut_slice(&mut self) -> &mut [T] {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn as_slice(&self) -> &[T] {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.inner.clear()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn into_inner(self) -> Vec<T> {
|
||||
self.inner
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.inner.is_empty()
|
||||
}
|
||||
|
||||
pub fn iter_mut(&mut self) -> IterMut<T> {
|
||||
IterMut {
|
||||
inner: self.inner.iter_mut(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> Iter<T> {
|
||||
Iter {
|
||||
inner: self.inner.iter(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pop(&mut self) -> Option<T> {
|
||||
self.inner.pop()
|
||||
}
|
||||
|
||||
pub fn push(&mut self, value: T) -> Result<()> {
|
||||
self.reserve(if self.inner.capacity() == 0 { 4 } else { 1 })?;
|
||||
self.inner.push(value);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn reserve(&mut self, additional: usize) -> Result<()> {
|
||||
#[cfg(feature = "mp4parse_fallible")]
|
||||
{
|
||||
let available = self
|
||||
.inner
|
||||
.capacity()
|
||||
.checked_sub(self.inner.len())
|
||||
.expect("capacity >= len");
|
||||
if additional > available {
|
||||
let increase = additional
|
||||
.checked_sub(available)
|
||||
.expect("additional > available");
|
||||
let new_cap = self
|
||||
.inner
|
||||
.capacity()
|
||||
.checked_add(increase)
|
||||
.ok_or(super::Error::OutOfMemory)?;
|
||||
self.try_extend(new_cap)?;
|
||||
debug_assert!(self.inner.capacity() == new_cap);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
#[cfg(not(feature = "mp4parse_fallible"))]
|
||||
{
|
||||
self.inner.reserve(additional);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resize_with<F>(&mut self, new_len: usize, f: F) -> Result<()>
|
||||
where
|
||||
F: FnMut() -> T,
|
||||
{
|
||||
self.reserve(new_len)?;
|
||||
self.inner.resize_with(new_len, f);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "mp4parse_fallible")]
|
||||
fn try_extend(&mut self, new_cap: usize) -> Result<()> {
|
||||
let old_ptr = self.as_mut_ptr();
|
||||
let old_len = self.inner.len();
|
||||
|
||||
let old_cap: usize = self.inner.capacity();
|
||||
|
||||
if old_cap >= new_cap {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let new_size_bytes = new_cap
|
||||
.checked_mul(std::mem::size_of::<T>())
|
||||
.ok_or(super::Error::OutOfMemory)?;
|
||||
|
||||
let new_ptr = unsafe {
|
||||
if old_cap == 0 {
|
||||
malloc(new_size_bytes)
|
||||
} else {
|
||||
realloc(old_ptr as *mut u8, new_size_bytes)
|
||||
}
|
||||
};
|
||||
|
||||
if new_ptr.is_null() {
|
||||
return Err(super::Error::OutOfMemory);
|
||||
}
|
||||
|
||||
let new_vec = unsafe { Vec::from_raw_parts(new_ptr as *mut T, old_len, new_cap) };
|
||||
|
||||
std::mem::forget(std::mem::replace(&mut self.inner, new_vec));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone> TryVec<TryVec<T>> {
|
||||
pub fn concat(&self) -> Result<TryVec<T>> {
|
||||
let size = self.iter().map(|v| v.inner.len()).sum();
|
||||
let mut result = TryVec::with_capacity(size)?;
|
||||
for v in self.iter() {
|
||||
result.extend_from_slice(&v.inner)?;
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone> TryVec<T> {
|
||||
pub fn extend_from_slice(&mut self, other: &[T]) -> Result<()> {
|
||||
self.reserve(other.len())?;
|
||||
self.inner.extend_from_slice(other);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "mp4parse_fallible")]
|
||||
fn oom() {
|
||||
let mut vec: TryVec<char> = TryVec::new();
|
||||
match vec.reserve(std::usize::MAX) {
|
||||
Ok(_) => panic!("it should be OOM"),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn try_reserve() {
|
||||
let mut vec: TryVec<_> = vec![1].into();
|
||||
let old_cap = vec.inner.capacity();
|
||||
let new_cap = old_cap + 1;
|
||||
vec.reserve(new_cap).unwrap();
|
||||
assert!(vec.inner.capacity() >= new_cap);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn try_reserve_idempotent() {
|
||||
let mut vec: TryVec<_> = vec![1].into();
|
||||
let old_cap = vec.inner.capacity();
|
||||
let new_cap = old_cap + 1;
|
||||
vec.reserve(new_cap).unwrap();
|
||||
let cap_after_reserve = vec.inner.capacity();
|
||||
vec.reserve(new_cap).unwrap();
|
||||
assert_eq!(cap_after_reserve, vec.inner.capacity());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "mp4parse_fallible")]
|
||||
fn capacity_overflow() {
|
||||
let mut vec: TryVec<_> = vec![1].into();
|
||||
match vec.reserve(std::usize::MAX) {
|
||||
Ok(_) => panic!("capacity calculation should overflow"),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extend_from_slice() {
|
||||
let mut vec: TryVec<u8> = b"foo".as_ref().try_into().unwrap();
|
||||
vec.extend_from_slice(b"bar").unwrap();
|
||||
assert_eq!(vec, b"foobar".as_ref());
|
||||
}
|
||||
|
||||
impl<T> IntoIterator for TryVec<T> {
|
||||
type Item = T;
|
||||
type IntoIter = std::vec::IntoIter<T>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.inner.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> IntoIterator for &'a TryVec<T> {
|
||||
type Item = &'a T;
|
||||
type IntoIter = std::slice::Iter<'a, T>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.inner.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::io::Write for TryVec<u8> {
|
||||
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
||||
self.extend_from_slice(buf)?;
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> std::io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PartialEq> PartialEq<Vec<T>> for TryVec<T> {
|
||||
fn eq(&self, other: &Vec<T>) -> bool {
|
||||
self.inner.eq(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: PartialEq> PartialEq<&'a [T]> for TryVec<T> {
|
||||
fn eq(&self, other: &&[T]) -> bool {
|
||||
self.inner.eq(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<&str> for TryVec<u8> {
|
||||
fn eq(&self, other: &&str) -> bool {
|
||||
self.as_slice() == other.as_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::AsRef<[u8]> for TryVec<u8> {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
self.inner.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> std::convert::From<Vec<T>> for TryVec<T> {
|
||||
fn from(value: Vec<T>) -> Self {
|
||||
Self { inner: value }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone> std::convert::TryFrom<&[T]> for TryVec<T> {
|
||||
type Error = super::Error;
|
||||
|
||||
fn try_from(value: &[T]) -> Result<Self> {
|
||||
let mut v = Self::new();
|
||||
v.extend_from_slice(value)?;
|
||||
Ok(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::TryFrom<&str> for TryVec<u8> {
|
||||
type Error = super::Error;
|
||||
|
||||
fn try_from(value: &str) -> Result<Self> {
|
||||
let mut v = Self::new();
|
||||
v.extend_from_slice(value.as_bytes())?;
|
||||
Ok(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> std::ops::Deref for TryVec<T> {
|
||||
type Target = [T];
|
||||
|
||||
fn deref(&self) -> &[T] {
|
||||
self.inner.deref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> std::ops::DerefMut for TryVec<T> {
|
||||
fn deref_mut(&mut self) -> &mut [T] {
|
||||
self.inner.deref_mut()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Iter<'a, T> {
|
||||
inner: std::slice::Iter<'a, T>,
|
||||
}
|
||||
|
||||
impl<'a, T> Iterator for Iter<'a, T> {
|
||||
type Item = &'a T;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.inner.next()
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.inner.size_hint()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct IterMut<'a, T> {
|
||||
inner: std::slice::IterMut<'a, T>,
|
||||
}
|
||||
|
||||
impl<'a, T> Iterator for IterMut<'a, T> {
|
||||
type Item = &'a mut T;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.inner.next()
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.inner.size_hint()
|
||||
}
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -5,9 +5,11 @@
|
|||
// 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/.
|
||||
|
||||
use super::fallible::TryRead as _;
|
||||
use super::read_mp4;
|
||||
use super::Error;
|
||||
use super::MediaContext;
|
||||
|
||||
#[cfg(feature = "mp4parse_fallible")]
|
||||
use std::convert::TryInto as _;
|
||||
use std::io::Cursor;
|
||||
|
@ -179,11 +181,11 @@ fn read_ftyp() {
|
|||
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, FourCC::from("mp42")); // mp42
|
||||
assert_eq!(parsed.major_brand, FourCC::from(*b"mp42")); // mp42
|
||||
assert_eq!(parsed.minor_version, 0);
|
||||
assert_eq!(parsed.compatible_brands.len(), 2);
|
||||
assert_eq!(parsed.compatible_brands[0], FourCC::from("isom")); // isom
|
||||
assert_eq!(parsed.compatible_brands[1], FourCC::from("mp42")); // mp42
|
||||
assert_eq!(parsed.compatible_brands[0], FourCC::from(*b"isom")); // isom
|
||||
assert_eq!(parsed.compatible_brands[1], FourCC::from(*b"mp42")); // mp42
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -221,11 +223,11 @@ fn read_ftyp_case() {
|
|||
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, FourCC::from("MP42"));
|
||||
assert_eq!(parsed.major_brand, FourCC::from(*b"MP42"));
|
||||
assert_eq!(parsed.minor_version, 0);
|
||||
assert_eq!(parsed.compatible_brands.len(), 2);
|
||||
assert_eq!(parsed.compatible_brands[0], FourCC::from("ISOM")); // ISOM
|
||||
assert_eq!(parsed.compatible_brands[1], FourCC::from("MP42")); // MP42
|
||||
assert_eq!(parsed.compatible_brands[0], FourCC::from(*b"ISOM")); // ISOM
|
||||
assert_eq!(parsed.compatible_brands[1], FourCC::from(*b"MP42")); // MP42
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -472,7 +474,7 @@ fn read_hdlr() {
|
|||
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, FourCC::from("vide"));
|
||||
assert_eq!(parsed.handler_type, FourCC::from(*b"vide"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -485,7 +487,7 @@ fn read_hdlr_short_name() {
|
|||
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, FourCC::from("vide"));
|
||||
assert_eq!(parsed.handler_type, FourCC::from(*b"vide"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -498,7 +500,7 @@ fn read_hdlr_zero_length_name() {
|
|||
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, FourCC::from("vide"));
|
||||
assert_eq!(parsed.handler_type, FourCC::from(*b"vide"));
|
||||
}
|
||||
|
||||
fn flac_streaminfo() -> Vec<u8> {
|
||||
|
@ -680,7 +682,7 @@ fn serialize_opus_header() {
|
|||
channel_mapping_table: Some(super::ChannelMappingTable {
|
||||
stream_count: 4,
|
||||
coupled_count: 2,
|
||||
channel_mapping: vec![0, 4, 1, 2, 3, 5],
|
||||
channel_mapping: vec![0, 4, 1, 2, 3, 5].into(),
|
||||
}),
|
||||
};
|
||||
let mut v = Vec::<u8>::new();
|
||||
|
@ -1057,7 +1059,7 @@ fn read_stsd_mp4v() {
|
|||
assert_eq!(v.height, 480);
|
||||
match v.codec_specific {
|
||||
super::VideoCodecSpecific::ESDSConfig(esds_data) => {
|
||||
assert_eq!(esds_data, esds_specific_data.to_vec());
|
||||
assert_eq!(esds_data.as_slice(), esds_specific_data);
|
||||
}
|
||||
_ => panic!("it should be ESDSConfig!"),
|
||||
}
|
||||
|
@ -1293,16 +1295,14 @@ fn read_stsd_lpcm() {
|
|||
#[test]
|
||||
fn read_to_end_() {
|
||||
let mut src = b"1234567890".take(5);
|
||||
let mut buf = vec![];
|
||||
let bytes_read = super::read_to_end(&mut src, &mut buf).unwrap();
|
||||
assert_eq!(bytes_read, 5);
|
||||
assert_eq!(buf, b"12345");
|
||||
let buf = src.read_into_try_vec().unwrap();
|
||||
assert_eq!(buf.len(), 5);
|
||||
assert_eq!(buf.into_inner(), b"12345");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "mp4parse_fallible")]
|
||||
fn read_to_end_oom() {
|
||||
let mut src = b"1234567890".take(std::usize::MAX.try_into().expect("usize < u64"));
|
||||
let mut buf = vec![];
|
||||
assert!(super::read_to_end(&mut src, &mut buf).is_err());
|
||||
assert!(src.read_into_try_vec().is_err());
|
||||
}
|
|
@ -4,6 +4,7 @@
|
|||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
extern crate mp4parse as mp4;
|
||||
|
||||
use std::convert::TryInto;
|
||||
use std::fs::File;
|
||||
use std::io::{Cursor, Read};
|
||||
use std::path::Path;
|
||||
|
@ -28,8 +29,7 @@ static VIDEO_EME_CBCS_MP4: &str = "tests/bipbop_cbcs_video_init.mp4";
|
|||
static VIDEO_AV1_MP4: &str = "tests/tiny_av1.mp4";
|
||||
static IMAGE_AVIF: &str = "av1-avif/testFiles/Microsoft/Monochrome.avif";
|
||||
static IMAGE_AVIF_GRID: &str = "av1-avif/testFiles/Microsoft/Summer_in_Tomsk_720p_5x4_grid.avif";
|
||||
static MICROSOFT_AVIF_TEST_DIR: &str = "av1-avif/testFiles/Microsoft";
|
||||
static NETFLIX_AVIF_TEST_DIR: &str = "av1-avif/testFiles/Netflix/avif";
|
||||
static AVIF_TEST_DIR: &str = "av1-avif/testFiles";
|
||||
|
||||
// Adapted from https://github.com/GuillaumeGomez/audio-video-metadata/blob/9dff40f565af71d5502e03a2e78ae63df95cfd40/src/metadata.rs#L53
|
||||
#[test]
|
||||
|
@ -175,7 +175,7 @@ fn public_metadata() {
|
|||
assert_eq!(meta.year.unwrap(), "2019");
|
||||
assert_eq!(
|
||||
meta.genre.unwrap(),
|
||||
mp4::Genre::CustomGenre("Custom Genre".to_string())
|
||||
mp4::Genre::CustomGenre("Custom Genre".try_into().unwrap())
|
||||
);
|
||||
assert_eq!(meta.encoder.unwrap(), "Lavf56.40.101");
|
||||
assert_eq!(meta.encoded_by.unwrap(), "Encoded-by");
|
||||
|
@ -311,7 +311,7 @@ fn public_audio_tenc() {
|
|||
Some(ref p) => {
|
||||
assert_eq!(p.code_name, "mp4a");
|
||||
if let Some(ref schm) = p.scheme_type {
|
||||
assert_eq!(schm.scheme_type.value, "cenc");
|
||||
assert_eq!(schm.scheme_type.value, *b"cenc");
|
||||
} else {
|
||||
panic!("Expected scheme type info");
|
||||
}
|
||||
|
@ -370,7 +370,7 @@ fn public_video_cenc() {
|
|||
Some(ref p) => {
|
||||
assert_eq!(p.code_name, "avc1");
|
||||
if let Some(ref schm) = p.scheme_type {
|
||||
assert_eq!(schm.scheme_type.value, "cenc");
|
||||
assert_eq!(schm.scheme_type.value, *b"cenc");
|
||||
} else {
|
||||
panic!("Expected scheme type info");
|
||||
}
|
||||
|
@ -443,7 +443,7 @@ fn public_audio_cbcs() {
|
|||
found_encrypted_sample_description = true;
|
||||
assert_eq!(p.code_name, "mp4a");
|
||||
if let Some(ref schm) = p.scheme_type {
|
||||
assert_eq!(schm.scheme_type.value, "cbcs");
|
||||
assert_eq!(schm.scheme_type.value, *b"cbcs");
|
||||
} else {
|
||||
panic!("Expected scheme type info");
|
||||
}
|
||||
|
@ -456,7 +456,7 @@ fn public_audio_cbcs() {
|
|||
// to indicate full encryption.
|
||||
assert_eq!(tenc.crypt_byte_block_count, Some(0));
|
||||
assert_eq!(tenc.skip_byte_block_count, Some(0));
|
||||
assert_eq!(tenc.constant_iv, Some(default_iv.clone()));
|
||||
assert_eq!(tenc.constant_iv, Some(default_iv.clone().into()));
|
||||
} else {
|
||||
panic!("Invalid test condition");
|
||||
}
|
||||
|
@ -528,7 +528,7 @@ fn public_video_cbcs() {
|
|||
found_encrypted_sample_description = true;
|
||||
assert_eq!(p.code_name, "avc1");
|
||||
if let Some(ref schm) = p.scheme_type {
|
||||
assert_eq!(schm.scheme_type.value, "cbcs");
|
||||
assert_eq!(schm.scheme_type.value, *b"cbcs");
|
||||
} else {
|
||||
panic!("Expected scheme type info");
|
||||
}
|
||||
|
@ -538,7 +538,7 @@ fn public_video_cbcs() {
|
|||
assert_eq!(tenc.kid, kid);
|
||||
assert_eq!(tenc.crypt_byte_block_count, Some(1));
|
||||
assert_eq!(tenc.skip_byte_block_count, Some(9));
|
||||
assert_eq!(tenc.constant_iv, Some(default_iv.clone()));
|
||||
assert_eq!(tenc.constant_iv, Some(default_iv.clone().into()));
|
||||
} else {
|
||||
panic!("Invalid test condition");
|
||||
}
|
||||
|
@ -639,22 +639,19 @@ fn public_avif_primary_item_is_grid() {
|
|||
#[test]
|
||||
fn public_avif_read_samples() {
|
||||
env_logger::init();
|
||||
let microsoft = Path::new(MICROSOFT_AVIF_TEST_DIR)
|
||||
.read_dir()
|
||||
.expect("Cannot read AVIF test dir");
|
||||
let netflix = Path::new(NETFLIX_AVIF_TEST_DIR)
|
||||
.read_dir()
|
||||
.expect("Cannot read AVIF test dir");
|
||||
for entry in microsoft.chain(netflix) {
|
||||
let path = entry.expect("AVIF entry").path();
|
||||
if path.extension().expect("no extension") != "avif" {
|
||||
|
||||
for entry in walkdir::WalkDir::new(AVIF_TEST_DIR) {
|
||||
let entry = entry.expect("AVIF entry");
|
||||
let path = entry.path();
|
||||
if !path.is_file() || path.extension().unwrap_or_default() != "avif" {
|
||||
eprintln!("Skipping {:?}", path);
|
||||
continue; // Skip ReadMe.txt, etc.
|
||||
continue; // Skip directories, ReadMe.txt, etc.
|
||||
}
|
||||
if path == Path::new(IMAGE_AVIF_GRID) {
|
||||
eprintln!("Skipping {:?}", path);
|
||||
continue; // Remove when public_avif_primary_item_is_grid passes
|
||||
}
|
||||
println!("parsing {:?}", path);
|
||||
let context = &mut mp4::AvifContext::new();
|
||||
let input = &mut File::open(path).expect("Unknow file");
|
||||
mp4::read_avif(input, context).expect("read_avif failed");
|
|
@ -0,0 +1 @@
|
|||
{"files":{"Cargo.toml":"13408d7785c5fe40f9db2bac1f93e8cf2aca7c35b5d2ba9acbbb23eb3a71e40a","cbindgen.toml":"5c9429f271d6e914d81b63e6509c04ffe84cab11ed3a53a2ed4715e5d5ace80e","examples/dump.rs":"2db33a7ffbb20880d140e95c93cdeabe7e2ff41e5c09d80aa10dfdda9277942d","src/lib.rs":"deff68e6d20bc20167aa94cc0aaa541d83ca80bd0f18d9524530706a51782f5c","tests/test_chunk_out_of_range.rs":"b5da583218d98027ed973a29c67434a91a1306f2d2fb39ec4d640d4824c308ce","tests/test_encryption.rs":"a26f2fdb40e1fb3619a0625d0afbd677c6bdff1e2e640962197142656499c409","tests/test_fragment.rs":"e90eb5a4418d30002655466c0c4b3125c7fd70a74b6871471eaa172f1def9db8","tests/test_rotation.rs":"fb43c2f2dfa496d151c33bdd46c0fd3252387c23cc71e2cac9ed0234de715a81","tests/test_sample_table.rs":"adc8d264c46aef78c047377fbb792a4400d6db98bc44583b464d7b3dc182c884","tests/test_workaround_stsc.rs":"7dd419f3d55b9a3a039cac57e58a9240a9c8166bcd4356c24f69f731c3ced83b"},"package":null}
|
|
@ -1,10 +1,11 @@
|
|||
[package]
|
||||
name = "mp4parse_capi"
|
||||
version = "0.11.2"
|
||||
version = "0.11.4"
|
||||
authors = [
|
||||
"Ralph Giles <giles@mozilla.com>",
|
||||
"Matthew Gregan <kinetik@flim.org>",
|
||||
"Alfredo Yang <ayang@mozilla.com>",
|
||||
"Jon Bauman <jbauman@mozilla.com>",
|
||||
]
|
||||
|
||||
description = "Parser for ISO base media file format (mp4)"
|
||||
|
@ -18,8 +19,6 @@ exclude = [
|
|||
"*.mp4",
|
||||
]
|
||||
|
||||
build = false # See bug 1611431 - Generate mp4parse-rust bindings as part of mach build
|
||||
|
||||
[badges]
|
||||
travis-ci = { repository = "https://github.com/mozilla/mp4parse-rust" }
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
header = """/* 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 http://mozilla.org/MPL/2.0/. */"""
|
||||
autogen_warning = """/* DO NOT MODIFY THIS MANUALLY! This file was generated using cbindgen. See RunCbindgen.py */
|
||||
#ifndef mozilla_media_mp4parse_rust_mp4parse_h
|
||||
autogen_warning = """/* DO NOT MODIFY THIS MANUALLY! This file was generated using cbindgen. */
|
||||
#ifndef mp4parse_rust_mp4parse_h
|
||||
#error "Don't include this file directly, instead include mp4parse.h"
|
||||
#endif
|
||||
"""
|
||||
|
@ -11,9 +11,7 @@ braces = "SameLine"
|
|||
line_length = 100
|
||||
tab_width = 2
|
||||
language = "C"
|
||||
|
||||
# If we change the language to C++, it would be nice to namespace it
|
||||
# namespaces = ["mozilla", "media", "ffi"]
|
||||
cpp_compat = true
|
||||
|
||||
[enum]
|
||||
rename_variants = "QualifiedScreamingSnakeCase"
|
|
@ -0,0 +1,168 @@
|
|||
extern crate mp4parse;
|
||||
extern crate mp4parse_capi;
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
extern crate env_logger;
|
||||
|
||||
use mp4parse_capi::*;
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
|
||||
extern "C" fn buf_read(buf: *mut u8, size: usize, userdata: *mut std::os::raw::c_void) -> isize {
|
||||
let 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,
|
||||
}
|
||||
}
|
||||
|
||||
fn dump_file(filename: &str) {
|
||||
let mut file = File::open(filename).expect("Unknown file");
|
||||
let io = Mp4parseIo {
|
||||
read: Some(buf_read),
|
||||
userdata: &mut file as *mut _ as *mut std::os::raw::c_void,
|
||||
};
|
||||
|
||||
unsafe {
|
||||
let mut parser = std::ptr::null_mut();
|
||||
let rv = mp4parse_new(&io, &mut parser);
|
||||
|
||||
match rv {
|
||||
Mp4parseStatus::Ok => (),
|
||||
_ => {
|
||||
println!("-- fail to parse, '-v' for more info");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let mut frag_info = Mp4parseFragmentInfo::default();
|
||||
match mp4parse_get_fragment_info(parser, &mut frag_info) {
|
||||
Mp4parseStatus::Ok => {
|
||||
println!("-- mp4parse_fragment_info {:?}", frag_info);
|
||||
}
|
||||
_ => {
|
||||
println!("-- mp4parse_fragment_info failed");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let mut counts: u32 = 0;
|
||||
match mp4parse_get_track_count(parser, &mut counts) {
|
||||
Mp4parseStatus::Ok => (),
|
||||
_ => {
|
||||
println!("-- mp4parse_get_track_count failed");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for i in 0..counts {
|
||||
let mut track_info = Mp4parseTrackInfo {
|
||||
track_type: Mp4parseTrackType::Audio,
|
||||
track_id: 0,
|
||||
duration: 0,
|
||||
media_time: 0,
|
||||
};
|
||||
match mp4parse_get_track_info(parser, i, &mut track_info) {
|
||||
Mp4parseStatus::Ok => {
|
||||
println!("-- mp4parse_get_track_info {:?}", track_info);
|
||||
}
|
||||
_ => {
|
||||
println!("-- mp4parse_get_track_info failed, track id: {}", i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
match track_info.track_type {
|
||||
Mp4parseTrackType::Audio => {
|
||||
let mut audio_info = Mp4parseTrackAudioInfo::default();
|
||||
match mp4parse_get_track_audio_info(parser, i, &mut audio_info) {
|
||||
Mp4parseStatus::Ok => {
|
||||
println!("-- mp4parse_get_track_audio_info {:?}", audio_info);
|
||||
for i in 0..audio_info.sample_info_count as isize {
|
||||
let sample_info = audio_info.sample_info.offset(i);
|
||||
println!(
|
||||
" -- mp4parse_get_track_audio_info sample_info[{:?}] {:?}",
|
||||
i, *sample_info
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
println!("-- mp4parse_get_track_audio_info failed, track id: {}", i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
Mp4parseTrackType::Video => {
|
||||
let mut video_info = Mp4parseTrackVideoInfo::default();
|
||||
match mp4parse_get_track_video_info(parser, i, &mut video_info) {
|
||||
Mp4parseStatus::Ok => {
|
||||
println!("-- mp4parse_get_track_video_info {:?}", video_info);
|
||||
for i in 0..video_info.sample_info_count as isize {
|
||||
let sample_info = video_info.sample_info.offset(i);
|
||||
println!(
|
||||
" -- mp4parse_get_track_video_info sample_info[{:?}] {:?}",
|
||||
i, *sample_info
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
println!("-- mp4parse_get_track_video_info failed, track id: {}", i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
Mp4parseTrackType::Metadata => {
|
||||
println!("TODO metadata track");
|
||||
}
|
||||
}
|
||||
|
||||
let mut indices = Mp4parseByteData::default();
|
||||
match mp4parse_get_indice_table(parser, track_info.track_id, &mut indices) {
|
||||
Mp4parseStatus::Ok => {
|
||||
println!(
|
||||
"-- mp4parse_get_indice_table track_id {} indices {:?}",
|
||||
track_info.track_id, indices
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
println!(
|
||||
"-- mp4parse_get_indice_table failed, track_info.track_id: {}",
|
||||
track_info.track_id
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
mp4parse_free(parser);
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
if args.len() < 2 {
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize logging, setting the log level if requested.
|
||||
let (skip, verbose) = if args[1] == "-v" {
|
||||
(2, true)
|
||||
} else {
|
||||
(1, false)
|
||||
};
|
||||
let env = env_logger::Env::default();
|
||||
let mut logger = env_logger::Builder::from_env(env);
|
||||
if verbose {
|
||||
logger.filter(None, log::LevelFilter::Debug);
|
||||
}
|
||||
logger.init();
|
||||
|
||||
for filename in args.iter().skip(skip) {
|
||||
info!("-- dump of '{}' --", filename);
|
||||
dump_file(filename);
|
||||
info!("-- end of '{}' --", filename);
|
||||
}
|
||||
}
|
|
@ -41,15 +41,12 @@ extern crate num_traits;
|
|||
|
||||
use byteorder::WriteBytesExt;
|
||||
use num_traits::{PrimInt, Zero};
|
||||
use std::collections::HashMap;
|
||||
use std::io::Read;
|
||||
|
||||
// Symbols we need from our rust api.
|
||||
use mp4parse::extend_from_slice;
|
||||
use mp4parse::read_avif;
|
||||
use mp4parse::read_mp4;
|
||||
use mp4parse::serialize_opus_header;
|
||||
use mp4parse::vec_push;
|
||||
use mp4parse::AudioCodecSpecific;
|
||||
use mp4parse::AvifContext;
|
||||
use mp4parse::CodecType;
|
||||
|
@ -62,8 +59,21 @@ use mp4parse::Track;
|
|||
use mp4parse::TrackScaledTime;
|
||||
use mp4parse::TrackTimeScale;
|
||||
use mp4parse::TrackType;
|
||||
use mp4parse::TryBox;
|
||||
use mp4parse::TryHashMap;
|
||||
use mp4parse::TryVec;
|
||||
use mp4parse::VideoCodecSpecific;
|
||||
|
||||
// To ensure we don't use stdlib allocating types by accident
|
||||
#[allow(dead_code)]
|
||||
struct Vec;
|
||||
#[allow(dead_code)]
|
||||
struct Box;
|
||||
#[allow(dead_code)]
|
||||
struct HashMap;
|
||||
#[allow(dead_code)]
|
||||
struct String;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub enum Mp4parseStatus {
|
||||
|
@ -295,15 +305,15 @@ pub struct Mp4parseFragmentInfo {
|
|||
#[derive(Default)]
|
||||
pub struct Mp4parseParser {
|
||||
context: MediaContext,
|
||||
opus_header: HashMap<u32, Vec<u8>>,
|
||||
pssh_data: Vec<u8>,
|
||||
sample_table: HashMap<u32, Vec<Mp4parseIndice>>,
|
||||
opus_header: TryHashMap<u32, TryVec<u8>>,
|
||||
pssh_data: TryVec<u8>,
|
||||
sample_table: TryHashMap<u32, TryVec<Mp4parseIndice>>,
|
||||
// Store a mapping from track index (not id) to associated sample
|
||||
// descriptions. Because each track has a variable number of sample
|
||||
// descriptions, and because we need the data to live long enough to be
|
||||
// copied out by callers, we store these on the parser struct.
|
||||
audio_track_sample_descriptions: HashMap<u32, Vec<Mp4parseTrackAudioSampleInfo>>,
|
||||
video_track_sample_descriptions: HashMap<u32, Vec<Mp4parseTrackVideoSampleInfo>>,
|
||||
audio_track_sample_descriptions: TryHashMap<u32, TryVec<Mp4parseTrackAudioSampleInfo>>,
|
||||
video_track_sample_descriptions: TryHashMap<u32, TryVec<Mp4parseTrackVideoSampleInfo>>,
|
||||
}
|
||||
|
||||
/// A unified interface for the parsers which have different contexts, but
|
||||
|
@ -328,18 +338,6 @@ impl Mp4parseParser {
|
|||
fn context_mut(&mut self) -> &mut MediaContext {
|
||||
&mut self.context
|
||||
}
|
||||
|
||||
fn opus_header_mut(&mut self) -> &mut HashMap<u32, Vec<u8>> {
|
||||
&mut self.opus_header
|
||||
}
|
||||
|
||||
fn pssh_data_mut(&mut self) -> &mut Vec<u8> {
|
||||
&mut self.pssh_data
|
||||
}
|
||||
|
||||
fn sample_table_mut(&mut self) -> &mut HashMap<u32, Vec<Mp4parseIndice>> {
|
||||
&mut self.sample_table
|
||||
}
|
||||
}
|
||||
|
||||
impl ContextParser for Mp4parseParser {
|
||||
|
@ -483,8 +481,8 @@ fn mp4parse_new_common_safe<T: Read, P: ContextParser>(
|
|||
|
||||
P::read(io, &mut context)
|
||||
.map(|_| P::with_context(context))
|
||||
.map(Box::new)
|
||||
.map(Box::into_raw)
|
||||
.and_then(TryBox::try_new)
|
||||
.map(TryBox::into_raw)
|
||||
.map_err(Mp4parseStatus::from)
|
||||
}
|
||||
|
||||
|
@ -505,6 +503,16 @@ impl From<mp4parse::Error> for Mp4parseStatus {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<Result<(), Mp4parseStatus>> for Mp4parseStatus {
|
||||
fn from(result: Result<(), Mp4parseStatus>) -> Self {
|
||||
match result {
|
||||
Ok(()) => Mp4parseStatus::Ok,
|
||||
Err(Mp4parseStatus::Ok) => unreachable!(),
|
||||
Err(e) => e,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Free an `Mp4parseParser*` allocated by `mp4parse_new()`.
|
||||
///
|
||||
/// # Safety
|
||||
|
@ -515,7 +523,7 @@ impl From<mp4parse::Error> for Mp4parseStatus {
|
|||
#[no_mangle]
|
||||
pub unsafe extern "C" fn mp4parse_free(parser: *mut Mp4parseParser) {
|
||||
assert!(!parser.is_null());
|
||||
let _ = Box::from_raw(parser);
|
||||
let _ = TryBox::from_raw(parser);
|
||||
}
|
||||
|
||||
/// Free an `Mp4parseAvifParser*` allocated by `mp4parse_avif_new()`.
|
||||
|
@ -528,7 +536,7 @@ pub unsafe extern "C" fn mp4parse_free(parser: *mut Mp4parseParser) {
|
|||
#[no_mangle]
|
||||
pub unsafe extern "C" fn mp4parse_avif_free(parser: *mut Mp4parseAvifParser) {
|
||||
assert!(!parser.is_null());
|
||||
let _ = Box::from_raw(parser);
|
||||
let _ = TryBox::from_raw(parser);
|
||||
}
|
||||
|
||||
/// Return the number of tracks parsed by previous `mp4parse_read()` call.
|
||||
|
@ -694,34 +702,46 @@ pub unsafe extern "C" fn mp4parse_get_track_audio_info(
|
|||
// Initialize fields to default values to ensure all fields are always valid.
|
||||
*info = Default::default();
|
||||
|
||||
let context = (*parser).context();
|
||||
get_track_audio_info(&mut *parser, track_index, &mut *info).into()
|
||||
}
|
||||
|
||||
fn get_track_audio_info(
|
||||
parser: &mut Mp4parseParser,
|
||||
track_index: u32,
|
||||
info: &mut Mp4parseTrackAudioInfo,
|
||||
) -> Result<(), Mp4parseStatus> {
|
||||
let Mp4parseParser {
|
||||
context,
|
||||
opus_header,
|
||||
..
|
||||
} = parser;
|
||||
|
||||
if track_index as usize >= context.tracks.len() {
|
||||
return Mp4parseStatus::BadArg;
|
||||
return Err(Mp4parseStatus::BadArg);
|
||||
}
|
||||
|
||||
let track = &context.tracks[track_index as usize];
|
||||
|
||||
if track.track_type != TrackType::Audio {
|
||||
return Mp4parseStatus::Invalid;
|
||||
return Err(Mp4parseStatus::Invalid);
|
||||
}
|
||||
|
||||
// Handle track.stsd
|
||||
let stsd = match track.stsd {
|
||||
Some(ref stsd) => stsd,
|
||||
None => return Mp4parseStatus::Invalid, // Stsd should be present
|
||||
None => return Err(Mp4parseStatus::Invalid), // Stsd should be present
|
||||
};
|
||||
|
||||
if stsd.descriptions.is_empty() {
|
||||
return Mp4parseStatus::Invalid; // Should have at least 1 description
|
||||
return Err(Mp4parseStatus::Invalid); // Should have at least 1 description
|
||||
}
|
||||
|
||||
let mut audio_sample_infos = Vec::with_capacity(stsd.descriptions.len());
|
||||
let mut audio_sample_infos = TryVec::with_capacity(stsd.descriptions.len())?;
|
||||
for description in stsd.descriptions.iter() {
|
||||
let mut sample_info = Mp4parseTrackAudioSampleInfo::default();
|
||||
let audio = match description {
|
||||
SampleEntry::Audio(a) => a,
|
||||
_ => return Mp4parseStatus::Invalid,
|
||||
_ => return Err(Mp4parseStatus::Invalid),
|
||||
};
|
||||
|
||||
// UNKNOWN for unsupported format.
|
||||
|
@ -748,7 +768,7 @@ pub unsafe extern "C" fn mp4parse_get_track_audio_info(
|
|||
match audio.codec_specific {
|
||||
AudioCodecSpecific::ES_Descriptor(ref esds) => {
|
||||
if esds.codec_esds.len() > std::u32::MAX as usize {
|
||||
return Mp4parseStatus::Invalid;
|
||||
return Err(Mp4parseStatus::Invalid);
|
||||
}
|
||||
sample_info.extra_data.length = esds.codec_esds.len() as u32;
|
||||
sample_info.extra_data.data = esds.codec_esds.as_ptr();
|
||||
|
@ -772,23 +792,22 @@ pub unsafe extern "C" fn mp4parse_get_track_audio_info(
|
|||
// Return the STREAMINFO metadata block in the codec_specific.
|
||||
let streaminfo = &flac.blocks[0];
|
||||
if streaminfo.block_type != 0 || streaminfo.data.len() != 34 {
|
||||
return Mp4parseStatus::Invalid;
|
||||
return Err(Mp4parseStatus::Invalid);
|
||||
}
|
||||
sample_info.codec_specific_config.length = streaminfo.data.len() as u32;
|
||||
sample_info.codec_specific_config.data = streaminfo.data.as_ptr();
|
||||
}
|
||||
AudioCodecSpecific::OpusSpecificBox(ref opus) => {
|
||||
let mut v = Vec::new();
|
||||
let mut v = TryVec::new();
|
||||
match serialize_opus_header(opus, &mut v) {
|
||||
Err(_) => {
|
||||
return Mp4parseStatus::Invalid;
|
||||
return Err(Mp4parseStatus::Invalid);
|
||||
}
|
||||
Ok(_) => {
|
||||
let header = (*parser).opus_header_mut();
|
||||
header.insert(track_index, v);
|
||||
if let Some(v) = header.get(&track_index) {
|
||||
opus_header.insert(track_index, v)?;
|
||||
if let Some(v) = opus_header.get(&track_index) {
|
||||
if v.len() > std::u32::MAX as usize {
|
||||
return Mp4parseStatus::Invalid;
|
||||
return Err(Mp4parseStatus::Invalid);
|
||||
}
|
||||
sample_info.codec_specific_config.length = v.len() as u32;
|
||||
sample_info.codec_specific_config.data = v.as_ptr();
|
||||
|
@ -811,8 +830,8 @@ pub unsafe extern "C" fn mp4parse_get_track_audio_info(
|
|||
sample_info.protected_data.scheme_type = match p.scheme_type {
|
||||
Some(ref scheme_type_box) => {
|
||||
match scheme_type_box.scheme_type.value.as_ref() {
|
||||
"cenc" => Mp4ParseEncryptionSchemeType::Cenc,
|
||||
"cbcs" => Mp4ParseEncryptionSchemeType::Cbcs,
|
||||
b"cenc" => Mp4ParseEncryptionSchemeType::Cenc,
|
||||
b"cbcs" => Mp4ParseEncryptionSchemeType::Cbcs,
|
||||
// We don't support other schemes, and shouldn't reach
|
||||
// this case. Try to gracefully handle by treating as
|
||||
// no encryption case.
|
||||
|
@ -835,35 +854,32 @@ pub unsafe extern "C" fn mp4parse_get_track_audio_info(
|
|||
};
|
||||
if let Some(ref iv_vec) = tenc.constant_iv {
|
||||
if iv_vec.len() > std::u32::MAX as usize {
|
||||
return Mp4parseStatus::Invalid;
|
||||
return Err(Mp4parseStatus::Invalid);
|
||||
}
|
||||
sample_info.protected_data.constant_iv.set_data(iv_vec);
|
||||
};
|
||||
}
|
||||
}
|
||||
let res = vec_push(&mut audio_sample_infos, sample_info);
|
||||
if res.is_err() {
|
||||
return Mp4parseStatus::Oom;
|
||||
}
|
||||
audio_sample_infos.push(sample_info)?;
|
||||
}
|
||||
|
||||
(*parser)
|
||||
parser
|
||||
.audio_track_sample_descriptions
|
||||
.insert(track_index, audio_sample_infos);
|
||||
match (*parser).audio_track_sample_descriptions.get(&track_index) {
|
||||
.insert(track_index, audio_sample_infos)?;
|
||||
match parser.audio_track_sample_descriptions.get(&track_index) {
|
||||
Some(sample_info) => {
|
||||
if sample_info.len() > std::u32::MAX as usize {
|
||||
// Should never happen due to upper limits on number of sample
|
||||
// descriptions a track can have, but lets be safe.
|
||||
return Mp4parseStatus::Invalid;
|
||||
return Err(Mp4parseStatus::Invalid);
|
||||
}
|
||||
(*info).sample_info_count = sample_info.len() as u32;
|
||||
(*info).sample_info = sample_info.as_ptr();
|
||||
info.sample_info_count = sample_info.len() as u32;
|
||||
info.sample_info = sample_info.as_ptr();
|
||||
}
|
||||
None => return Mp4parseStatus::Invalid, // Shouldn't happen, we just inserted the info!
|
||||
None => return Err(Mp4parseStatus::Invalid), // Shouldn't happen, we just inserted the info!
|
||||
}
|
||||
|
||||
Mp4parseStatus::Ok
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Fill the supplied `Mp4parseTrackVideoInfo` with metadata for `track`.
|
||||
|
@ -887,54 +903,62 @@ pub unsafe extern "C" fn mp4parse_get_track_video_info(
|
|||
// Initialize fields to default values to ensure all fields are always valid.
|
||||
*info = Default::default();
|
||||
|
||||
let context = (*parser).context();
|
||||
mp4parse_get_track_video_info_safe(&mut *parser, track_index, &mut *info).into()
|
||||
}
|
||||
|
||||
fn mp4parse_get_track_video_info_safe(
|
||||
parser: &mut Mp4parseParser,
|
||||
track_index: u32,
|
||||
info: &mut Mp4parseTrackVideoInfo,
|
||||
) -> Result<(), Mp4parseStatus> {
|
||||
let context = parser.context();
|
||||
|
||||
if track_index as usize >= context.tracks.len() {
|
||||
return Mp4parseStatus::BadArg;
|
||||
return Err(Mp4parseStatus::BadArg);
|
||||
}
|
||||
|
||||
let track = &context.tracks[track_index as usize];
|
||||
|
||||
if track.track_type != TrackType::Video {
|
||||
return Mp4parseStatus::Invalid;
|
||||
return Err(Mp4parseStatus::Invalid);
|
||||
}
|
||||
|
||||
// Handle track.tkhd
|
||||
if let Some(ref tkhd) = track.tkhd {
|
||||
(*info).display_width = tkhd.width >> 16; // 16.16 fixed point
|
||||
(*info).display_height = tkhd.height >> 16; // 16.16 fixed point
|
||||
info.display_width = tkhd.width >> 16; // 16.16 fixed point
|
||||
info.display_height = tkhd.height >> 16; // 16.16 fixed point
|
||||
let matrix = (
|
||||
tkhd.matrix.a >> 16,
|
||||
tkhd.matrix.b >> 16,
|
||||
tkhd.matrix.c >> 16,
|
||||
tkhd.matrix.d >> 16,
|
||||
);
|
||||
(*info).rotation = match matrix {
|
||||
info.rotation = match matrix {
|
||||
(0, 1, -1, 0) => 90, // rotate 90 degrees
|
||||
(-1, 0, 0, -1) => 180, // rotate 180 degrees
|
||||
(0, -1, 1, 0) => 270, // rotate 270 degrees
|
||||
_ => 0,
|
||||
};
|
||||
} else {
|
||||
return Mp4parseStatus::Invalid;
|
||||
return Err(Mp4parseStatus::Invalid);
|
||||
}
|
||||
|
||||
// Handle track.stsd
|
||||
let stsd = match track.stsd {
|
||||
Some(ref stsd) => stsd,
|
||||
None => return Mp4parseStatus::Invalid, // Stsd should be present
|
||||
None => return Err(Mp4parseStatus::Invalid), // Stsd should be present
|
||||
};
|
||||
|
||||
if stsd.descriptions.is_empty() {
|
||||
return Mp4parseStatus::Invalid; // Should have at least 1 description
|
||||
return Err(Mp4parseStatus::Invalid); // Should have at least 1 description
|
||||
}
|
||||
|
||||
let mut video_sample_infos = Vec::with_capacity(stsd.descriptions.len());
|
||||
let mut video_sample_infos = TryVec::with_capacity(stsd.descriptions.len())?;
|
||||
for description in stsd.descriptions.iter() {
|
||||
let mut sample_info = Mp4parseTrackVideoSampleInfo::default();
|
||||
let video = match description {
|
||||
SampleEntry::Video(v) => v,
|
||||
_ => return Mp4parseStatus::Invalid,
|
||||
_ => return Err(Mp4parseStatus::Invalid),
|
||||
};
|
||||
|
||||
// UNKNOWN for unsupported format.
|
||||
|
@ -966,8 +990,8 @@ pub unsafe extern "C" fn mp4parse_get_track_video_info(
|
|||
sample_info.protected_data.scheme_type = match p.scheme_type {
|
||||
Some(ref scheme_type_box) => {
|
||||
match scheme_type_box.scheme_type.value.as_ref() {
|
||||
"cenc" => Mp4ParseEncryptionSchemeType::Cenc,
|
||||
"cbcs" => Mp4ParseEncryptionSchemeType::Cbcs,
|
||||
b"cenc" => Mp4ParseEncryptionSchemeType::Cenc,
|
||||
b"cbcs" => Mp4ParseEncryptionSchemeType::Cbcs,
|
||||
// We don't support other schemes, and shouldn't reach
|
||||
// this case. Try to gracefully handle by treating as
|
||||
// no encryption case.
|
||||
|
@ -990,34 +1014,31 @@ pub unsafe extern "C" fn mp4parse_get_track_video_info(
|
|||
};
|
||||
if let Some(ref iv_vec) = tenc.constant_iv {
|
||||
if iv_vec.len() > std::u32::MAX as usize {
|
||||
return Mp4parseStatus::Invalid;
|
||||
return Err(Mp4parseStatus::Invalid);
|
||||
}
|
||||
sample_info.protected_data.constant_iv.set_data(iv_vec);
|
||||
};
|
||||
}
|
||||
}
|
||||
let res = vec_push(&mut video_sample_infos, sample_info);
|
||||
if res.is_err() {
|
||||
return Mp4parseStatus::Oom;
|
||||
}
|
||||
video_sample_infos.push(sample_info)?;
|
||||
}
|
||||
|
||||
(*parser)
|
||||
parser
|
||||
.video_track_sample_descriptions
|
||||
.insert(track_index, video_sample_infos);
|
||||
match (*parser).video_track_sample_descriptions.get(&track_index) {
|
||||
.insert(track_index, video_sample_infos)?;
|
||||
match parser.video_track_sample_descriptions.get(&track_index) {
|
||||
Some(sample_info) => {
|
||||
if sample_info.len() > std::u32::MAX as usize {
|
||||
// Should never happen due to upper limits on number of sample
|
||||
// descriptions a track can have, but lets be safe.
|
||||
return Mp4parseStatus::Invalid;
|
||||
return Err(Mp4parseStatus::Invalid);
|
||||
}
|
||||
(*info).sample_info_count = sample_info.len() as u32;
|
||||
(*info).sample_info = sample_info.as_ptr();
|
||||
info.sample_info_count = sample_info.len() as u32;
|
||||
info.sample_info = sample_info.as_ptr();
|
||||
}
|
||||
None => return Mp4parseStatus::Invalid, // Shouldn't happen, we just inserted the info!
|
||||
None => return Err(Mp4parseStatus::Invalid), // Shouldn't happen, we just inserted the info!
|
||||
}
|
||||
Mp4parseStatus::Ok
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Return a pointer to the primary item parsed by previous `mp4parse_avif_new()` call.
|
||||
|
@ -1071,17 +1092,28 @@ pub unsafe extern "C" fn mp4parse_get_indice_table(
|
|||
// Initialize fields to default values to ensure all fields are always valid.
|
||||
*indices = Default::default();
|
||||
|
||||
let context = (*parser).context();
|
||||
get_indice_table(&mut *parser, track_id, &mut *indices).into()
|
||||
}
|
||||
|
||||
fn get_indice_table(
|
||||
parser: &mut Mp4parseParser,
|
||||
track_id: u32,
|
||||
indices: &mut Mp4parseByteData,
|
||||
) -> Result<(), Mp4parseStatus> {
|
||||
let Mp4parseParser {
|
||||
context,
|
||||
sample_table: index_table,
|
||||
..
|
||||
} = parser;
|
||||
let tracks = &context.tracks;
|
||||
let track = match tracks.iter().find(|track| track.track_id == Some(track_id)) {
|
||||
Some(t) => t,
|
||||
_ => return Mp4parseStatus::Invalid,
|
||||
_ => return Err(Mp4parseStatus::Invalid),
|
||||
};
|
||||
|
||||
let index_table = (*parser).sample_table_mut();
|
||||
if let Some(v) = index_table.get(&track_id) {
|
||||
(*indices).set_indices(v);
|
||||
return Mp4parseStatus::Ok;
|
||||
indices.set_indices(v);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let media_time = match (&track.media_time, &track.timescale) {
|
||||
|
@ -1105,12 +1137,12 @@ pub unsafe extern "C" fn mp4parse_get_indice_table(
|
|||
};
|
||||
|
||||
if let Some(v) = create_sample_table(track, offset_time) {
|
||||
(*indices).set_indices(&v);
|
||||
index_table.insert(track_id, v);
|
||||
return Mp4parseStatus::Ok;
|
||||
indices.set_indices(&v);
|
||||
index_table.insert(track_id, v)?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
Mp4parseStatus::Invalid
|
||||
Err(Mp4parseStatus::Invalid)
|
||||
}
|
||||
|
||||
// Convert a 'ctts' compact table to full table by iterator,
|
||||
|
@ -1274,7 +1306,7 @@ impl<'a> SampleToChunkIterator<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn create_sample_table(track: &Track, track_offset_time: i64) -> Option<Vec<Mp4parseIndice>> {
|
||||
fn create_sample_table(track: &Track, track_offset_time: i64) -> Option<TryVec<Mp4parseIndice>> {
|
||||
let timescale = match track.timescale {
|
||||
Some(ref t) => TrackTimeScale::<i64>(t.0 as i64, t.1),
|
||||
_ => return None,
|
||||
|
@ -1291,7 +1323,7 @@ fn create_sample_table(track: &Track, track_offset_time: i64) -> Option<Vec<Mp4p
|
|||
_ => false,
|
||||
};
|
||||
|
||||
let mut sample_table = Vec::new();
|
||||
let mut sample_table = TryVec::new();
|
||||
let mut sample_size_iter = stsz.sample_sizes.iter();
|
||||
|
||||
// Get 'stsc' iterator for (chunk_id, chunk_sample_count) and calculate the sample
|
||||
|
@ -1322,20 +1354,16 @@ fn create_sample_table(track: &Track, track_offset_time: i64) -> Option<Vec<Mp4p
|
|||
}
|
||||
cur_position = end_offset;
|
||||
|
||||
let res = vec_push(
|
||||
&mut sample_table,
|
||||
Mp4parseIndice {
|
||||
sample_table
|
||||
.push(Mp4parseIndice {
|
||||
start_offset,
|
||||
end_offset,
|
||||
start_composition: 0,
|
||||
end_composition: 0,
|
||||
start_decode: 0,
|
||||
sync: !has_sync_table,
|
||||
},
|
||||
);
|
||||
if res.is_err() {
|
||||
return None;
|
||||
}
|
||||
})
|
||||
.ok()?;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1407,11 +1435,9 @@ fn create_sample_table(track: &Track, track_offset_time: i64) -> Option<Vec<Mp4p
|
|||
// calculate to correct the composition end time.
|
||||
if !sample_table.is_empty() {
|
||||
// Create an index table refers to sample_table and sorted by start_composisiton time.
|
||||
let mut sort_table = Vec::new();
|
||||
let mut sort_table = TryVec::new();
|
||||
for i in 0..sample_table.len() {
|
||||
if vec_push(&mut sort_table, i).is_err() {
|
||||
return None;
|
||||
}
|
||||
sort_table.push(i).ok()?;
|
||||
}
|
||||
|
||||
sort_table.sort_by_key(|i| match sample_table.get(*i) {
|
||||
|
@ -1419,15 +1445,14 @@ fn create_sample_table(track: &Track, track_offset_time: i64) -> Option<Vec<Mp4p
|
|||
_ => 0,
|
||||
});
|
||||
|
||||
let iter = sort_table.iter();
|
||||
for i in 0..(iter.len() - 1) {
|
||||
let current_index = sort_table[i];
|
||||
let peek_index = sort_table[i + 1];
|
||||
for indices in sort_table.windows(2) {
|
||||
if let [current_index, peek_index] = *indices {
|
||||
let next_start_composition_time = sample_table[peek_index].start_composition;
|
||||
let sample = &mut sample_table[current_index];
|
||||
sample.end_composition = next_start_composition_time;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some(sample_table)
|
||||
}
|
||||
|
@ -1544,37 +1569,38 @@ pub unsafe extern "C" fn mp4parse_get_pssh_info(
|
|||
// Initialize fields to default values to ensure all fields are always valid.
|
||||
*info = Default::default();
|
||||
|
||||
let context = (*parser).context_mut();
|
||||
let pssh_data = (*parser).pssh_data_mut();
|
||||
let info: &mut Mp4parsePsshInfo = &mut *info;
|
||||
get_pssh_info(&mut *parser, &mut *info).into()
|
||||
}
|
||||
|
||||
fn get_pssh_info(
|
||||
parser: &mut Mp4parseParser,
|
||||
info: &mut Mp4parsePsshInfo,
|
||||
) -> Result<(), Mp4parseStatus> {
|
||||
let Mp4parseParser {
|
||||
context, pssh_data, ..
|
||||
} = parser;
|
||||
|
||||
pssh_data.clear();
|
||||
for pssh in &context.psshs {
|
||||
let content_len = pssh.box_content.len();
|
||||
if content_len > std::u32::MAX as usize {
|
||||
return Mp4parseStatus::Invalid;
|
||||
return Err(Mp4parseStatus::Invalid);
|
||||
}
|
||||
let mut data_len = Vec::new();
|
||||
let mut data_len = TryVec::new();
|
||||
if data_len
|
||||
.write_u32::<byteorder::NativeEndian>(content_len as u32)
|
||||
.is_err()
|
||||
{
|
||||
return Mp4parseStatus::Io;
|
||||
}
|
||||
pssh_data.extend_from_slice(pssh.system_id.as_slice());
|
||||
pssh_data.extend_from_slice(data_len.as_slice());
|
||||
// The previous two calls have known, small sizes, but pssh_data has
|
||||
// arbitrary size based on untrusted input, so use fallible allocation
|
||||
let res = extend_from_slice(pssh_data, pssh.box_content.as_slice());
|
||||
|
||||
if res.is_err() {
|
||||
return Mp4parseStatus::Oom;
|
||||
return Err(Mp4parseStatus::Io);
|
||||
}
|
||||
pssh_data.extend_from_slice(pssh.system_id.as_slice())?;
|
||||
pssh_data.extend_from_slice(data_len.as_slice())?;
|
||||
pssh_data.extend_from_slice(pssh.box_content.as_slice())?;
|
||||
}
|
||||
|
||||
info.data.set_data(pssh_data);
|
||||
|
||||
Mp4parseStatus::Ok
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -1676,7 +1702,7 @@ fn parser_input_must_be_null() {
|
|||
read: Some(error_read),
|
||||
userdata: &mut dummy_value as *mut _ as *mut std::os::raw::c_void,
|
||||
};
|
||||
let mut parser = 0xDEADBEEF as *mut _;
|
||||
let mut parser = 0xDEAD_BEEF as *mut _;
|
||||
let rv = unsafe { mp4parse_new(&io, &mut parser) };
|
||||
assert_eq!(rv, Mp4parseStatus::BadArg);
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
extern crate mp4parse_capi;
|
||||
use mp4parse_capi::*;
|
||||
use std::io::Read;
|
||||
|
||||
extern "C" fn buf_read(buf: *mut u8, size: usize, userdata: *mut std::os::raw::c_void) -> isize {
|
||||
let 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 parse_out_of_chunk_range() {
|
||||
let mut file = std::fs::File::open("tests/chunk_out_of_range.mp4").expect("Unknown file");
|
||||
let io = Mp4parseIo {
|
||||
read: Some(buf_read),
|
||||
userdata: &mut file as *mut _ as *mut std::os::raw::c_void,
|
||||
};
|
||||
|
||||
unsafe {
|
||||
let mut parser = std::ptr::null_mut();
|
||||
let mut rv = mp4parse_new(&io, &mut parser);
|
||||
assert_eq!(rv, Mp4parseStatus::Ok);
|
||||
assert!(!parser.is_null());
|
||||
|
||||
let mut counts: u32 = 0;
|
||||
rv = mp4parse_get_track_count(parser, &mut counts);
|
||||
assert_eq!(rv, Mp4parseStatus::Ok);
|
||||
assert_eq!(counts, 1);
|
||||
|
||||
// its first chunk is out of range.
|
||||
// <SampleToChunkBox EntryCount="1">
|
||||
// <BoxInfo Size="28" Type="stsc"/>
|
||||
// <FullBoxInfo Version="0" Flags="0x0"/>
|
||||
// <SampleToChunkEntry FirstChunk="16777217" SamplesPerChunk="17" SampleDescriptionIndex="1"/>
|
||||
//
|
||||
let mut indice = Mp4parseByteData::default();
|
||||
let rv = mp4parse_get_indice_table(parser, 1, &mut indice);
|
||||
assert_eq!(rv, Mp4parseStatus::Invalid);
|
||||
|
||||
mp4parse_free(parser);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,200 @@
|
|||
extern crate mp4parse_capi;
|
||||
use mp4parse_capi::*;
|
||||
use std::io::Read;
|
||||
|
||||
extern "C" fn buf_read(buf: *mut u8, size: usize, userdata: *mut std::os::raw::c_void) -> isize {
|
||||
let 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]
|
||||
#[allow(clippy::cognitive_complexity)] // TODO: Consider simplifying this
|
||||
fn parse_cenc() {
|
||||
let mut file = std::fs::File::open("tests/short-cenc.mp4").expect("Unknown file");
|
||||
let io = Mp4parseIo {
|
||||
read: Some(buf_read),
|
||||
userdata: &mut file as *mut _ as *mut std::os::raw::c_void,
|
||||
};
|
||||
|
||||
unsafe {
|
||||
let mut parser = std::ptr::null_mut();
|
||||
let mut rv = mp4parse_new(&io, &mut parser);
|
||||
assert_eq!(rv, Mp4parseStatus::Ok);
|
||||
assert!(!parser.is_null());
|
||||
let mut counts: u32 = 0;
|
||||
rv = mp4parse_get_track_count(parser, &mut counts);
|
||||
assert_eq!(rv, Mp4parseStatus::Ok);
|
||||
assert_eq!(counts, 2);
|
||||
|
||||
// Make sure we have a video track and it's at index 0
|
||||
let mut video_track_info = Mp4parseTrackInfo::default();
|
||||
rv = mp4parse_get_track_info(parser, 0, &mut video_track_info);
|
||||
assert_eq!(rv, Mp4parseStatus::Ok);
|
||||
assert_eq!(video_track_info.track_type, Mp4parseTrackType::Video);
|
||||
|
||||
// Make sure we have a audio track and it's at index 1
|
||||
let mut audio_track_info = Mp4parseTrackInfo::default();
|
||||
rv = mp4parse_get_track_info(parser, 1, &mut audio_track_info);
|
||||
assert_eq!(rv, Mp4parseStatus::Ok);
|
||||
assert_eq!(audio_track_info.track_type, Mp4parseTrackType::Audio);
|
||||
|
||||
// Verify video track and crypto information
|
||||
let mut video = Mp4parseTrackVideoInfo::default();
|
||||
rv = mp4parse_get_track_video_info(parser, 0, &mut video);
|
||||
assert_eq!(rv, Mp4parseStatus::Ok);
|
||||
assert_eq!(video.sample_info_count, 1);
|
||||
assert_eq!((*video.sample_info).codec_type, Mp4parseCodec::Avc);
|
||||
assert_eq!((*video.sample_info).image_width, 320);
|
||||
assert_eq!((*video.sample_info).image_height, 240);
|
||||
let protected_data = &(*video.sample_info).protected_data;
|
||||
assert_eq!(
|
||||
protected_data.scheme_type,
|
||||
Mp4ParseEncryptionSchemeType::Cenc
|
||||
);
|
||||
assert_eq!(protected_data.is_encrypted, 0x01);
|
||||
assert_eq!(protected_data.iv_size, 16);
|
||||
assert_eq!(protected_data.kid.length, 16);
|
||||
let expected_kid = [
|
||||
0x7e, 0x57, 0x1d, 0x01, 0x7e, 0x57, 0x1d, 0x01, 0x7e, 0x57, 0x1d, 0x01, 0x7e, 0x57,
|
||||
0x1d, 0x01,
|
||||
];
|
||||
for (i, expected_byte) in expected_kid.iter().enumerate() {
|
||||
assert_eq!(&(*protected_data.kid.data.add(i)), expected_byte);
|
||||
}
|
||||
assert_eq!(protected_data.crypt_byte_block, 0);
|
||||
assert_eq!(protected_data.skip_byte_block, 0);
|
||||
assert_eq!(protected_data.constant_iv.length, 0);
|
||||
|
||||
// Verify audio track and crypto information
|
||||
let mut audio = Mp4parseTrackAudioInfo::default();
|
||||
rv = mp4parse_get_track_audio_info(parser, 1, &mut audio);
|
||||
assert_eq!(rv, Mp4parseStatus::Ok);
|
||||
assert_eq!(audio.sample_info_count, 1);
|
||||
assert_eq!((*audio.sample_info).codec_type, Mp4parseCodec::Aac);
|
||||
assert_eq!((*audio.sample_info).channels, 2);
|
||||
assert_eq!((*audio.sample_info).bit_depth, 16);
|
||||
assert_eq!((*audio.sample_info).sample_rate, 44100);
|
||||
let protected_data = &(*audio.sample_info).protected_data;
|
||||
assert_eq!(protected_data.is_encrypted, 0x01);
|
||||
assert_eq!(protected_data.iv_size, 16);
|
||||
assert_eq!(protected_data.kid.length, 16);
|
||||
let expected_kid = [
|
||||
0x7e, 0x57, 0x1d, 0x02, 0x7e, 0x57, 0x1d, 0x02, 0x7e, 0x57, 0x1d, 0x02, 0x7e, 0x57,
|
||||
0x1d, 0x02,
|
||||
];
|
||||
for (i, expected_byte) in expected_kid.iter().enumerate() {
|
||||
assert_eq!(&(*protected_data.kid.data.add(i)), expected_byte);
|
||||
}
|
||||
assert_eq!(protected_data.crypt_byte_block, 0);
|
||||
assert_eq!(protected_data.skip_byte_block, 0);
|
||||
assert_eq!(protected_data.constant_iv.length, 0);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_cbcs() {
|
||||
let mut file = std::fs::File::open("tests/bipbop_cbcs_video_init.mp4").expect("Unknown file");
|
||||
let io = Mp4parseIo {
|
||||
read: Some(buf_read),
|
||||
userdata: &mut file as *mut _ as *mut std::os::raw::c_void,
|
||||
};
|
||||
|
||||
unsafe {
|
||||
let mut parser = std::ptr::null_mut();
|
||||
let mut rv = mp4parse_new(&io, &mut parser);
|
||||
assert_eq!(rv, Mp4parseStatus::Ok);
|
||||
assert!(!parser.is_null());
|
||||
let mut counts: u32 = 0;
|
||||
rv = mp4parse_get_track_count(parser, &mut counts);
|
||||
assert_eq!(rv, Mp4parseStatus::Ok);
|
||||
assert_eq!(counts, 1);
|
||||
|
||||
// Make sure we have a video track
|
||||
let mut video_track_info = Mp4parseTrackInfo::default();
|
||||
rv = mp4parse_get_track_info(parser, 0, &mut video_track_info);
|
||||
assert_eq!(rv, Mp4parseStatus::Ok);
|
||||
assert_eq!(video_track_info.track_type, Mp4parseTrackType::Video);
|
||||
|
||||
// Verify video track and crypto information
|
||||
let mut video = Mp4parseTrackVideoInfo::default();
|
||||
rv = mp4parse_get_track_video_info(parser, 0, &mut video);
|
||||
assert_eq!(rv, Mp4parseStatus::Ok);
|
||||
assert_eq!(video.sample_info_count, 2);
|
||||
assert_eq!((*video.sample_info).codec_type, Mp4parseCodec::Avc);
|
||||
assert_eq!((*video.sample_info).image_width, 400);
|
||||
assert_eq!((*video.sample_info).image_height, 300);
|
||||
let protected_data = &(*video.sample_info).protected_data;
|
||||
assert_eq!(
|
||||
protected_data.scheme_type,
|
||||
Mp4ParseEncryptionSchemeType::Cbcs
|
||||
);
|
||||
assert_eq!(protected_data.is_encrypted, 0x01);
|
||||
assert_eq!(protected_data.iv_size, 0);
|
||||
assert_eq!(protected_data.kid.length, 16);
|
||||
let expected_kid = [
|
||||
0x7e, 0x57, 0x1d, 0x04, 0x7e, 0x57, 0x1d, 0x04, 0x7e, 0x57, 0x1d, 0x04, 0x7e, 0x57,
|
||||
0x1d, 0x21,
|
||||
];
|
||||
for (i, expected_byte) in expected_kid.iter().enumerate() {
|
||||
assert_eq!(&(*protected_data.kid.data.add(i)), expected_byte);
|
||||
}
|
||||
assert_eq!(protected_data.crypt_byte_block, 1);
|
||||
assert_eq!(protected_data.skip_byte_block, 9);
|
||||
assert_eq!(protected_data.constant_iv.length, 16);
|
||||
let expected_iv = [
|
||||
0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00, 0x11, 0x22, 0x33, 0x44,
|
||||
0x55, 0x66,
|
||||
];
|
||||
for (i, expected_byte) in expected_iv.iter().enumerate() {
|
||||
assert_eq!(&(*protected_data.constant_iv.data.add(i)), expected_byte);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_unencrypted() {
|
||||
// Ensure the encryption related data is not populated for files without
|
||||
// encryption metadata.
|
||||
let mut file = std::fs::File::open("tests/opus_audioinit.mp4").expect("Unknown file");
|
||||
let io = Mp4parseIo {
|
||||
read: Some(buf_read),
|
||||
userdata: &mut file as *mut _ as *mut std::os::raw::c_void,
|
||||
};
|
||||
|
||||
unsafe {
|
||||
let mut parser = std::ptr::null_mut();
|
||||
let mut rv = mp4parse_new(&io, &mut parser);
|
||||
assert_eq!(rv, Mp4parseStatus::Ok);
|
||||
assert!(!parser.is_null());
|
||||
|
||||
let mut counts: u32 = 0;
|
||||
rv = mp4parse_get_track_count(parser, &mut counts);
|
||||
assert_eq!(rv, Mp4parseStatus::Ok);
|
||||
assert_eq!(counts, 1);
|
||||
|
||||
let mut track_info = Mp4parseTrackInfo::default();
|
||||
rv = mp4parse_get_track_info(parser, 0, &mut track_info);
|
||||
assert_eq!(rv, Mp4parseStatus::Ok);
|
||||
assert_eq!(track_info.track_type, Mp4parseTrackType::Audio);
|
||||
|
||||
let mut audio = Mp4parseTrackAudioInfo::default();
|
||||
rv = mp4parse_get_track_audio_info(parser, 0, &mut audio);
|
||||
assert_eq!(rv, Mp4parseStatus::Ok);
|
||||
assert_eq!(audio.sample_info_count, 1);
|
||||
let protected_data = &(*audio.sample_info).protected_data;
|
||||
assert_eq!(
|
||||
protected_data.scheme_type,
|
||||
Mp4ParseEncryptionSchemeType::None
|
||||
);
|
||||
assert_eq!(protected_data.is_encrypted, 0x00);
|
||||
assert_eq!(protected_data.iv_size, 0);
|
||||
assert_eq!(protected_data.kid.length, 0);
|
||||
assert_eq!(protected_data.crypt_byte_block, 0);
|
||||
assert_eq!(protected_data.skip_byte_block, 0);
|
||||
assert_eq!(protected_data.constant_iv.length, 0);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
extern crate mp4parse_capi;
|
||||
use mp4parse_capi::*;
|
||||
use std::io::Read;
|
||||
|
||||
extern "C" fn buf_read(buf: *mut u8, size: usize, userdata: *mut std::os::raw::c_void) -> isize {
|
||||
let 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 parse_fragment() {
|
||||
let mut file = std::fs::File::open("tests/bipbop_audioinit.mp4").expect("Unknown file");
|
||||
let io = Mp4parseIo {
|
||||
read: Some(buf_read),
|
||||
userdata: &mut file as *mut _ as *mut std::os::raw::c_void,
|
||||
};
|
||||
|
||||
unsafe {
|
||||
let mut parser = std::ptr::null_mut();
|
||||
let mut rv = mp4parse_new(&io, &mut parser);
|
||||
assert_eq!(rv, Mp4parseStatus::Ok);
|
||||
assert!(!parser.is_null());
|
||||
|
||||
let mut counts: u32 = 0;
|
||||
rv = mp4parse_get_track_count(parser, &mut counts);
|
||||
assert_eq!(rv, Mp4parseStatus::Ok);
|
||||
assert_eq!(counts, 1);
|
||||
|
||||
let mut track_info = Mp4parseTrackInfo::default();
|
||||
rv = mp4parse_get_track_info(parser, 0, &mut track_info);
|
||||
assert_eq!(rv, Mp4parseStatus::Ok);
|
||||
assert_eq!(track_info.track_type, Mp4parseTrackType::Audio);
|
||||
assert_eq!(track_info.track_id, 1);
|
||||
assert_eq!(track_info.duration, 0);
|
||||
assert_eq!(track_info.media_time, 0);
|
||||
|
||||
let mut audio = Default::default();
|
||||
rv = mp4parse_get_track_audio_info(parser, 0, &mut audio);
|
||||
assert_eq!(rv, Mp4parseStatus::Ok);
|
||||
assert_eq!(audio.sample_info_count, 1);
|
||||
|
||||
assert_eq!((*audio.sample_info).codec_type, Mp4parseCodec::Aac);
|
||||
assert_eq!((*audio.sample_info).channels, 2);
|
||||
assert_eq!((*audio.sample_info).bit_depth, 16);
|
||||
assert_eq!((*audio.sample_info).sample_rate, 22050);
|
||||
assert_eq!((*audio.sample_info).extra_data.length, 27);
|
||||
assert_eq!((*audio.sample_info).codec_specific_config.length, 2);
|
||||
|
||||
let mut is_fragmented_file: u8 = 0;
|
||||
rv = mp4parse_is_fragmented(parser, track_info.track_id, &mut is_fragmented_file);
|
||||
assert_eq!(rv, Mp4parseStatus::Ok);
|
||||
assert_eq!(is_fragmented_file, 1);
|
||||
|
||||
let mut fragment_info = Mp4parseFragmentInfo::default();
|
||||
rv = mp4parse_get_fragment_info(parser, &mut fragment_info);
|
||||
assert_eq!(rv, Mp4parseStatus::Ok);
|
||||
assert_eq!(fragment_info.fragment_duration, 10_032_000);
|
||||
|
||||
mp4parse_free(parser);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_opus_fragment() {
|
||||
let mut file = std::fs::File::open("tests/opus_audioinit.mp4").expect("Unknown file");
|
||||
let io = Mp4parseIo {
|
||||
read: Some(buf_read),
|
||||
userdata: &mut file as *mut _ as *mut std::os::raw::c_void,
|
||||
};
|
||||
|
||||
unsafe {
|
||||
let mut parser = std::ptr::null_mut();
|
||||
let mut rv = mp4parse_new(&io, &mut parser);
|
||||
assert_eq!(rv, Mp4parseStatus::Ok);
|
||||
assert!(!parser.is_null());
|
||||
|
||||
let mut counts: u32 = 0;
|
||||
rv = mp4parse_get_track_count(parser, &mut counts);
|
||||
assert_eq!(rv, Mp4parseStatus::Ok);
|
||||
assert_eq!(counts, 1);
|
||||
|
||||
let mut track_info = Mp4parseTrackInfo::default();
|
||||
rv = mp4parse_get_track_info(parser, 0, &mut track_info);
|
||||
assert_eq!(rv, Mp4parseStatus::Ok);
|
||||
assert_eq!(track_info.track_type, Mp4parseTrackType::Audio);
|
||||
assert_eq!(track_info.track_id, 1);
|
||||
assert_eq!(track_info.duration, 0);
|
||||
assert_eq!(track_info.media_time, 0);
|
||||
|
||||
let mut audio = Default::default();
|
||||
rv = mp4parse_get_track_audio_info(parser, 0, &mut audio);
|
||||
assert_eq!(rv, Mp4parseStatus::Ok);
|
||||
assert_eq!(audio.sample_info_count, 1);
|
||||
|
||||
assert_eq!((*audio.sample_info).codec_type, Mp4parseCodec::Opus);
|
||||
assert_eq!((*audio.sample_info).channels, 1);
|
||||
assert_eq!((*audio.sample_info).bit_depth, 16);
|
||||
assert_eq!((*audio.sample_info).sample_rate, 48000);
|
||||
assert_eq!((*audio.sample_info).extra_data.length, 0);
|
||||
assert_eq!((*audio.sample_info).codec_specific_config.length, 19);
|
||||
|
||||
let mut is_fragmented_file: u8 = 0;
|
||||
rv = mp4parse_is_fragmented(parser, track_info.track_id, &mut is_fragmented_file);
|
||||
assert_eq!(rv, Mp4parseStatus::Ok);
|
||||
assert_eq!(is_fragmented_file, 1);
|
||||
|
||||
let mut fragment_info = Mp4parseFragmentInfo::default();
|
||||
rv = mp4parse_get_fragment_info(parser, &mut fragment_info);
|
||||
assert_eq!(rv, Mp4parseStatus::Ok);
|
||||
|
||||
mp4parse_free(parser);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
extern crate mp4parse_capi;
|
||||
use mp4parse_capi::*;
|
||||
use std::io::Read;
|
||||
|
||||
extern "C" fn buf_read(buf: *mut u8, size: usize, userdata: *mut std::os::raw::c_void) -> isize {
|
||||
let 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 parse_rotation() {
|
||||
let mut file = std::fs::File::open("tests/video_rotation_90.mp4").expect("Unknown file");
|
||||
let io = Mp4parseIo {
|
||||
read: Some(buf_read),
|
||||
userdata: &mut file as *mut _ as *mut std::os::raw::c_void,
|
||||
};
|
||||
|
||||
unsafe {
|
||||
let mut parser = std::ptr::null_mut();
|
||||
let mut rv = mp4parse_new(&io, &mut parser);
|
||||
assert_eq!(rv, Mp4parseStatus::Ok);
|
||||
assert!(!parser.is_null());
|
||||
|
||||
let mut counts: u32 = 0;
|
||||
rv = mp4parse_get_track_count(parser, &mut counts);
|
||||
assert_eq!(rv, Mp4parseStatus::Ok);
|
||||
assert_eq!(counts, 1);
|
||||
|
||||
let mut video = Mp4parseTrackVideoInfo::default();
|
||||
|
||||
let rv = mp4parse_get_track_video_info(parser, 0, &mut video);
|
||||
assert_eq!(rv, Mp4parseStatus::Ok);
|
||||
assert_eq!(video.rotation, 90);
|
||||
|
||||
mp4parse_free(parser);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,278 @@
|
|||
extern crate mp4parse_capi;
|
||||
use mp4parse_capi::*;
|
||||
use std::io::Read;
|
||||
|
||||
extern "C" fn buf_read(buf: *mut u8, size: usize, userdata: *mut std::os::raw::c_void) -> isize {
|
||||
let 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 parse_sample_table() {
|
||||
let mut file =
|
||||
std::fs::File::open("tests/bipbop_nonfragment_header.mp4").expect("Unknown file");
|
||||
let io = Mp4parseIo {
|
||||
read: Some(buf_read),
|
||||
userdata: &mut file as *mut _ as *mut std::os::raw::c_void,
|
||||
};
|
||||
|
||||
unsafe {
|
||||
let mut parser = std::ptr::null_mut();
|
||||
let mut rv = mp4parse_new(&io, &mut parser);
|
||||
assert_eq!(rv, Mp4parseStatus::Ok);
|
||||
assert!(!parser.is_null());
|
||||
|
||||
let mut counts: u32 = 0;
|
||||
rv = mp4parse_get_track_count(parser, &mut counts);
|
||||
assert_eq!(rv, Mp4parseStatus::Ok);
|
||||
assert_eq!(counts, 2);
|
||||
|
||||
let mut track_info = Mp4parseTrackInfo::default();
|
||||
rv = mp4parse_get_track_info(parser, 1, &mut track_info);
|
||||
assert_eq!(rv, Mp4parseStatus::Ok);
|
||||
assert_eq!(track_info.track_type, Mp4parseTrackType::Audio);
|
||||
|
||||
// Check audio smaple table
|
||||
let mut is_fragmented_file: u8 = 0;
|
||||
rv = mp4parse_is_fragmented(parser, track_info.track_id, &mut is_fragmented_file);
|
||||
assert_eq!(rv, Mp4parseStatus::Ok);
|
||||
assert_eq!(is_fragmented_file, 0);
|
||||
|
||||
let mut indice = Mp4parseByteData::default();
|
||||
rv = mp4parse_get_indice_table(parser, track_info.track_id, &mut indice);
|
||||
assert_eq!(rv, Mp4parseStatus::Ok);
|
||||
|
||||
// Compare the value from stagefright.
|
||||
let audio_indice_0 = Mp4parseIndice {
|
||||
start_offset: 27_046,
|
||||
end_offset: 27_052,
|
||||
start_composition: 0,
|
||||
end_composition: 46_439,
|
||||
start_decode: 0,
|
||||
sync: true,
|
||||
};
|
||||
let audio_indice_215 = Mp4parseIndice {
|
||||
start_offset: 283_550,
|
||||
end_offset: 283_556,
|
||||
start_composition: 9_984_580,
|
||||
end_composition: 10_031_020,
|
||||
start_decode: 9_984_580,
|
||||
sync: true,
|
||||
};
|
||||
assert_eq!(indice.length, 216);
|
||||
assert_eq!(*indice.indices.offset(0), audio_indice_0);
|
||||
assert_eq!(*indice.indices.offset(215), audio_indice_215);
|
||||
|
||||
// Check video smaple table
|
||||
rv = mp4parse_get_track_info(parser, 0, &mut track_info);
|
||||
assert_eq!(rv, Mp4parseStatus::Ok);
|
||||
assert_eq!(track_info.track_type, Mp4parseTrackType::Video);
|
||||
|
||||
let mut is_fragmented_file: u8 = 0;
|
||||
rv = mp4parse_is_fragmented(parser, track_info.track_id, &mut is_fragmented_file);
|
||||
assert_eq!(rv, Mp4parseStatus::Ok);
|
||||
assert_eq!(is_fragmented_file, 0);
|
||||
|
||||
let mut indice = Mp4parseByteData::default();
|
||||
rv = mp4parse_get_indice_table(parser, track_info.track_id, &mut indice);
|
||||
assert_eq!(rv, Mp4parseStatus::Ok);
|
||||
|
||||
// Compare the last few data from stagefright.
|
||||
let video_indice_291 = Mp4parseIndice {
|
||||
start_offset: 280_226,
|
||||
end_offset: 280_855,
|
||||
start_composition: 9_838_333,
|
||||
end_composition: 9_871_677,
|
||||
start_decode: 9_710_000,
|
||||
sync: false,
|
||||
};
|
||||
let video_indice_292 = Mp4parseIndice {
|
||||
start_offset: 280_855,
|
||||
end_offset: 281_297,
|
||||
start_composition: 9_805_011,
|
||||
end_composition: 9_838_333,
|
||||
start_decode: 9_710_011,
|
||||
sync: false,
|
||||
};
|
||||
// TODO: start_composition time in stagefright is 9905000, but it is 9904999 in parser, it
|
||||
// could be rounding error.
|
||||
//let video_indice_293 = Mp4parseIndice { start_offset: 281_297, end_offset: 281_919, start_composition: 9_905_000, end_composition: 9_938_344, start_decode: 9_776_666, sync: false };
|
||||
//let video_indice_294 = Mp4parseIndice { start_offset: 281_919, end_offset: 282_391, start_composition: 9_871_677, end_composition: 9_905_000, start_decode: 9_776_677, sync: false };
|
||||
let video_indice_295 = Mp4parseIndice {
|
||||
start_offset: 282_391,
|
||||
end_offset: 283_032,
|
||||
start_composition: 9_971_666,
|
||||
end_composition: 9_971_677,
|
||||
start_decode: 9_843_333,
|
||||
sync: false,
|
||||
};
|
||||
let video_indice_296 = Mp4parseIndice {
|
||||
start_offset: 283_092,
|
||||
end_offset: 283_526,
|
||||
start_composition: 9_938_344,
|
||||
end_composition: 9_971_666,
|
||||
start_decode: 9_843_344,
|
||||
sync: false,
|
||||
};
|
||||
|
||||
assert_eq!(indice.length, 297);
|
||||
assert_eq!(*indice.indices.offset(291), video_indice_291);
|
||||
assert_eq!(*indice.indices.offset(292), video_indice_292);
|
||||
//assert_eq!(*indice.indices.offset(293), video_indice_293);
|
||||
//assert_eq!(*indice.indices.offset(294), video_indice_294);
|
||||
assert_eq!(*indice.indices.offset(295), video_indice_295);
|
||||
assert_eq!(*indice.indices.offset(296), video_indice_296);
|
||||
|
||||
mp4parse_free(parser);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_sample_table_with_elst() {
|
||||
let mut file = std::fs::File::open("tests/short-cenc.mp4").expect("Unknown file");
|
||||
let io = Mp4parseIo {
|
||||
read: Some(buf_read),
|
||||
userdata: &mut file as *mut _ as *mut std::os::raw::c_void,
|
||||
};
|
||||
|
||||
unsafe {
|
||||
let mut parser = std::ptr::null_mut();
|
||||
let mut rv = mp4parse_new(&io, &mut parser);
|
||||
assert_eq!(rv, Mp4parseStatus::Ok);
|
||||
assert!(!parser.is_null());
|
||||
|
||||
let mut counts: u32 = 0;
|
||||
rv = mp4parse_get_track_count(parser, &mut counts);
|
||||
assert_eq!(rv, Mp4parseStatus::Ok);
|
||||
assert_eq!(counts, 2);
|
||||
|
||||
let mut track_info = Mp4parseTrackInfo::default();
|
||||
rv = mp4parse_get_track_info(parser, 1, &mut track_info);
|
||||
assert_eq!(rv, Mp4parseStatus::Ok);
|
||||
assert_eq!(track_info.track_type, Mp4parseTrackType::Audio);
|
||||
|
||||
// Check audio sample table
|
||||
let mut is_fragmented_file: u8 = std::u8::MAX;
|
||||
rv = mp4parse_is_fragmented(parser, track_info.track_id, &mut is_fragmented_file);
|
||||
assert_eq!(rv, Mp4parseStatus::Ok);
|
||||
assert_eq!(is_fragmented_file, 0);
|
||||
|
||||
let mut indice = Mp4parseByteData::default();
|
||||
rv = mp4parse_get_indice_table(parser, track_info.track_id, &mut indice);
|
||||
assert_eq!(rv, Mp4parseStatus::Ok);
|
||||
|
||||
// Compare the value from stagefright.
|
||||
// Due to 'elst', the start_composition and end_composition are negative
|
||||
// at first two samples.
|
||||
let audio_indice_0 = Mp4parseIndice {
|
||||
start_offset: 6992,
|
||||
end_offset: 7363,
|
||||
start_composition: -36281,
|
||||
end_composition: -13062,
|
||||
start_decode: 0,
|
||||
sync: true,
|
||||
};
|
||||
let audio_indice_1 = Mp4parseIndice {
|
||||
start_offset: 7363,
|
||||
end_offset: 7735,
|
||||
start_composition: -13062,
|
||||
end_composition: 10158,
|
||||
start_decode: 23219,
|
||||
sync: true,
|
||||
};
|
||||
let audio_indice_2 = Mp4parseIndice {
|
||||
start_offset: 7735,
|
||||
end_offset: 8106,
|
||||
start_composition: 10158,
|
||||
end_composition: 33378,
|
||||
start_decode: 46439,
|
||||
sync: true,
|
||||
};
|
||||
assert_eq!(indice.length, 21);
|
||||
assert_eq!(*indice.indices.offset(0), audio_indice_0);
|
||||
assert_eq!(*indice.indices.offset(1), audio_indice_1);
|
||||
assert_eq!(*indice.indices.offset(2), audio_indice_2);
|
||||
|
||||
mp4parse_free(parser);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_sample_table_with_negative_ctts() {
|
||||
let mut file = std::fs::File::open("tests/white.mp4").expect("Unknown file");
|
||||
let io = Mp4parseIo {
|
||||
read: Some(buf_read),
|
||||
userdata: &mut file as *mut _ as *mut std::os::raw::c_void,
|
||||
};
|
||||
|
||||
unsafe {
|
||||
let mut parser = std::ptr::null_mut();
|
||||
let mut rv = mp4parse_new(&io, &mut parser);
|
||||
assert_eq!(rv, Mp4parseStatus::Ok);
|
||||
assert!(!parser.is_null());
|
||||
|
||||
let mut counts: u32 = 0;
|
||||
rv = mp4parse_get_track_count(parser, &mut counts);
|
||||
assert_eq!(rv, Mp4parseStatus::Ok);
|
||||
assert_eq!(counts, 1);
|
||||
|
||||
let mut track_info = Mp4parseTrackInfo::default();
|
||||
rv = mp4parse_get_track_info(parser, 0, &mut track_info);
|
||||
assert_eq!(rv, Mp4parseStatus::Ok);
|
||||
assert_eq!(track_info.track_type, Mp4parseTrackType::Video);
|
||||
|
||||
let mut is_fragmented_file: u8 = std::u8::MAX;
|
||||
rv = mp4parse_is_fragmented(parser, track_info.track_id, &mut is_fragmented_file);
|
||||
assert_eq!(rv, Mp4parseStatus::Ok);
|
||||
assert_eq!(is_fragmented_file, 0);
|
||||
|
||||
let mut indice = Mp4parseByteData::default();
|
||||
rv = mp4parse_get_indice_table(parser, track_info.track_id, &mut indice);
|
||||
assert_eq!(rv, Mp4parseStatus::Ok);
|
||||
|
||||
// There are negative value in 'ctts' table.
|
||||
let video_indice_0 = Mp4parseIndice {
|
||||
start_offset: 48,
|
||||
end_offset: 890,
|
||||
start_composition: 0,
|
||||
end_composition: 33_333,
|
||||
start_decode: 0,
|
||||
sync: true,
|
||||
};
|
||||
let video_indice_1 = Mp4parseIndice {
|
||||
start_offset: 890,
|
||||
end_offset: 913,
|
||||
start_composition: 133_333,
|
||||
end_composition: 166_666,
|
||||
start_decode: 33_333,
|
||||
sync: false,
|
||||
};
|
||||
let video_indice_2 = Mp4parseIndice {
|
||||
start_offset: 913,
|
||||
end_offset: 934,
|
||||
start_composition: 66_666,
|
||||
end_composition: 100_000,
|
||||
start_decode: 66_666,
|
||||
sync: false,
|
||||
};
|
||||
let video_indice_3 = Mp4parseIndice {
|
||||
start_offset: 934,
|
||||
end_offset: 955,
|
||||
start_composition: 33_333,
|
||||
end_composition: 66_666,
|
||||
start_decode: 100_000,
|
||||
sync: false,
|
||||
};
|
||||
assert_eq!(indice.length, 300);
|
||||
assert_eq!(*indice.indices.offset(0), video_indice_0);
|
||||
assert_eq!(*indice.indices.offset(1), video_indice_1);
|
||||
assert_eq!(*indice.indices.offset(2), video_indice_2);
|
||||
assert_eq!(*indice.indices.offset(3), video_indice_3);
|
||||
|
||||
mp4parse_free(parser);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
extern crate mp4parse_capi;
|
||||
use mp4parse_capi::*;
|
||||
use std::io::Read;
|
||||
|
||||
extern "C" fn buf_read(buf: *mut u8, size: usize, userdata: *mut std::os::raw::c_void) -> isize {
|
||||
let 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 parse_invalid_stsc_table() {
|
||||
let mut file = std::fs::File::open("tests/zero_empty_stsc.mp4").expect("Unknown file");
|
||||
let io = Mp4parseIo {
|
||||
read: Some(buf_read),
|
||||
userdata: &mut file as *mut _ as *mut std::os::raw::c_void,
|
||||
};
|
||||
|
||||
unsafe {
|
||||
let mut parser = std::ptr::null_mut();
|
||||
let rv = mp4parse_new(&io, &mut parser);
|
||||
|
||||
assert_eq!(rv, Mp4parseStatus::Ok);
|
||||
assert!(!parser.is_null());
|
||||
|
||||
let mut counts: u32 = 0;
|
||||
let rv = mp4parse_get_track_count(parser, &mut counts);
|
||||
assert_eq!(rv, Mp4parseStatus::Ok);
|
||||
assert_eq!(counts, 2);
|
||||
|
||||
let mut indice_video = Mp4parseByteData::default();
|
||||
let rv = mp4parse_get_indice_table(parser, 1, &mut indice_video);
|
||||
assert_eq!(rv, Mp4parseStatus::Ok);
|
||||
assert_eq!(indice_video.length, 1040);
|
||||
|
||||
let mut indice_audio = Mp4parseByteData::default();
|
||||
let rv = mp4parse_get_indice_table(parser, 2, &mut indice_audio);
|
||||
assert_eq!(rv, Mp4parseStatus::Ok);
|
||||
assert_eq!(indice_audio.length, 1952);
|
||||
|
||||
mp4parse_free(parser);
|
||||
}
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
{"files":{"CODE_OF_CONDUCT.md":"902d5357af363426631d907e641e220b3ec89039164743f8442b3f120479b7cf","Cargo.toml":"2edecc4249f6ff011255fe3c92892050e466246b23ee6d002243b0df760625c7","LICENSE":"fab3dd6bdab226f1c08630b1dd917e11fcb4ec5e1e020e2c16f83a0a13863e85","README":"6f28a5c89ff7c018760402038a991a581771b8f66869268a7288f64915f192a6","lib.rs":"70f5bec52c586809882a1edce407552e8a5c3d0f126eaa92abd676484395cfc8"},"package":"704f773471ac3e7110427b6bdf93184932b19319c9b7717688da5424e519b10a"}
|
|
@ -1,15 +0,0 @@
|
|||
# Community Participation Guidelines
|
||||
|
||||
This repository is governed by Mozilla's code of conduct and etiquette guidelines.
|
||||
For more details, please read the
|
||||
[Mozilla Community Participation Guidelines](https://www.mozilla.org/about/governance/policies/participation/).
|
||||
|
||||
## How to Report
|
||||
For more information on how to report violations of the Community Participation Guidelines, please read our '[How to Report](https://www.mozilla.org/about/governance/policies/participation/reporting/)' page.
|
||||
|
||||
<!--
|
||||
## Project Specific Etiquette
|
||||
|
||||
In some cases, there will be additional project etiquette i.e.: (https://bugzilla.mozilla.org/page.cgi?id=etiquette.html).
|
||||
Please update for your project.
|
||||
-->
|
|
@ -1,26 +0,0 @@
|
|||
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g., crates.io) dependencies
|
||||
#
|
||||
# If you believe there's an error in this file please file an
|
||||
# issue against the rust-lang/cargo repository. If you're
|
||||
# editing this file be aware that the upstream Cargo.toml
|
||||
# will likely look very different (and much more reasonable)
|
||||
|
||||
[package]
|
||||
name = "mp4parse_fallible"
|
||||
version = "0.0.3"
|
||||
authors = ["The Servo Project Developers"]
|
||||
description = "Fallible replacement for Vec"
|
||||
documentation = "https://docs.rs/mp4parse_fallible/"
|
||||
license = "MPL-2.0"
|
||||
repository = "https://github.com/mozilla/mp4parse_fallible"
|
||||
|
||||
[lib]
|
||||
name = "mp4parse_fallible"
|
||||
path = "lib.rs"
|
||||
[badges.travis-ci]
|
||||
repository = "https://github.com/mozilla/mp4parse_fallible"
|
|
@ -1,373 +0,0 @@
|
|||
Mozilla Public License Version 2.0
|
||||
==================================
|
||||
|
||||
1. Definitions
|
||||
--------------
|
||||
|
||||
1.1. "Contributor"
|
||||
means each individual or legal entity that creates, contributes to
|
||||
the creation of, or owns Covered Software.
|
||||
|
||||
1.2. "Contributor Version"
|
||||
means the combination of the Contributions of others (if any) used
|
||||
by a Contributor and that particular Contributor's Contribution.
|
||||
|
||||
1.3. "Contribution"
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
1.4. "Covered Software"
|
||||
means Source Code Form to which the initial Contributor has attached
|
||||
the notice in Exhibit A, the Executable Form of such Source Code
|
||||
Form, and Modifications of such Source Code Form, in each case
|
||||
including portions thereof.
|
||||
|
||||
1.5. "Incompatible With Secondary Licenses"
|
||||
means
|
||||
|
||||
(a) that the initial Contributor has attached the notice described
|
||||
in Exhibit B to the Covered Software; or
|
||||
|
||||
(b) that the Covered Software was made available under the terms of
|
||||
version 1.1 or earlier of the License, but not also under the
|
||||
terms of a Secondary License.
|
||||
|
||||
1.6. "Executable Form"
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
1.7. "Larger Work"
|
||||
means a work that combines Covered Software with other material, in
|
||||
a separate file or files, that is not Covered Software.
|
||||
|
||||
1.8. "License"
|
||||
means this document.
|
||||
|
||||
1.9. "Licensable"
|
||||
means having the right to grant, to the maximum extent possible,
|
||||
whether at the time of the initial grant or subsequently, any and
|
||||
all of the rights conveyed by this License.
|
||||
|
||||
1.10. "Modifications"
|
||||
means any of the following:
|
||||
|
||||
(a) any file in Source Code Form that results from an addition to,
|
||||
deletion from, or modification of the contents of Covered
|
||||
Software; or
|
||||
|
||||
(b) any new file in Source Code Form that contains any Covered
|
||||
Software.
|
||||
|
||||
1.11. "Patent Claims" of a Contributor
|
||||
means any patent claim(s), including without limitation, method,
|
||||
process, and apparatus claims, in any patent Licensable by such
|
||||
Contributor that would be infringed, but for the grant of the
|
||||
License, by the making, using, selling, offering for sale, having
|
||||
made, import, or transfer of either its Contributions or its
|
||||
Contributor Version.
|
||||
|
||||
1.12. "Secondary License"
|
||||
means either the GNU General Public License, Version 2.0, the GNU
|
||||
Lesser General Public License, Version 2.1, the GNU Affero General
|
||||
Public License, Version 3.0, or any later versions of those
|
||||
licenses.
|
||||
|
||||
1.13. "Source Code Form"
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
1.14. "You" (or "Your")
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, "You" includes any entity that
|
||||
controls, is controlled by, or is under common control with You. For
|
||||
purposes of this definition, "control" means (a) the power, direct
|
||||
or indirect, to cause the direction or management of such entity,
|
||||
whether by contract or otherwise, or (b) ownership of more than
|
||||
fifty percent (50%) of the outstanding shares or beneficial
|
||||
ownership of such entity.
|
||||
|
||||
2. License Grants and Conditions
|
||||
--------------------------------
|
||||
|
||||
2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
|
||||
(a) under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or
|
||||
as part of a Larger Work; and
|
||||
|
||||
(b) under Patent Claims of such Contributor to make, use, sell, offer
|
||||
for sale, have made, import, and otherwise transfer either its
|
||||
Contributions or its Contributor Version.
|
||||
|
||||
2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution
|
||||
become effective for each Contribution on the date the Contributor first
|
||||
distributes such Contribution.
|
||||
|
||||
2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under
|
||||
this License. No additional rights or licenses will be implied from the
|
||||
distribution or licensing of Covered Software under this License.
|
||||
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||
Contributor:
|
||||
|
||||
(a) for any code that a Contributor has removed from Covered Software;
|
||||
or
|
||||
|
||||
(b) for infringements caused by: (i) Your and any other third party's
|
||||
modifications of Covered Software, or (ii) the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
|
||||
(c) under Patent Claims infringed by Covered Software in the absence of
|
||||
its Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks,
|
||||
or logos of any Contributor (except as may be necessary to comply with
|
||||
the notice requirements in Section 3.4).
|
||||
|
||||
2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this
|
||||
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||
permitted under the terms of Section 3.3).
|
||||
|
||||
2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its
|
||||
Contributions are its original creation(s) or it has sufficient rights
|
||||
to grant the rights to its Contributions conveyed by this License.
|
||||
|
||||
2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under
|
||||
applicable copyright doctrines of fair use, fair dealing, or other
|
||||
equivalents.
|
||||
|
||||
2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
|
||||
in Section 2.1.
|
||||
|
||||
3. Responsibilities
|
||||
-------------------
|
||||
|
||||
3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under
|
||||
the terms of this License. You must inform recipients that the Source
|
||||
Code Form of the Covered Software is governed by the terms of this
|
||||
License, and how they can obtain a copy of this License. You may not
|
||||
attempt to alter or restrict the recipients' rights in the Source Code
|
||||
Form.
|
||||
|
||||
3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
(a) such Covered Software must also be made available in Source Code
|
||||
Form, as described in Section 3.1, and You must inform recipients of
|
||||
the Executable Form how they can obtain a copy of such Source Code
|
||||
Form by reasonable means in a timely manner, at a charge no more
|
||||
than the cost of distribution to the recipient; and
|
||||
|
||||
(b) You may distribute such Executable Form under the terms of this
|
||||
License, or sublicense it under different terms, provided that the
|
||||
license for the Executable Form does not attempt to limit or alter
|
||||
the recipients' rights in the Source Code Form under this License.
|
||||
|
||||
3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for
|
||||
the Covered Software. If the Larger Work is a combination of Covered
|
||||
Software with a work governed by one or more Secondary Licenses, and the
|
||||
Covered Software is not Incompatible With Secondary Licenses, this
|
||||
License permits You to additionally distribute such Covered Software
|
||||
under the terms of such Secondary License(s), so that the recipient of
|
||||
the Larger Work may, at their option, further distribute the Covered
|
||||
Software under the terms of either this License or such Secondary
|
||||
License(s).
|
||||
|
||||
3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices
|
||||
(including copyright notices, patent notices, disclaimers of warranty,
|
||||
or limitations of liability) contained within the Source Code Form of
|
||||
the Covered Software, except that You may alter any license notices to
|
||||
the extent required to remedy known factual inaccuracies.
|
||||
|
||||
3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on
|
||||
behalf of any Contributor. You must make it absolutely clear that any
|
||||
such warranty, support, indemnity, or liability obligation is offered by
|
||||
You alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
|
||||
4. Inability to Comply Due to Statute or Regulation
|
||||
---------------------------------------------------
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this
|
||||
License with respect to some or all of the Covered Software due to
|
||||
statute, judicial order, or regulation then You must: (a) comply with
|
||||
the terms of this License to the maximum extent possible; and (b)
|
||||
describe the limitations and the code they affect. Such description must
|
||||
be placed in a text file included with all distributions of the Covered
|
||||
Software under this License. Except to the extent prohibited by statute
|
||||
or regulation, such description must be sufficiently detailed for a
|
||||
recipient of ordinary skill to be able to understand it.
|
||||
|
||||
5. Termination
|
||||
--------------
|
||||
|
||||
5.1. The rights granted under this License will terminate automatically
|
||||
if You fail to comply with any of its terms. However, if You become
|
||||
compliant, then the rights granted under this License from a particular
|
||||
Contributor are reinstated (a) provisionally, unless and until such
|
||||
Contributor explicitly and finally terminates Your grants, and (b) on an
|
||||
ongoing basis, if such Contributor fails to notify You of the
|
||||
non-compliance by some reasonable means prior to 60 days after You have
|
||||
come back into compliance. Moreover, Your grants from a particular
|
||||
Contributor are reinstated on an ongoing basis if such Contributor
|
||||
notifies You of the non-compliance by some reasonable means, this is the
|
||||
first time You have received notice of non-compliance with this License
|
||||
from such Contributor, and You become compliant prior to 30 days after
|
||||
Your receipt of the notice.
|
||||
|
||||
5.2. If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions,
|
||||
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||
directly or indirectly infringes any patent, then the rights granted to
|
||||
You by any and all Contributors for the Covered Software under Section
|
||||
2.1 of this License shall terminate.
|
||||
|
||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
|
||||
end user license agreements (excluding distributors and resellers) which
|
||||
have been validly granted by You or Your distributors under this License
|
||||
prior to termination shall survive termination.
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 6. Disclaimer of Warranty *
|
||||
* ------------------------- *
|
||||
* *
|
||||
* Covered Software is provided under this License on an "as is" *
|
||||
* basis, without warranty of any kind, either expressed, implied, or *
|
||||
* statutory, including, without limitation, warranties that the *
|
||||
* Covered Software is free of defects, merchantable, fit for a *
|
||||
* particular purpose or non-infringing. The entire risk as to the *
|
||||
* quality and performance of the Covered Software is with You. *
|
||||
* Should any Covered Software prove defective in any respect, You *
|
||||
* (not any Contributor) assume the cost of any necessary servicing, *
|
||||
* repair, or correction. This disclaimer of warranty constitutes an *
|
||||
* essential part of this License. No use of any Covered Software is *
|
||||
* authorized under this License except under this disclaimer. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 7. Limitation of Liability *
|
||||
* -------------------------- *
|
||||
* *
|
||||
* Under no circumstances and under no legal theory, whether tort *
|
||||
* (including negligence), contract, or otherwise, shall any *
|
||||
* Contributor, or anyone who distributes Covered Software as *
|
||||
* permitted above, be liable to You for any direct, indirect, *
|
||||
* special, incidental, or consequential damages of any character *
|
||||
* including, without limitation, damages for lost profits, loss of *
|
||||
* goodwill, work stoppage, computer failure or malfunction, or any *
|
||||
* and all other commercial damages or losses, even if such party *
|
||||
* shall have been informed of the possibility of such damages. This *
|
||||
* limitation of liability shall not apply to liability for death or *
|
||||
* personal injury resulting from such party's negligence to the *
|
||||
* extent applicable law prohibits such limitation. Some *
|
||||
* jurisdictions do not allow the exclusion or limitation of *
|
||||
* incidental or consequential damages, so this exclusion and *
|
||||
* limitation may not apply to You. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
8. Litigation
|
||||
-------------
|
||||
|
||||
Any litigation relating to this License may be brought only in the
|
||||
courts of a jurisdiction where the defendant maintains its principal
|
||||
place of business and such litigation shall be governed by laws of that
|
||||
jurisdiction, without reference to its conflict-of-law provisions.
|
||||
Nothing in this Section shall prevent a party's ability to bring
|
||||
cross-claims or counter-claims.
|
||||
|
||||
9. Miscellaneous
|
||||
----------------
|
||||
|
||||
This License represents the complete agreement concerning the subject
|
||||
matter hereof. If any provision of this License is held to be
|
||||
unenforceable, such provision shall be reformed only to the extent
|
||||
necessary to make it enforceable. Any law or regulation which provides
|
||||
that the language of a contract shall be construed against the drafter
|
||||
shall not be used to construe this License against a Contributor.
|
||||
|
||||
10. Versions of the License
|
||||
---------------------------
|
||||
|
||||
10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version
|
||||
of the License under which You originally received the Covered Software,
|
||||
or under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
|
||||
10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a
|
||||
modified version of this License if you rename the license and remove
|
||||
any references to the name of the license steward (except to note that
|
||||
such modified license differs from this License).
|
||||
|
||||
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||
Licenses
|
||||
|
||||
If You choose to distribute Source Code Form that is Incompatible With
|
||||
Secondary Licenses under the terms of this version of the License, the
|
||||
notice described in Exhibit B of this License must be attached.
|
||||
|
||||
Exhibit A - Source Code Form License Notice
|
||||
-------------------------------------------
|
||||
|
||||
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 http://mozilla.org/MPL/2.0/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular
|
||||
file, then You may include the notice in a location (such as a LICENSE
|
||||
file in a relevant directory) where a recipient would be likely to look
|
||||
for such a notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||
---------------------------------------------------------
|
||||
|
||||
This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
defined by the Mozilla Public License, v. 2.0.
|
|
@ -1,7 +0,0 @@
|
|||
This is from https://github.com/servo/servo/tree/master/components/fallible
|
||||
with modification for mp4 demuxer.
|
||||
|
||||
The purpose of this crate is to solve infallible memory allocation problem
|
||||
which causes OOM easily on win32. This is more like a temporary solution.
|
||||
Once rust supports fallible memory allocation in its stdlib, this can be
|
||||
retired.
|
|
@ -1,147 +0,0 @@
|
|||
/* 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 http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use std::mem;
|
||||
use std::vec::Vec;
|
||||
|
||||
extern "C" {
|
||||
fn realloc(ptr: *mut u8, bytes: usize) -> *mut u8;
|
||||
fn malloc(bytes: usize) -> *mut u8;
|
||||
}
|
||||
|
||||
pub trait FallibleVec<T> {
|
||||
/// Append |val| to the end of |vec|. Returns Ok(()) on success,
|
||||
/// Err(()) if it fails, which can only be due to lack of memory.
|
||||
fn try_push(&mut self, value: T) -> Result<(), ()>;
|
||||
|
||||
/// Reserves capacity for at least `additional` more elements to
|
||||
/// be inserted in the vector. Does nothing if capacity is already
|
||||
/// sufficient. Return Ok(()) on success, Err(()) if it fails either
|
||||
/// due to lack of memory, or overflowing the `usize` used to store
|
||||
/// the capacity.
|
||||
fn try_reserve(&mut self, additional: usize) -> Result<(), ()>;
|
||||
|
||||
/// Clones and appends all elements in a slice to the Vec.
|
||||
/// Returns Ok(()) on success, Err(()) if it fails, which can
|
||||
/// only be due to lack of memory.
|
||||
fn try_extend_from_slice(&mut self, other: &[T]) -> Result<(), ()> where T: Clone;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// Vec
|
||||
|
||||
impl<T> FallibleVec<T> for Vec<T> {
|
||||
#[inline]
|
||||
fn try_push(&mut self, val: T) -> Result<(), ()> {
|
||||
if self.capacity() == self.len() {
|
||||
let old_cap: usize = self.capacity();
|
||||
let new_cap: usize
|
||||
= if old_cap == 0 { 4 } else { old_cap.checked_mul(2).ok_or(()) ? };
|
||||
|
||||
try_extend_vec(self, new_cap)?;
|
||||
debug_assert!(self.capacity() > self.len());
|
||||
}
|
||||
self.push(val);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_reserve(&mut self, additional: usize) -> Result<(), ()> {
|
||||
let available = self.capacity().checked_sub(self.len()).expect("capacity >= len");
|
||||
if additional > available {
|
||||
let increase = additional.checked_sub(available).expect("additional > available");
|
||||
let new_cap = self.capacity().checked_add(increase).ok_or(())?;
|
||||
try_extend_vec(self, new_cap)?;
|
||||
debug_assert!(self.capacity() == new_cap);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_extend_from_slice(&mut self, other: &[T]) -> Result<(), ()> where T: Clone {
|
||||
FallibleVec::try_reserve(self, other.len())?;
|
||||
self.extend_from_slice(other);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
#[cold]
|
||||
fn try_extend_vec<T>(vec: &mut Vec<T>, new_cap: usize) -> Result<(), ()> {
|
||||
let old_ptr = vec.as_mut_ptr();
|
||||
let old_len = vec.len();
|
||||
|
||||
let old_cap: usize = vec.capacity();
|
||||
|
||||
if old_cap >= new_cap {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let new_size_bytes
|
||||
= new_cap.checked_mul(mem::size_of::<T>()).ok_or(()) ? ;
|
||||
|
||||
let new_ptr = unsafe {
|
||||
if old_cap == 0 {
|
||||
malloc(new_size_bytes)
|
||||
} else {
|
||||
realloc(old_ptr as *mut u8, new_size_bytes)
|
||||
}
|
||||
};
|
||||
|
||||
if new_ptr.is_null() {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
let new_vec = unsafe {
|
||||
Vec::from_raw_parts(new_ptr as *mut T, old_len, new_cap)
|
||||
};
|
||||
|
||||
mem::forget(mem::replace(vec, new_vec));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn oom() {
|
||||
let mut vec: Vec<char> = Vec::new();
|
||||
match FallibleVec::try_reserve(&mut vec, std::usize::MAX) {
|
||||
Ok(_) => panic!("it should be OOM"),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn try_reserve() {
|
||||
let mut vec = vec![1];
|
||||
let old_cap = vec.capacity();
|
||||
let new_cap = old_cap + 1;
|
||||
FallibleVec::try_reserve(&mut vec, new_cap).unwrap();
|
||||
assert!(vec.capacity() >= new_cap);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn try_reserve_idempotent() {
|
||||
let mut vec = vec![1];
|
||||
let old_cap = vec.capacity();
|
||||
let new_cap = old_cap + 1;
|
||||
FallibleVec::try_reserve(&mut vec, new_cap).unwrap();
|
||||
let cap_after_reserve = vec.capacity();
|
||||
FallibleVec::try_reserve(&mut vec, new_cap).unwrap();
|
||||
assert_eq!(cap_after_reserve, vec.capacity());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn capacity_overflow() {
|
||||
let mut vec = vec![1];
|
||||
match FallibleVec::try_reserve(&mut vec, std::usize::MAX) {
|
||||
Ok(_) => panic!("capacity calculation should overflow"),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extend_from_slice() {
|
||||
let mut vec = b"foo".to_vec();
|
||||
FallibleVec::try_extend_from_slice(&mut vec, b"bar").unwrap();
|
||||
assert_eq!(&vec, b"foobar");
|
||||
}
|
|
@ -9,7 +9,7 @@ description = "Shared Rust code for libxul"
|
|||
geckoservo = { path = "../../../../servo/ports/geckolib" }
|
||||
kvstore = { path = "../../../components/kvstore" }
|
||||
lmdb-rkv-sys = { version = "0.11", features = ["mdb_idl_logn_9"] }
|
||||
mp4parse_capi = { path = "../../../../media/mp4parse-rust/mp4parse_capi" }
|
||||
mp4parse_capi = { git = "https://github.com/mozilla/mp4parse-rust", rev = "0dc3e6e7c5371fe21f69b847f61c65fe6d6dc317" }
|
||||
nserror = { path = "../../../../xpcom/rust/nserror" }
|
||||
nsstring = { path = "../../../../xpcom/rust/nsstring" }
|
||||
netwerk_helper = { path = "../../../../netwerk/base/rust-helper" }
|
||||
|
|
|
@ -17,8 +17,6 @@ clippy:
|
|||
- js/src/rust/shared/
|
||||
- js/src/wasm/cranelift/
|
||||
- media/audioipc/
|
||||
- media/mp4parse-rust/mp4parse/
|
||||
- media/mp4parse-rust/mp4parse_capi/
|
||||
- modules/libpref/init/static_prefs/
|
||||
- mozglue/static/rust/
|
||||
- netwerk/base/mozurl/
|
||||
|
@ -39,6 +37,8 @@ clippy:
|
|||
- testing/mozbase/rust/mozrunner/
|
||||
- testing/mozbase/rust/mozversion/
|
||||
- testing/webdriver/
|
||||
- third_party/rust/mp4parse/
|
||||
- third_party/rust/mp4parse_capi/
|
||||
- toolkit/components/kvstore/
|
||||
- toolkit/components/glean/
|
||||
- toolkit/components/xulstore/tests/gtest/
|
||||
|
|
Загрузка…
Ссылка в новой задаче