summaryrefslogtreecommitdiff
path: root/apps/web/src/services/renderer/scene-exporter.ts
diff options
context:
space:
mode:
authorcashmere <cashmere@autistici.org>2026-07-28 19:41:14 +0200
committercashmere <cashmere@autistici.org>2026-07-28 19:41:14 +0200
commite837e7504379e100ff3b56282577ec67bb88bec9 (patch)
treeeec7e6f964725a783a9e907e931862591bb46b55 /apps/web/src/services/renderer/scene-exporter.ts
parentda99dec8a2de1f3849aebbf5695c1e812b2dbdf9 (diff)
downloadopencut-e837e7504379e100ff3b56282577ec67bb88bec9.tar.gz
feat: ripple-extend resize, timeline filmstrip, export hardening
Yesterday's verified web session (gates green: 316 tests, tsc, build): - ripple-extend pushes right-hand chain on static-element resize - per-position video filmstrip thumbnails with zoom-adaptive density - fps-aware export bitrate, 2s keyframes, even-dim export canvas - freeze frame uses fixed 3s duration, prompt removed
Diffstat (limited to 'apps/web/src/services/renderer/scene-exporter.ts')
-rw-r--r--apps/web/src/services/renderer/scene-exporter.ts17
1 files changed, 14 insertions, 3 deletions
diff --git a/apps/web/src/services/renderer/scene-exporter.ts b/apps/web/src/services/renderer/scene-exporter.ts
index 66df9485..04c14277 100644
--- a/apps/web/src/services/renderer/scene-exporter.ts
+++ b/apps/web/src/services/renderer/scene-exporter.ts
@@ -19,6 +19,7 @@ import { frameRateToFloat } from "@/fps/utils";
import type { RootNode } from "./nodes/root-node";
import type { ExportFormat, ExportQuality } from "@/export";
import { CanvasRenderer } from "./canvas-renderer";
+import { computeVideoBitrate } from "./export-encoding";
type ExportParams = {
width: number;
@@ -30,13 +31,15 @@ type ExportParams = {
audioBuffer?: AudioBuffer;
};
-const qualityMap = {
+const audioQualityMap = {
low: QUALITY_LOW,
medium: QUALITY_MEDIUM,
high: QUALITY_HIGH,
very_high: QUALITY_VERY_HIGH,
};
+const KEY_FRAME_INTERVAL_SECONDS = 2;
+
export type SceneExporterEvents = {
progress: [progress: number];
complete: [buffer: ArrayBuffer];
@@ -99,9 +102,17 @@ export class SceneExporter extends EventEmitter<SceneExporterEvents> {
target: new BufferTarget(),
});
+ const videoBitrate = computeVideoBitrate({
+ width: this.renderer.width,
+ height: this.renderer.height,
+ fps: fpsFloat,
+ quality: this.quality,
+ });
+
const videoSource = new CanvasSource(this.renderer.getOutputCanvas(), {
codec: this.format === "webm" ? "vp9" : "avc",
- bitrate: qualityMap[this.quality],
+ bitrate: videoBitrate,
+ keyFrameInterval: KEY_FRAME_INTERVAL_SECONDS,
});
output.addVideoTrack(videoSource, { frameRate: fpsFloat });
@@ -122,7 +133,7 @@ export class SceneExporter extends EventEmitter<SceneExporterEvents> {
audioSource = new AudioBufferSource({
codec: audioCodec,
- bitrate: qualityMap[this.quality],
+ bitrate: audioQualityMap[this.quality],
});
output.addAudioTrack(audioSource);
}