Add more conversion tests for trimming wholly outside of the available media range

This commit is contained in:
Vanilagy
2026-09-08 17:20:14 +02:00
parent 450c5def97
commit 6e6785e5fc
2 changed files with 150 additions and 6 deletions
+21 -4
View File
@@ -1781,8 +1781,16 @@ export class Conversion {
break;
}
const adjustedSampleTimestamp = Math.max(0, sample.timestamp + this._timestampOffset);
sample.setTimestamp(adjustedSampleTimestamp);
const clampedStartTimestamp = Math.max(this._startTimestamp, sample.timestamp);
const clampedEndTimestamp = Math.min(this._endTimestamp, sample.timestamp + sample.duration);
if (clampedStartTimestamp >= clampedEndTimestamp) {
// Wholly out of the trim region
continue;
}
sample.setTimestamp(clampedStartTimestamp + this._timestampOffset);
sample.setDuration(clampedEndTimestamp - clampedStartTimestamp);
this._reportProgress(outputTrackId, sample.timestamp + sample.duration);
await source.add(sample);
@@ -1874,8 +1882,17 @@ export class Conversion {
if (
startPacket
&& startPacket.timestamp < this._startTimestamp
&& this._copyBoundaryPolicy === 'shrink'
&& (
(
this._copyBoundaryPolicy === 'shrink'
&& startPacket.timestamp < this._startTimestamp
)
|| (
this._copyBoundaryPolicy === 'expand'
// Check if packet is wholly before the start
&& startPacket.timestamp + startPacket.duration <= this._startTimestamp
)
)
) {
startPacket = await sink.getNextKeyPacket(startPacket);
}
+129 -2
View File
@@ -939,8 +939,8 @@ test('Packet copy, delta frame trim, video transcoded, Matroska', async () => {
shiftTolerance: Infinity,
},
},
expectedTimeOffset: 1.484,
videoStartTimestamp: 0.008,
expectedTimeOffset: 1.472,
videoStartTimestamp: 0.028,
videoEndTimestamp: 1.008,
audioStartTimestamp: 0,
audioEndTimestamp: 1.024,
@@ -1053,3 +1053,130 @@ const testCopy = async (options: {
}
}
};
test('Trim wholly before media data, transcode', async () => {
using input = new Input({
source: new UrlSource('/demo.mp4'),
formats: ALL_FORMATS,
});
const output = new Output({
format: new Mp4OutputFormat(),
target: new BufferTarget(),
});
const conversion = await Conversion.init({
input,
output,
video: {
forceTranscode: true,
},
audio: {
forceTranscode: true,
},
trim: {
start: -10,
end: -5,
},
});
await conversion.execute();
using newInput = new Input({
source: new BufferSource(output.target.buffer!),
formats: ALL_FORMATS,
});
expect(await newInput.getPrimaryVideoTrack()).toBeNull(); // No video data
expect(await newInput.getPrimaryAudioTrack()).toBeNull(); // No audio data
});
test('Trim wholly before media data, copy', async () => {
using input = new Input({
source: new UrlSource('/demo.mp4'),
formats: ALL_FORMATS,
});
const output = new Output({
format: new Mp4OutputFormat(),
target: new BufferTarget(),
});
const conversion = await Conversion.init({
input,
output,
copy: { mode: 'forced' },
trim: {
start: -10,
end: -5,
},
});
await conversion.execute();
using newInput = new Input({
source: new BufferSource(output.target.buffer!),
formats: ALL_FORMATS,
});
expect(await newInput.getPrimaryVideoTrack()).toBeNull(); // No video data
expect(await newInput.getPrimaryAudioTrack()).toBeNull(); // No audio data
});
test('Trim wholly past media data, transcode', async () => {
using input = new Input({
source: new UrlSource('/demo.mp4'),
formats: ALL_FORMATS,
});
const output = new Output({
format: new Mp4OutputFormat(),
target: new BufferTarget(),
});
const conversion = await Conversion.init({
input,
output,
video: {
forceTranscode: true,
},
audio: {
forceTranscode: true,
},
trim: {
start: 10,
end: 15,
},
});
await conversion.execute();
using newInput = new Input({
source: new BufferSource(output.target.buffer!),
formats: ALL_FORMATS,
});
expect(await newInput.getPrimaryVideoTrack()).toBeNull(); // No video data
expect(await newInput.getPrimaryAudioTrack()).toBeNull(); // No audio data
});
test('Trim wholly past media data, copy', async () => {
using input = new Input({
source: new UrlSource('/demo.mp4'),
formats: ALL_FORMATS,
});
const output = new Output({
format: new Mp4OutputFormat(),
target: new BufferTarget(),
});
const conversion = await Conversion.init({
input,
output,
copy: { mode: 'forced' },
trim: {
start: 10,
end: 15,
},
});
await conversion.execute();
using newInput = new Input({
source: new BufferSource(output.target.buffer!),
formats: ALL_FORMATS,
});
// Don't test video yet; requires extensions to packet fetching logic. B-frames!!
// expect(await newInput.getPrimaryVideoTrack()).toBeNull(); // No video data
expect(await newInput.getPrimaryAudioTrack()).toBeNull(); // No audio data
});