From 4580d79f5e37ca8e3da647fcf6ce82ad3a83fc96 Mon Sep 17 00:00:00 2001 From: livepeer-tessa Date: Fri, 20 Feb 2026 22:47:02 +0000 Subject: [PATCH 1/2] fix: reduce WebGL ReadPixels GPU stalls in video source capture Replace requestAnimationFrame + captureStream(fps) with setInterval + captureStream(0) + manual requestFrame(). This ensures canvas drawing only happens at the target FPS rate, eliminating the mismatch between ~60fps draws and lower-rate captures that caused 'GPU stall due to ReadPixels' warnings. Fixes #509 --- frontend/src/hooks/useVideoSource.ts | 41 ++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/frontend/src/hooks/useVideoSource.ts b/frontend/src/hooks/useVideoSource.ts index e2a0a38d7..e6997f00c 100644 --- a/frontend/src/hooks/useVideoSource.ts +++ b/frontend/src/hooks/useVideoSource.ts @@ -83,8 +83,23 @@ export function useVideoSource(props?: UseVideoSourceProps) { video .play() .then(() => { - // Draw video frame to canvas at original resolution - const drawFrame = () => { + // Use captureStream(0) for manual frame capture to avoid + // GPU stalls from automatic ReadPixels at mismatched rates. + // See: https://github.com/daydreamlive/scope/issues/509 + const stream = canvas.captureStream(0); + const videoTrack = stream.getVideoTracks()[0]; + + // Draw video frames to canvas at the target FPS rate only, + // then manually request a frame capture. This avoids the + // "GPU stall due to ReadPixels" warning caused by drawing + // at ~60fps via requestAnimationFrame while capturing at a + // lower rate. + const intervalMs = 1000 / fps; + const intervalId = setInterval(() => { + if (video.paused || video.ended) { + clearInterval(intervalId); + return; + } ctx.drawImage( video, 0, @@ -92,14 +107,24 @@ export function useVideoSource(props?: UseVideoSourceProps) { detectedResolution.width, detectedResolution.height ); - if (!video.paused && !video.ended) { - requestAnimationFrame(drawFrame); + // Manually request frame capture from the stream + if ( + videoTrack && + "requestFrame" in videoTrack && + videoTrack.readyState === "live" + ) { + ( + videoTrack as MediaStreamTrack & { + requestFrame: () => void; + } + ).requestFrame(); } - }; - drawFrame(); + }, intervalMs); - // Capture stream from canvas at original resolution - const stream = canvas.captureStream(fps); + // Clean up interval when track ends + videoTrack?.addEventListener("ended", () => { + clearInterval(intervalId); + }); resolve({ stream, resolution: detectedResolution }); }) From bd040091b35c75786fad6c9297cdafea5d430a63 Mon Sep 17 00:00:00 2001 From: livepeer-tessa Date: Fri, 20 Feb 2026 22:48:06 +0000 Subject: [PATCH 2/2] fix: strip .git suffix from plugin package_spec URLs (#508) The web platform parses package_spec to construct GitHub API URLs (e.g., api.github.com/repos/owner/repo). When git_url contains a .git suffix, the API call fails with 404. Strip the suffix when building package_spec since it's not needed for pip/uv operations either. --- src/scope/core/plugins/manager.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/scope/core/plugins/manager.py b/src/scope/core/plugins/manager.py index 5605893ff..8d1ac0294 100644 --- a/src/scope/core/plugins/manager.py +++ b/src/scope/core/plugins/manager.py @@ -660,8 +660,10 @@ def _list_plugins_sync(self) -> list[dict[str, Any]]: ) # For git packages, use the git URL; for PyPI, use package name + # Strip .git suffix - not needed for pip/uv and causes issues + # when web platform parses the URL for GitHub API calls (#508) package_spec = ( - f"git+{git_url}" + f"git+{git_url.removesuffix('.git')}" if source == "git" and git_url else package_name )