Create Engine class

pull/493/head
Mattia Iavarone 5 years ago
parent e7727fb65c
commit e2e3054a77
  1. 9
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraViewCallbacksTest.java
  2. 12
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraViewTest.java
  3. 3
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/engine/IntegrationTest.java
  4. 6
      cameraview/src/main/java/com/otaliastudios/cameraview/CameraOptions.java
  5. 114
      cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java
  6. 6
      cameraview/src/main/java/com/otaliastudios/cameraview/controls/ControlParser.java
  7. 48
      cameraview/src/main/java/com/otaliastudios/cameraview/controls/Engine.java
  8. 6
      cameraview/src/main/java/com/otaliastudios/cameraview/engine/CameraEngine.java
  9. 5
      cameraview/src/main/res/values/attrs.xml

@ -10,6 +10,8 @@ import androidx.test.filters.MediumTest;
import android.view.ViewGroup; import android.view.ViewGroup;
import com.otaliastudios.cameraview.controls.Audio; import com.otaliastudios.cameraview.controls.Audio;
import com.otaliastudios.cameraview.controls.Engine;
import com.otaliastudios.cameraview.controls.Preview;
import com.otaliastudios.cameraview.engine.CameraEngine; import com.otaliastudios.cameraview.engine.CameraEngine;
import com.otaliastudios.cameraview.frame.Frame; import com.otaliastudios.cameraview.frame.Frame;
import com.otaliastudios.cameraview.frame.FrameProcessor; import com.otaliastudios.cameraview.frame.FrameProcessor;
@ -37,7 +39,6 @@ import static org.junit.Assert.assertNull;
import static org.mockito.Matchers.any; import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyFloat; import static org.mockito.Matchers.anyFloat;
import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.floatThat;
import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never; import static org.mockito.Mockito.never;
@ -70,14 +71,14 @@ public class CameraViewCallbacksTest extends BaseTest {
@NonNull @NonNull
@Override @Override
protected CameraEngine instantiateCameraController(@NonNull CameraEngine.Callback callback) { protected CameraEngine instantiateCameraEngine(@NonNull Engine engine, @NonNull CameraEngine.Callback callback) {
mockController = new MockCameraEngine(callback); mockController = new MockCameraEngine(callback);
return mockController; return mockController;
} }
@NonNull @NonNull
@Override @Override
protected CameraPreview instantiatePreview(@NonNull Context context, @NonNull ViewGroup container) { protected CameraPreview instantiatePreview(@NonNull Preview preview, @NonNull Context context, @NonNull ViewGroup container) {
mockPreview = new MockCameraPreview(context, container); mockPreview = new MockCameraPreview(context, container);
return mockPreview; return mockPreview;
} }
@ -87,7 +88,7 @@ public class CameraViewCallbacksTest extends BaseTest {
return true; return true;
} }
}; };
camera.instantiatePreview(); camera.doInstantiatePreview();
camera.addCameraListener(listener); camera.addCameraListener(listener);
camera.addFrameProcessor(processor); camera.addFrameProcessor(processor);
task = new Task<>(true); task = new Task<>(true);

@ -13,8 +13,10 @@ import android.view.ViewGroup;
import com.otaliastudios.cameraview.controls.Audio; import com.otaliastudios.cameraview.controls.Audio;
import com.otaliastudios.cameraview.controls.ControlParser; import com.otaliastudios.cameraview.controls.ControlParser;
import com.otaliastudios.cameraview.controls.Engine;
import com.otaliastudios.cameraview.controls.Facing; import com.otaliastudios.cameraview.controls.Facing;
import com.otaliastudios.cameraview.controls.Flash; import com.otaliastudios.cameraview.controls.Flash;
import com.otaliastudios.cameraview.controls.Preview;
import com.otaliastudios.cameraview.engine.CameraEngine; import com.otaliastudios.cameraview.engine.CameraEngine;
import com.otaliastudios.cameraview.frame.Frame; import com.otaliastudios.cameraview.frame.Frame;
import com.otaliastudios.cameraview.frame.FrameProcessor; import com.otaliastudios.cameraview.frame.FrameProcessor;
@ -40,14 +42,10 @@ import com.otaliastudios.cameraview.size.Size;
import com.otaliastudios.cameraview.size.SizeSelector; import com.otaliastudios.cameraview.size.SizeSelector;
import com.otaliastudios.cameraview.size.SizeSelectors; import com.otaliastudios.cameraview.size.SizeSelectors;
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.Matchers;
import org.mockito.invocation.InvocationOnMock; import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer; import org.mockito.stubbing.Answer;
@ -77,14 +75,14 @@ public class CameraViewTest extends BaseTest {
@NonNull @NonNull
@Override @Override
protected CameraEngine instantiateCameraController(@NonNull CameraEngine.Callback callback) { protected CameraEngine instantiateCameraEngine(@NonNull Engine engine, @NonNull CameraEngine.Callback callback) {
mockController = spy(new MockCameraEngine(callback)); mockController = spy(new MockCameraEngine(callback));
return mockController; return mockController;
} }
@NonNull @NonNull
@Override @Override
protected CameraPreview instantiatePreview(@NonNull Context context, @NonNull ViewGroup container) { protected CameraPreview instantiatePreview(@NonNull Preview preview, @NonNull Context context, @NonNull ViewGroup container) {
mockPreview = spy(new MockCameraPreview(context, container)); mockPreview = spy(new MockCameraPreview(context, container));
return mockPreview; return mockPreview;
} }
@ -95,7 +93,7 @@ public class CameraViewTest extends BaseTest {
} }
}; };
// Instantiate preview now. // Instantiate preview now.
cameraView.instantiatePreview(); cameraView.doInstantiatePreview();
} }
}); });
} }

