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

Method startCaptureStream

app/src/utils/tauriStreamCapture.ts:878–1266  ·  view source on GitHub ↗

* Start capture with channel-based streaming. * Works on both desktop and iOS - frames are pushed from Rust as they arrive. * * On desktop: Shows selector window, waits for target selection, then starts xcap with channel * On iOS: Triggers ReplayKit picker, then HTTP-received frames are

(targetId?: string)

Source from the content-addressed store, hash-verified

876 * Returns MediaStreams for display and a cleanup function.
877 */
878 async startCaptureStream(targetId?: string): Promise<CaptureStreamResult> {
879 if (!isTauri()) {
880 throw new Error('Screen capture only available in Tauri');
881 }
882
883 Logger.info("TAURI_STREAM", `Starting channel-based capture stream`);
884
885 // On desktop, show selector and wait for target selection (unless a target is provided
886 // explicitly or was pre-seated via setPreselectedTarget).
887 let selectedTargetId: string | undefined = targetId ?? this.preselectedTargetId ?? undefined;
888 this.preselectedTargetId = null; // consume once
889 if (isDesktop() && !selectedTargetId) {
890 const selected = await this.waitForTargetSelection();
891 if (!selected) {
892 throw new Error('Screen capture cancelled by user');
893 }
894 selectedTargetId = selected;
895 }
896
897 Logger.info("TAURI_STREAM", `Starting capture with target: ${selectedTargetId || 'default'}`);
898
899 // Clean canvas (for AI/recording/preview). The PiP canvas only exists on mobile,
900 // where the status/timer overlay is baked into a separate stream. On desktop the
901 // overlay never draws (it's mobile-only), so a PiP canvas would be a pixel-identical
902 // copy of clean — a wasted per-frame drawImage plus a second canvas.captureStream()
903 // read-back. So on desktop we skip it and reuse the clean stream for the with-pip slot.
904 const usePip = !isDesktop();
905
906 const canvasClean = document.createElement('canvas');
907 // Initial size - will be adapted to actual frame dimensions
908 canvasClean.width = 1920;
909 canvasClean.height = 1080;
910 const ctxClean = canvasClean.getContext('2d');
911 if (!ctxClean) {
912 throw new Error('Failed to create canvas context');
913 }
914
915 const canvasPip = usePip ? document.createElement('canvas') : null;
916 const ctxPip = canvasPip ? canvasPip.getContext('2d') : null;
917 if (canvasPip) {
918 canvasPip.width = 1920;
919 canvasPip.height = 1080;
920 if (!ctxPip) {
921 throw new Error('Failed to create PiP canvas context');
922 }
923 }
924
925 // Create MediaStreams from canvases
926 const cleanStream = canvasClean.captureStream(30);
927 const pipStream = canvasPip ? canvasPip.captureStream(30) : cleanStream;
928
929 let canvasSizeInitialized = false;
930 let frameCount = 0;
931 let isActive = true;
932
933 // Create channels to receive frames and audio from Rust
934 const frameChannel = new Channel<FrameData>();
935 const audioChannel = new Channel<AudioData>();

Callers

nothing calls this directly

Calls 13

pushCaptureConfigMethod · 0.95
stopCaptureMethod · 0.95
getLatestBase64FrameMethod · 0.95
isTauriFunction · 0.90
isDesktopFunction · 0.90
decodeBase64PCMFunction · 0.90
infoMethod · 0.80
warnMethod · 0.80
pcmCallbackMethod · 0.80
errorMethod · 0.80
debugMethod · 0.45

Tested by

no test coverage detected