captureSnapshot() API

pull/1/head
Mattia Iavarone 7 years ago
parent fe33c1fa7a
commit 1e5563e833
  1. 11
      README.md
  2. 109
      camerakit/src/main/api16/com/flurgle/camerakit/Camera1.java
  3. 5
      camerakit/src/main/api21/com/flurgle/camerakit/Camera2.java
  4. 1
      camerakit/src/main/base/com/flurgle/camerakit/CameraImpl.java
  5. 11
      camerakit/src/main/java/com/flurgle/camerakit/CameraUtils.java
  6. 44
      camerakit/src/main/java/com/flurgle/camerakit/CameraView.java

@ -7,6 +7,7 @@
- *simpler APIs*
- *docs and comments in code*
- *introduced sessionType (picture or video), replacing Method and Permissions stuff*
- *new `captureSnapshot` API*
- *new `setLocation` and `setWhiteBalance` APIs*
- *option to pass a `File` when recording a video*
- *introduced a smart measuring and sizing behavior, replacing bugged `adjustViewBounds`*
@ -51,11 +52,11 @@ CameraKit is an easy to use utility to work with the Android Camera APIs. Everyt
- System permission handling
- Dynamic sizing behavior
- Create a `CameraView` of any size (not just presets!)
- Or let it adapt to the sensor preview size
- Center inside or center crop behaviors
- Automatic output cropping to match your `CameraView` bounds
- Multiple capture methods
- While taking pictures, image is captured normally using the camera APIs.
- While shooting videos, image is captured as a freeze frame of the `CameraView` preview (similar to SnapChat and Instagram)
- Take high-resolution pictures with `capturePicture`
- Take quick snapshots as a freeze frame of the preview with `captureSnapshot`, even while recording videos (similar to SnapChat and Instagram)
- Built-in tap to focus
- `CameraUtils` to help with Bitmaps and orientations
- EXIF support
@ -109,7 +110,7 @@ camera.setCameraListener(new CameraListener() {
@Override
public void onPictureTaken(byte[] picture) {
// Create a bitmap or a file...
// CameraUtils will read EXIF orientation for you.
// CameraUtils will read EXIF orientation for you, in a worker thread.
CameraUtils.decodeBitmap(picture, ...);
}
});
@ -117,6 +118,8 @@ camera.setCameraListener(new CameraListener() {
camera.captureImage();
```
You can also use `camera.captureSnapshot()` to capture a preview frame. This is faster, though has lower quality, and can be used while recording videos.
### Capturing Video
TODO: test size and orientation stuff.

@ -63,6 +63,7 @@ class Camera1 extends CameraImpl {
private ConstantMapper.MapperImpl mMapper = new ConstantMapper.Mapper1();
private boolean mIsSetup = false;
private boolean mIsCapturingImage = false;
private boolean mIsCapturingVideo = false;
private final Object mLock = new Object();
@ -360,57 +361,62 @@ class Camera1 extends CameraImpl {
void captureImage() {
if (mIsCapturingImage) return;
if (!isCameraOpened()) return;
switch (mSessionType) {
case SESSION_TYPE_PICTURE:
// Set boolean to wait for image callback
mIsCapturingImage = true;
synchronized (mLock) {
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);
mIsCapturingImage = false;
camera.startPreview(); // This is needed, read somewhere in the docs.
}
});
break;
case SESSION_TYPE_VIDEO:
// If we are in a video session, camera captures are fast captures coming
// from the preview stream.
// TODO: will this work while recording a video? test...
mIsCapturingImage = true;
mCamera.setOneShotPreviewCallback(new Camera.PreviewCallback() {
if (mIsCapturingVideo || mSessionType == SESSION_TYPE_VIDEO) {
captureSnapshot();
return;
}
// Set boolean to wait for image callback
mIsCapturingImage = true;
synchronized (mLock) {
Camera.Parameters parameters = mCamera.getParameters();
parameters.setRotation(computeExifRotation());
// TODO: add flipping
mCamera.setParameters(parameters);
}
mCamera.takePicture(null, null, null,
new Camera.PictureCallback() {
@Override
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.
Camera.Parameters params = mCamera.getParameters();
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 = params.getPreviewFormat();
new Thread(new Runnable() {
@Override
public void run() {
byte[] rotatedData = RotationHelper.rotate(data, preWidth, preHeight, rotation);
YuvImage yuv = new YuvImage(rotatedData, format, postWidth, postHeight, null);
mCameraListener.processYuvImage(yuv);
mIsCapturingImage = false;
}
}).start();
public void onPictureTaken(byte[] data, Camera camera) {
mCameraListener.onPictureTaken(data);
mIsCapturingImage = false;
camera.startPreview(); // This is needed, read somewhere in the docs.
}
});
break;
}
}
@Override
void captureSnapshot() {
if (mIsCapturingImage) return;
if (!isCameraOpened()) return;
// TODO: will this work while recording a video? test...
mIsCapturingImage = true;
mCamera.setOneShotPreviewCallback(new Camera.PreviewCallback() {
@Override
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.
// TODO: add exif, and also care about flipping.
Camera.Parameters params = mCamera.getParameters();
final int rotation = computeExifRotation();
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 = params.getPreviewFormat();
new Thread(new Runnable() {
@Override
public void run() {
byte[] rotatedData = RotationHelper.rotate(data, preWidth, preHeight, rotation);
YuvImage yuv = new YuvImage(rotatedData, format, postWidth, postHeight, null);
mCameraListener.processYuvImage(yuv);
mIsCapturingImage = false;
}
}).start();
}
});
}
@Override
@ -458,8 +464,9 @@ class Camera1 extends CameraImpl {
/**
* Returns the orientation to be set as a exif tag. This is already managed by
* the camera APIs as long as you call {@link Camera.Parameters#setRotation(int)}.
* This ignores flipping for facing camera.
*/
private int computeExifOrientation() {
private int computeExifRotation() {
return (mDeviceOrientation + mSensorOffset) % 360;
}
@ -507,7 +514,10 @@ class Camera1 extends CameraImpl {
@Override
void startVideo(@NonNull File videoFile) {
mVideoFile = videoFile;
if (mIsCapturingVideo) return;
if (!isCameraOpened()) return;
if (mSessionType == SESSION_TYPE_VIDEO) {
mIsCapturingVideo = true;
initMediaRecorder();
try {
mMediaRecorder.prepare();
@ -525,6 +535,7 @@ class Camera1 extends CameraImpl {
@Override
void endVideo() {
mIsCapturingVideo = false;
mMediaRecorder.stop();
mMediaRecorder.release();
mMediaRecorder = null;

@ -182,6 +182,11 @@ class Camera2 extends CameraImpl {
}
@Override
void captureSnapshot() {
}
@Override
void startVideo(@NonNull File videoFile) {

@ -33,6 +33,7 @@ abstract class CameraImpl implements PreviewImpl.SurfaceCallback {
abstract void setLocation(double latitude, double longitude);
abstract void captureImage();
abstract void captureSnapshot();
abstract void startVideo(@NonNull File file);
abstract void endVideo();

@ -52,6 +52,7 @@ public class CameraUtils {
public void run() {
int orientation = 0;
boolean flip = false;
try {
// http://sylvana.net/jpegcrop/exif_orientation.html
ExifInterface exif = new ExifInterface(new ByteArrayInputStream(source));
@ -75,16 +76,24 @@ public class CameraUtils {
default: orientation = 0;
}
flip = exifOrientation == ExifInterface.ORIENTATION_FLIP_HORIZONTAL ||
exifOrientation == ExifInterface.ORIENTATION_FLIP_VERTICAL ||
exifOrientation == ExifInterface.ORIENTATION_TRANSPOSE ||
exifOrientation == ExifInterface.ORIENTATION_TRANSVERSE;
} catch (IOException e) {
e.printStackTrace();
orientation = 0;
flip = false;
}
Bitmap bitmap = BitmapFactory.decodeByteArray(source, 0, source.length);
if (orientation != 0) {
if (orientation != 0 || flip) {
Matrix matrix = new Matrix();
matrix.setRotate(orientation);
// matrix.postScale(1, -1) Flip... needs testing.
Bitmap temp = bitmap;
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
temp.recycle();

@ -660,6 +660,9 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
/**
* Set the current session type to either picture or video.
* When sessionType is video,
* - {@link #startCapturingVideo(File)} will not throw any exception
* - {@link #captureImage()} will fallback to {@link #captureSnapshot()}
*
* @see CameraKit.Constants#SESSION_TYPE_PICTURE
* @see CameraKit.Constants#SESSION_TYPE_VIDEO
@ -773,11 +776,38 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
}
/**
* Asks the camera to capture an image of the current scene.
* This will trigger {@link CameraListener#onPictureTaken(byte[])} if a listener
* was registered.
*
* Note that if sessionType is {@link CameraKit.Constants#SESSION_TYPE_VIDEO}, this
* falls back to {@link #captureSnapshot()} (that is, we will capture a preview frame).
*
* @see #captureSnapshot()
*/
public void captureImage() {
mCameraImpl.captureImage();
}
/**
* Asks the camera to capture a snapshot of the current preview.
* This eventually triggers {@link CameraListener#onPictureTaken(byte[])} if a listener
* was registered.
*
* The difference with {@link #captureImage()} is that:
* - this capture is faster, so it might be better on slower cameras, though the result can be
* generally blurry or low quality
* - this can be called even if sessionType is {@link CameraKit.Constants#SESSION_TYPE_VIDEO}
*
* @see #captureImage()
*/
public void captureSnapshot() {
mCameraImpl.captureSnapshot();
}
/**
* Starts recording a video with selected options, in a file called
* "video.mp4" in the default folder.
@ -824,6 +854,7 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
return mCameraImpl != null ? mCameraImpl.getPreviewSize() : null;
}
/**
* Returns the size used for the capture,
* or null if it hasn't been computed yet (for example if the surface is not ready).
@ -834,6 +865,19 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
return mCameraImpl != null ? mCameraImpl.getCaptureSize() : null;
}
/**
* Returns the size used for capturing snapshots.
* This is equal to {@link #getPreviewSize()}.
*
* @return a Size
*/
@Nullable
public Size getSnapshotSize() {
return getPreviewSize();
}
private void requestPermissions(boolean requestCamera, boolean requestAudio) {
Activity activity = null;
Context context = getContext();

Loading…
Cancel
Save