зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1817997
- Update mp4parse to 888ce901bb604d9e385. r=kinetik,glandium
Differential Revision: https://phabricator.services.mozilla.com/D176046
This commit is contained in:
Родитель
5852e70c3f
Коммит
ae2988ec95
|
@ -90,9 +90,9 @@ git = "https://github.com/mozilla/midir.git"
|
|||
rev = "519e651241e867af3391db08f9ae6400bc023e18"
|
||||
replace-with = "vendored-sources"
|
||||
|
||||
[source."git+https://github.com/mozilla/mp4parse-rust?rev=2b572e83608a3d0867b935e076f45d9fe248069d"]
|
||||
[source."git+https://github.com/mozilla/mp4parse-rust?rev=1825cee59139d53669a5b8c563e4ea3e440d6a6c"]
|
||||
git = "https://github.com/mozilla/mp4parse-rust"
|
||||
rev = "2b572e83608a3d0867b935e076f45d9fe248069d"
|
||||
rev = "1825cee59139d53669a5b8c563e4ea3e440d6a6c"
|
||||
replace-with = "vendored-sources"
|
||||
|
||||
[source."git+https://github.com/mozilla/neqo?tag=v0.6.4"]
|
||||
|
|
|
@ -3469,8 +3469,8 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "mp4parse"
|
||||
version = "0.16.0"
|
||||
source = "git+https://github.com/mozilla/mp4parse-rust?rev=2b572e83608a3d0867b935e076f45d9fe248069d#2b572e83608a3d0867b935e076f45d9fe248069d"
|
||||
version = "0.17.0"
|
||||
source = "git+https://github.com/mozilla/mp4parse-rust?rev=1825cee59139d53669a5b8c563e4ea3e440d6a6c#1825cee59139d53669a5b8c563e4ea3e440d6a6c"
|
||||
dependencies = [
|
||||
"bitreader",
|
||||
"byteorder",
|
||||
|
@ -3486,8 +3486,8 @@ version = "0.1.0"
|
|||
|
||||
[[package]]
|
||||
name = "mp4parse_capi"
|
||||
version = "0.16.0"
|
||||
source = "git+https://github.com/mozilla/mp4parse-rust?rev=2b572e83608a3d0867b935e076f45d9fe248069d#2b572e83608a3d0867b935e076f45d9fe248069d"
|
||||
version = "0.17.0"
|
||||
source = "git+https://github.com/mozilla/mp4parse-rust?rev=1825cee59139d53669a5b8c563e4ea3e440d6a6c#1825cee59139d53669a5b8c563e4ea3e440d6a6c"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"fallible_collections",
|
||||
|
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -12,7 +12,7 @@
|
|||
[package]
|
||||
edition = "2018"
|
||||
name = "mp4parse"
|
||||
version = "0.16.0"
|
||||
version = "0.17.0"
|
||||
authors = [
|
||||
"Ralph Giles <giles@mozilla.com>",
|
||||
"Matthew Gregan <kinetik@flim.org>",
|
||||
|
|
|
@ -2273,11 +2273,11 @@ impl<'a, T: Read> BoxIter<'a, T> {
|
|||
fn next_box(&mut self) -> Result<Option<BMFFBox<T>>> {
|
||||
let r = read_box_header(self.src);
|
||||
match r {
|
||||
Ok(h) => Ok(Some(BMFFBox {
|
||||
Ok(Some(h)) => Ok(Some(BMFFBox {
|
||||
head: h,
|
||||
content: self.src.take(h.size.saturating_sub(h.offset)),
|
||||
})),
|
||||
Err(Error::UnexpectedEOF) => Ok(None),
|
||||
Ok(None) => Ok(None),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
@ -2332,8 +2332,12 @@ impl<'a, T> Drop for BMFFBox<'a, T> {
|
|||
/// skip unknown or uninteresting boxes.
|
||||
///
|
||||
/// See ISOBMFF (ISO 14496-12:2020) § 4.2
|
||||
fn read_box_header<T: ReadBytesExt>(src: &mut T) -> Result<BoxHeader> {
|
||||
let size32 = be_u32(src)?;
|
||||
fn read_box_header<T: ReadBytesExt>(src: &mut T) -> Result<Option<BoxHeader>> {
|
||||
let size32 = match be_u32(src) {
|
||||
Ok(v) => v,
|
||||
Err(Error::UnexpectedEOF) => return Ok(None),
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
let name = BoxType::from(be_u32(src)?);
|
||||
let size = match size32 {
|
||||
// valid only for top-level box and indicates it's the last box in the file. usually mdat.
|
||||
|
@ -2344,19 +2348,8 @@ fn read_box_header<T: ReadBytesExt>(src: &mut T) -> Result<BoxHeader> {
|
|||
return Err(Error::Unsupported("unknown sized box"));
|
||||
}
|
||||
}
|
||||
1 => {
|
||||
let size64 = be_u64(src)?;
|
||||
if size64 < BoxHeader::MIN_LARGE_SIZE {
|
||||
return Status::BoxBadWideSize.into();
|
||||
}
|
||||
size64
|
||||
}
|
||||
_ => {
|
||||
if u64::from(size32) < BoxHeader::MIN_SIZE {
|
||||
return Status::BoxBadSize.into();
|
||||
}
|
||||
u64::from(size32)
|
||||
}
|
||||
1 => be_u64(src)?,
|
||||
_ => u64::from(size32),
|
||||
};
|
||||
trace!("read_box_header: name: {:?}, size: {}", name, size);
|
||||
let mut offset = match size32 {
|
||||
|
@ -2371,23 +2364,27 @@ fn read_box_header<T: ReadBytesExt>(src: &mut T) -> Result<BoxHeader> {
|
|||
if count == 16 {
|
||||
Some(buffer)
|
||||
} else {
|
||||
debug!("malformed uuid (short read), skipping");
|
||||
None
|
||||
debug!("malformed uuid (short read)");
|
||||
return Err(Error::UnexpectedEOF);
|
||||
}
|
||||
} else {
|
||||
debug!("malformed uuid, skipping");
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
assert!(offset <= size || size == 0);
|
||||
Ok(BoxHeader {
|
||||
match size32 {
|
||||
0 => (),
|
||||
1 if offset > size => return Err(Error::from(Status::BoxBadWideSize)),
|
||||
_ if offset > size => return Err(Error::from(Status::BoxBadSize)),
|
||||
_ => (),
|
||||
}
|
||||
Ok(Some(BoxHeader {
|
||||
name,
|
||||
size,
|
||||
offset,
|
||||
uuid,
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
||||
/// Parse the extra header fields for a full box.
|
||||
|
@ -2521,7 +2518,19 @@ pub fn read_avif<T: Read>(f: &mut T, strictness: ParseStrictness) -> Result<Avif
|
|||
let mut image_sequence = None;
|
||||
let mut media_storage = TryVec::new();
|
||||
|
||||
while let Some(mut b) = iter.next_box()? {
|
||||
loop {
|
||||
let mut b = match iter.next_box() {
|
||||
Ok(Some(b)) => b,
|
||||
Ok(_) => break,
|
||||
Err(Error::UnexpectedEOF) => {
|
||||
if strictness == ParseStrictness::Strict {
|
||||
return Err(Error::UnexpectedEOF);
|
||||
}
|
||||
break;
|
||||
}
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
|
||||
trace!("read_avif parsing {:?} box", b.head.name);
|
||||
match b.head.name {
|
||||
BoxType::MetadataBox => {
|
||||
|
@ -2564,7 +2573,14 @@ pub fn read_avif<T: Read>(f: &mut T, strictness: ParseStrictness) -> Result<Avif
|
|||
};
|
||||
media_storage.push(DataBox::from_mdat(file_offset, data))?;
|
||||
}
|
||||
_ => skip_box_content(&mut b)?,
|
||||
_ => {
|
||||
let result = skip_box_content(&mut b);
|
||||
// Allow garbage at EOF if we aren't in strict mode.
|
||||
if b.bytes_left() > 0 && strictness != ParseStrictness::Strict {
|
||||
break;
|
||||
}
|
||||
result?;
|
||||
}
|
||||
}
|
||||
|
||||
check_parser_state!(b.content);
|
||||
|
|
|
@ -93,7 +93,7 @@ where
|
|||
#[test]
|
||||
fn read_box_header_short() {
|
||||
let mut stream = make_box(BoxSize::Short(8), b"test", |s| s);
|
||||
let header = super::read_box_header(&mut stream).unwrap();
|
||||
let header = super::read_box_header(&mut stream).unwrap().unwrap();
|
||||
assert_eq!(header.name, BoxType::UnknownBox(0x7465_7374)); // "test"
|
||||
assert_eq!(header.size, 8);
|
||||
assert!(header.uuid.is_none());
|
||||
|
@ -102,7 +102,7 @@ fn read_box_header_short() {
|
|||
#[test]
|
||||
fn read_box_header_long() {
|
||||
let mut stream = make_box(BoxSize::Long(16), b"test", |s| s);
|
||||
let header = super::read_box_header(&mut stream).unwrap();
|
||||
let header = super::read_box_header(&mut stream).unwrap().unwrap();
|
||||
assert_eq!(header.name, BoxType::UnknownBox(0x7465_7374)); // "test"
|
||||
assert_eq!(header.size, 16);
|
||||
assert!(header.uuid.is_none());
|
||||
|
@ -166,6 +166,23 @@ fn read_box_header_truncated_uuid() {
|
|||
assert!(stream.head.uuid.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_box_header_uuid_past_eof() {
|
||||
const HEADER_UUID: [u8; 20] = [
|
||||
0x00, 0x00, 0x00, 0x18, // size = 24
|
||||
0x75, 0x75, 0x69, 0x64, // type = uuid
|
||||
0x85, 0xc0, 0xb6, 0x87, 0x82, 0x0f, 0x11, 0xe0, 0x81, 0x11, 0xf4, 0xce,
|
||||
];
|
||||
|
||||
let mut cursor = Cursor::new(HEADER_UUID);
|
||||
let mut iter = super::BoxIter::new(&mut cursor);
|
||||
match iter.next_box() {
|
||||
Err(Error::UnexpectedEOF) => (),
|
||||
Ok(_) => panic!("expected an error result"),
|
||||
_ => panic!("expected a different error result"),
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_ftyp() {
|
||||
let mut stream = make_box(BoxSize::Short(24), b"ftyp", |s| {
|
||||
|
|
|
@ -115,14 +115,14 @@ pub struct Indice {
|
|||
/// sample. Typically this will be the `start_offset` of the next sample
|
||||
/// in the file.
|
||||
pub end_offset: CheckedInteger<u64>,
|
||||
/// The time in microseconds when the indexed sample should be displayed.
|
||||
/// The time in ticks when the indexed sample should be displayed.
|
||||
/// Analogous to the concept of presentation time stamp (pts).
|
||||
pub start_composition: CheckedInteger<i64>,
|
||||
/// The time in microseconds when the indexed sample should stop being
|
||||
/// The time in ticks when the indexed sample should stop being
|
||||
/// displayed. Typically this would be the `start_composition` time of the
|
||||
/// next sample if samples were ordered by composition time.
|
||||
pub end_composition: CheckedInteger<i64>,
|
||||
/// The time in microseconds that the indexed sample should be decoded at.
|
||||
/// The time in ticks that the indexed sample should be decoded at.
|
||||
/// Analogous to the concept of decode time stamp (dts).
|
||||
pub start_decode: CheckedInteger<i64>,
|
||||
/// Set if the indexed sample is a sync sample. The meaning of sync is
|
||||
|
@ -140,11 +140,6 @@ pub fn create_sample_table(
|
|||
track: &Track,
|
||||
track_offset_time: CheckedInteger<i64>,
|
||||
) -> Option<TryVec<Indice>> {
|
||||
let timescale = match track.timescale {
|
||||
Some(ref t) => TrackTimeScale::<i64>(t.0 as i64, t.1),
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
let (stsc, stco, stsz, stts) = match (&track.stsc, &track.stco, &track.stsz, &track.stts) {
|
||||
(Some(a), Some(b), Some(c), Some(d)) => (a, b, c, d),
|
||||
_ => return None,
|
||||
|
@ -238,15 +233,15 @@ pub fn create_sample_table(
|
|||
// ctts_offset is the current sample offset time.
|
||||
let ctts_offset = ctts_offset_iter.next_offset_time();
|
||||
|
||||
let start_composition = track_time_to_us((decode_time + ctts_offset)?, timescale)?.0;
|
||||
let start_composition = decode_time + ctts_offset;
|
||||
|
||||
let end_composition = track_time_to_us((sum_delta + ctts_offset)?, timescale)?.0;
|
||||
let end_composition = sum_delta + ctts_offset;
|
||||
|
||||
let start_decode = track_time_to_us(decode_time, timescale)?.0;
|
||||
let start_decode = decode_time;
|
||||
|
||||
sample.start_composition = (track_offset_time + start_composition)?;
|
||||
sample.end_composition = (track_offset_time + end_composition)?;
|
||||
sample.start_decode = start_decode.into();
|
||||
sample.start_composition = CheckedInteger(track_offset_time.0 + start_composition?.0);
|
||||
sample.end_composition = CheckedInteger(track_offset_time.0 + end_composition?.0);
|
||||
sample.start_decode = CheckedInteger(start_decode.0);
|
||||
}
|
||||
|
||||
// Correct composition end time due to 'ctts' causes composition time re-ordering.
|
||||
|
|
|
@ -68,6 +68,10 @@ static IMAGE_AVIF_CLAP_MISSING_ESSENTIAL: &str = "tests/clap-missing-essential.a
|
|||
static IMAGE_AVIF_UNKNOWN_MDAT_SIZE: &str = "tests/unknown_mdat.avif";
|
||||
static IMAGE_AVIF_UNKNOWN_MDAT_SIZE_IN_OVERSIZED_META: &str =
|
||||
"tests/unknown_mdat_in_oversized_meta.avif";
|
||||
static IMAGE_AVIF_VALID_WITH_GARBAGE_OVERREAD_AT_END: &str =
|
||||
"tests/valid_with_garbage_overread.avif";
|
||||
static IMAGE_AVIF_VALID_WITH_GARBAGE_BYTE_AT_END: &str = "tests/valid_with_garbage_byte.avif";
|
||||
static IMAGE_AVIF_WIDE_BOX_SIZE_0: &str = "tests/wide_box_size_0.avif";
|
||||
static AVIF_TEST_DIRS: &[&str] = &["tests", "av1-avif/testFiles", "link-u-avif-sample-images"];
|
||||
|
||||
// These files are
|
||||
|
@ -127,6 +131,7 @@ static AVIF_UNSUPPORTED_IMAGES: &[&str] = &[
|
|||
// TODO: make this into a map of expected errors?
|
||||
static AV1_AVIF_CORRUPT_IMAGES: &[&str] = &[
|
||||
IMAGE_AVIF_UNKNOWN_MDAT_SIZE_IN_OVERSIZED_META,
|
||||
IMAGE_AVIF_WIDE_BOX_SIZE_0,
|
||||
"av1-avif/testFiles/Link-U/kimono.crop.avif",
|
||||
"av1-avif/testFiles/Link-U/kimono.mirror-horizontal.avif",
|
||||
"av1-avif/testFiles/Link-U/kimono.mirror-vertical.avif",
|
||||
|
@ -1279,6 +1284,28 @@ fn public_avif_avis_with_pitm_no_iloc() {
|
|||
assert_avif_should(AVIF_AVIS_WITH_PITM_NO_ILOC, Status::PitmNotFound);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn public_avif_valid_with_garbage_overread_at_end() {
|
||||
assert_avif_should(
|
||||
IMAGE_AVIF_VALID_WITH_GARBAGE_OVERREAD_AT_END,
|
||||
Status::CheckParserStateErr,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn public_avif_valid_with_garbage_byte_at_end() {
|
||||
assert_avif_should(IMAGE_AVIF_VALID_WITH_GARBAGE_BYTE_AT_END, Status::Eof);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn public_avif_bad_video_sample_entry() {
|
||||
let input = &mut File::open(IMAGE_AVIF_WIDE_BOX_SIZE_0).expect("Unknown file");
|
||||
assert_eq!(
|
||||
Status::from(mp4::read_avif(input, ParseStrictness::Normal)),
|
||||
Status::BoxBadWideSize
|
||||
);
|
||||
}
|
||||
|
||||
fn public_avis_loop_impl(path: &str, looped: bool) {
|
||||
let input = &mut File::open(path).expect("Unknown file");
|
||||
match mp4::read_avif(input, ParseStrictness::Normal) {
|
||||
|
|
Двоичные данные
third_party/rust/mp4parse/tests/valid_with_garbage_byte.avif
поставляемый
Normal file
Двоичные данные
third_party/rust/mp4parse/tests/valid_with_garbage_byte.avif
поставляемый
Normal file
Двоичный файл не отображается.
Двоичные данные
third_party/rust/mp4parse/tests/valid_with_garbage_overread.avif
поставляемый
Normal file
Двоичные данные
third_party/rust/mp4parse/tests/valid_with_garbage_overread.avif
поставляемый
Normal file
Двоичный файл не отображается.
Двоичный файл не отображается.
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"0e0f8d369a370833da96719c0e56439063f136e9f8e3caf520b9db11f86f0ef1","LICENSE":"fab3dd6bdab226f1c08630b1dd917e11fcb4ec5e1e020e2c16f83a0a13863e85","README.md":"f776ed4bbb7b58a5684402a9c5c28dfe1fa02b6b184139b2c2c49384cc1e3723","cbindgen.toml":"62066cd34285ab9e7f1cc5db8950a51e9e080f5a85bd55ad43d7022e4eae2758","examples/dump.rs":"2a3cdebc5ed6f0f6b640e6722cd13fc7f4534774eb057b369a791c2eddb8132d","src/lib.rs":"e3cdb813622bc4c2894e5a72ba70994dd06e6d43a3d43ecdec87b912708967db","tests/loop_1.avif":"bf8777d103e023a0da94f16dbe89b7081d13fe8b50e6418c69381a23561a5a84","tests/loop_2.avif":"be5f53417f8432342c0e0cbace60e33a791f2e98a03a44c4a9f5af3ec0bf6906","tests/loop_ceiled_4.avif":"136c36b7d2f5839c9107e56b6e3ce327d8b97bea291756f0ce377e6011716712","tests/loop_forever.avif":"aa425b4da99646d3b71d63f6b1741b60a2331ee2c9ab3bcf2fbc05b0cc832565","tests/no_edts.avif":"8940e97673d7d67c283a0f8f52fdbc45a9b74d5fb6c430507d406b70e4bbfe5f","tests/test_avis_loop_count.rs":"bcf67821a9ca91a13e5a4abf0fe6f766320f5654ebbd69c1d69cea771ed6159c","tests/test_chunk_out_of_range.rs":"4039d0db0ee5973787e4ca14cea510fd958ae5d21856a79240a5e7b826caa18d","tests/test_encryption.rs":"f62131a36b0516caf9e2c48f8aea060d300b0f5c8a32bc54d31cbc97aa25b4e6","tests/test_fragment.rs":"b87fe0a967c5a3184f9264955b08209e64769e6358ff670665fc719380f22157","tests/test_rotation.rs":"23fa4898eca2e17255bc1ba2f538707a6554fb4644bb75f80548ae56a7cd2d44","tests/test_sample_table.rs":"6e2f5eaadede1e37a5ced722bc071506fd0eecf3cd17310479d3c9692632a2cd","tests/test_workaround_stsc.rs":"1d17a394f55e1524c30888bfe1e57e2b0457444b79c23eb91b02d2edf859c9ad"},"package":null}
|
||||
{"files":{"Cargo.toml":"6b2b4576bf1bca3cf6b019f297a84189a3dd595353c451a4af38246ec14bcbef","LICENSE":"fab3dd6bdab226f1c08630b1dd917e11fcb4ec5e1e020e2c16f83a0a13863e85","README.md":"f776ed4bbb7b58a5684402a9c5c28dfe1fa02b6b184139b2c2c49384cc1e3723","cbindgen.toml":"62066cd34285ab9e7f1cc5db8950a51e9e080f5a85bd55ad43d7022e4eae2758","examples/dump.rs":"2a3cdebc5ed6f0f6b640e6722cd13fc7f4534774eb057b369a791c2eddb8132d","src/lib.rs":"554b96bb3a9a5d0c311004d040404f8da17a74e04ab48b044930a49909d0343a","tests/loop_1.avif":"bf8777d103e023a0da94f16dbe89b7081d13fe8b50e6418c69381a23561a5a84","tests/loop_2.avif":"be5f53417f8432342c0e0cbace60e33a791f2e98a03a44c4a9f5af3ec0bf6906","tests/loop_ceiled_4.avif":"136c36b7d2f5839c9107e56b6e3ce327d8b97bea291756f0ce377e6011716712","tests/loop_forever.avif":"aa425b4da99646d3b71d63f6b1741b60a2331ee2c9ab3bcf2fbc05b0cc832565","tests/no_edts.avif":"8940e97673d7d67c283a0f8f52fdbc45a9b74d5fb6c430507d406b70e4bbfe5f","tests/test_avis.rs":"d480b104ab2dfde7a25afd6705532caf7988aea21fc955dcf2f86fc8a5e85151","tests/test_chunk_out_of_range.rs":"4039d0db0ee5973787e4ca14cea510fd958ae5d21856a79240a5e7b826caa18d","tests/test_encryption.rs":"f62131a36b0516caf9e2c48f8aea060d300b0f5c8a32bc54d31cbc97aa25b4e6","tests/test_fragment.rs":"6614df84bf0184acc33eccf86a8cd62e8192f8d93ab4593f5e54dd6b2a6d5724","tests/test_rotation.rs":"23fa4898eca2e17255bc1ba2f538707a6554fb4644bb75f80548ae56a7cd2d44","tests/test_sample_table.rs":"53ed6a5e8db463ad8dc0300116f470c2aadd39896e6ba4cabcd01c6b9a7b5c59","tests/test_workaround_stsc.rs":"1d17a394f55e1524c30888bfe1e57e2b0457444b79c23eb91b02d2edf859c9ad"},"package":null}
|
|
@ -12,7 +12,7 @@
|
|||
[package]
|
||||
edition = "2018"
|
||||
name = "mp4parse_capi"
|
||||
version = "0.16.0"
|
||||
version = "0.17.0"
|
||||
authors = [
|
||||
"Ralph Giles <giles@mozilla.com>",
|
||||
"Matthew Gregan <kinetik@flim.org>",
|
||||
|
@ -37,7 +37,7 @@ version = "0.4"
|
|||
features = ["std_io"]
|
||||
|
||||
[dependencies.mp4parse]
|
||||
version = "0.16.0"
|
||||
version = "0.17.0"
|
||||
path = "../mp4parse"
|
||||
features = ["unstable-api"]
|
||||
|
||||
|
|
|
@ -42,9 +42,7 @@ use std::io::Read;
|
|||
|
||||
// Symbols we need from our rust api.
|
||||
use mp4parse::serialize_opus_header;
|
||||
use mp4parse::unstable::{
|
||||
create_sample_table, media_time_to_us, track_time_to_us, CheckedInteger, Indice, Microseconds,
|
||||
};
|
||||
use mp4parse::unstable::{create_sample_table, CheckedInteger, Indice};
|
||||
use mp4parse::AV1ConfigBox;
|
||||
use mp4parse::AudioCodecSpecific;
|
||||
use mp4parse::AvifContext;
|
||||
|
@ -127,10 +125,8 @@ pub struct Mp4parseTrackInfo {
|
|||
pub track_type: Mp4parseTrackType,
|
||||
pub track_id: u32,
|
||||
pub duration: u64,
|
||||
pub media_time: CheckedInteger<i64>, // wants to be u64? understand how elst adjustment works
|
||||
// TODO(kinetik): include crypto guff
|
||||
// If this changes to u64, we can get rid of the strange
|
||||
// impl Sub for CheckedInteger<u64>
|
||||
pub media_time: CheckedInteger<i64>,
|
||||
pub time_scale: u32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
|
@ -278,9 +274,9 @@ impl Default for Mp4parseTrackVideoInfo {
|
|||
#[repr(C)]
|
||||
#[derive(Default, Debug)]
|
||||
pub struct Mp4parseFragmentInfo {
|
||||
pub fragment_duration: u64,
|
||||
// TODO:
|
||||
// info in trex box.
|
||||
pub fragment_duration: u64, // in ticks
|
||||
// TODO:
|
||||
// info in trex box.
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
|
@ -631,46 +627,34 @@ pub unsafe extern "C" fn mp4parse_get_track_info(
|
|||
|
||||
let track = &context.tracks[track_index];
|
||||
|
||||
if let (Some(track_timescale), Some(context_timescale)) = (track.timescale, context.timescale) {
|
||||
let media_time: CheckedInteger<_> = match track
|
||||
if let (Some(timescale), Some(_)) = (track.timescale, context.timescale) {
|
||||
info.time_scale = timescale.0 as u32;
|
||||
let media_time: CheckedInteger<u64> = track
|
||||
.media_time
|
||||
.map_or(Some(Microseconds(0)), |media_time| {
|
||||
track_time_to_us(media_time, track_timescale)
|
||||
}) {
|
||||
Some(time) => time.0.into(),
|
||||
None => return Mp4parseStatus::Invalid,
|
||||
};
|
||||
let empty_duration: CheckedInteger<_> = match track
|
||||
.map_or(0.into(), |media_time| media_time.0.into());
|
||||
|
||||
let empty_duration: CheckedInteger<u64> = track
|
||||
.empty_duration
|
||||
.map_or(Some(Microseconds(0)), |empty_duration| {
|
||||
media_time_to_us(empty_duration, context_timescale)
|
||||
}) {
|
||||
Some(time) => time.0.into(),
|
||||
None => return Mp4parseStatus::Invalid,
|
||||
};
|
||||
.map_or(0.into(), |empty_duration| empty_duration.0.into());
|
||||
|
||||
info.media_time = match media_time - empty_duration {
|
||||
Some(difference) => difference,
|
||||
None => return Mp4parseStatus::Invalid,
|
||||
};
|
||||
|
||||
if let Some(track_duration) = track.duration {
|
||||
match track_time_to_us(track_duration, track_timescale) {
|
||||
Some(duration) => info.duration = duration.0,
|
||||
None => return Mp4parseStatus::Invalid,
|
||||
match track.duration {
|
||||
Some(duration) => info.duration = duration.0,
|
||||
None => {
|
||||
// Duration unknown; stagefright returns 0 for this.
|
||||
info.duration = 0
|
||||
}
|
||||
} else {
|
||||
// Duration unknown; stagefright returns 0 for this.
|
||||
info.duration = 0
|
||||
}
|
||||
} else {
|
||||
return Mp4parseStatus::Invalid;
|
||||
}
|
||||
};
|
||||
|
||||
info.track_id = match track.track_id {
|
||||
Some(track_id) => track_id,
|
||||
None => return Mp4parseStatus::Invalid,
|
||||
};
|
||||
|
||||
Mp4parseStatus::Ok
|
||||
}
|
||||
|
||||
|
@ -1313,6 +1297,7 @@ pub unsafe extern "C" fn mp4parse_avif_get_indice_table(
|
|||
parser: *mut Mp4parseAvifParser,
|
||||
track_id: u32,
|
||||
indices: *mut Mp4parseByteData,
|
||||
timescale: *mut u64,
|
||||
) -> Mp4parseStatus {
|
||||
if parser.is_null() {
|
||||
return Mp4parseStatus::BadArg;
|
||||
|
@ -1326,6 +1311,27 @@ pub unsafe extern "C" fn mp4parse_avif_get_indice_table(
|
|||
*indices = Default::default();
|
||||
|
||||
if let Some(sequence) = &(*parser).context.sequence {
|
||||
// Use the top level timescale, and the track timescale if present.
|
||||
let mut found_timescale = false;
|
||||
if let Some(context_timescale) = sequence.timescale {
|
||||
*timescale = context_timescale.0;
|
||||
found_timescale = true;
|
||||
}
|
||||
let maybe_track_timescale = match sequence
|
||||
.tracks
|
||||
.iter()
|
||||
.find(|track| track.track_id == Some(track_id))
|
||||
{
|
||||
Some(track) => track.timescale,
|
||||
_ => None,
|
||||
};
|
||||
if let Some(track_timescale) = maybe_track_timescale {
|
||||
found_timescale = true;
|
||||
*timescale = track_timescale.0;
|
||||
}
|
||||
if !found_timescale {
|
||||
return Mp4parseStatus::Invalid;
|
||||
}
|
||||
return get_indice_table(
|
||||
sequence,
|
||||
&mut (*parser).sample_table,
|
||||
|
@ -1355,20 +1361,15 @@ fn get_indice_table(
|
|||
return Ok(());
|
||||
}
|
||||
|
||||
let media_time = match (&track.media_time, &track.timescale) {
|
||||
(&Some(t), &Some(s)) => track_time_to_us(t, s)
|
||||
.and_then(|v| i64::try_from(v.0).ok())
|
||||
.map(Into::into),
|
||||
let media_time = match &track.media_time {
|
||||
&Some(t) => i64::try_from(t.0).ok().map(Into::into),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let empty_duration: Option<CheckedInteger<_>> =
|
||||
match (&track.empty_duration, &context.timescale) {
|
||||
(&Some(e), &Some(s)) => media_time_to_us(e, s)
|
||||
.and_then(|v| i64::try_from(v.0).ok())
|
||||
.map(Into::into),
|
||||
_ => None,
|
||||
};
|
||||
let empty_duration: Option<CheckedInteger<_>> = match &track.empty_duration {
|
||||
&Some(e) => i64::try_from(e.0).ok().map(Into::into),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
// Find the track start offset time from 'elst'.
|
||||
// 'media_time' maps start time onward, 'empty_duration' adds time offset
|
||||
|
@ -1420,12 +1421,12 @@ pub unsafe extern "C" fn mp4parse_get_fragment_info(
|
|||
None => return Mp4parseStatus::Invalid,
|
||||
};
|
||||
|
||||
if let (Some(time), Some(scale)) = (duration, context.timescale) {
|
||||
info.fragment_duration = match media_time_to_us(time, scale) {
|
||||
Some(time_us) => time_us.0,
|
||||
None => return Mp4parseStatus::Invalid,
|
||||
match duration {
|
||||
Some(duration_ticks) => {
|
||||
info.fragment_duration = duration_ticks.0;
|
||||
}
|
||||
}
|
||||
None => return Mp4parseStatus::Invalid,
|
||||
};
|
||||
|
||||
Mp4parseStatus::Ok
|
||||
}
|
||||
|
@ -1741,7 +1742,7 @@ fn minimal_mp4_get_track_info() {
|
|||
});
|
||||
assert_eq!(info.track_type, Mp4parseTrackType::Video);
|
||||
assert_eq!(info.track_id, 1);
|
||||
assert_eq!(info.duration, 40000);
|
||||
assert_eq!(info.duration, 512);
|
||||
assert_eq!(info.media_time, 0);
|
||||
|
||||
assert_eq!(Mp4parseStatus::Ok, unsafe {
|
||||
|
@ -1749,8 +1750,8 @@ fn minimal_mp4_get_track_info() {
|
|||
});
|
||||
assert_eq!(info.track_type, Mp4parseTrackType::Audio);
|
||||
assert_eq!(info.track_id, 2);
|
||||
assert_eq!(info.duration, 61333);
|
||||
assert_eq!(info.media_time, 21333);
|
||||
assert_eq!(info.duration, 2944);
|
||||
assert_eq!(info.media_time, 1024);
|
||||
|
||||
unsafe {
|
||||
mp4parse_free(parser);
|
||||
|
|
|
@ -0,0 +1,113 @@
|
|||
use mp4parse_capi::*;
|
||||
use num_traits::ToPrimitive;
|
||||
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 buf = unsafe { std::slice::from_raw_parts_mut(buf, size) };
|
||||
match input.read(buf) {
|
||||
Ok(n) => n as isize,
|
||||
Err(_) => -1,
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn parse_file_and_get_info(path: &str) -> (*mut Mp4parseAvifParser, Mp4parseAvifInfo) {
|
||||
let mut file = std::fs::File::open(path).expect("Unknown file");
|
||||
let io = Mp4parseIo {
|
||||
read: Some(buf_read),
|
||||
userdata: &mut file as *mut _ as *mut std::os::raw::c_void,
|
||||
};
|
||||
|
||||
let mut parser = std::ptr::null_mut();
|
||||
let mut rv = mp4parse_avif_new(&io, ParseStrictness::Normal, &mut parser);
|
||||
assert_eq!(rv, Mp4parseStatus::Ok);
|
||||
assert!(!parser.is_null());
|
||||
|
||||
let mut info = Mp4parseAvifInfo {
|
||||
premultiplied_alpha: Default::default(),
|
||||
major_brand: Default::default(),
|
||||
unsupported_features_bitfield: Default::default(),
|
||||
spatial_extents: std::ptr::null(),
|
||||
nclx_colour_information: std::ptr::null(),
|
||||
icc_colour_information: Default::default(),
|
||||
image_rotation: mp4parse::ImageRotation::D0,
|
||||
image_mirror: std::ptr::null(),
|
||||
pixel_aspect_ratio: std::ptr::null(),
|
||||
has_primary_item: Default::default(),
|
||||
primary_item_bit_depth: Default::default(),
|
||||
has_alpha_item: Default::default(),
|
||||
alpha_item_bit_depth: Default::default(),
|
||||
has_sequence: Default::default(),
|
||||
loop_mode: Default::default(),
|
||||
loop_count: Default::default(),
|
||||
color_track_id: Default::default(),
|
||||
color_track_bit_depth: Default::default(),
|
||||
alpha_track_id: Default::default(),
|
||||
alpha_track_bit_depth: Default::default(),
|
||||
};
|
||||
rv = mp4parse_avif_get_info(parser, &mut info);
|
||||
assert_eq!(rv, Mp4parseStatus::Ok);
|
||||
(parser, info)
|
||||
}
|
||||
|
||||
fn check_loop_count(path: &str, expected_loop_count: i64) {
|
||||
let (parser, info) = unsafe { parse_file_and_get_info(path) };
|
||||
match info.loop_mode {
|
||||
Mp4parseAvifLoopMode::NoEdits => assert_eq!(expected_loop_count, -1),
|
||||
Mp4parseAvifLoopMode::LoopByCount => {
|
||||
assert_eq!(info.loop_count.to_i64(), Some(expected_loop_count))
|
||||
}
|
||||
Mp4parseAvifLoopMode::LoopInfinitely => assert_eq!(expected_loop_count, std::i64::MIN),
|
||||
}
|
||||
|
||||
unsafe { mp4parse_avif_free(parser) };
|
||||
}
|
||||
|
||||
fn check_timescale(path: &str, expected_timescale: u64) {
|
||||
let (parser, info) = unsafe { parse_file_and_get_info(path) };
|
||||
|
||||
let mut indices: Mp4parseByteData = Mp4parseByteData::default();
|
||||
let mut timescale: u64 = 0;
|
||||
let rv = unsafe {
|
||||
mp4parse_avif_get_indice_table(parser, info.color_track_id, &mut indices, &mut timescale)
|
||||
};
|
||||
|
||||
assert_eq!(rv, Mp4parseStatus::Ok);
|
||||
assert_eq!(timescale, expected_timescale);
|
||||
|
||||
unsafe { mp4parse_avif_free(parser) };
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn loop_once() {
|
||||
check_loop_count("tests/loop_1.avif", 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn loop_twice() {
|
||||
check_loop_count("tests/loop_2.avif", 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn loop_four_times_due_to_ceiling() {
|
||||
check_loop_count("tests/loop_ceiled_4.avif", 4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn loop_forever() {
|
||||
check_loop_count("tests/loop_forever.avif", std::i64::MIN);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_edts() {
|
||||
check_loop_count("tests/no_edts.avif", -1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_timescales() {
|
||||
check_timescale("tests/loop_1.avif", 2);
|
||||
check_timescale("tests/loop_2.avif", 2);
|
||||
check_timescale("tests/loop_ceiled_4.avif", 2);
|
||||
check_timescale("tests/loop_forever.avif", 2);
|
||||
check_timescale("tests/no_edts.avif", 16384);
|
||||
}
|
|
@ -1,87 +0,0 @@
|
|||
use mp4parse_capi::*;
|
||||
use num_traits::ToPrimitive;
|
||||
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 buf = unsafe { std::slice::from_raw_parts_mut(buf, size) };
|
||||
match input.read(buf) {
|
||||
Ok(n) => n as isize,
|
||||
Err(_) => -1,
|
||||
}
|
||||
}
|
||||
|
||||
fn check_loop_count(path: &str, expected_loop_count: i64) {
|
||||
let mut file = std::fs::File::open(path).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_avif_new(&io, ParseStrictness::Normal, &mut parser);
|
||||
assert_eq!(rv, Mp4parseStatus::Ok);
|
||||
assert!(!parser.is_null());
|
||||
|
||||
let mut info = Mp4parseAvifInfo {
|
||||
premultiplied_alpha: Default::default(),
|
||||
major_brand: Default::default(),
|
||||
unsupported_features_bitfield: Default::default(),
|
||||
spatial_extents: std::ptr::null(),
|
||||
nclx_colour_information: std::ptr::null(),
|
||||
icc_colour_information: Default::default(),
|
||||
image_rotation: mp4parse::ImageRotation::D0,
|
||||
image_mirror: std::ptr::null(),
|
||||
pixel_aspect_ratio: std::ptr::null(),
|
||||
has_primary_item: Default::default(),
|
||||
primary_item_bit_depth: Default::default(),
|
||||
has_alpha_item: Default::default(),
|
||||
alpha_item_bit_depth: Default::default(),
|
||||
has_sequence: Default::default(),
|
||||
loop_mode: Default::default(),
|
||||
loop_count: Default::default(),
|
||||
color_track_id: Default::default(),
|
||||
color_track_bit_depth: Default::default(),
|
||||
alpha_track_id: Default::default(),
|
||||
alpha_track_bit_depth: Default::default(),
|
||||
};
|
||||
rv = mp4parse_avif_get_info(parser, &mut info);
|
||||
assert_eq!(rv, Mp4parseStatus::Ok);
|
||||
|
||||
match info.loop_mode {
|
||||
Mp4parseAvifLoopMode::NoEdits => assert_eq!(expected_loop_count, -1),
|
||||
Mp4parseAvifLoopMode::LoopByCount => {
|
||||
assert_eq!(info.loop_count.to_i64(), Some(expected_loop_count))
|
||||
}
|
||||
Mp4parseAvifLoopMode::LoopInfinitely => assert_eq!(expected_loop_count, std::i64::MIN),
|
||||
}
|
||||
|
||||
mp4parse_avif_free(parser);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn loop_once() {
|
||||
check_loop_count("tests/loop_1.avif", 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn loop_twice() {
|
||||
check_loop_count("tests/loop_2.avif", 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn loop_four_times_due_to_ceiling() {
|
||||
check_loop_count("tests/loop_ceiled_4.avif", 4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn loop_forever() {
|
||||
check_loop_count("tests/loop_forever.avif", std::i64::MIN);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_edts() {
|
||||
check_loop_count("tests/no_edts.avif", -1);
|
||||
}
|
|
@ -36,6 +36,7 @@ fn parse_fragment() {
|
|||
assert_eq!(track_info.track_id, 1);
|
||||
assert_eq!(track_info.duration, 0);
|
||||
assert_eq!(track_info.media_time, 0);
|
||||
assert_eq!(track_info.time_scale, 22050);
|
||||
|
||||
let mut audio = Default::default();
|
||||
rv = mp4parse_get_track_audio_info(parser, 0, &mut audio);
|
||||
|
@ -57,7 +58,7 @@ fn parse_fragment() {
|
|||
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);
|
||||
assert_eq!(fragment_info.fragment_duration, 10_032);
|
||||
|
||||
mp4parse_free(parser);
|
||||
}
|
||||
|
@ -89,6 +90,7 @@ fn parse_opus_fragment() {
|
|||
assert_eq!(track_info.track_id, 1);
|
||||
assert_eq!(track_info.duration, 0);
|
||||
assert_eq!(track_info.media_time, 0);
|
||||
assert_eq!(track_info.time_scale, 48000);
|
||||
|
||||
let mut audio = Default::default();
|
||||
rv = mp4parse_get_track_audio_info(parser, 0, &mut audio);
|
||||
|
|
|
@ -51,16 +51,16 @@ fn parse_sample_table() {
|
|||
start_offset: 27_046.into(),
|
||||
end_offset: 27_052.into(),
|
||||
start_composition: 0.into(),
|
||||
end_composition: 46_439.into(),
|
||||
end_composition: 1024.into(),
|
||||
start_decode: 0.into(),
|
||||
sync: true,
|
||||
};
|
||||
let audio_indice_215 = Indice {
|
||||
start_offset: 283_550.into(),
|
||||
end_offset: 283_556.into(),
|
||||
start_composition: 9_984_580.into(),
|
||||
end_composition: 10_031_020.into(),
|
||||
start_decode: 9_984_580.into(),
|
||||
start_composition: 220160.into(),
|
||||
end_composition: 221184.into(),
|
||||
start_decode: 220160.into(),
|
||||
sync: true,
|
||||
};
|
||||
assert_eq!(indice.length, 216);
|
||||
|
@ -85,17 +85,17 @@ fn parse_sample_table() {
|
|||
let video_indice_291 = Indice {
|
||||
start_offset: 280_226.into(),
|
||||
end_offset: 280_855.into(),
|
||||
start_composition: 9_838_333.into(),
|
||||
end_composition: 9_871_677.into(),
|
||||
start_decode: 9_710_000.into(),
|
||||
start_composition: 876995.into(),
|
||||
end_composition: 879996.into(),
|
||||
start_decode: 873900.into(),
|
||||
sync: false,
|
||||
};
|
||||
let video_indice_292 = Indice {
|
||||
start_offset: 280_855.into(),
|
||||
end_offset: 281_297.into(),
|
||||
start_composition: 9_805_011.into(),
|
||||
end_composition: 9_838_333.into(),
|
||||
start_decode: 9_710_011.into(),
|
||||
start_composition: 873996.into(),
|
||||
end_composition: 876995.into(),
|
||||
start_decode: 873901.into(),
|
||||
sync: false,
|
||||
};
|
||||
// TODO: start_composition time in stagefright is 9905000, but it is 9904999 in parser, it
|
||||
|
@ -105,17 +105,17 @@ fn parse_sample_table() {
|
|||
let video_indice_295 = Indice {
|
||||
start_offset: 282_391.into(),
|
||||
end_offset: 283_032.into(),
|
||||
start_composition: 9_971_666.into(),
|
||||
end_composition: 9_971_677.into(),
|
||||
start_decode: 9_843_333.into(),
|
||||
start_composition: 888995.into(),
|
||||
end_composition: 888996.into(),
|
||||
start_decode: 885900.into(),
|
||||
sync: false,
|
||||
};
|
||||
let video_indice_296 = Indice {
|
||||
start_offset: 283_092.into(),
|
||||
end_offset: 283_526.into(),
|
||||
start_composition: 9_938_344.into(),
|
||||
end_composition: 9_971_666.into(),
|
||||
start_decode: 9_843_344.into(),
|
||||
start_composition: 885996.into(),
|
||||
end_composition: 888995.into(),
|
||||
start_decode: 885901.into(),
|
||||
sync: false,
|
||||
};
|
||||
|
||||
|
@ -171,25 +171,25 @@ fn parse_sample_table_with_elst() {
|
|||
let audio_indice_0 = Indice {
|
||||
start_offset: 6992.into(),
|
||||
end_offset: 7363.into(),
|
||||
start_composition: (-36281).into(),
|
||||
end_composition: (-13062).into(),
|
||||
start_composition: (-1600).into(),
|
||||
end_composition: (-576).into(),
|
||||
start_decode: 0.into(),
|
||||
sync: true,
|
||||
};
|
||||
let audio_indice_1 = Indice {
|
||||
start_offset: 7363.into(),
|
||||
end_offset: 7735.into(),
|
||||
start_composition: (-13062).into(),
|
||||
end_composition: 10158.into(),
|
||||
start_decode: 23219.into(),
|
||||
start_composition: (-576).into(),
|
||||
end_composition: 448.into(),
|
||||
start_decode: 1024.into(),
|
||||
sync: true,
|
||||
};
|
||||
let audio_indice_2 = Indice {
|
||||
start_offset: 7735.into(),
|
||||
end_offset: 8106.into(),
|
||||
start_composition: 10158.into(),
|
||||
end_composition: 33378.into(),
|
||||
start_decode: 46439.into(),
|
||||
start_composition: 448.into(),
|
||||
end_composition: 1472.into(),
|
||||
start_decode: 2048.into(),
|
||||
sync: true,
|
||||
};
|
||||
assert_eq!(indice.length, 21);
|
||||
|
@ -239,32 +239,32 @@ fn parse_sample_table_with_negative_ctts() {
|
|||
start_offset: 48.into(),
|
||||
end_offset: 890.into(),
|
||||
start_composition: 0.into(),
|
||||
end_composition: 33_333.into(),
|
||||
end_composition: 100.into(),
|
||||
start_decode: 0.into(),
|
||||
sync: true,
|
||||
};
|
||||
let video_indice_1 = Indice {
|
||||
start_offset: 890.into(),
|
||||
end_offset: 913.into(),
|
||||
start_composition: 133_333.into(),
|
||||
end_composition: 166_666.into(),
|
||||
start_decode: 33_333.into(),
|
||||
start_composition: 400.into(),
|
||||
end_composition: 500.into(),
|
||||
start_decode: 100.into(),
|
||||
sync: false,
|
||||
};
|
||||
let video_indice_2 = Indice {
|
||||
start_offset: 913.into(),
|
||||
end_offset: 934.into(),
|
||||
start_composition: 66_666.into(),
|
||||
end_composition: 100_000.into(),
|
||||
start_decode: 66_666.into(),
|
||||
start_composition: 200.into(),
|
||||
end_composition: 300.into(),
|
||||
start_decode: 200.into(),
|
||||
sync: false,
|
||||
};
|
||||
let video_indice_3 = Indice {
|
||||
start_offset: 934.into(),
|
||||
end_offset: 955.into(),
|
||||
start_composition: 33_333.into(),
|
||||
end_composition: 66_666.into(),
|
||||
start_decode: 100_000.into(),
|
||||
start_composition: 100.into(),
|
||||
end_composition: 200.into(),
|
||||
start_decode: 300.into(),
|
||||
sync: false,
|
||||
};
|
||||
assert_eq!(indice.length, 300);
|
||||
|
|
|
@ -12,7 +12,7 @@ mozglue-static = { path = "../../../../mozglue/static/rust" }
|
|||
geckoservo = { path = "../../../../servo/ports/geckolib" }
|
||||
kvstore = { path = "../../../components/kvstore" }
|
||||
lmdb-rkv-sys = { version = "0.11", features = ["mdb_idl_logn_9"] }
|
||||
mp4parse_capi = { git = "https://github.com/mozilla/mp4parse-rust", rev = "2b572e83608a3d0867b935e076f45d9fe248069d", features = ["missing-pixi-permitted"] }
|
||||
mp4parse_capi = { git = "https://github.com/mozilla/mp4parse-rust", rev = "1825cee59139d53669a5b8c563e4ea3e440d6a6c", features = ["missing-pixi-permitted"] }
|
||||
nserror = { path = "../../../../xpcom/rust/nserror" }
|
||||
nsstring = { path = "../../../../xpcom/rust/nsstring" }
|
||||
netwerk_helper = { path = "../../../../netwerk/base/rust-helper" }
|
||||
|
|
Загрузка…
Ссылка в новой задаче