Skip to content

Commit f1742b7

Browse files
committed
Limit the allocated size of the sample indice table by the file size.
1 parent f908f80 commit f1742b7

File tree

2 files changed

+59
-8
lines changed

2 files changed

+59
-8
lines changed

mp4parse/src/unstable.rs

+51-7
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22
// License, v. 2.0. If a copy of the MPL was not distributed with this
33
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
44
use num_traits::{CheckedAdd, CheckedSub, PrimInt, Zero};
5-
use std::ops::{Add, Neg, Sub};
5+
use std::{
6+
convert::TryInto,
7+
ops::{Add, Neg, Sub},
8+
};
69

710
use super::*;
811

@@ -139,6 +142,7 @@ pub struct Indice {
139142
pub fn create_sample_table(
140143
track: &Track,
141144
track_offset_time: CheckedInteger<i64>,
145+
optional_available_size: Option<usize>,
142146
) -> Option<TryVec<Indice>> {
143147
let timescale = match track.timescale {
144148
Some(ref t) => TrackTimeScale::<i64>(t.0 as i64, t.1),
@@ -153,16 +157,48 @@ pub fn create_sample_table(
153157
// According to spec, no sync table means every sample is sync sample.
154158
let has_sync_table = matches!(track.stss, Some(_));
155159

156-
let mut sample_size_iter = stsz.sample_sizes.iter();
157-
158160
// Get 'stsc' iterator for (chunk_id, chunk_sample_count) and calculate the sample
159161
// offset address.
160162

161163
// With large numbers of samples, the cost of many allocations dominates,
162164
// so it's worth iterating twice to allocate sample_table just once.
163-
let total_sample_count = sample_to_chunk_iter(&stsc.samples, &stco.offsets)
164-
.map(|(_, sample_counts)| sample_counts.to_usize())
165-
.try_fold(0usize, usize::checked_add)?;
165+
let total_sample_count = {
166+
let mut sample_size_iter = stsz.sample_sizes.iter();
167+
let mut sample_number: usize = 0;
168+
for (chunk_id, sample_counts) in sample_to_chunk_iter(&stsc.samples, &stco.offsets) {
169+
match optional_available_size {
170+
Some(available_size) => {
171+
let mut end_offset = match stco.offsets.get(chunk_id as usize) {
172+
Some(v) => *v as usize,
173+
None => return None,
174+
};
175+
for _ in 0..sample_counts {
176+
match (stsz.sample_size, sample_size_iter.next()) {
177+
(_, Some(single_sample_size)) => {
178+
end_offset += *single_sample_size as usize
179+
}
180+
(sample_size, _) if sample_size > 0 => {
181+
end_offset += sample_size as usize
182+
}
183+
_ => return None,
184+
}
185+
if end_offset > available_size {
186+
continue;
187+
}
188+
sample_number = match sample_number.checked_add(1) {
189+
Some(v) => v,
190+
None => return None,
191+
};
192+
}
193+
}
194+
None => sample_number += sample_counts as usize,
195+
}
196+
}
197+
sample_number
198+
};
199+
200+
let mut sample_size_iter = stsz.sample_sizes.iter();
201+
166202
let mut sample_table = TryVec::with_capacity(total_sample_count).ok()?;
167203

168204
for i in sample_to_chunk_iter(&stsc.samples, &stco.offsets) {
@@ -174,11 +210,19 @@ pub fn create_sample_table(
174210
};
175211
for _ in 0..sample_counts {
176212
let start_offset = cur_position;
177-
let end_offset = match (stsz.sample_size, sample_size_iter.next()) {
213+
let end_offset: CheckedInteger<u64> = match (stsz.sample_size, sample_size_iter.next())
214+
{
178215
(_, Some(t)) => (start_offset + *t)?,
179216
(t, _) if t > 0 => (start_offset + t)?,
180217
_ => 0.into(),
181218
};
219+
match optional_available_size
220+
.map(TryInto::try_into)
221+
.and_then(Result::ok)
222+
{
223+
Some(available_size) if end_offset.0 > available_size => continue,
224+
_ => (),
225+
}
182226
if end_offset == 0 {
183227
return None;
184228
}

mp4parse_capi/src/lib.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -1295,6 +1295,7 @@ pub unsafe extern "C" fn mp4parse_get_indice_table(
12951295
&(*parser).context,
12961296
&mut (*parser).sample_table,
12971297
track_id,
1298+
None,
12981299
&mut *indices,
12991300
)
13001301
.into()
@@ -1312,6 +1313,7 @@ pub unsafe extern "C" fn mp4parse_get_indice_table(
13121313
pub unsafe extern "C" fn mp4parse_avif_get_indice_table(
13131314
parser: *mut Mp4parseAvifParser,
13141315
track_id: u32,
1316+
available_size: usize,
13151317
indices: *mut Mp4parseByteData,
13161318
) -> Mp4parseStatus {
13171319
if parser.is_null() {
@@ -1330,6 +1332,10 @@ pub unsafe extern "C" fn mp4parse_avif_get_indice_table(
13301332
sequence,
13311333
&mut (*parser).sample_table,
13321334
track_id,
1335+
match available_size {
1336+
0 => None,
1337+
_ => Some(available_size),
1338+
},
13331339
&mut *indices,
13341340
)
13351341
.into();
@@ -1342,6 +1348,7 @@ fn get_indice_table(
13421348
context: &MediaContext,
13431349
sample_table_cache: &mut TryHashMap<u32, TryVec<Indice>>,
13441350
track_id: u32,
1351+
available_size: Option<usize>,
13451352
indices: &mut Mp4parseByteData,
13461353
) -> Result<(), Mp4parseStatus> {
13471354
let tracks = &context.tracks;
@@ -1380,7 +1387,7 @@ fn get_indice_table(
13801387
_ => 0.into(),
13811388
};
13821389

1383-
if let Some(v) = create_sample_table(track, offset_time) {
1390+
if let Some(v) = create_sample_table(track, offset_time, available_size) {
13841391
indices.set_indices(&v);
13851392
sample_table_cache.insert(track_id, v)?;
13861393
return Ok(());

0 commit comments

Comments
 (0)