Skip to content

Commit 9b35d1f

Browse files
authored
Merge pull request #548 from lideen/fix/invalid-fragmentation-state-on-seek
fix: reset fragmentation state on seek to prevent invalid fragment ranges
2 parents 2af7e20 + fe69a56 commit 9b35d1f

3 files changed

Lines changed: 121 additions & 0 deletions

File tree

.changeset/eager-socks-leave.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"mp4box": patch
3+
---
4+
5+
fix: reset fragmentation state on seek to prevent invalid fragment ranges

src/isofile.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1073,6 +1073,8 @@ export class ISOFile<TSegmentUser = unknown, TSampleUser = unknown> {
10731073
}
10741074
time = trak.samples[seek_sample_num].cts;
10751075
trak.nextSample = seek_sample_num;
1076+
this.resetFragmentedTrackStateAfterSeek(trak, seek_sample_num);
1077+
this.resetExtractedTrackStateAfterSeek(trak);
10761078
while (trak.samples[seek_sample_num].alreadyRead === trak.samples[seek_sample_num].size) {
10771079
// No remaining samples to look for, all are downloaded.
10781080
if (!trak.samples[seek_sample_num + 1]) {
@@ -1098,6 +1100,27 @@ export class ISOFile<TSegmentUser = unknown, TSampleUser = unknown> {
10981100
return { offset: seek_offset, time: time / timescale };
10991101
}
11001102

1103+
resetFragmentedTrackStateAfterSeek(trak: trakBox, seekSampleNumber: number) {
1104+
const fragTrack = this.fragmentedTracks.find(t => t.trak === trak);
1105+
if (!fragTrack) {
1106+
return;
1107+
}
1108+
1109+
fragTrack.state.lastFragmentSampleNumber = seekSampleNumber;
1110+
fragTrack.state.lastSegmentSampleNumber = seekSampleNumber;
1111+
fragTrack.state.accumulatedSize = 0;
1112+
fragTrack.segmentStream = undefined;
1113+
}
1114+
1115+
resetExtractedTrackStateAfterSeek(trak: trakBox) {
1116+
const extractTrack = this.extractedTracks.find(t => t.trak === trak);
1117+
if (!extractTrack) {
1118+
return;
1119+
}
1120+
1121+
extractTrack.samples = [];
1122+
}
1123+
11011124
getTrackDuration(trak: trakBox) {
11021125
if (!trak.samples) {
11031126
return Infinity;
@@ -1182,6 +1205,14 @@ export class ISOFile<TSegmentUser = unknown, TSampleUser = unknown> {
11821205
sampleEnd: number,
11831206
existingStream: DataStream,
11841207
) {
1208+
if (sampleEnd < sampleStart) {
1209+
Log.warn(
1210+
'ISOFile',
1211+
`Skipping fragment creation on track #${track_id}: invalid sample range [${sampleStart}, ${sampleEnd}]`,
1212+
);
1213+
return existingStream || new DataStream();
1214+
}
1215+
11851216
// Check existence of all samples
11861217
const samples: Array<Sample> = [];
11871218
for (let i = sampleStart; i <= sampleEnd; i++) {

tests/segmentation.test.ts

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,4 +206,89 @@ describe('File Segmentation', () => {
206206
expect(newMP4.getBoxes('moof', false).length).toBe(10);
207207
expect(out.getAbsoluteEndPosition()).toBe(203_175);
208208
});
209+
210+
it('resets segmentation state on seek', async () => {
211+
const { testFile } = getFilePath('isobmff', '01_simple.mp4');
212+
const { mp4 } = await loadAndGetInfo(testFile, true, true);
213+
214+
mp4.setSegmentOptions(201, undefined, {
215+
nbSamples: 100,
216+
nbSamplesPerFragment: 50,
217+
rapAlignement: false,
218+
});
219+
mp4.initializeSegmentation();
220+
mp4.onSegment = () => undefined;
221+
222+
mp4.start();
223+
224+
const fragTrack = mp4.fragmentedTracks.find(track => track.id === 201);
225+
expect(fragTrack).toBeDefined();
226+
if (!fragTrack) {
227+
throw new Error('Missing fragmented track #201');
228+
}
229+
230+
mp4.seek(0, false);
231+
232+
expect(fragTrack.state.lastFragmentSampleNumber).toBe(fragTrack.trak.nextSample);
233+
expect(fragTrack.state.lastSegmentSampleNumber).toBe(fragTrack.trak.nextSample);
234+
expect(fragTrack.state.accumulatedSize).toBe(0);
235+
expect(fragTrack.segmentStream).toBeUndefined();
236+
expect(() => mp4.start()).not.toThrow();
237+
});
238+
239+
it('resets segmentation state on forward seek before processing', async () => {
240+
const { testFile } = getFilePath('isobmff', '01_simple.mp4');
241+
const { mp4 } = await loadAndGetInfo(testFile, true, true);
242+
243+
mp4.setSegmentOptions(201, undefined, {
244+
nbSamples: 100,
245+
nbSamplesPerFragment: 50,
246+
rapAlignement: false,
247+
});
248+
mp4.initializeSegmentation();
249+
250+
const fragTrack = mp4.fragmentedTracks.find(track => track.id === 201);
251+
expect(fragTrack).toBeDefined();
252+
if (!fragTrack) {
253+
throw new Error('Missing fragmented track #201');
254+
}
255+
256+
mp4.seek(3, false);
257+
258+
expect(fragTrack.trak.nextSample).toBeGreaterThan(0);
259+
expect(fragTrack.state.lastFragmentSampleNumber).toBe(fragTrack.trak.nextSample);
260+
expect(fragTrack.state.lastSegmentSampleNumber).toBe(fragTrack.trak.nextSample);
261+
expect(fragTrack.state.accumulatedSize).toBe(0);
262+
expect(fragTrack.segmentStream).toBeUndefined();
263+
});
264+
265+
it('resets segmentation state on backward seek after a forward seek', async () => {
266+
const { testFile } = getFilePath('isobmff', '01_simple.mp4');
267+
const { mp4 } = await loadAndGetInfo(testFile, true, true);
268+
269+
mp4.setSegmentOptions(201, undefined, {
270+
nbSamples: 100,
271+
nbSamplesPerFragment: 50,
272+
rapAlignement: false,
273+
});
274+
mp4.initializeSegmentation();
275+
276+
const fragTrack = mp4.fragmentedTracks.find(track => track.id === 201);
277+
expect(fragTrack).toBeDefined();
278+
if (!fragTrack) {
279+
throw new Error('Missing fragmented track #201');
280+
}
281+
282+
mp4.seek(3, false);
283+
const forwardSample = fragTrack.trak.nextSample;
284+
expect(forwardSample).toBeGreaterThan(0);
285+
286+
mp4.seek(0, false);
287+
288+
expect(fragTrack.trak.nextSample).toBeLessThan(forwardSample);
289+
expect(fragTrack.state.lastFragmentSampleNumber).toBe(fragTrack.trak.nextSample);
290+
expect(fragTrack.state.lastSegmentSampleNumber).toBe(fragTrack.trak.nextSample);
291+
expect(fragTrack.state.accumulatedSize).toBe(0);
292+
expect(fragTrack.segmentStream).toBeUndefined();
293+
});
209294
});

0 commit comments

Comments
 (0)