From f75ea7ead9b0b13af757951e70b3376b3873cb53 Mon Sep 17 00:00:00 2001 From: Byunghun Jeon Date: Tue, 19 Jul 2016 12:54:50 -0700 Subject: SnapdragonCamera: Add High Framerate Video capture Add high framerate video capture. Only show supported options for current video size. CRs-Fixed: 1045405 Change-Id: Ib0dd7e63c167ead0fd734382d3ad4fdc7a64f8cc --- assets/dependency.json | 7 ++ res/xml/capture_preferences.xml | 9 ++ src/com/android/camera/CaptureModule.java | 135 ++++++++++++++++++++++------ src/com/android/camera/CaptureUI.java | 1 + src/com/android/camera/SettingsManager.java | 102 +++++++++++++++++++++ 5 files changed, 226 insertions(+), 28 deletions(-) diff --git a/assets/dependency.json b/assets/dependency.json index 390adcde4..39f9f91b0 100644 --- a/assets/dependency.json +++ b/assets/dependency.json @@ -53,5 +53,12 @@ "off":{}, "on": {"pref_camera2_mpo_key":"on"} + }, + "pref_camera2_video_time_lapse_frame_interval_key": + { + "default": + {"pref_camera2_hfr_key":"off"} + , + "0":{} } } diff --git a/res/xml/capture_preferences.xml b/res/xml/capture_preferences.xml index 2f0849442..9ffe7d537 100644 --- a/res/xml/capture_preferences.xml +++ b/res/xml/capture_preferences.xml @@ -280,10 +280,19 @@ camera:entryValues="@array/pref_camera2_videosnap_entryvalues" camera:key="pref_camera2_videosnap_key" camera:title="@string/pref_camera2_videosnap_title" /> + + + diff --git a/src/com/android/camera/CaptureModule.java b/src/com/android/camera/CaptureModule.java index 899f40f7d..db0cef352 100644 --- a/src/com/android/camera/CaptureModule.java +++ b/src/com/android/camera/CaptureModule.java @@ -35,6 +35,7 @@ import android.graphics.RectF; import android.hardware.camera2.CameraAccessException; import android.hardware.camera2.CameraCaptureSession; import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CameraConstrainedHighSpeedCaptureSession; import android.hardware.camera2.CameraDevice; import android.hardware.camera2.CameraManager; import android.hardware.camera2.CameraMetadata; @@ -62,6 +63,7 @@ import android.os.Message; import android.os.SystemClock; import android.provider.MediaStore; import android.util.Log; +import android.util.Range; import android.util.Size; import android.view.Display; import android.view.KeyEvent; @@ -270,6 +272,10 @@ public class CaptureModule implements CameraModule, PhotoController, private long mRecordingTotalTime; private boolean mRecordingTimeCountsDown = false; private ImageReader mVideoSnapshotImageReader; + private Range mHighSpeedFPSRange; + private boolean mHighSpeedCapture = false; + private boolean mHighSpeedCaptureSlowMode = false; //HFR + private int mHighSpeedCaptureRate; private class MediaSaveNotifyThread extends Thread { private Uri uri; @@ -2160,35 +2166,78 @@ public class CaptureModule implements CameraModule, PhotoController, mFrameProcessor.setOutputSurface(surface); mFrameProcessor.setVideoOutputSurface(mMediaRecorder.getSurface()); addPreviewSurface(mPreviewBuilder, surfaces, cameraId); - surfaces.add(mVideoSnapshotImageReader.getSurface()); + if (!mHighSpeedCapture) surfaces.add(mVideoSnapshotImageReader.getSurface()); + else mPreviewBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, mHighSpeedFPSRange); - mCameraDevice[cameraId].createCaptureSession(surfaces, new CameraCaptureSession - .StateCallback() { + if (!mHighSpeedCapture) { + mCameraDevice[cameraId].createCaptureSession(surfaces, new CameraCaptureSession + .StateCallback() { - @Override - public void onConfigured(CameraCaptureSession cameraCaptureSession) { - Log.d(TAG, "StartRecordingVideo session onConfigured"); - mCurrentSession = cameraCaptureSession; - try { - setUpVideoCaptureRequestBuilder(mPreviewBuilder); - mCurrentSession.setRepeatingRequest(mPreviewBuilder.build(), null, mCameraHandler); - } catch (CameraAccessException e) { - e.printStackTrace(); + @Override + public void onConfigured(CameraCaptureSession cameraCaptureSession) { + Log.d(TAG, "StartRecordingVideo session onConfigured"); + mCurrentSession = cameraCaptureSession; + try { + setUpVideoCaptureRequestBuilder(mPreviewBuilder); + mCurrentSession.setRepeatingRequest(mPreviewBuilder.build(), null, mCameraHandler); + } catch (CameraAccessException e) { + e.printStackTrace(); + } + mMediaRecorder.start(); + mUI.clearFocus(); + mUI.resetPauseButton(); + mRecordingTotalTime = 0L; + mRecordingStartTime = SystemClock.uptimeMillis(); + mUI.showRecordingUI(true); + updateRecordingTime(); } - mMediaRecorder.start(); - mUI.clearFocus(); - mUI.resetPauseButton(); - mRecordingTotalTime = 0L; - mRecordingStartTime = SystemClock.uptimeMillis(); - mUI.showRecordingUI(true); - updateRecordingTime(); - } - @Override - public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) { - Toast.makeText(mActivity, "Video Failed", Toast.LENGTH_SHORT).show(); - } - }, null); + @Override + public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) { + Toast.makeText(mActivity, "Video Failed", Toast.LENGTH_SHORT).show(); + } + }, null); + } else { + mCameraDevice[cameraId].createConstrainedHighSpeedCaptureSession(surfaces, new + CameraConstrainedHighSpeedCaptureSession.StateCallback() { + + @Override + public void onConfigured(CameraCaptureSession cameraCaptureSession) { + mCurrentSession = cameraCaptureSession; + CameraConstrainedHighSpeedCaptureSession session = + (CameraConstrainedHighSpeedCaptureSession) mCurrentSession; + try { + List list = session + .createHighSpeedRequestList(mPreviewBuilder.build()); + session.setRepeatingBurst(list, null, mCameraHandler); + } catch (CameraAccessException e) { + Log.e(TAG, "Failed to start high speed video recording " + + e.getMessage()); + e.printStackTrace(); + } catch (IllegalArgumentException e) { + Log.e(TAG, "Failed to start high speed video recording " + + e.getMessage()); + e.printStackTrace(); + } catch (IllegalStateException e) { + Log.e(TAG, "Failed to start high speed video recording " + + e.getMessage()); + e.printStackTrace(); + } + mMediaRecorder.start(); + mUI.clearFocus(); + mUI.resetPauseButton(); + mRecordingTotalTime = 0L; + mRecordingStartTime = SystemClock.uptimeMillis(); + mUI.showRecordingUI(true); + updateRecordingTime(); + } + + @Override + public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) { + Toast.makeText(mActivity, "Failed", Toast.LENGTH_SHORT).show(); + } + }, null); + } } catch (CameraAccessException e) { e.printStackTrace(); } catch (IOException e) { @@ -2206,6 +2255,19 @@ public class CaptureModule implements CameraModule, PhotoController, mUI.showTimeLapseUI(mCaptureTimeLapse); } + private void updateHFRSetting() { + String value = mSettingsManager.getValue(SettingsManager.KEY_VIDEO_HIGH_FRAME_RATE); + if (value == null) return; + if (value.equals("off")) { + mHighSpeedCapture = false; + } else { + mHighSpeedCapture = true; + String mode = value.substring(0, 3); + mHighSpeedCaptureSlowMode = mode.equals("hsr"); + mHighSpeedCaptureRate = Integer.parseInt(value.substring(3)); + } + } + private void setUpVideoCaptureRequestBuilder(CaptureRequest.Builder builder) { builder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO); builder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest @@ -2429,8 +2491,11 @@ public class CaptureModule implements CameraModule, PhotoController, Log.d(TAG, "setUpMediaRecorder"); String videoSize = mSettingsManager.getValue(SettingsManager.KEY_VIDEO_QUALITY); int size = CameraSettings.VIDEO_QUALITY_TABLE.get(videoSize); - if (mCaptureTimeLapse) + if (mCaptureTimeLapse) { size = CameraSettings.getTimeLapseQualityFor(size); + } + updateHFRSetting(); + boolean hfr = mHighSpeedCapture && !mHighSpeedCaptureSlowMode; mProfile = CamcorderProfile.get(cameraId, size); int videoEncoder = SettingTranslation @@ -2440,7 +2505,7 @@ public class CaptureModule implements CameraModule, PhotoController, int outputFormat = MediaRecorder.OutputFormat.MPEG_4; - if (!mCaptureTimeLapse) { + if (!mCaptureTimeLapse && !hfr) { mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); } mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE); @@ -2457,7 +2522,7 @@ public class CaptureModule implements CameraModule, PhotoController, mMediaRecorder.setVideoSize(mProfile.videoFrameWidth, mProfile.videoFrameHeight); } mMediaRecorder.setVideoEncoder(videoEncoder); - if (!mCaptureTimeLapse) { + if (!mCaptureTimeLapse && !hfr) { mMediaRecorder.setAudioEncodingBitRate(mProfile.audioBitRate); mMediaRecorder.setAudioChannels(mProfile.audioChannels); mMediaRecorder.setAudioSamplingRate(mProfile.audioSampleRate); @@ -2467,7 +2532,21 @@ public class CaptureModule implements CameraModule, PhotoController, if (mCaptureTimeLapse) { double fps = 1000 / (double) mTimeBetweenTimeLapseFrameCaptureMs; mMediaRecorder.setCaptureRate(fps); + } else if (mHighSpeedCapture) { + mHighSpeedFPSRange = new Range(mHighSpeedCaptureRate, mHighSpeedCaptureRate); + int fps = (int) mHighSpeedFPSRange.getUpper(); + mMediaRecorder.setCaptureRate(fps); + if (mHighSpeedCaptureSlowMode) { + mMediaRecorder.setVideoFrameRate(30); + } else { + mMediaRecorder.setVideoFrameRate(fps); + } + + int scaledBitrate = mProfile.videoBitRate * fps / mProfile.videoFrameRate; + Log.i(TAG, "Scaled Video bitrate : " + scaledBitrate); + mMediaRecorder.setVideoEncodingBitRate(scaledBitrate); } + Location loc = mLocationManager.getCurrentLocation(); if (loc != null) { mMediaRecorder.setLocation((float) loc.getLatitude(), diff --git a/src/com/android/camera/CaptureUI.java b/src/com/android/camera/CaptureUI.java index dadbb3144..47a1a857c 100644 --- a/src/com/android/camera/CaptureUI.java +++ b/src/com/android/camera/CaptureUI.java @@ -111,6 +111,7 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, SettingsManager.KEY_WHITE_BALANCE, SettingsManager.KEY_CAMERA2, SettingsManager.KEY_FACE_DETECTION, + SettingsManager.KEY_VIDEO_HIGH_FRAME_RATE, SettingsManager.KEY_VIDEO_FLASH_MODE, SettingsManager.KEY_VIDEO_DURATION, SettingsManager.KEY_VIDEO_QUALITY, diff --git a/src/com/android/camera/SettingsManager.java b/src/com/android/camera/SettingsManager.java index ae46b9587..a3778b444 100644 --- a/src/com/android/camera/SettingsManager.java +++ b/src/com/android/camera/SettingsManager.java @@ -115,6 +115,7 @@ public class SettingsManager implements ListMenu.SettingsListener { "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"; + public static final String KEY_VIDEO_HIGH_FRAME_RATE = "pref_camera2_hfr_key"; private static final String TAG = "SnapCam_SettingsManager"; private static SettingsManager sInstance; @@ -128,6 +129,7 @@ public class SettingsManager implements ListMenu.SettingsListener { private boolean mIsMonoCameraPresent = false; private boolean mIsFrontCameraPresent = false; private JSONObject mDependency; + private int mCameraId; private SettingsManager(Context context) { mListeners = new ArrayList<>(); @@ -201,6 +203,7 @@ public class SettingsManager implements ListMenu.SettingsListener { @Override public void onSettingChanged(ListPreference pref) { String key = pref.getKey(); + if (pref.getKey().equals(KEY_VIDEO_QUALITY)) buildHFR(); List changed = checkDependencyAndUpdate(key); if (changed == null) return; notifyListeners(changed); @@ -219,6 +222,7 @@ public class SettingsManager implements ListMenu.SettingsListener { private void setLocalIdAndInitialize(int cameraId) { mPreferences.setLocalId(mContext, cameraId); + mCameraId = cameraId; CameraSettings.upgradeLocalPreferences(mPreferences.getLocal()); PreferenceInflater inflater = new PreferenceInflater(mContext); @@ -505,6 +509,7 @@ public class SettingsManager implements ListMenu.SettingsListener { ListPreference faceDetection = mPreferenceGroup.findPreference(KEY_FACE_DETECTION); ListPreference makeup = mPreferenceGroup.findPreference(KEY_MAKEUP); ListPreference trackingfocus = mPreferenceGroup.findPreference(KEY_TRACKINGFOCUS); + ListPreference hfr = mPreferenceGroup.findPreference(KEY_VIDEO_HIGH_FRAME_RATE); if (whiteBalance != null) { CameraSettings.filterUnsupportedOptions(mPreferenceGroup, @@ -591,6 +596,10 @@ public class SettingsManager implements ListMenu.SettingsListener { if (!TrackingFocusFrameListener.isSupportedStatic()) removePreference(mPreferenceGroup, KEY_TRACKINGFOCUS); } + + if (hfr != null) { + buildHFR(); + } } private void buildExposureCompensation(int cameraId) { @@ -653,6 +662,86 @@ public class SettingsManager implements ListMenu.SettingsListener { cameraIdPref.setEntries(entries); } + private void buildHFR() { + ListPreference hfrPref = mPreferenceGroup.findPreference(KEY_VIDEO_HIGH_FRAME_RATE); + + + Size[] highSpeedVideoSize = getSupportedHighSpeedVideoSize(mCameraId); + if (highSpeedVideoSize.length == 0) { + CharSequence[] entryValues = new CharSequence[1]; + CharSequence[] entries = new CharSequence[1]; + entryValues[0] = "off"; + entries[0] = "off"; + hfrPref.setEntryValues(entryValues); + hfrPref.setEntries(entries); + hfrPref.setValueIndex(0); + return; + } + + ListPreference videoQuality = mPreferenceGroup.findPreference(KEY_VIDEO_QUALITY); + String video = videoQuality.getValue(); + int x = video.indexOf('x'); + Size videoSize = new Size(Integer.parseInt(video.substring(0, x)), + Integer.parseInt(video.substring(x + 1))); + + boolean found = false; + for (Size s : highSpeedVideoSize) { + if (videoSize.equals(s)) { + found = true; + break; + } + } + + if (!found) { + CharSequence[] entryValues = new CharSequence[1]; + CharSequence[] entries = new CharSequence[1]; + entryValues[0] = "off"; + entries[0] = "Off"; + hfrPref.setEntryValues(entryValues); + hfrPref.setEntries(entries); + hfrPref.setValueIndex(0); + return; + } + + Range[] range = getSupportedHighSpeedVideoFPSRange(mCameraId, highSpeedVideoSize[0]); + ArrayList list = new ArrayList<>(); + for (Range r : range) { + if (r.getLower() == r.getUpper()) { + list.add(r); + } + } + + if (list.size() == 0) { + CharSequence[] entryValues = new CharSequence[1]; + CharSequence[] entries = new CharSequence[1]; + entryValues[0] = "off"; + entries[0] = "Off"; + hfrPref.setEntryValues(entryValues); + hfrPref.setEntries(entries); + hfrPref.setValueIndex(0); + return; + } + + CharSequence[] entryValues = new CharSequence[list.size() * 2 + 1]; + CharSequence[] entries = new CharSequence[list.size() * 2 + 1]; + entryValues[0] = "off"; + entries[0] = "Off"; + int i = 1; + for (Range r : list) { + entries[i] = "HFR " + r.getLower(); + entryValues[i] = "hfr" + r.getLower(); + i++; + } + for (Range r : list) { + entries[i] = "HSR " + r.getLower(); + entryValues[i] = "hsr" + r.getLower(); + i++; + } + + hfrPref.setEntryValues(entryValues); + hfrPref.setEntries(entries); + } + private boolean removePreference(PreferenceGroup group, String key) { for (int i = 0, n = group.size(); i < n; i++) { CameraPreference child = group.get(i); @@ -816,6 +905,18 @@ public class SettingsManager implements ListMenu.SettingsListener { return res; } + public Size[] getSupportedHighSpeedVideoSize(int cameraId) { + StreamConfigurationMap map = mCharacteristics.get(cameraId).get( + CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); + return map.getHighSpeedVideoSizes(); + } + + public Range[] getSupportedHighSpeedVideoFPSRange(int cameraId, Size videoSize) { + StreamConfigurationMap map = mCharacteristics.get(cameraId).get( + CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); + return map.getHighSpeedVideoFpsRangesFor(videoSize); + } + private List getSupportedRedeyeReduction(int cameraId) { int[] flashModes = mCharacteristics.get(cameraId).get(CameraCharacteristics .CONTROL_AE_AVAILABLE_MODES); @@ -983,6 +1084,7 @@ public class SettingsManager implements ListMenu.SettingsListener { JSONObject dependencyMap = getDependencyMapForKey(key); if (dependencyMap == null) return null; if (!dependencyMap.has(value)) value = "default"; + if (!dependencyMap.has(value)) return null; value = getDependencyKey(dependencyMap, value); try { return dependencyMap.getJSONObject(value); -- cgit v1.2.3