mirror of
https://github.com/OpenCut-app/OpenCut.git
synced 2026-07-13 21:52:53 +02:00
refactor: split effects and masks into dedicated rust crates, introduce MediaTime and FrameRate
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,6 @@
|
||||
import type { EditorCore } from "@/core";
|
||||
import { TICKS_PER_SECOND } from "@/lib/wasm";
|
||||
import { roundToFrame } from "opencut-wasm";
|
||||
|
||||
export class PlaybackManager {
|
||||
private isPlaying = false;
|
||||
@@ -9,11 +11,12 @@ export class PlaybackManager {
|
||||
private isScrubbing = false;
|
||||
private listeners = new Set<() => void>();
|
||||
private playbackTimer: number | null = null;
|
||||
private lastUpdate = 0;
|
||||
private playbackStartWallTime = 0;
|
||||
private playbackStartTime = 0;
|
||||
|
||||
constructor(private editor: EditorCore) {
|
||||
this.editor.timeline.subscribe(() => {
|
||||
const maxTime = this.editor.timeline.getLastSeekableTime();
|
||||
const maxTime = this.editor.timeline.getTotalDuration();
|
||||
if (this.currentTime > maxTime && maxTime > 0) {
|
||||
this.currentTime = maxTime;
|
||||
this.notify();
|
||||
@@ -22,7 +25,7 @@ export class PlaybackManager {
|
||||
}
|
||||
|
||||
play(): void {
|
||||
const maxTime = this.editor.timeline.getLastSeekableTime();
|
||||
const maxTime = this.editor.timeline.getTotalDuration();
|
||||
|
||||
if (maxTime > 0) {
|
||||
if (this.currentTime >= maxTime) {
|
||||
@@ -50,8 +53,12 @@ export class PlaybackManager {
|
||||
}
|
||||
|
||||
seek({ time }: { time: number }): void {
|
||||
const maxTime = this.editor.timeline.getLastSeekableTime();
|
||||
const maxTime = this.editor.timeline.getTotalDuration();
|
||||
this.currentTime = Math.max(0, Math.min(maxTime, time));
|
||||
if (this.isPlaying) {
|
||||
this.playbackStartWallTime = performance.now();
|
||||
this.playbackStartTime = this.currentTime;
|
||||
}
|
||||
this.notify();
|
||||
|
||||
window.dispatchEvent(
|
||||
@@ -135,7 +142,8 @@ export class PlaybackManager {
|
||||
cancelAnimationFrame(this.playbackTimer);
|
||||
}
|
||||
|
||||
this.lastUpdate = performance.now();
|
||||
this.playbackStartWallTime = performance.now();
|
||||
this.playbackStartTime = this.currentTime;
|
||||
this.updateTime();
|
||||
}
|
||||
|
||||
@@ -149,12 +157,11 @@ export class PlaybackManager {
|
||||
private updateTime = (): void => {
|
||||
if (!this.isPlaying) return;
|
||||
|
||||
const now = performance.now();
|
||||
const delta = (now - this.lastUpdate) / 1000;
|
||||
this.lastUpdate = now;
|
||||
|
||||
const newTime = this.currentTime + delta;
|
||||
const maxTime = this.editor.timeline.getLastSeekableTime();
|
||||
const fps = this.editor.project.getActive()?.settings.fps;
|
||||
const elapsedSeconds = (performance.now() - this.playbackStartWallTime) / 1000;
|
||||
const rawTime = this.playbackStartTime + Math.round(elapsedSeconds * TICKS_PER_SECOND);
|
||||
const newTime = fps ? (roundToFrame({ time: rawTime, rate: fps }) ?? rawTime) : rawTime;
|
||||
const maxTime = this.editor.timeline.getTotalDuration();
|
||||
|
||||
if (maxTime > 0 && newTime >= maxTime) {
|
||||
this.pause();
|
||||
|
||||
@@ -496,7 +496,7 @@ export class ProjectManager {
|
||||
importedAssets,
|
||||
}: {
|
||||
importedAssets: Array<Pick<MediaAsset, "type" | "fps">>;
|
||||
}): number | null {
|
||||
}): import("opencut-wasm").FrameRate | null {
|
||||
if (!this.active) return null;
|
||||
|
||||
const nextFps = getRaisedProjectFpsForImportedMedia({
|
||||
|
||||
@@ -5,7 +5,8 @@ import { CanvasRenderer } from "@/services/renderer/canvas-renderer";
|
||||
import { SceneExporter } from "@/services/renderer/scene-exporter";
|
||||
import { buildScene } from "@/services/renderer/scene-builder";
|
||||
import { createTimelineAudioBuffer } from "@/lib/media/audio";
|
||||
import { formatTimeCode } from "opencut-wasm";
|
||||
import { formatTimecode } from "opencut-wasm";
|
||||
import { frameRateToFloat } from "@/lib/fps/utils";
|
||||
import { downloadBlob } from "@/utils/browser";
|
||||
|
||||
type SnapshotResult =
|
||||
@@ -81,7 +82,10 @@ export class RendererManager {
|
||||
}
|
||||
|
||||
const { canvasSize, fps } = activeProject.settings;
|
||||
const renderTime = this.editor.playback.getCurrentTime();
|
||||
const renderTime = Math.min(
|
||||
this.editor.playback.getCurrentTime(),
|
||||
this.editor.timeline.getLastFrameTime(),
|
||||
);
|
||||
|
||||
const renderer = new CanvasRenderer({
|
||||
width: canvasSize.width,
|
||||
@@ -107,7 +111,7 @@ export class RendererManager {
|
||||
return { success: false, error: "Failed to create image" };
|
||||
}
|
||||
|
||||
const timecode = formatTimeCode({ timeInSeconds: renderTime, fps })!.replace(/:/g, "-");
|
||||
const timecode = formatTimecode({ time: renderTime, rate: fps })!.replace(/:/g, "-");
|
||||
const safeName =
|
||||
activeProject.metadata.name.replace(/[<>:"/\\|?*]/g, "-").trim() ||
|
||||
"snapshot";
|
||||
@@ -148,7 +152,7 @@ export class RendererManager {
|
||||
return { success: false, error: "Project is empty" };
|
||||
}
|
||||
|
||||
const exportFps = fps || activeProject.settings.fps;
|
||||
const exportFps = fps ?? activeProject.settings.fps;
|
||||
const canvasSize = activeProject.settings.canvasSize;
|
||||
|
||||
let audioBuffer: AudioBuffer | null = null;
|
||||
|
||||
@@ -18,7 +18,7 @@ import type {
|
||||
AnimationValue,
|
||||
ScalarCurveKeyframePatch,
|
||||
} from "@/lib/animation/types";
|
||||
import { getLastFrameTime } from "opencut-wasm";
|
||||
import { lastFrameTime } from "opencut-wasm";
|
||||
import { BatchCommand } from "@/lib/commands";
|
||||
import {
|
||||
AddTrackCommand,
|
||||
@@ -196,11 +196,11 @@ export class TimelineManager {
|
||||
return calculateTotalDuration({ tracks: this.getTracks() });
|
||||
}
|
||||
|
||||
getLastSeekableTime(): number {
|
||||
getLastFrameTime(): number {
|
||||
const duration = this.getTotalDuration();
|
||||
const fps = this.editor.project.getActive()?.settings.fps;
|
||||
if (!fps || duration <= 0) return duration;
|
||||
return getLastFrameTime({ duration, fps });
|
||||
return lastFrameTime({ duration, rate: fps }) ?? duration;
|
||||
}
|
||||
|
||||
getTrackById({ trackId }: { trackId: string }): TimelineTrack | null {
|
||||
|
||||
Reference in New Issue
Block a user