Skip to content

fix: improve performance on windows and macos by passing canvas direclty to VideoFrame()#448

Open
theopfr wants to merge 3 commits intosiddharthvaddem:mainfrom
theopfr:fix/cpu-readback-only-for-linux
Open

fix: improve performance on windows and macos by passing canvas direclty to VideoFrame()#448
theopfr wants to merge 3 commits intosiddharthvaddem:mainfrom
theopfr:fix/cpu-readback-only-for-linux

Conversation

@theopfr
Copy link
Copy Markdown
Contributor

@theopfr theopfr commented Apr 14, 2026

Pull Request Template

Description

Because of a potential bug on some Linux systems we were not able to pass the canvas directly to VideoFrame(...). There was a workaround implemented to read the raw pixels from the canvas using getImageData() for every single frame which required a CPU readback and reduces performance. After this PR, this workaround is only used on Linux systems, to allow better performance at least for other OS'.

Motivation

Rendering can take quite long. The current CPU readback workaround makes it take even longer and is not necessary for win and darwin systems.

Here is a quick benchmark I did (duration in ms):

Run 1 Run 2 Run 3 Run 4 Run 5 Avg Std
Readback 44951 38267 33148 43556 44927 40970 4619
Direct 31125 28171 29122 30897 28146 29492 1291

Info about the video used:

[VideoExporter] Original duration: 16.211 s
[VideoExporter] Effective duration: 8.8246 s
[VideoExporter] Total frames to export: 530

So for this video about a 30% speed up. I've seen similar speed ups with other videos as well. Additionally, there is this willReadFrequently attribute to canvas.getContext. Since for Linux we do read frequently from the canvas, I set it there to be true.

Type of Change

  • New Feature
  • Bug Fix
  • Refactor / Code Cleanup
  • Documentation Update
  • Other: Performance improvement

Related Issue(s)

Screenshots / Video

Testing

You can time how long a video takes to export with and without the PR. A way to do that is to drop in this snippet:

try {
    const benchmark = async (useReadback: boolean) => {
        const durations: number[] = [];
        let res;
        for (let i = 0; i < 5; i++) {
            const t = performance.now();
            res = await this.exportWithEncoderPreference(useReadback, encoderPreference);
            durations.push(performance.now() - t);
        }
        const avg = durations.reduce((acc, r) => acc + r, 0) / durations.length;
        const std = Math.sqrt(durations.reduce((acc, r) => acc + (r - avg) ** 2, 0) / durations.length);
        console.log(`useReadback=${useReadback} avg=${avg.toFixed(2)}ms std=${std.toFixed(2)}ms`);
        return res!;
    };

    await benchmark(true);
    return await benchmark(false);
}
. . . 

... where this.exportWithEncoderPreference is currently being called and check the console (CTRL+SHIFT+I).

Checklist

  • I have performed a self-review of my code.
  • I have added any necessary screenshots or videos.
  • I have linked related issue(s) and updated the changelog if applicable.

Thank you for contributing!

Summary by CodeRabbit

  • Performance Improvements

    • Exporters now detect the host OS and choose an optimized canvas handling path, improving export speed and reliability.
    • Non-Linux hosts avoid unnecessary CPU readbacks, reducing export overhead and stabilizing rendering performance.
  • Bug Fixes

    • Frame capture and compositing adjusted per-platform to ensure consistent video/GIF output.
    • Reduced timing and color mismatches across operating systems.

…d not necessary for other OS' line win or darwin
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 14, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: cf1135b1-3f1e-4973-967f-188b9b7bd604

📥 Commits

Reviewing files that changed from the base of the PR and between 9ae6247 and 9712eea.

📒 Files selected for processing (3)
  • src/lib/exporter/gifExporter.ts
  • src/lib/exporter/videoExporter.ts
  • src/utils/platformUtils.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/lib/exporter/gifExporter.ts
  • src/utils/platformUtils.ts

📝 Walkthrough

Walkthrough

FrameRenderer and exporter code now accept a runtime platform string and branch compositing and VideoFrame construction on it: Linux paths perform GPU→CPU readback and set willReadFrequently; non‑Linux use the canvas directly with no readback.

Changes

