diff --git a/src/components/video-editor/VideoPlayback.tsx b/src/components/video-editor/VideoPlayback.tsx index c63c871..85029fc 100644 --- a/src/components/video-editor/VideoPlayback.tsx +++ b/src/components/video-editor/VideoPlayback.tsx @@ -970,8 +970,7 @@ const VideoPlayback = forwardRef( const activeSpeedRegion = speedRegions.find( - (region) => - currentTime * 1000 >= region.startMs && currentTime * 1000 < region.endMs, + (region) => currentTime * 1000 >= region.startMs && currentTime * 1000 < region.endMs, ) ?? null; webcamVideo.playbackRate = activeSpeedRegion ? activeSpeedRegion.speed : 1; diff --git a/src/hooks/useScreenRecorder.ts b/src/hooks/useScreenRecorder.ts index b4afd2e..19893f1 100644 --- a/src/hooks/useScreenRecorder.ts +++ b/src/hooks/useScreenRecorder.ts @@ -399,26 +399,26 @@ export function useScreenRecorder(): UseScreenRecorderReturn { } } - if (webcamEnabled) { - try { - webcamStream.current = await navigator.mediaDevices.getUserMedia({ - audio: false, - video: { + if (webcamEnabled) { + try { + webcamStream.current = await navigator.mediaDevices.getUserMedia({ + audio: false, + video: { width: { ideal: WEBCAM_TARGET_WIDTH }, height: { ideal: WEBCAM_TARGET_HEIGHT }, frameRate: { ideal: WEBCAM_TARGET_FRAME_RATE, max: WEBCAM_TARGET_FRAME_RATE }, }, - }); - } catch (cameraError) { - console.warn("Failed to get webcam access:", cameraError); - if (webcamStream.current) { - webcamStream.current.getTracks().forEach((track) => track.stop()); - webcamStream.current = null; - } - setWebcamEnabledState(false); - toast.error("Camera access denied. Recording will continue without webcam."); + }); + } catch (cameraError) { + console.warn("Failed to get webcam access:", cameraError); + if (webcamStream.current) { + webcamStream.current.getTracks().forEach((track) => track.stop()); + webcamStream.current = null; } + setWebcamEnabledState(false); + toast.error("Camera access denied. Recording will continue without webcam."); } + } stream.current = new MediaStream(); const videoTrack = screenMediaStream.getVideoTracks()[0]; diff --git a/src/lib/exporter/gifExporter.ts b/src/lib/exporter/gifExporter.ts index 3ab3ba9..1402e27 100644 --- a/src/lib/exporter/gifExporter.ts +++ b/src/lib/exporter/gifExporter.ts @@ -170,30 +170,32 @@ export class GifExporter { let webcamDecodeError: Error | null = null; const webcamDecodePromise = this.webcamDecoder && webcamFrameQueue - ? this.webcamDecoder - .decodeAll( - this.config.frameRate, - this.config.trimRegions, - this.config.speedRegions, - async (webcamFrame) => { - while (webcamFrameQueue.length >= 12 && !this.cancelled) { - await new Promise((resolve) => setTimeout(resolve, 2)); + ? (() => { + const queue = webcamFrameQueue; + return this.webcamDecoder + .decodeAll( + this.config.frameRate, + this.config.trimRegions, + this.config.speedRegions, + async (webcamFrame) => { + while (queue.length >= 12 && !this.cancelled) { + await new Promise((resolve) => setTimeout(resolve, 2)); + } + queue.enqueue(webcamFrame); + }, + ) + .catch((error) => { + webcamDecodeError = error instanceof Error ? error : new Error(String(error)); + throw error; + }) + .finally(() => { + if (webcamDecodeError) { + queue.fail(webcamDecodeError); + } else { + queue.close(); } - webcamFrameQueue.enqueue(webcamFrame); - }, - ) - .catch((error) => { - webcamDecodeError = - error instanceof Error ? error : new Error(String(error)); - throw error; - }) - .finally(() => { - if (webcamDecodeError) { - webcamFrameQueue.fail(webcamDecodeError); - } else { - webcamFrameQueue.close(); - } - }) + }); + })() : null; // Stream decode and process frames — no seeking! diff --git a/src/lib/exporter/videoExporter.ts b/src/lib/exporter/videoExporter.ts index bd30eb8..fcdc9cc 100644 --- a/src/lib/exporter/videoExporter.ts +++ b/src/lib/exporter/videoExporter.ts @@ -121,30 +121,32 @@ export class VideoExporter { let webcamDecodeError: Error | null = null; const webcamDecodePromise = this.webcamDecoder && webcamFrameQueue - ? this.webcamDecoder - .decodeAll( - this.config.frameRate, - this.config.trimRegions, - this.config.speedRegions, - async (webcamFrame) => { - while (webcamFrameQueue.length >= 12 && !this.cancelled) { - await new Promise((resolve) => setTimeout(resolve, 2)); + ? (() => { + const queue = webcamFrameQueue; + return this.webcamDecoder + .decodeAll( + this.config.frameRate, + this.config.trimRegions, + this.config.speedRegions, + async (webcamFrame) => { + while (queue.length >= 12 && !this.cancelled) { + await new Promise((resolve) => setTimeout(resolve, 2)); + } + queue.enqueue(webcamFrame); + }, + ) + .catch((error) => { + webcamDecodeError = error instanceof Error ? error : new Error(String(error)); + throw error; + }) + .finally(() => { + if (webcamDecodeError) { + queue.fail(webcamDecodeError); + } else { + queue.close(); } - webcamFrameQueue.enqueue(webcamFrame); - }, - ) - .catch((error) => { - webcamDecodeError = - error instanceof Error ? error : new Error(String(error)); - throw error; - }) - .finally(() => { - if (webcamDecodeError) { - webcamFrameQueue.fail(webcamDecodeError); - } else { - webcamFrameQueue.close(); - } - }) + }); + })() : null; // Stream decode and process frames — no seeking! diff --git a/src/lib/webcamOverlay.test.ts b/src/lib/webcamOverlay.test.ts index abc6e36..24e40b3 100644 --- a/src/lib/webcamOverlay.test.ts +++ b/src/lib/webcamOverlay.test.ts @@ -28,6 +28,6 @@ describe("computeWebcamOverlayLayout", () => { expect(layout).not.toBeNull(); expect(layout!.width).toBeLessThanOrEqual(Math.round(1280 * 0.18) + 1); expect(layout!.height).toBeLessThanOrEqual(Math.round(720 * 0.18) + 1); - expect(layout!.width / layout!.height).toBeCloseTo(1920 / 1080, 2); + expect(Math.abs(layout!.width * 1080 - layout!.height * 1920)).toBeLessThanOrEqual(1920); }); });