@ -15,6 +15,7 @@ import com.otaliastudios.cameraview.PictureResult;
import com.otaliastudios.cameraview.TestActivity; import com.otaliastudios.cameraview.TestActivity;
import com.otaliastudios.cameraview.VideoResult; import com.otaliastudios.cameraview.VideoResult;
import com.otaliastudios.cameraview.controls.Audio; import com.otaliastudios.cameraview.controls.Audio;
import com.otaliastudios.cameraview.controls.Engine;
import com.otaliastudios.cameraview.controls.Flash; import com.otaliastudios.cameraview.controls.Flash;
import com.otaliastudios.cameraview.controls.Hdr; import com.otaliastudios.cameraview.controls.Hdr;
import com.otaliastudios.cameraview.controls.Mode; import com.otaliastudios.cameraview.controls.Mode;
@ -87,7 +88,7 @@ public class IntegrationTest extends BaseTest {
@NonNull @NonNull
@Override @Override
protected CameraEngine instantiateCameraController(@NonNull CameraEngine.Callback callback) { protected CameraEngine instantiateCameraEngine(@NonNull Engine engine, @NonNull CameraEngine.Callback callback) {
controller = new Camera1Engine(callback); controller = new Camera1Engine(callback);
return controller; return controller;
} }

@ -5,6 +5,7 @@ import android.hardware.Camera;
import com.otaliastudios.cameraview.controls.Audio; import com.otaliastudios.cameraview.controls.Audio;
import com.otaliastudios.cameraview.controls.Control; import com.otaliastudios.cameraview.controls.Control;
import com.otaliastudios.cameraview.controls.Engine;
import com.otaliastudios.cameraview.controls.Facing; import com.otaliastudios.cameraview.controls.Facing;
import com.otaliastudios.cameraview.controls.Flash; import com.otaliastudios.cameraview.controls.Flash;
import com.otaliastudios.cameraview.engine.Mapper; import com.otaliastudios.cameraview.engine.Mapper;
@ -183,6 +184,8 @@ public class CameraOptions {
return (Collection<T>) Arrays.asList(VideoCodec.values()); return (Collection<T>) Arrays.asList(VideoCodec.values());
} else if (controlClass.equals(WhiteBalance.class)) { } else if (controlClass.equals(WhiteBalance.class)) {
return (Collection<T>) getSupportedWhiteBalance(); return (Collection<T>) getSupportedWhiteBalance();
} else if (controlClass.equals(Engine.class)) {
return (Collection<T>) Arrays.asList(Engine.values());
} }
// Unrecognized control. // Unrecognized control.
return Collections.emptyList(); return Collections.emptyList();
@ -311,8 +314,7 @@ public class CameraOptions {
/** /**
* Whether auto focus is supported. This means you can map gestures to * Whether auto focus is supported. This means you can map gestures to
* {@link GestureAction#FOCUS} or {@link GestureAction#FOCUS_WITH_MARKER} * {@link GestureAction#AUTO_FOCUS} and focus will be changed on tap.
* and focus will be changed on tap.
* *
* @return whether auto focus is supported. * @return whether auto focus is supported.
*/ */

@ -33,6 +33,7 @@ import android.widget.FrameLayout;
import com.otaliastudios.cameraview.controls.Audio; import com.otaliastudios.cameraview.controls.Audio;
import com.otaliastudios.cameraview.controls.Control; import com.otaliastudios.cameraview.controls.Control;
import com.otaliastudios.cameraview.controls.ControlParser; import com.otaliastudios.cameraview.controls.ControlParser;
import com.otaliastudios.cameraview.controls.Engine;
import com.otaliastudios.cameraview.controls.Facing; import com.otaliastudios.cameraview.controls.Facing;
import com.otaliastudios.cameraview.controls.Flash; import com.otaliastudios.cameraview.controls.Flash;
import com.otaliastudios.cameraview.markers.MarkerLayout; import com.otaliastudios.cameraview.markers.MarkerLayout;
@ -100,6 +101,7 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
private boolean mPlaySounds; private boolean mPlaySounds;
private HashMap<Gesture, GestureAction> mGestureMap = new HashMap<>(4); private HashMap<Gesture, GestureAction> mGestureMap = new HashMap<>(4);
private Preview mPreview; private Preview mPreview;
private Engine mEngine;
// Components // Components
@VisibleForTesting CameraCallbacks mCameraCallbacks; @VisibleForTesting CameraCallbacks mCameraCallbacks;
@ -148,8 +150,9 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
boolean playSounds = a.getBoolean(R.styleable.CameraView_cameraPlaySounds, DEFAULT_PLAY_SOUNDS); boolean playSounds = a.getBoolean(R.styleable.CameraView_cameraPlaySounds, DEFAULT_PLAY_SOUNDS);
mExperimental = a.getBoolean(R.styleable.CameraView_cameraExperimental, false); mExperimental = a.getBoolean(R.styleable.CameraView_cameraExperimental, false);
mPreview = controls.getPreview(); mPreview = controls.getPreview();
mEngine = controls.getEngine();
// Camera controller params // Camera engine params
int gridColor = a.getColor(R.styleable.CameraView_cameraGridColor, GridLinesLayout.DEFAULT_COLOR); int gridColor = a.getColor(R.styleable.CameraView_cameraGridColor, GridLinesLayout.DEFAULT_COLOR);
long videoMaxSize = (long) a.getFloat(R.styleable.CameraView_cameraVideoMaxSize, 0); long videoMaxSize = (long) a.getFloat(R.styleable.CameraView_cameraVideoMaxSize, 0);
int videoMaxDuration = a.getInteger(R.styleable.CameraView_cameraVideoMaxDuration, 0); int videoMaxDuration = a.getInteger(R.styleable.CameraView_cameraVideoMaxDuration, 0);
@ -166,7 +169,6 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
// Components // Components
mCameraCallbacks = new CameraCallbacks(); mCameraCallbacks = new CameraCallbacks();
mCameraEngine = instantiateCameraController(mCameraCallbacks);
mUiHandler = new Handler(Looper.getMainLooper()); mUiHandler = new Handler(Looper.getMainLooper());
mFrameProcessorsHandler = WorkerHandler.get("FrameProcessorsWorker"); mFrameProcessorsHandler = WorkerHandler.get("FrameProcessorsWorker");
@ -182,16 +184,20 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
addView(mScrollGestureLayout); addView(mScrollGestureLayout);
addView(mMarkerLayout); addView(mMarkerLayout);
// Create the engine
doInstantiateEngine();
// Apply self managed // Apply self managed
setPlaySounds(playSounds); setPlaySounds(playSounds);
setGrid(controls.getGrid());
setGridColor(gridColor);
// Apply camera controller params // Apply camera engine params
// Adding new ones? See setEngine().
setFacing(controls.getFacing()); setFacing(controls.getFacing());
setFlash(controls.getFlash()); setFlash(controls.getFlash());
setMode(controls.getMode()); setMode(controls.getMode());
setWhiteBalance(controls.getWhiteBalance()); setWhiteBalance(controls.getWhiteBalance());
setGrid(controls.getGrid());
setGridColor(gridColor);
setHdr(controls.getHdr()); setHdr(controls.getHdr());
setAudio(controls.getAudio()); setAudio(controls.getAudio());
setAudioBitRate(audioBitRate); setAudioBitRate(audioBitRate);
@ -220,24 +226,33 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
/** /**
* Instantiates the camera engine. * Instantiates the camera engine.
*
* @param engine the engine preference
* @param callback the engine callback * @param callback the engine callback
* @return the engine * @return the engine
*/ */
@NonNull @NonNull
protected CameraEngine instantiateCameraController(@NonNull CameraEngine.Callback callback) { protected CameraEngine instantiateCameraEngine(@NonNull Engine engine, @NonNull CameraEngine.Callback callback) {
return new Camera1Engine(callback); if (mExperimental && engine == Engine.CAMERA2) {
throw new RuntimeException("TODO");
} else {
mEngine = Engine.CAMERA1;
return new Camera1Engine(callback);
}
} }
/** /**
* Instantiates the camera preview. * Instantiates the camera preview.
*
* @param preview current preview value
* @param context a context * @param context a context
* @param container the container * @param container the container
* @return the preview * @return the preview
*/ */
@NonNull @NonNull
protected CameraPreview instantiatePreview(@NonNull Context context, @NonNull ViewGroup container) { protected CameraPreview instantiatePreview(@NonNull Preview preview, @NonNull Context context, @NonNull ViewGroup container) {
LOG.w("preview:", "isHardwareAccelerated:", isHardwareAccelerated()); LOG.w("preview:", "isHardwareAccelerated:", isHardwareAccelerated());
switch (mPreview) { switch (preview) {
case SURFACE: case SURFACE:
return new SurfaceCameraPreview(context, container, null); return new SurfaceCameraPreview(context, container, null);
case TEXTURE: { case TEXTURE: {
@ -254,18 +269,22 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
} }
@VisibleForTesting @VisibleForTesting
void instantiatePreview() { void doInstantiatePreview() {
mCameraPreview = instantiatePreview(getContext(), this); mCameraPreview = instantiatePreview(mPreview, getContext(), this);
mCameraEngine.setPreview(mCameraPreview); mCameraEngine.setPreview(mCameraPreview);
} }
private void doInstantiateEngine() {
mCameraEngine = instantiateCameraEngine(mEngine, mCameraCallbacks);
}
@Override @Override
protected void onAttachedToWindow() { protected void onAttachedToWindow() {
super.onAttachedToWindow(); super.onAttachedToWindow();
if (mCameraPreview == null) { if (mCameraPreview == null) {
// isHardwareAccelerated will return the real value only after we are // isHardwareAccelerated will return the real value only after we are
// attached. That's why we instantiate the preview here. // attached. That's why we instantiate the preview here.
instantiatePreview(); doInstantiatePreview();
} }
if (!isInEditMode()) { if (!isInEditMode()) {
mOrientationHelper.enable(getContext()); mOrientationHelper.enable(getContext());
@ -719,10 +738,64 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
setVideoCodec((VideoCodec) control); setVideoCodec((VideoCodec) control);
} else if (control instanceof Preview) { } else if (control instanceof Preview) {
setPreview((Preview) control); setPreview((Preview) control);
} else if (control instanceof Engine) {
setEngine((Engine) control);
} }
} }
/**
* Controls the preview engine. Should only be called
* if this CameraView was never added to any window
* (like if you created it programmatically).
* Otherwise, it has no effect.
*
* @see Preview#SURFACE
* @see Preview#TEXTURE
* @see Preview#GL_SURFACE
*
* @param preview desired preview engine
*/
public void setPreview(@NonNull Preview preview) {
mPreview = preview;
}
/**
* Controls the core engine. Should only be called
* if this CameraView is closed (open() was never called).
* Otherwise, it has no effect.
*
* @see Engine#CAMERA1
* @see Engine#CAMERA2
*
* @param engine desired engine
*/
public void setEngine(@NonNull Engine engine) {
if (!isClosed()) return;
mEngine = engine;
CameraEngine oldEngine = mCameraEngine;
doInstantiateEngine();
if (mCameraPreview != null) mCameraEngine.setPreview(mCameraPreview);
// Set again all parameters
setFacing(oldEngine.getFacing());
setFlash(oldEngine.getFlash());
setMode(oldEngine.getMode());
setWhiteBalance(oldEngine.getWhiteBalance());
setHdr(oldEngine.getHdr());
setAudio(oldEngine.getAudio());
setAudioBitRate(oldEngine.getAudioBitRate());
setPictureSize(oldEngine.getPictureSizeSelector());
setVideoSize(oldEngine.getVideoSizeSelector());
setVideoCodec(oldEngine.getVideoCodec());
setVideoMaxSize(oldEngine.getVideoMaxSize());
setVideoMaxDuration(oldEngine.getVideoMaxDuration());
setVideoBitRate(oldEngine.getVideoBitRate());
setAutoFocusResetDelay(oldEngine.getAutoFocusResetDelay());
}
/** /**
* Returns a {@link CameraOptions} instance holding supported options for this camera * Returns a {@link CameraOptions} instance holding supported options for this camera
* session. This might change over time. It's better to hold a reference from * session. This might change over time. It's better to hold a reference from
@ -854,23 +927,6 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
} }
/**
* Controls the preview engine. Should only be called
* if this CameraView was never added to any window
* (like if you created it programmatically).
* Otherwise, it has no effect.
*
* @see Preview#SURFACE
* @see Preview#TEXTURE
* @see Preview#GL_SURFACE
*
* @param preview desired preview engine
*/
public void setPreview(@NonNull Preview preview) {
mPreview = preview;
}
/** /**
* Gets the current hdr value. * Gets the current hdr value.
* @return the current hdr value * @return the current hdr value

@ -21,6 +21,7 @@ public class ControlParser {
private int hdr; private int hdr;
private int audio; private int audio;
private int videoCodec; private int videoCodec;
private int engine;
public ControlParser(@NonNull Context context, @NonNull TypedArray array) { public ControlParser(@NonNull Context context, @NonNull TypedArray array) {
this.preview = array.getInteger(R.styleable.CameraView_cameraPreview, Preview.DEFAULT.value()); this.preview = array.getInteger(R.styleable.CameraView_cameraPreview, Preview.DEFAULT.value());
@ -32,6 +33,7 @@ public class ControlParser {
this.hdr = array.getInteger(R.styleable.CameraView_cameraHdr, Hdr.DEFAULT.value()); this.hdr = array.getInteger(R.styleable.CameraView_cameraHdr, Hdr.DEFAULT.value());
this.audio = array.getInteger(R.styleable.CameraView_cameraAudio, Audio.DEFAULT.value()); this.audio = array.getInteger(R.styleable.CameraView_cameraAudio, Audio.DEFAULT.value());
this.videoCodec = array.getInteger(R.styleable.CameraView_cameraVideoCodec, VideoCodec.DEFAULT.value()); this.videoCodec = array.getInteger(R.styleable.CameraView_cameraVideoCodec, VideoCodec.DEFAULT.value());
this.engine = array.getInteger(R.styleable.CameraView_cameraEngine, Engine.DEFAULT.value());
} }
public Preview getPreview() { public Preview getPreview() {
@ -69,4 +71,8 @@ public class ControlParser {
public VideoCodec getVideoCodec() { public VideoCodec getVideoCodec() {
return VideoCodec.fromValue(videoCodec); return VideoCodec.fromValue(videoCodec);
} }
public Engine getEngine() {
return Engine.fromValue(engine);
}
} }

@ -0,0 +1,48 @@
package com.otaliastudios.cameraview.controls;
import com.otaliastudios.cameraview.CameraView;
import androidx.annotation.Nullable;
/**
* The engine to be used.
*
* @see CameraView#setEngine(Engine)
*/
public enum Engine implements Control {
/**
* Camera1 based engine.
*/
CAMERA1(0),
/**
* Camera2 based engine. For API versions older than 21,
* the system falls back to {@link #CAMERA1}.
*/
CAMERA2(1);
final static Engine DEFAULT = CAMERA1;
private int value;
Engine(int value) {
this.value = value;
}
int value() {
return value;
}
@Nullable
static Engine fromValue(int value) {
Engine[] list = Engine.values();
for (Engine action : list) {
if (action.value() == value) {
return action;
}
}
return null;
}
}

@ -494,15 +494,13 @@ public abstract class CameraEngine implements
@SuppressWarnings("unused") @SuppressWarnings("unused")
@NonNull @NonNull
@VisibleForTesting public final SizeSelector getPictureSizeSelector() {
final SizeSelector getPictureSizeSelector() {
return mPictureSizeSelector; return mPictureSizeSelector;
} }
@SuppressWarnings("unused") @SuppressWarnings("unused")
@NonNull @NonNull
@VisibleForTesting public final SizeSelector getVideoSizeSelector() {
final SizeSelector getVideoSizeSelector() {
return mVideoSizeSelector; return mVideoSizeSelector;
} }

@ -108,6 +108,11 @@
<enum name="glSurface" value="2" /> <enum name="glSurface" value="2" />
</attr> </attr>
<attr name="cameraEngine" format="enum">
<enum name="camera1" value="0" />
<enum name="camera2" value="1" />
</attr>
<attr name="cameraPlaySounds" format="boolean" /> <attr name="cameraPlaySounds" format="boolean" />
<attr name="cameraVideoMaxSize" format="float" /> <attr name="cameraVideoMaxSize" format="float" />

Loading…
Cancel
Save