From 9c912e925bdef23c9544ad8e3239f6365a5a671c Mon Sep 17 00:00:00 2001 From: Mattia Iavarone Date: Mon, 31 Jul 2017 22:44:27 +0200 Subject: [PATCH] Correct orientation for captured preview frames. Bugs fixed. Refactoring useless classes --- .../api16/com/flurgle/camerakit/Camera1.java | 95 ++++++----- .../flurgle/camerakit/ProcessStillTask.java | 54 ------ .../com/flurgle/camerakit/CameraImpl.java | 4 +- .../com/flurgle/camerakit/CameraListener.java | 4 - .../com/flurgle/camerakit/CameraView.java | 156 +++++++++++------- .../utils/com/flurgle/camerakit/Rotation.java | 50 ------ 6 files changed, 148 insertions(+), 215 deletions(-) delete mode 100644 camerakit/src/main/api16/com/flurgle/camerakit/ProcessStillTask.java delete mode 100644 camerakit/src/main/utils/com/flurgle/camerakit/Rotation.java diff --git a/camerakit/src/main/api16/com/flurgle/camerakit/Camera1.java b/camerakit/src/main/api16/com/flurgle/camerakit/Camera1.java index e14a04ae..7bfa4ba8 100644 --- a/camerakit/src/main/api16/com/flurgle/camerakit/Camera1.java +++ b/camerakit/src/main/api16/com/flurgle/camerakit/Camera1.java @@ -46,7 +46,7 @@ class Camera1 extends CameraImpl { private MediaRecorder mMediaRecorder; private File mVideoFile; private Camera.AutoFocusCallback mAutofocusCallback; - private boolean capturingImage = false; + private boolean isCapturingImage = false; private int mDisplayOffset; private int mDeviceOrientation; @@ -65,7 +65,7 @@ class Camera1 extends CameraImpl { private Handler mHandler = new Handler(); private ConstantMapper.MapperImpl mMapper = new ConstantMapper.Mapper1(); - Camera1(CameraListener callback, PreviewImpl preview) { + Camera1(CameraView.CameraListenerWrapper callback, PreviewImpl preview) { super(callback, preview); preview.setCallback(new PreviewImpl.OnPreviewSurfaceChangedCallback() { @Override @@ -144,9 +144,20 @@ class Camera1 extends CameraImpl { mLatitude = latitude; mLongitude = longitude; if (mCameraParameters != null) { - mCameraParameters.setGpsLatitude(latitude); - mCameraParameters.setGpsLongitude(longitude); - mCamera.setParameters(mCameraParameters); + // Sometimes this will fail... I have no idea why. + // Since native_setParameters is quite a black box, there's nothing we can do about it. + try { + mCameraParameters.setGpsLatitude(latitude); + mCameraParameters.setGpsLongitude(longitude); + // mCameraParameters.setGpsAltitude(0); + // mCameraParameters.setGpsTimestamp(System.currentTimeMillis()); + // mCameraParameters.setGpsProcessingMethod("GPS"); + mCamera.setParameters(mCameraParameters); + } catch (Exception e) { + // Reset or everything after will throw as well. + e.printStackTrace(); + mCameraParameters = mCamera.getParameters(); + } } } @@ -260,43 +271,52 @@ class Camera1 extends CameraImpl { @Override void captureImage() { + if (isCapturingImage) return; + if (mCamera == null) return; switch (mSessionType) { case SESSION_TYPE_PICTURE: - // Null check required for camera here as is briefly null when View is detached - if (!capturingImage && mCamera != null) { - // Set boolean to wait for image callback - capturingImage = true; - Camera.Parameters parameters = mCamera.getParameters(); - parameters.setRotation(computeExifOrientation()); - mCamera.setParameters(parameters); - mCamera.takePicture(null, null, null, - new Camera.PictureCallback() { - @Override - public void onPictureTaken(byte[] data, Camera camera) { - mCameraListener.onPictureTaken(data); - // Reset capturing state to allow photos to be taken - capturingImage = false; - camera.startPreview(); - } - }); - } else { - Log.w(TAG, "Unable, waiting for picture to be taken"); - } + // Set boolean to wait for image callback + isCapturingImage = true; + Camera.Parameters parameters = mCamera.getParameters(); + parameters.setRotation(computeExifOrientation()); + mCamera.setParameters(parameters); + mCamera.takePicture(null, null, null, + new Camera.PictureCallback() { + @Override + public void onPictureTaken(byte[] data, Camera camera) { + mCameraListener.onPictureTaken(data); + isCapturingImage = false; + camera.startPreview(); // TODO: is this needed? why? + } + }); break; case SESSION_TYPE_VIDEO: // If we are in a video session, camera captures are fast captures coming // from the preview stream. - // TODO: will calculateCaptureRotation work here? It's the only usage. Test... + // TODO: will this work while recording a video? test... + isCapturingImage = true; mCamera.setOneShotPreviewCallback(new Camera.PreviewCallback() { @Override - public void onPreviewFrame(byte[] data, Camera camera) { - new Thread(new ProcessStillTask(data, camera, calculateCaptureRotation(), new ProcessStillTask.OnStillProcessedListener() { + public void onPreviewFrame(final byte[] data, Camera camera) { + // Got to rotate the preview frame, since byte[] data here does not include + // EXIF tags automatically set by camera. So either we add EXIF, or we rotate. + final int rotation = computeExifOrientation(); + final boolean flip = rotation % 180 != 0; + final int preWidth = mPreviewSize.getWidth(); + final int preHeight = mPreviewSize.getHeight(); + final int postWidth = flip ? preHeight : preWidth; + final int postHeight = flip ? preWidth : preHeight; + final int format = mCameraParameters.getPreviewFormat(); + new Thread(new Runnable() { @Override - public void onStillProcessed(final YuvImage yuv) { - mCameraListener.onPictureTaken(yuv); + public void run() { + byte[] rotatedData = RotationHelper.rotate(data, preWidth, preHeight, rotation); + YuvImage yuv = new YuvImage(rotatedData, format, postWidth, postHeight, null); + mCameraListener.processYuvImage(yuv); + isCapturingImage = false; } - })).start(); + }).start(); } }); break; @@ -378,16 +398,6 @@ class Camera1 extends CameraImpl { return (mDeviceOrientation + mSensorOffset) % 360; } - private int calculateCaptureRotation() { - int previewRotation = computeCameraToDisplayOffset(); - if (mCameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) { - //Front is flipped - return (previewRotation + 180 + 2* mDisplayOffset + 720) % 360; - } else { - return previewRotation; - } - } - /** * This is called either on cameraView.start(), or when the underlying surface changes. @@ -411,9 +421,10 @@ class Camera1 extends CameraImpl { mCaptureSize = new Size(camcorderProfile.videoFrameWidth, camcorderProfile.videoFrameHeight); } mPreviewSize = computePreviewSize(previewSizes, mCaptureSize, mPreview.getSurfaceSize()); + Log.e("Camera1", "CaptureSize is "+mCaptureSize.toString()); + Log.e("Camera1", "PreviewSize is "+mPreviewSize.toString()); } - private void adjustCameraParameters() { boolean invertPreviewSizes = shouldFlipSizes(); // mDisplayOffset % 180 != 0; mPreview.setDesiredSize( diff --git a/camerakit/src/main/api16/com/flurgle/camerakit/ProcessStillTask.java b/camerakit/src/main/api16/com/flurgle/camerakit/ProcessStillTask.java deleted file mode 100644 index 44a63bb6..00000000 --- a/camerakit/src/main/api16/com/flurgle/camerakit/ProcessStillTask.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.flurgle.camerakit; - -import android.graphics.YuvImage; -import android.hardware.Camera; - -class ProcessStillTask implements Runnable { - - private byte[] data; - private Camera camera; - private int rotation; - private OnStillProcessedListener onStillProcessedListener; - - public ProcessStillTask(byte[] data, Camera camera, int rotation, OnStillProcessedListener onStillProcessedListener) { - this.data = data; - this.camera = camera; - this.rotation = rotation; - this.onStillProcessedListener = onStillProcessedListener; - } - - @Override - public void run() { - Camera.Parameters parameters = camera.getParameters(); - int width = parameters.getPreviewSize().width; - int height = parameters.getPreviewSize().height; - byte[] rotatedData = new Rotation(data, width, height, rotation).getYuv(); - - int postWidth; - int postHeight; - - switch (rotation) { - case 90: - case 270: - postWidth = height; - postHeight = width; - break; - - case 0: - case 180: - default: - postWidth = width; - postHeight = height; - break; - } - - YuvImage yuv = new YuvImage(rotatedData, parameters.getPreviewFormat(), postWidth, postHeight, null); - - onStillProcessedListener.onStillProcessed(yuv); - } - - interface OnStillProcessedListener { - void onStillProcessed(YuvImage yuv); - } - -} \ No newline at end of file diff --git a/camerakit/src/main/base/com/flurgle/camerakit/CameraImpl.java b/camerakit/src/main/base/com/flurgle/camerakit/CameraImpl.java index c36e2286..e5bc44d4 100644 --- a/camerakit/src/main/base/com/flurgle/camerakit/CameraImpl.java +++ b/camerakit/src/main/base/com/flurgle/camerakit/CameraImpl.java @@ -4,10 +4,10 @@ import android.support.annotation.Nullable; abstract class CameraImpl { - protected final CameraListener mCameraListener; + protected final CameraView.CameraListenerWrapper mCameraListener; protected final PreviewImpl mPreview; - CameraImpl(CameraListener callback, PreviewImpl preview) { + CameraImpl(CameraView.CameraListenerWrapper callback, PreviewImpl preview) { mCameraListener = callback; mPreview = preview; } diff --git a/camerakit/src/main/java/com/flurgle/camerakit/CameraListener.java b/camerakit/src/main/java/com/flurgle/camerakit/CameraListener.java index 39be2ce2..f4da5fd0 100644 --- a/camerakit/src/main/java/com/flurgle/camerakit/CameraListener.java +++ b/camerakit/src/main/java/com/flurgle/camerakit/CameraListener.java @@ -18,10 +18,6 @@ public abstract class CameraListener { } - public void onPictureTaken(YuvImage yuv) { - - } - public void onVideoTaken(File video) { } diff --git a/camerakit/src/main/java/com/flurgle/camerakit/CameraView.java b/camerakit/src/main/java/com/flurgle/camerakit/CameraView.java index 1a899bf1..3b4967ca 100644 --- a/camerakit/src/main/java/com/flurgle/camerakit/CameraView.java +++ b/camerakit/src/main/java/com/flurgle/camerakit/CameraView.java @@ -129,7 +129,6 @@ public class CameraView extends FrameLayout implements LifecycleObserver { } mCameraListener = new CameraListenerWrapper(); - mPreviewImpl = new TextureViewPreview(context, this); mCameraImpl = new Camera1(mCameraListener, mPreviewImpl); @@ -138,8 +137,6 @@ public class CameraView extends FrameLayout implements LifecycleObserver { setFlash(mFlash); setFocus(mFocus); setSessionType(mSessionType); - // setCaptureMethod(mMethod); - // setPermissionPolicy(mPermissions); setZoom(mZoom); setVideoQuality(mVideoQuality); setWhiteBalance(mWhiteBalance); @@ -182,8 +179,7 @@ public class CameraView extends FrameLayout implements LifecycleObserver { super.onAttachedToWindow(); if (!isInEditMode()) { WindowManager manager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE); - Display display = manager.getDefaultDisplay(); - mOrientationHelper.enable(display); + mOrientationHelper.enable(manager.getDefaultDisplay()); } } @@ -216,8 +212,7 @@ public class CameraView extends FrameLayout implements LifecycleObserver { } Size previewSize = getPreviewSize(); - if (previewSize == null) { - // Early measure. + if (previewSize == null) { // Early measure. super.onMeasure(widthMeasureSpec, heightMeasureSpec); return; } @@ -236,8 +231,8 @@ public class CameraView extends FrameLayout implements LifecycleObserver { // preview: 1600x1200 // parent: 1080x1794 - float parentHeight = MeasureSpec.getSize(heightMeasureSpec); // 1794. - float parentWidth = MeasureSpec.getSize(widthMeasureSpec); // 1080. mode = AT_MOST + float parentHeight = MeasureSpec.getSize(heightMeasureSpec); + float parentWidth = MeasureSpec.getSize(widthMeasureSpec); // mode = AT_MOST float targetRatio = previewHeight / previewWidth; float currentRatio = parentHeight / parentWidth; Log.e(TAG, "parentHeight="+parentHeight+", parentWidth="+parentWidth); @@ -273,10 +268,16 @@ public class CameraView extends FrameLayout implements LifecycleObserver { } } + + /** + * Returns whether the camera has started showing its preview. + * @return whether the camera has started + */ public boolean isStarted() { return mIsStarted; } + @Override public void setEnabled(boolean enabled) { super.setEnabled(enabled); @@ -315,7 +316,7 @@ public class CameraView extends FrameLayout implements LifecycleObserver { return; } - if (checkPermissions()) { + if (checkPermissions(mSessionType)) { mIsStarted = true; run(new Runnable() { @Override @@ -332,11 +333,11 @@ public class CameraView extends FrameLayout implements LifecycleObserver { * Throws if session = audio and manifest did not add the microphone permissions. * @return true if we can go on, false otherwise. */ - private boolean checkPermissions() { - checkPermissionsManifestOrThrow(); + private boolean checkPermissions(@SessionType int sessionType) { + checkPermissionsManifestOrThrow(sessionType); int cameraCheck = ContextCompat.checkSelfPermission(getContext(), Manifest.permission.CAMERA); int audioCheck = ContextCompat.checkSelfPermission(getContext(), Manifest.permission.RECORD_AUDIO); - switch (mSessionType) { + switch (sessionType) { case SESSION_TYPE_VIDEO: if (cameraCheck != PackageManager.PERMISSION_GRANTED || audioCheck != PackageManager.PERMISSION_GRANTED) { requestPermissions(true, true); @@ -360,8 +361,8 @@ public class CameraView extends FrameLayout implements LifecycleObserver { * If the developer did not add this to its manifest, throw and fire warnings. * (Hoping this is not cought elsewhere... we should test). */ - private void checkPermissionsManifestOrThrow() { - if (mSessionType == SESSION_TYPE_VIDEO) { + private void checkPermissionsManifestOrThrow(@SessionType int sessionType) { + if (sessionType == SESSION_TYPE_VIDEO) { try { PackageManager manager = getContext().getPackageManager(); PackageInfo info = manager.getPackageInfo(getContext().getPackageName(), PackageManager.GET_PERMISSIONS); @@ -464,6 +465,28 @@ public class CameraView extends FrameLayout implements LifecycleObserver { } + /** + * Toggles the facing value between {@link CameraKit.Constants#FACING_BACK} + * and {@link CameraKit.Constants#FACING_FRONT}. + * + * @return the new facing value + */ + @Facing + public int toggleFacing() { + switch (mFacing) { + case FACING_BACK: + setFacing(FACING_FRONT); + break; + + case FACING_FRONT: + setFacing(FACING_BACK); + break; + } + + return mFacing; + } + + /** * Sets the flash mode. * @@ -490,6 +513,34 @@ public class CameraView extends FrameLayout implements LifecycleObserver { } + /** + * Toggles the flash mode between {@link CameraKit.Constants#FLASH_OFF}, + * {@link CameraKit.Constants#FLASH_ON}, {@link CameraKit.Constants#FLASH_AUTO} and + * {@link CameraKit.Constants#FOCUS_OFF}, in this order. + * + * @return the new flash value + */ + @Flash + public int toggleFlash() { + switch (mFlash) { + case FLASH_OFF: + setFlash(FLASH_ON); + break; + + case FLASH_ON: + setFlash(FLASH_AUTO); + break; + + case FLASH_AUTO: + case FLASH_TORCH: + setFlash(FLASH_OFF); + break; + } + + return mFlash; + } + + /** * Sets the current focus behavior. * @@ -511,24 +562,30 @@ public class CameraView extends FrameLayout implements LifecycleObserver { } + /** + * Gets the current focus behavior. + * @return a focus behavior + */ + @Focus + public int getFocus() { + return mFocus; + } + + /** * This does nothing. - * @param method no-op * @deprecated */ @Deprecated - public void setCaptureMethod(@Method int method) { - } + public void setCaptureMethod(@Method int method) {} /** * This does nothing. - * @param permissions no-op * @deprecated */ @Deprecated - public void setPermissionPolicy(@Permissions int permissions) { - } + public void setPermissionPolicy(@Permissions int permissions) {} /** @@ -546,7 +603,7 @@ public class CameraView extends FrameLayout implements LifecycleObserver { mSessionType = sessionType; mCameraImpl.setSessionType(sessionType); - } else if (checkPermissions()) { + } else if (checkPermissions(sessionType)) { // Camera is running. CameraImpl setSessionType will do the trick. mSessionType = sessionType; mCameraImpl.setSessionType(sessionType); @@ -560,6 +617,18 @@ public class CameraView extends FrameLayout implements LifecycleObserver { } } + + /** + * Gets the current session type. + * @return the current session type + */ + @SessionType + public int getSessionType() { + return mSessionType; + } + + + public void setVideoQuality(@VideoQuality int videoQuality) { this.mVideoQuality = videoQuality; mCameraImpl.setVideoQuality(mVideoQuality); @@ -581,46 +650,9 @@ public class CameraView extends FrameLayout implements LifecycleObserver { } - /** - * Toggles the facing value between {@link CameraKit.Constants#FACING_BACK} - * and {@link CameraKit.Constants#FACING_FRONT}. - * - * @return the new facing value - */ - @Facing - public int toggleFacing() { - switch (mFacing) { - case FACING_BACK: - setFacing(FACING_FRONT); - break; - case FACING_FRONT: - setFacing(FACING_BACK); - break; - } - return mFacing; - } - @Flash - public int toggleFlash() { - switch (mFlash) { - case FLASH_OFF: - setFlash(FLASH_ON); - break; - - case FLASH_ON: - setFlash(FLASH_AUTO); - break; - - case FLASH_AUTO: - case FLASH_TORCH: - setFlash(FLASH_OFF); - break; - } - - return mFlash; - } /** @@ -685,7 +717,7 @@ public class CameraView extends FrameLayout implements LifecycleObserver { } } - private class CameraListenerWrapper extends CameraListener { + class CameraListenerWrapper extends CameraListener { private CameraListener mWrappedListener; @@ -715,9 +747,7 @@ public class CameraView extends FrameLayout implements LifecycleObserver { } } - @Override - public void onPictureTaken(YuvImage yuv) { - super.onPictureTaken(yuv); + void processYuvImage(YuvImage yuv) { if (mWrappedListener == null) return; if (mCropOutput) { AspectRatio outputRatio = AspectRatio.of(getWidth(), getHeight()); diff --git a/camerakit/src/main/utils/com/flurgle/camerakit/Rotation.java b/camerakit/src/main/utils/com/flurgle/camerakit/Rotation.java deleted file mode 100644 index 4e6ee6e9..00000000 --- a/camerakit/src/main/utils/com/flurgle/camerakit/Rotation.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.flurgle.camerakit; - -public class Rotation { - - private byte[] rotatedYuv; - - public Rotation(final byte[] yuv, final int width, final int height, final int rotation) { - if (rotation == 0) this.rotatedYuv = yuv; - if (rotation % 90 != 0 || rotation < 0 || rotation > 270) { - throw new IllegalArgumentException("0 <= rotation < 360, rotation % 90 == 0"); - } - - final byte[] output = new byte[yuv.length]; - final int frameSize = width * height; - final boolean swap = rotation % 180 != 0; - final boolean xflip = rotation % 270 != 0; - final boolean yflip = rotation >= 180; - - for (int j = 0; j < height; j++) { - for (int i = 0; i < width; i++) { - final int yIn = j * width + i; - final int uIn = frameSize + (j >> 1) * width + (i & ~1); - final int vIn = uIn + 1; - - final int wOut = swap ? height : width; - final int hOut = swap ? width : height; - final int iSwapped = swap ? j : i; - final int jSwapped = swap ? i : j; - final int iOut = xflip ? wOut - iSwapped - 1 : iSwapped; - final int jOut = yflip ? hOut - jSwapped - 1 : jSwapped; - - final int yOut = jOut * wOut + iOut; - final int uOut = frameSize + (jOut >> 1) * wOut + (iOut & ~1); - final int vOut = uOut + 1; - - output[yOut] = (byte) (0xff & yuv[yIn]); - output[uOut] = (byte) (0xff & yuv[uIn]); - output[vOut] = (byte) (0xff & yuv[vIn]); - } - } - - this.rotatedYuv = output; - } - - public byte[] getYuv() { - return this.rotatedYuv; - } - -} -