Skip to content

Commit 62bcaea

Browse files
committed
fix: default the output frame rate to the render's
1 parent 948ad7b commit 62bcaea

6 files changed

Lines changed: 26 additions & 7 deletions

File tree

‎src/components/canvas/players/html5-player.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ export class Html5Player extends Player {
117117
private decodedFrames: Map<number, pixi.Texture> = new Map();
118118
private decodeInFlight: number | null = null;
119119
private captureInFlight: Promise<Blob[] | null> | null = null;
120-
private captureFps: number = 30;
120+
private captureFps: number = 25;
121121
private mode: Html5Mode = "editing";
122122
private contentHash: string | null = null;
123123
private capturedHash: string | null = null;
@@ -283,7 +283,7 @@ export class Html5Player extends Player {
283283
const height = this.clipConfiguration.height || this.edit.size.height;
284284
this.renderedWidth = width;
285285
this.renderedHeight = height;
286-
this.captureFps = this.edit.getOutputFps() || 30;
286+
this.captureFps = this.edit.getOutputFps();
287287
this.contentHash = await this.hashAsset();
288288

289289
const iframe = createOffscreenIframe(width, height);

‎src/core/export/export-coordinator.ts‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,15 +72,15 @@ export class ExportCoordinator {
7272
}
7373

7474
async exportBlob(options: ExportBlobOptions = {}): Promise<Blob> {
75-
return this.encode(this.edit.getEdit().output?.fps ?? 30, options);
75+
return this.encode(this.edit.getOutputFps(), options);
7676
}
7777

7878
async export(filename = "shotstack-export.mp4", fps?: number): Promise<void> {
7979
if (exporting.has(this.edit)) throw new ExportError("Export in progress", "init");
8080

8181
this.progressUI.create();
8282
try {
83-
const blob = await this.encode(fps ?? this.edit.getEdit().output?.fps ?? 30, {
83+
const blob = await this.encode(fps ?? this.edit.getOutputFps(), {
8484
onProgress: (fraction, stage) => this.progressUI.update(fraction * 100, 100, stage)
8585
});
8686

@@ -282,7 +282,7 @@ export class ExportCoordinator {
282282
if (e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement) return;
283283
if (e.code === "KeyE" && (e.metaKey || e.ctrlKey) && !exporting.has(this.edit)) {
284284
e.preventDefault();
285-
this.export("shotstack-export.mp4", this.edit.getEdit().output?.fps || 30).catch(err => console.error("Export failed:", err));
285+
this.export("shotstack-export.mp4", this.edit.getOutputFps()).catch(err => console.error("Export failed:", err));
286286
}
287287
};
288288

‎src/core/output-settings-manager.ts‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,9 @@ export class OutputSettingsManager {
130130
}
131131

132132
getFps(): number {
133-
return this.edit.getResolvedEdit()?.output?.fps ?? 30;
133+
const output = this.edit.getResolvedEdit()?.output;
134+
// The render's defaults, so the preview and the render step through the same frames.
135+
return output?.fps ?? (output?.resolution === "preview" ? 12 : 25);
134136
}
135137

136138
// ─── Format ───────────────────────────────────────────────────────────────

‎tests/export-coordinator.test.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ function createHarness() {
8080
tracks: [] as Array<Array<{ clipId: string; needsResolution: boolean }>>,
8181
getEdit: jest.fn(() => ({ output: { fps: 2 } as { fps: number; mute?: boolean } })),
8282
getOutputFormat: jest.fn(() => "mp4"),
83+
getOutputFps: jest.fn(() => 2),
8384
getResolvedEdit: () => ({ timeline: { tracks: [] }, output: { format: "mp4" } }),
8485
getFontUrlByFamilyAndWeight: () => null,
8586
whenSettled: jest.fn(() => Promise.resolve()),

‎tests/html5-player.test.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -494,7 +494,7 @@ const frameMarkup = (blob: Blob) =>
494494
function createStillPlayer() {
495495
const created = createPlayer();
496496
Object.assign(created.edit, { getCanvas: () => null });
497-
Object.assign(created.player, { getLength: () => 2 });
497+
Object.assign(created.player, { getLength: () => 2, captureFps: 30 });
498498
jest.spyOn(HTMLIFrameElement.prototype, "srcdoc", "set").mockImplementation(function navigate(this: HTMLIFrameElement) {
499499
setTimeout(() => {
500500
const doc = this.contentDocument!;

‎tests/output-resolution.test.ts‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,22 @@ describe("Output Resolution and AspectRatio", () => {
122122
let edit: Edit;
123123
let emitSpy: jest.SpyInstance;
124124

125+
describe("output fps", () => {
126+
it("defaults to the render's 25 fps", () => {
127+
expect(edit.getOutputFps()).toBe(25);
128+
});
129+
130+
it("defaults to the render's 12 fps at preview resolution", async () => {
131+
await edit.setOutputResolution("preview");
132+
expect(edit.getOutputFps()).toBe(12);
133+
});
134+
135+
it("uses the edit's fps when set", async () => {
136+
await edit.setOutputFps(30);
137+
expect(edit.getOutputFps()).toBe(30);
138+
});
139+
});
140+
125141
beforeEach(async () => {
126142
edit = new Edit(createMinimalEdit());
127143
await edit.load();

0 commit comments

Comments
 (0)