Refactor CameraPreviews

pull/494/head
Mattia Iavarone 6 years ago
parent 62cfb5d7f0
commit 98741c2bfa
  1. 5
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/preview/CameraPreviewTest.java
  2. 4
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/preview/GlCameraPreviewTest.java
  3. 4
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/preview/SurfaceCameraPreviewTest.java
  4. 4
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/preview/TextureCameraPreviewTest.java
  5. 6
      cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java
  6. 12
      cameraview/src/main/java/com/otaliastudios/cameraview/preview/CameraPreview.java
  7. 94
      cameraview/src/main/java/com/otaliastudios/cameraview/preview/GlCameraPreview.java
  8. 5
      cameraview/src/main/java/com/otaliastudios/cameraview/preview/SurfaceCameraPreview.java
  9. 4
      cameraview/src/main/java/com/otaliastudios/cameraview/preview/TextureCameraPreview.java

@ -27,7 +27,7 @@ public abstract class CameraPreviewTest extends BaseTest {
private final static long DELAY = 4000; private final static long DELAY = 4000;
protected abstract CameraPreview createPreview(Context context, ViewGroup parent, CameraPreview.SurfaceCallback callback); protected abstract CameraPreview createPreview(Context context, ViewGroup parent);
@Rule @Rule
public ActivityTestRule<TestActivity> rule = new ActivityTestRule<>(TestActivity.class); public ActivityTestRule<TestActivity> rule = new ActivityTestRule<>(TestActivity.class);
@ -69,7 +69,8 @@ public abstract class CameraPreviewTest extends BaseTest {
} }
}).when(callback).onSurfaceDestroyed(); }).when(callback).onSurfaceDestroyed();
preview = createPreview(a, a.getContentView(), callback); preview = createPreview(a, a.getContentView());
preview.setSurfaceCallback(callback);
} }
}); });
} }

