Add CameraEngine support, integration tests

pull/704/head
Mattia Iavarone 6 years ago
parent ed5d55da37
commit ea29b0236f
  1. 1
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/engine/Camera2IntegrationTest.java
  2. 33
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/engine/CameraIntegrationTest.java
  3. 10
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/engine/MockCameraEngine.java
  4. 12
      cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera1Engine.java
  5. 51
      cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Engine.java
  6. 13
      cameraview/src/main/java/com/otaliastudios/cameraview/engine/CameraBaseEngine.java
  7. 3
      cameraview/src/main/java/com/otaliastudios/cameraview/engine/CameraEngine.java
  8. 4
      demo/src/main/java/com/otaliastudios/cameraview/demo/CameraActivity.java
  9. 23
      demo/src/main/java/com/otaliastudios/cameraview/demo/Option.java

@ -9,6 +9,7 @@ import com.otaliastudios.cameraview.controls.Engine;
import com.otaliastudios.cameraview.engine.action.ActionHolder;
import com.otaliastudios.cameraview.engine.action.BaseAction;
import org.junit.Test;
import org.junit.runner.RunWith;
import androidx.annotation.NonNull;

@ -51,6 +51,7 @@ import org.junit.Test;
import org.mockito.ArgumentMatcher;
import java.io.File;
import java.util.Collection;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@ -1130,6 +1131,38 @@ public abstract class CameraIntegrationTest<E extends CameraBaseEngine> extends
}
}
@Test
@Retry(emulatorOnly = true)
@SdkExclude(maxSdkVersion = 22, emulatorOnly = true)
public void testFrameProcessing_format() {
CameraOptions o = openSync(true);
Collection<Integer> formats = o.getSupportedFrameProcessingFormats();
closeSync(true);
for (int format : formats) {
LOG.i("[TEST FRAME FORMAT]", "Testing", format, "...");
Op<Boolean> op = testFrameProcessorFormat(format);
assertNotNull(op.await(DELAY));
}
}
@NonNull
private Op<Boolean> testFrameProcessorFormat(final int format) {
final Op<Boolean> op = new Op<>();
camera.setFrameProcessingFormat(format);
camera.clearFrameProcessors();
camera.addFrameProcessor(new FrameProcessor() {
@Override
public void process(@NonNull Frame frame) {
if (frame.getFormat() == format) {
op.controller().start();
op.controller().end(true);
}
}
});
return op;
}
//endregion
//region Overlays

