Fix tests, improve performance

pull/502/head
Mattia Iavarone 6 years ago
parent e83206be7a
commit bff7bc4320
  1. 13
      cameraview/src/main/java/com/otaliastudios/cameraview/overlay/Overlay.java
  2. 27
      cameraview/src/main/java/com/otaliastudios/cameraview/overlay/OverlayLayout.java
  3. 7
      cameraview/src/main/java/com/otaliastudios/cameraview/picture/SnapshotGlPictureRecorder.java
  4. 32
      cameraview/src/main/java/com/otaliastudios/cameraview/preview/CameraPreview.java
  5. 3
      cameraview/src/main/java/com/otaliastudios/cameraview/preview/GlCameraPreview.java
  6. 2
      cameraview/src/main/java/com/otaliastudios/cameraview/preview/SurfaceCameraPreview.java
  7. 2
      cameraview/src/main/java/com/otaliastudios/cameraview/preview/TextureCameraPreview.java
  8. 19
      cameraview/src/main/java/com/otaliastudios/cameraview/video/SnapshotVideoRecorder.java
  9. 18
      cameraview/src/main/java/com/otaliastudios/cameraview/video/encoding/TextureMediaEncoder.java
  10. 2
      cameraview/src/main/java/com/otaliastudios/cameraview/video/encoding/VideoMediaEncoder.java

@ -15,8 +15,19 @@ public interface Overlay {
/** /**
* Called for this overlay to draw itself on the specified target and canvas. * Called for this overlay to draw itself on the specified target and canvas.
*
* @param target target * @param target target
* @param canvas target canvas * @param canvas target canvas
*/ */
void draw(@NonNull Target target, @NonNull Canvas canvas); void drawOn(@NonNull Target target, @NonNull Canvas canvas);
/**
* Called to understand if this overlay would like to draw onto the given
* target or not. If true is returned, {@link #drawOn(Target, Canvas)} can be
* called at a future time.
*
* @param target the target
* @return true to draw on it
*/
boolean drawsOn(@NonNull Target target);
} }

