Add startVideo(File), refactor FocusMarkerLayout, remove static thread

pull/1/head
Mattia Iavarone 7 years ago
parent 132a3474fd
commit 5bbccb494f
  1. 2
      README.md
  2. 30
      camerakit/src/main/api16/com/flurgle/camerakit/Camera1.java
  3. 6
      camerakit/src/main/api21/com/flurgle/camerakit/Camera2.java
  4. 5
      camerakit/src/main/base/com/flurgle/camerakit/CameraImpl.java
  5. 68
      camerakit/src/main/java/com/flurgle/camerakit/CameraView.java
  6. 12
      camerakit/src/main/java/com/flurgle/camerakit/FocusMarkerLayout.java

@ -2,8 +2,8 @@
*A fork of [Dylan McIntyre's CameraKit-Android library](https://github.com/gogopop/CameraKit-Android), originally a fork of [Google's CameraView library](https://github.com/google/cameraview). Right now this is like CameraKit-Android, but with *a lot* of serious bugs fixed, new sizing behavior, better orientation and EXIF support, new `setLocation` and `setWhiteBalance` APIs. Feel free to open issues with suggestions or contribute. Roadmap:* *A fork of [Dylan McIntyre's CameraKit-Android library](https://github.com/gogopop/CameraKit-Android), originally a fork of [Google's CameraView library](https://github.com/google/cameraview). Right now this is like CameraKit-Android, but with *a lot* of serious bugs fixed, new sizing behavior, better orientation and EXIF support, new `setLocation` and `setWhiteBalance` APIs. Feel free to open issues with suggestions or contribute. Roadmap:*
- [x] *delete `captureMethod` and `permissionPolicy`, replace with `sessionType` (either picture or video) such that when `sessionType=video`, pictures are captured with the fast 'frame' method* - [x] *delete `captureMethod` and `permissionPolicy`, replace with `sessionType` (either picture or video) such that when `sessionType=video`, pictures are captured with the fast 'frame' method*
- [x] *pass a nullable File to startVideo, so user can choose where to save the file*
- [ ] *test video and 'frame' capture behavior, I expect some bugs there* - [ ] *test video and 'frame' capture behavior, I expect some bugs there*
- [ ] *pass a nullable File to startVideo, so user can choose where to save the file*
- [ ] *simple APIs to draw grid lines* - [ ] *simple APIs to draw grid lines*
- [ ] *rethink `adjustViewBounds`, maybe replace with a `scaleType` flag (center crop or center inside)* - [ ] *rethink `adjustViewBounds`, maybe replace with a `scaleType` flag (center crop or center inside)*
- [ ] *add a `sizingMethod` API to choose the capture size? Could be `max`, `4:3`, `16:9`... Right now it's `max`* - [ ] *add a `sizingMethod` API to choose the capture size? Could be `max`, `4:3`, `16:9`... Right now it's `max`*

@ -447,9 +447,13 @@ class Camera1 extends CameraImpl {
} }
// -----------------
// Video recording stuff.
@Override @Override
void startVideo() { void startVideo(@NonNull File videoFile) {
mVideoFile = videoFile;
if (mSessionType == SESSION_TYPE_VIDEO) { if (mSessionType == SESSION_TYPE_VIDEO) {
initMediaRecorder(); initMediaRecorder();
try { try {
@ -488,15 +492,12 @@ class Camera1 extends CameraImpl {
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT); mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
mMediaRecorder.setProfile(getCamcorderProfile(mVideoQuality)); mMediaRecorder.setProfile(getCamcorderProfile(mVideoQuality));
mVideoFile = new File(mPreview.getView().getContext().getExternalFilesDir(null), "video.mp4");
mMediaRecorder.setOutputFile(mVideoFile.getAbsolutePath()); mMediaRecorder.setOutputFile(mVideoFile.getAbsolutePath());
mMediaRecorder.setOrientationHint(computeCameraToDisplayOffset()); // TODO is this correct? Should we use exif orientation? Maybe not. mMediaRecorder.setOrientationHint(computeCameraToDisplayOffset()); // TODO is this correct? Should we use exif orientation? Maybe not.
// Not needed. mMediaRecorder.setPreviewDisplay(mPreview.getSurface()); // Not needed. mMediaRecorder.setPreviewDisplay(mPreview.getSurface());
} }
@NonNull @NonNull
private CamcorderProfile getCamcorderProfile(@VideoQuality int videoQuality) { private CamcorderProfile getCamcorderProfile(@VideoQuality int videoQuality) {
switch (videoQuality) { switch (videoQuality) {
@ -542,32 +543,39 @@ class Camera1 extends CameraImpl {
} }
// -----------------
// Tap to focus stuff.
void setTapToAutofocusListener(Camera.AutoFocusCallback callback) { void setTapToAutofocusListener(Camera.AutoFocusCallback callback) {
if (this.mFocus != FOCUS_TAP) { if (this.mFocus != FOCUS_TAP) {
throw new IllegalArgumentException("Please set the camera to FOCUS_TAP."); throw new IllegalArgumentException("Please set the camera to FOCUS_TAP.");
} }
this.mAutofocusCallback = callback; this.mAutofocusCallback = callback;
} }
private int getFocusAreaSize() { private int getFocusAreaSize() {
return FOCUS_AREA_SIZE_DEFAULT; return FOCUS_AREA_SIZE_DEFAULT;
} }
private int getFocusMeteringAreaWeight() { private int getFocusMeteringAreaWeight() {
return FOCUS_METERING_AREA_WEIGHT_DEFAULT; return FOCUS_METERING_AREA_WEIGHT_DEFAULT;
} }
private void detachFocusTapListener() { private void detachFocusTapListener() {
mPreview.getView().setOnTouchListener(null); mPreview.getView().setOnTouchListener(null);
} }
private void attachFocusTapListener() { private void attachFocusTapListener() {
mPreview.getView().setOnTouchListener(new View.OnTouchListener() { mPreview.getView().setOnTouchListener(new View.OnTouchListener() {
@Override @Override
public boolean onTouch(View v, MotionEvent event) { public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP) { if (event.getAction() != MotionEvent.ACTION_UP) return false;
if (mCamera != null) { if (mCamera == null) return false;
Camera.Parameters parameters = mCamera.getParameters(); Camera.Parameters parameters = mCamera.getParameters();
String focusMode = parameters.getFocusMode(); String focusMode = parameters.getFocusMode();
Rect rect = calculateFocusArea(event.getX(), event.getY()); Rect rect = calculateFocusArea(event.getX(), event.getY());
@ -619,13 +627,12 @@ class Camera1 extends CameraImpl {
} }
}); });
} }
}
}
return true; return true;
} }
}); });
} }
private void resetFocus(final boolean success, final Camera camera) { private void resetFocus(final boolean success, final Camera camera) {
mHandler.removeCallbacksAndMessages(null); mHandler.removeCallbacksAndMessages(null);
mHandler.postDelayed(new Runnable() { mHandler.postDelayed(new Runnable() {
@ -649,6 +656,7 @@ class Camera1 extends CameraImpl {
}, DELAY_MILLIS_BEFORE_RESETTING_FOCUS); }, DELAY_MILLIS_BEFORE_RESETTING_FOCUS);
} }
private Rect calculateFocusArea(float x, float y) { private Rect calculateFocusArea(float x, float y) {
int buffer = getFocusAreaSize() / 2; int buffer = getFocusAreaSize() / 2;
int centerX = calculateCenter(x, mPreview.getView().getWidth(), buffer); int centerX = calculateCenter(x, mPreview.getView().getWidth(), buffer);
@ -661,6 +669,7 @@ class Camera1 extends CameraImpl {
); );
} }
private static int calculateCenter(float coord, int dimen, int buffer) { private static int calculateCenter(float coord, int dimen, int buffer) {
int normalized = (int) ((coord / dimen) * 2000 - 1000); int normalized = (int) ((coord / dimen) * 2000 - 1000);
if (Math.abs(normalized) + buffer > 1000) { if (Math.abs(normalized) + buffer > 1000) {
@ -675,9 +684,10 @@ class Camera1 extends CameraImpl {
} }
// -----------------
// Size static stuff. // Size static stuff.
/** /**
* Returns a list of {@link Size} out of Camera.Sizes. * Returns a list of {@link Size} out of Camera.Sizes.
*/ */

@ -8,10 +8,12 @@ import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice; import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager; import android.hardware.camera2.CameraManager;
import android.hardware.camera2.params.StreamConfigurationMap; import android.hardware.camera2.params.StreamConfigurationMap;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.util.Log; import android.util.Log;
import android.util.SizeF; import android.util.SizeF;
import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
@ -34,7 +36,7 @@ class Camera2 extends CameraImpl {
private ConstantMapper.MapperImpl mMapper = new ConstantMapper.Mapper2(); private ConstantMapper.MapperImpl mMapper = new ConstantMapper.Mapper2();
private final HashMap<String, ExtraProperties> mExtraPropertiesMap = new HashMap<>(); private final HashMap<String, ExtraProperties> mExtraPropertiesMap = new HashMap<>();
Camera2(CameraListener callback, PreviewImpl preview, Context context) { Camera2(CameraView.CameraListenerWrapper callback, PreviewImpl preview, Context context) {
super(callback, preview); super(callback, preview);
preview.setCallback(new PreviewImpl.OnPreviewSurfaceChangedCallback() { preview.setCallback(new PreviewImpl.OnPreviewSurfaceChangedCallback() {
@Override @Override
@ -177,7 +179,7 @@ class Camera2 extends CameraImpl {
} }
@Override @Override
void startVideo() { void startVideo(@NonNull File videoFile) {
} }

@ -1,7 +1,10 @@
package com.flurgle.camerakit; package com.flurgle.camerakit;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import java.io.File;
abstract class CameraImpl { abstract class CameraImpl {
protected final CameraView.CameraListenerWrapper mCameraListener; protected final CameraView.CameraListenerWrapper mCameraListener;
@ -28,7 +31,7 @@ abstract class CameraImpl {
abstract void setLocation(double latitude, double longitude); abstract void setLocation(double latitude, double longitude);
abstract void captureImage(); abstract void captureImage();
abstract void startVideo(); abstract void startVideo(@NonNull File file);
abstract void endVideo(); abstract void endVideo();
abstract Size getCaptureSize(); abstract Size getCaptureSize();

@ -60,15 +60,17 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
private final static String TAG = CameraView.class.getSimpleName(); private final static String TAG = CameraView.class.getSimpleName();
private static Handler sWorkerHandler; private Handler sWorkerHandler;
private static Handler getWorkerHandler() { private Handler getWorkerHandler() {
synchronized (this) {
if (sWorkerHandler == null) { if (sWorkerHandler == null) {
HandlerThread workerThread = new HandlerThread("CameraViewWorker"); HandlerThread workerThread = new HandlerThread("CameraViewWorker");
workerThread.setDaemon(true); workerThread.setDaemon(true);
workerThread.start(); workerThread.start();
sWorkerHandler = new Handler(workerThread.getLooper()); sWorkerHandler = new Handler(workerThread.getLooper());
} }
}
return sWorkerHandler; return sWorkerHandler;
} }
@ -94,6 +96,7 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
private PreviewImpl mPreviewImpl; private PreviewImpl mPreviewImpl;
private Lifecycle mLifecycle; private Lifecycle mLifecycle;
private FocusMarkerLayout mFocusMarkerLayout;
private boolean mIsStarted; private boolean mIsStarted;
public CameraView(@NonNull Context context) { public CameraView(@NonNull Context context) {
@ -108,14 +111,10 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
@SuppressWarnings("WrongConstant") @SuppressWarnings("WrongConstant")
private void init(@NonNull Context context, @Nullable AttributeSet attrs) { private void init(@NonNull Context context, @Nullable AttributeSet attrs) {
if (attrs != null) {
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CameraView, 0, 0); TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CameraView, 0, 0);
try {
mFacing = a.getInteger(R.styleable.CameraView_cameraFacing, CameraKit.Defaults.DEFAULT_FACING); mFacing = a.getInteger(R.styleable.CameraView_cameraFacing, CameraKit.Defaults.DEFAULT_FACING);
mFlash = a.getInteger(R.styleable.CameraView_cameraFlash, CameraKit.Defaults.DEFAULT_FLASH); mFlash = a.getInteger(R.styleable.CameraView_cameraFlash, CameraKit.Defaults.DEFAULT_FLASH);
mFocus = a.getInteger(R.styleable.CameraView_cameraFocus, CameraKit.Defaults.DEFAULT_FOCUS); mFocus = a.getInteger(R.styleable.CameraView_cameraFocus, CameraKit.Defaults.DEFAULT_FOCUS);
// mMethod = a.getInteger(R.styleable.CameraView_cameraCaptureMethod, CameraKit.Defaults.DEFAULT_METHOD);
// mPermissions = a.getInteger(R.styleable.CameraView_cameraPermissionPolicy, CameraKit.Defaults.DEFAULT_PERMISSIONS);
mSessionType = a.getInteger(R.styleable.CameraView_cameraSessionType, CameraKit.Defaults.DEFAULT_SESSION_TYPE); mSessionType = a.getInteger(R.styleable.CameraView_cameraSessionType, CameraKit.Defaults.DEFAULT_SESSION_TYPE);
mZoom = a.getInteger(R.styleable.CameraView_cameraZoomMode, CameraKit.Defaults.DEFAULT_ZOOM); mZoom = a.getInteger(R.styleable.CameraView_cameraZoomMode, CameraKit.Defaults.DEFAULT_ZOOM);
mWhiteBalance = a.getInteger(R.styleable.CameraView_cameraWhiteBalance, CameraKit.Defaults.DEFAULT_WHITE_BALANCE); mWhiteBalance = a.getInteger(R.styleable.CameraView_cameraWhiteBalance, CameraKit.Defaults.DEFAULT_WHITE_BALANCE);
@ -123,14 +122,13 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
mJpegQuality = a.getInteger(R.styleable.CameraView_cameraJpegQuality, CameraKit.Defaults.DEFAULT_JPEG_QUALITY); mJpegQuality = a.getInteger(R.styleable.CameraView_cameraJpegQuality, CameraKit.Defaults.DEFAULT_JPEG_QUALITY);
mCropOutput = a.getBoolean(R.styleable.CameraView_cameraCropOutput, CameraKit.Defaults.DEFAULT_CROP_OUTPUT); mCropOutput = a.getBoolean(R.styleable.CameraView_cameraCropOutput, CameraKit.Defaults.DEFAULT_CROP_OUTPUT);
mAdjustViewBounds = a.getBoolean(R.styleable.CameraView_android_adjustViewBounds, CameraKit.Defaults.DEFAULT_ADJUST_VIEW_BOUNDS); mAdjustViewBounds = a.getBoolean(R.styleable.CameraView_android_adjustViewBounds, CameraKit.Defaults.DEFAULT_ADJUST_VIEW_BOUNDS);
} finally {
a.recycle(); a.recycle();
}
}
mCameraListener = new CameraListenerWrapper(); mCameraListener = new CameraListenerWrapper();
mPreviewImpl = new TextureViewPreview(context, this); mPreviewImpl = new TextureViewPreview(context, this);
mCameraImpl = new Camera1(mCameraListener, mPreviewImpl); mCameraImpl = new Camera1(mCameraListener, mPreviewImpl);
mFocusMarkerLayout = new FocusMarkerLayout(context);
addView(mFocusMarkerLayout);
mIsStarted = false; mIsStarted = false;
setFacing(mFacing); setFacing(mFacing);
@ -156,9 +154,7 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
} }
}; };
final FocusMarkerLayout focusMarkerLayout = new FocusMarkerLayout(getContext()); /* focusMarkerLayout.setOnTouchListener(new OnTouchListener() {
addView(focusMarkerLayout);
focusMarkerLayout.setOnTouchListener(new OnTouchListener() {
@Override @Override
public boolean onTouch(View v, MotionEvent motionEvent) { public boolean onTouch(View v, MotionEvent motionEvent) {
int action = motionEvent.getAction(); int action = motionEvent.getAction();
@ -169,7 +165,7 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
mPreviewImpl.getView().dispatchTouchEvent(motionEvent); mPreviewImpl.getView().dispatchTouchEvent(motionEvent);
return true; return true;
} }
}); }); */
} }
mLifecycle = null; mLifecycle = null;
} }
@ -552,13 +548,12 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
* @param focus a Focus value. * @param focus a Focus value.
*/ */
public void setFocus(@Focus int focus) { public void setFocus(@Focus int focus) {
this.mFocus = focus; mFocus = focus;
if (this.mFocus == CameraKit.Constants.FOCUS_TAP_WITH_MARKER) { mFocusMarkerLayout.setEnabled(focus == CameraKit.Constants.FOCUS_TAP_WITH_MARKER);
mCameraImpl.setFocus(CameraKit.Constants.FOCUS_TAP); if (focus == CameraKit.Constants.FOCUS_TAP_WITH_MARKER) {
return; focus = CameraKit.Constants.FOCUS_TAP;
} }
mCameraImpl.setFocus(focus);
mCameraImpl.setFocus(mFocus);
} }
@ -669,14 +664,43 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
mCameraImpl.captureImage(); mCameraImpl.captureImage();
} }
public void startRecordingVideo() {
mCameraImpl.startVideo(); /**
* Starts recording a video with selected options, in a file called
* "video.mp4" in the default folder.
* This is discouraged, please use {@link #startCapturingVideo(File)} instead.
*
* @deprecated see {@link #startCapturingVideo(File)}
*/
@Deprecated
public void startCapturingVideo() {
startCapturingVideo(null);
} }
public void stopRecordingVideo() {
/**
* Starts recording a video with selected options. Video will be written to the given file,
* so callers should ensure they have appropriate permissions to write to the file.
*
* @param file a file where the video will be saved
*/
public void startCapturingVideo(File file) {
if (file == null) {
file = new File(getContext().getExternalFilesDir(null), "video.mp4");
}
mCameraImpl.startVideo(file);
}
/**
* Stops capturing video, if there was a video record going on.
* This will fire {@link CameraListener#onVideoTaken(File)}.
*/
public void stopCapturingVideo() {
mCameraImpl.endVideo(); mCameraImpl.endVideo();
} }
/** /**
* Returns the size used for the preview, * Returns the size used for the preview,
* or null if it hasn't been computed (for example if the surface is not ready). * or null if it hasn't been computed (for example if the surface is not ready).

@ -7,6 +7,8 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import android.widget.ImageView; import android.widget.ImageView;
@ -25,10 +27,18 @@ public class FocusMarkerLayout extends FrameLayout {
mFocusMarkerContainer = (FrameLayout) findViewById(R.id.focusMarkerContainer); mFocusMarkerContainer = (FrameLayout) findViewById(R.id.focusMarkerContainer);
mFill = (ImageView) findViewById(R.id.fill); mFill = (ImageView) findViewById(R.id.fill);
mFocusMarkerContainer.setAlpha(0); mFocusMarkerContainer.setAlpha(0);
} }
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
if (action == MotionEvent.ACTION_UP && isEnabled()) {
focus(event.getX(), event.getY());
}
return false; // We didn't consume, pass to parent
}
public void focus(float mx, float my) { public void focus(float mx, float my) {
int x = (int) (mx - mFocusMarkerContainer.getWidth() / 2); int x = (int) (mx - mFocusMarkerContainer.getWidth() / 2);
int y = (int) (my - mFocusMarkerContainer.getWidth() / 2); int y = (int) (my - mFocusMarkerContainer.getWidth() / 2);

Loading…
Cancel
Save