diff --git a/package/android/src/main/java/com/mrousavy/camera/core/CameraError.kt b/package/android/src/main/java/com/mrousavy/camera/core/CameraError.kt index 70dc1ed..c6bb6c2 100644 --- a/package/android/src/main/java/com/mrousavy/camera/core/CameraError.kt +++ b/package/android/src/main/java/com/mrousavy/camera/core/CameraError.kt @@ -113,6 +113,16 @@ class RecordingInProgressError : "recording-in-progress", "There is already an active video recording in progress! Did you call startRecording() twice?" ) +class FrameInvalidError : + CameraError( + "capture", + "frame-invalid", + "Trying to access an already closed Frame! " + + "Are you trying to access the Image data outside of a Frame Processor's lifetime?\n" + + "- If you want to use `console.log(frame)`, use `console.log(frame.toString())` instead.\n" + + "- If you want to do async processing, use `runAsync(...)` instead.\n" + + "- If you want to use runOnJS, increment it's ref-count: `frame.incrementRefCount()`" + ) class CodeTypeNotSupportedError(codeType: String) : CameraError( diff --git a/package/android/src/main/java/com/mrousavy/camera/frameprocessor/Frame.java b/package/android/src/main/java/com/mrousavy/camera/frameprocessor/Frame.java index 763b9cc..c8f6ecf 100644 --- a/package/android/src/main/java/com/mrousavy/camera/frameprocessor/Frame.java +++ b/package/android/src/main/java/com/mrousavy/camera/frameprocessor/Frame.java @@ -4,6 +4,7 @@ import android.hardware.HardwareBuffer; import android.media.Image; import android.os.Build; import com.facebook.proguard.annotations.DoNotStrip; +import com.mrousavy.camera.core.FrameInvalidError; import com.mrousavy.camera.core.HardwareBuffersNotAvailableError; import com.mrousavy.camera.types.PixelFormat; import com.mrousavy.camera.types.Orientation; @@ -23,42 +24,17 @@ public class Frame { this.isMirrored = isMirrored; } - public Image getImage() { - synchronized (this) { - Image img = image; - if (!getIsImageValid(img)) { - throw new RuntimeException("Frame is already closed! " + - "Are you trying to access the Image data outside of a Frame Processor's lifetime?\n" + - "- If you want to use `console.log(frame)`, use `console.log(frame.toString())` instead.\n" + - "- If you want to do async processing, use `runAsync(...)` instead.\n" + - "- If you want to use runOnJS, increment it's ref-count: `frame.incrementRefCount()`"); - } - return img; + private void assertIsValid() throws FrameInvalidError { + if (!getIsImageValid(image)) { + throw new FrameInvalidError(); } } - @SuppressWarnings("unused") - @DoNotStrip - public int getWidth() { - return getImage().getWidth(); - } - - @SuppressWarnings("unused") - @DoNotStrip - public int getHeight() { - return getImage().getHeight(); - } - - @SuppressWarnings("unused") - @DoNotStrip - public boolean getIsValid() { - return getIsImageValid(getImage()); - } - - private boolean getIsImageValid(Image image) { + private synchronized boolean getIsImageValid(Image image) { + if (refCount <= 0) return false; try { // will throw an exception if the image is already closed - synchronized (this) { image.getFormat(); } + image.getFormat(); // no exception thrown, image must still be valid. return true; } catch (IllegalStateException e) { @@ -67,78 +43,104 @@ public class Frame { } } + public synchronized Image getImage() { + return image; + } + @SuppressWarnings("unused") @DoNotStrip - public boolean getIsMirrored() { + public synchronized int getWidth() throws FrameInvalidError { + assertIsValid(); + return image.getWidth(); + } + + @SuppressWarnings("unused") + @DoNotStrip + public synchronized int getHeight() throws FrameInvalidError { + assertIsValid(); + return image.getHeight(); + } + + @SuppressWarnings("unused") + @DoNotStrip + public synchronized boolean getIsValid() throws FrameInvalidError { + assertIsValid(); + return getIsImageValid(image); + } + + @SuppressWarnings("unused") + @DoNotStrip + public synchronized boolean getIsMirrored() throws FrameInvalidError { + assertIsValid(); return isMirrored; } @SuppressWarnings("unused") @DoNotStrip - public long getTimestamp() { + public synchronized long getTimestamp() throws FrameInvalidError { + assertIsValid(); return timestamp; } @SuppressWarnings("unused") @DoNotStrip - public Orientation getOrientation() { + public synchronized Orientation getOrientation() throws FrameInvalidError { + assertIsValid(); return orientation; } @SuppressWarnings("unused") @DoNotStrip - public PixelFormat getPixelFormat() { - return PixelFormat.Companion.fromImageFormat(getImage().getFormat()); + public synchronized PixelFormat getPixelFormat() throws FrameInvalidError { + assertIsValid(); + return PixelFormat.Companion.fromImageFormat(image.getFormat()); } @SuppressWarnings("unused") @DoNotStrip - public int getPlanesCount() { - return getImage().getPlanes().length; + public synchronized int getPlanesCount() throws FrameInvalidError { + assertIsValid(); + return image.getPlanes().length; } @SuppressWarnings("unused") @DoNotStrip - public int getBytesPerRow() { - return getImage().getPlanes()[0].getRowStride(); + public synchronized int getBytesPerRow() throws FrameInvalidError { + assertIsValid(); + return image.getPlanes()[0].getRowStride(); } @SuppressWarnings("unused") @DoNotStrip - public Object getHardwareBufferBoxed() throws HardwareBuffersNotAvailableError { + private Object getHardwareBufferBoxed() throws HardwareBuffersNotAvailableError, FrameInvalidError { return getHardwareBuffer(); } - public HardwareBuffer getHardwareBuffer() throws HardwareBuffersNotAvailableError { + public synchronized HardwareBuffer getHardwareBuffer() throws HardwareBuffersNotAvailableError, FrameInvalidError { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) { throw new HardwareBuffersNotAvailableError(); } - return getImage().getHardwareBuffer(); + assertIsValid(); + return image.getHardwareBuffer(); } @SuppressWarnings("unused") @DoNotStrip - public void incrementRefCount() { - synchronized (this) { - refCount++; - } + public synchronized void incrementRefCount() { + refCount++; } @SuppressWarnings("unused") @DoNotStrip - public void decrementRefCount() { - synchronized (this) { - refCount--; - if (refCount <= 0) { - // If no reference is held on this Image, close it. - close(); - } + public synchronized void decrementRefCount() { + refCount--; + if (refCount <= 0) { + // If no reference is held on this Image, close it. + close(); } } - private void close() { - synchronized (this) { - image.close(); - } + private synchronized void close() { + image.close(); } } diff --git a/package/src/CameraError.ts b/package/src/CameraError.ts index 94b205a..baa02ce 100644 --- a/package/src/CameraError.ts +++ b/package/src/CameraError.ts @@ -40,6 +40,7 @@ export type CaptureError = | 'capture/recorder-error' | 'capture/video-not-enabled' | 'capture/photo-not-enabled' + | 'capture/frame-invalid' | 'capture/aborted' | 'capture/unknown' export type SystemError =