From 51147e88ef037002743dc0dc328444c9c05a6b5f Mon Sep 17 00:00:00 2001 From: Mattia Iavarone Date: Fri, 4 Aug 2017 22:15:28 +0200 Subject: [PATCH] Support for pics while recording videos --- .../api16/com/flurgle/camerakit/Camera1.java | 35 ++++-- .../com/flurgle/camerakit/CameraView.java | 14 +-- .../flurgle/camerakit/demo/MainActivity.java | 118 ++++++++---------- 3 files changed, 81 insertions(+), 86 deletions(-) diff --git a/camerakit/src/main/api16/com/flurgle/camerakit/Camera1.java b/camerakit/src/main/api16/com/flurgle/camerakit/Camera1.java index 86fd01ef..37835133 100644 --- a/camerakit/src/main/api16/com/flurgle/camerakit/Camera1.java +++ b/camerakit/src/main/api16/com/flurgle/camerakit/Camera1.java @@ -175,6 +175,7 @@ class Camera1 extends CameraImpl { void stop() { mFocusHandler.removeCallbacksAndMessages(null); if (isCameraOpened()) { + if (mIsCapturingVideo) endVideo(); mCamera.stopPreview(); mCamera.release(); mCameraListener.dispatchOnCameraClosed(); @@ -361,9 +362,9 @@ class Camera1 extends CameraImpl { void captureImage() { if (mIsCapturingImage) return; if (!isCameraOpened()) return; - if (mIsCapturingVideo || mSessionType == SESSION_TYPE_VIDEO) { - captureSnapshot(); - return; + if (mSessionType == SESSION_TYPE_VIDEO && mIsCapturingVideo) { + Camera.Parameters parameters = mCamera.getParameters(); + if (!parameters.isVideoSnapshotSupported()) return; } // Set boolean to wait for image callback @@ -395,9 +396,14 @@ class Camera1 extends CameraImpl { @Override void captureSnapshot() { - if (mIsCapturingImage) return; if (!isCameraOpened()) return; - // TODO: will this work while recording a video? test... + if (mIsCapturingImage) return; + // This won't work while capturing a video. + // Switch to captureImage. + if (mIsCapturingVideo) { + captureImage(); + return; + } mIsCapturingImage = true; mCamera.setOneShotPreviewCallback(new Camera.PreviewCallback() { @Override @@ -539,6 +545,8 @@ class Camera1 extends CameraImpl { mVideoFile = videoFile; if (mIsCapturingVideo) return; if (!isCameraOpened()) return; + Camera.Parameters params = mCamera.getParameters(); + params.setVideoStabilization(false); if (mSessionType == SESSION_TYPE_VIDEO) { mIsCapturingVideo = true; initMediaRecorder(); @@ -558,13 +566,15 @@ class Camera1 extends CameraImpl { @Override void endVideo() { - mIsCapturingVideo = false; - mMediaRecorder.stop(); - mMediaRecorder.release(); - mMediaRecorder = null; - if (mVideoFile != null) { - mCameraListener.dispatchOnVideoTaken(mVideoFile); - mVideoFile = null; + if (mIsCapturingVideo) { + mIsCapturingVideo = false; + mMediaRecorder.stop(); + mMediaRecorder.release(); + mMediaRecorder = null; + if (mVideoFile != null) { + mCameraListener.dispatchOnVideoTaken(mVideoFile); + mVideoFile = null; + } } } @@ -572,7 +582,6 @@ class Camera1 extends CameraImpl { private void initMediaRecorder() { mMediaRecorder = new MediaRecorder(); mCamera.unlock(); - mMediaRecorder.setCamera(mCamera); mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); diff --git a/camerakit/src/main/java/com/flurgle/camerakit/CameraView.java b/camerakit/src/main/java/com/flurgle/camerakit/CameraView.java index 7679a61e..dc55c358 100644 --- a/camerakit/src/main/java/com/flurgle/camerakit/CameraView.java +++ b/camerakit/src/main/java/com/flurgle/camerakit/CameraView.java @@ -804,16 +804,12 @@ public class CameraView extends FrameLayout implements LifecycleObserver { * 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). + * might fall back to {@link #captureSnapshot()} (that is, we might capture a preview frame). * * @see #captureSnapshot() */ public void captureImage() { - if (mSessionType == CameraKit.Constants.SESSION_TYPE_PICTURE) { - mCameraImpl.captureImage(); - } else { - captureSnapshot(); - } + mCameraImpl.captureImage(); } @@ -822,10 +818,8 @@ public class CameraView extends FrameLayout implements LifecycleObserver { * 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} + * 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. * * @see #captureImage() */ 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 8c131220..f6259af4 100644 --- a/demo/src/main/java/com/flurgle/camerakit/demo/MainActivity.java +++ b/demo/src/main/java/com/flurgle/camerakit/demo/MainActivity.java @@ -1,8 +1,6 @@ package com.flurgle.camerakit.demo; import android.content.Intent; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; import android.net.Uri; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; @@ -35,17 +33,14 @@ public class MainActivity extends AppCompatActivity implements View.OnLayoutChan CameraView camera; // Capture Mode: - @BindView(R.id.sessionTypeRadioGroup) RadioGroup sessionTypeRadioGroup; // Crop Mode: - @BindView(R.id.cropModeRadioGroup) RadioGroup cropModeRadioGroup; // Width: - @BindView(R.id.screenWidth) TextView screenWidth; @BindView(R.id.width) @@ -56,7 +51,6 @@ public class MainActivity extends AppCompatActivity implements View.OnLayoutChan RadioGroup widthModeRadioGroup; // Height: - @BindView(R.id.screenHeight) TextView screenHeight; @BindView(R.id.height) @@ -66,9 +60,11 @@ public class MainActivity extends AppCompatActivity implements View.OnLayoutChan @BindView(R.id.heightModeRadioGroup) RadioGroup heightModeRadioGroup; - private int mCameraWidth; - private int mCameraHeight; - private boolean mCapturing; + private boolean mCapturingPicture; + private boolean mCapturingVideo; + + private CameraListener mPictureListener; + private CameraListener mVideoListener; @Override protected void onCreate(Bundle savedInstanceState) { @@ -92,6 +88,11 @@ public class MainActivity extends AppCompatActivity implements View.OnLayoutChan heightModeRadioGroup.setOnCheckedChangeListener(heightModeChangedListener); } + private void message(String content, boolean important) { + int length = important ? Toast.LENGTH_LONG : Toast.LENGTH_SHORT; + Toast.makeText(this, content, length).show(); + } + @Override protected void onResume() { super.onResume(); @@ -112,18 +113,23 @@ public class MainActivity extends AppCompatActivity implements View.OnLayoutChan @OnClick(R.id.capturePhoto) void capturePhoto() { - if (mCapturing) return; - mCapturing = true; + if (mCapturingPicture) return; + mCapturingPicture = true; final long startTime = System.currentTimeMillis(); - final Size nativeSize = camera.getSessionType() == CameraKit.Constants.SESSION_TYPE_PICTURE ? - camera.getCaptureSize() : camera.getSnapshotSize(); - camera.clearCameraListeners(); - camera.addCameraListener(new CameraListener() { + final boolean snapshot = camera.getSessionType() != CameraKit.Constants.SESSION_TYPE_PICTURE; + final Size nativeSize = snapshot ? camera.getSnapshotSize() : camera.getCaptureSize(); + message(snapshot ? "Capturing snapshot..." : "Capturing picture...", false); + camera.removeCameraListener(mPictureListener); + mPictureListener = new CameraListener() { @Override public void onPictureTaken(byte[] jpeg) { super.onPictureTaken(jpeg); - mCapturing = false; + mCapturingPicture = false; long callbackTime = System.currentTimeMillis(); + if (mCapturingVideo) { + message("Captured while taking video. Size="+nativeSize, false); + return; + } PicturePreviewActivity.setImage(jpeg); Intent intent = new Intent(MainActivity.this, PicturePreviewActivity.class); intent.putExtra("delay", callbackTime-startTime); @@ -131,30 +137,32 @@ public class MainActivity extends AppCompatActivity implements View.OnLayoutChan intent.putExtra("nativeHeight", nativeSize.getHeight()); startActivity(intent); } - }); + }; + camera.addCameraListener(mPictureListener); camera.captureImage(); } @OnClick(R.id.captureVideo) void captureVideo() { if (camera.getSessionType() != CameraKit.Constants.SESSION_TYPE_VIDEO) { - Toast.makeText(this, "Can't record video while session type is 'picture'.", Toast.LENGTH_SHORT).show(); + message("Can't record video while session type is 'picture'.", false); return; } - if (mCapturing) return; - mCapturing = true; - camera.clearCameraListeners(); - camera.addCameraListener(new CameraListener() { + if (mCapturingPicture || mCapturingVideo) return; + mCapturingVideo = true; + camera.removeCameraListener(mVideoListener); + mVideoListener = new CameraListener() { @Override public void onVideoTaken(File video) { super.onVideoTaken(video); - mCapturing = false; + mCapturingVideo = false; Intent intent = new Intent(MainActivity.this, VideoPreviewActivity.class); intent.putExtra("video", Uri.fromFile(video)); startActivity(intent); } - }); - Toast.makeText(this, "Recording for 8 seconds...", Toast.LENGTH_LONG).show(); + }; + camera.addCameraListener(mVideoListener); + message("Recording for 8 seconds...", true); camera.startCapturingVideo(null); camera.postDelayed(new Runnable() { @Override @@ -166,32 +174,32 @@ public class MainActivity extends AppCompatActivity implements View.OnLayoutChan @OnClick(R.id.toggleCamera) void toggleCamera() { - if (mCapturing) return; + if (mCapturingPicture) return; switch (camera.toggleFacing()) { case CameraKit.Constants.FACING_BACK: - Toast.makeText(this, "Switched to back camera!", Toast.LENGTH_SHORT).show(); + message("Switched to back camera!", false); break; case CameraKit.Constants.FACING_FRONT: - Toast.makeText(this, "Switched to front camera!", Toast.LENGTH_SHORT).show(); + message("Switched to front camera!", false); break; } } @OnClick(R.id.toggleFlash) void toggleFlash() { - if (mCapturing) return; + if (mCapturingPicture) return; switch (camera.toggleFlash()) { case CameraKit.Constants.FLASH_ON: - Toast.makeText(this, "Flash on!", Toast.LENGTH_SHORT).show(); + message("Flash on!", false); break; case CameraKit.Constants.FLASH_OFF: - Toast.makeText(this, "Flash off!", Toast.LENGTH_SHORT).show(); + message("Flash off!", false); break; case CameraKit.Constants.FLASH_AUTO: - Toast.makeText(this, "Flash auto!", Toast.LENGTH_SHORT).show(); + message("Flash auto!", false); break; } } @@ -199,28 +207,28 @@ public class MainActivity extends AppCompatActivity implements View.OnLayoutChan RadioGroup.OnCheckedChangeListener sessionTypeChangedListener = new RadioGroup.OnCheckedChangeListener() { @Override public void onCheckedChanged(RadioGroup group, int checkedId) { - if (mCapturing) return; + if (mCapturingPicture) return; camera.setSessionType( checkedId == R.id.sessionTypePicture ? CameraKit.Constants.SESSION_TYPE_PICTURE : CameraKit.Constants.SESSION_TYPE_VIDEO ); - Toast.makeText(MainActivity.this, "Session type set to" + (checkedId == R.id.sessionTypePicture ? " picture!" : " video!"), Toast.LENGTH_SHORT).show(); + message("Session type set to" + (checkedId == R.id.sessionTypePicture ? " picture!" : " video!"), true); } }; RadioGroup.OnCheckedChangeListener cropModeChangedListener = new RadioGroup.OnCheckedChangeListener() { @Override public void onCheckedChanged(RadioGroup group, int checkedId) { - if (mCapturing) return; + if (mCapturingPicture) return; camera.setCropOutput(checkedId == R.id.modeCropVisible); - Toast.makeText(MainActivity.this, "Picture cropping is" + (checkedId == R.id.modeCropVisible ? " on!" : " off!"), Toast.LENGTH_SHORT).show(); + message("Picture cropping is" + (checkedId == R.id.modeCropVisible ? " on!" : " off!"), false); } }; @OnClick(R.id.widthUpdate) void widthUpdateClicked() { - if (mCapturing) return; + if (mCapturingPicture) return; if (widthUpdate.getAlpha() >= 1) { updateCamera(true, false); } @@ -229,7 +237,7 @@ public class MainActivity extends AppCompatActivity implements View.OnLayoutChan RadioGroup.OnCheckedChangeListener widthModeChangedListener = new RadioGroup.OnCheckedChangeListener() { @Override public void onCheckedChanged(RadioGroup group, int checkedId) { - if (mCapturing) return; + if (mCapturingPicture) return; widthUpdate.setEnabled(checkedId == R.id.widthCustom); widthUpdate.setAlpha(checkedId == R.id.widthCustom ? 1f : 0.3f); width.clearFocus(); @@ -242,7 +250,7 @@ public class MainActivity extends AppCompatActivity implements View.OnLayoutChan @OnClick(R.id.heightUpdate) void heightUpdateClicked() { - if (mCapturing) return; + if (mCapturingPicture) return; if (heightUpdate.getAlpha() >= 1) { updateCamera(false, true); } @@ -251,7 +259,7 @@ public class MainActivity extends AppCompatActivity implements View.OnLayoutChan RadioGroup.OnCheckedChangeListener heightModeChangedListener = new RadioGroup.OnCheckedChangeListener() { @Override public void onCheckedChanged(RadioGroup group, int checkedId) { - if (mCapturing) return; + if (mCapturingPicture) return; heightUpdate.setEnabled(checkedId == R.id.heightCustom); heightUpdate.setAlpha(checkedId == R.id.heightCustom ? 1f : 0.3f); height.clearFocus(); @@ -263,7 +271,7 @@ public class MainActivity extends AppCompatActivity implements View.OnLayoutChan }; private void updateCamera(boolean updateWidth, boolean updateHeight) { - if (mCapturing) return; + if (mCapturingPicture) return; ViewGroup.LayoutParams cameraLayoutParams = camera.getLayoutParams(); int width = cameraLayoutParams.width; int height = cameraLayoutParams.height; @@ -272,20 +280,11 @@ public class MainActivity extends AppCompatActivity implements View.OnLayoutChan switch (widthModeRadioGroup.getCheckedRadioButtonId()) { case R.id.widthCustom: String widthInput = this.width.getText().toString(); - if (widthInput.length() > 0) { - try { - width = Integer.valueOf(widthInput); - } catch (Exception e) { - - } - } - + try { width = Integer.valueOf(widthInput); } catch (Exception e) {} break; - case R.id.widthWrapContent: width = ViewGroup.LayoutParams.WRAP_CONTENT; break; - case R.id.widthMatchParent: width = ViewGroup.LayoutParams.MATCH_PARENT; break; @@ -296,19 +295,11 @@ public class MainActivity extends AppCompatActivity implements View.OnLayoutChan switch (heightModeRadioGroup.getCheckedRadioButtonId()) { case R.id.heightCustom: String heightInput = this.height.getText().toString(); - if (heightInput.length() > 0) { - try { - height = Integer.valueOf(heightInput); - } catch (Exception e) { - - } - } + try { height = Integer.valueOf(heightInput); } catch (Exception e) {} break; - case R.id.heightWrapContent: height = ViewGroup.LayoutParams.WRAP_CONTENT; break; - case R.id.heightMatchParent: // We are in a vertically scrolling container, match parent would not work at all. height = parent.getHeight(); @@ -321,13 +312,14 @@ public class MainActivity extends AppCompatActivity implements View.OnLayoutChan camera.addOnLayoutChangeListener(this); camera.setLayoutParams(cameraLayoutParams); - Toast.makeText(this, (updateWidth && updateHeight ? "Width and height" : updateWidth ? "Width" : "Height") + " updated!", Toast.LENGTH_SHORT).show(); + String what = (updateWidth && updateHeight ? "Width and height" : updateWidth ? "Width" : "Height"); + message(what + " updated! Internal preview size: " + camera.getPreviewSize(), false); } @Override public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { - mCameraWidth = right - left; - mCameraHeight = bottom - top; + int mCameraWidth = right - left; + int mCameraHeight = bottom - top; width.setText(String.valueOf(mCameraWidth)); height.setText(String.valueOf(mCameraHeight)); camera.removeOnLayoutChangeListener(this);