Improve screencast performance: lower defaults, fps cap, drop mousemove flood

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-06-27 11:09:16 +08:00
parent b9ee546bc1
commit a409f35e6c
7 changed files with 116 additions and 41 deletions
+24 -7
View File
@@ -7,7 +7,13 @@ from typing import Any, Optional
from playwright.async_api import Browser, BrowserContext, Page, Playwright, async_playwright
from app.security import get_idle_timeout, get_screencast_quality, get_viewport_size
from app.security import (
get_idle_timeout,
get_screencast_max_fps,
get_screencast_nth_frame,
get_screencast_quality,
get_viewport_size,
)
@dataclass
@@ -25,8 +31,9 @@ class BrowserSession:
screencast_task: Optional[asyncio.Task] = None
idle_task: Optional[asyncio.Task] = None
closed: bool = False
viewport_width: int = 1280
viewport_height: int = 720
viewport_width: int = 1024
viewport_height: int = 576
last_frame_sent_at: float = 0.0
class BrowserManager:
@@ -53,7 +60,6 @@ class BrowserManager:
"--no-sandbox",
"--disable-setuid-sandbox",
"--disable-dev-shm-usage",
"--disable-gpu",
],
)
context = await browser.new_context(
@@ -143,7 +149,7 @@ class BrowserManager:
return session.url
def subscribe(self, session: BrowserSession) -> asyncio.Queue:
queue: asyncio.Queue = asyncio.Queue(maxsize=8)
queue: asyncio.Queue = asyncio.Queue(maxsize=2)
session.subscribers.add(queue)
return queue
@@ -173,10 +179,13 @@ class BrowserManager:
await self._broadcast(session, {"type": "url_update", "url": session.url})
async def _run_screencast(self, session: BrowserSession, quality: int) -> None:
nth_frame = get_screencast_nth_frame()
min_frame_interval = 1.0 / get_screencast_max_fps()
async def on_screencast_frame(params: dict) -> None:
if session.closed:
return
data = params.get("data", "")
session_id = params.get("sessionId")
try:
await session.cdp.send(
@@ -184,10 +193,18 @@ class BrowserManager:
)
except Exception:
return
now = time.time()
if now - session.last_frame_sent_at < min_frame_interval:
return
data = params.get("data", "")
try:
frame_bytes = base64.b64decode(data)
except Exception:
return
session.last_frame_sent_at = now
await self._broadcast(
session,
{
@@ -211,7 +228,7 @@ class BrowserManager:
"quality": quality,
"maxWidth": session.viewport_width,
"maxHeight": session.viewport_height,
"everyNthFrame": 1,
"everyNthFrame": nth_frame,
},
)
-3
View File
@@ -29,9 +29,6 @@ async def handle_input(
return {"type": "ack", "action": action}
if action == "mousemove":
x = float(payload.get("x", 0))
y = float(payload.get("y", 0))
await page.mouse.move(x, y)
return None
if action == "wheel":
+11 -3
View File
@@ -83,11 +83,19 @@ def get_idle_timeout() -> int:
def get_viewport_size() -> tuple[int, int]:
width = max(800, int(os.getenv("VIEWPORT_WIDTH", "1280")))
height = max(600, int(os.getenv("VIEWPORT_HEIGHT", "720")))
width = max(800, int(os.getenv("VIEWPORT_WIDTH", "1024")))
height = max(600, int(os.getenv("VIEWPORT_HEIGHT", "576")))
return width, height
def get_screencast_quality() -> int:
quality = int(os.getenv("SCREENCAST_QUALITY", "80"))
quality = int(os.getenv("SCREENCAST_QUALITY", "55"))
return min(100, max(10, quality))
def get_screencast_nth_frame() -> int:
return max(1, int(os.getenv("SCREENCAST_EVERY_NTH_FRAME", "2")))
def get_screencast_max_fps() -> int:
return min(30, max(5, int(os.getenv("SCREENCAST_MAX_FPS", "15"))))