mirror of
https://github.com/webadderallorg/Recordly.git
synced 2026-09-25 15:25:44 +00:00
fix(export): validate edited-track fast-path inputs
This commit is contained in:
@@ -40,4 +40,19 @@ describe("buildEditedTrackSourceAudioFilter", () => {
|
||||
),
|
||||
).toBeNull();
|
||||
});
|
||||
|
||||
it("returns null when the edited-track segments are malformed", () => {
|
||||
expect(
|
||||
buildEditedTrackSourceAudioFilter(
|
||||
[{ startMs: Number.NaN, endMs: 2_000, speed: 1.5 }],
|
||||
44_100,
|
||||
),
|
||||
).toBeNull();
|
||||
expect(
|
||||
buildEditedTrackSourceAudioFilter([{ startMs: 0, endMs: 2_000, speed: 0 }], 44_100),
|
||||
).toBeNull();
|
||||
expect(
|
||||
buildEditedTrackSourceAudioFilter([{ startMs: 0, endMs: 2_000, speed: 1 }], 0.4),
|
||||
).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -181,24 +181,46 @@ export function buildEditedTrackSourceAudioFilter(
|
||||
}
|
||||
|
||||
const normalizedSourceSampleRate = Math.round(sourceSampleRate);
|
||||
if (normalizedSourceSampleRate < 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const filterParts: string[] = [];
|
||||
const segmentLabels: string[] = [];
|
||||
let hasInvalidSegment = false;
|
||||
|
||||
segments.forEach((segment, index) => {
|
||||
if (!Number.isFinite(segment.startMs) || !Number.isFinite(segment.endMs)) {
|
||||
hasInvalidSegment = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (segment.endMs - segment.startMs <= 0.5) {
|
||||
hasInvalidSegment = true;
|
||||
return;
|
||||
}
|
||||
|
||||
const label = `edited_audio_${index}`;
|
||||
const speed = Number.isFinite(segment.speed) && segment.speed > 0 ? segment.speed : 1;
|
||||
const speed = segment.speed;
|
||||
if (!Number.isFinite(speed) || speed <= 0) {
|
||||
hasInvalidSegment = true;
|
||||
return;
|
||||
}
|
||||
|
||||
const segmentFilter = [
|
||||
`[1:a]atrim=start=${formatFfmpegSeconds(segment.startMs)}:end=${formatFfmpegSeconds(segment.endMs)}`,
|
||||
"asetpts=PTS-STARTPTS",
|
||||
];
|
||||
|
||||
if (Math.abs(speed - 1) > 0.0001) {
|
||||
const adjustedSampleRate = Math.round(normalizedSourceSampleRate * speed);
|
||||
if (adjustedSampleRate < 1) {
|
||||
hasInvalidSegment = true;
|
||||
return;
|
||||
}
|
||||
|
||||
segmentFilter.push(
|
||||
`asetrate=${Math.max(1, Math.round(normalizedSourceSampleRate * speed))}`,
|
||||
`asetrate=${adjustedSampleRate}`,
|
||||
`aresample=${normalizedSourceSampleRate}`,
|
||||
);
|
||||
}
|
||||
@@ -207,7 +229,7 @@ export function buildEditedTrackSourceAudioFilter(
|
||||
segmentLabels.push(`[${label}]`);
|
||||
});
|
||||
|
||||
if (segmentLabels.length === 0) {
|
||||
if (hasInvalidSegment || segmentLabels.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -100,6 +100,23 @@ describe("editedTrackStrategy", () => {
|
||||
).toBe("offline-render-fallback");
|
||||
});
|
||||
|
||||
it("falls back when source duration or speed-region bounds are invalid", () => {
|
||||
const invalidBounds: SpeedRegion[] = [
|
||||
{ id: "speed-1", startMs: Number.NaN, endMs: 10_000, speed: 1.5 },
|
||||
];
|
||||
|
||||
expect(
|
||||
classifyEditedTrackStrategy({
|
||||
primaryAudioSourcePath: "recording.mp4",
|
||||
sourceDurationMs: Number.POSITIVE_INFINITY,
|
||||
trimRegions: EMPTY_TRIMS,
|
||||
speedRegions: invalidBounds,
|
||||
audioRegions: [],
|
||||
sourceAudioFallbackPaths: [],
|
||||
}),
|
||||
).toBe("offline-render-fallback");
|
||||
});
|
||||
|
||||
it("builds source segments that preserve trims and speed boundaries", () => {
|
||||
const trimRegions: TrimRegion[] = [{ id: "trim-1", startMs: 10_000, endMs: 12_000 }];
|
||||
const speedRegions: SpeedRegion[] = [
|
||||
@@ -118,4 +135,12 @@ describe("editedTrackStrategy", () => {
|
||||
{ startMs: 18_000, endMs: 20_000, speed: 1 },
|
||||
]);
|
||||
});
|
||||
|
||||
it("returns no source segments when speed-region bounds are invalid", () => {
|
||||
expect(
|
||||
buildEditedTrackSourceSegments(SOURCE_DURATION_MS, EMPTY_TRIMS, [
|
||||
{ id: "speed-1", startMs: 15_000, endMs: 10_000, speed: 1.5 },
|
||||
]),
|
||||
).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -26,8 +26,29 @@ function isSafeFiltergraphSpeed(speed: number): boolean {
|
||||
);
|
||||
}
|
||||
|
||||
function hasSafeFiltergraphSpeedRegions(speedRegions: SpeedRegion[]): boolean {
|
||||
return speedRegions.every((region) => isSafeFiltergraphSpeed(region.speed));
|
||||
function hasFiniteTimelineRange(startMs: number, endMs: number, sourceDurationMs: number): boolean {
|
||||
return (
|
||||
Number.isFinite(startMs) &&
|
||||
Number.isFinite(endMs) &&
|
||||
startMs >= 0 &&
|
||||
endMs > startMs &&
|
||||
endMs <= sourceDurationMs
|
||||
);
|
||||
}
|
||||
|
||||
function hasSafeFiltergraphSpeedRegions(
|
||||
speedRegions: SpeedRegion[],
|
||||
sourceDurationMs: number,
|
||||
): boolean {
|
||||
if (!Number.isFinite(sourceDurationMs) || sourceDurationMs <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return speedRegions.every(
|
||||
(region) =>
|
||||
hasFiniteTimelineRange(region.startMs, region.endMs, sourceDurationMs) &&
|
||||
isSafeFiltergraphSpeed(region.speed),
|
||||
);
|
||||
}
|
||||
|
||||
function buildKeptRanges(
|
||||
@@ -64,6 +85,20 @@ export function buildEditedTrackSourceSegments(
|
||||
trimRegions: TrimRegion[],
|
||||
speedRegions: SpeedRegion[],
|
||||
): EditedTrackSourceSegment[] {
|
||||
if (!Number.isFinite(sourceDurationMs) || sourceDurationMs <= 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (
|
||||
speedRegions.some(
|
||||
(region) =>
|
||||
!hasFiniteTimelineRange(region.startMs, region.endMs, sourceDurationMs) ||
|
||||
!isSafeFiltergraphSpeed(region.speed),
|
||||
)
|
||||
) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const segments: EditedTrackSourceSegment[] = [];
|
||||
const keptRanges = buildKeptRanges(sourceDurationMs, trimRegions);
|
||||
|
||||
@@ -119,7 +154,7 @@ export function classifyEditedTrackStrategy(input: EditedTrackStrategyInput): Ed
|
||||
return "offline-render-fallback";
|
||||
}
|
||||
|
||||
if (!hasSafeFiltergraphSpeedRegions(input.speedRegions)) {
|
||||
if (!hasSafeFiltergraphSpeedRegions(input.speedRegions, input.sourceDurationMs)) {
|
||||
return "offline-render-fallback";
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user