diff --git a/README.md b/README.md
index 2c72a1f6..01982535 100644
--- a/README.md
+++ b/README.md
@@ -22,6 +22,7 @@ Try out all the unique features using the CameraKit Demo from the Google Play st
- [`ckPictureMode`](#ckpicturemode)
- [`ckTapToFocus`](#cktaptofocus)
- [`ckAutoFocus`](#ckautofocus)
+ - [`ckCaptureSize`](#ckcapturesize)
- [Capturing Images](#capturing-images)
- [Capturing Video](#capturing-video)
- [Automatic Permissions Behavior](#automatic-permissions-behavior)
@@ -126,7 +127,8 @@ camera.setCameraListener(new CameraListener() {
camerakit:ckFlash="off"
camerakit:ckPictureMode="quality"
camerakit:ckTapToFocus="on"
- camerakit:ckAutoFocus="true" />
+ camerakit:ckAutoFocus="true"
+ camerakit:ckCaptureSize="8" />
```
#### `ckCropOutput`
@@ -175,6 +177,13 @@ camera.setCameraListener(new CameraListener() {
| `true` | Continuously allow the `CameraView` preview to adjust focus automatically. |
| `false` | Never adjust focus during preview. |
+#### `ckCaptureSize`
+
+| Value | Description |
+| --------------| -------------|
+| `n <= 0` | Capture at the highest quality possible. |
+| `n > 0` | Capture at a size of approximately `n` megapixels. |
+
### Capturing Images
To capture an image just call `CameraView.capturePicture()`. Make sure you setup a `CameraListener` to handle the image callback.
diff --git a/camerakit/src/main/AndroidManifest.xml b/camerakit/src/main/AndroidManifest.xml
index f108e9a6..31b72ed2 100644
--- a/camerakit/src/main/AndroidManifest.xml
+++ b/camerakit/src/main/AndroidManifest.xml
@@ -8,6 +8,12 @@
+
+
diff --git a/camerakit/src/main/java/com/flurgle/camerakit/Camera1.java b/camerakit/src/main/java/com/flurgle/camerakit/Camera1.java
index 69ffea8f..2ccaabd3 100644
--- a/camerakit/src/main/java/com/flurgle/camerakit/Camera1.java
+++ b/camerakit/src/main/java/com/flurgle/camerakit/Camera1.java
@@ -27,6 +27,8 @@ public class Camera1 extends CameraViewImpl {
FLASH_MODES.put(CameraKit.Constants.FLASH_AUTO, Camera.Parameters.FLASH_MODE_AUTO);
}
+ private static File VIDEO_FILE;
+
private int mCameraId;
Camera mCamera;
@@ -36,13 +38,9 @@ public class Camera1 extends CameraViewImpl {
private final Camera.CameraInfo mCameraInfo = new Camera.CameraInfo();
private boolean mShowingPreview;
-
private boolean mAutoFocus;
-
private int mFacing;
-
private int mFlash;
-
private int mDisplayOrientation;
private SortedSet mPreviewSizes;
@@ -62,9 +60,10 @@ public class Camera1 extends CameraViewImpl {
}
});
-
mPreviewSizes = new TreeSet<>();
mCaptureSizes = new TreeSet<>();
+
+ VIDEO_FILE = new File(getView().getContext().getExternalFilesDir(null), "video.mp4");;
}
@Override
@@ -168,6 +167,8 @@ public class Camera1 extends CameraViewImpl {
@Override
void startVideo() {
+ if (!canRecordAudio()) return;
+
try {
prepareMediaRecorder();
} catch (IOException e) {
@@ -187,10 +188,10 @@ public class Camera1 extends CameraViewImpl {
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
+ mMediaRecorder.setProfile(CamcorderProfile.get(mFacing == CameraKit.Constants.FACING_BACK ? CamcorderProfile.QUALITY_HIGH : CamcorderProfile.QUALITY_LOW));
- mMediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH));
-
- mMediaRecorder.setOutputFile(new File(getView().getContext().getExternalFilesDir(null), "video.mp4").getAbsolutePath());
+ mMediaRecorder.setOutputFile(VIDEO_FILE.getAbsolutePath());
+ mMediaRecorder.setOrientationHint(mCameraInfo.orientation);
mMediaRecorder.prepare();
mMediaRecorder.start();
@@ -200,9 +201,11 @@ public class Camera1 extends CameraViewImpl {
void endVideo() {
if (mMediaRecorder != null) {
mMediaRecorder.stop();
+ mMediaRecorder = null;
}
- getCameraListener().onVideoTaken(new File(getView().getContext().getExternalFilesDir(null), "video.mp4"));
+ getCameraListener().onVideoTaken(VIDEO_FILE);
+
}
@Override
@@ -229,7 +232,7 @@ public class Camera1 extends CameraViewImpl {
private int calcCameraRotation(int rotation) {
if (mCameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
return (360 - (mCameraInfo.orientation + rotation) % 360) % 360;
- } else { // back-facing
+ } else {
return (mCameraInfo.orientation - rotation + 360) % 360;
}
}
@@ -267,7 +270,10 @@ public class Camera1 extends CameraViewImpl {
mPreview.setTruePreviewSize(previewSize.getWidth(), previewSize.getHeight());
mCameraParameters.setPictureSize(pictureSize.getWidth(), pictureSize.getHeight());
- mCameraParameters.setRotation(calcCameraRotation(mDisplayOrientation));
+
+ // TODO: fix this
+ mCameraParameters.setRotation(calcCameraRotation(mDisplayOrientation) + (mFacing == CameraKit.Constants.FACING_FRONT ? 180 : 0));
+
setAutoFocusInternal(mAutoFocus);
setFlashInternal(mFlash);
mCamera.setParameters(mCameraParameters);
diff --git a/camerakit/src/main/java/com/flurgle/camerakit/Camera2.java b/camerakit/src/main/java/com/flurgle/camerakit/Camera2.java
deleted file mode 100644
index a428a0ca..00000000
--- a/camerakit/src/main/java/com/flurgle/camerakit/Camera2.java
+++ /dev/null
@@ -1,613 +0,0 @@
-package com.flurgle.camerakit;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.graphics.ImageFormat;
-import android.graphics.Rect;
-import android.hardware.camera2.CameraAccessException;
-import android.hardware.camera2.CameraCaptureSession;
-import android.hardware.camera2.CameraCharacteristics;
-import android.hardware.camera2.CameraDevice;
-import android.hardware.camera2.CameraManager;
-import android.hardware.camera2.CaptureRequest;
-import android.hardware.camera2.TotalCaptureResult;
-import android.hardware.camera2.params.StreamConfigurationMap;
-import android.media.Image;
-import android.media.ImageReader;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.support.annotation.NonNull;
-import android.util.Log;
-import android.util.SparseIntArray;
-import android.view.Surface;
-import android.view.View;
-
-import com.flurgle.camerakit.encoding.VideoEncoder;
-import com.flurgle.camerakit.utils.AspectRatio;
-import com.flurgle.camerakit.utils.Size;
-import com.flurgle.camerakit.utils.YuvUtils;
-
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.SortedSet;
-import java.util.TreeSet;
-import java.util.concurrent.Semaphore;
-import java.util.concurrent.TimeUnit;
-
-import static android.content.ContentValues.TAG;
-
-@TargetApi(21)
-public class Camera2 extends CameraViewImpl {
-
- private static final SparseIntArray INTERNAL_FACINGS = new SparseIntArray();
-
- static {
- INTERNAL_FACINGS.put(CameraKit.Constants.FACING_BACK, CameraCharacteristics.LENS_FACING_BACK);
- INTERNAL_FACINGS.put(CameraKit.Constants.FACING_FRONT, CameraCharacteristics.LENS_FACING_FRONT);
- }
-
- private CameraManager mCameraManager;
- private CameraDevice mCamera;
- private CameraCaptureSession mCaptureSession;
- private CaptureRequest.Builder mPreviewRequestBuilder;
- private ImageReader mImageReader;
- private VideoEncoder mVideoEncoder;
-
- private Semaphore mCameraOpenCloseLock;
-
- private int mFacing;
- private int mFlash;
- private int mDisplayOrientation;
- private String mCameraId;
- private CameraCharacteristics mCameraCharacteristics;
-
- private SortedSet mPreviewSizes;
- private SortedSet mCaptureSizes;
-
- private HandlerThread mBackgroundThread;
- private Handler mBackgroundHandler;
-
- private boolean mCropOutput;
-
- private boolean mIsRecording;
-
- Camera2(Context context, CameraListener cameraListener, PreviewImpl preview) {
- super(cameraListener, preview);
- mCameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
- mPreview.setCallback(new PreviewImpl.Callback() {
- @Override
- public void onSurfaceChanged() {
- startCaptureSession();
- }
- });
-
- mCameraOpenCloseLock = new Semaphore(1);
-
- mPreviewSizes = new TreeSet<>();
- mCaptureSizes = new TreeSet<>();
- }
-
- @Override
- View getView() {
- return mPreview.getView();
- }
-
- @Override
- void start() {
- if (chooseCameraIdByFacing()) {
- startBackgroundThread();
- collectCameraInfo();
- prepareImageReader();
- startOpeningCamera();
- }
- }
-
- @Override
- void stop() {
- try {
- mCameraOpenCloseLock.acquire();
- if (mCaptureSession != null) {
- mCaptureSession.close();
- mCaptureSession = null;
- }
- if (mCamera != null) {
- mCamera.close();
- mCamera = null;
- }
- if (mImageReader != null) {
- mImageReader.close();
- mImageReader = null;
- }
- } catch (InterruptedException e) {
- throw new RuntimeException("Interrupted while trying to lock camera closing.", e);
- } finally {
- mCameraOpenCloseLock.release();
-
- if (mBackgroundThread != null) {
- stopBackgroundThread();
- }
- }
-
- }
-
- @Override
- boolean isCameraOpened() {
- return mCamera != null;
- }
-
- @Override
- void setFacing(int facing) {
- int internalFacing = INTERNAL_FACINGS.get(facing);
- if (mFacing == internalFacing) return;
- this.mFacing = internalFacing;
- if (isCameraOpened()) {
- stop();
- start();
- }
- }
-
- @Override
- int getFacing() {
- return mFacing;
- }
-
- @Override
- void setFlash(int flash) {
- if (mFlash == flash) return;
- int fallback = flash;
- mFlash = flash;
- if (mPreviewRequestBuilder != null) {
- updateFlash(mPreviewRequestBuilder);
- if (mCaptureSession != null) {
- try {
- mCaptureSession.setRepeatingRequest(mPreviewRequestBuilder.build(), mCaptureCallback, mBackgroundHandler);
- } catch (CameraAccessException e) {
- mFlash = fallback;
- }
- }
- }
- }
-
- @Override
- int getFlash() {
- return mFlash;
- }
-
- @Override
- boolean getAutoFocus() {
- return true;
- }
-
- @Override
- void capturePicture() {
- if (mFacing == INTERNAL_FACINGS.get(CameraKit.Constants.FACING_BACK)) {
- lockFocus();
- } else {
- captureStillPicture();
- }
- }
-
- @Override
- void captureStill() {
- if (mOnImageAvailableListener != null) {
- mOnImageAvailableListener.allowCallback();
- }
- }
-
- @Override
- void startVideo() {
- if (mCamera == null) {
- return;
- }
-
- mIsRecording = true;
- }
-
- @Override
- void endVideo() {
- mIsRecording = false;
- if (mVideoEncoder != null) {
- mVideoEncoder.stopEncoder();
- mVideoEncoder = null;
- }
- }
-
- @Override
- void setDisplayOrientation(int displayOrientation) {
- mDisplayOrientation = displayOrientation;
- mPreview.setDisplayOrientation(mDisplayOrientation);
- }
-
- void updateFlash(CaptureRequest.Builder builder) {
- switch (mFlash) {
- case CameraKit.Constants.FLASH_OFF:
- builder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON);
- builder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_OFF);
- break;
- case CameraKit.Constants.FLASH_ON:
- builder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_ALWAYS_FLASH);
- builder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_OFF);
- break;
- case CameraKit.Constants.FLASH_AUTO:
- builder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
- builder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_OFF);
- break;
- }
- }
-
- void setCropOutput(boolean cropOutput) {
- this.mCropOutput = cropOutput;
- }
-
- private boolean chooseCameraIdByFacing() {
- try {
- int internalFacing = mFacing;
- final String[] ids = mCameraManager.getCameraIdList();
- if (ids.length == 0) { // No camera
- throw new RuntimeException("No camera available.");
- }
- for (String id : ids) {
- CameraCharacteristics characteristics = mCameraManager.getCameraCharacteristics(id);
- Integer level = characteristics.get(
- CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
- if (level == null ||
- level == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) {
- continue;
- }
- Integer internal = characteristics.get(CameraCharacteristics.LENS_FACING);
- if (internal == null) {
- throw new NullPointerException("Unexpected state: LENS_FACING null");
- }
- if (internal == internalFacing) {
- mCameraId = id;
- mCameraCharacteristics = characteristics;
- return true;
- }
- }
- // Not found
- mCameraId = ids[0];
- mCameraCharacteristics = mCameraManager.getCameraCharacteristics(mCameraId);
- Integer level = mCameraCharacteristics.get(
- CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
- if (level == null ||
- level == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) {
- return false;
- }
- Integer internal = mCameraCharacteristics.get(CameraCharacteristics.LENS_FACING);
- if (internal == null) {
- throw new NullPointerException("Unexpected state: LENS_FACING null");
- }
- for (int i = 0, count = INTERNAL_FACINGS.size(); i < count; i++) {
- if (INTERNAL_FACINGS.valueAt(i) == internal) {
- mFacing = INTERNAL_FACINGS.keyAt(i);
- return true;
- }
- }
- // The operation can reach here when the only camera device is an external one.
- // We treat it as facing back.
- mFacing = CameraKit.Constants.FACING_BACK;
- return true;
- } catch (CameraAccessException e) {
- throw new RuntimeException("Failed to get a list of camera devices", e);
- }
- }
-
- private void collectCameraInfo() {
- StreamConfigurationMap map = mCameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
- if (map == null) {
- throw new IllegalStateException("Failed to get configuration map: " + mCameraId);
- }
-
- mPreviewSizes.clear();
- for (android.util.Size size : map.getOutputSizes(mPreview.getOutputClass())) {
- mPreviewSizes.add(new Size(size.getWidth(), size.getHeight()));
- }
-
- mCaptureSizes.clear();
- for (android.util.Size size : map.getOutputSizes(ImageFormat.JPEG)) {
- mCaptureSizes.add(new Size(size.getWidth(), size.getHeight()));
- }
- }
-
- private Size getOptimalPreviewSize() {
- int surfaceLonger, surfaceShorter;
- final int surfaceWidth = mPreview.getWidth();
- final int surfaceHeight = mPreview.getHeight();
- if (surfaceWidth < surfaceHeight) {
- surfaceLonger = surfaceHeight;
- surfaceShorter = surfaceWidth;
- } else {
- surfaceLonger = surfaceWidth;
- surfaceShorter = surfaceHeight;
- }
- // Pick the smallest of those big enough.
- for (Size size : mPreviewSizes) {
- if (size.getWidth() >= surfaceLonger && size.getHeight() >= surfaceShorter) {
- return size;
- }
- }
- // If no size is big enough, pick the largest one.
- return mPreviewSizes.last();
- }
-
- private void prepareImageReader() {
- Size previewSize = getOptimalPreviewSize();
- AspectRatio aspectRatio = AspectRatio.of(previewSize.getWidth(), previewSize.getHeight());
- Size bestSize = findSizeClosestTo(1500000, aspectRatio, mCaptureSizes);
- mImageReader = ImageReader.newInstance(bestSize.getWidth(), bestSize.getHeight(), ImageFormat.YUV_420_888, 3);
- mImageReader.setOnImageAvailableListener(mOnImageAvailableListener, mBackgroundHandler);
- }
-
- private Size findSizeClosestTo(int targetLength, AspectRatio targetAspectRatio, SortedSet sizes) {
- int closestDistance = Integer.MAX_VALUE;
- Size closestSize = null;
- for (Size size : sizes) {
- if (targetAspectRatio.matches(size)) {
- int length = size.getWidth() * size.getHeight();
- int distance = Math.abs(targetLength - length);
- if (closestSize == null) {
- closestSize = size;
- } else {
- if (distance < closestDistance) {
- closestSize = size;
- closestDistance = length;
- }
- }
- }
- }
-
- return closestSize;
- }
-
- private void startBackgroundThread() {
- mBackgroundThread = new HandlerThread("CameraBackground");
- mBackgroundThread.start();
- mBackgroundHandler = new Handler(mBackgroundThread.getLooper());
- }
-
- private void stopBackgroundThread() {
- mBackgroundThread.quitSafely();
- try {
- mBackgroundThread.join();
- mBackgroundThread = null;
- mBackgroundHandler = null;
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
-
-
- @SuppressWarnings("MissingPermission")
- private void startOpeningCamera() {
- try {
- if (!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {
- throw new RuntimeException("Time out waiting to lock camera opening.");
- }
- mCameraManager.openCamera(mCameraId, mCameraDeviceCallback, mBackgroundHandler);
- } catch (CameraAccessException e) {
- throw new RuntimeException("Failed to open camera: " + mCameraId, e);
- } catch (InterruptedException e) {
- throw new RuntimeException("Time out waiting to lock camera opening.");
- }
- }
-
- void startCaptureSession() {
- if (!isCameraOpened() || !mPreview.isReady() || mImageReader == null) {
- return;
- }
-
- Size previewSize = getOptimalPreviewSize();
-
- mPreview.setTruePreviewSize(previewSize.getWidth(), previewSize.getHeight());
- Surface surface = mPreview.getSurface();
- try {
- mPreviewRequestBuilder = mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
- mPreviewRequestBuilder.addTarget(surface);
- mPreviewRequestBuilder.addTarget(mImageReader.getSurface());
- mCamera.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()), mSessionCallback, mBackgroundHandler);
- } catch (CameraAccessException e) {
- throw new RuntimeException("Failed to start camera session");
- } catch (IllegalStateException e) {
- startOpeningCamera();
- }
- }
-
- private void lockFocus() {
- mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_START);
- try {
- mCaptureCallback.setState(PictureCaptureCallback.STATE_LOCKING);
- mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback, mBackgroundHandler);
- } catch (CameraAccessException e) {
- Log.e(TAG, "Failed to lock focus.", e);
- }
- }
-
- private void captureStillPicture() {
- try {
- CaptureRequest.Builder captureRequestBuilder = mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
- captureRequestBuilder.addTarget(mImageReader.getSurface());
- captureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, mPreviewRequestBuilder.get(CaptureRequest.CONTROL_AF_MODE));
- updateFlash(captureRequestBuilder);
- // Calculate JPEG orientation.
- @SuppressWarnings("ConstantConditions")
- int sensorOrientation = mCameraCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
- captureRequestBuilder.set(CaptureRequest.JPEG_ORIENTATION,
- (sensorOrientation +
- mDisplayOrientation * (mFacing == CameraKit.Constants.FACING_FRONT ? 1 : -1) +
- 360) % 360);
- // Stop preview and capture a still picture.
- mCaptureSession.stopRepeating();
-
- mOnImageAvailableListener.allowCallback();
- mCaptureSession.capture(captureRequestBuilder.build(), new CameraCaptureSession.CaptureCallback() {
- @Override
- public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {
- unlockFocus();
- }
- }, mBackgroundHandler);
- } catch (CameraAccessException e) {
- Log.e(TAG, "Cannot capture a still picture.", e);
- }
- }
-
- void unlockFocus() {
- mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_CANCEL);
- try {
- mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback, null);
- //updateAutoFocus();
- updateFlash(mPreviewRequestBuilder);
- mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_IDLE);
- mCaptureSession.setRepeatingRequest(mPreviewRequestBuilder.build(), mCaptureCallback, mBackgroundHandler);
- mCaptureCallback.setState(PictureCaptureCallback.STATE_PREVIEW);
- } catch (CameraAccessException e) {
- Log.e(TAG, "Failed to restart camera preview.", e);
- }
- }
-
- private final CameraDevice.StateCallback mCameraDeviceCallback = new CameraDevice.StateCallback() {
-
- @Override
- public void onOpened(@NonNull CameraDevice camera) {
- mCameraOpenCloseLock.release();
- mCamera = camera;
- getCameraListener().onCameraOpened();
- startCaptureSession();
- }
-
- @Override
- public void onClosed(@NonNull CameraDevice camera) {
- getCameraListener().onCameraClosed();
- }
-
- @Override
- public void onDisconnected(@NonNull CameraDevice camera) {
- mCameraOpenCloseLock.release();
- mCamera.close();
- mCamera = null;
- }
-
- @Override
- public void onError(@NonNull CameraDevice camera, int error) {
- mCameraOpenCloseLock.release();
- Log.e(TAG, "onError: " + camera.getId() + " (" + error + ")");
- mCamera = null;
- }
-
- };
-
- private final CameraCaptureSession.StateCallback mSessionCallback = new CameraCaptureSession.StateCallback() {
-
- @Override
- public void onConfigured(@NonNull CameraCaptureSession session) {
- if (mCamera == null) {
- return;
- }
- mCaptureSession = session;
- updateFlash(mPreviewRequestBuilder);
- try {
- mCaptureSession.setRepeatingRequest(mPreviewRequestBuilder.build(), mCaptureCallback, mBackgroundHandler);
- } catch (CameraAccessException e) {
- Log.e(TAG, "Failed to start camera preview because it couldn't access camera", e);
- } catch (IllegalStateException e) {
- Log.e(TAG, "Failed to start camera preview.", e);
- }
- }
-
- @Override
- public void onConfigureFailed(@NonNull CameraCaptureSession session) {
- Log.e(TAG, "Failed to configure capture session.");
- }
-
- @Override
- public void onClosed(@NonNull CameraCaptureSession session) {
- if (mCaptureSession != null && mCaptureSession.equals(session)) {
- mCaptureSession = null;
- }
- }
-
- };
-
- private PictureCaptureCallback mCaptureCallback = new PictureCaptureCallback() {
-
- @Override
- public void onPrecaptureRequired() {
- mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START);
- setState(STATE_PRECAPTURE);
- try {
- mCaptureSession.capture(mPreviewRequestBuilder.build(), this, mBackgroundHandler);
- mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_IDLE);
- } catch (CameraAccessException e) {
- Log.e(TAG, "Failed to run precapture sequence.", e);
- }
- }
-
- @Override
- public void onReady() {
- captureStillPicture();
- }
-
- };
-
-
- private abstract static class VariableCallbackOnImageAvailableListener implements ImageReader.OnImageAvailableListener {
-
- protected boolean mAllowOneCallback = false;
-
- @Override
- public abstract void onImageAvailable(ImageReader reader);
-
- public void allowCallback() {
- mAllowOneCallback = true;
- }
-
- }
-
- private VariableCallbackOnImageAvailableListener mOnImageAvailableListener = new VariableCallbackOnImageAvailableListener() {
- @Override
- public void onImageAvailable(ImageReader reader) {
- Image image = reader.acquireLatestImage();
-
- if (image == null) {
- return;
- }
-
- if (!mAllowOneCallback && !mIsRecording) {
- image.close();
- return;
- }
-
- Rect crop = null;
- if (mCropOutput) {
-
- }
-
- if (mAllowOneCallback) {
- mAllowOneCallback = false;
- byte[] out = YuvUtils.createRGB(image, crop);
- getCameraListener().onPictureTaken(out);
- }
-
- if (mIsRecording) {
- if (mVideoEncoder == null) {
- try {
- mVideoEncoder = new VideoEncoder(getView().getContext(), mFacing, image.getWidth(), image.getHeight());
- } catch (IOException e) {
-
- }
- }
-
- if (mVideoEncoder != null) {
- byte[] out = YuvUtils.getYUVData(image);
- try {
- mVideoEncoder.encode(out);
- } catch (Exception e) {
-
- }
- }
- }
-
- image.close();
- }
- };
-
-}
diff --git a/camerakit/src/main/java/com/flurgle/camerakit/CameraView.java b/camerakit/src/main/java/com/flurgle/camerakit/CameraView.java
index f970fe82..467dfc30 100644
--- a/camerakit/src/main/java/com/flurgle/camerakit/CameraView.java
+++ b/camerakit/src/main/java/com/flurgle/camerakit/CameraView.java
@@ -42,6 +42,8 @@ public class CameraView extends FrameLayout {
private static final int PERMISSION_REQUEST_CAMERA = 16;
+ private static final int DEFAULT_CAPTURE_SIZE = -1;
+
@IntDef({FACING_BACK, FACING_FRONT})
@Retention(RetentionPolicy.SOURCE)
@interface Facing {
@@ -75,6 +77,8 @@ public class CameraView extends FrameLayout {
private int mTapToFocus;
private boolean mAutoFocus;
+ private float mCaptureSize;
+
private boolean mAdjustViewBounds;
private boolean mWaitingForPermission;
@@ -122,6 +126,10 @@ public class CameraView extends FrameLayout {
mAutoFocus = a.getBoolean(R.styleable.CameraView_ckAutoFocus, true);
}
+ if (attr == R.styleable.CameraView_ckCaptureSize) {
+ mCaptureSize = a.getFloat(R.styleable.CameraView_ckCaptureSize, DEFAULT_CAPTURE_SIZE);
+ }
+
if (attr == R.styleable.CameraView_android_adjustViewBounds) {
mAdjustViewBounds = a.getBoolean(R.styleable.CameraView_android_adjustViewBounds, false);
}
@@ -184,6 +192,7 @@ public class CameraView extends FrameLayout {
int permissionCheck = ContextCompat.checkSelfPermission(getContext(), Manifest.permission.CAMERA);
if (permissionCheck == PackageManager.PERMISSION_GRANTED) {
mWaitingForPermission = false;
+ mCameraImpl.setCanRecordAudio(true);
mCameraImpl.start();
} else {
requestCameraPermission();
@@ -254,6 +263,10 @@ public class CameraView extends FrameLayout {
this.mAutoFocus = autoFocus;
}
+ public void setCaptureSize(float captureSize) {
+ this.mCaptureSize = captureSize;
+ }
+
public void setCameraListener(CameraListener cameraListener) {
this.mCameraListener = new CameraListenerMiddleWare(cameraListener);
mCameraImpl.setCameraListener(mCameraListener);
@@ -276,12 +289,6 @@ public class CameraView extends FrameLayout {
public void stopRecordingVideo() {
mCameraImpl.endVideo();
- postDelayed(new Runnable() {
- @Override
- public void run() {
- mCameraListener.onVideoTaken(new File(getContext().getExternalFilesDir(null), "video.mp4"));
- }
- }, 1000);
}
private void requestCameraPermission() {
@@ -295,7 +302,7 @@ public class CameraView extends FrameLayout {
}
if (activity != null) {
- ActivityCompat.requestPermissions(activity, new String[]{ Manifest.permission.CAMERA }, PERMISSION_REQUEST_CAMERA);
+ ActivityCompat.requestPermissions(activity, new String[]{ Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO }, PERMISSION_REQUEST_CAMERA);
mWaitingForPermission = true;
}
}
diff --git a/camerakit/src/main/java/com/flurgle/camerakit/CameraViewImpl.java b/camerakit/src/main/java/com/flurgle/camerakit/CameraViewImpl.java
index c9c986be..e9294374 100644
--- a/camerakit/src/main/java/com/flurgle/camerakit/CameraViewImpl.java
+++ b/camerakit/src/main/java/com/flurgle/camerakit/CameraViewImpl.java
@@ -8,6 +8,8 @@ public abstract class CameraViewImpl {
protected final PreviewImpl mPreview;
+ private boolean mCanRecordAudio;
+
CameraViewImpl(CameraListener callback, PreviewImpl preview) {
mCameraListener = callback;
mPreview = preview;
@@ -51,4 +53,12 @@ public abstract class CameraViewImpl {
return mCameraListener != null ? mCameraListener : new CameraListener() {};
}
+ public void setCanRecordAudio(boolean canRecordAudio) {
+ this.mCanRecordAudio = canRecordAudio;
+ }
+
+ protected boolean canRecordAudio() {
+ return mCanRecordAudio;
+ }
+
}
diff --git a/camerakit/src/main/java/com/flurgle/camerakit/PictureCaptureCallback.java b/camerakit/src/main/java/com/flurgle/camerakit/PictureCaptureCallback.java
deleted file mode 100644
index bc6297b5..00000000
--- a/camerakit/src/main/java/com/flurgle/camerakit/PictureCaptureCallback.java
+++ /dev/null
@@ -1,81 +0,0 @@
-package com.flurgle.camerakit;
-
-import android.hardware.camera2.CameraCaptureSession;
-import android.hardware.camera2.CaptureRequest;
-import android.hardware.camera2.CaptureResult;
-import android.hardware.camera2.TotalCaptureResult;
-import android.support.annotation.NonNull;
-
-public abstract class PictureCaptureCallback extends CameraCaptureSession.CaptureCallback {
-
- static final int STATE_PREVIEW = 0;
- static final int STATE_LOCKING = 1;
- static final int STATE_LOCKED = 2;
- static final int STATE_PRECAPTURE = 3;
- static final int STATE_WAITING = 4;
- static final int STATE_CAPTURING = 5;
-
- private int mState;
-
- PictureCaptureCallback() {
- }
-
- void setState(int state) {
- mState = state;
- }
-
- @Override
- public void onCaptureProgressed(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull CaptureResult partialResult) {
- process(partialResult);
- }
-
- @Override
- public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {
- process(result);
- }
-
- private void process(@NonNull CaptureResult result) {
- switch (mState) {
- case STATE_LOCKING: {
- Integer af = result.get(CaptureResult.CONTROL_AF_STATE);
- if (af == null) {
- break;
- }
- if (af == CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED ||
- af == CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED) {
- Integer ae = result.get(CaptureResult.CONTROL_AE_STATE);
- if (ae == null || ae == CaptureResult.CONTROL_AE_STATE_CONVERGED) {
- setState(STATE_CAPTURING);
- onReady();
- } else {
- setState(STATE_LOCKED);
- onPrecaptureRequired();
- }
- }
- break;
- }
- case STATE_PRECAPTURE: {
- Integer ae = result.get(CaptureResult.CONTROL_AE_STATE);
- if (ae == null || ae == CaptureResult.CONTROL_AE_STATE_PRECAPTURE ||
- ae == CaptureRequest.CONTROL_AE_STATE_FLASH_REQUIRED ||
- ae == CaptureResult.CONTROL_AE_STATE_CONVERGED) {
- setState(STATE_WAITING);
- }
- break;
- }
- case STATE_WAITING: {
- Integer ae = result.get(CaptureResult.CONTROL_AE_STATE);
- if (ae == null || ae != CaptureResult.CONTROL_AE_STATE_PRECAPTURE) {
- setState(STATE_CAPTURING);
- onReady();
- }
- break;
- }
- }
- }
-
- public abstract void onReady();
-
- public abstract void onPrecaptureRequired();
-
-}
\ No newline at end of file
diff --git a/camerakit/src/main/java/com/flurgle/camerakit/encoding/Encoder.java b/camerakit/src/main/java/com/flurgle/camerakit/encoding/Encoder.java
deleted file mode 100644
index 8759c97a..00000000
--- a/camerakit/src/main/java/com/flurgle/camerakit/encoding/Encoder.java
+++ /dev/null
@@ -1,82 +0,0 @@
-package com.flurgle.camerakit.encoding;
-
-import java.util.concurrent.ArrayBlockingQueue;
-import java.util.concurrent.BlockingQueue;
-
-public abstract class Encoder {
-
- protected BlockingQueue queue = new ArrayBlockingQueue(100);
- int width;
- int height;
- int cameraFacing;
- long startMS = 0;
- byte[] rotatedFrameData = null;
- byte[] planeManagedData = null;
- boolean encoderStarted = false;
-
- public Encoder(int cameraFacing, int width, int height) {
- this.cameraFacing = cameraFacing;
- this.height = height;
- this.width = width;
- }
-
- abstract public void encode(byte[] rawData);
-
- public abstract void stopEncoder();
-
- public boolean hasEncoderStarted() {
- return encoderStarted;
- }
-
- public void setStartMS(long ms) {
- this.startMS = ms;
- }
-
- public static void YV12toYUV420PackedSemiPlanar(final byte[] input, byte[] out, final int width, final int height) {
-
- final int frameSize = width * height;
- final int qFrameSize = frameSize / 4;
-
- for (int i = 0; i < input.length; i++) {
- if (i < frameSize)
- out[i] = input[i];
- if (i < (qFrameSize)) {
- out[frameSize + i * 2] = input[frameSize + i + qFrameSize]; // Cb (U)
- out[frameSize + i * 2 + 1] = input[frameSize + i]; // Cr (V)
- }
- }
- }
-
- static byte[] NV21toYUV420p(byte[] data, int width, int height) {
- int len_target = (width * height * 3) / 2;
- byte[] buf_target = new byte[len_target];
- System.arraycopy(data, 0, buf_target, 0, width * height);
-
- for (int i = 0; i < (width * height / 4); i++) {
- buf_target[(width * height) + i] = data[(width * height) + 2 * i + 1];
- buf_target[(width * height) + (width * height / 4) + i] = data[(width * height) + 2 * i];
- }
- return buf_target;
- }
-
- public void rotateYUV420Degree90(byte[] data, byte[] output, int imageWidth, int imageHeight) {
- int i = 0;
- for (int x = 0; x < imageWidth; x++) {
- for (int y = imageHeight - 1; y >= 0; y--) {
- output[i] = data[y * imageWidth + x];
- i++;
- }
- }
-
- i = imageWidth * imageHeight * 3 / 2 - 1;
- for (int x = imageWidth - 1; x > 0; x = x - 2) {
- for (int y = 0; y < imageHeight / 2; y++) {
- output[i] = data[(imageWidth * imageHeight) + (y * imageWidth) + x];
- i--;
- output[i] = data[(imageWidth * imageHeight) + (y * imageWidth) + (x - 1)];
- i--;
- }
- }
- }
-
-}
\ No newline at end of file
diff --git a/camerakit/src/main/java/com/flurgle/camerakit/encoding/Frame.java b/camerakit/src/main/java/com/flurgle/camerakit/encoding/Frame.java
deleted file mode 100644
index 1fbc7a69..00000000
--- a/camerakit/src/main/java/com/flurgle/camerakit/encoding/Frame.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package com.flurgle.camerakit.encoding;
-
-public class Frame {
-
- public int id;
- public byte[] frameData;
-
- public Frame(int id) {
- this.id = id;
- }
-
-}
\ No newline at end of file
diff --git a/camerakit/src/main/java/com/flurgle/camerakit/encoding/VideoEncoder.java b/camerakit/src/main/java/com/flurgle/camerakit/encoding/VideoEncoder.java
deleted file mode 100644
index c3163cbd..00000000
--- a/camerakit/src/main/java/com/flurgle/camerakit/encoding/VideoEncoder.java
+++ /dev/null
@@ -1,157 +0,0 @@
-package com.flurgle.camerakit.encoding;
-
-import android.content.Context;
-import android.graphics.ImageFormat;
-import android.media.MediaCodec;
-import android.media.MediaCodecInfo;
-import android.media.MediaFormat;
-import android.util.Log;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.util.concurrent.ArrayBlockingQueue;
-
-public class VideoEncoder extends Encoder {
-
- File mVideoFile;
- FileOutputStream mFOS;
-
- MediaCodec mMediaCodec;
- ByteBuffer[] inputBuffers;
- ByteBuffer[] outputBuffers;
- MediaFormat mediaFormat = null;
- public static int frameID = 0;
-
- public VideoEncoder(Context context, int cameraFacing, int width, int height) throws IOException {
- super(cameraFacing, width, height);
-
- try {
- mVideoFile = new File(context.getExternalFilesDir(null), "video.mp4");
- mFOS = new FileOutputStream(mVideoFile, false);
- } catch (FileNotFoundException e) {
- e.printStackTrace();
- }
- queue = new ArrayBlockingQueue<>(100);
-
- mMediaCodec = MediaCodec.createEncoderByType("video/avc");
- mediaFormat = MediaFormat.createVideoFormat("video/avc", width, height);
- mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, 125000);
- mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 15);
- mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 5);
- mediaFormat.setInteger(MediaFormat.KEY_SAMPLE_RATE, 8000);
- mediaFormat.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);
-
- try {
- mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar);
-
- mMediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
- frameID = 0;
-
- rotatedFrameData = new byte[width * height * (ImageFormat.getBitsPerPixel(ImageFormat.YV12)) / 8];
- planeManagedData = new byte[width * height * (ImageFormat.getBitsPerPixel(ImageFormat.YV12)) / 8];
-
- encoderStarted = true;
- mMediaCodec.start();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
-
- public void stopEncoder() {
- encoderStarted = false;
- if (mMediaCodec != null) {
- mMediaCodec.stop();
- mMediaCodec.release();
- mMediaCodec = null;
- }
-
- if (mFOS != null) {
- try {
- mFOS.close();
- } catch (IOException e) {
-
- }
- }
- }
-
-
- @Override
- public void encode(byte[] rawData) {
- inputBuffers = mMediaCodec.getInputBuffers();
- outputBuffers = mMediaCodec.getOutputBuffers();
-
- int inputBufferIndex = mMediaCodec.dequeueInputBuffer(0);
- if (inputBufferIndex >= 0) {
- ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
- inputBuffer.clear();
-
- int size = inputBuffer.limit();
- inputBuffer.put(rawData);
-
- mMediaCodec.queueInputBuffer(inputBufferIndex, 0, size, (System.currentTimeMillis() - startMS) * 1000, 0);
- } else {
- return;
- }
-
- MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
- int outputBufferIndex = mMediaCodec.dequeueOutputBuffer(bufferInfo, 0);
- do {
- if (outputBufferIndex >= 0) {
- Frame frame = new Frame(frameID);
- ByteBuffer outBuffer = outputBuffers[outputBufferIndex];
- byte[] outData = new byte[bufferInfo.size];
- int dataLength = 0;
-
- outBuffer.get(outData);
-
- dataLength = outData.length - 2;
- frame.frameData = new byte[dataLength];
- // skipping 0x00 0x80 while copying
- System.arraycopy(outData, 2, frame.frameData, 0, dataLength);
-
- try {
- if (bufferInfo.offset != 0) {
- mFOS.write(outData, bufferInfo.offset, outData.length - bufferInfo.offset);
- } else {
- mFOS.write(outData, 0, outData.length);
- }
- mFOS.flush();
- } catch (IOException e) {
- Log.e("Encoding", e.toString());
- }
-
- try {
- queue.put(frame);
- } catch (InterruptedException e) {
- Log.e("EncodeDecode", "interrupted while waiting");
- e.printStackTrace();
- } catch (NullPointerException e) {
- Log.e("EncodeDecode", "frame is null");
- e.printStackTrace();
- } catch (IllegalArgumentException e) {
- Log.e("EncodeDecode", "problem inserting in the queue");
- e.printStackTrace();
- }
- Log.d("EncodeDecode", "H263 frame enqueued. queue size now: " + queue.size());
-
- frameID++;
- mMediaCodec.releaseOutputBuffer(outputBufferIndex, false);
- outputBufferIndex = mMediaCodec.dequeueOutputBuffer(bufferInfo, 0);
-
- } else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
- outputBuffers = mMediaCodec.getOutputBuffers();
- Log.e("EncodeDecode", "output buffer of encoder : info changed");
- } else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
- Log.e("EncodeDecode", "output buffer of encoder : format changed");
- } else {
- Log.e("EncodeDecode", "unknown value of outputBufferIndex : " + outputBufferIndex);
- }
- } while (outputBufferIndex >= 0);
- }
-
-
-}
diff --git a/camerakit/src/main/res/values/attrs.xml b/camerakit/src/main/res/values/attrs.xml
index 154736ac..62d49bce 100644
--- a/camerakit/src/main/res/values/attrs.xml
+++ b/camerakit/src/main/res/values/attrs.xml
@@ -30,6 +30,8 @@
+
+
diff --git a/demo/src/main/java/com/flurgle/camerakit/demo/MainActivity.java b/demo/src/main/java/com/flurgle/camerakit/demo/MainActivity.java
index 6d620278..7c703f6f 100644
--- a/demo/src/main/java/com/flurgle/camerakit/demo/MainActivity.java
+++ b/demo/src/main/java/com/flurgle/camerakit/demo/MainActivity.java
@@ -112,14 +112,6 @@ public class MainActivity extends AppCompatActivity {
@OnClick(R.id.captureVideo)
void captureVideo() {
- camera.startRecordingVideo();
- camera.postDelayed(new Runnable() {
- @Override
- public void run() {
- camera.stopRecordingVideo();
- }
- }, 3000);
-
camera.setCameraListener(new CameraListener() {
@Override
public void onVideoTaken(File video) {
@@ -128,6 +120,14 @@ public class MainActivity extends AppCompatActivity {
previewDialog.show();
}
});
+
+ camera.startRecordingVideo();
+ camera.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ camera.stopRecordingVideo();
+ }
+ }, 3000);
}
@OnClick(R.id.toggleCamera)
diff --git a/demo/src/main/res/layout/activity_main.xml b/demo/src/main/res/layout/activity_main.xml
index 73b889be..369bb7bf 100644
--- a/demo/src/main/res/layout/activity_main.xml
+++ b/demo/src/main/res/layout/activity_main.xml
@@ -16,7 +16,7 @@
+ android:text="wrap_content" />
+ android:text="match_parent" />