Guarantee camera pictureSize is negotiated before countdown starts to prevent fallback to massive memory-crashing resolutions
This commit is contained in:
@@ -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}
|
||||
/>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user