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:
@@ -1,7 +1,61 @@
|
||||
import type { FrameRate } from "opencut-wasm";
|
||||
import type { MediaAsset } from "@/lib/media/types";
|
||||
|
||||
type MediaAssetFpsInput = Pick<MediaAsset, "type" | "fps">;
|
||||
|
||||
const STANDARD_FRAME_RATES: Array<{ value: number; rate: FrameRate }> = [
|
||||
{ value: 24_000 / 1_001, rate: { numerator: 24_000, denominator: 1_001 } },
|
||||
{ value: 24, rate: { numerator: 24, denominator: 1 } },
|
||||
{ value: 25, rate: { numerator: 25, denominator: 1 } },
|
||||
{ value: 30_000 / 1_001, rate: { numerator: 30_000, denominator: 1_001 } },
|
||||
{ value: 30, rate: { numerator: 30, denominator: 1 } },
|
||||
{ value: 48, rate: { numerator: 48, denominator: 1 } },
|
||||
{ value: 50, rate: { numerator: 50, denominator: 1 } },
|
||||
{ value: 60_000 / 1_001, rate: { numerator: 60_000, denominator: 1_001 } },
|
||||
{ value: 60, rate: { numerator: 60, denominator: 1 } },
|
||||
{ value: 120, rate: { numerator: 120, denominator: 1 } },
|
||||
];
|
||||
|
||||
const STANDARD_FRAME_RATE_TOLERANCE = 0.01;
|
||||
|
||||
export function frameRateToFloat(rate: FrameRate): number {
|
||||
return rate.numerator / rate.denominator;
|
||||
}
|
||||
|
||||
export function frameRatesEqual(a: FrameRate, b: FrameRate): boolean {
|
||||
return a.numerator === b.numerator && a.denominator === b.denominator;
|
||||
}
|
||||
|
||||
export function floatToFrameRate(fps: number): FrameRate {
|
||||
const standard = STANDARD_FRAME_RATES.find(
|
||||
(candidate) => Math.abs(fps - candidate.value) <= STANDARD_FRAME_RATE_TOLERANCE,
|
||||
);
|
||||
if (standard) return standard.rate;
|
||||
|
||||
if (Number.isInteger(fps)) {
|
||||
return { numerator: fps, denominator: 1 };
|
||||
}
|
||||
|
||||
const ARBITRARY_DENOMINATOR = 1_000_000;
|
||||
const scaledNumerator = Math.round(fps * ARBITRARY_DENOMINATOR);
|
||||
const divisor = gcd(scaledNumerator, ARBITRARY_DENOMINATOR);
|
||||
return {
|
||||
numerator: scaledNumerator / divisor,
|
||||
denominator: ARBITRARY_DENOMINATOR / divisor,
|
||||
};
|
||||
}
|
||||
|
||||
function gcd(left: number, right: number): number {
|
||||
let a = Math.abs(left);
|
||||
let b = Math.abs(right);
|
||||
while (b !== 0) {
|
||||
const remainder = a % b;
|
||||
a = b;
|
||||
b = remainder;
|
||||
}
|
||||
return a || 1;
|
||||
}
|
||||
|
||||
export function getHighestImportedVideoFps({
|
||||
mediaAssets,
|
||||
}: {
|
||||
@@ -24,16 +78,18 @@ export function getRaisedProjectFpsForImportedMedia({
|
||||
currentFps,
|
||||
importedAssets,
|
||||
}: {
|
||||
currentFps: number;
|
||||
currentFps: FrameRate;
|
||||
importedAssets: MediaAssetFpsInput[];
|
||||
}): number | null {
|
||||
}): FrameRate | null {
|
||||
const highestImportedVideoFps = getHighestImportedVideoFps({
|
||||
mediaAssets: importedAssets,
|
||||
});
|
||||
|
||||
if (highestImportedVideoFps === null || highestImportedVideoFps <= currentFps) {
|
||||
const currentFpsFloat = frameRateToFloat(currentFps);
|
||||
|
||||
if (highestImportedVideoFps === null || highestImportedVideoFps <= currentFpsFloat) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return highestImportedVideoFps;
|
||||
return floatToFrameRate(highestImportedVideoFps);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user