mirror of
https://github.com/webadderallorg/Recordly.git
synced 2026-11-09 15:33:31 +00:00
Address taskbar and rendering review feedback
This commit is contained in:
1 parent
b5bedd73d9
commit
83e2e5fbcf
6 files changed
+63
-16
No files matched your search
@@ -0,0 +1,21 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { getHudOverlayTaskbarOptions } from "./hudOverlayWindowOptions";
|
||||
|
||||
describe("getHudOverlayTaskbarOptions", () => {
|
||||
it("keeps a focusable HUD in the Windows taskbar", () => {
|
||||
expect(getHudOverlayTaskbarOptions("win32")).toEqual({
|
||||
skipTaskbar: false,
|
||||
focusable: true,
|
||||
});
|
||||
});
|
||||
|
||||
it.each([
|
||||
"darwin",
|
||||
"linux",
|
||||
] as const)("keeps the HUD non-focusable and out of the taskbar on %s", (platform) => {
|
||||
expect(getHudOverlayTaskbarOptions(platform)).toEqual({
|
||||
skipTaskbar: true,
|
||||
focusable: false,
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,12 @@
|
||||
export interface HudOverlayTaskbarOptions {
|
||||
skipTaskbar: boolean;
|
||||
focusable: boolean;
|
||||
}
|
||||
|
||||
export function getHudOverlayTaskbarOptions(platform: NodeJS.Platform): HudOverlayTaskbarOptions {
|
||||
const showInWindowsTaskbar = platform === "win32";
|
||||
return {
|
||||
skipTaskbar: !showInWindowsTaskbar,
|
||||
focusable: showInWindowsTaskbar,
|
||||
};
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
buildNativeVideoExportArgs,
|
||||
buildTrimmedSourceAudioFilter,
|
||||
createNativeSquircleMaskPgmBuffer,
|
||||
FFMPEG_BT709_VIDEO_COLOR_ARGS,
|
||||
isNativeCudaOutOfMemory,
|
||||
} from "./nativeVideoExport";
|
||||
|
||||
@@ -151,17 +152,15 @@ describe("native static layout command builders", () => {
|
||||
|
||||
expect(args).toContain("-filter_complex");
|
||||
expect(args).toContain(
|
||||
"color=c=0x101010:s=1920x1080:r=60:d=60.000,format=nv12,hwupload_cuda[bg];" +
|
||||
"[0:v]scale_cuda=w=1536:h=864:format=nv12,fps=60[fg];" +
|
||||
"color=c=0x101010:s=1920x1080:r=60:d=60.000,format=nv12,setrange=limited,hwupload_cuda[bg];" +
|
||||
"[0:v]scale_cuda=w=1536:h=864:format=nv12:passthrough=0,hwdownload,format=nv12,scale=in_range=auto:out_range=tv,fps=60,hwupload_cuda[fg];" +
|
||||
"[bg][fg]overlay_cuda=192:108:shortest=0:repeatlast=1:eof_action=repeat,trim=duration=60.000,setpts=PTS-STARTPTS[out]",
|
||||
);
|
||||
expect(args).toContain("h264_nvenc");
|
||||
expect(args).toContain("p1");
|
||||
expect(args).not.toContain("yuv420p");
|
||||
expect(args).toEqual(expect.arrayContaining(["-ss", "120.000", "-t", "60.000"]));
|
||||
expect(args).toEqual(
|
||||
expect.arrayContaining(["-colorspace", "bt709", "-color_range", "tv"]),
|
||||
);
|
||||
expect(args).toEqual(expect.arrayContaining([...FFMPEG_BT709_VIDEO_COLOR_ARGS]));
|
||||
});
|
||||
|
||||
it("converts full-range canvas pixels and tags native H.264 as BT.709 video range", () => {
|
||||
@@ -199,12 +198,13 @@ describe("native static layout command builders", () => {
|
||||
expect(args).toEqual(
|
||||
expect.arrayContaining([
|
||||
"-vf",
|
||||
"scale_cuda=w=1536:h=864:format=nv12:passthrough=0,hwdownload,format=nv12,fps=60,pad=w=1920:h=1080:x=192:y=108:color=0x101010",
|
||||
"scale_cuda=w=1536:h=864:format=nv12:passthrough=0,hwdownload,format=nv12,scale=in_range=auto:out_range=tv,fps=60,pad=w=1920:h=1080:x=192:y=108:color=0x101010",
|
||||
"-map",
|
||||
"0:v:0",
|
||||
"-an",
|
||||
]),
|
||||
);
|
||||
expect(args).toEqual(expect.arrayContaining([...FFMPEG_BT709_VIDEO_COLOR_ARGS]));
|
||||
});
|
||||
|
||||
it("sanitizes unsupported background colors to the safe dark fallback", () => {
|
||||
@@ -214,7 +214,7 @@ describe("native static layout command builders", () => {
|
||||
});
|
||||
|
||||
expect(args).toContain(
|
||||
"scale_cuda=w=1536:h=864:format=nv12:passthrough=0,hwdownload,format=nv12,fps=60,pad=w=1920:h=1080:x=192:y=108:color=0x101010",
|
||||
"scale_cuda=w=1536:h=864:format=nv12:passthrough=0,hwdownload,format=nv12,scale=in_range=auto:out_range=tv,fps=60,pad=w=1920:h=1080:x=192:y=108:color=0x101010",
|
||||
);
|
||||
});
|
||||
|
||||
@@ -262,12 +262,14 @@ describe("native static layout command builders", () => {
|
||||
|
||||
expect(args).toEqual(expect.arrayContaining(["-i", "background.png", "-i", "mask.pgm"]));
|
||||
expect(filterComplex).toContain(
|
||||
"scale_cuda=w=1536:h=864:format=nv12:passthrough=0,hwdownload,format=nv12,fps=60,format=rgba",
|
||||
"scale_cuda=w=1536:h=864:format=nv12:passthrough=0,hwdownload,format=nv12,scale=in_range=auto:out_range=full,fps=60,format=rgba",
|
||||
);
|
||||
expect(filterComplex).toContain("[fgbase][mask]alphamerge[fg]");
|
||||
expect(filterComplex).toContain("overlay=x=192:y=108:format=auto");
|
||||
expect(filterComplex).toContain("scale=in_range=full:out_range=tv,format=yuv420p[out]");
|
||||
expect(args).toContain("h264_nvenc");
|
||||
expect(args).toEqual(expect.arrayContaining(["-pix_fmt", "yuv420p"]));
|
||||
expect(args).toEqual(expect.arrayContaining([...FFMPEG_BT709_VIDEO_COLOR_ARGS]));
|
||||
});
|
||||
|
||||
it("creates an opaque PGM mask for square video corners and a partial mask for radius", () => {
|
||||
|
||||
@@ -20,6 +20,10 @@ export const FFMPEG_BT709_VIDEO_COLOR_ARGS = [
|
||||
"tv",
|
||||
] as const;
|
||||
|
||||
const FFMPEG_AUTO_TO_VIDEO_RANGE_FILTER = "scale=in_range=auto:out_range=tv";
|
||||
const FFMPEG_AUTO_TO_FULL_RANGE_FILTER = "scale=in_range=auto:out_range=full";
|
||||
const FFMPEG_FULL_TO_VIDEO_RANGE_FILTER = "scale=in_range=full:out_range=tv";
|
||||
|
||||
export type NativeExportEncodingMode = "fast" | "balanced" | "quality";
|
||||
|
||||
export type NativeVideoExportAudioMode = "none" | "copy-source" | "trim-source" | "edited-track";
|
||||
@@ -346,7 +350,7 @@ export function buildNativeCudaOverlayStaticLayoutArgs(
|
||||
"-i",
|
||||
config.inputPath,
|
||||
"-filter_complex",
|
||||
`color=c=${backgroundColor}:s=${config.width}x${config.height}:r=${config.frameRate}:d=${durationSec},format=nv12,hwupload_cuda[bg];[0:v]scale_cuda=w=${config.contentWidth}:h=${config.contentHeight}:format=nv12,fps=${config.frameRate}[fg];[bg][fg]overlay_cuda=${config.offsetX}:${config.offsetY}:shortest=0:repeatlast=1:eof_action=repeat,trim=duration=${durationSec},setpts=PTS-STARTPTS[out]`,
|
||||
`color=c=${backgroundColor}:s=${config.width}x${config.height}:r=${config.frameRate}:d=${durationSec},format=nv12,setrange=limited,hwupload_cuda[bg];[0:v]scale_cuda=w=${config.contentWidth}:h=${config.contentHeight}:format=nv12:passthrough=0,hwdownload,format=nv12,${FFMPEG_AUTO_TO_VIDEO_RANGE_FILTER},fps=${config.frameRate},hwupload_cuda[fg];[bg][fg]overlay_cuda=${config.offsetX}:${config.offsetY}:shortest=0:repeatlast=1:eof_action=repeat,trim=duration=${durationSec},setpts=PTS-STARTPTS[out]`,
|
||||
"-map",
|
||||
"[out]",
|
||||
"-an",
|
||||
@@ -378,7 +382,7 @@ export function buildNativeCudaScaleCpuPadStaticLayoutArgs(
|
||||
"-i",
|
||||
config.inputPath,
|
||||
"-vf",
|
||||
`scale_cuda=w=${config.contentWidth}:h=${config.contentHeight}:format=nv12:passthrough=0,hwdownload,format=nv12,fps=${config.frameRate},pad=w=${config.width}:h=${config.height}:x=${config.offsetX}:y=${config.offsetY}:color=${backgroundColor}`,
|
||||
`scale_cuda=w=${config.contentWidth}:h=${config.contentHeight}:format=nv12:passthrough=0,hwdownload,format=nv12,${FFMPEG_AUTO_TO_VIDEO_RANGE_FILTER},fps=${config.frameRate},pad=w=${config.width}:h=${config.height}:x=${config.offsetX}:y=${config.offsetY}:color=${backgroundColor}`,
|
||||
"-map",
|
||||
"0:v:0",
|
||||
"-an",
|
||||
@@ -534,10 +538,10 @@ export function buildNativePrecompositedStaticLayoutArgs(
|
||||
);
|
||||
}
|
||||
|
||||
const foregroundFilter = `[0:v]scale_cuda=w=${config.contentWidth}:h=${config.contentHeight}:format=nv12:passthrough=0,hwdownload,format=nv12,fps=${config.frameRate},format=rgba[fgbase]`;
|
||||
const foregroundFilter = `[0:v]scale_cuda=w=${config.contentWidth}:h=${config.contentHeight}:format=nv12:passthrough=0,hwdownload,format=nv12,${FFMPEG_AUTO_TO_FULL_RANGE_FILTER},fps=${config.frameRate},format=rgba[fgbase]`;
|
||||
const maskFilter = useMask ? ";[2:v]format=gray[mask];[fgbase][mask]alphamerge[fg]" : "";
|
||||
const foregroundLabel = useMask ? "fg" : "fgbase";
|
||||
const filterComplex = `${foregroundFilter}${maskFilter};[1:v]format=rgba[bg];[bg][${foregroundLabel}]overlay=x=${config.offsetX}:y=${config.offsetY}:format=auto,trim=duration=${durationSec},setpts=PTS-STARTPTS,format=yuv420p[out]`;
|
||||
const filterComplex = `${foregroundFilter}${maskFilter};[1:v]format=rgba[bg];[bg][${foregroundLabel}]overlay=x=${config.offsetX}:y=${config.offsetY}:format=auto,trim=duration=${durationSec},setpts=PTS-STARTPTS,${FFMPEG_FULL_TO_VIDEO_RANGE_FILTER},format=yuv420p[out]`;
|
||||
|
||||
args.push(
|
||||
"-filter_complex",
|
||||
|
||||
+9
-3
@@ -10,6 +10,7 @@ import {
|
||||
resizeHudOverlayFallbackBounds,
|
||||
shouldExpandHudOverlayFallback,
|
||||
} from "./hudOverlayBounds";
|
||||
import { getHudOverlayTaskbarOptions } from "./hudOverlayWindowOptions";
|
||||
import { getPackagedRendererBaseUrl } from "./rendererServer";
|
||||
|
||||
const electronWindowsDir = path.dirname(fileURLToPath(import.meta.url));
|
||||
@@ -466,10 +467,9 @@ export function createHudOverlayWindow(): BrowserWindow {
|
||||
alwaysOnTop: true,
|
||||
// The HUD is Recordly's persistent top-level window, so it owns the
|
||||
// Windows taskbar entry while auxiliary overlays stay hidden there.
|
||||
skipTaskbar: process.platform !== "win32",
|
||||
...getHudOverlayTaskbarOptions(process.platform),
|
||||
hasShadow: false,
|
||||
show: false,
|
||||
focusable: false,
|
||||
webPreferences: {
|
||||
preload: path.join(electronWindowsDir, "preload.mjs"),
|
||||
nodeIntegration: false,
|
||||
@@ -484,7 +484,13 @@ export function createHudOverlayWindow(): BrowserWindow {
|
||||
return;
|
||||
}
|
||||
hasShownHudWindow = true;
|
||||
win.show();
|
||||
if (process.platform === "win32") {
|
||||
// A focusable window is required for a Windows taskbar entry, but the
|
||||
// always-on-top HUD must not steal focus when Recordly starts.
|
||||
win.showInactive();
|
||||
} else {
|
||||
win.show();
|
||||
}
|
||||
win.moveTop();
|
||||
if (process.platform === "win32" && isHudOverlayMousePassthroughSupported()) {
|
||||
win.setIgnoreMouseEvents(false);
|
||||
|
||||
@@ -2948,7 +2948,7 @@ const VideoPlayback = forwardRef<VideoPlaybackRef, VideoPlaybackProps>(
|
||||
<video
|
||||
key={resolvedWallpaper}
|
||||
ref={bgVideoRef}
|
||||
className="absolute inset-0 h-full w-full object-cover"
|
||||
className="absolute object-cover"
|
||||
src={resolvedWallpaper}
|
||||
muted
|
||||
loop
|
||||
@@ -2956,6 +2956,8 @@ const VideoPlayback = forwardRef<VideoPlaybackRef, VideoPlaybackProps>(
|
||||
style={{
|
||||
filter: backgroundBlur > 0 ? `blur(${backgroundBlur}px)` : "none",
|
||||
inset: -backgroundBlurOverscan,
|
||||
width: `calc(100% + ${backgroundBlurOverscan * 2}px)`,
|
||||
height: `calc(100% + ${backgroundBlurOverscan * 2}px)`,
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
|
||||
Reference in new issue
Block a user