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",
|
"recording-in-progress",
|
||||||
"There is already an active video recording in progress! Did you call startRecording() twice?"
|
"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) :
|
class CodeTypeNotSupportedError(codeType: String) :
|
||||||
CameraError(
|
CameraError(
|
||||||
|
@ -4,6 +4,7 @@ import android.hardware.HardwareBuffer;
|
|||||||
import android.media.Image;
|
import android.media.Image;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import com.facebook.proguard.annotations.DoNotStrip;
|
import com.facebook.proguard.annotations.DoNotStrip;
|
||||||
|
import com.mrousavy.camera.core.FrameInvalidError;
|
||||||
import com.mrousavy.camera.core.HardwareBuffersNotAvailableError;
|
import com.mrousavy.camera.core.HardwareBuffersNotAvailableError;
|
||||||
import com.mrousavy.camera.types.PixelFormat;
|
import com.mrousavy.camera.types.PixelFormat;
|
||||||
import com.mrousavy.camera.types.Orientation;
|
import com.mrousavy.camera.types.Orientation;
|
||||||
@ -23,42 +24,17 @@ public class Frame {
|
|||||||
this.isMirrored = isMirrored;
|
this.isMirrored = isMirrored;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Image getImage() {
|
private void assertIsValid() throws FrameInvalidError {
|
||||||
synchronized (this) {
|
if (!getIsImageValid(image)) {
|
||||||
Image img = image;
|
throw new FrameInvalidError();
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
private synchronized boolean getIsImageValid(Image image) {
|
||||||
@DoNotStrip
|
if (refCount <= 0) return false;
|
||||||
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) {
|
|
||||||
try {
|
try {
|
||||||
// will throw an exception if the image is already closed
|
// will throw an exception if the image is already closed
|
||||||
synchronized (this) { image.getFormat(); }
|
image.getFormat();
|
||||||
// no exception thrown, image must still be valid.
|
// no exception thrown, image must still be valid.
|
||||||
return true;
|
return true;
|
||||||
} catch (IllegalStateException e) {
|
} catch (IllegalStateException e) {
|
||||||
@ -67,78 +43,104 @@ public class Frame {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public synchronized Image getImage() {
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
@DoNotStrip
|
@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;
|
return isMirrored;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
@DoNotStrip
|
@DoNotStrip
|
||||||
public long getTimestamp() {
|
public synchronized long getTimestamp() throws FrameInvalidError {
|
||||||
|
assertIsValid();
|
||||||
return timestamp;
|
return timestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
@DoNotStrip
|
@DoNotStrip
|
||||||
public Orientation getOrientation() {
|
public synchronized Orientation getOrientation() throws FrameInvalidError {
|
||||||
|
assertIsValid();
|
||||||
return orientation;
|
return orientation;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
@DoNotStrip
|
@DoNotStrip
|
||||||
public PixelFormat getPixelFormat() {
|
public synchronized PixelFormat getPixelFormat() throws FrameInvalidError {
|
||||||
return PixelFormat.Companion.fromImageFormat(getImage().getFormat());
|
assertIsValid();
|
||||||
|
return PixelFormat.Companion.fromImageFormat(image.getFormat());
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
@DoNotStrip
|
@DoNotStrip
|
||||||
public int getPlanesCount() {
|
public synchronized int getPlanesCount() throws FrameInvalidError {
|
||||||
return getImage().getPlanes().length;
|
assertIsValid();
|
||||||
|
return image.getPlanes().length;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
@DoNotStrip
|
@DoNotStrip
|
||||||
public int getBytesPerRow() {
|
public synchronized int getBytesPerRow() throws FrameInvalidError {
|
||||||
return getImage().getPlanes()[0].getRowStride();
|
assertIsValid();
|
||||||
|
return image.getPlanes()[0].getRowStride();
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
@DoNotStrip
|
@DoNotStrip
|
||||||
public Object getHardwareBufferBoxed() throws HardwareBuffersNotAvailableError {
|
private Object getHardwareBufferBoxed() throws HardwareBuffersNotAvailableError, FrameInvalidError {
|
||||||
return getHardwareBuffer();
|
return getHardwareBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
public HardwareBuffer getHardwareBuffer() throws HardwareBuffersNotAvailableError {
|
public synchronized HardwareBuffer getHardwareBuffer() throws HardwareBuffersNotAvailableError, FrameInvalidError {
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
|
||||||
throw new HardwareBuffersNotAvailableError();
|
throw new HardwareBuffersNotAvailableError();
|
||||||
}
|
}
|
||||||
return getImage().getHardwareBuffer();
|
assertIsValid();
|
||||||
|
return image.getHardwareBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
@DoNotStrip
|
@DoNotStrip
|
||||||
public void incrementRefCount() {
|
public synchronized void incrementRefCount() {
|
||||||
synchronized (this) {
|
refCount++;
|
||||||
refCount++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
@DoNotStrip
|
@DoNotStrip
|
||||||
public void decrementRefCount() {
|
public synchronized void decrementRefCount() {
|
||||||
synchronized (this) {
|
refCount--;
|
||||||
refCount--;
|
if (refCount <= 0) {
|
||||||
if (refCount <= 0) {
|
// If no reference is held on this Image, close it.
|
||||||
// If no reference is held on this Image, close it.
|
close();
|
||||||
close();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void close() {
|
private synchronized void close() {
|
||||||
synchronized (this) {
|
image.close();
|
||||||
image.close();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,7 @@ export type CaptureError =
|
|||||||
| 'capture/recorder-error'
|
| 'capture/recorder-error'
|
||||||
| 'capture/video-not-enabled'
|
| 'capture/video-not-enabled'
|
||||||
| 'capture/photo-not-enabled'
|
| 'capture/photo-not-enabled'
|
||||||
|
| 'capture/frame-invalid'
|
||||||
| 'capture/aborted'
|
| 'capture/aborted'
|
||||||
| 'capture/unknown'
|
| 'capture/unknown'
|
||||||
export type SystemError =
|
export type SystemError =
|
||||||
|
Loading…
Reference in New Issue
Block a user