@ -14,8 +14,8 @@ import org.junit.runner.RunWith;
public class GlCameraPreviewTest extends CameraPreviewTest { public class GlCameraPreviewTest extends CameraPreviewTest {
@Override @Override
protected CameraPreview createPreview(Context context, ViewGroup parent, CameraPreview.SurfaceCallback callback) { protected CameraPreview createPreview(Context context, ViewGroup parent) {
return new GlCameraPreview(context, parent, callback); return new GlCameraPreview(context, parent);
} }
@Override @Override

@ -18,8 +18,8 @@ import org.junit.runner.RunWith;
public class SurfaceCameraPreviewTest extends CameraPreviewTest { public class SurfaceCameraPreviewTest extends CameraPreviewTest {
@Override @Override
protected CameraPreview createPreview(Context context, ViewGroup parent, CameraPreview.SurfaceCallback callback) { protected CameraPreview createPreview(Context context, ViewGroup parent) {
return new SurfaceCameraPreview(context, parent, callback); return new SurfaceCameraPreview(context, parent);
} }
@Override @Override

@ -18,8 +18,8 @@ import org.junit.runner.RunWith;
public class TextureCameraPreviewTest extends CameraPreviewTest { public class TextureCameraPreviewTest extends CameraPreviewTest {
@Override @Override
protected CameraPreview createPreview(Context context, ViewGroup parent, CameraPreview.SurfaceCallback callback) { protected CameraPreview createPreview(Context context, ViewGroup parent) {
return new TextureCameraPreview(context, parent, callback); return new TextureCameraPreview(context, parent);
} }
@Override @Override

@ -276,16 +276,16 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
LOG.w("preview:", "isHardwareAccelerated:", isHardwareAccelerated()); LOG.w("preview:", "isHardwareAccelerated:", isHardwareAccelerated());
switch (preview) { switch (preview) {
case SURFACE: case SURFACE:
return new SurfaceCameraPreview(context, container, null); return new SurfaceCameraPreview(context, container);
case TEXTURE: { case TEXTURE: {
if (isHardwareAccelerated()) { if (isHardwareAccelerated()) {
// TextureView is not supported without hardware acceleration. // TextureView is not supported without hardware acceleration.
return new TextureCameraPreview(context, container, null); return new TextureCameraPreview(context, container);
} }
} }
case GL_SURFACE: default: { case GL_SURFACE: default: {
mPreview = Preview.GL_SURFACE; mPreview = Preview.GL_SURFACE;
return new GlCameraPreview(context, container, null); return new GlCameraPreview(context, container);
} }
} }
} }

@ -67,11 +67,9 @@ public abstract class CameraPreview<T extends View, Output> {
* Creates a new preview. * Creates a new preview.
* @param context a context * @param context a context
* @param parent where to inflate our view * @param parent where to inflate our view
* @param callback the callback
*/ */
public CameraPreview(@NonNull Context context, @NonNull ViewGroup parent, @Nullable SurfaceCallback callback) { public CameraPreview(@NonNull Context context, @NonNull ViewGroup parent) {
mView = onCreateView(context, parent); mView = onCreateView(context, parent);
mSurfaceCallback = callback;
} }
/** /**
@ -79,10 +77,12 @@ public abstract class CameraPreview<T extends View, Output> {
* @param callback a callback * @param callback a callback
*/ */
public final void setSurfaceCallback(@Nullable SurfaceCallback callback) { public final void setSurfaceCallback(@Nullable SurfaceCallback callback) {
if (hasSurface() && mSurfaceCallback != null) {
mSurfaceCallback.onSurfaceDestroyed();
}
mSurfaceCallback = callback; mSurfaceCallback = callback;
// If surface already available, dispatch. if (hasSurface() && mSurfaceCallback != null) {
if (mOutputSurfaceWidth != 0 || mOutputSurfaceHeight != 0) { mSurfaceCallback.onSurfaceAvailable();
if (callback != null) callback.onSurfaceAvailable();
} }
} }

@ -57,7 +57,7 @@ import javax.microedition.khronos.opengles.GL10;
* Callbacks are guaranteed to be called on the renderer thread, which means that we can fetch * Callbacks are guaranteed to be called on the renderer thread, which means that we can fetch
* the GL context that was created and is managed by the {@link GLSurfaceView}. * the GL context that was created and is managed by the {@link GLSurfaceView}.
*/ */
public class GlCameraPreview extends CameraPreview<GLSurfaceView, SurfaceTexture> implements GLSurfaceView.Renderer { public class GlCameraPreview extends CameraPreview<GLSurfaceView, SurfaceTexture> {
private boolean mDispatched; private boolean mDispatched;
private final float[] mTransformMatrix = new float[16]; private final float[] mTransformMatrix = new float[16];
@ -68,33 +68,11 @@ public class GlCameraPreview extends CameraPreview<GLSurfaceView, SurfaceTexture
@VisibleForTesting float mCropScaleX = 1F; @VisibleForTesting float mCropScaleX = 1F;
@VisibleForTesting float mCropScaleY = 1F; @VisibleForTesting float mCropScaleY = 1F;
private View mRootView; private View mRootView;
private final GLSurfaceView.Renderer mRenderer;
/** public GlCameraPreview(@NonNull Context context, @NonNull ViewGroup parent) {
* Method specific to the GL preview. Adds a {@link RendererFrameCallback} super(context, parent);
* to receive renderer frame events. mRenderer = new Renderer();
* @param callback a callback
*/
public void addRendererFrameCallback(@NonNull final RendererFrameCallback callback) {
getView().queueEvent(new Runnable() {
@Override
public void run() {
mRendererFrameCallbacks.add(callback);
if (mOutputTextureId != 0) callback.onRendererTextureCreated(mOutputTextureId);
}
});
}
/**
* Method specific to the GL preview. Removes a {@link RendererFrameCallback}
* that was previously added to receive renderer frame events.
* @param callback a callback
*/
public void removeRendererFrameCallback(@NonNull final RendererFrameCallback callback) {
mRendererFrameCallbacks.remove(callback);
}
public GlCameraPreview(@NonNull Context context, @NonNull ViewGroup parent, @Nullable SurfaceCallback callback) {
super(context, parent, callback);
} }
@NonNull @NonNull
@ -103,11 +81,8 @@ public class GlCameraPreview extends CameraPreview<GLSurfaceView, SurfaceTexture
ViewGroup root = (ViewGroup) LayoutInflater.from(context).inflate(R.layout.cameraview_gl_view, parent, false); ViewGroup root = (ViewGroup) LayoutInflater.from(context).inflate(R.layout.cameraview_gl_view, parent, false);
GLSurfaceView glView = root.findViewById(R.id.gl_surface_view); GLSurfaceView glView = root.findViewById(R.id.gl_surface_view);
glView.setEGLContextClientVersion(2); glView.setEGLContextClientVersion(2);
glView.setRenderer(this); glView.setRenderer(mRenderer);
glView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); glView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
// Tried these 2 to remove the black background, does not work.
// glView.getHolder().setFormat(PixelFormat.TRANSLUCENT);
// glView.setZOrderMediaOverlay(true);
glView.getHolder().addCallback(new SurfaceHolder.Callback() { glView.getHolder().addCallback(new SurfaceHolder.Callback() {
public void surfaceCreated(SurfaceHolder holder) {} public void surfaceCreated(SurfaceHolder holder) {}
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { }
@ -158,6 +133,11 @@ public class GlCameraPreview extends CameraPreview<GLSurfaceView, SurfaceTexture
} }
} }
/**
* The core renderer that performs the actual drawing operations.
*/
public class Renderer implements GLSurfaceView.Renderer {
@RendererThread @RendererThread
@Override @Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) { public void onSurfaceCreated(GL10 gl, EGLConfig config) {
@ -184,14 +164,13 @@ public class GlCameraPreview extends CameraPreview<GLSurfaceView, SurfaceTexture
} }
@RendererThread @RendererThread
@SuppressWarnings("StatementWithEmptyBody")
@Override @Override
public void onSurfaceChanged(GL10 gl, final int width, final int height) { public void onSurfaceChanged(GL10 gl, final int width, final int height) {
gl.glViewport(0, 0, width, height); gl.glViewport(0, 0, width, height);
if (!mDispatched) { if (!mDispatched) {
dispatchOnSurfaceAvailable(width, height); dispatchOnSurfaceAvailable(width, height);
mDispatched = true; mDispatched = true;
} else if (width != mOutputSurfaceWidth || height != mOutputSurfaceHeight){ } else if (width != mOutputSurfaceWidth || height != mOutputSurfaceHeight) {
dispatchOnSurfaceSizeChanged(width, height); dispatchOnSurfaceSizeChanged(width, height);
} }
} }
@ -218,10 +197,9 @@ public class GlCameraPreview extends CameraPreview<GLSurfaceView, SurfaceTexture
} }
if (isCropping()) { if (isCropping()) {
// Scaling is easy. However: // Scaling is easy, but we must also translate before:
// If the view is 10x1000 (very tall), it will show only the left strip of the preview (not the center one). // If the view is 10x1000 (very tall), it will show only the left strip of the preview (not the center one).
// If the view is 1000x10 (very large), it will show only the bottom strip of the preview (not the center one). // If the view is 1000x10 (very large), it will show only the bottom strip of the preview (not the center one).
// So we must use Matrix.translateM, and it must happen before the crop.
float translX = (1F - mCropScaleX) / 2F; float translX = (1F - mCropScaleX) / 2F;
float translY = (1F - mCropScaleY) / 2F; float translY = (1F - mCropScaleY) / 2F;
Matrix.translateM(mTransformMatrix, 0, translX, translY, 0); Matrix.translateM(mTransformMatrix, 0, translX, translY, 0);
@ -234,6 +212,7 @@ public class GlCameraPreview extends CameraPreview<GLSurfaceView, SurfaceTexture
callback.onRendererFrame(mInputSurfaceTexture, mCropScaleX, mCropScaleY); callback.onRendererFrame(mInputSurfaceTexture, mCropScaleX, mCropScaleY);
} }
} }
}
@NonNull @NonNull
@Override @Override
@ -262,7 +241,7 @@ public class GlCameraPreview extends CameraPreview<GLSurfaceView, SurfaceTexture
* platform when its window is positioned asynchronously. * platform when its window is positioned asynchronously.
* *
* But to support older platforms, this seem to work - computing scale values and requesting a new frame, * But to support older platforms, this seem to work - computing scale values and requesting a new frame,
* then drawing it with a scaled transformation matrix. See {@link #onDrawFrame(GL10)}. * then drawing it with a scaled transformation matrix. See {@link Renderer#onDrawFrame(GL10)}.
*/ */
@Override @Override
protected void crop(@NonNull Op<Void> op) { protected void crop(@NonNull Op<Void> op) {
@ -285,4 +264,47 @@ public class GlCameraPreview extends CameraPreview<GLSurfaceView, SurfaceTexture
} }
op.end(null); op.end(null);
} }
/**
* Method specific to the GL preview. Adds a {@link RendererFrameCallback}
* to receive renderer frame events.
* @param callback a callback
*/
public void addRendererFrameCallback(@NonNull final RendererFrameCallback callback) {
getView().queueEvent(new Runnable() {
@Override
public void run() {
mRendererFrameCallbacks.add(callback);
if (mOutputTextureId != 0) callback.onRendererTextureCreated(mOutputTextureId);
}
});
}
/**
* Method specific to the GL preview. Removes a {@link RendererFrameCallback}
* that was previously added to receive renderer frame events.
* @param callback a callback
*/
public void removeRendererFrameCallback(@NonNull final RendererFrameCallback callback) {
mRendererFrameCallbacks.remove(callback);
}
/**
* Returns the output GL texture id.
* @return the output GL texture id
*/
@SuppressWarnings("unused")
protected int getTextureId() {
return mOutputTextureId;
}
/**
* Returns the GL renderer.
* @return the GL renderer
*/
@SuppressWarnings("unused")
@NonNull
protected GLSurfaceView.Renderer getRenderer() {
return mRenderer;
}
} }

