From 0f0b166ecb5b8b45cb494ba2186863e4faefd9ec Mon Sep 17 00:00:00 2001 From: Byunghun Jeon Date: Tue, 10 May 2016 16:59:33 -0700 Subject: SnapdragonCamera: Add video to Camera2 module Add video to Camera2 module and combine the UI Change-Id: I0bcd86eb6b95fe2180856780ddd794682259e799 --- src/com/android/camera/CameraActivity.java | 3 +- src/com/android/camera/CaptureModule.java | 526 ++++++++++++++++++++- src/com/android/camera/CaptureUI.java | 162 ++++++- src/com/android/camera/SettingsManager.java | 94 ++++ src/com/android/camera/ui/CameraControls.java | 10 +- src/com/android/camera/util/CameraUtil.java | 59 +++ .../android/camera/util/SettingTranslation.java | 117 +++++ 7 files changed, 936 insertions(+), 35 deletions(-) create mode 100644 src/com/android/camera/util/SettingTranslation.java (limited to 'src/com/android') diff --git a/src/com/android/camera/CameraActivity.java b/src/com/android/camera/CameraActivity.java index 15d894fa3..268535747 100644 --- a/src/com/android/camera/CameraActivity.java +++ b/src/com/android/camera/CameraActivity.java @@ -794,7 +794,8 @@ public class CameraActivity extends Activity public void updateThumbnail(boolean videoOnly) { // Only handle OnDataInserted if it's video. // Photo and Panorama have their own way of updating thumbnail. - if (!videoOnly || (mCurrentModule instanceof VideoModule)) { + if (!videoOnly || (mCurrentModule instanceof VideoModule) || + ((mCurrentModule instanceof CaptureModule) && videoOnly)) { (new UpdateThumbnailTask(null, true)).execute(); } } diff --git a/src/com/android/camera/CaptureModule.java b/src/com/android/camera/CaptureModule.java index ee4bcc724..336e7b410 100644 --- a/src/com/android/camera/CaptureModule.java +++ b/src/com/android/camera/CaptureModule.java @@ -22,6 +22,7 @@ package com.android.camera; import android.app.ActivityManager; import android.app.AlertDialog; import android.content.ContentResolver; +import android.content.ContentValues; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; @@ -45,15 +46,22 @@ import android.hardware.camera2.TotalCaptureResult; import android.hardware.camera2.params.Face; import android.hardware.camera2.params.MeteringRectangle; import android.hardware.camera2.params.StreamConfigurationMap; +import android.location.Location; +import android.media.AudioManager; +import android.media.CamcorderProfile; import android.media.CameraProfile; import android.media.Image; import android.media.ImageReader; +import android.media.MediaMetadataRetriever; +import android.media.MediaRecorder; import android.net.Uri; import android.os.Debug; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; import android.os.Message; +import android.os.SystemClock; +import android.provider.MediaStore; import android.util.Log; import android.util.Size; import android.util.SparseIntArray; @@ -76,15 +84,20 @@ import com.android.camera.ui.ModuleSwitcher; import com.android.camera.ui.RotateTextToast; import com.android.camera.util.CameraUtil; import com.android.camera.util.PersistUtil; +import com.android.camera.util.SettingTranslation; import com.android.internal.util.MemInfoReader; import org.codeaurora.snapcam.R; import org.codeaurora.snapcam.filter.ClearSightImageProcessor; +import java.io.File; +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; @@ -108,10 +121,6 @@ public class CaptureModule implements CameraModule, PhotoController, private static final int MAX_NUM_CAM = 3; private static final MeteringRectangle[] ZERO_WEIGHT_3A_REGION = new MeteringRectangle[]{ new MeteringRectangle(0, 0, 0, 0, 0)}; - /** - * Conversion from screen rotation to JPEG orientation. - */ - private static final SparseIntArray ORIENTATIONS = new SparseIntArray(); /** * Camera state: Showing camera preview. */ @@ -143,12 +152,6 @@ public class CaptureModule implements CameraModule, PhotoController, // we can change it based on memory status or other requirements. private static final int LONGSHOT_CANCEL_THRESHOLD = 40 * 1024 * 1024; - static { - ORIENTATIONS.append(Surface.ROTATION_0, 90); - ORIENTATIONS.append(Surface.ROTATION_90, 0); - ORIENTATIONS.append(Surface.ROTATION_180, 270); - ORIENTATIONS.append(Surface.ROTATION_270, 180); - } private static final int MAX_IMAGE_NUM = 8; MeteringRectangle[][] mAFRegions = new MeteringRectangle[MAX_NUM_CAM][]; @@ -238,6 +241,28 @@ 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 Size mPreviewSize; + private Size mPictureSize; + private Size mVideoPreviewSize; + private Size mVideoSize; + + private MediaRecorder mMediaRecorder; + private boolean mIsRecordingVideo; + // The video duration limit. 0 means no limit. + private int mMaxVideoDurationInMs; + private boolean mIsMute = false; + // Default 0. If it is larger than 0, the camcorder is in time lapse mode. + private int mTimeBetweenTimeLapseFrameCaptureMs = 0; + private boolean mCaptureTimeLapse = false; + private CamcorderProfile mProfile; + private static final int UPDATE_RECORD_TIME = 5; + private ContentValues mCurrentVideoValues; + private String mVideoFilename; + private boolean mMediaRecorderPausing = false; + private long mRecordingStartTime; + private long mRecordingTotalTime; + private boolean mRecordingTimeCountsDown = false; private class MediaSaveNotifyThread extends Thread { private Uri uri; @@ -274,6 +299,17 @@ public class CaptureModule implements CameraModule, PhotoController, } private MediaSaveNotifyThread mediaSaveNotifyThread; + + private final MediaSaveService.OnMediaSavedListener mOnVideoSavedListener = + new MediaSaveService.OnMediaSavedListener() { + @Override + public void onMediaSaved(Uri uri) { + if (uri != null) { + mActivity.notifyNewMedia(uri); + } + } + }; + private MediaSaveService.OnMediaSavedListener mOnMediaSavedListener = new MediaSaveService.OnMediaSavedListener() { @Override @@ -634,6 +670,7 @@ public class CaptureModule implements CameraModule, PhotoController, } // When the session is ready, we start displaying the preview. mCaptureSession[id] = cameraCaptureSession; + mPreviewSession = cameraCaptureSession; initializePreviewConfiguration(id); try { if (isBackCamera() && getCameraMode() == DUAL_MODE) { @@ -1085,28 +1122,29 @@ public class CaptureModule implements CameraModule, PhotoController, } mCameraId[i] = cameraId; - String pictureSize = mSettingsManager.getValue(SettingsManager - .KEY_PICTURE_SIZE); + if (i == getMainCameraId()) { + String pictureSize = mSettingsManager.getValue(SettingsManager + .KEY_PICTURE_SIZE); - Size size = parsePictureSize(pictureSize); + Size size = parsePictureSize(pictureSize); - if (i == getMainCameraId()) { Point screenSize = new Point(); mActivity.getWindowManager().getDefaultDisplay().getSize(screenSize); Size[] prevSizes = map.getOutputSizes(imageFormat); mFrameProcPreviewOutputSize = getOptimalPreviewSize(size, prevSizes, screenSize.x, screenSize.y); - mUI.setPreviewSize(mFrameProcPreviewOutputSize.getWidth(), mFrameProcPreviewOutputSize.getHeight()); } + if (isClearSightOn()) { if(i == getMainCameraId()) { - ClearSightImageProcessor.getInstance().init(size.getWidth(), size.getHeight(), - mActivity, mOnMediaSavedListener); + ClearSightImageProcessor.getInstance().init(mPictureSize.getWidth(), + mPictureSize.getHeight(), mActivity, mOnMediaSavedListener); ClearSightImageProcessor.getInstance().setCallback(this); } } else { // No Clearsight - mImageReader[i] = ImageReader.newInstance(size.getWidth(), size.getHeight(), imageFormat, MAX_IMAGE_NUM); + mImageReader[i] = ImageReader.newInstance(mPictureSize.getWidth(), + mPictureSize.getHeight(), imageFormat, MAX_IMAGE_NUM); if(mPostProcessor.isFilterOn() && i == getMainCameraId()) { mImageReader[i].setOnImageAvailableListener(mPostProcessor, mImageAvailableHandler); // if(mFrameProcessor.isFrameFilterEnabled()) { @@ -1138,8 +1176,8 @@ public class CaptureModule implements CameraModule, PhotoController, }, mImageAvailableHandler); } } - } + mMediaRecorder = new MediaRecorder(); mAutoFocusRegionSupported = mSettingsManager.isAutoFocusRegionSupported(mCameraIdList); mAutoExposureRegionSupported = mSettingsManager.isAutoExposureRegionSupported(mCameraIdList); } catch (CameraAccessException e) { @@ -1233,6 +1271,10 @@ public class CaptureModule implements CameraModule, PhotoController, mCameraDevice[i] = null; mCameraOpened[i] = false; } + if (null != mMediaRecorder) { + mMediaRecorder.release(); + mMediaRecorder = null; + } } } catch (InterruptedException e) { mCameraOpenCloseLock.release(); @@ -1367,6 +1409,9 @@ public class CaptureModule implements CameraModule, PhotoController, @Override public void onPauseBeforeSuper() { mPaused = true; + if (mIsRecordingVideo) { + stopRecordingVideo(getMainCameraId()); + } } @Override @@ -1412,9 +1457,19 @@ public class CaptureModule implements CameraModule, PhotoController, return PostProcessor.FILTER_NONE; } + private void initializeValues() { + updatePictureSize(); + updateVideoSize(); + updateTimeLapseSetting(); + estimateJpegFileSize(); + updateMaxVideoDuration(); + } + @Override public void onResumeAfterSuper() { Log.d(TAG, "onResume " + getCameraMode()); + initializeValues(); + mUI.setPreviewSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());; mUI.showSurfaceView(); mUI.setSwitcherIndex(); mCameraIdList = new ArrayList<>(); @@ -1470,7 +1525,6 @@ public class CaptureModule implements CameraModule, PhotoController, mActivity.updateStorageSpaceAndHint(); } }); - estimateJpegFileSize(); mUI.enableShutter(true); } @@ -1642,7 +1696,7 @@ public class CaptureModule implements CameraModule, PhotoController, } private boolean isTouchToFocusAllowed() { - if (isTakingPicture() || isSceneModeOn()) return false; + if (isTakingPicture() || mIsRecordingVideo || isSceneModeOn()) return false; return true; } @@ -1685,7 +1739,7 @@ public class CaptureModule implements CameraModule, PhotoController, @Override public void onPreviewUIReady() { - if (mPaused) { + if (mPaused || mIsRecordingVideo) { return; } Log.d(TAG, "onPreviewUIReady"); @@ -1790,6 +1844,411 @@ public class CaptureModule implements CameraModule, PhotoController, } } + private void updatePictureSize() { + String pictureSize = mSettingsManager.getValue(SettingsManager.KEY_PICTURE_SIZE); + mPictureSize = parsePictureSize(pictureSize); + Point screenSize = new Point(); + mActivity.getWindowManager().getDefaultDisplay().getSize(screenSize); + Size[] prevSizes = mSettingsManager.getSupportedOutputSize(getMainCameraId(), + SurfaceHolder.class); + mPreviewSize = getOptimalPreviewSize(mPictureSize, prevSizes, screenSize.x, screenSize.y); + } + + public boolean isRecordingVideo() { + return mIsRecordingVideo; + } + + public void setMute(boolean enable, boolean isValue) { + AudioManager am = (AudioManager) mActivity.getSystemService(Context.AUDIO_SERVICE); + am.setMicrophoneMute(enable); + if (isValue) { + mIsMute = enable; + } + } + + public boolean isAudioMute() { + return mIsMute; + } + + private void updateVideoSize() { + String videoSize = mSettingsManager.getValue(SettingsManager.KEY_VIDEO_QUALITY); + mVideoSize = parsePictureSize(videoSize); + Point screenSize = new Point(); + mActivity.getWindowManager().getDefaultDisplay().getSize(screenSize); + Size[] prevSizes = mSettingsManager.getSupportedOutputSize(getMainCameraId(), + MediaRecorder.class); + mVideoPreviewSize = getOptimalPreviewSize(mVideoSize, prevSizes, screenSize.x, screenSize.y); + } + + private void updateMaxVideoDuration() { + String minutesStr = mSettingsManager.getValue(SettingsManager.KEY_VIDEO_DURATION); + int minutes = Integer.parseInt(minutesStr); + if (minutes == -1) { + // User wants lowest, set 30s */ + mMaxVideoDurationInMs = 30000; + } else { + // 1 minute = 60000ms + mMaxVideoDurationInMs = 60000 * minutes; + } + } + + private void startRecordingVideo(int cameraId) { + if (null == mCameraDevice[cameraId]) { + return; + } + Log.d(TAG, "StartRecordingVideo " + cameraId); + mIsRecordingVideo = true; + mMediaRecorderPausing = false; + mUI.hideUIwhileRecording(); + mUI.clearFocus(); + mCameraHandler.removeMessages(CANCEL_TOUCH_FOCUS, cameraId); + mState[cameraId] = STATE_PREVIEW; + mControlAFMode = CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE; + closePreviewSession(); + boolean changed = mUI.setPreviewSize(mVideoPreviewSize.getWidth(), mVideoPreviewSize + .getHeight()); + if (changed) { + mUI.hideSurfaceView(); + mUI.showSurfaceView(); + } + + try { + setUpMediaRecorder(cameraId); + final CaptureRequest.Builder mPreviewBuilder = mCameraDevice[cameraId] + .createCaptureRequest(CameraDevice.TEMPLATE_RECORD); + List surfaces = new ArrayList<>(); + + Surface previewSurface = getPreviewSurface(cameraId); + surfaces.add(previewSurface); + mPreviewBuilder.addTarget(previewSurface); + surfaces.add(mMediaRecorder.getSurface()); + mPreviewBuilder.addTarget(mMediaRecorder.getSurface()); + + mCameraDevice[cameraId].createCaptureSession(surfaces, new CameraCaptureSession + .StateCallback() { + + @Override + public void onConfigured(CameraCaptureSession cameraCaptureSession) { + Log.d(TAG, "StartRecordingVideo session onConfigured"); + mPreviewSession = cameraCaptureSession; + try { + setUpVideoCaptureRequestBuilder(mPreviewBuilder); + mPreviewSession.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(); + } + + @Override + public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) { + Toast.makeText(mActivity, "Video Failed", Toast.LENGTH_SHORT).show(); + } + }, null); + } catch (CameraAccessException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private void updateTimeLapseSetting() { + String value = mSettingsManager.getValue(SettingsManager + .KEY_VIDEO_TIME_LAPSE_FRAME_INTERVAL); + if (value == null) return; + int time = Integer.parseInt(value); + mTimeBetweenTimeLapseFrameCaptureMs = time; + mCaptureTimeLapse = mTimeBetweenTimeLapseFrameCaptureMs != 0; + mUI.showTimeLapseUI(mCaptureTimeLapse); + } + + private void setUpVideoCaptureRequestBuilder(CaptureRequest.Builder builder) { + builder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO); + builder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest + .CONTROL_AF_MODE_CONTINUOUS_VIDEO); + builder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON); + applyVideoStabilization(builder); + applyNoiseReduction(builder); + applyColorEffect(builder); + applyWhiteBalance(builder); + applyVideoFlash(builder); + } + + private void applyVideoFlash(CaptureRequest.Builder builder) { + String value = mSettingsManager.getValue(SettingsManager.KEY_VIDEO_FLASH_MODE); + if (value == null) return; + + if (value.equals("torch")) { + builder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_TORCH); + } else { + builder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_OFF); + } + } + + private void applyNoiseReduction(CaptureRequest.Builder builder) { + String value = mSettingsManager.getValue(SettingsManager.KEY_NOISE_REDUCTION); + if (value == null) return; + int noiseReduction = SettingTranslation.getNoiseReduction(value); + builder.set(CaptureRequest.NOISE_REDUCTION_MODE, noiseReduction); + } + + private void applyVideoStabilization(CaptureRequest.Builder builder) { + String value = mSettingsManager.getValue(SettingsManager.KEY_DIS); + if (value == null) return; + if (value.equals("enable")) { + builder.set(CaptureRequest.CONTROL_VIDEO_STABILIZATION_MODE, CaptureRequest + .CONTROL_VIDEO_STABILIZATION_MODE_ON); + } else { + builder.set(CaptureRequest.CONTROL_VIDEO_STABILIZATION_MODE, CaptureRequest + .CONTROL_VIDEO_STABILIZATION_MODE_OFF); + } + } + + private long getTimeLapseVideoLength(long deltaMs) { + // For better approximation calculate fractional number of frames captured. + // This will update the video time at a higher resolution. + double numberOfFrames = (double) deltaMs / mTimeBetweenTimeLapseFrameCaptureMs; + return (long) (numberOfFrames / mProfile.videoFrameRate * 1000); + } + + private void updateRecordingTime() { + if (!mIsRecordingVideo) { + return; + } + if (mMediaRecorderPausing) { + return; + } + + long now = SystemClock.uptimeMillis(); + long delta = now - mRecordingStartTime + mRecordingTotalTime; + + // Starting a minute before reaching the max duration + // limit, we'll countdown the remaining time instead. + boolean countdownRemainingTime = (mMaxVideoDurationInMs != 0 + && delta >= mMaxVideoDurationInMs - 60000); + + long deltaAdjusted = delta; + if (countdownRemainingTime) { + deltaAdjusted = Math.max(0, mMaxVideoDurationInMs - deltaAdjusted) + 999; + } + String text; + + long targetNextUpdateDelay; + if (!mCaptureTimeLapse) { + text = CameraUtil.millisecondToTimeString(deltaAdjusted, false); + targetNextUpdateDelay = 1000; + } else { + // The length of time lapse video is different from the length + // of the actual wall clock time elapsed. Display the video length + // only in format hh:mm:ss.dd, where dd are the centi seconds. + text = CameraUtil.millisecondToTimeString(getTimeLapseVideoLength(delta), true); + targetNextUpdateDelay = mTimeBetweenTimeLapseFrameCaptureMs; + } + + mUI.setRecordingTime(text); + + if (mRecordingTimeCountsDown != countdownRemainingTime) { + // Avoid setting the color on every update, do it only + // when it needs changing. + mRecordingTimeCountsDown = countdownRemainingTime; + + int color = mActivity.getResources().getColor(countdownRemainingTime + ? R.color.recording_time_remaining_text + : R.color.recording_time_elapsed_text); + + mUI.setRecordingTimeTextColor(color); + } + + long actualNextUpdateDelay = targetNextUpdateDelay - (delta % targetNextUpdateDelay); + mHandler.sendEmptyMessageDelayed( + UPDATE_RECORD_TIME, actualNextUpdateDelay); + } + + private void pauseVideoRecording() { + Log.v(TAG, "pauseVideoRecording"); + mMediaRecorderPausing = true; + mRecordingTotalTime += SystemClock.uptimeMillis() - mRecordingStartTime; + mMediaRecorder.pause(); + } + + private void resumeVideoRecording() { + Log.v(TAG, "resumeVideoRecording"); + mMediaRecorderPausing = false; + mRecordingStartTime = SystemClock.uptimeMillis(); + updateRecordingTime(); + mMediaRecorder.start(); + } + + public void onButtonPause() { + pauseVideoRecording(); + } + + public void onButtonContinue() { + resumeVideoRecording(); + } + + private void stopRecordingVideo(int cameraId) { + Log.d(TAG, "stopRecordingVideo " + cameraId); + + // Stop recording + closePreviewSession(); + mMediaRecorder.stop(); + mMediaRecorder.reset(); + + saveVideo(); + + mUI.showRecordingUI(false); + mIsRecordingVideo = false; + boolean changed = mUI.setPreviewSize(mPreviewSize.getWidth(), mPreviewSize.getHeight()); + if (changed) { + mUI.hideSurfaceView(); + mUI.showSurfaceView(); + } else { + createSession(cameraId); + } + mUI.showUIafterRecording(); + } + + private void closePreviewSession() { + Log.d(TAG, "closePreviewSession"); + if (mPreviewSession != null) { + mPreviewSession.close(); + mPreviewSession = null; + } + } + + private String createName(long dateTaken) { + Date date = new Date(dateTaken); + SimpleDateFormat dateFormat = new SimpleDateFormat( + mActivity.getString(R.string.video_file_name_format)); + + return dateFormat.format(date); + } + + private String generateVideoFilename(int outputFileFormat) { + long dateTaken = System.currentTimeMillis(); + String title = createName(dateTaken); + String filename = title + CameraUtil.convertOutputFormatToFileExt(outputFileFormat); + String mime = CameraUtil.convertOutputFormatToMimeType(outputFileFormat); + String path; + if (Storage.isSaveSDCard() && SDCard.instance().isWriteable()) { + path = SDCard.instance().getDirectory() + '/' + filename; + } else { + path = Storage.DIRECTORY + '/' + filename; + } + mCurrentVideoValues = new ContentValues(9); + mCurrentVideoValues.put(MediaStore.Video.Media.TITLE, title); + mCurrentVideoValues.put(MediaStore.Video.Media.DISPLAY_NAME, filename); + mCurrentVideoValues.put(MediaStore.Video.Media.DATE_TAKEN, dateTaken); + mCurrentVideoValues.put(MediaStore.MediaColumns.DATE_MODIFIED, dateTaken / 1000); + mCurrentVideoValues.put(MediaStore.Video.Media.MIME_TYPE, mime); + mCurrentVideoValues.put(MediaStore.Video.Media.DATA, path); + mCurrentVideoValues.put(MediaStore.Video.Media.RESOLUTION, + "" + mVideoSize.getWidth() + "x" + mVideoSize.getHeight()); + Location loc = mLocationManager.getCurrentLocation(); + if (loc != null) { + mCurrentVideoValues.put(MediaStore.Video.Media.LATITUDE, loc.getLatitude()); + mCurrentVideoValues.put(MediaStore.Video.Media.LONGITUDE, loc.getLongitude()); + } + mVideoFilename = path; + return path; + } + + private void saveVideo() { + File origFile = new File(mVideoFilename); + if (!origFile.exists() || origFile.length() <= 0) { + Log.e(TAG, "Invalid file"); + mCurrentVideoValues = null; + return; + } + + long duration = 0L; + MediaMetadataRetriever retriever = new MediaMetadataRetriever(); + + try { + retriever.setDataSource(mVideoFilename); + duration = Long.valueOf(retriever.extractMetadata( + MediaMetadataRetriever.METADATA_KEY_DURATION)); + } catch (IllegalArgumentException e) { + Log.e(TAG, "cannot access the file"); + } + retriever.release(); + + mActivity.getMediaSaveService().addVideo(mVideoFilename, + duration, mCurrentVideoValues, + mOnVideoSavedListener, mContentResolver); + mCurrentVideoValues = null; + } + + private void setUpMediaRecorder(int cameraId) throws IOException { + Log.d(TAG, "setUpMediaRecorder"); + String videoSize = mSettingsManager.getValue(SettingsManager.KEY_VIDEO_QUALITY); + int size = CameraSettings.VIDEO_QUALITY_TABLE.get(videoSize); + if (mCaptureTimeLapse) + size = CameraSettings.getTimeLapseQualityFor(size); + mProfile = CamcorderProfile.get(cameraId, size); + + int videoEncoder = SettingTranslation + .getVideoEncoder(mSettingsManager.getValue(SettingsManager.KEY_VIDEO_ENCODER)); + int audioEncoder = SettingTranslation + .getAudioEncoder(mSettingsManager.getValue(SettingsManager.KEY_AUDIO_ENCODER)); + + int outputFormat = MediaRecorder.OutputFormat.MPEG_4; + + if (!mCaptureTimeLapse) { + mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); + } + mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE); + + mMediaRecorder.setOutputFormat(mProfile.fileFormat); + String fileName = generateVideoFilename(outputFormat); + Log.v(TAG, "New video filename: " + fileName); + mMediaRecorder.setOutputFile(fileName); + mMediaRecorder.setVideoEncodingBitRate(mProfile.videoBitRate); + mMediaRecorder.setVideoFrameRate(mProfile.videoFrameRate); + mMediaRecorder.setVideoSize(mProfile.videoFrameWidth, mProfile.videoFrameHeight); + mMediaRecorder.setVideoEncoder(videoEncoder); + if (!mCaptureTimeLapse) { + mMediaRecorder.setAudioEncodingBitRate(mProfile.audioBitRate); + mMediaRecorder.setAudioChannels(mProfile.audioChannels); + mMediaRecorder.setAudioSamplingRate(mProfile.audioSampleRate); + mMediaRecorder.setAudioEncoder(audioEncoder); + } + mMediaRecorder.setMaxDuration(mMaxVideoDurationInMs); + if (mCaptureTimeLapse) { + double fps = 1000 / (double) mTimeBetweenTimeLapseFrameCaptureMs; + mMediaRecorder.setCaptureRate(fps); + } + Location loc = mLocationManager.getCurrentLocation(); + if (loc != null) { + mMediaRecorder.setLocation((float) loc.getLatitude(), + (float) loc.getLongitude()); + } + int rotation = CameraUtil.getJpegRotation(cameraId, mOrientation); + String videoRotation = mSettingsManager.getValue(SettingsManager.KEY_VIDEO_ROTATION); + if (videoRotation != null) { + rotation += Integer.parseInt(videoRotation); + rotation = rotation % 360; + } + mMediaRecorder.setOrientationHint(rotation); + mMediaRecorder.prepare(); + } + + public void onVideoButtonClick() { + if (getCameraMode() == DUAL_MODE) return; + if (mIsRecordingVideo) { + stopRecordingVideo(getMainCameraId()); + } else { + startRecordingVideo(getMainCameraId()); + } + } + @Override public void onShutterButtonClick() { if (mActivity.getStorageSpaceBytes() <= Storage.LOW_STORAGE_THRESHOLD_BYTES) { @@ -2226,13 +2685,25 @@ public class CaptureModule implements CameraModule, PhotoController, case SettingsManager.KEY_JPEG_QUALITY: estimateJpegFileSize(); continue; + case SettingsManager.KEY_VIDEO_DURATION: + updateMaxVideoDuration(); + continue; + case SettingsManager.KEY_VIDEO_QUALITY: + updateVideoSize(); + continue; + case SettingsManager.KEY_VIDEO_TIME_LAPSE_FRAME_INTERVAL: + updateTimeLapseSetting(); + continue; case SettingsManager.KEY_CAMERA2: switchCameraMode(value); return; + case SettingsManager.KEY_PICTURE_SIZE: + updatePictureSize(); + if (count == 0) restart(); + return; case SettingsManager.KEY_CAMERA_ID: case SettingsManager.KEY_MONO_ONLY: case SettingsManager.KEY_CLEARSIGHT: - case SettingsManager.KEY_PICTURE_SIZE: case SettingsManager.KEY_MONO_PREVIEW: if (count == 0) restart(); return; @@ -2414,5 +2885,14 @@ public class CaptureModule implements CameraModule, PhotoController, public MainHandler() { super(Looper.getMainLooper()); } + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case UPDATE_RECORD_TIME: { + updateRecordingTime(); + break; + } + } + } } } diff --git a/src/com/android/camera/CaptureUI.java b/src/com/android/camera/CaptureUI.java index 71d39e067..f10be5703 100644 --- a/src/com/android/camera/CaptureUI.java +++ b/src/com/android/camera/CaptureUI.java @@ -67,7 +67,8 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, CameraManager.CameraFaceDetectionCallback, SettingsManager.Listener, ListMenu.Listener, - ListSubMenu.Listener { + ListSubMenu.Listener, + PauseButton.OnPauseButtonListener { private static final int HIGHLIGHT_COLOR = 0xff33b5e5; private static final String TAG = "SnapCam_CaptureUI"; private static final int SETTING_MENU_NONE = 0; @@ -93,13 +94,22 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, SettingsManager.KEY_EXPOSURE, SettingsManager.KEY_WHITE_BALANCE, SettingsManager.KEY_CAMERA2, - SettingsManager.KEY_MAKEUP + SettingsManager.KEY_MAKEUP, + SettingsManager.KEY_VIDEO_FLASH_MODE, + SettingsManager.KEY_VIDEO_DURATION, + SettingsManager.KEY_VIDEO_QUALITY }; String[] mDeveloperKeys = new String[]{ SettingsManager.KEY_REDEYE_REDUCTION, SettingsManager.KEY_MONO_ONLY, SettingsManager.KEY_CLEARSIGHT, - SettingsManager.KEY_MONO_PREVIEW + SettingsManager.KEY_MONO_PREVIEW, + SettingsManager.KEY_NOISE_REDUCTION, + SettingsManager.KEY_DIS, + SettingsManager.KEY_VIDEO_ENCODER, + SettingsManager.KEY_AUDIO_ENCODER, + SettingsManager.KEY_VIDEO_TIME_LAPSE_FRAME_INTERVAL, + SettingsManager.KEY_VIDEO_ROTATION }; private CameraActivity mActivity; private View mRootView; @@ -148,6 +158,7 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, }; private ShutterButton mShutterButton; + private ImageView mVideoButton; private RenderOverlay mRenderOverlay; private View mMenuButton; private ModuleSwitcher mSwitcher; @@ -165,6 +176,15 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, private View mFilterModeSwitcher; private View mSceneModeSwitcher; private View mFrontBackSwitcher; + private TextView mRecordingTimeView; + private LinearLayout mLabelsLinearLayout; + private View mTimeLapseLabel; + private RotateLayout mRecordingTimeRect; + private PauseButton mPauseButton; + private RotateImageView mMuteButton; + + int mPreviewWidth; + int mPreviewHeight; private SurfaceHolder.Callback callback2 = new SurfaceHolder.Callback() { @@ -224,6 +244,7 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, mRenderOverlay = (RenderOverlay) mRootView.findViewById(R.id.render_overlay); mShutterButton = (ShutterButton) mRootView.findViewById(R.id.shutter_button); + mVideoButton = (ImageView) mRootView.findViewById(R.id.video_button); mFilterModeSwitcher = mRootView.findViewById(R.id.filter_mode_switcher); mSceneModeSwitcher = mRootView.findViewById(R.id.scene_mode_switcher); mFrontBackSwitcher = mRootView.findViewById(R.id.front_back_switcher); @@ -246,6 +267,32 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, }); mMenuButton = mRootView.findViewById(R.id.menu); + mRecordingTimeView = (TextView) mRootView.findViewById(R.id.recording_time); + mRecordingTimeRect = (RotateLayout) mRootView.findViewById(R.id.recording_time_rect); + mTimeLapseLabel = mRootView.findViewById(R.id.time_lapse_label); + mLabelsLinearLayout = (LinearLayout) mRootView.findViewById(R.id.labels); + mPauseButton = (PauseButton) mRootView.findViewById(R.id.video_pause); + mPauseButton.setOnPauseButtonListener(this); + + mMuteButton = (RotateImageView)mRootView.findViewById(R.id.mute_button); + mMuteButton.setVisibility(View.VISIBLE); + if(!mModule.isAudioMute()) { + mMuteButton.setImageResource(R.drawable.ic_unmuted_button); + } else { + mMuteButton.setImageResource(R.drawable.ic_muted_button); + } + mMuteButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + boolean isEnabled = !mModule.isAudioMute(); + mModule.setMute(isEnabled, true); + if (!isEnabled) + mMuteButton.setImageResource(R.drawable.ic_unmuted_button); + else + mMuteButton.setImageResource(R.drawable.ic_muted_button); + } + }); + RotateImageView muteButton = (RotateImageView) mRootView.findViewById(R.id.mute_button); muteButton.setVisibility(View.GONE); @@ -283,6 +330,8 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, mRenderOverlay.requestLayout(); mActivity.setPreviewGestures(mGestures); + ((ViewGroup)mRootView).removeView(mRecordingTimeRect); + mCameraControls.setPreviewRatio(0, true); } public void onCameraOpened(List cameraIds) { @@ -314,6 +363,14 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, }); mShutterButton.setOnShutterButtonListener(mModule); mShutterButton.setVisibility(View.VISIBLE); + mVideoButton.setVisibility(View.VISIBLE); + mVideoButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + mModule.onVideoButtonClick(); + } + }); + mCameraControls.setPreviewRatio(0, true); } public void initializeZoom(List ids) { @@ -421,6 +478,53 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, }); } + public void showTimeLapseUI(boolean enable) { + if (mTimeLapseLabel != null) { + mTimeLapseLabel.setVisibility(enable ? View.VISIBLE : View.GONE); + } + } + + public void showRecordingUI(boolean recording) { + mMenuButton.setVisibility(recording ? View.GONE : View.VISIBLE); + if (recording) { + mVideoButton.setImageResource(R.drawable.shutter_button_video_stop); + hideSwitcher(); + mRecordingTimeView.setText(""); + ((ViewGroup)mRootView).addView(mRecordingTimeRect); + mMuteButton.setVisibility(View.VISIBLE); + } else { + mVideoButton.setImageResource(R.drawable.btn_new_shutter_video); + showSwitcher(); + ((ViewGroup)mRootView).removeView(mRecordingTimeRect); + mMuteButton.setVisibility(View.INVISIBLE); + } + } + + public void hideUIwhileRecording() { + mCameraControls.setWillNotDraw(true); + mFrontBackSwitcher.setVisibility(View.INVISIBLE); + mFilterModeSwitcher.setVisibility(View.INVISIBLE); + mSceneModeSwitcher.setVisibility(View.INVISIBLE); + mShutterButton.setVisibility(View.INVISIBLE); + } + + public void showUIafterRecording() { + mCameraControls.setWillNotDraw(false); + mFrontBackSwitcher.setVisibility(View.VISIBLE); + mFilterModeSwitcher.setVisibility(View.VISIBLE); + mSceneModeSwitcher.setVisibility(View.VISIBLE); + mShutterButton.setVisibility(View.VISIBLE); + } + + public void hideSwitcher() { + mSwitcher.closePopup(); + mSwitcher.setVisibility(View.INVISIBLE); + } + + public void showSwitcher() { + mSwitcher.setVisibility(View.VISIBLE); + } + public void setSwitcherIndex() { mSwitcher.setCurrentIndex(ModuleSwitcher.PHOTO_MODULE_INDEX); } @@ -1015,7 +1119,8 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, mThumbnail.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - if (!CameraControls.isAnimating() && !mModule.isTakingPicture()) + if (!CameraControls.isAnimating() && !mModule.isTakingPicture() && + !mModule.isRecordingVideo()) mActivity.gotoGallery(); } }); @@ -1301,6 +1406,15 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, } } } + if (mRecordingTimeRect != null) { + if (orientation == 180) { + mRecordingTimeRect.setOrientation(0, false); + mRecordingTimeView.setRotation(180); + } else { + mRecordingTimeView.setRotation(0); + mRecordingTimeRect.setOrientation(orientation, false); + } + } if (mCountDownView != null) mCountDownView.setOrientation(orientation); RotateTextToast.setOrientation(orientation); @@ -1364,6 +1478,34 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, mShutterButton.setPressed(true); } + public void setRecordingTime(String text) { + mRecordingTimeView.setText(text); + } + + public void setRecordingTimeTextColor(int color) { + mRecordingTimeView.setTextColor(color); + } + + public void resetPauseButton() { + mRecordingTimeView.setCompoundDrawablesWithIntrinsicBounds( + R.drawable.ic_recording_indicator, 0, 0, 0); + mPauseButton.setPaused(false); + } + + @Override + public void onButtonPause() { + mRecordingTimeView.setCompoundDrawablesWithIntrinsicBounds( + R.drawable.ic_pausing_indicator, 0, 0, 0); + mModule.onButtonPause(); + } + + @Override + public void onButtonContinue() { + mRecordingTimeView.setCompoundDrawablesWithIntrinsicBounds( + R.drawable.ic_recording_indicator, 0, 0, 0); + mModule.onButtonContinue(); + } + @Override public void onSettingsChanged(List settings) { for (SettingsManager.SettingState setting : settings) { @@ -1385,6 +1527,8 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, } public void showSurfaceView() { + mSurfaceView.getHolder().setFixedSize(mPreviewWidth, mPreviewHeight); + mSurfaceView.setAspectRatio(mPreviewHeight, mPreviewWidth); mSurfaceView.setVisibility(View.VISIBLE); mSurfaceView2.setVisibility(View.VISIBLE); } @@ -1397,10 +1541,12 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, } } - public void setPreviewSize(int width, int height) { - mSurfaceView.getHolder().setFixedSize(width, height); - mCameraControls.setPreviewRatio(0, true); - mSurfaceView.setAspectRatio(height, width); + public boolean setPreviewSize(int width, int height) { + Log.d(TAG, "setPreviewSize " + width + " " + height); + boolean changed = (width != mPreviewWidth) || (height != mPreviewHeight); + mPreviewWidth = width; + mPreviewHeight = height; + return changed; } @Override diff --git a/src/com/android/camera/SettingsManager.java b/src/com/android/camera/SettingsManager.java index a5b72488c..dbffb8401 100644 --- a/src/com/android/camera/SettingsManager.java +++ b/src/com/android/camera/SettingsManager.java @@ -38,6 +38,7 @@ import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CameraManager; import android.hardware.camera2.CameraMetadata; import android.hardware.camera2.params.StreamConfigurationMap; +import android.media.MediaRecorder; import android.util.Log; import android.util.Range; import android.util.Rational; @@ -45,6 +46,7 @@ import android.util.Size; import com.android.camera.imageprocessor.filter.OptizoomFilter; import com.android.camera.ui.ListMenu; +import com.android.camera.util.SettingTranslation; import org.codeaurora.snapcam.R; @@ -87,6 +89,16 @@ public class SettingsManager implements ListMenu.SettingsListener { public static final String KEY_EXPOSURE = "pref_camera2_exposure_key"; public static final String KEY_TIMER = "pref_camera2_timer_key"; public static final String KEY_LONGSHOT = "pref_camera2_longshot_key"; + public static final String KEY_VIDEO_DURATION = "pref_camera2_video_duration_key"; + public static final String KEY_VIDEO_QUALITY = "pref_camera2_video_quality_key"; + public static final String KEY_VIDEO_ENCODER = "pref_camera2_videoencoder_key"; + public static final String KEY_AUDIO_ENCODER = "pref_camera2_audioencoder_key"; + public static final String KEY_DIS = "pref_camera2_dis_key"; + public static final String KEY_NOISE_REDUCTION = "pref_camera2_noise_reduction_key"; + public static final String KEY_VIDEO_FLASH_MODE = "pref_camera2_video_flashmode_key"; + public static final String KEY_VIDEO_ROTATION = "pref_camera2_video_rotation_key"; + public static final String KEY_VIDEO_TIME_LAPSE_FRAME_INTERVAL = + "pref_camera2_video_time_lapse_frame_interval_key"; private static final String TAG = "SnapCam_SettingsManager"; private static SettingsManager sInstance; @@ -502,6 +514,11 @@ public class SettingsManager implements ListMenu.SettingsListener { ListPreference monoPreview = mPreferenceGroup.findPreference(KEY_MONO_PREVIEW); ListPreference monoOnly = mPreferenceGroup.findPreference(KEY_MONO_ONLY); ListPreference redeyeReduction = mPreferenceGroup.findPreference(KEY_REDEYE_REDUCTION); + ListPreference videoQuality = mPreferenceGroup.findPreference(KEY_VIDEO_QUALITY); + ListPreference videoEncoder = mPreferenceGroup.findPreference(KEY_VIDEO_ENCODER); + ListPreference audioEncoder = mPreferenceGroup.findPreference(KEY_AUDIO_ENCODER); + ListPreference noiseReduction = mPreferenceGroup.findPreference(KEY_NOISE_REDUCTION); + ListPreference videoFlash = mPreferenceGroup.findPreference(KEY_VIDEO_FLASH_MODE); if (whiteBalance != null) { CameraSettings.filterUnsupportedOptions(mPreferenceGroup, @@ -537,6 +554,11 @@ public class SettingsManager implements ListMenu.SettingsListener { iso, getSupportedIso(cameraId)); } + if (iso != null) { + CameraSettings.filterUnsupportedOptions(mPreferenceGroup, + videoQuality, getSupportedVideoSize(cameraId)); + } + if (!mIsMonoCameraPresent) { if (clearsight != null) removePreference(mPreferenceGroup, KEY_CLEARSIGHT); if (monoPreview != null) removePreference(mPreferenceGroup, KEY_MONO_PREVIEW); @@ -547,6 +569,26 @@ public class SettingsManager implements ListMenu.SettingsListener { CameraSettings.filterUnsupportedOptions(mPreferenceGroup, redeyeReduction, getSupportedRedeyeReduction(cameraId)); } + + if (videoEncoder != null) { + CameraSettings.filterUnsupportedOptions(mPreferenceGroup, videoEncoder, + getSupportedVideoEncoders(videoEncoder.getEntryValues())); + } + + if (audioEncoder != null) { + CameraSettings.filterUnsupportedOptions(mPreferenceGroup, audioEncoder, + getSupportedAudioEncoders(audioEncoder.getEntryValues())); + } + + if (noiseReduction != null) { + CameraSettings.filterUnsupportedOptions(mPreferenceGroup, noiseReduction, + getSupportedNoiseReductionModes(cameraId)); + } + + if (videoFlash != null) { + if (!isFlashAvailable(cameraId)) + removePreference(mPreferenceGroup, KEY_VIDEO_FLASH_MODE); + } } private void buildExposureCompensation(int cameraId) { @@ -721,6 +763,23 @@ public class SettingsManager implements ListMenu.SettingsListener { return res; } + public Size[] getSupportedOutputSize(int cameraId, Class cl) { + StreamConfigurationMap map = mCharacteristics.get(cameraId).get( + CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); + return map.getOutputSizes(cl); + } + + private List getSupportedVideoSize(int cameraId) { + StreamConfigurationMap map = mCharacteristics.get(cameraId).get( + CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); + Size[] sizes = map.getOutputSizes(MediaRecorder.class); + List res = new ArrayList<>(); + for (int i = 0; i < sizes.length; i++) { + res.add(sizes[i].toString()); + } + return res; + } + private List getSupportedRedeyeReduction(int cameraId) { int[] flashModes = mCharacteristics.get(cameraId).get(CameraCharacteristics .CONTROL_AE_AVAILABLE_MODES); @@ -768,6 +827,10 @@ public class SettingsManager implements ListMenu.SettingsListener { return modes; } + private boolean isFlashAvailable(int cameraId) { + return mCharacteristics.get(cameraId).get(CameraCharacteristics.FLASH_INFO_AVAILABLE); + } + public List getSupportedColorEffects(int cameraId) { int[] flashModes = mCharacteristics.get(cameraId).get(CameraCharacteristics .CONTROL_AVAILABLE_EFFECTS); @@ -794,6 +857,37 @@ public class SettingsManager implements ListMenu.SettingsListener { return supportedIso; } + private static List getSupportedVideoEncoders(CharSequence[] strings) { + ArrayList supported = new ArrayList<>(); + for (CharSequence cs: strings) { + String s = cs.toString(); + int value = SettingTranslation.getVideoEncoder(s.toString()); + if (value != SettingTranslation.NOT_FOUND) supported.add(s.toString()); + } + return supported; + } + + private static List getSupportedAudioEncoders(CharSequence[] strings) { + ArrayList supported = new ArrayList<>(); + for (CharSequence cs: strings) { + String s = cs.toString(); + int value = SettingTranslation.getAudioEncoder(s); + if (value != SettingTranslation.NOT_FOUND) supported.add(s); + } + return supported; + } + + public List getSupportedNoiseReductionModes(int cameraId) { + int[] noiseReduction = mCharacteristics.get(cameraId).get(CameraCharacteristics + .NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES); + List modes = new ArrayList<>(); + for (int mode : noiseReduction) { + String str = SettingTranslation.getNoiseReduction(mode); + if (str != null) modes.add(str); + } + return modes; + } + private boolean specialDepedency(String key) { return key.equals(KEY_SCENE_MODE); } diff --git a/src/com/android/camera/ui/CameraControls.java b/src/com/android/camera/ui/CameraControls.java index d7dd713ef..980bff2ab 100644 --- a/src/com/android/camera/ui/CameraControls.java +++ b/src/com/android/camera/ui/CameraControls.java @@ -49,6 +49,7 @@ public class CameraControls extends RotatableLayout { private View mBackgroundView; private View mShutter; + private View mVideoShutter; private View mSwitcher; private View mMenu; private View mFrontBackSwitcher; @@ -69,6 +70,9 @@ public class CameraControls extends RotatableLayout { private static final int ANIME_DURATION = 300; private boolean mFilterModeEnabled; + private float[][] mLocX = new float[4][11]; + private float[][] mLocY = new float[4][11]; + private boolean[] mTempEnabled = new boolean[11]; private LinearLayout mRemainingPhotos; private TextView mRemainingPhotosText; private int mOrientation; @@ -155,6 +159,7 @@ public class CameraControls extends RotatableLayout { public void enableTouch(boolean enable) { if (enable) { mShutter.setPressed(false); + mVideoShutter.setPressed(false); mSwitcher.setPressed(false); mMenu.setPressed(false); mFrontBackSwitcher.setPressed(false); @@ -165,6 +170,7 @@ public class CameraControls extends RotatableLayout { mFilterModeEnabled = mFilterModeSwitcher.isEnabled(); } ((ShutterButton) mShutter).enableTouch(enable); + mVideoShutter.setClickable(enable); ((ModuleSwitcher) mSwitcher).enableTouch(enable); mMenu.setEnabled(enable); mFrontBackSwitcher.setEnabled(enable); @@ -189,6 +195,7 @@ public class CameraControls extends RotatableLayout { mBackgroundView = findViewById(R.id.blocker); mSwitcher = findViewById(R.id.camera_switcher); mShutter = findViewById(R.id.shutter_button); + mVideoShutter = findViewById(R.id.video_button); mFrontBackSwitcher = findViewById(R.id.front_back_switcher); mHdrSwitcher = findViewById(R.id.hdr_switcher); mMenu = findViewById(R.id.menu); @@ -485,9 +492,6 @@ public class CameraControls extends RotatableLayout { mFrontBackSwitcher.animate().setListener(inlistener); boolean isYTranslation = rotation == 0 || rotation == 180; animateViews(0, isYTranslation); - /*if (mRemainingPhotos.getVisibility() == View.INVISIBLE) { - mRemainingPhotos.setVisibility(View.VISIBLE); - }*/ mRefocusToast.setVisibility(View.GONE); } diff --git a/src/com/android/camera/util/CameraUtil.java b/src/com/android/camera/util/CameraUtil.java index 9b728b642..b89e13675 100644 --- a/src/com/android/camera/util/CameraUtil.java +++ b/src/com/android/camera/util/CameraUtil.java @@ -36,6 +36,7 @@ import android.hardware.Camera.CameraInfo; import android.hardware.Camera.Parameters; import android.hardware.Camera.Size; import android.location.Location; +import android.media.MediaRecorder; import android.net.Uri; import android.os.Handler; import android.os.ParcelFileDescriptor; @@ -1223,4 +1224,62 @@ public class CameraUtil { return retRatio; } + public static String millisecondToTimeString(long milliSeconds, boolean displayCentiSeconds) { + long seconds = milliSeconds / 1000; // round down to compute seconds + long minutes = seconds / 60; + long hours = minutes / 60; + long remainderMinutes = minutes - (hours * 60); + long remainderSeconds = seconds - (minutes * 60); + + StringBuilder timeStringBuilder = new StringBuilder(); + + // Hours + if (hours > 0) { + if (hours < 10) { + timeStringBuilder.append('0'); + } + timeStringBuilder.append(hours); + + timeStringBuilder.append(':'); + } + + // Minutes + if (remainderMinutes < 10) { + timeStringBuilder.append('0'); + } + timeStringBuilder.append(remainderMinutes); + timeStringBuilder.append(':'); + + // Seconds + if (remainderSeconds < 10) { + timeStringBuilder.append('0'); + } + timeStringBuilder.append(remainderSeconds); + + // Centi seconds + if (displayCentiSeconds) { + timeStringBuilder.append('.'); + long remainderCentiSeconds = (milliSeconds - seconds * 1000) / 10; + if (remainderCentiSeconds < 10) { + timeStringBuilder.append('0'); + } + timeStringBuilder.append(remainderCentiSeconds); + } + + return timeStringBuilder.toString(); + } + + public static String convertOutputFormatToMimeType(int outputFileFormat) { + if (outputFileFormat == MediaRecorder.OutputFormat.MPEG_4) { + return "video/mp4"; + } + return "video/3gpp"; + } + + public static String convertOutputFormatToFileExt(int outputFileFormat) { + if (outputFileFormat == MediaRecorder.OutputFormat.MPEG_4) { + return ".mp4"; + } + return ".3gp"; + } } diff --git a/src/com/android/camera/util/SettingTranslation.java b/src/com/android/camera/util/SettingTranslation.java new file mode 100644 index 000000000..6aee0ede0 --- /dev/null +++ b/src/com/android/camera/util/SettingTranslation.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.android.camera.util; + +import android.hardware.camera2.CameraMetadata; +import android.media.MediaRecorder; + +import java.util.HashMap; +import java.util.Map; + +public class SettingTranslation { + public static final int NOT_FOUND = -1; + private static final TwoWayMap VIDEO_ENCODER_TABLE = new TwoWayMap(); + private static final TwoWayMap AUDIO_ENCODER_TABLE = new TwoWayMap(); + private static final TwoWayMap NOISE_REDUCTION_TABLE = new TwoWayMap(); + + static { + VIDEO_ENCODER_TABLE.put("default", MediaRecorder.VideoEncoder.DEFAULT); + VIDEO_ENCODER_TABLE.put("h263", MediaRecorder.VideoEncoder.H263); + VIDEO_ENCODER_TABLE.put("h264", MediaRecorder.VideoEncoder.H264); + int h265 = ApiHelper.getIntFieldIfExists(MediaRecorder.VideoEncoder.class, + "HEVC", null, MediaRecorder.VideoEncoder.DEFAULT); + if (h265 == MediaRecorder.VideoEncoder.DEFAULT) { + h265 = ApiHelper.getIntFieldIfExists(MediaRecorder.VideoEncoder.class, + "H265", null, MediaRecorder.VideoEncoder.DEFAULT); + } + VIDEO_ENCODER_TABLE.put("h265", h265); + VIDEO_ENCODER_TABLE.put("mpeg-4-sp", MediaRecorder.VideoEncoder.MPEG_4_SP); + VIDEO_ENCODER_TABLE.put("vp8", MediaRecorder.VideoEncoder.VP8); + + AUDIO_ENCODER_TABLE.put("aac", MediaRecorder.AudioEncoder.AAC); + AUDIO_ENCODER_TABLE.put("aac-eld", MediaRecorder.AudioEncoder.AAC_ELD); + AUDIO_ENCODER_TABLE.put("amr-nb", MediaRecorder.AudioEncoder.AMR_NB); + AUDIO_ENCODER_TABLE.put("amr-wb", MediaRecorder.AudioEncoder.AMR_WB); + AUDIO_ENCODER_TABLE.put("default", MediaRecorder.AudioEncoder.DEFAULT); + AUDIO_ENCODER_TABLE.put("he-aac", MediaRecorder.AudioEncoder.HE_AAC); + AUDIO_ENCODER_TABLE.put("vorbis", MediaRecorder.AudioEncoder.VORBIS); + + NOISE_REDUCTION_TABLE.put("off", CameraMetadata.NOISE_REDUCTION_MODE_OFF); + NOISE_REDUCTION_TABLE.put("fast", CameraMetadata.NOISE_REDUCTION_MODE_FAST); + NOISE_REDUCTION_TABLE.put("high-quality", CameraMetadata + .NOISE_REDUCTION_MODE_HIGH_QUALITY); + NOISE_REDUCTION_TABLE.put("minimal", CameraMetadata.NOISE_REDUCTION_MODE_MINIMAL); + NOISE_REDUCTION_TABLE.put("zero-shutter-lag", CameraMetadata + .NOISE_REDUCTION_MODE_ZERO_SHUTTER_LAG); + } + + public static int getVideoEncoder(String key) { + return VIDEO_ENCODER_TABLE.get(key); + } + + public static String getVideoEncoder(int key) { + return VIDEO_ENCODER_TABLE.get(key); + } + + public static int getAudioEncoder(String key) { + return AUDIO_ENCODER_TABLE.get(key); + } + + public static String getAudioEncoder(int key) { + return AUDIO_ENCODER_TABLE.get(key); + } + + public static int getNoiseReduction(String key) { + return NOISE_REDUCTION_TABLE.get(key); + } + + public static String getNoiseReduction(int key) { + return NOISE_REDUCTION_TABLE.get(key); + } + + private static class TwoWayMap { + private Map strToInt = new HashMap<>(); + private Map intToStr = new HashMap<>(); + + public void put(String key, int value) { + strToInt.put(key, value); + intToStr.put(value, key); + } + + public int get(String key) { + Integer res = strToInt.get(key); + if (res != null) return res; + else return NOT_FOUND; + } + + public String get(int key) { + return intToStr.get(key); + } + } +} -- cgit v1.2.3