feat: Synchronize Frame properly (#2501)
				
					
				
			* feat: Synchronize `Frame` properly * Update CameraError.ts * Image is not valid if `refCount` < 0
This commit is contained in:
		| @@ -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( | ||||
|   | ||||
| @@ -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(); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -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 = | ||||
|   | ||||
		Reference in New Issue
	
	Block a user