From e4b67094e757917588fb624a30aa046667403add Mon Sep 17 00:00:00 2001 From: Maze Winther Date: Tue, 7 Apr 2026 01:09:13 +0200 Subject: refactor: split effects and masks into dedicated rust crates, introduce MediaTime and FrameRate --- .../renderer/nodes/blur-background-node.ts | 9 ++- apps/web/src/services/renderer/nodes/video-node.ts | 76 +++++++++++----------- .../web/src/services/renderer/nodes/visual-node.ts | 3 +- 3 files changed, 44 insertions(+), 44 deletions(-) (limited to 'apps/web/src/services/renderer/nodes') diff --git a/apps/web/src/services/renderer/nodes/blur-background-node.ts b/apps/web/src/services/renderer/nodes/blur-background-node.ts index c67a5113..4cb45b33 100644 --- a/apps/web/src/services/renderer/nodes/blur-background-node.ts +++ b/apps/web/src/services/renderer/nodes/blur-background-node.ts @@ -1,5 +1,5 @@ -import { TIME_EPSILON_SECONDS } from "@/constants/animation-constants"; import { buildGaussianBlurPasses, intensityToSigma } from "@/lib/effects/definitions/blur"; +import { mediaTimeToSeconds } from "opencut-wasm"; import { getSourceTimeAtClipTime } from "@/lib/retime"; import { videoCache } from "@/services/video-cache/service"; import type { RetimeConfig } from "@/lib/timeline"; @@ -39,9 +39,7 @@ export class BlurBackgroundNode extends BaseNode { private isInRange({ time }: { time: number }): boolean { const localTime = time - this.params.timeOffset; - return ( - localTime >= -TIME_EPSILON_SECONDS && localTime < this.params.duration - ); + return localTime >= 0 && localTime < this.params.duration; } private getSourceLocalTime({ time }: { time: number }): number { @@ -61,10 +59,11 @@ export class BlurBackgroundNode extends BaseNode { time: number; }): Promise { if (this.params.mediaType === "video") { + const sourceTimeTicks = this.getSourceLocalTime({ time }); const frame = await videoCache.getFrameAt({ mediaId: this.params.mediaId, file: this.params.file, - time: this.getSourceLocalTime({ time }), + time: mediaTimeToSeconds({ time: sourceTimeTicks }), }); if (!frame) { diff --git a/apps/web/src/services/renderer/nodes/video-node.ts b/apps/web/src/services/renderer/nodes/video-node.ts index 3bb836ec..4f225567 100644 --- a/apps/web/src/services/renderer/nodes/video-node.ts +++ b/apps/web/src/services/renderer/nodes/video-node.ts @@ -1,37 +1,39 @@ -import type { CanvasRenderer } from "../canvas-renderer"; -import { VisualNode, type VisualNodeParams } from "./visual-node"; -import { videoCache } from "@/services/video-cache/service"; - -export interface VideoNodeParams extends VisualNodeParams { - url: string; - file: File; - mediaId: string; -} - -export class VideoNode extends VisualNode { - async render({ renderer, time }: { renderer: CanvasRenderer; time: number }) { - await super.render({ renderer, time }); - - if (!this.isInRange({ time })) { - return; - } - - const videoTime = this.getSourceLocalTime({ time }); - - const frame = await videoCache.getFrameAt({ - mediaId: this.params.mediaId, - file: this.params.file, - time: videoTime, - }); - - if (frame) { - this.renderVisual({ - renderer, - source: frame.canvas, - sourceWidth: frame.canvas.width, - sourceHeight: frame.canvas.height, - timelineTime: time, - }); - } - } -} +import type { CanvasRenderer } from "../canvas-renderer"; +import { mediaTimeToSeconds } from "opencut-wasm"; +import { VisualNode, type VisualNodeParams } from "./visual-node"; +import { videoCache } from "@/services/video-cache/service"; + +export interface VideoNodeParams extends VisualNodeParams { + url: string; + file: File; + mediaId: string; +} + +export class VideoNode extends VisualNode { + async render({ renderer, time }: { renderer: CanvasRenderer; time: number }) { + await super.render({ renderer, time }); + + if (!this.isInRange({ time })) { + return; + } + + const videoTimeTicks = this.getSourceLocalTime({ time }); + const videoTimeSeconds = mediaTimeToSeconds({ time: videoTimeTicks }); + + const frame = await videoCache.getFrameAt({ + mediaId: this.params.mediaId, + file: this.params.file, + time: videoTimeSeconds, + }); + + if (frame) { + this.renderVisual({ + renderer, + source: frame.canvas, + sourceWidth: frame.canvas.width, + sourceHeight: frame.canvas.height, + timelineTime: time, + }); + } + } +} diff --git a/apps/web/src/services/renderer/nodes/visual-node.ts b/apps/web/src/services/renderer/nodes/visual-node.ts index 82093da4..4a5fcdc6 100644 --- a/apps/web/src/services/renderer/nodes/visual-node.ts +++ b/apps/web/src/services/renderer/nodes/visual-node.ts @@ -12,7 +12,6 @@ import { resolveTransformAtTime, } from "@/lib/animation"; import { resolveEffectParamsAtTime } from "@/lib/animation/effect-param-channel"; -import { TIME_EPSILON_SECONDS } from "@/constants/animation-constants"; import { effectsRegistry, resolveEffectPasses } from "@/lib/effects"; import { masksRegistry } from "@/lib/masks"; import { getSourceTimeAtClipTime } from "@/lib/retime"; @@ -58,7 +57,7 @@ export abstract class VisualNode< protected isInRange({ time }: { time: number }): boolean { const localTime = time - this.params.timeOffset; return ( - localTime >= -TIME_EPSILON_SECONDS && + localTime >= 0 && localTime < this.params.duration ); } -- cgit v1.3.1