mirror of
https://github.com/webadderallorg/Recordly.git
synced 2026-11-09 07:23:14 +00:00
117 lines
3.3 KiB
TypeScript
117 lines
3.3 KiB
TypeScript
import { beforeEach, expect, it, vi } from "vitest";
|
|
|
|
const state = vi.hoisted(() => ({ run: vi.fn() }));
|
|
vi.mock("./process", () => ({ runFfmpegWithMetrics: state.run }));
|
|
vi.mock("../../ffmpeg/metadata", () => ({ probeNativeVideoMetadata: vi.fn() }));
|
|
|
|
import { buildWindowsGpuWebcamProxyArgs, prepareWindowsGpuWebcamInput } from "./sourceProxy";
|
|
import type { NativeStaticLayoutExportOptions } from "./types";
|
|
|
|
const base: NativeStaticLayoutExportOptions = {
|
|
inputPath: "/screen.mp4",
|
|
webcamInputPath: "/camera.webm",
|
|
width: 1920,
|
|
height: 1080,
|
|
frameRate: 30,
|
|
bitrate: 12000000,
|
|
durationSec: 10,
|
|
contentWidth: 1920,
|
|
contentHeight: 1080,
|
|
offsetX: 0,
|
|
offsetY: 0,
|
|
backgroundColor: "#000000",
|
|
encodingMode: "balanced",
|
|
};
|
|
const segment = (start: number, end: number, outputStart = 0, speed = 1) => ({
|
|
sourceStartMs: start * 1000,
|
|
sourceEndMs: end * 1000,
|
|
outputStartMs: outputStart * 1000,
|
|
outputEndMs: (outputStart + (end - start) / speed) * 1000,
|
|
speed,
|
|
});
|
|
const flag = (args: string[], name: string) => args[args.indexOf(name) + 1];
|
|
beforeEach(() => {
|
|
state.run.mockReset().mockResolvedValue({ success: true, elapsedMs: 5 });
|
|
});
|
|
it.each([
|
|
["unedited", {}, "10"],
|
|
["later trimmed source", { timelineSegments: [segment(60, 70)] }, "70"],
|
|
["speed-up", { timelineSegments: [segment(60, 80, 0, 2)] }, "80"],
|
|
["slow-down", { timelineSegments: [segment(60, 65, 0, 0.5)] }, "65"],
|
|
["negative webcam offset", { webcamTimeOffsetMs: -500 }, "10.5"],
|
|
[
|
|
"trim with webcam delay",
|
|
{ timelineSegments: [segment(60, 70)], webcamTimeOffsetMs: 3000 },
|
|
"67",
|
|
],
|
|
[
|
|
"nonmonotonic source ranges",
|
|
{ timelineSegments: [segment(60, 65), segment(10, 15, 5)] },
|
|
"65",
|
|
],
|
|
["original coverage with positive offset", { webcamTimeOffsetMs: 5000 }, "10"],
|
|
] as const)("covers required camera samples for %s without changing proxy encoding", (_name, changes, duration) => {
|
|
const args = buildWindowsGpuWebcamProxyArgs(
|
|
{
|
|
...base,
|
|
...changes,
|
|
timelineSegments: changes.timelineSegments ? [...changes.timelineSegments] : undefined,
|
|
},
|
|
"/proxy.mp4",
|
|
);
|
|
expect(flag(args, "-t")).toBe(duration);
|
|
const reference = [
|
|
"-y",
|
|
"-hide_banner",
|
|
"-loglevel",
|
|
"error",
|
|
"-i",
|
|
"/camera.webm",
|
|
"-map",
|
|
"0:v:0",
|
|
"-an",
|
|
"-t",
|
|
duration,
|
|
"-vf",
|
|
"scale=trunc(iw/2)*2:trunc(ih/2)*2,setsar=1,fps=30",
|
|
"-c:v",
|
|
"libx264",
|
|
"-preset",
|
|
"ultrafast",
|
|
"-tune",
|
|
"zerolatency",
|
|
"-b:v",
|
|
"4000000",
|
|
"-maxrate",
|
|
"4800000",
|
|
"-bufsize",
|
|
"8000000",
|
|
"-pix_fmt",
|
|
"yuv420p",
|
|
"-movflags",
|
|
"+faststart",
|
|
"/proxy.mp4",
|
|
];
|
|
expect(args).toEqual(reference);
|
|
});
|
|
it("passes mapped coverage and a matching processing deadline to the actual proxy process", async () => {
|
|
const options = { ...base, durationSec: 60, timelineSegments: [segment(60, 660, 0, 10)] };
|
|
await prepareWindowsGpuWebcamInput("ffmpeg", options, "/proxy.mp4", {
|
|
terminating: false,
|
|
currentProcess: null,
|
|
});
|
|
const call = state.run.mock.calls[0];
|
|
expect(flag(call[1], "-t")).toBe("660");
|
|
expect(call[2]).toBe(660000);
|
|
});
|
|
it("keeps supported native webcam files unchanged", async () => {
|
|
const result = await prepareWindowsGpuWebcamInput(
|
|
"ffmpeg",
|
|
{ ...base, webcamInputPath: "/camera.mp4" },
|
|
"/proxy.mp4",
|
|
{ terminating: false, currentProcess: null },
|
|
);
|
|
expect(result.inputPath).toBe("/camera.mp4");
|
|
expect(state.run).not.toHaveBeenCalled();
|
|
});
|