diff --git a/AndroidManifest.xml b/AndroidManifest.xml old mode 100755 new mode 100644 index 6ec0df351..c2f80cda9 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -63,6 +63,7 @@ - - - { - private final byte[] mJpegData; - private final boolean mCheckOrientation; + private byte[] mJpegData; + private boolean mCheckOrientation; public UpdateThumbnailTask(final byte[] jpegData, boolean checkOrientation) { mJpegData = jpegData; @@ -800,6 +810,17 @@ protected void onPostExecute(Bitmap bitmap) { } else { updateThumbnail(bitmap); } + + mJpegData = null; + } + + @Override + protected void onCancelled(Bitmap bitmap) { + if(bitmap != null) + bitmap.recycle(); + + bitmap = null; + mJpegData = null; } private Bitmap decodeImageCenter(final String path) { @@ -1413,8 +1434,6 @@ public void onCreate(Bundle state) { mSecureCamera = intent.getBooleanExtra(SECURE_CAMERA_EXTRA, false); } - mCursor = getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, null, null, null, null); - if (mSecureCamera) { // Change the window flags so that secure camera can show when locked Window win = getWindow(); @@ -1435,7 +1454,7 @@ public void onCreate(Bundle state) { getWindow().requestFeature(Window.FEATURE_ACTION_BAR); - SettingsManager.createInstance(this); + mSettingsManager = new SettingsManager(this); LayoutInflater inflater = getLayoutInflater(); View rootLayout = inflater.inflate(R.layout.camera, null, false); @@ -1472,14 +1491,14 @@ public void onCreate(Bundle state) { } } - boolean cam2on = SettingsManager.getInstance().isCamera2On(); + boolean cam2on = mSettingsManager.isCamera2On(); if (cam2on && moduleIndex == ModuleSwitcher.PHOTO_MODULE_INDEX) moduleIndex = ModuleSwitcher.CAPTURE_MODULE_INDEX; mOrientationListener = new MyOrientationEventListener(this); + setModuleFromIndex(moduleIndex); setContentView(R.layout.camera_filmstrip); mFilmStripView = (FilmStripView) findViewById(R.id.filmstrip_view); - setModuleFromIndex(moduleIndex); mActionBar = getActionBar(); mActionBar.addOnMenuVisibilityListener(this); @@ -1560,6 +1579,8 @@ public void onClick(View view) { mLocalImagesObserver = new LocalMediaObserver(); mLocalVideosObserver = new LocalMediaObserver(); + mCursor = getContentResolver().query( + MediaStore.Images.Media.EXTERNAL_CONTENT_URI, null, null, null, null); getContentResolver().registerContentObserver( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, true, mLocalImagesObserver); @@ -1674,26 +1695,29 @@ public void onWindowFocusChanged(boolean focus) { private boolean checkPermissions() { boolean requestPermission = false; - if (checkSelfPermission(Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED && - checkSelfPermission(Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED && - checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { + if (checkSelfPermission(Manifest.permission.CAMERA) == + PackageManager.PERMISSION_GRANTED && + checkSelfPermission(Manifest.permission.RECORD_AUDIO) == + PackageManager.PERMISSION_GRANTED && + checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == + PackageManager.PERMISSION_GRANTED && + checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) == + PackageManager.PERMISSION_GRANTED) { mHasCriticalPermissions = true; } else { mHasCriticalPermissions = false; } - if (!mHasCriticalPermissions) { - final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); - boolean isRequestShown = prefs.getBoolean(CameraSettings.KEY_REQUEST_PERMISSION, false); - if(!isRequestShown || !mHasCriticalPermissions) { - Log.v(TAG, "Request permission"); - Intent intent = new Intent(this, PermissionsActivity.class); - startActivity(intent); - SharedPreferences.Editor editor = prefs.edit(); - editor.putBoolean(CameraSettings.KEY_REQUEST_PERMISSION, true); - editor.apply(); - requestPermission = true; - } - } + final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); + boolean isRequestShown = prefs.getBoolean(CameraSettings.KEY_REQUEST_PERMISSION, false); + if(!isRequestShown || !mHasCriticalPermissions) { + Log.v(TAG, "Request permission"); + Intent intent = new Intent(this, PermissionsActivity.class); + startActivity(intent); + SharedPreferences.Editor editor = prefs.edit(); + editor.putBoolean(CameraSettings.KEY_REQUEST_PERMISSION, true); + editor.apply(); + requestPermission = true; + } return requestPermission; } @@ -1770,9 +1794,8 @@ public void onDestroy() { mWakeLock.release(); Log.d(TAG, "wake lock release"); } - SettingsManager settingsMngr = SettingsManager.getInstance(); - if (settingsMngr != null) { - settingsMngr.destroyInstance(); + if (mSettingsManager != null) { + mSettingsManager = null; } if (mCursor != null) { getContentResolver().unregisterContentObserver(mLocalImagesObserver); @@ -1829,16 +1852,21 @@ public void setPreviewGestures(PreviewGestures previewGestures) { mFilmStripView.setPreviewGestures(previewGestures); } - protected void updateStorageSpace() { - mStorageSpaceBytes = Storage.getAvailableSpace(); - if (Storage.switchSavePath()) { + protected long updateStorageSpace() { + synchronized (mStorageSpaceLock) { mStorageSpaceBytes = Storage.getAvailableSpace(); - mCurrentModule.onSwitchSavePath(); + if (Storage.switchSavePath()) { + mStorageSpaceBytes = Storage.getAvailableSpace(); + mCurrentModule.onSwitchSavePath(); + } + return mStorageSpaceBytes; } } protected long getStorageSpaceBytes() { - return mStorageSpaceBytes; + synchronized (mStorageSpaceLock) { + return mStorageSpaceBytes; + } } protected void updateStorageSpaceAndHint() { @@ -1846,6 +1874,31 @@ protected void updateStorageSpaceAndHint() { updateStorageHint(mStorageSpaceBytes); } + protected interface OnStorageUpdateDoneListener { + void onStorageUpdateDone(long storageSpace); + } + + protected void updateStorageSpaceAndHint(final OnStorageUpdateDoneListener callback) { + (new AsyncTask() { + @Override + protected Long doInBackground(Void ... arg) { + return updateStorageSpace(); + } + + @Override + protected void onPostExecute(Long storageSpace) { + updateStorageHint(storageSpace); + // This callback returns after I/O to check disk, so we could be + // pausing and shutting down. If so, don't bother invoking. + if (callback != null && !mPaused) { + callback.onStorageUpdateDone(storageSpace); + } else { + Log.v(TAG, "ignoring storage callback after activity pause"); + } + } + }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + protected void updateStorageHint(long storageSpace) { String message = null; if (storageSpace == Storage.UNAVAILABLE) { @@ -1931,8 +1984,9 @@ public boolean isForceReleaseCamera() { @Override public void onModuleSelected(int moduleIndex) { - boolean cam2on = SettingsManager.getInstance().isCamera2On(); - mForceReleaseCamera = cam2on && moduleIndex == ModuleSwitcher.PHOTO_MODULE_INDEX; + boolean cam2on = mSettingsManager.isCamera2On(); + mForceReleaseCamera = moduleIndex == ModuleSwitcher.CAPTURE_MODULE_INDEX || + (cam2on && moduleIndex == ModuleSwitcher.PHOTO_MODULE_INDEX); if (mForceReleaseCamera) { moduleIndex = ModuleSwitcher.CAPTURE_MODULE_INDEX; } @@ -2241,4 +2295,6 @@ public CameraOpenErrorCallback getCameraOpenErrorCallback() { public CameraModule getCurrentModule() { return mCurrentModule; } + + public SettingsManager getSettingsManager() {return mSettingsManager;} } diff --git a/src/com/android/camera/CaptureModule.java b/src/com/android/camera/CaptureModule.java index 1a7811000..45b47e2d1 100644 --- a/src/com/android/camera/CaptureModule.java +++ b/src/com/android/camera/CaptureModule.java @@ -131,15 +131,15 @@ public class CaptureModule implements CameraModule, PhotoController, /** * Camera state: Waiting for the focus to be locked. */ - private static final int STATE_WAITING_LOCK = 1; + private static final int STATE_WAITING_AF_LOCK = 1; /** * Camera state: Waiting for the exposure to be precapture state. */ private static final int STATE_WAITING_PRECAPTURE = 2; /** - * Camera state: Waiting for the exposure state to be something other than precapture. + * Camera state: Waiting for the exposure state to be locked. */ - private static final int STATE_WAITING_NON_PRECAPTURE = 3; + private static final int STATE_WAITING_AE_LOCK = 3; /** * Camera state: Picture was taken. */ @@ -170,12 +170,17 @@ public class CaptureModule implements CameraModule, PhotoController, CaptureRequest.Key BayerMonoLinkSessionIdKey = new CaptureRequest.Key<>("org.codeaurora.qcamera3.dualcam_link_meta_data" + ".related_camera_id", Integer.class); + CaptureRequest.Key CdsModeKey = + new CaptureRequest.Key<>("org.codeaurora.qcamera3.CDS.cds_mode", Integer.class); public static CaptureRequest.Key JpegCropEnableKey = new CaptureRequest.Key<>("org.codeaurora.qcamera3.jpeg_encode_crop.enable", Byte.class); public static CaptureRequest.Key JpegCropRectKey = new CaptureRequest.Key<>("org.codeaurora.qcamera3.jpeg_encode_crop.rect", int[].class); + public static CaptureRequest.Key JpegRoiRectKey = + new CaptureRequest.Key<>("org.codeaurora.qcamera3.jpeg_encode_crop.roi", + int[].class); public static CameraCharacteristics.Key MetaDataMonoOnlyKey = new CameraCharacteristics.Key<>("org.codeaurora.qcamera3.sensor_meta_data.is_mono_only", Byte.class); @@ -254,6 +259,8 @@ public class CaptureModule implements CameraModule, PhotoController, private Size mVideoPreviewSize; private Size mVideoSize; private Size mVideoSnapshotSize; + private Size mPictureThumbSize; + private Size mVideoSnapshotThumbSize; private MediaRecorder mMediaRecorder; private boolean mIsRecordingVideo; @@ -357,7 +364,6 @@ public void onMediaSaved(Uri uri) { if (uri != null) { mActivity.notifyNewMedia(uri); } - if (mLastJpegData != null) mActivity.updateThumbnail(mLastJpegData); } } }; @@ -482,7 +488,7 @@ public void onOpened(CameraDevice cameraDevice) { mCameraOpened[id] = true; if (isBackCamera() && getCameraMode() == DUAL_MODE && id == BAYER_ID) { - Message msg = mCameraHandler.obtainMessage(OPEN_CAMERA, MONO_ID); + Message msg = mCameraHandler.obtainMessage(OPEN_CAMERA, MONO_ID, 0); mCameraHandler.sendMessage(msg); } else { mCamerasOpened = true; @@ -535,10 +541,10 @@ private void updateCaptureStateMachine(int id, CaptureResult result) { case STATE_PREVIEW: { break; } - case STATE_WAITING_LOCK: { + case STATE_WAITING_AF_LOCK: { Integer afState = result.get(CaptureResult.CONTROL_AF_STATE); Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE); - Log.d(TAG, "STATE_WAITING_LOCK id: " + id + " afState:" + afState + " aeState:" + aeState); + Log.d(TAG, "STATE_WAITING_AF_LOCK id: " + id + " afState:" + afState + " aeState:" + aeState); // AF_PASSIVE is added for continous auto focus mode if (CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED == afState || CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED == afState || @@ -546,38 +552,56 @@ private void updateCaptureStateMachine(int id, CaptureResult result) { CaptureResult.CONTROL_AF_STATE_PASSIVE_UNFOCUSED == afState || (mLockRequestHashCode[id] == result.getRequest().hashCode() && afState == CaptureResult.CONTROL_AF_STATE_INACTIVE)) { - // CONTROL_AE_STATE can be null on some devices - if (aeState == null || (aeState == CaptureResult - .CONTROL_AE_STATE_CONVERGED) && isFlashOff(id)) { - checkAfAeStatesAndCapture(id); + if(id == MONO_ID && getCameraMode() == DUAL_MODE && isBackCamera()) { + // in dual mode, mono AE dictated by bayer AE. + // if not already locked, wait for lock update from bayer + if(aeState == CaptureResult.CONTROL_AE_STATE_LOCKED) + checkAfAeStatesAndCapture(id); + else + mState[id] = STATE_WAITING_AE_LOCK; } else { runPrecaptureSequence(id); + // CONTROL_AE_STATE can be null on some devices + if(aeState == null || (aeState == CaptureResult + .CONTROL_AE_STATE_CONVERGED) && isFlashOff(id)) { + lockExposure(id); + } else { + runPrecaptureSequence(id); + } } } break; } case STATE_WAITING_PRECAPTURE: { // CONTROL_AE_STATE can be null on some devices + Integer afState = result.get(CaptureResult.CONTROL_AF_STATE); Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE); - Log.d(TAG, "STATE_WAITING_PRECAPTURE id: " + id + " aeState:" + aeState); + Log.d(TAG, "STATE_WAITING_PRECAPTURE id: " + id + " afState: " + afState + " aeState:" + aeState); if (aeState == null || aeState == CaptureResult.CONTROL_AE_STATE_PRECAPTURE || aeState == CaptureResult.CONTROL_AE_STATE_FLASH_REQUIRED || aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED) { if (mPrecaptureRequestHashCode[id] == result.getRequest().hashCode()) - mState[id] = STATE_WAITING_NON_PRECAPTURE; + lockExposure(id); } break; } - case STATE_WAITING_NON_PRECAPTURE: { + case STATE_WAITING_AE_LOCK: { // CONTROL_AE_STATE can be null on some devices + Integer afState = result.get(CaptureResult.CONTROL_AF_STATE); Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE); - Log.d(TAG, "STATE_WAITING_NON_PRECAPTURE id: " + id + " aeState:" + aeState); - if (aeState == null || aeState != CaptureResult.CONTROL_AE_STATE_PRECAPTURE) { + Log.d(TAG, "STATE_WAITING_AE_LOCK id: " + id + " afState: " + afState + " aeState:" + aeState); + if (aeState == null || aeState == CaptureResult.CONTROL_AE_STATE_LOCKED) { checkAfAeStatesAndCapture(id); } break; } + case STATE_AF_AE_LOCKED: { + Integer afState = result.get(CaptureResult.CONTROL_AF_STATE); + Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE); + Log.d(TAG, "STATE_AF_AE_LOCKED id: " + id + " afState:" + afState + " aeState:" + aeState); + break; + } case STATE_WAITING_TOUCH_FOCUS: break; } @@ -586,6 +610,16 @@ private void updateCaptureStateMachine(int id, CaptureResult result) { private void checkAfAeStatesAndCapture(int id) { if(isBackCamera() && getCameraMode() == DUAL_MODE) { mState[id] = STATE_AF_AE_LOCKED; + try { + // stop repeating request once we have AF/AE lock + // for mono when mono preview is off. + if(id == MONO_ID && !canStartMonoPreview()) { + mCaptureSession[id].stopRepeating(); + } + } catch (CameraAccessException e) { + e.printStackTrace(); + } + if(mState[BAYER_ID] == STATE_AF_AE_LOCKED && mState[MONO_ID] == STATE_AF_AE_LOCKED) { mState[BAYER_ID] = STATE_PICTURE_TAKEN; @@ -902,7 +936,7 @@ public boolean getRecordLocation() { @Override public void init(CameraActivity activity, View parent) { mActivity = activity; - mSettingsManager = SettingsManager.getInstance(); + mSettingsManager = activity.getSettingsManager(); mSettingsManager.registerListener(this); mSettingsManager.init(); mFirstPreviewLoaded = false; @@ -965,10 +999,21 @@ private void lockFocus(int id) { } Log.d(TAG, "lockFocus " + id); + try { + // start repeating request to get AF/AE state updates + // for mono when mono preview is off. + if(id == MONO_ID && !canStartMonoPreview()) { + mCaptureSession[id].setRepeatingRequest(mPreviewRequestBuilder[id] + .build(), mCaptureCallback, mCameraHandler); + } + } catch (CameraAccessException e) { + e.printStackTrace(); + } + mTakingPicture[id] = true; if (mState[id] == STATE_WAITING_TOUCH_FOCUS) { - mCameraHandler.removeMessages(CANCEL_TOUCH_FOCUS, id); - mState[id] = STATE_WAITING_LOCK; + mCameraHandler.removeMessages(CANCEL_TOUCH_FOCUS, mCameraId[id]); + mState[id] = STATE_WAITING_AF_LOCK; return; } @@ -981,7 +1026,7 @@ private void lockFocus(int id) { applySettingsForLockFocus(builder, id); CaptureRequest request = builder.build(); mLockRequestHashCode[id] = request.hashCode(); - mState[id] = STATE_WAITING_LOCK; + mState[id] = STATE_WAITING_AF_LOCK; mCaptureSession[id].capture(request, mCaptureCallback, mCameraHandler); } catch (CameraAccessException e) { e.printStackTrace(); @@ -1005,7 +1050,7 @@ private void autoFocusTrigger(int id) { mState[id] = STATE_WAITING_TOUCH_FOCUS; mCaptureSession[id].capture(builder.build(), mCaptureCallback, mCameraHandler); setAFModeToPreview(id, mControlAFMode); - Message message = mCameraHandler.obtainMessage(CANCEL_TOUCH_FOCUS, id); + Message message = mCameraHandler.obtainMessage(CANCEL_TOUCH_FOCUS, mCameraId[id]); mCameraHandler.sendMessageDelayed(message, CANCEL_TOUCH_FOCUS_DELAY); } catch (CameraAccessException e) { e.printStackTrace(); @@ -1076,11 +1121,24 @@ public void onCaptureSequenceCompleted(CameraCaptureSession session, int captureBuilder = mCameraDevice[id].createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); } + Location location = mLocationManager.getCurrentLocation(); + if(location != null) { + Log.d(TAG, "captureStillPicture gps: " + location.toString()); + // workaround for Google bug. Need to convert timestamp from ms -> sec + location.setTime(location.getTime()/1000); + captureBuilder.set(CaptureRequest.JPEG_GPS_LOCATION, location); + } else { + Log.d(TAG, "captureStillPicture no location - getRecordLocation: " + getRecordLocation()); + } captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, CameraUtil.getJpegRotation(id, mOrientation)); + captureBuilder.set(CaptureRequest.JPEG_THUMBNAIL_SIZE, mPictureThumbSize); + captureBuilder.set(CaptureRequest.JPEG_THUMBNAIL_QUALITY, (byte)80); captureBuilder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO); addPreviewSurface(captureBuilder, null, id); captureBuilder.set(CaptureRequest.CONTROL_AF_MODE, mControlAFMode); captureBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_IDLE); + captureBuilder.set(CdsModeKey, 2); // CDS 0-OFF, 1-ON, 2-AUTO + applySettingsForLockExposure(captureBuilder, id); applySettingsForCapture(captureBuilder, id); if(csEnabled) { @@ -1196,6 +1254,8 @@ private void captureVideoSnapshot(final int id) { mCameraDevice[id].createCaptureRequest(CameraDevice.TEMPLATE_VIDEO_SNAPSHOT); captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, CameraUtil.getJpegRotation(id, mOrientation)); + captureBuilder.set(CaptureRequest.JPEG_THUMBNAIL_SIZE, mVideoSnapshotThumbSize); + captureBuilder.set(CaptureRequest.JPEG_THUMBNAIL_QUALITY, (byte)80); applyVideoSnapshot(captureBuilder, id); captureBuilder.addTarget(mVideoSnapshotImageReader.getSurface()); @@ -1234,7 +1294,7 @@ public void onCaptureSequenceCompleted(CameraCaptureSession session, int * we get a response in {@link #mCaptureCallback} from {@link #lockFocus()}. */ private void runPrecaptureSequence(int id) { - Log.d(TAG, "runPrecaptureSequence"); + Log.d(TAG, "runPrecaptureSequence: " + id); try { CaptureRequest.Builder builder = mCameraDevice[id].createCaptureRequest(CameraDevice .TEMPLATE_PREVIEW); @@ -1285,7 +1345,7 @@ private void setUpCameraOutputs(int imageFormat) { if (isClearSightOn()) { if(i == getMainCameraId()) { - ClearSightImageProcessor.getInstance().init(mPictureSize.getWidth(), + ClearSightImageProcessor.getInstance().init(map, mPictureSize.getWidth(), mPictureSize.getHeight(), mActivity, mOnMediaSavedListener); ClearSightImageProcessor.getInstance().setCallback(this); } @@ -1314,7 +1374,6 @@ public void onImageAvailable(ImageReader reader) { long date = (name == null) ? -1 : name.date; byte[] bytes = getJpegData(image); - mLastJpegData = bytes; ExifInterface exif = Exif.getExif(bytes); int orientation = Exif.getOrientation(exif); @@ -1322,6 +1381,12 @@ public void onImageAvailable(ImageReader reader) { mActivity.getMediaSaveService().addImage(bytes, title, date, null, image.getWidth(), image.getHeight(), orientation, null, mOnMediaSavedListener, mContentResolver, "jpeg"); + + if(mLongshotActive) { + mLastJpegData = bytes; + } else { + mActivity.updateThumbnail(bytes); + } image.close(); } } @@ -1356,7 +1421,6 @@ public void onImageAvailable(ImageReader reader) { ByteBuffer buffer = image.getPlanes()[0].getBuffer(); byte[] bytes = new byte[buffer.remaining()]; - mLastJpegData = bytes; buffer.get(bytes); ExifInterface exif = Exif.getExif(bytes); @@ -1365,6 +1429,8 @@ public void onImageAvailable(ImageReader reader) { mActivity.getMediaSaveService().addImage(bytes, title, date, null, image.getWidth(), image.getHeight(), orientation, null, mOnMediaSavedListener, mContentResolver, "jpeg"); + + mActivity.updateThumbnail(bytes); image.close(); } }, mImageAvailableHandler); @@ -1385,23 +1451,27 @@ private void unlockFocus(int id) { applySettingsForUnlockFocus(builder, id); mCaptureSession[id].capture(builder.build(), mCaptureCallback, mCameraHandler); mState[id] = STATE_PREVIEW; + if (id == getMainCameraId()) { + mActivity.runOnUiThread(new Runnable() { + @Override + public void run() { + mUI.clearFocus(); + } + }); + } mControlAFMode = CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE; + applySettingsForUnlockExposure(mPreviewRequestBuilder[id], id); setAFModeToPreview(id, mControlAFMode); - if(id == MONO_ID && !canStartMonoPreview()) { - mCaptureSession[id].capture(mPreviewRequestBuilder[id] - .build(), mCaptureCallback, mCameraHandler); - } else { - mCaptureSession[id].setRepeatingRequest(mPreviewRequestBuilder[id].build(), - mCaptureCallback, mCameraHandler); - } mTakingPicture[id] = false; - mActivity.runOnUiThread(new Runnable() { - @Override - public void run() { - mUI.stopSelfieFlash(); - mUI.enableShutter(true); - } - }); + if (id == getMainCameraId()) { + mActivity.runOnUiThread(new Runnable() { + @Override + public void run() { + mUI.stopSelfieFlash(); + mUI.enableShutter(true); + } + }); + } } catch (CameraAccessException e) { e.printStackTrace(); } @@ -1427,51 +1497,44 @@ private void closeCamera() { mFrameProcessor.onClose(); } - for (int i = 0; i < MAX_NUM_CAM; i++) { - if (null != mCaptureSession[i]) { - if (mIsLinked && mCamerasOpened) { - unLinkBayerMono(i); - try { - mCaptureSession[i].capture(mPreviewRequestBuilder[i].build(), null, - mCameraHandler); - } catch (CameraAccessException e) { - e.printStackTrace(); - } - } - mCaptureSession[i].close(); - mCaptureSession[i] = null; - } - - if (null != mImageReader[i]) { - mImageReader[i].close(); - mImageReader[i] = 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 as we don't have to protect the case where camera open() gets called during camera close(). The low level framework/HAL handles the synchronization for open() happens after close() */ - mIsLinked = false; try { - mCameraOpenCloseLock.acquire(); - for (int i = 0; i < MAX_NUM_CAM; i++) { + // Close camera starting with AUX first + for (int i = MAX_NUM_CAM-1; i >= 0; i--) { if (null != mCameraDevice[i]) { + if (!mCameraOpenCloseLock.tryAcquire(2000, TimeUnit.MILLISECONDS)) { + Log.d(TAG, "Time out waiting to lock camera closing."); + throw new RuntimeException("Time out waiting to lock camera closing"); + } + Log.d(TAG, "Closing camera: " + mCameraDevice[i].getId()); mCameraDevice[i].close(); mCameraDevice[i] = null; mCameraOpened[i] = false; - } - if (null != mMediaRecorder) { - mMediaRecorder.release(); - mMediaRecorder = null; + mCaptureSession[i] = null; } - if (null != mVideoSnapshotImageReader) { - mVideoSnapshotImageReader.close(); - mVideoSnapshotImageReader = null; + if (null != mImageReader[i]) { + mImageReader[i].close(); + mImageReader[i] = null; } } + + mIsLinked = false; + + if (null != mMediaRecorder) { + mMediaRecorder.release(); + mMediaRecorder = null; + } + + if (null != mVideoSnapshotImageReader) { + mVideoSnapshotImageReader.close(); + mVideoSnapshotImageReader = null; + } } catch (InterruptedException e) { mCameraOpenCloseLock.release(); throw new RuntimeException("Interrupted while trying to lock camera closing.", e); @@ -1480,6 +1543,21 @@ happens after close() */ } } + /** + * Lock the exposure for capture + */ + private void lockExposure(int id) { + Log.d(TAG, "lockExposure: " + id); + try { + applySettingsForLockExposure(mPreviewRequestBuilder[id], id); + mState[id] = STATE_WAITING_AE_LOCK; + mCaptureSession[id].setRepeatingRequest(mPreviewRequestBuilder[id].build(), + mCaptureCallback, mCameraHandler); + } catch (CameraAccessException e) { + e.printStackTrace(); + } + } + private void applySettingsForLockFocus(CaptureRequest.Builder builder, int id) { builder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_START); applyAFRegions(builder, id); @@ -1501,6 +1579,14 @@ private void applySettingsForPrecapture(CaptureRequest.Builder builder, int id) applyFaceDetect(builder, id); } + private void applySettingsForLockExposure(CaptureRequest.Builder builder, int id) { + builder.set(CaptureRequest.CONTROL_AE_LOCK, Boolean.TRUE); + } + + private void applySettingsForUnlockExposure(CaptureRequest.Builder builder, int id) { + builder.set(CaptureRequest.CONTROL_AE_LOCK, Boolean.FALSE); + } + private void applySettingsForUnlockFocus(CaptureRequest.Builder builder, int id) { builder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_CANCEL); @@ -1650,6 +1736,7 @@ public void onPauseAfterSuper() { mUI.hideSurfaceView(); mFirstPreviewLoaded = false; stopBackgroundThread(); + mLastJpegData = null; } @Override @@ -1795,17 +1882,16 @@ public void onResumeAfterSuper() { switch (getCameraMode()) { case DUAL_MODE: case BAYER_MODE: - msg.obj = BAYER_ID; + msg.arg1 = BAYER_ID; mCameraHandler.sendMessage(msg); break; case MONO_MODE: - msg.what = OPEN_CAMERA; - msg.obj = MONO_ID; + msg.arg1 = MONO_ID; mCameraHandler.sendMessage(msg); break; } } else { - msg.obj = FRONT_ID; + msg.arg1 = FRONT_ID; mCameraHandler.sendMessage(msg); } if (!mFirstTimeInitialized) { @@ -2173,6 +2259,8 @@ private void updatePictureSize() { Size[] prevSizes = mSettingsManager.getSupportedOutputSize(getMainCameraId(), SurfaceHolder.class); mPreviewSize = getOptimalPreviewSize(mPictureSize, prevSizes, screenSize.x, screenSize.y); + Size[] thumbSizes = mSettingsManager.getSupportedThumbnailSizes(getMainCameraId()); + mPictureThumbSize = getOptimalPreviewSize(mPictureSize, thumbSizes, 0, 0); // get largest thumb size } public boolean isRecordingVideo() { @@ -2209,6 +2297,8 @@ private void updateVideoSnapshotSize() { } else { mVideoSnapshotSize = mPictureSize; } + Size[] thumbSizes = mSettingsManager.getSupportedThumbnailSizes(getMainCameraId()); + mVideoSnapshotThumbSize = getOptimalPreviewSize(mVideoSnapshotSize, thumbSizes, 0, 0); // get largest thumb size } private void updateMaxVideoDuration() { @@ -2232,7 +2322,7 @@ private void startRecordingVideo(int cameraId) { mMediaRecorderPausing = false; mUI.hideUIwhileRecording(); mUI.clearFocus(); - mCameraHandler.removeMessages(CANCEL_TOUCH_FOCUS, cameraId); + mCameraHandler.removeMessages(CANCEL_TOUCH_FOCUS, mCameraId[cameraId]); mState[cameraId] = STATE_PREVIEW; mControlAFMode = CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE; closePreviewSession(); @@ -3063,10 +3153,19 @@ public void triggerFocusAtPoint(float x, float y, int id) { int height = p.y; mAFRegions[id] = afaeRectangle(x, y, width, height, 1f, mCropRegion[id]); mAERegions[id] = afaeRectangle(x, y, width, height, 1.5f, mCropRegion[id]); - mCameraHandler.removeMessages(CANCEL_TOUCH_FOCUS, id); + mCameraHandler.removeMessages(CANCEL_TOUCH_FOCUS, mCameraId[id]); autoFocusTrigger(id); } + private void cancelTouchFocus(int id) { + if(mPaused) + return; + + mState[id] = STATE_PREVIEW; + mControlAFMode = CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE; + setAFModeToPreview(id, mControlAFMode); + } + private MeteringRectangle[] afaeRectangle(float x, float y, int width, int height, float multiple, Rect cropRegion) { int side = (int) (Math.max(width, height) / 8 * multiple); @@ -3341,20 +3440,6 @@ public TrackingFocusRenderer getTrackingForcusRenderer() { return mUI.getTrackingFocusRenderer(); } - /** - * Compares two {@code Size}s based on their areas. - */ - static class CompareSizesByArea implements Comparator { - - @Override - public int compare(Size lhs, Size rhs) { - // We cast here to ensure the multiplications won't overflow - return Long.signum((long) lhs.getWidth() * lhs.getHeight() - - (long) rhs.getWidth() * rhs.getHeight()); - } - - } - private class MyCameraHandler extends Handler { public MyCameraHandler(Looper looper) { @@ -3363,15 +3448,13 @@ public MyCameraHandler(Looper looper) { @Override public void handleMessage(Message msg) { - int id = (int) msg.obj; + int id = msg.arg1; switch (msg.what) { case OPEN_CAMERA: openCamera(id); break; case CANCEL_TOUCH_FOCUS: - mState[id] = STATE_PREVIEW; - mControlAFMode = CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE; - setAFModeToPreview(id, mControlAFMode); + cancelTouchFocus(id); break; } } @@ -3424,7 +3507,6 @@ private void saveMpoImage() { byte[] bayerBytes = getJpegData(bayerImage); byte[] monoBytes = getJpegData(monoImage); - mLastJpegData = bayerBytes; ExifInterface exif = Exif.getExif(bayerBytes); int orientation = Exif.getOrientation(exif); @@ -3432,6 +3514,8 @@ private void saveMpoImage() { null, bayerBytes, monoBytes, width, height, title, date, null, orientation, mOnMediaSavedListener, mContentResolver, "jpeg"); + mActivity.updateThumbnail(bayerBytes); + bayerImage.close(); bayerImage = null; monoImage.close(); @@ -3441,8 +3525,16 @@ private void saveMpoImage() { } @Override - public void onClearSightSuccess() { + public void onReleaseShutterLock() { + Log.d(TAG, "onReleaseShutterLock"); + unlockFocus(BAYER_ID); + unlockFocus(MONO_ID); + } + + @Override + public void onClearSightSuccess(byte[] thumbnailBytes) { Log.d(TAG, "onClearSightSuccess"); + if(thumbnailBytes != null) mActivity.updateThumbnail(thumbnailBytes); mActivity.runOnUiThread(new Runnable() { @Override public void run() { @@ -3450,14 +3542,12 @@ public void run() { Toast.LENGTH_SHORT).show(); } }); - - unlockFocus(BAYER_ID); - unlockFocus(MONO_ID); } @Override - public void onClearSightFailure() { + public void onClearSightFailure(byte[] thumbnailBytes) { Log.d(TAG, "onClearSightFailure"); + if(thumbnailBytes != null) mActivity.updateThumbnail(thumbnailBytes); mActivity.runOnUiThread(new Runnable() { @Override public void run() { diff --git a/src/com/android/camera/CaptureUI.java b/src/com/android/camera/CaptureUI.java index cf3ca6d3a..481546438 100644 --- a/src/com/android/camera/CaptureUI.java +++ b/src/com/android/camera/CaptureUI.java @@ -261,7 +261,7 @@ public CaptureUI(CameraActivity activity, CaptureModule module, View parent) { mActivity = activity; mModule = module; mRootView = parent; - mSettingsManager = SettingsManager.getInstance(); + mSettingsManager = activity.getSettingsManager(); mSettingsManager.registerListener(this); mActivity.getLayoutInflater().inflate(R.layout.capture_module, (ViewGroup) mRootView, true); @@ -384,7 +384,6 @@ public void onClick(View v) { mGestures.setRenderOverlay(mRenderOverlay); mRenderOverlay.requestLayout(); - mActivity.setPreviewGestures(mGestures); ((ViewGroup)mRootView).removeView(mRecordingTimeRect); mCameraControls.setPreviewRatio(0, true); } @@ -393,6 +392,7 @@ public void onCameraOpened(List cameraIds) { mGestures.setCaptureUI(this); mGestures.setZoomEnabled(mSettingsManager.isZoomSupported(cameraIds)); initializeZoom(cameraIds); + mActivity.setPreviewGestures(mGestures); } public ViewGroup getSceneAndFilterLayout() { @@ -1430,6 +1430,7 @@ public void onPause() { mMonoDummyOutputAllocation.destroy(); mMonoDummyOutputAllocation = null; } + mSurfaceViewMono.setVisibility(View.GONE); } public boolean collapseCameraControls() { @@ -1491,6 +1492,8 @@ public void onFocusSucceeded(boolean timeout) { @Override public void onFocusFailed(boolean timeOut) { + FocusIndicator indicator = getFocusIndicator(); + if (indicator != null) indicator.showFail(timeOut); } diff --git a/src/com/android/camera/ListPreference.java b/src/com/android/camera/ListPreference.java index 000baf125..722e958b2 100644 --- a/src/com/android/camera/ListPreference.java +++ b/src/com/android/camera/ListPreference.java @@ -171,7 +171,11 @@ public int getCurrentIndex() { } public String getEntry() { - return mEntries[findIndexOfValue(getValue())].toString(); + int index = findIndexOfValue(getValue()); + if(index < 0) { + return findSupportedDefaultValue(); + } + return mEntries[index].toString(); } public String getLabel() { diff --git a/src/com/android/camera/PermissionsActivity.java b/src/com/android/camera/PermissionsActivity.java index 5ebe72075..2bb5f0f7c 100644 --- a/src/com/android/camera/PermissionsActivity.java +++ b/src/com/android/camera/PermissionsActivity.java @@ -23,7 +23,8 @@ public class PermissionsActivity extends Activity { private int mIndexPermissionRequestCamera; private int mIndexPermissionRequestMicrophone; private int mIndexPermissionRequestLocation; - private int mIndexPermissionRequestStorage; + private int mIndexPermissionRequestStorageWrite; + private int mIndexPermissionRequestStorageRead; private boolean mShouldRequestCameraPermission; private boolean mShouldRequestMicrophonePermission; private boolean mShouldRequestLocationPermission; @@ -72,8 +73,10 @@ private void checkPermissions() { } if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) + != PackageManager.PERMISSION_GRANTED || + checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { - mNumPermissionsToRequest++; + mNumPermissionsToRequest = mNumPermissionsToRequest + 2; mShouldRequestStoragePermission = true; } else { mFlagHasStoragePermission = true; @@ -107,12 +110,19 @@ private void buildPermissionsRequest() { permissionsRequestIndex++; } if (mShouldRequestStoragePermission) { - permissionsToRequest[permissionsRequestIndex] = Manifest.permission.WRITE_EXTERNAL_STORAGE; - mIndexPermissionRequestStorage = permissionsRequestIndex; + permissionsToRequest[permissionsRequestIndex] = + Manifest.permission.WRITE_EXTERNAL_STORAGE; + mIndexPermissionRequestStorageWrite = permissionsRequestIndex; + permissionsRequestIndex++; + permissionsToRequest[permissionsRequestIndex] = + Manifest.permission.READ_EXTERNAL_STORAGE; + mIndexPermissionRequestStorageRead = permissionsRequestIndex; permissionsRequestIndex++; + } if (mShouldRequestLocationPermission) { - permissionsToRequest[permissionsRequestIndex] = Manifest.permission.ACCESS_COARSE_LOCATION; + permissionsToRequest[permissionsRequestIndex] = + Manifest.permission.ACCESS_COARSE_LOCATION; mIndexPermissionRequestLocation = permissionsRequestIndex; } requestPermissions(permissionsToRequest, PERMISSION_REQUEST_CODE); @@ -124,7 +134,8 @@ public void onRequestPermissionsResult(int requestCode, if (mShouldRequestCameraPermission) { if ((grantResults.length >= mIndexPermissionRequestCamera + 1) && - (grantResults[mIndexPermissionRequestCamera] == PackageManager.PERMISSION_GRANTED)) { + (grantResults[mIndexPermissionRequestCamera] == + PackageManager.PERMISSION_GRANTED)) { mFlagHasCameraPermission = true; } else { mCriticalPermissionDenied = true; @@ -132,15 +143,19 @@ public void onRequestPermissionsResult(int requestCode, } if (mShouldRequestMicrophonePermission) { if ((grantResults.length >= mIndexPermissionRequestMicrophone + 1) && - (grantResults[mIndexPermissionRequestMicrophone] == PackageManager.PERMISSION_GRANTED)) { + (grantResults[mIndexPermissionRequestMicrophone] == + PackageManager.PERMISSION_GRANTED)) { mFlagHasMicrophonePermission = true; } else { mCriticalPermissionDenied = true; } } if (mShouldRequestStoragePermission) { - if ((grantResults.length >= mIndexPermissionRequestStorage + 1) && - (grantResults[mIndexPermissionRequestStorage] == PackageManager.PERMISSION_GRANTED)) { + if ((grantResults.length >= mIndexPermissionRequestStorageRead + 1) && + (grantResults[mIndexPermissionRequestStorageWrite] == + PackageManager.PERMISSION_GRANTED) && + (grantResults[mIndexPermissionRequestStorageRead] == + PackageManager.PERMISSION_GRANTED)) { mFlagHasStoragePermission = true; } else { mCriticalPermissionDenied = true; @@ -149,14 +164,16 @@ public void onRequestPermissionsResult(int requestCode, if (mShouldRequestLocationPermission) { if ((grantResults.length >= mIndexPermissionRequestLocation + 1) && - (grantResults[mIndexPermissionRequestLocation] == PackageManager.PERMISSION_GRANTED)) { + (grantResults[mIndexPermissionRequestLocation] == + PackageManager.PERMISSION_GRANTED)) { // Do nothing } else { // Do nothing } } - if (mFlagHasCameraPermission && mFlagHasMicrophonePermission && mFlagHasStoragePermission) { + if (mFlagHasCameraPermission && mFlagHasMicrophonePermission && + mFlagHasStoragePermission) { handlePermissionsSuccess(); } else if (mCriticalPermissionDenied) { handlePermissionsFailure(); @@ -165,21 +182,22 @@ public void onRequestPermissionsResult(int requestCode, private void handlePermissionsSuccess() { if (mIntent != null) { - mIsReturnResult = true; - mIntent.setClass(this, CameraActivity.class); - startActivityForResult(mIntent, 1); + mIsReturnResult = true; + mIntent.setClass(this, CameraActivity.class); + mIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT); + startActivity(mIntent); + finish(); } else { - mIsReturnResult = false; - Intent intent = new Intent(this, CameraActivity.class); - startActivity(intent); - finish(); + mIsReturnResult = false; + Intent intent = new Intent(this, CameraActivity.class); + startActivity(intent); + finish(); } } private void handlePermissionsFailure() { new AlertDialog.Builder(this).setTitle(getResources().getString(R.string.camera_error_title)) .setMessage(getResources().getString(R.string.error_permissions)) - .setCancelable(false) .setOnKeyListener(new Dialog.OnKeyListener() { @Override public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) { @@ -198,11 +216,4 @@ public void onClick(DialogInterface dialog, int which) { }) .show(); } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - setResult(resultCode, data); - finish(); - } } diff --git a/src/com/android/camera/PhotoModule.java b/src/com/android/camera/PhotoModule.java old mode 100644 new mode 100755 index 279c903d4..299eac42d --- a/src/com/android/camera/PhotoModule.java +++ b/src/com/android/camera/PhotoModule.java @@ -693,7 +693,6 @@ public void onPreviewUIDestroyed() { } catch (InterruptedException ex) { // ignore } - mCameraDevice.setPreviewDisplay(null); stopPreview(); } @@ -1463,7 +1462,7 @@ public void onPictureTaken(byte [] jpegData, CameraProxy camera) { mUI.setDownFactor(4); } if (mAnimateCapture) { - mUI.animateCapture(jpegData, orientation, mMirror); + mUI.animateCapture(jpegData); } } else { // In long shot mode, we do not want to update the preview thumbnail @@ -1477,14 +1476,22 @@ public void onPictureTaken(byte [] jpegData, CameraProxy camera) { stopPreview(); mJpegImageData = jpegData; if (!mQuickCapture) { - mUI.showCapturedImageForReview(jpegData, orientation, mMirror); + mUI.showCapturedImageForReview(jpegData, orientation, false); } else { onCaptureDone(); } } - if(!mLongshotActive) - mActivity.updateStorageSpaceAndHint(); - mUI.updateRemainingPhotos(--mRemainingPhotos); + if(!mLongshotActive) { + mActivity.updateStorageSpaceAndHint( + new CameraActivity.OnStorageUpdateDoneListener() { + @Override + public void onStorageUpdateDone(long storageSpace) { + mUI.updateRemainingPhotos(--mRemainingPhotos); + } + }); + } else { + mUI.updateRemainingPhotos(--mRemainingPhotos); + } long now = System.currentTimeMillis(); mJpegCallbackFinishTime = now - mJpegPictureCallbackTime; Log.v(TAG, "mJpegCallbackFinishTime = " @@ -1702,6 +1709,12 @@ public void run() { mUI.enableShutter(false); } + if (!isShutterSoundOn()) { + mCameraDevice.enableShutterSound(false); + } else { + mCameraDevice.enableShutterSound(!mRefocus); + } + if (mCameraState == LONGSHOT) { mLongShotCaptureCountLimit = SystemProperties.getInt( "persist.camera.longshot.shotnum", 0); @@ -1718,12 +1731,6 @@ public void run() { new JpegPictureCallback(loc)); } } else { - if (!isShutterSoundOn()) { - mCameraDevice.enableShutterSound(false); - } else { - mCameraDevice.enableShutterSound(!mRefocus); - } - mCameraDevice.takePicture(mHandler, new ShutterCallback(!animateBefore), mRawPictureCallback, mPostViewPictureCallback, @@ -2239,7 +2246,7 @@ public void onShutterButtonFocus(boolean pressed) { if (mCameraState == LONGSHOT) { mLongshotActive = false; mCameraDevice.setLongshot(false); - mUI.animateCapture(mLastJpegData, mLastJpegOrientation, mMirror); + mUI.animateCapture(mLastJpegData); mLastJpegData = null; if (!mFocusManager.isZslEnabled()) { setupPreview(); @@ -2650,6 +2657,12 @@ private void updateFocusManager(PhotoUI mUI) { // we will update focus manager with proper UI. if (mFocusManager != null && mUI != null) { mFocusManager.setPhotoUI(mUI); + + View root = mUI.getRootView(); + // These depend on camera parameters. + int width = root.getWidth(); + int height = root.getHeight(); + mFocusManager.setPreviewSize(width, height); } } @@ -4850,6 +4863,10 @@ public boolean isRefocus() { @Override public void onMakeupLevel(String key, String value) { + if (mCameraDevice == null) { + Log.d(TAG,"MakeupLevel failed CameraDevice not yet initialized"); + return; + } synchronized (mCameraDevice) { onMakeupLevelSync(key, value); } diff --git a/src/com/android/camera/PhotoUI.java b/src/com/android/camera/PhotoUI.java index fd848c623..14c55101c 100644 --- a/src/com/android/camera/PhotoUI.java +++ b/src/com/android/camera/PhotoUI.java @@ -538,7 +538,7 @@ public void onCameraOpened(PreferenceGroup prefGroup, ComboPreferences prefs, mActivity.setPreviewGestures(mGestures); } - public void animateCapture(final byte[] jpegData, int orientation, boolean mirror) { + public void animateCapture(final byte[] jpegData) { // Decode jpeg byte array and then animate the jpeg mActivity.updateThumbnail(jpegData); } @@ -559,15 +559,17 @@ private void openMenu() { } public void initializeControlByIntent() { - mThumbnail = (ImageView) mRootView.findViewById(R.id.preview_thumb); - mThumbnail.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - if (!CameraControls.isAnimating() - && mController.getCameraState() != PhotoController.SNAPSHOT_IN_PROGRESS) - mActivity.gotoGallery(); - } - }); + if (!mActivity.isSecureCamera()) { + mThumbnail = (ImageView) mRootView.findViewById(R.id.preview_thumb); + mThumbnail.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + if (!CameraControls.isAnimating() + && mController.getCameraState() != PhotoController.SNAPSHOT_IN_PROGRESS) + mActivity.gotoGallery(); + } + }); + } mMenuButton = mRootView.findViewById(R.id.menu); mMenuButton.setOnClickListener(new OnClickListener() { @Override @@ -1299,9 +1301,6 @@ public void onStartFaceDetection(int orientation, boolean mirror) { mFaceView.setVisibility(View.VISIBLE); mFaceView.setDisplayOrientation(orientation); mFaceView.setMirror(mirror); - LayoutParams layoutParams = new LayoutParams( - LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); - mFaceView.setLayoutParams(layoutParams); mFaceView.resume(); } diff --git a/src/com/android/camera/SettingsManager.java b/src/com/android/camera/SettingsManager.java index c8f3f8c7b..374c6eb2b 100644 --- a/src/com/android/camera/SettingsManager.java +++ b/src/com/android/camera/SettingsManager.java @@ -120,7 +120,6 @@ public class SettingsManager implements ListMenu.SettingsListener { public static final String KEY_SHUTTER_SOUND = "pref_camera2_shutter_sound_key"; private static final String TAG = "SnapCam_SettingsManager"; - private static SettingsManager sInstance; private ArrayList mCharacteristics; private ArrayList mListeners; private Map mValuesMap; @@ -133,7 +132,7 @@ public class SettingsManager implements ListMenu.SettingsListener { private JSONObject mDependency; private int mCameraId; - private SettingsManager(Context context) { + public SettingsManager(Context context) { mListeners = new ArrayList<>(); mCharacteristics = new ArrayList<>(); mContext = context; @@ -173,23 +172,6 @@ private SettingsManager(Context context) { mDependency = parseJson("dependency.json"); } - public static SettingsManager createInstance(Context context) { - if (sInstance == null) { - sInstance = new SettingsManager(context); - } - return sInstance; - } - - public static SettingsManager getInstance() { - return sInstance; - } - - public void destroyInstance() { - if (sInstance != null) { - sInstance = null; - } - } - public List getDisabledList() { List list = new ArrayList<>(); Set keySet = mValuesMap.keySet(); @@ -875,6 +857,11 @@ private List getSupportedPictureSize(int cameraId) { return res; } + public Size[] getSupportedThumbnailSizes(int cameraId) { + return mCharacteristics.get(cameraId).get( + CameraCharacteristics.JPEG_AVAILABLE_THUMBNAIL_SIZES); + } + public Size[] getSupportedOutputSize(int cameraId, int format) { StreamConfigurationMap map = mCharacteristics.get(cameraId).get( CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); diff --git a/src/com/android/camera/VideoModule.java b/src/com/android/camera/VideoModule.java old mode 100755 new mode 100644 index b0699bb08..fc801aea4 --- a/src/com/android/camera/VideoModule.java +++ b/src/com/android/camera/VideoModule.java @@ -98,6 +98,7 @@ public class VideoModule implements CameraModule, private static final int SHOW_TAP_TO_SNAPSHOT_TOAST = 7; private static final int SWITCH_CAMERA = 8; private static final int SWITCH_CAMERA_START_ANIMATION = 9; + private static final int HANDLE_FLASH_TORCH_DELAY = 10; private static final int SCREEN_DELAY = 2 * 60 * 1000; @@ -415,6 +416,11 @@ public void handleMessage(Message msg) { break; } + case HANDLE_FLASH_TORCH_DELAY: { + forceFlashOff(!mPreviewFocused); + break; + } + default: Log.v(TAG, "Unhandled message: " + msg.what); break; @@ -1218,7 +1224,7 @@ private void startPreview() { setDisplayOrientation(); mCameraDevice.setDisplayOrientation(mCameraDisplayOrientation); - setCameraParameters(); + setCameraParameters(true); try { mCameraDevice.setPreviewDisplay(sh); @@ -1317,6 +1323,7 @@ public void onPauseBeforeSuper() { mHandler.removeMessages(CHECK_DISPLAY_ROTATION); mHandler.removeMessages(SWITCH_CAMERA); mHandler.removeMessages(SWITCH_CAMERA_START_ANIMATION); + mHandler.removeMessages(HANDLE_FLASH_TORCH_DELAY); mPendingSwitchCameraId = -1; mSwitchingCamera = false; mPreferenceRead = false; @@ -1403,6 +1410,7 @@ private void doReturnToCaller(boolean valid) { if (valid) { resultCode = Activity.RESULT_OK; resultIntent.setData(mCurrentVideoUri); + resultIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); } else { resultCode = Activity.RESULT_CANCELED; } @@ -2593,7 +2601,7 @@ private void qcomSetCameraParameters(){ } @SuppressWarnings("deprecation") - private void setCameraParameters() { + private void setCameraParameters(boolean isFlashDelay) { Log.d(TAG,"Preview dimension in App->"+mDesiredPreviewWidth+"X"+mDesiredPreviewHeight); mParameters.setPreviewSize(mDesiredPreviewWidth, mDesiredPreviewHeight); mParameters.set("video-size", mProfile.videoFrameWidth+"x"+mProfile.videoFrameHeight); @@ -2606,7 +2614,11 @@ private void setCameraParameters() { mParameters.setPreviewFrameRate(mProfile.videoFrameRate); } - forceFlashOffIfSupported(!mPreviewFocused); + if (isFlashDelay) { + mHandler.sendEmptyMessageDelayed(HANDLE_FLASH_TORCH_DELAY, 800); + } else { + forceFlashOffIfSupported(!mPreviewFocused); + } videoWidth = mProfile.videoFrameWidth; videoHeight = mProfile.videoFrameHeight; @@ -2781,7 +2793,7 @@ public void onSharedPreferenceChanged() { resizeForPreviewAspectRatio(); startPreview(); // Parameters will be set in startPreview(). } else { - setCameraParameters(); + setCameraParameters(false); } mRestartPreview = false; mUI.updateOnScreenIndicators(mParameters, mPreferences); @@ -2910,7 +2922,7 @@ private void forceFlashOff(boolean forceOff) { @Override public void onPreviewFocusChanged(boolean previewFocused) { mUI.onPreviewFocusChanged(previewFocused); - forceFlashOff(!previewFocused); + mHandler.sendEmptyMessageDelayed(HANDLE_FLASH_TORCH_DELAY, 800); mPreviewFocused = previewFocused; } diff --git a/src/com/android/camera/VideoUI.java b/src/com/android/camera/VideoUI.java index 41b9cfd89..6ee532dbf 100644 --- a/src/com/android/camera/VideoUI.java +++ b/src/com/android/camera/VideoUI.java @@ -295,17 +295,19 @@ public void onClick(View v) { Point size = new Point(); mActivity.getWindowManager().getDefaultDisplay().getSize(size); mScreenRatio = CameraUtil.determineRatio(size.x, size.y); - if (mScreenRatio == CameraUtil.RATIO_16_9) { - int l = size.x > size.y ? size.x : size.y; - int tm = mActivity.getResources().getDimensionPixelSize(R.dimen.preview_top_margin); - int bm = mActivity.getResources().getDimensionPixelSize(R.dimen.preview_bottom_margin); - mTopMargin = l / 4 * tm / (tm + bm); - mBottomMargin = l / 4 - mTopMargin; - } + calculateMargins(size); mCameraControls.setMargins(mTopMargin, mBottomMargin); ((ViewGroup)mRootView).removeView(mRecordingTimeRect); } + private void calculateMargins(Point size) { + int l = size.x > size.y ? size.x : size.y; + int tm = mActivity.getResources().getDimensionPixelSize(R.dimen.preview_top_margin); + int bm = mActivity.getResources().getDimensionPixelSize(R.dimen.preview_bottom_margin); + mTopMargin = l / 4 * tm / (tm + bm); + mBottomMargin = l / 4 - mTopMargin; + } + public void cameraOrientationPreviewResize(boolean orientation){ mPrevOrientationResize = mOrientationResize; mOrientationResize = orientation; @@ -400,6 +402,7 @@ private void layoutPreview(float ratio) { float scaledTextureWidth, scaledTextureHeight; int rotation = CameraUtil.getDisplayRotation(mActivity); + mScreenRatio = CameraUtil.determineRatio(ratio); if (mScreenRatio == CameraUtil.RATIO_16_9 && CameraUtil.determinCloseRatio(ratio) == CameraUtil.RATIO_4_3) { int l = (mTopMargin + mBottomMargin) * 4; @@ -432,6 +435,9 @@ private void layoutPreview(float ratio) { } } else { float width = mMaxPreviewWidth, height = mMaxPreviewHeight; + if (width == 0 || height == 0) return; + if(mScreenRatio == CameraUtil.RATIO_4_3) + height -= (mTopMargin + mBottomMargin); if (mOrientationResize) { scaledTextureWidth = height * mAspectRatio; if (scaledTextureWidth > width) { @@ -671,17 +677,18 @@ private void initializeOverlay() { mGestures.setRenderOverlay(mRenderOverlay); - mThumbnail = (ImageView) mRootView.findViewById(R.id.preview_thumb); - mThumbnail.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - // Do not allow navigation to filmstrip during video recording - if (!mRecordingStarted && !CameraControls.isAnimating()) { - mActivity.gotoGallery(); + if (!mActivity.isSecureCamera()) { + mThumbnail = (ImageView) mRootView.findViewById(R.id.preview_thumb); + mThumbnail.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + // Do not allow navigation to filmstrip during video recording + if (!mRecordingStarted && !CameraControls.isAnimating()) { + mActivity.gotoGallery(); + } } - } - }); - + }); + } } public void setPreviewGesturesVideoUI() { @@ -796,8 +803,11 @@ public boolean sendTouchToPreviewMenu(MotionEvent ev) { } public boolean sendTouchToMenu(MotionEvent ev) { - View v = mMenuLayout.getChildAt(0); - return v.dispatchTouchEvent(ev); + if (mMenuLayout != null) { + View v = mMenuLayout.getChildAt(0); + return v.dispatchTouchEvent(ev); + } + return false; } public void dismissSceneModeMenu() { @@ -1250,9 +1260,6 @@ public void onStartFaceDetection(int orientation, boolean mirror) { mFaceView.setVisibility(View.VISIBLE); mFaceView.setDisplayOrientation(orientation); mFaceView.setMirror(mirror); - LayoutParams layoutParams = new LayoutParams( - LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); - mFaceView.setLayoutParams(layoutParams); mFaceView.resume(); } diff --git a/src/com/android/camera/WideAnglePanoramaUI.java b/src/com/android/camera/WideAnglePanoramaUI.java index 14907e842..46cf173bb 100644 --- a/src/com/android/camera/WideAnglePanoramaUI.java +++ b/src/com/android/camera/WideAnglePanoramaUI.java @@ -122,14 +122,16 @@ public WideAnglePanoramaUI( mSwitcher = (ModuleSwitcher) mRootView.findViewById(R.id.camera_switcher); mSwitcher.setCurrentIndex(ModuleSwitcher.WIDE_ANGLE_PANO_MODULE_INDEX); mSwitcher.setSwitchListener(mActivity); - mThumbnail = (ImageView) mRootView.findViewById(R.id.preview_thumb); - mThumbnail.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - if (!CameraControls.isAnimating()) - mActivity.gotoGallery(); - } - }); + if (!mActivity.isSecureCamera()) { + mThumbnail = (ImageView) mRootView.findViewById(R.id.preview_thumb); + mThumbnail.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + if (!CameraControls.isAnimating()) + mActivity.gotoGallery(); + } + }); + } mSwitcher.setOnClickListener(new View.OnClickListener() { @Override diff --git a/src/com/android/camera/data/CameraDataAdapter.java b/src/com/android/camera/data/CameraDataAdapter.java old mode 100644 new mode 100755 index 4643d03bf..4b810a0fa --- a/src/com/android/camera/data/CameraDataAdapter.java +++ b/src/com/android/camera/data/CameraDataAdapter.java @@ -42,7 +42,6 @@ public class CameraDataAdapter implements LocalDataAdapter { private static final String TAG = "CAM_CameraDataAdapter"; private static final int DEFAULT_DECODE_SIZE = 1600; - private static final String[] CAMERA_PATH = { Storage.DIRECTORY + "/%" ,SDCard.instance().getDirectory() + "/%"}; private LocalDataList mImages; @@ -137,7 +136,7 @@ public void addNewVideo(ContentResolver cr, Uri uri) { Cursor c = cr.query(uri, LocalMediaData.VideoData.QUERY_PROJECTION, MediaStore.Video.Media.DATA + " like ? or " + - MediaStore.Video.Media.DATA + " like ? ", CAMERA_PATH, + MediaStore.Video.Media.DATA + " like ? ", getCameraPath(), LocalMediaData.VideoData.QUERY_ORDER); if (c == null || !c.moveToFirst()) { return; @@ -164,7 +163,7 @@ public void addNewPhoto(ContentResolver cr, Uri uri) { Cursor c = cr.query(uri, LocalMediaData.PhotoData.QUERY_PROJECTION, MediaStore.Images.Media.DATA + " like ? or " + - MediaStore.Images.Media.DATA + " like ? ", CAMERA_PATH, + MediaStore.Images.Media.DATA + " like ? ", getCameraPath(), LocalMediaData.PhotoData.QUERY_ORDER); if (c == null || !c.moveToFirst()) { return; @@ -272,6 +271,12 @@ private void replaceData(LocalDataList list) { } } + private static String[] getCameraPath() { + String[] cameraPath = + {Storage.DIRECTORY + "/%", SDCard.instance().getDirectory() + "/%"}; + return cameraPath; + } + private class QueryTask extends AsyncTask { /** @@ -289,7 +294,7 @@ protected LocalDataList doInBackground(ContentResolver... resolver) { LocalMediaData.PhotoData.CONTENT_URI, LocalMediaData.PhotoData.QUERY_PROJECTION, MediaStore.Images.Media.DATA + " like ? or " + - MediaStore.Images.Media.DATA + " like ? ", CAMERA_PATH, + MediaStore.Images.Media.DATA + " like ? ", getCameraPath(), LocalMediaData.PhotoData.QUERY_ORDER); if (c != null && c.moveToFirst()) { // build up the list. @@ -319,7 +324,7 @@ protected LocalDataList doInBackground(ContentResolver... resolver) { LocalMediaData.VideoData.CONTENT_URI, LocalMediaData.VideoData.QUERY_PROJECTION, MediaStore.Video.Media.DATA + " like ? or " + - MediaStore.Video.Media.DATA + " like ? ", CAMERA_PATH, + MediaStore.Video.Media.DATA + " like ? ", getCameraPath(), LocalMediaData.VideoData.QUERY_ORDER); if (c != null && c.moveToFirst()) { // build up the list. diff --git a/src/com/android/camera/data/LocalMediaData.java b/src/com/android/camera/data/LocalMediaData.java index e77578b16..bc3f09f18 100644 --- a/src/com/android/camera/data/LocalMediaData.java +++ b/src/com/android/camera/data/LocalMediaData.java @@ -206,10 +206,12 @@ protected ImageView fillImageView(Context ctx, ImageView v, LocalDataAdapter adapter) { v.setScaleType(ImageView.ScaleType.FIT_XY); v.setImageDrawable(placeHolder); - - BitmapLoadTask task = getBitmapLoadTask(v, decodeWidth, decodeHeight, - ctx.getContentResolver(), adapter); - task.execute(); +/* + * Remove loading bitmaps for this adapter as we no longer use it. + */ +// BitmapLoadTask task = getBitmapLoadTask(v, decodeWidth, decodeHeight, +// ctx.getContentResolver(), adapter); +// task.execute(); return v; } diff --git a/src/com/android/camera/exif/ExifInterface.java b/src/com/android/camera/exif/ExifInterface.java index 773518821..0f495f227 100644 --- a/src/com/android/camera/exif/ExifInterface.java +++ b/src/com/android/camera/exif/ExifInterface.java @@ -2047,7 +2047,11 @@ public boolean addGpsDateTimeStampTag(long timestamp) { } public boolean addMakeAndModelTag() { - ExifTag t = buildTag(TAG_MAKE, Build.MANUFACTURER); + String maker = Build.MANUFACTURER; + if ( maker.equals("unknown") ) { + maker = "QCOM-AA"; + } + ExifTag t = buildTag(TAG_MAKE, maker); if (t == null) { return false; } diff --git a/src/com/android/camera/util/CameraUtil.java b/src/com/android/camera/util/CameraUtil.java index 172a54582..eb237b8a4 100644 --- a/src/com/android/camera/util/CameraUtil.java +++ b/src/com/android/camera/util/CameraUtil.java @@ -61,12 +61,14 @@ import com.android.camera.CameraSettings; import com.android.camera.ui.RotateTextToast; import com.android.camera.util.IntentHelper; + import org.codeaurora.snapcam.R; import java.io.Closeable; import java.io.IOException; import java.lang.reflect.Method; import java.text.SimpleDateFormat; +import java.util.Comparator; import java.util.Date; import java.util.List; import java.util.Locale; @@ -1227,4 +1229,17 @@ public static String convertOutputFormatToFileExt(int outputFileFormat) { } return ".3gp"; } + + /** + * Compares two {@code Size}s based on their areas. + */ + public static class CompareSizesByArea implements Comparator { + + @Override + public int compare(android.util.Size lhs, android.util.Size rhs) { + // We cast here to ensure the multiplications won't overflow + return Long.signum((long) lhs.getWidth() * lhs.getHeight() - + (long) rhs.getWidth() * rhs.getHeight()); + } + } } diff --git a/src/org/codeaurora/snapcam/filter/ClearSightImageProcessor.java b/src/org/codeaurora/snapcam/filter/ClearSightImageProcessor.java index 3a94baec9..595df8122 100644 --- a/src/org/codeaurora/snapcam/filter/ClearSightImageProcessor.java +++ b/src/org/codeaurora/snapcam/filter/ClearSightImageProcessor.java @@ -34,9 +34,11 @@ import java.nio.ByteBuffer; import java.util.ArrayDeque; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import org.codeaurora.snapcam.filter.ClearSightNativeEngine.CamSystemCalibrationData; +import org.codeaurora.snapcam.filter.ClearSightNativeEngine.ClearsightImage; import android.content.Context; import android.graphics.ImageFormat; @@ -52,6 +54,7 @@ import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.TotalCaptureResult; import android.hardware.camera2.params.InputConfiguration; +import android.hardware.camera2.params.StreamConfigurationMap; import android.media.Image; import android.media.Image.Plane; import android.media.ImageReader; @@ -63,6 +66,8 @@ import android.os.Message; import android.os.SystemProperties; import android.util.Log; +import android.util.SparseLongArray; +import android.util.Size; import android.view.Surface; import com.android.camera.CaptureModule; @@ -73,6 +78,7 @@ import com.android.camera.PhotoModule.NamedImages; import com.android.camera.PhotoModule.NamedImages.NamedEntity; import com.android.camera.Storage; +import com.android.camera.util.CameraUtil; public class ClearSightImageProcessor { private static final String TAG = "ClearSightImageProcessor"; @@ -80,13 +86,26 @@ public class ClearSightImageProcessor { private static final String PERSIST_BURST_COUNT_KEY = "persist.camera.cs.burstcount"; private static final String PERSIST_DUMP_FRAMES_KEY = "persist.camera.cs.dumpframes"; private static final String PERSIST_DUMP_YUV_KEY = "persist.camera.cs.dumpyuv"; + private static final String PERSIST_CS_TIMEOUT_KEY = "persist.camera.cs.timeout"; private static final long DEFAULT_TIMESTAMP_THRESHOLD_MS = 10; - private static final int DEFAULT_IMAGES_TO_BURST = 5; + private static final int DEFAULT_IMAGES_TO_BURST = 4; + private static final int DEFAULT_CS_TIMEOUT_MS = 300; + + private static final long MIN_MONO_AREA = 1900000; // ~1.9 MP + private static final Size[] MONO_SIZES = { + new Size(1600, 1200), // 4:3 + new Size(1920, 1080), // 16:9 + new Size(1400, 1400) // 1:1 + }; private static final int MSG_START_CAPTURE = 0; private static final int MSG_NEW_IMG = 1; - private static final int MSG_NEW_RESULT = 2; + private static final int MSG_NEW_CAPTURE_RESULT = 2; + private static final int MSG_NEW_CAPTURE_FAIL = 3; + private static final int MSG_NEW_REPROC_RESULT = 4; + private static final int MSG_NEW_REPROC_FAIL = 5; + private static final int MSG_END_CAPTURE = 6; private static final int CAM_TYPE_BAYER = 0; private static final int CAM_TYPE_MONO = 1; @@ -101,10 +120,17 @@ public class ClearSightImageProcessor { private ImageReader[] mImageReader = new ImageReader[NUM_CAM]; private ImageReader[] mEncodeImageReader = new ImageReader[NUM_CAM]; private ImageWriter[] mImageWriter = new ImageWriter[NUM_CAM]; + private float mFinalPictureRatio; + private Size mFinalPictureSize; + private Size mFinalMonoSize; private ImageProcessHandler mImageProcessHandler; + private ClearsightRegisterHandler mClearsightRegisterHandler; + private ClearsightProcessHandler mClearsightProcessHandler; private ImageEncodeHandler mImageEncodeHandler; private HandlerThread mImageProcessThread; + private HandlerThread mClearsightRegisterThread; + private HandlerThread mClearsightProcessThread; private HandlerThread mImageEncodeThread; private Callback mCallback; @@ -114,8 +140,11 @@ public class ClearSightImageProcessor { private long mTimestampThresholdNs; private int mNumBurstCount; + private int mNumFrameCount; + private int mCsTimeout; private boolean mDumpImages; private boolean mDumpYUV; + private boolean mIsClosing; private static ClearSightImageProcessor mInstance; @@ -128,11 +157,17 @@ private ClearSightImageProcessor() { mNumBurstCount = SystemProperties.getInt(PERSIST_BURST_COUNT_KEY, DEFAULT_IMAGES_TO_BURST); Log.d(TAG, "mNumBurstCount: " + mNumBurstCount); + mNumFrameCount = mNumBurstCount - 1; + Log.d(TAG, "mNumFrameCount: " + mNumFrameCount); + mDumpImages = SystemProperties.getBoolean(PERSIST_DUMP_FRAMES_KEY, false); Log.d(TAG, "mDumpImages: " + mDumpImages); mDumpYUV = SystemProperties.getBoolean(PERSIST_DUMP_YUV_KEY, false); Log.d(TAG, "mDumpYUV: " + mDumpYUV); + + mCsTimeout = SystemProperties.getInt(PERSIST_CS_TIMEOUT_KEY, DEFAULT_CS_TIMEOUT_MS); + Log.d(TAG, "mCsTimeout: " + mCsTimeout); } public static void createInstance() { @@ -149,32 +184,102 @@ public static ClearSightImageProcessor getInstance() { return mInstance; } - public void init(int width, int height, Context context, OnMediaSavedListener mediaListener) { + public void init(StreamConfigurationMap map, int width, int height, + Context context, OnMediaSavedListener mediaListener) { + Log.d(TAG, "init() start"); + mIsClosing = false; mImageProcessThread = new HandlerThread("CameraImageProcess"); mImageProcessThread.start(); + mClearsightRegisterThread = new HandlerThread("ClearsightRegister"); + mClearsightRegisterThread.start(); + mClearsightProcessThread = new HandlerThread("ClearsightProcess"); + mClearsightProcessThread.start(); mImageEncodeThread = new HandlerThread("CameraImageEncode"); mImageEncodeThread.start(); mImageProcessHandler = new ImageProcessHandler(mImageProcessThread.getLooper()); + mClearsightRegisterHandler = new ClearsightRegisterHandler(mClearsightRegisterThread.getLooper()); + mClearsightProcessHandler = new ClearsightProcessHandler(mClearsightProcessThread.getLooper()); mImageEncodeHandler = new ImageEncodeHandler(mImageEncodeThread.getLooper()); - mImageReader[CAM_TYPE_BAYER] = createImageReader(CAM_TYPE_BAYER, width, height); - mImageReader[CAM_TYPE_MONO] = createImageReader(CAM_TYPE_MONO, width, height); - mEncodeImageReader[CAM_TYPE_BAYER] = createEncodeImageReader(CAM_TYPE_BAYER, width, height); - mEncodeImageReader[CAM_TYPE_MONO] = createEncodeImageReader(CAM_TYPE_MONO, width, height); + mFinalPictureSize = new Size(width, height); + mFinalPictureRatio = (float)width / (float)height; + mFinalMonoSize = getFinalMonoSize(); + Size maxSize = findMaxOutputSize(map); + int maxWidth = maxSize.getWidth(); + int maxHeight = maxSize.getHeight(); + mImageReader[CAM_TYPE_BAYER] = createImageReader(CAM_TYPE_BAYER, maxWidth, maxHeight); + mImageReader[CAM_TYPE_MONO] = createImageReader(CAM_TYPE_MONO, maxWidth, maxHeight); + mEncodeImageReader[CAM_TYPE_BAYER] = createEncodeImageReader(CAM_TYPE_BAYER, maxWidth, maxHeight); + mEncodeImageReader[CAM_TYPE_MONO] = createEncodeImageReader(CAM_TYPE_MONO, maxWidth, maxHeight); mMediaSavedListener = mediaListener; CameraManager cm = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE); try { CameraCharacteristics cc = cm.getCameraCharacteristics("0"); byte[] blob = cc.get(OTP_CALIB_BLOB); - ClearSightNativeEngine.setOtpCalibData(CamSystemCalibrationData.createFromBytes(blob)); + ClearSightNativeEngine.getInstance().init(mNumFrameCount*2, + maxWidth, maxHeight, CamSystemCalibrationData.createFromBytes(blob)); } catch (CameraAccessException e) { e.printStackTrace(); } + Log.d(TAG, "init() done"); } public void close() { + Log.d(TAG, "close() start"); + mIsClosing = true; + // use quit instead of quitSafely + // because we don't want to process any more queued events. + // just clean up and exit. + if(mImageProcessThread != null) { + mImageProcessThread.quit(); + + try { + mImageProcessThread.join(); + mImageProcessThread = null; + mImageProcessHandler = null; + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + if(mClearsightRegisterThread != null) { + mClearsightRegisterThread.quit(); + + try { + mClearsightRegisterThread.join(); + mClearsightRegisterThread = null; + mClearsightRegisterHandler = null; + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + if(mClearsightProcessThread != null) { + mClearsightProcessThread.quit(); + + try { + mClearsightProcessThread.join(); + mClearsightProcessThread = null; + mClearsightProcessHandler = null; + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + if(mImageEncodeThread != null) { + mImageEncodeThread.quit(); + + try { + mImageEncodeThread.join(); + mImageEncodeThread = null; + mImageEncodeHandler = null; + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + for(int i=0; i mBayerFrames = new ArrayDeque( mNumBurstCount); @@ -354,10 +457,16 @@ private class ImageProcessHandler extends Handler { mNumBurstCount); private ArrayDeque mMonoImages = new ArrayDeque( mNumBurstCount); - private ReprocessableImage mClearSightFrame; + + private SparseLongArray mReprocessingFrames = new SparseLongArray(); + private ArrayList mReprocessingRequests = new ArrayList(); + private int mReprocessingPairCount; + private int mReprocessedBayerCount; + private int mReprocessedMonoCount; private NamedEntity mNamedEntity; private int[] mNumImagesToProcess = new int[NUM_CAM]; - private ProcessState mState = ProcessState.IDLE; + private boolean mCaptureDone; + private boolean mHasFailures; ImageProcessHandler(Looper looper) { super(looper); @@ -365,22 +474,93 @@ private class ImageProcessHandler extends Handler { @Override public void handleMessage(Message msg) { + if(isClosing()) return; + switch (msg.what) { case MSG_START_CAPTURE: + mCaptureDone = false; + mHasFailures = false; + mReprocessingPairCount = 0; + mReprocessedBayerCount = 0; + mReprocessedMonoCount = 0; mNumImagesToProcess[msg.arg1] = msg.arg2; - mState = (ProcessState)msg.obj; + mNamedImages.nameNewImage(System.currentTimeMillis()); + mNamedEntity = mNamedImages.getNextNameEntity(); + mClearsightRegisterHandler.obtainMessage(MSG_START_CAPTURE, + 0, 0, mNamedEntity).sendToTarget(); + break; + case MSG_END_CAPTURE: + // TIMED OUT WAITING FOR FRAME + handleTimeout(); break; case MSG_NEW_IMG: - case MSG_NEW_RESULT: - if(mState == ProcessState.CAPTURE) - processNewCaptureEvent(msg); - else - processNewReprocessEvent(msg); + processImg(msg); + break; + case MSG_NEW_CAPTURE_RESULT: + processNewCaptureEvent(msg); + break; + case MSG_NEW_REPROC_RESULT: + processNewReprocessResult(msg); + break; + case MSG_NEW_CAPTURE_FAIL: + processNewCaptureEvent(msg); + break; + case MSG_NEW_REPROC_FAIL: + processNewReprocessFailure(msg); break; } } + private void handleTimeout() { + Log.d(TAG, "handleTimeout"); + releaseBayerFrames(); + releaseMonoFrames(); + mReprocessingFrames.clear(); + mReprocessingRequests.clear(); + + removeMessages(MSG_NEW_CAPTURE_RESULT); + removeMessages(MSG_NEW_CAPTURE_FAIL); + removeMessages(MSG_NEW_REPROC_RESULT); + removeMessages(MSG_NEW_REPROC_FAIL); + removeMessages(MSG_END_CAPTURE); + + // set capture done so that any loose frames coming in will be closed + mCaptureDone = true; + mClearsightRegisterHandler.obtainMessage(MSG_END_CAPTURE, 0, 1).sendToTarget(); + } + + private void kickTimeout() { + removeMessages(MSG_END_CAPTURE); + sendEmptyMessageDelayed(MSG_END_CAPTURE, mCsTimeout); + } + + private void processImg(Message msg) { + Log.d(TAG, "processImg: " + msg.arg1); + Image image = (Image) msg.obj; + if(mReprocessingFrames.size() > 0 + && mReprocessingFrames.indexOfValue(image.getTimestamp()) >= 0) { + // reproc frame + processNewReprocessImage(msg); + } else { + // new capture frame + processNewCaptureEvent(msg); + } + } + private void processNewCaptureEvent(Message msg) { + kickTimeout(); + + // Toss extra frames + if(mCaptureDone) { + Log.d(TAG, "processNewCaptureEvent - captureDone - we already have required frame pairs " + msg.arg1); + if(msg.what == MSG_NEW_IMG) { + Image image = (Image) msg.obj; + Log.d(TAG, "processNewCaptureEvent - captureDone - tossed frame ts: " + image.getTimestamp()); + image.close(); + } + return; + } + ArrayDeque imageQueue; ArrayDeque resultQueue; ArrayDeque frameQueue; @@ -396,19 +576,19 @@ private void processNewCaptureEvent(Message msg) { } if(msg.what == MSG_NEW_IMG) { - Log.d(TAG, "processNewEvent - newImg: " + msg.arg1); + Log.d(TAG, "processNewCaptureEvent - newImg: " + msg.arg1); Image image = (Image) msg.obj; imageQueue.add(image); - } else if(msg.arg2 == 1) { - Log.d(TAG, "processNewEvent - new failed result: " + msg.arg1); + } else if(msg.what == MSG_NEW_CAPTURE_FAIL) { + Log.d(TAG, "processNewCaptureEvent - new failed result: " + msg.arg1); mNumImagesToProcess[msg.arg1]--; } else { - Log.d(TAG, "processNewEvent - newResult: " + msg.arg1); + Log.d(TAG, "processNewCaptureEvent - newResult: " + msg.arg1); TotalCaptureResult result = (TotalCaptureResult) msg.obj; resultQueue.add(result); } - Log.d(TAG, "processNewEvent - cam: " + msg.arg1 + " num imgs: " + Log.d(TAG, "processNewCaptureEvent - cam: " + msg.arg1 + " num imgs: " + imageQueue.size() + " num results: " + resultQueue.size()); if (!imageQueue.isEmpty() && !resultQueue.isEmpty()) { @@ -416,19 +596,20 @@ private void processNewCaptureEvent(Message msg) { TotalCaptureResult headResult = resultQueue.poll(); frameQueue.add(new ReprocessableImage(headImage, headResult)); mNumImagesToProcess[msg.arg1]--; - checkForValidFramePair(); + checkForValidFramePairAndReprocess(); } - Log.d(TAG, "processNewEvent - imagestoprocess[bayer] " + mNumImagesToProcess[CAM_TYPE_BAYER] + + Log.d(TAG, "processNewCaptureEvent - imagestoprocess[bayer] " + mNumImagesToProcess[CAM_TYPE_BAYER] + " imagestoprocess[mono]: " + mNumImagesToProcess[CAM_TYPE_MONO]); - if (mNumImagesToProcess[CAM_TYPE_BAYER] == 0 - && mNumImagesToProcess[CAM_TYPE_MONO] == 0) { - processReprocess(); + if (mReprocessingPairCount == mNumFrameCount || + (mNumImagesToProcess[CAM_TYPE_BAYER] == 0 + && mNumImagesToProcess[CAM_TYPE_MONO] == 0)) { + processFinalPair(); } } - private void checkForValidFramePair() { + private void checkForValidFramePairAndReprocess() { // if we have images from both // as we just added an image onto one of the queues // this condition is only true when both are not empty @@ -464,101 +645,42 @@ private void checkForValidFramePair() { bayer = mBayerFrames.poll(); bayer.mImage.close(); } - } - } - } - - private void releaseBayerFrames() { - for (ReprocessableImage reprocImg : mBayerFrames) { - reprocImg.mImage.close(); - } - - mBayerFrames.clear(); - } - - private void releaseMonoFrames() { - for (ReprocessableImage reprocImg : mMonoFrames) { - reprocImg.mImage.close(); - } - - mMonoFrames.clear(); - } - - private void processReprocess() { - if(mCallback != null) { - if (mMonoFrames.isEmpty() - || mBayerFrames.isEmpty()) { - Log.d(TAG, "processReprocess - frames are empty"); - releaseBayerFrames(); - releaseMonoFrames(); - mCallback.onClearSightFailure(); - return; } else { - // Generate filename - mNamedImages.nameNewImage(System.currentTimeMillis()); - mNamedEntity = mNamedImages.getNextNameEntity(); - - int frameCount = Math.min(mMonoFrames.size(), mBayerFrames.size()); - sendReprocessRequests(CAM_TYPE_BAYER, frameCount); - sendReprocessRequests(CAM_TYPE_MONO, frameCount); + // send for reproc + sendReprocessRequest(CAM_TYPE_BAYER, mBayerFrames.poll()); + sendReprocessRequest(CAM_TYPE_MONO, mMonoFrames.poll()); + mReprocessingPairCount++; } - } else { - releaseBayerFrames(); - releaseMonoFrames(); } } - private void sendReprocessRequests(final int camId, int frameCount) { + private void sendReprocessRequest(final int camId, ReprocessableImage reprocImg) { CameraCaptureSession session = mCaptureSessions[camId]; CameraDevice device = session.getDevice(); try { - ArrayDeque frameQueue; - if (camId == CAM_TYPE_BAYER) { - frameQueue = mBayerFrames; - } else { - frameQueue = mMonoFrames; - } - Log.d(TAG, "sendReprocessRequests - start cam: " + camId - + " num frames: " + frameQueue.size() - + " frameCount: " + frameCount); - - ArrayList reprocRequests = new ArrayList( - frameQueue.size()); - while (reprocRequests.size() < frameCount) { - ReprocessableImage reprocImg = frameQueue.poll(); - - CaptureRequest.Builder reprocRequest = device - .createReprocessCaptureRequest(reprocImg.mCaptureResult); - reprocRequest.addTarget(mImageReader[camId] - .getSurface()); - reprocRequest.set(CaptureRequest.COLOR_CORRECTION_ABERRATION_MODE, - CaptureRequest.COLOR_CORRECTION_ABERRATION_MODE_HIGH_QUALITY); - reprocRequest.set(CaptureRequest.EDGE_MODE, - CaptureRequest.EDGE_MODE_HIGH_QUALITY); - reprocRequest.set(CaptureRequest.NOISE_REDUCTION_MODE, - CaptureRequest.NOISE_REDUCTION_MODE_HIGH_QUALITY); - - if(reprocRequests.size() == 0) { - reprocRequest.setTag(new Object()); - } - reprocRequests.add(reprocRequest.build()); + Log.d(TAG, "sendReprocessRequest - cam: " + camId); + CaptureRequest.Builder reprocRequest = device + .createReprocessCaptureRequest(reprocImg.mCaptureResult); + reprocRequest.addTarget(mImageReader[camId] + .getSurface()); + reprocRequest.set(CaptureRequest.COLOR_CORRECTION_ABERRATION_MODE, + CaptureRequest.COLOR_CORRECTION_ABERRATION_MODE_HIGH_QUALITY); + reprocRequest.set(CaptureRequest.EDGE_MODE, + CaptureRequest.EDGE_MODE_HIGH_QUALITY); + reprocRequest.set(CaptureRequest.NOISE_REDUCTION_MODE, + CaptureRequest.NOISE_REDUCTION_MODE_HIGH_QUALITY); - mImageWriter[camId].queueInputImage(reprocImg.mImage); - } + Long ts = Long.valueOf(reprocImg.mImage.getTimestamp()); + Integer hash = ts.hashCode(); + reprocRequest.setTag(hash); + mReprocessingFrames.put(hash, ts); + Log.d(TAG, "sendReprocessRequest - adding reproc frame - hash: " + hash + ", ts: " + ts); - if(!frameQueue.isEmpty()) { - // clear remaining frames - if (camId == CAM_TYPE_BAYER) { - releaseBayerFrames(); - } else { - releaseMonoFrames(); - } - } + mImageWriter[camId].queueInputImage(reprocImg.mImage); - mImageProcessHandler.obtainMessage(MSG_START_CAPTURE, camId, - reprocRequests.size(), ProcessState.REPROCESS).sendToTarget(); - session.captureBurst(reprocRequests, + CaptureRequest request = reprocRequest.build(); + session.capture(request, new CaptureCallback() { @Override public void onCaptureCompleted( @@ -568,11 +690,10 @@ public void onCaptureCompleted( super.onCaptureCompleted(session, request, result); Log.d(TAG, "reprocess - onCaptureCompleted: " + camId); - if(request.getTag() != null) { - mImageProcessHandler.obtainMessage( - MSG_NEW_RESULT, camId, 0, result) - .sendToTarget(); - } + Integer ts = (Integer)request.getTag(); + obtainMessage( + MSG_NEW_REPROC_RESULT, camId, ts.intValue(), result) + .sendToTarget(); } @Override @@ -583,137 +704,260 @@ public void onCaptureFailed( super.onCaptureFailed(session, request, failure); Log.d(TAG, "reprocess - onCaptureFailed: " + camId); - mImageProcessHandler.obtainMessage( - MSG_NEW_RESULT, camId, 1) + Integer ts = (Integer)request.getTag(); + obtainMessage( + MSG_NEW_REPROC_FAIL, camId, ts.intValue(), failure) .sendToTarget(); } }, null); + + mReprocessingRequests.add(request); } catch (CameraAccessException e) { e.printStackTrace(); } } - private void processNewReprocessEvent(Message msg) { + private void releaseBayerFrames() { + for (ReprocessableImage reprocImg : mBayerFrames) { + reprocImg.mImage.close(); + } + + mBayerFrames.clear(); + + for (Image img : mBayerImages) { + img.close(); + } + + mBayerImages.clear(); + mBayerCaptureResults.clear(); + } + + private void releaseMonoFrames() { + for (ReprocessableImage reprocImg : mMonoFrames) { + reprocImg.mImage.close(); + } + + mMonoFrames.clear(); + + for (Image img : mMonoImages) { + img.close(); + } + + mMonoImages.clear(); + mMonoCaptureResults.clear(); + } + + private void processFinalPair() { + Log.d(TAG, "processFinalPair"); + releaseBayerFrames(); + releaseMonoFrames(); + + removeMessages(MSG_NEW_CAPTURE_RESULT); + removeMessages(MSG_NEW_CAPTURE_FAIL); + + mCaptureDone = true; + + if(mReprocessingPairCount == 0) { + // No matching pairs = nothing registered, no need to reset engine + Log.w(TAG, "processFinalPair - no matching pairs found"); + removeMessages(MSG_END_CAPTURE); + if(mCallback != null) mCallback.onClearSightFailure(null); + } + } + + private void processNewReprocessImage(Message msg) { boolean isBayer = (msg.arg1 == CAM_TYPE_BAYER); - if(msg.what == MSG_NEW_IMG) { - Image image = (Image) msg.obj; - Log.d(TAG, "reprocess - processNewImg"); + Image image = (Image) msg.obj; + long ts = image.getTimestamp(); + Log.d(TAG, "processNewReprocessImage - cam: " + msg.arg1 + ", ts: " + ts); + int frameCount = isBayer?++mReprocessedBayerCount:++mReprocessedMonoCount; - if(mDumpImages) { - saveDebugImageAsJpeg(mMediaSaveService, image, isBayer, mNamedEntity, - ClearSightNativeEngine.getInstance().getImageCount(isBayer)); - } - if(mDumpYUV) { - saveDebugImageAsNV21(image, isBayer, mNamedEntity, - ClearSightNativeEngine.getInstance().getImageCount(isBayer)); - } + if(mDumpImages) { + saveDebugImageAsJpeg(mMediaSaveService, image, isBayer, mNamedEntity, + frameCount, ts/1000000); + } + if(mDumpYUV) { + saveDebugImageAsNV21(image, isBayer, mNamedEntity, frameCount, ts/1000000); + } + + mClearsightRegisterHandler.obtainMessage(MSG_NEW_IMG, + msg.arg1, 0, msg.obj).sendToTarget(); - if (!ClearSightNativeEngine.getInstance() - .hasReferenceImage(isBayer)) { - // reference not yet set - ClearSightNativeEngine.getInstance().setReferenceImage(isBayer, - image); + mReprocessingFrames.removeAt(mReprocessingFrames.indexOfValue(ts)); + checkReprocessDone(); + } + + private void processNewReprocessResult(Message msg) { + Log.d(TAG, "processNewReprocessResult: " + msg.arg1); + boolean isBayer = (msg.arg1 == CAM_TYPE_BAYER); + TotalCaptureResult result = (TotalCaptureResult)msg.obj; + mReprocessingRequests.remove(result.getRequest()); + + if (ClearSightNativeEngine.getInstance() + .getReferenceResult(isBayer) == null) { + // reference not yet set + Log.d(TAG, "reprocess - setReferenceResult: " + msg.obj); + ClearSightNativeEngine.getInstance().setReferenceResult(isBayer, result); + } + + checkReprocessDone(); + } + + private void processNewReprocessFailure(Message msg) { + Log.d(TAG, "processNewReprocessFailure: " + msg.arg1); + CaptureFailure failure = (CaptureFailure)msg.obj; + mReprocessingRequests.remove(failure.getRequest()); + mReprocessingFrames.delete(msg.arg2); + mHasFailures = true; + checkReprocessDone(); + } + + private void checkReprocessDone() { + Log.d(TAG, "checkReprocessDone capture done: " + mCaptureDone + + ", reproc frames: " + mReprocessingFrames.size()); + // If all burst frames and results have been processed + if(mCaptureDone && mReprocessingFrames.size() == 0 && mReprocessingRequests.isEmpty()) { + mClearsightRegisterHandler.obtainMessage(MSG_END_CAPTURE, mHasFailures?1:0, 0).sendToTarget(); + removeMessages(MSG_NEW_REPROC_RESULT); + removeMessages(MSG_NEW_REPROC_FAIL); + mCaptureDone = false; + mHasFailures = false; + + // all burst and reproc frames processed. + // remove timeout msg. + removeMessages(MSG_END_CAPTURE); + } else { + kickTimeout(); + } + } + }; + + private class ClearsightRegisterHandler extends Handler { + private NamedEntity mNamedEntity; + + ClearsightRegisterHandler(Looper looper) { + super(looper); + } + + @Override + public void handleMessage(Message msg) { + if(isClosing()) return; + + switch (msg.what) { + case MSG_START_CAPTURE: + mNamedEntity = (NamedEntity) msg.obj; + break; + case MSG_NEW_IMG: + registerImage(msg); + break; + case MSG_END_CAPTURE: + // Check if timeout + if(msg.arg2 == 1) { + Log.d(TAG, "ClearsightRegisterHandler - handleTimeout"); + ClearSightNativeEngine.getInstance().reset(); + if(mCallback != null) mCallback.onClearSightFailure(null); } else { - // if ref images set, register this image - if(ClearSightNativeEngine.getInstance().registerImage( - isBayer, image) == false) { - Log.w(TAG, "registerImage : terminal error with input image"); - } + mClearsightProcessHandler.obtainMessage(MSG_START_CAPTURE, + msg.arg1, 0, mNamedEntity).sendToTarget(); } - mNumImagesToProcess[msg.arg1]--; - } else if (msg.arg2 == 1) { - // capture failed - mNumImagesToProcess[msg.arg1]--; + break; + } + } + + private void registerImage(Message msg) { + boolean isBayer = (msg.arg1 == CAM_TYPE_BAYER); + Image image = (Image)msg.obj; + + if (!ClearSightNativeEngine.getInstance() + .hasReferenceImage(isBayer)) { + // reference not yet set + ClearSightNativeEngine.getInstance().setReferenceImage(isBayer, image); } else { - Log.d(TAG, "reprocess - processNewResult: " + msg.arg1); - if (ClearSightNativeEngine.getInstance() - .getReferenceResult(isBayer) == null) { - // reference not yet set - Log.d(TAG, "reprocess - setReferenceResult: " + msg.obj); - ClearSightNativeEngine.getInstance().setReferenceResult(isBayer, - (TotalCaptureResult)msg.obj); + // if ref images set, register this image + if(ClearSightNativeEngine.getInstance().registerImage( + isBayer, image) == false) { + Log.w(TAG, "registerImage : terminal error with input image"); } } + } + } + + private class ClearsightProcessHandler extends Handler { + ClearsightProcessHandler(Looper looper) { + super(looper); + } - Log.d(TAG, "reprocess - processNewEvent, cam: " + msg.arg1 - + " count: " + mNumImagesToProcess[msg.arg1]); + @Override + public void handleMessage(Message msg) { + if(isClosing()) return; - if (mNumImagesToProcess[CAM_TYPE_BAYER] == 0 - && mNumImagesToProcess[CAM_TYPE_MONO] == 0 - && ClearSightNativeEngine.getInstance().getReferenceResult(true) != null - && ClearSightNativeEngine.getInstance().getReferenceResult(false) != null) { - processClearSight(); + switch (msg.what) { + case MSG_START_CAPTURE: + processClearSight((NamedEntity) msg.obj); + break; } } - private void processClearSight() { - Log.d(TAG, "reprocess - processClearSight, bayercount: " - + mNumImagesToProcess[CAM_TYPE_BAYER] + " mono count: " - + mNumImagesToProcess[CAM_TYPE_MONO]); + private void processClearSight(NamedEntity namedEntity) { + mImageEncodeHandler.obtainMessage(MSG_START_CAPTURE).sendToTarget(); - ClearSightNativeEngine.ClearsightImage csImage = ClearSightNativeEngine - .getInstance().processImage(); + short encodeRequest = 0; + long csTs = ClearSightNativeEngine.getInstance().getReferenceImage(true).getTimestamp(); + CaptureRequest.Builder csRequest = createEncodeReprocRequest( + ClearSightNativeEngine.getInstance().getReferenceResult(true), CAM_TYPE_BAYER); + csRequest.setTag(new Object()); - if(csImage != null) { - Log.d(TAG, "reprocess - processClearSight, roiRect: " - + csImage.getRoiRect().toString()); + boolean processInit = ClearSightNativeEngine.getInstance().initProcessImage(); + sendReferenceMonoEncodeRequest(); + sendReferenceBayerEncodeRequest(); + encodeRequest |= ImageEncodeHandler.MASK_BAYER_ENCODE|ImageEncodeHandler.MASK_MONO_ENCODE; + ClearSightNativeEngine.getInstance().reset(); - Image encodeImage = mImageWriter[CAM_TYPE_BAYER].dequeueInputImage(); - encodeImage.setCropRect(csImage.getRoiRect()); - encodeImage.setTimestamp( - ClearSightNativeEngine.getInstance().getReferenceImage(true).getTimestamp()); - Plane[] planes = encodeImage.getPlanes(); - planes[0].getBuffer().put(csImage.mYplane); - planes[2].getBuffer().put(csImage.mVUplane); - - mClearSightFrame = new ReprocessableImage(encodeImage, - ClearSightNativeEngine.getInstance().getReferenceResult(true)); - if(mCallback != null) - mCallback.onClearSightSuccess(); - } else { - mClearSightFrame = null; + if(processInit) { if(mCallback != null) - mCallback.onClearSightFailure(); + mCallback.onReleaseShutterLock(); + + Image encodeImage = mImageWriter[CAM_TYPE_BAYER].dequeueInputImage(); + ClearSightNativeEngine.ClearsightImage csImage = new ClearsightImage(encodeImage); + encodeImage.setTimestamp(csTs); + + if(ClearSightNativeEngine.getInstance().processImage(csImage)) { + encodeRequest |= ImageEncodeHandler.MASK_CS_ENCODE; + sendReprocessRequest(csRequest, encodeImage, CAM_TYPE_BAYER); + } else { + csImage = null; + encodeImage.close(); + } } - sendEncodeRequests(); - ClearSightNativeEngine.getInstance().reset(); - mClearSightFrame = null; - mState = ProcessState.IDLE; - mNamedEntity = null; - } - private void sendEncodeRequests() { - mImageEncodeHandler.obtainMessage( - MSG_START_CAPTURE, (mClearSightFrame != null)?1:0, 0, mNamedEntity).sendToTarget(); + mImageEncodeHandler.obtainMessage(MSG_END_CAPTURE, + encodeRequest, 0, namedEntity).sendToTarget(); + } + private void sendReferenceMonoEncodeRequest() { // First Mono - CameraCaptureSession session = mCaptureSessions[CAM_TYPE_MONO]; - sendReprocessRequest(session, - new ReprocessableImage( - ClearSightNativeEngine.getInstance().getReferenceImage(false), - ClearSightNativeEngine.getInstance().getReferenceResult(false)), - CAM_TYPE_MONO); + CaptureRequest.Builder monoRequest = createEncodeReprocRequest( + ClearSightNativeEngine.getInstance().getReferenceResult(false), CAM_TYPE_MONO); + sendReprocessRequest(monoRequest, + ClearSightNativeEngine.getInstance().getReferenceImage(false), + CAM_TYPE_MONO); + } + private void sendReferenceBayerEncodeRequest() { // bayer - session = mCaptureSessions[CAM_TYPE_BAYER]; - sendReprocessRequest(session, - new ReprocessableImage( - ClearSightNativeEngine.getInstance().getReferenceImage(true), - ClearSightNativeEngine.getInstance().getReferenceResult(true)), - CAM_TYPE_BAYER); - - // clearsight - if(mClearSightFrame != null) - sendReprocessRequest(session, mClearSightFrame, CAM_TYPE_BAYER); + CaptureRequest.Builder bayerRequest = createEncodeReprocRequest( + ClearSightNativeEngine.getInstance().getReferenceResult(true), CAM_TYPE_BAYER); + sendReprocessRequest(bayerRequest, + ClearSightNativeEngine.getInstance().getReferenceImage(true), + CAM_TYPE_BAYER); } - private void sendReprocessRequest(CameraCaptureSession session, ReprocessableImage image, final int camType) { - CameraDevice device = session.getDevice(); - + private CaptureRequest.Builder createEncodeReprocRequest(TotalCaptureResult captureResult, int camType) { + CaptureRequest.Builder reprocRequest = null; try { - CaptureRequest.Builder reprocRequest = device - .createReprocessCaptureRequest(image.mCaptureResult); + reprocRequest = mCaptureSessions[camType].getDevice() + .createReprocessCaptureRequest(captureResult); reprocRequest.addTarget(mEncodeImageReader[camType] .getSurface()); reprocRequest.set(CaptureRequest.COLOR_CORRECTION_ABERRATION_MODE, @@ -722,19 +966,41 @@ private void sendReprocessRequest(CameraCaptureSession session, ReprocessableIma CaptureRequest.EDGE_MODE_OFF); reprocRequest.set(CaptureRequest.NOISE_REDUCTION_MODE, CaptureRequest.NOISE_REDUCTION_MODE_OFF); + } catch (CameraAccessException e) { + e.printStackTrace(); + } - Rect cropRect = image.mImage.getCropRect(); - if(cropRect != null && - !cropRect.isEmpty()) { - // has crop rect. apply to jpeg request - reprocRequest.set(CaptureModule.JpegCropEnableKey, (byte)1); - reprocRequest.set(CaptureModule.JpegCropRectKey, - new int[] {cropRect.left, cropRect.top, cropRect.width(), cropRect.height()}); + return reprocRequest; + } + + private void sendReprocessRequest(CaptureRequest.Builder reprocRequest, Image image, final int camType) { + + try { + reprocRequest.set(CaptureModule.JpegCropEnableKey, (byte)1); + + Rect cropRect = image.getCropRect(); + if(cropRect == null || + cropRect.isEmpty()) { + // if no crop rect set, init to default image width + height + cropRect = new Rect(0, 0, image.getWidth(), image.getHeight()); + } + + cropRect = getFinalCropRect(cropRect); + // has crop rect. apply to jpeg request + reprocRequest.set(CaptureModule.JpegCropRectKey, + new int[] {cropRect.left, cropRect.top, cropRect.width(), cropRect.height()}); + + if(camType == CAM_TYPE_MONO) { + reprocRequest.set(CaptureModule.JpegRoiRectKey, + new int[] {0, 0, mFinalMonoSize.getWidth(), mFinalMonoSize.getHeight()}); + } else { + reprocRequest.set(CaptureModule.JpegRoiRectKey, + new int[] {0, 0, mFinalPictureSize.getWidth(), mFinalPictureSize.getHeight()}); } - mImageWriter[camType].queueInputImage(image.mImage); + mImageWriter[camType].queueInputImage(image); - session.capture(reprocRequest.build(), + mCaptureSessions[camType].capture(reprocRequest.build(), new CaptureCallback() { @Override public void onCaptureCompleted( @@ -744,7 +1010,7 @@ public void onCaptureCompleted( super.onCaptureCompleted(session, request, result); Log.d(TAG, "encode - onCaptureCompleted: " + camType); mImageEncodeHandler.obtainMessage( - MSG_NEW_RESULT, camType, 0, result) + MSG_NEW_CAPTURE_RESULT, camType, 0, result) .sendToTarget(); } @@ -756,7 +1022,7 @@ public void onCaptureFailed( super.onCaptureFailed(session, request, failure); Log.d(TAG, "encode - onCaptureFailed: " + camType); mImageEncodeHandler.obtainMessage( - MSG_NEW_RESULT, camType, 1) + MSG_NEW_CAPTURE_FAIL, camType, 0, failure) .sendToTarget(); } }, null); @@ -764,10 +1030,17 @@ public void onCaptureFailed( e.printStackTrace(); } } - }; + } private class ImageEncodeHandler extends Handler { - private boolean mClearsightEncode; + static final short MASK_CS_ENCODE = 0x01; + static final short MASK_BAYER_ENCODE = 0x02; + static final short MASK_MONO_ENCODE = 0x04; + + private short mEncodeRequest; + private short mEncodeResults; + private boolean mReadyToSave; + private boolean mHasFailure; private Image mMonoImage; private Image mBayerImage; private Image mClearSightImage; @@ -779,14 +1052,25 @@ public ImageEncodeHandler(Looper looper) { @Override public void handleMessage(Message msg) { + if(isClosing()) return; + switch (msg.what) { case MSG_START_CAPTURE: - mClearsightEncode = (msg.arg1 == 1); + Log.d(TAG, "ImageEncodeEvent - START_CAPTURE"); + resetParams(); + break; + case MSG_END_CAPTURE: + Log.d(TAG, "ImageEncodeEvent - END_CAPTURE"); mNamedEntity = (NamedEntity) msg.obj; + mEncodeRequest = (short)msg.arg1; + mReadyToSave = true; + saveMpoImage(); break; case MSG_NEW_IMG: - case MSG_NEW_RESULT: + case MSG_NEW_CAPTURE_RESULT: + case MSG_NEW_CAPTURE_FAIL: processNewEvent(msg); + saveMpoImage(); break; } } @@ -796,62 +1080,114 @@ private void processNewEvent(Message msg) { Log.d(TAG, "processNewEncodeEvent - newImg: " + msg.arg1); if(msg.arg1 == CAM_TYPE_MONO) { mMonoImage = (Image)msg.obj; + mEncodeResults |= MASK_MONO_ENCODE; } else if(mBayerImage == null){ mBayerImage = (Image)msg.obj; + mEncodeResults |= MASK_BAYER_ENCODE; } else { mClearSightImage = (Image)msg.obj; + mEncodeResults |= MASK_CS_ENCODE; } - } else if (msg.arg2 == 0) { + } else if (msg.what == MSG_NEW_CAPTURE_RESULT) { Log.d(TAG, "processNewEncodeEvent - newResult: " + msg.arg1); } else { Log.d(TAG, "processNewEncodeEvent - newFailure: " + msg.arg1); - } - if(mMonoImage != null && mBayerImage != null - && (!mClearsightEncode || - (mClearsightEncode && mClearSightImage != null))) { - saveMpoImage(); + mHasFailure = true; + if(msg.arg1 == CAM_TYPE_MONO) { + mEncodeResults |= MASK_MONO_ENCODE; + } else { + CaptureFailure failure = (CaptureFailure)msg.obj; + if(failure.getRequest().getTag() != null) + mEncodeResults |= MASK_CS_ENCODE; + else + mEncodeResults |= MASK_BAYER_ENCODE; + } } } private void saveMpoImage() { + if(!mReadyToSave || mEncodeRequest != mEncodeResults) { + Log.d(TAG, "saveMpoImage - not yet ready to save"); + return; + } + + Log.d(TAG, "saveMpoImage"); + if(mHasFailure) { + // don't save anything and fail + Log.d(TAG, "saveMpoImage has failure - aborting."); + if(mCallback != null) mCallback.onClearSightFailure(null); + resetParams(); + return; + } + String title = (mNamedEntity == null) ? null : mNamedEntity.title; long date = (mNamedEntity == null) ? -1 : mNamedEntity.date; int width = mBayerImage.getWidth(); int height = mBayerImage.getHeight(); - if(mClearsightEncode) { + if(mClearSightImage != null) { width = mClearSightImage.getWidth(); height = mClearSightImage.getHeight(); } + byte[] clearSightBytes = getJpegData(mClearSightImage); byte[] bayerBytes = getJpegData(mBayerImage); + byte[] monoBytes = getJpegData(mMonoImage); ExifInterface exif = Exif.getExif(bayerBytes); int orientation = Exif.getOrientation(exif); - mMediaSaveService.addMpoImage( - getJpegData(mClearSightImage), - bayerBytes, - getJpegData(mMonoImage), width, height, title, - date, null, orientation, mMediaSavedListener, - mMediaSaveService.getContentResolver(), "jpeg"); - - mBayerImage.close(); - mBayerImage = null; - mMonoImage.close(); - mMonoImage = null; + if(clearSightBytes != null) { + if(mCallback != null) mCallback.onClearSightSuccess(clearSightBytes); + } else if (bayerBytes != null) { + if(mCallback != null) mCallback.onClearSightFailure(bayerBytes); + } else { + if(mCallback != null) mCallback.onClearSightFailure(null); + } + + if(monoBytes == null) { + mMediaSaveService.addImage( + clearSightBytes!=null?clearSightBytes:bayerBytes, title, date, null, + width, height, orientation, exif, + mMediaSavedListener, + mMediaSaveService.getContentResolver(), "jpeg"); + } else if (bayerBytes != null) { + mMediaSaveService.addMpoImage( + clearSightBytes, + bayerBytes, + monoBytes, width, height, title, + date, null, orientation, mMediaSavedListener, + mMediaSaveService.getContentResolver(), "jpeg"); + } + + resetParams(); + } + + void resetParams() { + if(mBayerImage != null) { + mBayerImage.close(); + mBayerImage = null; + } + if(mMonoImage != null) { + mMonoImage.close(); + mMonoImage = null; + } if(mClearSightImage != null) { mClearSightImage.close(); mClearSightImage = null; } mNamedEntity = null; + mReadyToSave = false; + mHasFailure = false; + mEncodeRequest = 0; + mEncodeResults = 0; } } public void saveDebugImageAsJpeg(MediaSaveService service, byte[] data, - int width, int height, boolean isBayer, NamedEntity namedEntity, int count) { - String type = isBayer?"bayer":"mono"; + int width, int height, boolean isBayer, NamedEntity namedEntity, int count, long ts) { + String type = isBayer?"b":"m"; long date = (namedEntity == null) ? -1 : namedEntity.date; - String title = String.format("%s_%s_%02d", namedEntity.title, type, count); + String title = String.format("%s_%s%02d_%d", namedEntity.title, type, count, ts); service.addImage(data, title, date, null, width, height, 0, null, null, @@ -859,33 +1195,33 @@ public void saveDebugImageAsJpeg(MediaSaveService service, byte[] data, } public void saveDebugImageAsJpeg(MediaSaveService service, YuvImage image, boolean isBayer, - NamedEntity namedEntity, int count) { + NamedEntity namedEntity, int count, long ts) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); image.compressToJpeg( new Rect(0, 0, image.getWidth(), image.getHeight()), 100, baos); saveDebugImageAsJpeg(service, baos.toByteArray(), image.getWidth(), image.getHeight(), - isBayer, namedEntity, count); + isBayer, namedEntity, count, ts); } public void saveDebugImageAsJpeg(MediaSaveService service, Image image, boolean isBayer, - NamedEntity namedEntity, int count) { + NamedEntity namedEntity, int count, long ts) { if(image.getFormat() == ImageFormat.YUV_420_888) - saveDebugImageAsJpeg(service, createYuvImage(image), isBayer, namedEntity, count); + saveDebugImageAsJpeg(service, createYuvImage(image), isBayer, namedEntity, count, ts); else if (image.getFormat() == ImageFormat.JPEG) { saveDebugImageAsJpeg(service, getJpegData(image), image.getWidth(), image.getHeight(), - isBayer, namedEntity, count); + isBayer, namedEntity, count, ts); } } - public void saveDebugImageAsNV21(Image image, boolean isBayer, NamedEntity namedEntity, int count) { + public void saveDebugImageAsNV21(Image image, boolean isBayer, NamedEntity namedEntity, int count, long ts) { if(image.getFormat() != ImageFormat.YUV_420_888) { Log.d(TAG, "saveDebugImageAsNV21 - invalid param"); } - String type = isBayer?"bayer":"mono"; - String title = String.format("%s_%dx%d_NV21_%s_%02d", namedEntity.title, - image.getWidth(), image.getHeight(), type, count); + String type = isBayer?"b":"m"; + String title = String.format("%s_%dx%d_NV21_%s%02d_%d", namedEntity.title, + image.getWidth(), image.getHeight(), type, count, ts); YuvImage yuv = createYuvImage(image); String path = Storage.generateFilepath(title, "yuv"); @@ -928,4 +1264,76 @@ public byte[] getJpegData(Image image) { return data; } + + private Size findMaxOutputSize(StreamConfigurationMap map) { + Size[] sizes = map.getOutputSizes(ImageFormat.YUV_420_888); + Arrays.sort(sizes, new CameraUtil.CompareSizesByArea()); + return sizes[sizes.length-1]; + } + + private Size getFinalMonoSize() { + Size finalSize = null; + long finalPicArea = mFinalPictureSize.getWidth() * mFinalPictureSize.getHeight(); + + // if final pic size is less than 2MP, then use same MONO size + if(finalPicArea > MIN_MONO_AREA) { + for(Size size:MONO_SIZES) { + float monoRatio = (float)size.getWidth() / (float)size.getHeight(); + if(monoRatio == mFinalPictureRatio) { + finalSize = size; + break; + } else if (Math.abs(monoRatio - mFinalPictureRatio) < 0.1f) { + // close enough + int monoWidth = size.getWidth(); + int monoHeight = size.getHeight(); + if(monoRatio > mFinalPictureRatio) { + // keep width, increase height to match final ratio + // add .5 to round up if necessary + monoHeight = (int)(((float)monoWidth / mFinalPictureRatio) + .5f); + } else if(monoRatio < mFinalPictureRatio) { + // keep height, increase width to match final ratio + // add .5 to round up if necessary + monoWidth = (int)(((float)monoHeight * mFinalPictureRatio) + .5f); + } + finalSize = new Size(monoWidth, monoHeight); + } + } + } + + if(finalSize == null) { + // set to mFinalPictureSize if matching size not found + // or if final resolution is less than 2 MP + finalSize = mFinalPictureSize; + } + + return finalSize; + } + + private Rect getFinalCropRect(Rect rect) { + Rect finalRect = new Rect(rect); + float rectRatio = (float) rect.width()/(float) rect.height(); + + // if ratios are different, adjust crop rect to fit ratio + // if ratios are same, no need to adjust crop + Log.d(TAG, "getFinalCropRect - rect: " + rect.toString()); + Log.d(TAG, "getFinalCropRect - ratios: " + rectRatio + ", " + mFinalPictureRatio); + if(rectRatio > mFinalPictureRatio) { + // ratio indicates need for horizontal crop + // add .5 to round up if necessary + int newWidth = (int)(((float)rect.height() * mFinalPictureRatio) + .5f); + int newXoffset = (rect.width() - newWidth)/2 + rect.left; + finalRect.left = newXoffset; + finalRect.right = newXoffset + newWidth; + } else if(rectRatio < mFinalPictureRatio) { + // ratio indicates need for vertical crop + // add .5 to round up if necessary + int newHeight = (int)(((float)rect.width() / mFinalPictureRatio) + .5f); + int newYoffset = (rect.height() - newHeight)/2 + rect.top; + finalRect.top = newYoffset; + finalRect.bottom = newYoffset + newHeight; + } + + Log.d(TAG, "getFinalCropRect - final rect: " + finalRect.toString()); + return finalRect; + } } diff --git a/src/org/codeaurora/snapcam/filter/ClearSightNativeEngine.java b/src/org/codeaurora/snapcam/filter/ClearSightNativeEngine.java index 18e7671b9..402ffce84 100644 --- a/src/org/codeaurora/snapcam/filter/ClearSightNativeEngine.java +++ b/src/org/codeaurora/snapcam/filter/ClearSightNativeEngine.java @@ -56,18 +56,23 @@ public class ClearSightNativeEngine { } } - private static final int METADATA_SIZE = 5; + private static final int METADATA_SIZE = 6; private static final int Y_PLANE = 0; private static final int VU_PLANE = 2; private static boolean mLibLoaded; - private static byte[] mOtpCalibData; private static ClearSightNativeEngine mInstance; + private byte[] mOtpCalibData; + private int mImageWidth; + private int mImageHeight; + private int mYStride; + private int mVUStride; private Image mRefColorImage; private Image mRefMonoImage; private TotalCaptureResult mRefColorResult; private TotalCaptureResult mRefMonoResult; + private ArrayList mCache = new ArrayList(); private ArrayList mSrcColor = new ArrayList(); private ArrayList mSrcMono = new ArrayList(); @@ -85,10 +90,27 @@ public static ClearSightNativeEngine getInstance() { return mInstance; } - public static void setOtpCalibData(CamSystemCalibrationData calibData) { + public void init(int frameCount, int width, int height, CamSystemCalibrationData calibData) { String calibStr = calibData.toString(); Log.d(TAG, "OTP calibration data: \n" + calibStr); mOtpCalibData = calibStr.getBytes(); + mImageWidth = width; + mImageHeight = height; + mYStride = width; + mVUStride = width; + while(frameCount > 0) { + cacheSourceImage(new SourceImage(width*height, width*height/2)); + frameCount --; + } + } + + public void close() { + reset(); + mCache.clear(); + mImageWidth = 0; + mImageHeight = 0; + mYStride = 0; + mVUStride = 0; } public boolean isLibLoaded() { @@ -96,14 +118,28 @@ public boolean isLibLoaded() { } public void reset() { - mSrcColor.clear(); - mSrcMono.clear(); + while(!mSrcColor.isEmpty()) { + cacheSourceImage(mSrcColor.remove(0)); + } + while(!mSrcMono.isEmpty()) { + cacheSourceImage(mSrcMono.remove(0)); + } setReferenceColorImage(null); setReferenceMonoImage(null); setReferenceColorResult(null); setReferenceMonoResult(null); } + private SourceImage getNewSourceImage() { + Log.d(TAG, "getNewSourceImage: " + mCache.size()); + return mCache.remove(0); + } + + private void cacheSourceImage(SourceImage image) { + mCache.add(image); + Log.d(TAG, "cacheSourceImage: " + mCache.size()); + } + public void setReferenceResult(boolean color, TotalCaptureResult result) { if (color) setReferenceColorResult(result); @@ -136,9 +172,17 @@ private void setReferenceColorImage(Image reference) { if (mRefColorImage != null) { Log.d(TAG, "setRefColorImage"); - mSrcColor.add(new SourceImage(mRefColorImage.getPlanes()[Y_PLANE] - .getBuffer(), mRefColorImage.getPlanes()[VU_PLANE] - .getBuffer(), new int[] { 0, 0, 0, 0, 0 })); + Plane[] planes = mRefColorImage.getPlanes(); + SourceImage newSrc = getNewSourceImage(); + ByteBuffer refY = planes[Y_PLANE].getBuffer(); + ByteBuffer refVU = planes[VU_PLANE].getBuffer(); + refY.rewind(); + refVU.rewind(); + newSrc.mY.rewind(); + newSrc.mY.put(refY); + newSrc.mVU.rewind(); + newSrc.mVU.put(refVU); + mSrcColor.add(newSrc); } } @@ -152,8 +196,13 @@ private void setReferenceMonoImage(Image reference) { if (mRefMonoImage != null) { Log.d(TAG, "setRefMonoImage"); - mSrcMono.add(new SourceImage(mRefMonoImage.getPlanes()[Y_PLANE] - .getBuffer(), null, new int[] { 0, 0, 0, 0, 0 })); + Plane[] planes = mRefMonoImage.getPlanes(); + SourceImage newSrc = getNewSourceImage(); + ByteBuffer refY = planes[Y_PLANE].getBuffer(); + refY.rewind(); + newSrc.mY.rewind(); + newSrc.mY.put(refY); + mSrcMono.add(newSrc); } } @@ -180,55 +229,54 @@ public boolean registerImage(boolean color, Image image) { return false; } - Image referenceImage = color?mRefColorImage:mRefMonoImage; + SourceImage newSrc = getNewSourceImage(); + SourceImage referenceImage = sourceImages.get(0); Plane[] planes = image.getPlanes(); ByteBuffer yBuf = planes[Y_PLANE].getBuffer(); - ByteBuffer refY = referenceImage.getPlanes()[Y_PLANE].getBuffer(); - ByteBuffer regY = ByteBuffer.allocateDirect(refY.capacity()); + ByteBuffer refY = referenceImage.mY; + ByteBuffer regY = newSrc.mY; int yRowStride = planes[Y_PLANE].getRowStride(); ByteBuffer vuBuf = null; - ByteBuffer refVU = null; ByteBuffer regVU = null; int vuRowStride = 0; if(color) { vuBuf = planes[VU_PLANE].getBuffer(); - refVU = referenceImage.getPlanes()[VU_PLANE].getBuffer(); - regVU = ByteBuffer.allocateDirect(refVU.capacity()); + regVU = newSrc.mVU; vuRowStride = planes[VU_PLANE].getRowStride(); } - int[] metadata = new int[METADATA_SIZE]; + float[] metadata = newSrc.mMetadata; boolean result = nativeClearSightRegisterImage(refY, - yBuf, vuBuf, image.getWidth(), image.getHeight(), + yBuf, vuBuf, mImageWidth, mImageHeight, yRowStride, vuRowStride, regY, regVU, metadata); if (result) { - sourceImages.add(new SourceImage(regY, regVU, metadata)); + sourceImages.add(newSrc); } image.close(); return result; } - public ClearsightImage processImage() { + public boolean initProcessImage() { // check data validity if (mSrcColor.size() != mSrcMono.size()) { // mis-match in num images Log.d(TAG, "processImage - numImages mismatch - bayer: " + mSrcColor.size() + ", mono: " + mSrcMono.size()); - return null; + return false; } int numImages = mSrcColor.size(); ByteBuffer[] srcColorY = new ByteBuffer[numImages]; ByteBuffer[] srcColorVU = new ByteBuffer[numImages]; - int[][] metadataColor = new int[numImages][]; + float[][] metadataColor = new float[numImages][]; ByteBuffer[] srcMonoY = new ByteBuffer[numImages]; - int[][] metadataMono = new int[numImages][]; + float[][] metadataMono = new float[numImages][]; - Log.d(TAG, "processImage - numImages: " + numImages); + Log.d(TAG, "processImage - num Images: " + numImages); for (int i = 0; i < numImages; i++) { SourceImage color = mSrcColor.get(i); @@ -242,96 +290,88 @@ public ClearsightImage processImage() { metadataMono[i] = mono.mMetadata; } - Plane[] colorPlanes = mRefColorImage.getPlanes(); - Plane[] monoPlanes = mRefMonoImage.getPlanes(); - ByteBuffer dstY = ByteBuffer.allocateDirect(colorPlanes[Y_PLANE] - .getBuffer().capacity()); - ByteBuffer dstVU = ByteBuffer.allocateDirect(colorPlanes[VU_PLANE] - .getBuffer().capacity()); - int[] roiRect = new int[4]; - - Log.d(TAG, "processImage - refImage size - y: " - + colorPlanes[Y_PLANE].getBuffer().capacity() - + " vu: " + colorPlanes[VU_PLANE].getBuffer().capacity()); - - Log.d(TAG, "processImage - dst size - y: " - + dstY.capacity() + " vu: " + dstVU.capacity()); - int iso = mRefMonoResult.get(CaptureResult.SENSOR_SENSITIVITY); long exposure = mRefMonoResult.get(CaptureResult.SENSOR_EXPOSURE_TIME); // capture result stores exposure time in NS and we need MS. exposure /= 100000; Log.d(TAG, "processImage - iso: " + iso + " exposure ms: " + exposure); - boolean result = nativeClearSightProcess(numImages, srcColorY, srcColorVU, - metadataColor, mRefColorImage.getWidth(), - mRefColorImage.getHeight(), - colorPlanes[Y_PLANE].getRowStride(), - colorPlanes[VU_PLANE].getRowStride(), srcMonoY, metadataMono, - mRefMonoImage.getWidth(), mRefMonoImage.getHeight(), - monoPlanes[Y_PLANE].getRowStride(), mOtpCalibData, - (int)exposure, iso, dstY, dstVU, - colorPlanes[Y_PLANE].getRowStride(), - colorPlanes[VU_PLANE].getRowStride(), roiRect); - - if (result) { - dstY.rewind(); - dstVU.rewind(); - return new ClearsightImage(dstY, dstVU, roiRect); - } else { - return null; - } + return nativeClearSightProcessInit(numImages, + srcColorY, srcColorVU, metadataColor, mImageWidth, mImageHeight, + mYStride, mVUStride, srcMonoY, metadataMono, mImageWidth, mImageHeight, + mYStride, mOtpCalibData, (int)exposure, iso); } - private native final boolean nativeConfigureClearSight(float brIntensity, float sharpenIntensity); + public boolean processImage(ClearsightImage csImage) { + // check data validity + ByteBuffer dstY = csImage.getYBuffer(); + ByteBuffer dstVU = csImage.getVUBuffer(); + int[] roiRect = new int[4]; + + Log.d(TAG, "processImage - dst size - y: " + + dstY.capacity() + " vu: " + dstVU.capacity()); + + boolean result = nativeClearSightProcess(dstY, dstVU, + mYStride, mVUStride, roiRect); + csImage.setRoiRect(roiRect); + + Log.d(TAG, "processImage - roiRect: " + + csImage.getRoiRect().toString()); + + return result; + } private native final boolean nativeClearSightRegisterImage(ByteBuffer refY, ByteBuffer srcY, ByteBuffer srcVU, int width, int height, int strideY, int strideVU, ByteBuffer dstY, ByteBuffer dstVU, - int[] metadata); + float[] metadata); - private native final boolean nativeClearSightProcess(int numImagePairs, + private native final boolean nativeClearSightProcessInit(int numImagePairs, ByteBuffer[] srcColorY, ByteBuffer[] srcColorVU, - int[][] metadataColor, int srcColorWidth, int srcColorHeight, + float[][] metadataColor, int srcColorWidth, int srcColorHeight, int srcColorStrideY, int srcColorStrideVU, ByteBuffer[] srcMonoY, - int[][] metadataMono, int srcMonoWidth, int srcMonoHeight, - int srcMonoStrideY, byte[] otp, int exposureMs, int iso, - ByteBuffer dstY, ByteBuffer dstVU, int dstStrideY, int dstStrideVU, int[] roiRect); + float[][] metadataMono, int srcMonoWidth, int srcMonoHeight, + int srcMonoStrideY, byte[] otp, int exposureMs, int iso); + + private native final boolean nativeClearSightProcess(ByteBuffer dstY, + ByteBuffer dstVU, int dstStrideY, int dstStrideVU, int[] roiRect); private class SourceImage { ByteBuffer mY; ByteBuffer mVU; - int[] mMetadata; + float[] mMetadata; - SourceImage(ByteBuffer y, ByteBuffer vu, int[] metadata) { - mY = y; - mVU = vu; - mMetadata = metadata; + SourceImage(int ySize, int vuSize) { + mY = ByteBuffer.allocateDirect(ySize); + mVU = ByteBuffer.allocateDirect(vuSize); + mMetadata = new float[METADATA_SIZE]; } } public static class ClearsightImage { - public final ByteBuffer mYplane; - public final ByteBuffer mVUplane; + private Image mImage; private Rect mRoiRect; - ClearsightImage(ByteBuffer yPlane, ByteBuffer vuPlane, int[] rect) { - mYplane = yPlane; - mVUplane = vuPlane; - mRoiRect = new Rect(rect[0], rect[1], rect[0] + rect[2], rect[1] - + rect[3]); + ClearsightImage(Image image) { + mImage = image; } - public Rect getRoiRect() { - return mRoiRect; + public ByteBuffer getYBuffer() { + return mImage.getPlanes()[Y_PLANE].getBuffer(); + } + + public ByteBuffer getVUBuffer() { + return mImage.getPlanes()[VU_PLANE].getBuffer(); } - public int getWidth() { - return (mRoiRect.right - mRoiRect.left); + public void setRoiRect(int[] rect) { + mRoiRect = new Rect(rect[0], rect[1], rect[0] + rect[2], rect[1] + + rect[3]); + mImage.setCropRect(mRoiRect); } - public int getHeight() { - return (mRoiRect.bottom - mRoiRect.top); + public Rect getRoiRect() { + return mRoiRect; } }