MCPcopy Create free account
hub / github.com/Roy3838/Observer / captureFrame

Method captureFrame

app/src/utils/streamManager.ts:386–467  ·  view source on GitHub ↗

* Capture a frame from a video stream. Returns base64 PNG or null. * * For Tauri screen capture: uses raw frame bytes from the channel directly. * This bypasses the canvas-backed video element which can freeze when iOS * backgrounds the app. The broadcast extension continues sending fram

(
    streamType: 'camera' | 'screen',
    agentId?: string
  )

Source from the content-addressed store, hash-verified

384 * For camera/browser: uses persistent video elements for instant capture.
385 */
386 public async captureFrame(
387 streamType: 'camera' | 'screen',
388 agentId?: string
389 ): Promise<string | null> {
390 // For Tauri screen capture, use raw frame bytes from channel
391 // This is more reliable than canvas-backed video element (especially on iOS)
392 if (!isWeb() && streamType === 'screen') {
393 const rawFrame = tauriStreamCapture.getLatestBase64Frame();
394 if (!rawFrame) {
395 Logger.warn("StreamManager", "Cannot capture screen: no raw frame available from channel");
396 return null;
397 }
398
399 const crop = agentId ? getAgentCrop(agentId, streamType) : null;
400
401 // If no crop, return raw frame directly (more efficient - no re-encoding)
402 if (!crop) {
403 return rawFrame;
404 }
405
406 // Apply crop by decoding, cropping, and re-encoding.
407 // crop is normalized (0–1); resolve it against the real frame pixels here.
408 try {
409 const blob = await fetch(`data:image/jpeg;base64,${rawFrame}`).then(r => r.blob());
410 const bitmap = await createImageBitmap(blob);
411
412 const px = resolveCrop(crop, bitmap.width, bitmap.height);
413 const canvas = document.createElement('canvas');
414 canvas.width = px.width;
415 canvas.height = px.height;
416 const ctx = canvas.getContext('2d');
417 if (!ctx) {
418 bitmap.close();
419 return rawFrame; // Fallback to uncropped
420 }
421
422 ctx.drawImage(bitmap, px.x, px.y, px.width, px.height, 0, 0, canvas.width, canvas.height);
423 bitmap.close();
424
425 return canvas.toDataURL('image/png').split(',')[1];
426 } catch (e) {
427 Logger.warn("StreamManager", `Failed to apply crop to raw frame: ${e}`);
428 return rawFrame; // Fallback to uncropped
429 }
430 }
431
432 // For camera or browser, use persistent video element
433 const video = streamType === 'camera'
434 ? this.cameraVideoElement
435 : this.screenVideoElement;
436
437 if (!video || video.readyState < 2 || video.videoWidth === 0) {
438 Logger.warn("StreamManager", `Cannot capture ${streamType}: video not ready`);
439 return null;
440 }
441
442 const canvas = document.createElement('canvas');
443 const crop = agentId ? getAgentCrop(agentId, streamType) : null;

Callers 3

captureFrameAndOCRFunction · 0.80
captureCameraImageFunction · 0.80
captureScreenImageFunction · 0.80

Calls 5

isWebFunction · 0.90
getAgentCropFunction · 0.90
resolveCropFunction · 0.85
getLatestBase64FrameMethod · 0.80
warnMethod · 0.80

Tested by

no test coverage detected