@ -135,6 +135,16 @@ public class MockCameraEngine extends CameraBaseEngine {
mPictureFormat = pictureFormat;
}
@Override
public void setHasFrameProcessors(boolean hasFrameProcessors) {
mHasFrameProcessors = hasFrameProcessors;
}
@Override
public void setFrameProcessingFormat(int format) {
mFrameProcessingFormat = format;
}
@Override
public void takePicture(@NonNull PictureResult.Stub stub) {
super.takePicture(stub);

@ -767,6 +767,18 @@ public class Camera1Engine extends CameraBaseEngine implements
return (ByteBufferFrameManager) super.getFrameManager();
}
@Override
public void setHasFrameProcessors(boolean hasFrameProcessors) {
// we don't care, FP is always on
mHasFrameProcessors = hasFrameProcessors;
}
@Override
public void setFrameProcessingFormat(int format) {
// Ignore input: we only support NV21.
mFrameProcessingFormat = ImageFormat.NV21;
}
@Override
public void onBufferAvailable(@NonNull byte[] buffer) {
if (getState().isAtLeast(CameraState.ENGINE)

@ -80,6 +80,7 @@ import java.util.concurrent.ExecutionException;
public class Camera2Engine extends CameraBaseEngine implements
ImageReader.OnImageAvailableListener,
ActionHolder {
private static final int FRAME_PROCESSING_FORMAT = ImageFormat.NV21;
@VisibleForTesting static final long METER_TIMEOUT = 2500;
@ -331,7 +332,7 @@ public class Camera2Engine extends CameraBaseEngine implements
if (streamMap == null) {
throw new RuntimeException("StreamConfigurationMap is null. Should not happen.");
}
android.util.Size[] sizes = streamMap.getOutputSizes(getFrameProcessingImageFormat());
android.util.Size[] sizes = streamMap.getOutputSizes(getFrameProcessingFormat());
List<Size> candidates = new ArrayList<>(sizes.length);
for (android.util.Size size : sizes) {
Size add = new Size(size.getWidth(), size.getHeight());
@ -541,7 +542,7 @@ public class Camera2Engine extends CameraBaseEngine implements
mFrameProcessingReader = ImageReader.newInstance(
mFrameProcessingSize.getWidth(),
mFrameProcessingSize.getHeight(),
getFrameProcessingImageFormat(),
mFrameProcessingFormat,
3);
mFrameProcessingReader.setOnImageAvailableListener(this, null);
mFrameProcessingSurface = mFrameProcessingReader.getSurface();
@ -595,7 +596,7 @@ public class Camera2Engine extends CameraBaseEngine implements
mPreview.setStreamSize(previewSizeForView.getWidth(), previewSizeForView.getHeight());
mPreview.setDrawRotation(getAngles().offset(Reference.BASE, Reference.VIEW, Axis.ABSOLUTE));
if (hasFrameProcessors()) {
getFrameManager().setUp(FRAME_PROCESSING_FORMAT, mFrameProcessingSize);
getFrameManager().setUp(mFrameProcessingFormat, mFrameProcessingSize);
}
LOG.i("onStartPreview:", "Starting preview.");
@ -1402,17 +1403,6 @@ public class Camera2Engine extends CameraBaseEngine implements
return new ImageFrameManager(2);
}
/**
* Returns the frame processing image format.
* Defaults to {@link ImageFormat#YUV_420_888}.
* Override at your own risk!
* @return format
*/
@SuppressWarnings("WeakerAccess")
protected int getFrameProcessingImageFormat() {
return ImageFormat.YUV_420_888;
}
@Override
public void onImageAvailable(ImageReader reader) {
LOG.v("onImageAvailable", "trying to acquire Image.");
@ -1448,13 +1438,34 @@ public class Camera2Engine extends CameraBaseEngine implements
// Extremely rare case in which this was called in between startBind and
// startPreview. This can cause issues. Try later.
setHasFrameProcessors(hasFrameProcessors);
} else if (getState().isAtLeast(CameraState.BIND)) {
// Apply and restart.
Camera2Engine.super.setHasFrameProcessors(hasFrameProcessors);
return;
}
// Apply and restart.
mHasFrameProcessors = hasFrameProcessors;
if (getState().isAtLeast(CameraState.BIND)) {
restartBind();
}
}
});
}
@Override
public void setFrameProcessingFormat(final int format) {
// Frame processing format is used both when binding and when starting the preview.
// If the value is changed between the two, the preview step can crash.
getOrchestrator().schedule("frame processing format (" + format + ")",
true, new Runnable() {
@Override
public void run() {
if (getState().isAtLeast(CameraState.BIND) && isChangingState()) {
// Extremely rare case in which this was called in between startBind and
// startPreview. This can cause issues. Try later.
setFrameProcessingFormat(format);
return;
}
mFrameProcessingFormat = format > 0 ? format : ImageFormat.YUV_420_888;
if (getState().isAtLeast(CameraState.BIND)) {
restartBind();
} else {
// Just apply.
Camera2Engine.super.setHasFrameProcessors(hasFrameProcessors);
}
}
});

@ -52,6 +52,8 @@ public abstract class CameraBaseEngine extends CameraEngine {
@SuppressWarnings("WeakerAccess") protected Size mCaptureSize;
@SuppressWarnings("WeakerAccess") protected Size mPreviewStreamSize;
@SuppressWarnings("WeakerAccess") protected Size mFrameProcessingSize;
@SuppressWarnings("WeakerAccess") protected int mFrameProcessingFormat;
@SuppressWarnings("WeakerAccess") protected boolean mHasFrameProcessors;
@SuppressWarnings("WeakerAccess") protected Flash mFlash;
@SuppressWarnings("WeakerAccess") protected WhiteBalance mWhiteBalance;
@SuppressWarnings("WeakerAccess") protected VideoCodec mVideoCodec;
@ -77,7 +79,6 @@ public abstract class CameraBaseEngine extends CameraEngine {
private int mVideoMaxDuration;
private int mVideoBitRate;
private int mAudioBitRate;
private boolean mHasFrameProcessors;
private long mAutoFocusResetDelayMillis;
private int mSnapshotMaxWidth; // in REF_VIEW like SizeSelectors
private int mSnapshotMaxHeight; // in REF_VIEW like SizeSelectors
@ -284,6 +285,11 @@ public abstract class CameraBaseEngine extends CameraEngine {
return mFrameProcessingMaxHeight;
}
@Override
public final int getFrameProcessingFormat() {
return mFrameProcessingFormat;
}
@Override
public final void setAutoFocusResetDelay(long delayMillis) {
mAutoFocusResetDelayMillis = delayMillis;
@ -423,11 +429,6 @@ public abstract class CameraBaseEngine extends CameraEngine {
return mPreviewFrameRate;
}
@Override
public void setHasFrameProcessors(boolean hasFrameProcessors) {
mHasFrameProcessors = hasFrameProcessors;
}
@Override
public final boolean hasFrameProcessors() {
return mHasFrameProcessors;

@ -647,6 +647,9 @@ public abstract class CameraEngine implements
public abstract void setFrameProcessingMaxHeight(int maxHeight);
public abstract int getFrameProcessingMaxHeight();
public abstract void setFrameProcessingFormat(int format);
public abstract int getFrameProcessingFormat();
public abstract void setAutoFocusResetDelay(long delayMillis);
public abstract long getAutoFocusResetDelay();

@ -131,6 +131,8 @@ public class CameraActivity extends AppCompatActivity implements View.OnClickLis
new Option.OverlayInPreview(watermark),
new Option.OverlayInPictureSnapshot(watermark),
new Option.OverlayInVideoSnapshot(watermark),
// Frame Processing
new Option.FrameProcessingFormat(),
// Other
new Option.Grid(), new Option.GridColor(), new Option.UseDeviceOrientation()
);
@ -147,6 +149,8 @@ public class CameraActivity extends AppCompatActivity implements View.OnClickLis
false, false, false, false, true,
// Watermarks
false, false, true,
// Frame Processing
true,
// Other
false, false, true
);

@ -554,4 +554,27 @@ public abstract class Option<T> {
}
}
public static class FrameProcessingFormat extends Option<Integer> {
FrameProcessingFormat() {
super("Frame Processing Format");
}
@Override
public void set(@NonNull CameraView view, @NonNull Integer value) {
view.setFrameProcessingFormat(value);
}
@NonNull
@Override
public Integer get(@NonNull CameraView view) {
return view.getFrameProcessingFormat();
}
@NonNull
@Override
public Collection<Integer> getAll(@NonNull CameraView view, @NonNull CameraOptions options) {
return options.getSupportedFrameProcessingFormats();
}
}
}

Loading…
Cancel
Save