feat: Synchronize Frame
properly (#2501)
* feat: Synchronize `Frame` properly * Update CameraError.ts * Image is not valid if `refCount` < 0
This commit is contained in:
parent
97168c647c
commit
d8c95c901f
@ -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) {
|
||||
public synchronized void incrementRefCount() {
|
||||
refCount++;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@DoNotStrip
|
||||
public void decrementRefCount() {
|
||||
synchronized (this) {
|
||||
public synchronized void decrementRefCount() {
|
||||
refCount--;
|
||||
if (refCount <= 0) {
|
||||
// If no reference is held on this Image, close it.
|
||||
close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void close() {
|
||||
synchronized (this) {
|
||||
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 =
|
||||
|
Loading…
Reference in New Issue
Block a user