@ -81,7 +81,18 @@ public class OverlayLayout extends FrameLayout implements Overlay {
@Override @Override
public void draw(Canvas canvas) { public void draw(Canvas canvas) {
LOG.i("normal draw called."); LOG.i("normal draw called.");
draw(Target.PREVIEW, canvas); if (drawsOn(Target.PREVIEW)) {
drawOn(Target.PREVIEW, canvas);
}
}
@Override
public boolean drawsOn(@NonNull Target target) {
for (int i = 0; i < getChildCount(); i++) {
LayoutParams params = (LayoutParams) getChildAt(i).getLayoutParams();
if (params.drawsOn(target)) return true;
}
return false;
} }
/** /**
@ -96,7 +107,7 @@ public class OverlayLayout extends FrameLayout implements Overlay {
* @param canvas the canvas * @param canvas the canvas
*/ */
@Override @Override
public void draw(@NonNull Target target, @NonNull Canvas canvas) { public void drawOn(@NonNull Target target, @NonNull Canvas canvas) {
synchronized (this) { synchronized (this) {
currentTarget = target; currentTarget = target;
switch (target) { switch (target) {
@ -134,11 +145,7 @@ public class OverlayLayout extends FrameLayout implements Overlay {
@Override @Override
protected boolean drawChild(Canvas canvas, View child, long drawingTime) { protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
LayoutParams params = (LayoutParams) child.getLayoutParams(); LayoutParams params = (LayoutParams) child.getLayoutParams();
boolean draw = ((currentTarget == Target.PREVIEW && params.drawOnPreview) if (params.drawsOn(currentTarget)) {
|| (currentTarget == Target.VIDEO_SNAPSHOT && params.drawOnVideoSnapshot)
|| (currentTarget == Target.PICTURE_SNAPSHOT && params.drawOnPictureSnapshot)
);
if (draw) {
LOG.v("Performing drawing for view:", child.getClass().getSimpleName(), LOG.v("Performing drawing for view:", child.getClass().getSimpleName(),
"target:", currentTarget, "target:", currentTarget,
"params:", params); "params:", params);
@ -173,6 +180,12 @@ public class OverlayLayout extends FrameLayout implements Overlay {
} }
} }
private boolean drawsOn(@NonNull Target target) {
return ((target == Target.PREVIEW && drawOnPreview)
|| (target == Target.VIDEO_SNAPSHOT && drawOnVideoSnapshot)
|| (target == Target.PICTURE_SNAPSHOT && drawOnPictureSnapshot));
}
@NonNull @NonNull
@Override @Override
public String toString() { public String toString() {

@ -35,9 +35,6 @@ import androidx.annotation.Nullable;
import android.view.Surface; import android.view.Surface;
import java.util.ArrayList;
import java.util.List;
public class SnapshotGlPictureRecorder extends PictureRecorder { public class SnapshotGlPictureRecorder extends PictureRecorder {
private static final String TAG = SnapshotGlPictureRecorder.class.getSimpleName(); private static final String TAG = SnapshotGlPictureRecorder.class.getSimpleName();
@ -61,7 +58,7 @@ public class SnapshotGlPictureRecorder extends PictureRecorder {
mPreview = preview; mPreview = preview;
mOutputRatio = outputRatio; mOutputRatio = outputRatio;
mOverlay = overlay; mOverlay = overlay;
mHasOverlay = overlay != null; mHasOverlay = overlay != null && overlay.drawsOn(Overlay.Target.PICTURE_SNAPSHOT);
} }
@TargetApi(Build.VERSION_CODES.KITKAT) @TargetApi(Build.VERSION_CODES.KITKAT)
@ -168,7 +165,7 @@ public class SnapshotGlPictureRecorder extends PictureRecorder {
try { try {
final Canvas surfaceCanvas = mOverlaySurface.lockCanvas(null); final Canvas surfaceCanvas = mOverlaySurface.lockCanvas(null);
surfaceCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); surfaceCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
mOverlay.draw(Overlay.Target.PICTURE_SNAPSHOT, surfaceCanvas); mOverlay.drawOn(Overlay.Target.PICTURE_SNAPSHOT, surfaceCanvas);
mOverlaySurface.unlockCanvasAndPost(surfaceCanvas); mOverlaySurface.unlockCanvasAndPost(surfaceCanvas);
} catch (Surface.OutOfResourcesException e) { } catch (Surface.OutOfResourcesException e) {
LOG.w("Got Surface.OutOfResourcesException while drawing picture overlays", e); LOG.w("Got Surface.OutOfResourcesException while drawing picture overlays", e);

@ -1,14 +1,21 @@
package com.otaliastudios.cameraview.preview; package com.otaliastudios.cameraview.preview;
import android.content.Context; import android.content.Context;
import androidx.annotation.CallSuper;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import android.os.Handler;
import android.os.Looper;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.ViewParent; import android.view.ViewParent;
import com.google.android.gms.tasks.TaskCompletionSource;
import com.google.android.gms.tasks.Tasks;
import com.otaliastudios.cameraview.CameraLogger; import com.otaliastudios.cameraview.CameraLogger;
import com.otaliastudios.cameraview.engine.CameraEngine; import com.otaliastudios.cameraview.engine.CameraEngine;
import com.otaliastudios.cameraview.internal.utils.Op; import com.otaliastudios.cameraview.internal.utils.Op;
@ -240,7 +247,32 @@ public abstract class CameraPreview<T extends View, Output> {
* Called by the hosting {@link com.otaliastudios.cameraview.CameraView}, * Called by the hosting {@link com.otaliastudios.cameraview.CameraView},
* this is a lifecycle event. * this is a lifecycle event.
*/ */
@CallSuper
public void onDestroy() { public void onDestroy() {
if (Thread.currentThread() == Looper.getMainLooper().getThread()) {
onDestroyView();
} else {
// Do this on the UI thread and wait.
Handler ui = new Handler(Looper.getMainLooper());
final TaskCompletionSource<Void> task = new TaskCompletionSource<>();
ui.post(new Runnable() {
@Override
public void run() {
onDestroyView();
task.setResult(null);
}
});
try { Tasks.await(task.getTask()); } catch (Exception ignore) {}
}
}
/**
* At this point we undo the work that was done during {@link #onCreateView(Context, ViewGroup)},
* which basically means removing the root view from the hierarchy.
*/
@SuppressWarnings("WeakerAccess")
@UiThread
protected void onDestroyView() {
View root = getRootView(); View root = getRootView();
ViewParent parent = root.getParent(); ViewParent parent = root.getParent();
if (parent instanceof ViewGroup) { if (parent instanceof ViewGroup) {

@ -5,7 +5,6 @@ import android.graphics.SurfaceTexture;
import android.opengl.GLSurfaceView; import android.opengl.GLSurfaceView;
import android.opengl.Matrix; import android.opengl.Matrix;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import android.view.LayoutInflater; import android.view.LayoutInflater;
@ -91,7 +90,7 @@ public class GlCameraPreview extends CameraPreview<GLSurfaceView, SurfaceTexture
mDispatched = false; mDispatched = false;
} }
}); });
parent.addView(root, 1); parent.addView(root, 0);
mRootView = root; mRootView = root;
return glView; return glView;
} }

@ -34,7 +34,7 @@ public class SurfaceCameraPreview extends CameraPreview<SurfaceView, SurfaceHold
@Override @Override
protected SurfaceView onCreateView(@NonNull Context context, @NonNull ViewGroup parent) { protected SurfaceView onCreateView(@NonNull Context context, @NonNull ViewGroup parent) {
View root = LayoutInflater.from(context).inflate(R.layout.cameraview_surface_view, parent, false); View root = LayoutInflater.from(context).inflate(R.layout.cameraview_surface_view, parent, false);
parent.addView(root, 1); 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();
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

@ -38,7 +38,7 @@ public class TextureCameraPreview extends CameraPreview<TextureView, SurfaceText
@Override @Override
protected TextureView onCreateView(@NonNull Context context, @NonNull ViewGroup parent) { protected TextureView onCreateView(@NonNull Context context, @NonNull ViewGroup parent) {
View root = LayoutInflater.from(context).inflate(R.layout.cameraview_texture_view, parent, false); View root = LayoutInflater.from(context).inflate(R.layout.cameraview_texture_view, parent, false);
parent.addView(root, 1); parent.addView(root, 0);
TextureView texture = root.findViewById(R.id.texture_view); TextureView texture = root.findViewById(R.id.texture_view);
texture.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() { texture.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {

@ -8,15 +8,11 @@ import android.opengl.EGL14;
import android.os.Build; import android.os.Build;
import android.view.Surface; import android.view.Surface;
import java.util.ArrayList;
import java.util.List;
import com.otaliastudios.cameraview.CameraLogger; import com.otaliastudios.cameraview.CameraLogger;
import com.otaliastudios.cameraview.overlay.Overlay; import com.otaliastudios.cameraview.overlay.Overlay;
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.engine.CameraEngine; import com.otaliastudios.cameraview.engine.CameraEngine;
import com.otaliastudios.cameraview.engine.offset.Reference;
import com.otaliastudios.cameraview.internal.egl.EglViewport; import com.otaliastudios.cameraview.internal.egl.EglViewport;
import com.otaliastudios.cameraview.preview.GlCameraPreview; import com.otaliastudios.cameraview.preview.GlCameraPreview;
import com.otaliastudios.cameraview.preview.RendererFrameCallback; import com.otaliastudios.cameraview.preview.RendererFrameCallback;
@ -49,9 +45,7 @@ public class SnapshotVideoRecorder extends VideoRecorder implements RendererFram
private static final int STATE_NOT_RECORDING = 1; private static final int STATE_NOT_RECORDING = 1;
private MediaEncoderEngine mEncoderEngine; private MediaEncoderEngine mEncoderEngine;
private CameraEngine mEngine;
private GlCameraPreview mPreview; private GlCameraPreview mPreview;
private boolean mFlipped;
private int mCurrentState = STATE_NOT_RECORDING; private int mCurrentState = STATE_NOT_RECORDING;
private int mDesiredState = STATE_NOT_RECORDING; private int mDesiredState = STATE_NOT_RECORDING;
@ -70,16 +64,14 @@ public class SnapshotVideoRecorder extends VideoRecorder implements RendererFram
int overlayRotation) { int overlayRotation) {
super(engine); super(engine);
mPreview = preview; mPreview = preview;
mEngine = engine;
mOverlay = overlay; mOverlay = overlay;
mHasOverlay = overlay != null; mHasOverlay = overlay != null && overlay.drawsOn(Overlay.Target.VIDEO_SNAPSHOT);
mOverlayRotation = overlayRotation; mOverlayRotation = overlayRotation;
} }
@Override @Override
protected void onStart() { protected void onStart() {
mPreview.addRendererFrameCallback(this); mPreview.addRendererFrameCallback(this);
mFlipped = mEngine.getAngles().flip(Reference.SENSOR, Reference.VIEW);
mDesiredState = STATE_RECORDING; mDesiredState = STATE_RECORDING;
dispatchVideoRecordingStart(); dispatchVideoRecordingStart();
} }
@ -131,11 +123,10 @@ public class SnapshotVideoRecorder extends VideoRecorder implements RendererFram
mResult.videoFrameRate, mResult.videoFrameRate,
mResult.rotation, mResult.rotation,
type, mTextureId, type, mTextureId,
mHasOverlay ? mOverlayTextureId : TextureMediaEncoder.NO_TEXTURE,
mOverlayRotation,
scaleX, scaleY, scaleX, scaleY,
mFlipped, EGL14.eglGetCurrentContext(),
EGL14.eglGetCurrentContext() mHasOverlay ? mOverlayTextureId : TextureMediaEncoder.NO_TEXTURE,
mOverlayRotation
); );
TextureMediaEncoder videoEncoder = new TextureMediaEncoder(config); TextureMediaEncoder videoEncoder = new TextureMediaEncoder(config);
@ -165,7 +156,7 @@ public class SnapshotVideoRecorder extends VideoRecorder implements RendererFram
try { try {
final Canvas surfaceCanvas = mOverlaySurface.lockCanvas(null); final Canvas surfaceCanvas = mOverlaySurface.lockCanvas(null);
surfaceCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); surfaceCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
mOverlay.draw(Overlay.Target.VIDEO_SNAPSHOT, surfaceCanvas); mOverlay.drawOn(Overlay.Target.VIDEO_SNAPSHOT, surfaceCanvas);
mOverlaySurface.unlockCanvasAndPost(surfaceCanvas); mOverlaySurface.unlockCanvasAndPost(surfaceCanvas);
} catch (Surface.OutOfResourcesException e) { } catch (Surface.OutOfResourcesException e) {
LOG.w("Got Surface.OutOfResourcesException while drawing video overlays", e); LOG.w("Got Surface.OutOfResourcesException while drawing video overlays", e);

@ -31,25 +31,27 @@ public class TextureMediaEncoder extends VideoMediaEncoder<TextureMediaEncoder.C
int overlayTextureId; int overlayTextureId;
float scaleX; float scaleX;
float scaleY; float scaleY;
boolean scaleFlipped;
EGLContext eglContext; EGLContext eglContext;
int transformRotation; int transformRotation;
int overlayTransformRotation; int overlayTransformRotation;
public Config(int width, int height, int bitRate, int frameRate, int rotation, String mimeType, public Config(int width, int height,
int textureId, int overlayTextureId, int overlayTransformRotation, int bitRate, int frameRate,
float scaleX, float scaleY, boolean scaleFlipped, EGLContext eglContext) { int rotation, @NonNull String mimeType,
int textureId,
float scaleX, float scaleY,
@NonNull EGLContext eglContext,
int overlayTextureId, int overlayRotation) {
// We rotate the texture using transformRotation. Pass rotation=0 to super so that // We rotate the texture using transformRotation. Pass rotation=0 to super so that
// no rotation metadata is written into the output file. // no rotation metadata is written into the output file.
super(width, height, bitRate, frameRate, 0, mimeType); super(width, height, bitRate, frameRate, 0, mimeType);
this.transformRotation = rotation; this.transformRotation = rotation;
this.textureId = textureId; this.textureId = textureId;
this.overlayTextureId = overlayTextureId;
this.overlayTransformRotation = overlayTransformRotation;
this.scaleX = scaleX; this.scaleX = scaleX;
this.scaleY = scaleY; this.scaleY = scaleY;
this.scaleFlipped = scaleFlipped;
this.eglContext = eglContext; this.eglContext = eglContext;
this.overlayTextureId = overlayTextureId;
this.overlayTransformRotation = overlayRotation;
} }
} }
@ -149,8 +151,6 @@ public class TextureMediaEncoder extends VideoMediaEncoder<TextureMediaEncoder.C
LOG.v("onEvent", "frameNum:", thisFrameNum, "realFrameNum:", mFrameNum, "calling drainOutput."); LOG.v("onEvent", "frameNum:", thisFrameNum, "realFrameNum:", mFrameNum, "calling drainOutput.");
drainOutput(false); drainOutput(false);
// Future note: passing scale values to the viewport? They are scaleX and scaleY,
// but flipped based on the mConfig.scaleFlipped boolean.
LOG.v("onEvent", "frameNum:", thisFrameNum, "realFrameNum:", mFrameNum, "calling drawFrame."); LOG.v("onEvent", "frameNum:", thisFrameNum, "realFrameNum:", mFrameNum, "calling drawFrame.");
mViewport.drawFrame(mConfig.textureId, transform); mViewport.drawFrame(mConfig.textureId, transform);
if (hasOverlay) { if (hasOverlay) {

@ -42,7 +42,7 @@ abstract class VideoMediaEncoder<C extends VideoMediaEncoder.Config> extends Med
int rotation; int rotation;
String mimeType; String mimeType;
Config(int width, int height, int bitRate, int frameRate, int rotation, String mimeType) { Config(int width, int height, int bitRate, int frameRate, int rotation, @NonNull String mimeType) {
this.width = width; this.width = width;
this.height = height; this.height = height;
this.bitRate = bitRate; this.bitRate = bitRate;

Loading…
Cancel
Save