@ -26,8 +26,8 @@ public class SurfaceCameraPreview extends CameraPreview<SurfaceView, SurfaceHold
private boolean mDispatched; private boolean mDispatched;
private View mRootView; private View mRootView;
public SurfaceCameraPreview(@NonNull Context context, @NonNull ViewGroup parent, @Nullable SurfaceCallback callback) { public SurfaceCameraPreview(@NonNull Context context, @NonNull ViewGroup parent) {
super(context, parent, callback); super(context, parent);
} }
@NonNull @NonNull
@ -37,7 +37,6 @@ public class SurfaceCameraPreview extends CameraPreview<SurfaceView, SurfaceHold
parent.addView(root, 0); parent.addView(root, 0);
SurfaceView surfaceView = root.findViewById(R.id.surface_view); SurfaceView surfaceView = root.findViewById(R.id.surface_view);
final SurfaceHolder holder = surfaceView.getHolder(); final SurfaceHolder holder = surfaceView.getHolder();
//noinspection deprecation
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
holder.addCallback(new SurfaceHolder.Callback() { holder.addCallback(new SurfaceHolder.Callback() {

@ -30,8 +30,8 @@ public class TextureCameraPreview extends CameraPreview<TextureView, SurfaceText
private View mRootView; private View mRootView;
public TextureCameraPreview(@NonNull Context context, @NonNull ViewGroup parent, @Nullable SurfaceCallback callback) { public TextureCameraPreview(@NonNull Context context, @NonNull ViewGroup parent) {
super(context, parent, callback); super(context, parent);
} }
@NonNull @NonNull

Loading…
Cancel
Save