Guarantee camera pictureSize is negotiated before countdown starts to prevent fallback to massive memory-crashing resolutions

This commit is contained in:
2026-05-31 21:18:11 +02:00
parent dbd6fd1bbf
commit d6a3d4651b
2 changed files with 39 additions and 24 deletions
+22 -11
View File
@@ -94,6 +94,7 @@ export default function CameraScreen({
const usbCameraRef = useRef<UsbCameraRef>(null);
const expoCameraRef = useRef<any>(null);
const burstTimerRef = useRef<any>(null);
const countdownTimerRef = useRef<any>(null);
const isCancelledRef = useRef<boolean>(false);
// Reanimated countdown pulse animation
@@ -159,11 +160,12 @@ export default function CameraScreen({
useEffect(() => {
return () => {
if (burstTimerRef.current) clearTimeout(burstTimerRef.current);
if (countdownTimerRef.current) clearTimeout(countdownTimerRef.current);
};
}, []);
// Set safe picture size to avoid OOM
const handleCameraReady = async () => {
const handleCameraReady = useCallback(async () => {
if (expoCameraRef.current) {
try {
const sizes = await expoCameraRef.current.getAvailablePictureSizes();
@@ -188,18 +190,24 @@ export default function CameraScreen({
setPictureSize(optimal.size);
} else if (parsedSizes.length > 0) {
setPictureSize(parsedSizes[parsedSizes.length - 1].size);
} else {
setPictureSize('fallback');
}
} else {
setPictureSize('fallback');
}
} catch (e) {
console.warn('Failed to fetch picture sizes:', e);
setPictureSize('fallback');
}
}
};
}, []);
// Cancel capture process
const handleLocalCancel = async () => {
isCancelledRef.current = true;
if (burstTimerRef.current) clearTimeout(burstTimerRef.current);
if (countdownTimerRef.current) clearTimeout(countdownTimerRef.current);
// Clean up any temp files from a partially completed burst
for (const uri of burstUris) {
@@ -229,14 +237,20 @@ export default function CameraScreen({
return;
}
// Delay start until safe picture size is negotiated (or fallback is set)
// Only applies to built-in camera, USB camera ignores pictureSize
if (pictureSize === undefined && !isUsbConnected) {
console.log('Waiting for pictureSize negotiation...');
return;
}
startCountdown();
return () => {};
}, [countdownDuration, isIdle]);
}, [countdownDuration, isIdle, pictureSize, isUsbConnected]);
const startCountdown = () => {
isCancelledRef.current = false;
let timer: any;
let count = countdownDuration;
setCountdown(count);
@@ -248,20 +262,17 @@ export default function CameraScreen({
if (count > 1) {
count -= 1;
setCountdown(count);
timer = setTimeout(runTimer, 1000);
countdownTimerRef.current = setTimeout(runTimer, 1000);
} else if (count === 1) {
setCountdown('smile');
setIsCapturing(true);
timer = setTimeout(() => {
countdownTimerRef.current = setTimeout(() => {
capture();
}, 800);
}
};
timer = setTimeout(runTimer, 1000);
// Store timer for cleanup
burstTimerRef.current = timer;
countdownTimerRef.current = setTimeout(runTimer, 1000);
};
// 4. Capture photo function
@@ -552,7 +563,7 @@ export default function CameraScreen({
ref={expoCameraRef}
style={styles.cameraPreview}
facing="front"
pictureSize={pictureSize}
pictureSize={pictureSize === 'fallback' ? undefined : pictureSize}
onCameraReady={handleCameraReady}
/>
)}