Cohort / File(s) Summary
Frame rendering / compositing
src/lib/exporter/frameRenderer.ts
Added required platform: string to FrameRenderConfig; compute isLinux and set composite context willReadFrequently when true. On Linux use readbackVideoCanvas() as compositing source; otherwise use this.app.canvas.
Video export frame creation
src/lib/exporter/videoExporter.ts
Call await getPlatform() and pass into FrameRenderer. Per-frame VideoFrame creation now branches: Linux builds from CPU readback (getImageData(...).data.buffer with explicit format/dimensions/colorSpace); non‑Linux creates VideoFrame` directly from canvas. Removed unconditional readback.
GIF exporter platform plumbing
src/lib/exporter/gifExporter.ts
Await getPlatform() and include platform in FrameRenderer config; core GIF flow unchanged.
Platform utility export
src/utils/platformUtils.ts
Made getPlatform exported and expanded fallback detection to include "linux" when navigator.platform matches /Linux/.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • siddharthvaddem

Poem

tiny penguins debug at 2am,
the gpu coughs up pixels, the cpu hums,
linux gets a special readback lane — kinda cursed,
canvases keep secrets, frames keep time,
ship it, sleep, repeat.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed Title accurately describes the main change: conditional platform-based canvas handling for performance. However, contains typo 'direclty' instead of 'directly' which reduces polish.
Description check ✅ Passed Description is thorough and well-structured with clear motivation, benchmarks, testing instructions, and relates to actual code changes. Covers problem statement, solution, performance data, and includes a testing snippet.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@theopfr theopfr changed the title fix: improve performance on win and macos by passing canvas direclty to VideoFrame() fix: improve performance on windows and macos by passing canvas direclty to VideoFrame() Apr 14, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/lib/exporter/videoExporter.ts (1)

114-115: nit: thread platform into FrameRenderer instead of doing a second IPC hop.

VideoExporter already resolves the platform here, and FrameRenderer.initialize() fetches it again at Lines 191-192. not a blocker, just kinda cleaner to pass platform/isLinux down in the renderer config so the export path owns the Electron-specific check in one place.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/exporter/videoExporter.ts` around lines 114 - 115, VideoExporter
currently calls window.electronAPI.getPlatform() and then
FrameRenderer.initialize() re-queries the platform; instead, thread the resolved
platform (or a boolean like isLinux) from VideoExporter into the FrameRenderer
configuration so Electron-specific logic is centralized. Update the call site in
VideoExporter where platform is awaited to include that value in the
FrameRenderer config/constructor, and remove the redundant IPC call inside
FrameRenderer.initialize() (or guard it to use the provided platform when
present); reference VideoExporter, FrameRenderer.initialize, platform, and
isLinux when making these changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/lib/exporter/frameRenderer.ts`:
- Around line 191-195: The renderer still does a GPU→CPU readback on non-Linux
because readbackVideoCanvas() (which calls gl.readPixels()) is invoked
unconditionally; update the rendering path to reuse the existing isLinux flag
(from the compositeCtx creation) to gate readbackVideoCanvas() so it only runs
on Linux, and on non-Linux draw this.app.canvas directly into the
compositeCanvas/decorative canvas instead of performing gl.readPixels(); locate
and modify the call site that invokes readbackVideoCanvas() and ensure the new
branch uses the existing isLinux boolean to choose between readbackVideoCanvas()
and drawing this.app.canvas, leaving readbackVideoCanvas() implementation
unchanged.

---

Nitpick comments:
In `@src/lib/exporter/videoExporter.ts`:
- Around line 114-115: VideoExporter currently calls
window.electronAPI.getPlatform() and then FrameRenderer.initialize() re-queries
the platform; instead, thread the resolved platform (or a boolean like isLinux)
from VideoExporter into the FrameRenderer configuration so Electron-specific
logic is centralized. Update the call site in VideoExporter where platform is
awaited to include that value in the FrameRenderer config/constructor, and
remove the redundant IPC call inside FrameRenderer.initialize() (or guard it to
use the provided platform when present); reference VideoExporter,
FrameRenderer.initialize, platform, and isLinux when making these changes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 9a87bc0f-d9d6-4733-b8ee-4477d788efcd

📥 Commits

Reviewing files that changed from the base of the PR and between a6ae0e6 and f2b9437.

📒 Files selected for processing (2)
  • src/lib/exporter/frameRenderer.ts
  • src/lib/exporter/videoExporter.ts

Comment thread src/lib/exporter/frameRenderer.ts Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (3)
src/lib/exporter/videoExporter.ts (2)

533-538: kinda inconsistent: platform detection methods

heads up - getEncoderPreferences() still uses navigator.userAgent to detect Windows, while the new frame export logic uses window.electronAPI.getPlatform(). might be worth harmonizing these at some point for consistency. not blocking since both approaches work, just a bit of tech debt.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/exporter/videoExporter.ts` around lines 533 - 538,
getEncoderPreferences currently detects Windows via navigator.userAgent which is
inconsistent with the rest of the frame export logic that uses
window.electronAPI.getPlatform(); update getEncoderPreferences to call
window.electronAPI.getPlatform() when available (fall back to
navigator.userAgent or a default) and base the Windows check on that result so
the platform detection is unified across the codebase, keeping the existing
return order for ["prefer-software","prefer-hardware"] on Windows and the
opposite otherwise.

