From b5821430f199a516c4b3181b63609a264e613719 Mon Sep 17 00:00:00 2001 From: Byunghun Jeon Date: Fri, 17 Jun 2016 15:52:25 -0700 Subject: SnapdragonCamera: Allow take snapshot while recording video Allow take snapshot while recording video by showing shutter button. Directly call capture when shutter button is pressed. Change-Id: Iae4d42d8878ea82b459ec67709b0ad8e340c8226 CRs-Fixed: 1028463 --- res/values/camera2arrays.xml | 10 ++ res/values/qcomstrings.xml | 9 ++ res/xml/capture_preferences.xml | 7 ++ src/com/android/camera/CaptureModule.java | 165 +++++++++++++++++++++++----- src/com/android/camera/CaptureUI.java | 5 +- src/com/android/camera/SettingsManager.java | 7 ++ 6 files changed, 175 insertions(+), 28 deletions(-) diff --git a/res/values/camera2arrays.xml b/res/values/camera2arrays.xml index ac569f473..089838405 100644 --- a/res/values/camera2arrays.xml +++ b/res/values/camera2arrays.xml @@ -795,4 +795,14 @@ for time lapse recording --> off on + + + @string/pref_camera2_videosnap_entry_enable + @string/pref_camera2_videosnap_entry_disable + + + + @string/pref_camera2_videosnap_value_enable + @string/pref_camera2_videosnap_value_disable + diff --git a/res/values/qcomstrings.xml b/res/values/qcomstrings.xml index 859803b23..6d1ec7862 100644 --- a/res/values/qcomstrings.xml +++ b/res/values/qcomstrings.xml @@ -994,5 +994,14 @@ off fast high-quality + + enable + Auto Snapshot Size + + enable + disable + + Enable + Disable diff --git a/res/xml/capture_preferences.xml b/res/xml/capture_preferences.xml index 06f816c7f..d7d478d0a 100644 --- a/res/xml/capture_preferences.xml +++ b/res/xml/capture_preferences.xml @@ -266,4 +266,11 @@ camera:key="pref_camera2_facedetection_key" camera:singleIcon="@drawable/ic_settings_facerec" camera:title="@string/pref_camera_facedetection_title"/> + + diff --git a/src/com/android/camera/CaptureModule.java b/src/com/android/camera/CaptureModule.java index 9a43db0a8..da72adc66 100644 --- a/src/com/android/camera/CaptureModule.java +++ b/src/com/android/camera/CaptureModule.java @@ -27,7 +27,6 @@ import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.res.Configuration; -import android.graphics.Camera; import android.graphics.ImageFormat; import android.graphics.Matrix; import android.graphics.Point; @@ -64,7 +63,6 @@ import android.os.SystemClock; import android.provider.MediaStore; import android.util.Log; import android.util.Size; -import android.util.SparseIntArray; import android.view.Display; import android.view.KeyEvent; import android.view.OrientationEventListener; @@ -95,12 +93,10 @@ import java.io.IOException; import java.nio.ByteBuffer; import java.text.SimpleDateFormat; import java.util.ArrayList; -import java.util.Arrays; import java.util.Comparator; import java.util.Date; import java.util.LinkedList; import java.util.List; -import java.util.Set; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; @@ -236,11 +232,12 @@ public class CaptureModule implements CameraModule, PhotoController, private int[] mPrecaptureRequestHashCode = new int[MAX_NUM_CAM]; private int[] mLockRequestHashCode = new int[MAX_NUM_CAM]; private final Handler mHandler = new MainHandler(); - private CameraCaptureSession mPreviewSession; + private CameraCaptureSession mCurrentSession; private Size mPreviewSize; private Size mPictureSize; private Size mVideoPreviewSize; private Size mVideoSize; + private Size mVideoSnapshotSize; private MediaRecorder mMediaRecorder; private boolean mIsRecordingVideo; @@ -258,6 +255,7 @@ public class CaptureModule implements CameraModule, PhotoController, private long mRecordingStartTime; private long mRecordingTotalTime; private boolean mRecordingTimeCountsDown = false; + private ImageReader mVideoSnapshotImageReader; private class MediaSaveNotifyThread extends Thread { private Uri uri; @@ -679,7 +677,7 @@ public class CaptureModule implements CameraModule, PhotoController, } // When the session is ready, we start displaying the preview. mCaptureSession[id] = cameraCaptureSession; - mPreviewSession = cameraCaptureSession; + mCurrentSession = cameraCaptureSession; initializePreviewConfiguration(id); setDisplayOrientation(); updateFaceDetection(); @@ -1049,6 +1047,50 @@ public class CaptureModule implements CameraModule, PhotoController, } } + private void captureVideoSnapshot(final int id) { + Log.d(TAG, "captureStillPicture " + id); + try { + if (null == mActivity || null == mCameraDevice[id]) { + warningToast("Camera is not ready yet to take a video snapshot."); + return; + } + CaptureRequest.Builder captureBuilder = + mCameraDevice[id].createCaptureRequest(CameraDevice.TEMPLATE_VIDEO_SNAPSHOT); + + captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, CameraUtil.getJpegRotation(id, mOrientation)); + applyVideoSnapshot(captureBuilder, id); + + captureBuilder.addTarget(mVideoSnapshotImageReader.getSurface()); + + mCurrentSession.capture(captureBuilder.build(), + new CameraCaptureSession.CaptureCallback() { + + @Override + public void onCaptureCompleted(CameraCaptureSession session, + CaptureRequest request, + TotalCaptureResult result) { + Log.d(TAG, "captureVideoSnapshot onCaptureCompleted: " + id); + } + + @Override + public void onCaptureFailed(CameraCaptureSession session, + CaptureRequest request, + CaptureFailure result) { + Log.d(TAG, "captureVideoSnapshot onCaptureFailed: " + id); + } + + @Override + public void onCaptureSequenceCompleted(CameraCaptureSession session, int + sequenceId, long frameNumber) { + Log.d(TAG, "captureVideoSnapshot onCaptureSequenceCompleted: " + id); + } + }, mCaptureCallbackHandler); + } catch (CameraAccessException e) { + Log.d(TAG, "captureVideoSnapshot failed"); + e.printStackTrace(); + } + } + /** * Run the precapture sequence for capturing a still image. This method should be called when * we get a response in {@link #mCaptureCallback} from {@link #lockFocus()}. @@ -1196,6 +1238,36 @@ public class CaptureModule implements CameraModule, PhotoController, } } + private void createVideoSnapshotImageReader() { + if (mVideoSnapshotImageReader != null) { + mVideoSnapshotImageReader.close(); + } + mVideoSnapshotImageReader = ImageReader.newInstance(mVideoSnapshotSize.getWidth(), + mVideoSnapshotSize.getHeight(), ImageFormat.JPEG, MAX_IMAGE_NUM); + mVideoSnapshotImageReader.setOnImageAvailableListener( + new ImageReader.OnImageAvailableListener() { + @Override + public void onImageAvailable(ImageReader reader) { + Image image = reader.acquireNextImage(); + mCaptureStartTime = System.currentTimeMillis(); + mNamedImages.nameNewImage(mCaptureStartTime); + NamedEntity name = mNamedImages.getNextNameEntity(); + String title = (name == null) ? null : name.title; + long date = (name == null) ? -1 : name.date; + + ByteBuffer buffer = image.getPlanes()[0].getBuffer(); + byte[] bytes = new byte[buffer.remaining()]; + mLastJpegData = bytes; + buffer.get(bytes); + + mActivity.getMediaSaveService().addImage(bytes, title, date, + null, image.getWidth(), image.getHeight(), 0, null, + mOnMediaSavedListener, mContentResolver, "jpeg"); + image.close(); + } + }, mImageAvailableHandler); + } + /** * Unlock the focus. This method should be called when still image capture sequence is * finished. @@ -1276,6 +1348,11 @@ public class CaptureModule implements CameraModule, PhotoController, mMediaRecorder.release(); mMediaRecorder = null; } + + if (null != mVideoSnapshotImageReader) { + mVideoSnapshotImageReader.close(); + mVideoSnapshotImageReader = null; + } } /* no need to set this in the callback and handle asynchronously. This is the same reason as why we release the semaphore here, not in camera close callback function @@ -1328,6 +1405,13 @@ public class CaptureModule implements CameraModule, PhotoController, applyFaceDetect(builder, id); } + private void applyVideoSnapshot(CaptureRequest.Builder builder, int id) { + builder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO); + applyWhiteBalance(builder); + applyColorEffect(builder); + applyVideoFlash(builder); + } + private void applyCommonSettings(CaptureRequest.Builder builder, int id) { builder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO); builder.set(CaptureRequest.CONTROL_AF_MODE, mControlAFMode); @@ -1476,6 +1560,7 @@ public class CaptureModule implements CameraModule, PhotoController, private void initializeValues() { updatePictureSize(); updateVideoSize(); + updateVideoSnapshotSize(); updateTimeLapseSetting(); estimateJpegFileSize(); updateMaxVideoDuration(); @@ -1912,6 +1997,16 @@ public class CaptureModule implements CameraModule, PhotoController, mVideoPreviewSize = getOptimalPreviewSize(mVideoSize, prevSizes, screenSize.x, screenSize.y); } + private void updateVideoSnapshotSize() { + String auto = mSettingsManager.getValue(SettingsManager.KEY_AUTO_VIDEOSNAP_SIZE); + if (auto != null && auto.equals("enable")) { + Size[] sizes = mSettingsManager.getSupportedOutputSize(getMainCameraId(), ImageFormat.JPEG); + mVideoSnapshotSize = getMaxSizeWithRatio(sizes, mVideoSize); + } else { + mVideoSnapshotSize = mPictureSize; + } + } + private void updateMaxVideoDuration() { String minutesStr = mSettingsManager.getValue(SettingsManager.KEY_VIDEO_DURATION); int minutes = Integer.parseInt(minutesStr); @@ -1946,6 +2041,7 @@ public class CaptureModule implements CameraModule, PhotoController, try { setUpMediaRecorder(cameraId); + createVideoSnapshotImageReader(); final CaptureRequest.Builder mPreviewBuilder = mCameraDevice[cameraId] .createCaptureRequest(CameraDevice.TEMPLATE_RECORD); List surfaces = new ArrayList<>(); @@ -1955,6 +2051,7 @@ public class CaptureModule implements CameraModule, PhotoController, mPreviewBuilder.addTarget(previewSurface); surfaces.add(mMediaRecorder.getSurface()); mPreviewBuilder.addTarget(mMediaRecorder.getSurface()); + surfaces.add(mVideoSnapshotImageReader.getSurface()); mCameraDevice[cameraId].createCaptureSession(surfaces, new CameraCaptureSession .StateCallback() { @@ -1962,10 +2059,10 @@ public class CaptureModule implements CameraModule, PhotoController, @Override public void onConfigured(CameraCaptureSession cameraCaptureSession) { Log.d(TAG, "StartRecordingVideo session onConfigured"); - mPreviewSession = cameraCaptureSession; + mCurrentSession = cameraCaptureSession; try { setUpVideoCaptureRequestBuilder(mPreviewBuilder); - mPreviewSession.setRepeatingRequest(mPreviewBuilder.build(), null, mCameraHandler); + mCurrentSession.setRepeatingRequest(mPreviewBuilder.build(), null, mCameraHandler); } catch (CameraAccessException e) { e.printStackTrace(); } @@ -2149,9 +2246,9 @@ public class CaptureModule implements CameraModule, PhotoController, private void closePreviewSession() { Log.d(TAG, "closePreviewSession"); - if (mPreviewSession != null) { - mPreviewSession.close(); - mPreviewSession = null; + if (mCurrentSession != null) { + mCurrentSession.close(); + mCurrentSession = null; } } @@ -2289,22 +2386,26 @@ public class CaptureModule implements CameraModule, PhotoController, return; } - String timer = mSettingsManager.getValue(SettingsManager.KEY_TIMER); - - int seconds = Integer.parseInt(timer); - // When shutter button is pressed, check whether the previous countdown is - // finished. If not, cancel the previous countdown and start a new one. - if (mUI.isCountingDown()) { - mUI.cancelCountDown(); - } - if (seconds > 0) { - mUI.startCountDown(seconds, true); + if (mIsRecordingVideo) { + captureVideoSnapshot(getMainCameraId()); } else { - if(mPostProcessor.isFilterOn() && mPostProcessor.isItBusy()) { - warningToast("It's still busy processing previous scene mode request."); - return; + String timer = mSettingsManager.getValue(SettingsManager.KEY_TIMER); + + int seconds = Integer.parseInt(timer); + // When shutter button is pressed, check whether the previous countdown is + // finished. If not, cancel the previous countdown and start a new one. + if (mUI.isCountingDown()) { + mUI.cancelCountDown(); + } + if (seconds > 0) { + mUI.startCountDown(seconds, true); + } else { + if (mPostProcessor.isFilterOn() && mPostProcessor.isItBusy()) { + warningToast("It's still busy processing previous scene mode request."); + return; + } + takePicture(); } - takePicture(); } } @@ -2730,6 +2831,9 @@ public class CaptureModule implements CameraModule, PhotoController, case SettingsManager.KEY_VIDEO_QUALITY: updateVideoSize(); continue; + case SettingsManager.KEY_AUTO_VIDEOSNAP_SIZE: + updateVideoSnapshotSize(); + continue; case SettingsManager.KEY_VIDEO_TIME_LAPSE_FRAME_INTERVAL: updateTimeLapseSetting(); continue; @@ -2871,6 +2975,17 @@ public class CaptureModule implements CameraModule, PhotoController, return optimal; } + private Size getMaxSizeWithRatio(Size[] sizes, Size reference) { + float ratio = (float) reference.getWidth() / reference.getHeight(); + for (Size size: sizes) { + float prevRatio = (float) size.getWidth() / size.getHeight(); + if (Math.abs(prevRatio - ratio) < 0.01) { + return size; + } + } + return sizes[0]; + } + /** * Compares two {@code Size}s based on their areas. */ diff --git a/src/com/android/camera/CaptureUI.java b/src/com/android/camera/CaptureUI.java index c6576d7f4..f5f23fa18 100644 --- a/src/com/android/camera/CaptureUI.java +++ b/src/com/android/camera/CaptureUI.java @@ -112,7 +112,8 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, SettingsManager.KEY_VIDEO_ENCODER, SettingsManager.KEY_AUDIO_ENCODER, SettingsManager.KEY_VIDEO_TIME_LAPSE_FRAME_INTERVAL, - SettingsManager.KEY_VIDEO_ROTATION + SettingsManager.KEY_VIDEO_ROTATION, + SettingsManager.KEY_AUTO_VIDEOSNAP_SIZE }; private CameraActivity mActivity; private View mRootView; @@ -522,7 +523,6 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, mFrontBackSwitcher.setVisibility(View.INVISIBLE); mFilterModeSwitcher.setVisibility(View.INVISIBLE); mSceneModeSwitcher.setVisibility(View.INVISIBLE); - mShutterButton.setVisibility(View.INVISIBLE); } public void showUIafterRecording() { @@ -530,7 +530,6 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, mFrontBackSwitcher.setVisibility(View.VISIBLE); mFilterModeSwitcher.setVisibility(View.VISIBLE); mSceneModeSwitcher.setVisibility(View.VISIBLE); - mShutterButton.setVisibility(View.VISIBLE); } public void hideSwitcher() { diff --git a/src/com/android/camera/SettingsManager.java b/src/com/android/camera/SettingsManager.java index 9b229d97d..5dbda0af3 100644 --- a/src/com/android/camera/SettingsManager.java +++ b/src/com/android/camera/SettingsManager.java @@ -104,6 +104,7 @@ public class SettingsManager implements ListMenu.SettingsListener { public static final String KEY_VIDEO_TIME_LAPSE_FRAME_INTERVAL = "pref_camera2_video_time_lapse_frame_interval_key"; public static final String KEY_FACE_DETECTION = "pref_camera2_facedetection_key"; + public static final String KEY_AUTO_VIDEOSNAP_SIZE = "pref_camera2_videosnap_key"; private static final String TAG = "SnapCam_SettingsManager"; private static SettingsManager sInstance; @@ -727,6 +728,12 @@ public class SettingsManager implements ListMenu.SettingsListener { return res; } + public Size[] getSupportedOutputSize(int cameraId, int format) { + StreamConfigurationMap map = mCharacteristics.get(cameraId).get( + CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); + return map.getOutputSizes(format); + } + public Size[] getSupportedOutputSize(int cameraId, Class cl) { StreamConfigurationMap map = mCharacteristics.get(cameraId).get( CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); -- cgit v1.2.3