114-115: same deal: getPlatform() outside try-catch

same issue as gifExporter - if this throws, it won't be caught. consistency is nice but both files could be slightly more defensive here.

suggested fix
 private async exportWithEncoderPreference(
   encoderPreference: HardwareAcceleration,
 ): Promise<ExportResult> {
   let webcamFrameQueue: AsyncVideoFrameQueue | null = null;
   let stopWebcamDecode = false;
   let webcamDecodeError: Error | null = null;
   let webcamDecodePromise: Promise<void> | null = null;
   let webcamDecoder: StreamingVideoDecoder | null = null;

   this.cleanup();
   this.cancelled = false;
   this.fatalEncoderError = null;

-  const platform = await window.electronAPI.getPlatform();
-
   try {
+    const platform = await window.electronAPI.getPlatform();
+
     const streamingDecoder = new StreamingVideoDecoder();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/exporter/videoExporter.ts` around lines 114 - 115, The call to
window.electronAPI.getPlatform() is currently outside any try-catch, so if it
throws the exception won't be handled; move or re-invoke this call inside the
existing try-catch block that surrounds the export logic (or add a small
try-catch specifically around the const platform = await
window.electronAPI.getPlatform() line) so errors from getPlatform() are caught
and logged/handled consistently with the rest of the export flow (reference the
const platform assignment and the surrounding export routine in
videoExporter.ts).
src/lib/exporter/gifExporter.ts (1)

118-120: lowkey risky: getPlatform() called outside try-catch

if window.electronAPI is undefined or the call throws for some reason, the error won't be caught by the try block starting at line 121. probably fine in practice since this is an electron app, but moving it inside the try block would be a bit more defensive.

suggested fix
 async export(): Promise<ExportResult> {
   let webcamFrameQueue: AsyncVideoFrameQueue | null = null;
-
-  const platform = await window.electronAPI.getPlatform();
-
   try {
+    const platform = await window.electronAPI.getPlatform();
+
     this.cleanup();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/exporter/gifExporter.ts` around lines 118 - 120, Move the call to
window.electronAPI.getPlatform() inside the existing try/catch so failures are
handled; specifically, update the code that assigns const platform = await
window.electronAPI.getPlatform() to be executed within the try block (or wrap
that single call in its own try/catch) so any thrown error from
window.electronAPI or getPlatform is caught by the existing error handling in
the gifExporter logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/lib/exporter/gifExporter.ts`:
- Around line 118-120: Move the call to window.electronAPI.getPlatform() inside
the existing try/catch so failures are handled; specifically, update the code
that assigns const platform = await window.electronAPI.getPlatform() to be
executed within the try block (or wrap that single call in its own try/catch) so
any thrown error from window.electronAPI or getPlatform is caught by the
existing error handling in the gifExporter logic.

In `@src/lib/exporter/videoExporter.ts`:
- Around line 533-538: getEncoderPreferences currently detects Windows via
navigator.userAgent which is inconsistent with the rest of the frame export
logic that uses window.electronAPI.getPlatform(); update getEncoderPreferences
to call window.electronAPI.getPlatform() when available (fall back to
navigator.userAgent or a default) and base the Windows check on that result so
the platform detection is unified across the codebase, keeping the existing
return order for ["prefer-software","prefer-hardware"] on Windows and the
opposite otherwise.
- Around line 114-115: The call to window.electronAPI.getPlatform() is currently
outside any try-catch, so if it throws the exception won't be handled; move or
re-invoke this call inside the existing try-catch block that surrounds the
export logic (or add a small try-catch specifically around the const platform =
await window.electronAPI.getPlatform() line) so errors from getPlatform() are
caught and logged/handled consistently with the rest of the export flow
(reference the const platform assignment and the surrounding export routine in
videoExporter.ts).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b0e6c0c4-e546-462a-b20f-2c3f03b3dbd6

📥 Commits

Reviewing files that changed from the base of the PR and between f2b9437 and bd090f0.

📒 Files selected for processing (3)
  • src/lib/exporter/frameRenderer.ts
  • src/lib/exporter/gifExporter.ts
  • src/lib/exporter/videoExporter.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/lib/exporter/frameRenderer.ts

…back also for canvas composition for non-linux
@theopfr theopfr force-pushed the fix/cpu-readback-only-for-linux branch from bd090f0 to 3445810 Compare April 15, 2026 09:34
@siddharthvaddem
Copy link
Copy Markdown
Owner

@theopfr can you take a look at the ci failures

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/utils/platformUtils.ts (1)

14-21: ⚠️ Potential issue | 🟡 Minor

fallback doesn't detect linux, which could cause the readback workaround to be skipped

if electronAPI.getPlatform() fails (e.g., during browser-based dev/testing on linux), the fallback only checks for macOS and defaults to "win32". this means linux users in fallback mode would hit the direct VideoFrame(canvas) path, potentially triggering the GPU shared-image bug the PR is trying to work around.

might want to add linux detection here too:

🛠️ suggested fix
 	} catch (error) {
 		console.warn("Failed to get platform from Electron, falling back to navigator:", error);
 		// Fallback for development/testing
 		let fallbackPlatform = "win32";
-		if (typeof navigator !== "undefined" && /Mac|iPhone|iPad|iPod/.test(navigator.platform)) {
+		if (typeof navigator !== "undefined") {
+			if (/Linux/.test(navigator.platform)) {
+				fallbackPlatform = "linux";
+			} else if (/Mac|iPhone|iPad|iPod/.test(navigator.platform)) {
 				fallbackPlatform = "darwin";
+			}
 		}
 		cachedPlatform = fallbackPlatform;
 		return fallbackPlatform;
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/platformUtils.ts` around lines 14 - 21, The fallback logic in the
getPlatform fallback block (where cachedPlatform is set) only detects macOS and
otherwise defaults to "win32", which misses Linux; update that block in the
function that calls electronAPI.getPlatform() (referencing cachedPlatform and
the existing navigator check) to also detect Linux (e.g., check
navigator.platform or navigator.userAgent for "Linux" or "X11") and set
fallbackPlatform to "linux" when matched so Linux clients use the correct
readback workaround instead of defaulting to "win32".
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/lib/exporter/videoExporter.ts`:
- Around line 248-263: The code assumes a non-null 2D context by calling
canvas.getContext("2d")! before using getImageData which can throw if
readbackVideoCanvas() returned a WebGL canvas; update the block around
canvas.getContext("2d") in videoExporter.ts to check for a valid 2D context
(e.g., const canvasCtx = canvas.getContext("2d"); if (!canvasCtx) throw new
Error("Expected 2D canvas context for video readback")) before calling
getImageData and creating the VideoFrame, or alternatively ensure the canvas
returned by readbackVideoCanvas() is 2D-compatible earlier (reference
readbackVideoCanvas(), getContext("2d"), and the VideoFrame creation block).

---

Outside diff comments:
In `@src/utils/platformUtils.ts`:
- Around line 14-21: The fallback logic in the getPlatform fallback block (where
cachedPlatform is set) only detects macOS and otherwise defaults to "win32",
which misses Linux; update that block in the function that calls
electronAPI.getPlatform() (referencing cachedPlatform and the existing navigator
check) to also detect Linux (e.g., check navigator.platform or
navigator.userAgent for "Linux" or "X11") and set fallbackPlatform to "linux"
when matched so Linux clients use the correct readback workaround instead of
defaulting to "win32".
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 85c7de26-0106-40e5-8c3f-d377cb32a2c5

📥 Commits

Reviewing files that changed from the base of the PR and between 3445810 and 9ae6247.

📒 Files selected for processing (3)
  • src/lib/exporter/gifExporter.ts
  • src/lib/exporter/videoExporter.ts
  • src/utils/platformUtils.ts

Comment on lines +248 to +263
if (platform === "linux") {
const canvasCtx = canvas.getContext("2d")!;
const imageData = canvasCtx.getImageData(0, 0, canvas.width, canvas.height);
exportFrame = new VideoFrame(imageData.data.buffer, {
format: "RGBA",
codedWidth: canvas.width,
codedHeight: canvas.height,
timestamp,
duration: frameDuration,
colorSpace: {
primaries: "bt709",
transfer: "iec61966-2-1",
matrix: "rgb",
fullRange: true,
},
});
Copy link
Copy Markdown

@coderabbitai coderabbitai bot Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check how FrameRenderer acquires/uses the canvas context
ast-grep --pattern $'getContext($$$)'

Repository: siddharthvaddem/openscreen

Length of output: 52


🏁 Script executed:

# Find FrameRenderer class definition
rg "class FrameRenderer" -A 5

Repository: siddharthvaddem/openscreen

Length of output: 548


🏁 Script executed:

# Search for readbackVideoCanvas function mentioned in the summary
rg "readbackVideoCanvas" -B 2 -A 10

Repository: siddharthvaddem/openscreen

Length of output: 1810


🏁 Script executed:

# Also look for where canvas context is initialized in videoExporter
rg "getContext" -B 2 -A 2

Repository: siddharthvaddem/openscreen

Length of output: 3212


non-null assertion on getContext("2d") is risky

the canvas from readbackVideoCanvas() could be a WebGL canvas if the raster canvas fails to initialize or isn't available. in that case, getContext("2d") returns null, and the ! blows up on the next line when calling getImageData().

even though FrameRenderer throws if rasterCtx initialization fails, the fallback to glCanvas in readbackVideoCanvas() means the 2D context isn't guaranteed. either:

  • add a guard: const canvasCtx = canvas.getContext("2d"); if (!canvasCtx) throw new Error(...)
  • or ensure the canvas is always 2D-compatible before this point
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/exporter/videoExporter.ts` around lines 248 - 263, The code assumes a
non-null 2D context by calling canvas.getContext("2d")! before using
getImageData which can throw if readbackVideoCanvas() returned a WebGL canvas;
update the block around canvas.getContext("2d") in videoExporter.ts to check for
a valid 2D context (e.g., const canvasCtx = canvas.getContext("2d"); if
(!canvasCtx) throw new Error("Expected 2D canvas context for video readback"))
before calling getImageData and creating the VideoFrame, or alternatively ensure
the canvas returned by readbackVideoCanvas() is 2D-compatible earlier (reference
readbackVideoCanvas(), getContext("2d"), and the VideoFrame creation block).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unrelated

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@theopfr, fair point — I misread the canvas source. The canvas here comes from renderer.getCanvas() which is the composite output canvas (always 2D), not the result of readbackVideoCanvas(). The non-null assertion concern doesn't apply. My bad!


🧠 Learnings used
Learnt from: theodorpeifer
Repo: siddharthvaddem/openscreen PR: 414
File: src/lib/exporter/streamingDecoder.ts:249-251
Timestamp: 2026-04-14T20:54:11.170Z
Learning: In `src/lib/exporter/streamingDecoder.ts` (TypeScript), `getExportMetrics()` intentionally computes `totalFrames` with a slight over-estimate (using `Math.ceil` with `EPSILON_SEC` subtracted per segment). This is a deliberate design trade-off: since metrics are computed before rendering starts, perfect prediction is impossible. A slightly high `totalFrames` (progress bar stops just below 100%) is acceptable and preferred over under-counting (progress bar exceeds 100%, the original bug).

Learnt from: zoharbabin
Repo: siddharthvaddem/openscreen PR: 415
File: src/components/video-editor/KalturaBrowseDialog.tsx:102-106
Timestamp: 2026-04-11T01:41:30.163Z
Learning: In `src/components/video-editor/KalturaBrowseDialog.tsx` and `electron/windows.ts`, the `createKalturaBrowseWindow` function creates a BrowserWindow with the full preload/electronAPI bridge attached. A remote Kaltura CDN ESM module is dynamically imported (`await import(loaderUrl)`) in this window. The author is aware of the security concern and has noted a future hardening plan to migrate to a `<webview>` tag or sandboxed BrowserWindow with no preload. `contextIsolation: true` is in place but does not prevent remote code from accessing `window.electronAPI`.

Learnt from: michTheBrandofficial
Repo: siddharthvaddem/openscreen PR: 450
File: src/components/launch/SourceSelector.tsx:68-68
Timestamp: 2026-04-15T13:59:58.679Z
Learning: In this codebase, Tailwind is configured such that `duration-*` classes (e.g., `duration-500`) produce both `transition-duration` and `animation-duration` rules. As a result, `duration-*` used together with `animate-*` utilities (e.g., `animate-spin`) is effective here and sets the animation duration (e.g., to 500ms). Do not flag occurrences of `duration-*` alongside `animate-*` as ineffective.

@theopfr theopfr force-pushed the fix/cpu-readback-only-for-linux branch from 9ae6247 to 9712eea Compare April 16, 2026 15:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants