diff options
| author | Julian Veit <claymore1298@gmail.com> | 2022-03-20 00:06:45 +0100 |
|---|---|---|
| committer | Julian Veit <claymore1298@gmail.com> | 2022-03-20 00:06:45 +0100 |
| commit | d250e4a60f3b6219c6925534629eee12834a92b8 (patch) | |
| tree | 52fb90369927e24dc962b6ec283f6a3f710f7a6c /src/com/android/camera/multi/MultiCaptureModule.java | |
| parent | ef111dceb9e3fce395bff4f6a4ef1591c701e80c (diff) | |
| parent | 8c444160bd565319468cce5bfdcfc11d20e8b7cb (diff) | |
Merge branch 'lineage-18.1' of https://github.com/LineageOS/android_packages_apps_Snap into HEADHEADs12.1
Change-Id: Ia09760085480a6a028c8969f16f503070ab816e5
Diffstat (limited to 'src/com/android/camera/multi/MultiCaptureModule.java')
| -rw-r--r--[-rwxr-xr-x] | src/com/android/camera/multi/MultiCaptureModule.java | 1646 |
1 files changed, 823 insertions, 823 deletions
diff --git a/src/com/android/camera/multi/MultiCaptureModule.java b/src/com/android/camera/multi/MultiCaptureModule.java index cf629daa1..34065150f 100755..100644 --- a/src/com/android/camera/multi/MultiCaptureModule.java +++ b/src/com/android/camera/multi/MultiCaptureModule.java @@ -1,824 +1,824 @@ -/*
- * Copyright (c) 2019 The Linux Foundation. All rights reserved.
- * Not a Contribution.
- *
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.camera.multi;
-
-import android.content.Context;
-import android.content.ContentResolver;
-import android.content.SharedPreferences;
-import android.graphics.ImageFormat;
-import android.graphics.Point;
-import android.hardware.camera2.CameraDevice;
-import android.hardware.camera2.CameraManager;
-import android.hardware.camera2.CameraAccessException;
-import android.hardware.camera2.CameraCaptureSession;
-import android.hardware.camera2.CameraCharacteristics;
-import android.hardware.camera2.CaptureFailure;
-import android.hardware.camera2.CaptureRequest;
-import android.hardware.camera2.CaptureResult;
-import android.hardware.camera2.TotalCaptureResult;
-import android.hardware.camera2.params.OutputConfiguration;
-import android.hardware.camera2.params.SessionConfiguration;
-import android.hardware.camera2.params.StreamConfigurationMap;
-import android.media.CameraProfile;
-import android.media.Image;
-import android.media.ImageReader;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.Message;
-import android.util.Log;
-import android.util.Size;
-import android.view.Surface;
-import android.view.SurfaceHolder;
-import android.widget.Toast;
-
-import java.nio.ByteBuffer;
-import java.util.concurrent.Executor;
-import java.util.concurrent.Semaphore;
-import java.util.concurrent.TimeUnit;
-import java.util.Arrays;
-import java.util.ArrayList;
-import java.util.List;
-
-import com.android.camera.CameraActivity;
-import com.android.camera.CaptureModule;
-import com.android.camera.ComboPreferences;
-import com.android.camera.MediaSaveService;
-import com.android.camera.Exif;
-import com.android.camera.SoundClips;
-import com.android.camera.exif.ExifInterface;
-import com.android.camera.PhotoModule.NamedImages;
-import com.android.camera.PhotoModule.NamedImages.NamedEntity;
-import com.android.camera.util.CameraUtil;
-import com.android.camera.util.PersistUtil;
-import org.codeaurora.snapcam.R;
-
-public class MultiCaptureModule implements MultiCamera {
-
- private static final String TAG = "SnapCam_MultiCaptureModule";
-
- private static final boolean DEBUG =
- (PersistUtil.getCamera2Debug() == PersistUtil.CAMERA2_DEBUG_DUMP_LOG) ||
- (PersistUtil.getCamera2Debug() == PersistUtil.CAMERA2_DEBUG_DUMP_ALL);
-
- private static final int WAIT_SURFACE = 0;
- private static final int OPEN_CAMERA = 1;
-
- private int mCameraListIndex = 0;
-
- private static final int MAX_NUM_CAM = 16;
-
- private CameraActivity mActivity;
- private MultiCameraUI mMultiCameraUI;
- private MultiCameraModule mMultiCameraModule;
- private CameraDevice[] mCameraDevices = new CameraDevice[MAX_NUM_CAM];
- private SharedPreferences mLocalSharedPref;
- private ArrayList<CameraCharacteristics> mCharacteristics;
-
- private ArrayList<String> mCameraIDList = new ArrayList<>();
- private CameraCaptureSession[] mCameraCaptureSessions = new CameraCaptureSession[MAX_NUM_CAM];
- private ImageReader[] mImageReaders = new ImageReader[MAX_NUM_CAM];
-
- private Size mPreviewSizes[] = new Size[MAX_NUM_CAM];
- private Size mPictureSizes[] = new Size[MAX_NUM_CAM];
- private int[][] mMaxPreviewSize = new int[MAX_NUM_CAM][];
-
- private Handler mCameraHandler;
- private HandlerThread mCameraThread;
-
- private static final CaptureRequest.Key<Byte> override_resource_cost_validation =
- new CaptureRequest.Key<>(
- "org.codeaurora.qcamera3.sessionParameters.overrideResourceCostValidation",
- byte.class);
-
- /**
- * {@link CaptureRequest.Builder} for the camera preview
- */
- private CaptureRequest.Builder[] mPreviewRequestBuilders = new CaptureRequest.Builder[MAX_NUM_CAM];
- /**
- * {@link CaptureRequest} generated by {@link #mPreviewRequestBuilder}
- */
- private CaptureRequest mPreviewRequest;
- /**
- * A {@link Semaphore} make sure the camera open callback happens first before closing the
- * camera.
- */
- private Semaphore mCameraOpenCloseLock = new Semaphore(3);
-
- /**
- * Orientation of the camera sensor
- */
- private int mSensorOrientation;
-
- private long mCaptureStartTime;
- private NamedImages mNamedImages;
- private ContentResolver mContentResolver;
- private SoundClips.Player mSoundPlayer;
-
- public MultiCaptureModule(CameraActivity activity, MultiCameraUI ui, MultiCameraModule module) {
- mActivity = activity;
- mMultiCameraUI = ui;
- mMultiCameraModule = module;
- mContentResolver = mActivity.getContentResolver();
- mNamedImages = new NamedImages();
- mLocalSharedPref = mActivity.getSharedPreferences(
- ComboPreferences.getLocalSharedPreferencesName(mActivity,
- "multi" + mMultiCameraModule.getCurrenCameraMode()), Context.MODE_PRIVATE);
- initCameraCharacteristics();
- startBackgroundThread();
- }
-
- @Override
- public void onResume(String[] ids) {
- for (String id : ids) {
- mCameraIDList.add(id);
- }
- // Set up sound playback for shutter button, video record and video stop
- if (mSoundPlayer == null) {
- mSoundPlayer = SoundClips.getPlayer(mActivity);
- }
- startBackgroundThread();
- initCameraCharacteristics();
- for (String id : ids) {
- int cameraId = Integer.parseInt(id);
- updatePictureSize(cameraId);
- int index = mCameraIDList.indexOf(id);
- mMultiCameraUI.setPreviewSize(index, mPreviewSizes[cameraId].getWidth(),
- mPreviewSizes[cameraId].getHeight());
- }
- }
-
- @Override
- public void onPause() {
- if (mSoundPlayer != null) {
- mSoundPlayer.release();
- mSoundPlayer = null;
- }
- closeCamera();
- if (mCameraIDList != null) {
- mCameraIDList.clear();
- }
- mCameraListIndex = 0;
- stopBackgroundThread();
- }
-
- @Override
- public boolean openCamera(String[] ids) {
- Message msg = Message.obtain();
- msg.what = OPEN_CAMERA;
- if (mCameraHandler != null) {
- mCameraHandler.sendMessage(msg);
- }
- return true;
- }
-
- @Override
- public void startPreview() {
-
- }
-
- @Override
- public void closeSession() {
-
- }
-
- @Override
- public void closeCamera() {
- Log.d(TAG, "closeCamera");
- /* 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() */
- try {
- // Close camera starting with AUX first
- for (int i = MAX_NUM_CAM - 1; i >= 0; i--) {
- if (null != mCameraDevices[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: " + mCameraDevices[i].getId());
- mCameraDevices[i].close();
- mCameraDevices[i] = null;
- mCameraCaptureSessions[i] = null;
- }
- if (null != mImageReaders[i]) {
- mImageReaders[i].close();
- mImageReaders[i] = null;
- }
- }
- } catch (InterruptedException e) {
- mCameraOpenCloseLock.release();
- throw new RuntimeException("Interrupted while trying to lock camera closing.", e);
- } catch (IllegalStateException e) {
- e.printStackTrace();
- } finally {
- mCameraOpenCloseLock.release();
- }
- }
-
- private void openCameraInSequence(String id) {
- CameraManager manager = (CameraManager) mActivity.getSystemService(Context.CAMERA_SERVICE);
- CameraCharacteristics characteristics = null;
- try {
- characteristics = manager.getCameraCharacteristics(id);
- } catch (CameraAccessException e) {
- e.printStackTrace();
- }
- mSensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
- Log.d(TAG, "openCameraInSequence " + id + ", mSensorOrientation :" + mSensorOrientation);
- try {
- if (!mCameraOpenCloseLock.tryAcquire(5000, TimeUnit.MILLISECONDS)) {
- Log.d(TAG, "Time out waiting to lock camera opening.");
- throw new RuntimeException("Time out waiting to lock camera opening");
- }
- manager.openCamera(id, mStateCallback, mMultiCameraModule.getMyCameraHandler());
- } catch (CameraAccessException e) {
- e.printStackTrace();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- manager = null;
- characteristics = null;
- }
-
- private void startBackgroundThread() {
- if (mCameraThread == null) {
- mCameraThread = new HandlerThread("CameraBackground");
- mCameraThread.start();
- }
- if (mCameraHandler == null) {
- mCameraHandler = new MyCameraHandler(mCameraThread.getLooper());
- }
- }
-
- private void stopBackgroundThread() {
- mCameraThread.quitSafely();
- try {
- mCameraThread.join();
- mCameraThread = null;
- mCameraHandler = null;
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
-
- private Size[] getSupportedOutputSize(int cameraId, Class cl) {
- StreamConfigurationMap map = mCharacteristics.get(cameraId).get(
- CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
- Size[] normal = map.getOutputSizes(cl);
- Size[] high = map.getHighResolutionOutputSizes(ImageFormat.PRIVATE);
- Size[] ret = new Size[normal.length + high.length];
- System.arraycopy(normal, 0, ret, 0, normal.length);
- System.arraycopy(high, 0, ret, normal.length, high.length);
- return ret;
- }
-
- private Size getOptimalPreviewSize(int id, Size pictureSize, Size[] prevSizes) {
- Point[] points = new Point[prevSizes.length];
-
- double targetRatio = (double) pictureSize.getWidth() / pictureSize.getHeight();
- int index = 0;
- int point_max[] = mMaxPreviewSize[id];
- int max_size = -1;
- if (point_max != null){
- max_size = point_max[0] * point_max[1];
- }
- for (Size s : prevSizes) {
- if (max_size != -1){
- int size = s.getWidth() * s.getHeight();
- if (s.getWidth() == s.getHeight()){
- if (s.getWidth() > Math.max(point_max[0],point_max[1]))
- continue;
- } else if (size > max_size || size == 0) {
- continue;
- }
- }
- points[index++] = new Point(s.getWidth(), s.getHeight());
- }
-
- int optimalPickIndex = CameraUtil.getOptimalPreviewSize(mActivity, points, targetRatio);
- return (optimalPickIndex == -1) ? null :
- new Size(points[optimalPickIndex].x,points[optimalPickIndex].y);
- }
-
- private class MyCameraHandler extends Handler {
-
- public MyCameraHandler(Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case WAIT_SURFACE:
- int id = msg.arg1;
- int index = mCameraIDList.indexOf(String.valueOf(id));
- Log.v(TAG, "WAIT_SURFACE id :" + id + ", index :" + index);
- if (index == -1) {
- break;
- }
- Surface surface = mMultiCameraUI.getSurfaceViewList().get(index)
- .getHolder().getSurface();
- if (surface.isValid()) {
- createCaptureSessions(id, surface);
- } else {
- mCameraHandler.sendMessageDelayed(msg, 200);
- Log.v(TAG, "Surface is invalid, wait more 200ms surfaceCreated");
- }
- break;
- case OPEN_CAMERA:
- if (mCameraListIndex == mCameraIDList.size()) {
- mCameraListIndex = 0;
- } else {
- String cameraId = mCameraIDList.get(mCameraListIndex);
- openCameraInSequence(cameraId);
- mCameraListIndex ++;
- Log.v(TAG, " OPEN_CAMERA cameraId :" + cameraId + ", mCameraListIndex :"
- + mCameraListIndex);
- }
- break;
- }
- }
- }
-
- private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {
-
- @Override
- public void onOpened(CameraDevice cameraDevice) {
- final int id = Integer.parseInt(cameraDevice.getId());
- mCameraDevices[id] = cameraDevice;
- Log.d(TAG, "onOpened " + id);
- mCameraOpenCloseLock.release();
- //updatePictureSize(id);
- createCameraPreviewSession(id);
- mActivity.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- mMultiCameraUI.onCameraOpened(id);
- }
- });
- }
-
- @Override
- public void onDisconnected(CameraDevice cameraDevice) {
- int id = Integer.parseInt(cameraDevice.getId());
- Log.d(TAG, "onDisconnected " + id);
- mCameraOpenCloseLock.release();
- mCameraDevices[id] = null;
- }
-
- @Override
- public void onError(CameraDevice cameraDevice, int error) {
- int id = Integer.parseInt(cameraDevice.getId());
- Log.e(TAG, "onError " + id + " " + error);
- mCameraOpenCloseLock.release();
-
- if (null != mActivity) {
- Toast.makeText(mActivity,"open camera error id =" + id,
- Toast.LENGTH_LONG).show();
- mActivity.finish();
- }
- }
-
- @Override
- public void onClosed(CameraDevice cameraDevice) {
- int id = Integer.parseInt(cameraDevice.getId());
- Log.d(TAG, "onClosed " + id);
- mCameraOpenCloseLock.release();
- mCameraDevices[id] = null;
- }
- };
-
- /**
- * Creates a new {@link CameraCaptureSession} for camera preview.
- */
- private void createCameraPreviewSession(int id) {
- // This is the output Surface we need to start preview.
- int index = mCameraIDList.indexOf(String.valueOf(id));
- Log.v(TAG, "createCameraPreviewSession id :" + id + ", index :" + index);
- Surface surface = mMultiCameraUI.getSurfaceViewList().get(index).getHolder().getSurface();
- if (surface.isValid()) {
- createCaptureSessions(id, surface);
- } else {
- Message msg = Message.obtain();
- msg.what = WAIT_SURFACE;
- msg.arg1 = id;
- mCameraHandler.sendMessageDelayed(msg, 200);
- Log.v(TAG, "Surface is invalid, wait 200ms surfaceCreated");
- }
- }
-
- private void createCaptureSessions(final int id, Surface surface) {
- try {
- // We set up a CaptureRequest.Builder with the output Surface.
- mPreviewRequestBuilders[id]
- = mCameraDevices[id].createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
- mPreviewRequestBuilders[id].addTarget(surface);
- mPreviewRequestBuilders[id].setTag(id);
- CameraCaptureSession.StateCallback stateCallback =
- new CameraCaptureSession.StateCallback() {
- @Override
- public void onConfigured(CameraCaptureSession cameraCaptureSession) {
- // The camera is already closed
- if (null == mCameraDevices[id]) {
- return;
- }
- Log.v(TAG, " CameraCaptureSession onConfigured id :" + id);
- // When the session is ready, we start displaying the preview.
- mCameraCaptureSessions[id] = cameraCaptureSession;
- try {
- // Auto focus should be continuous for camera preview.
- mPreviewRequestBuilders[id].set(CaptureRequest.CONTROL_AF_MODE,
- CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
- // Finally, we start displaying the camera preview.
- mCameraCaptureSessions[id].setRepeatingRequest(
- mPreviewRequestBuilders[id].build(),
- mCaptureCallback, mMultiCameraModule.getMyCameraHandler());
- } catch (CameraAccessException e) {
- e.printStackTrace();
- }
-
- Message msg = Message.obtain();
- msg.what = OPEN_CAMERA;
- if (mCameraHandler != null) {
- mCameraHandler.sendMessage(msg);
- }
- }
-
- @Override
- public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) {
- showToast("onConfigureFailed");
- }
- };
-
- try {
- final byte enable = 1;
- mPreviewRequestBuilders[id].set(override_resource_cost_validation, enable);
- Log.v(TAG, " set" + override_resource_cost_validation + " is 1");
- } catch (IllegalArgumentException e) {
- e.printStackTrace();
- }
-
- List<OutputConfiguration> outConfigurations = new ArrayList<>(2);
- outConfigurations.add(new OutputConfiguration(surface));
- outConfigurations.add(new OutputConfiguration(mImageReaders[id].getSurface()));
-
- SessionConfiguration sessionConfiguration = new SessionConfiguration(
- SessionConfiguration.SESSION_REGULAR, outConfigurations,
- new HandlerExecutor(mCameraHandler), stateCallback);
- sessionConfiguration.setSessionParameters(mPreviewRequestBuilders[id].build());
- mCameraDevices[id].createCaptureSession(sessionConfiguration);
- try {
- CaptureRequest captureRequest = sessionConfiguration.getSessionParameters();
- Log.v(TAG, " override_resource_cost_validation result: " +
- captureRequest.get(override_resource_cost_validation));
- } catch (IllegalArgumentException e) {
- e.printStackTrace();
- }
- } catch (CameraAccessException e) {
- e.printStackTrace();
- }
- }
-
- private class HandlerExecutor implements Executor {
- private final Handler ihandler;
-
- public HandlerExecutor(Handler handler) {
- ihandler = handler;
- }
-
- @Override
- public void execute(Runnable runCmd) {
- ihandler.post(runCmd);
- }
- }
-
- /**
- * A {@link CameraCaptureSession.CaptureCallback} that handles events related to JPEG capture.
- */
- private CameraCaptureSession.CaptureCallback mCaptureCallback
- = new CameraCaptureSession.CaptureCallback() {
-
- private void process(CaptureResult result) {
- Integer afState = result.get(CaptureResult.CONTROL_AF_STATE);
- Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
- if (DEBUG) {
- Log.v(TAG, "process afState :" + afState + ", aeState :" + aeState);
- }
- }
-
- @Override
- public void onCaptureProgressed(CameraCaptureSession session, CaptureRequest request,
- CaptureResult partialResult) {
- process(partialResult);
- }
-
- @Override
- public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request,
- TotalCaptureResult result) {
- process(result);
- }
-
- };
-
- private void updatePictureSize(int id) {
- String defaultSize = mActivity.getString(R.string.pref_multi_camera_picturesize_default);
- int index = mCameraIDList.indexOf(String.valueOf(id));
- String pictureSize = mLocalSharedPref.getString(
- MultiSettingsActivity.KEY_PICTURE_SIZES.get(index), defaultSize);
- mPictureSizes[id] = parsePictureSize(pictureSize);
- Log.v(TAG, " updatePictureSize size :" + mPictureSizes[id].getWidth() + "x"
- + mPictureSizes[id].getHeight());
- mImageReaders[id] = ImageReader.newInstance(mPictureSizes[id].getWidth(),
- mPictureSizes[id].getHeight(), ImageFormat.JPEG, /*maxImages*/2);
- mImageReaders[id].setOnImageAvailableListener(
- mOnImageAvailableListener, mMultiCameraModule.getMyCameraHandler());
-
- Size[] prevSizes = getSupportedOutputSize(id, SurfaceHolder.class);
- mPreviewSizes[id] = getOptimalPreviewSize(id, mPictureSizes[id], prevSizes);
- }
-
- private final ImageReader.OnImageAvailableListener mOnImageAvailableListener
- = new ImageReader.OnImageAvailableListener() {
-
- @Override
- public void onImageAvailable(ImageReader reader) {
- Log.v(TAG, "onImageAvailable ...");
- Image image = reader.acquireNextImage();
- mCaptureStartTime = System.currentTimeMillis();
- mNamedImages.nameNewImage(mCaptureStartTime);
- NamedEntity name = mNamedImages.getNextNameEntity();
- String title = (name == null) ? null : name.title;
- long date = (name == null) ? -1 : name.date;
-
- byte[] bytes = getJpegData(image);
- Log.i(TAG, "image format:" + image.getFormat());
- if (image.getFormat() == ImageFormat.RAW10) {
- mActivity.getMediaSaveService().addRawImage(bytes, title,
- "raw");
- image.close();
- } else if (image.getFormat() == ImageFormat.YUV_420_888) {
- Log.d(TAG, "YUV buffer received" );
- image.close();
- } else {
- int orientation = 0;
- ExifInterface exif = null;
- if (image.getFormat() != ImageFormat.HEIC) {
- exif = Exif.getExif(bytes);
- orientation = Exif.getOrientation(exif);
- }
-
- String pictureFormat = "jpeg";
- if (image.getFormat() == ImageFormat.HEIC) {
- pictureFormat = "heic";
- }
- mActivity.getMediaSaveService().addImage(bytes, title, date,
- null, image.getWidth(), image.getHeight(), orientation, exif,
- mOnMediaSavedListener, mContentResolver, pictureFormat);
-
- if (image.getFormat() != ImageFormat.HEIC) {
- mActivity.updateThumbnail(bytes);
- }
- image.close();
- mMultiCameraModule.updateTakingPicture();
- }
- }
-
- };
-
- /**
- * Shows a {@link Toast} on the UI thread.
- * @param text The message to show
- */
- private void showToast(final String text) {
- if (mActivity != null) {
- mActivity.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- Toast.makeText(mActivity, text, Toast.LENGTH_SHORT).show();
- }
- });
- }
- }
-
- private byte[] getJpegData(Image image) {
- ByteBuffer buffer = image.getPlanes()[0].getBuffer();
- byte[] bytes = new byte[buffer.remaining()];
- buffer.get(bytes);
- return bytes;
- }
-
- @Override
- public void onShutterButtonClick(String[] ids) {
- checkAndPlayShutterSound();
- mMultiCameraUI.enableShutter(false);
- boolean halZSLCheck = mLocalSharedPref.getBoolean(MultiSettingsActivity.KEY_HAL_ZAL, false);
- for (String id : ids) {
- try {
- int cameraId = Integer.parseInt(id);
- final CaptureRequest.Builder captureBuilder =
- mCameraDevices[cameraId].createCaptureRequest(
- CameraDevice.TEMPLATE_STILL_CAPTURE);
- captureBuilder.addTarget(mImageReaders[cameraId].getSurface());
- int index = mCameraIDList.indexOf(String.valueOf(cameraId));
- captureBuilder.addTarget(mMultiCameraUI.getSurfaceViewList().get(
- index).getHolder().getSurface());
- applySettingsForCapture(captureBuilder, cameraId);
- // Use the same AE and AF modes as the preview.
- captureBuilder.set(CaptureRequest.CONTROL_AF_MODE,
- CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
- if (halZSLCheck) {
- captureBuilder.set(CaptureRequest.CONTROL_ENABLE_ZSL, true);
- } else {
- captureBuilder.set(CaptureRequest.CONTROL_ENABLE_ZSL, false);
- }
- setAutoFlash(captureBuilder);
- // Orientation
- int rotation = mActivity.getWindowManager().getDefaultDisplay().getRotation();
- captureBuilder.set(CaptureRequest.JPEG_ORIENTATION,
- CameraUtil.getJpegRotation(cameraId, rotation));
- mCameraCaptureSessions[cameraId].capture(captureBuilder.build(),
- mCaptureStillCallback, mMultiCameraModule.getMyCameraHandler());
- Log.d(TAG, " cameraCaptureSession" + id + " captured ");
- } catch (CameraAccessException e) {
- e.printStackTrace();
- }
- }
- }
-
- @Override
- public void onVideoButtonClick(String[] ids) {
-
- }
-
- @Override
- public void onButtonPause(String[] ids) {
-
- }
-
- @Override
- public void onButtonContinue(String[] ids) {
-
- }
-
- @Override
- public void onOrientationChanged(int orientation) {
-
- }
-
- @Override
- public boolean isRecordingVideo() {
- return false;
- }
-
- private void checkAndPlayShutterSound() {
- boolean isPlay = mLocalSharedPref.getBoolean(MultiSettingsActivity.KEY_SHUTTER_SOUND, true);
- if (mSoundPlayer != null && isPlay) {
- mSoundPlayer.play(SoundClips.SHUTTER_CLICK);
- }
- }
-
- private MediaSaveService.OnMediaSavedListener mOnMediaSavedListener =
- new MediaSaveService.OnMediaSavedListener() {
- @Override
- public void onMediaSaved(Uri uri) {
- if (uri != null) {
- mActivity.notifyNewMedia(uri);
- }
- }
- };
-
- private CameraCaptureSession.CaptureCallback mCaptureStillCallback
- = new CameraCaptureSession.CaptureCallback() {
-
- @Override
- public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request,
- TotalCaptureResult result) {
- Log.d(TAG, " mCaptureCallback onCaptureCompleted ");
- mMultiCameraModule.getMainHandler().post(new Runnable() {
- @Override
- public void run() {
- Log.d(TAG, " enable Shutter " );
- mMultiCameraUI.enableShutter(true);
- }
- });
- }
-
- @Override
- public void onCaptureFailed(CameraCaptureSession session,
- CaptureRequest request,
- CaptureFailure result) {
- Log.d(TAG, " mCaptureCallback onCaptureFailed " );
- }
-
-
- @Override
- public void onCaptureSequenceCompleted(CameraCaptureSession session, int
- sequenceId, long frameNumber) {
- Log.d(TAG, " mCaptureCallback onCaptureSequenceCompleted ");
- }
- };
-
- private void applySettingsForCapture(CaptureRequest.Builder builder, int id) {
- builder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_IDLE);
- applyJpegQuality(builder);
- }
-
- private void applyJpegQuality(CaptureRequest.Builder request) {
- String defaultValue = mActivity.getString(R.string.pref_camera_jpegquality_default);
- String value = mLocalSharedPref.getString(MultiSettingsActivity.KEY_PICTURE_QUALITY,
- defaultValue);
- int jpegQuality = getQualityNumber(value);
- request.set(CaptureRequest.JPEG_QUALITY, (byte) jpegQuality);
- }
-
- private int getQualityNumber(String jpegQuality) {
- if (jpegQuality == null) {
- return 85;
- }
- try {
- int qualityPercentile = Integer.parseInt(jpegQuality);
- if (qualityPercentile >= 0 && qualityPercentile <= 100)
- return qualityPercentile;
- else
- return 85;
- } catch (NumberFormatException nfe) {
- //chosen quality is not a number, continue
- }
- int value = 0;
- switch (jpegQuality) {
- case "superfine":
- value = CameraProfile.QUALITY_HIGH;
- break;
- case "fine":
- value = CameraProfile.QUALITY_MEDIUM;
- break;
- case "normal":
- value = CameraProfile.QUALITY_LOW;
- break;
- default:
- return 85;
- }
- return CameraProfile.getJpegEncodingQualityParameter(value);
- }
-
- private Size parsePictureSize(String value) {
- int indexX = value.indexOf('x');
- int width = Integer.parseInt(value.substring(0, indexX));
- int height = Integer.parseInt(value.substring(indexX + 1));
- return new Size(width, height);
- }
-
- private void initCameraCharacteristics() {
- mCharacteristics = new ArrayList<>();
- CameraManager manager = (CameraManager) mActivity.getSystemService(Context.CAMERA_SERVICE);
- try {
- String[] cameraIdList = manager.getCameraIdList();
- Log.d(TAG, "cameraIdList size =" + cameraIdList.length);
- for (int i = 0; i < cameraIdList.length; i++) {
- String cameraId = cameraIdList[i];
- CameraCharacteristics characteristics
- = manager.getCameraCharacteristics(cameraId);
- mCharacteristics.add(i, characteristics);
- try {
- mMaxPreviewSize[i] = characteristics.get(CaptureModule.max_preview_size);
- } catch (IllegalArgumentException e) {
- Log.e(TAG, "getMaxPreviewSize no vendorTag max_preview_size:");
- }
- if (mMaxPreviewSize[i] != null) {
- Log.d(TAG, " init cameraId :" + cameraId + ", i :" + i +
- ", maxPreviewSize :" + mMaxPreviewSize[i][0]+ "x" +
- mMaxPreviewSize[i][1]);
- }
- }
- } catch (CameraAccessException e) {
- e.printStackTrace();
- }
- }
-
- private void setAutoFlash(CaptureRequest.Builder requestBuilder) {
- if (true) {
- requestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
- CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
- }
- }
+/* + * Copyright (c) 2019 The Linux Foundation. All rights reserved. + * Not a Contribution. + * + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.camera.multi; + +import android.content.Context; +import android.content.ContentResolver; +import android.content.SharedPreferences; +import android.graphics.ImageFormat; +import android.graphics.Point; +import android.hardware.camera2.CameraDevice; +import android.hardware.camera2.CameraManager; +import android.hardware.camera2.CameraAccessException; +import android.hardware.camera2.CameraCaptureSession; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CaptureFailure; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.CaptureResult; +import android.hardware.camera2.TotalCaptureResult; +import android.hardware.camera2.params.OutputConfiguration; +import android.hardware.camera2.params.SessionConfiguration; +import android.hardware.camera2.params.StreamConfigurationMap; +import android.media.CameraProfile; +import android.media.Image; +import android.media.ImageReader; +import android.net.Uri; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Looper; +import android.os.Message; +import android.util.Log; +import android.util.Size; +import android.view.Surface; +import android.view.SurfaceHolder; +import android.widget.Toast; + +import java.nio.ByteBuffer; +import java.util.concurrent.Executor; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; +import java.util.Arrays; +import java.util.ArrayList; +import java.util.List; + +import com.android.camera.CameraActivity; +import com.android.camera.CaptureModule; +import com.android.camera.ComboPreferences; +import com.android.camera.MediaSaveService; +import com.android.camera.Exif; +import com.android.camera.SoundClips; +import com.android.camera.exif.ExifInterface; +import com.android.camera.PhotoModule.NamedImages; +import com.android.camera.PhotoModule.NamedImages.NamedEntity; +import com.android.camera.util.CameraUtil; +import com.android.camera.util.PersistUtil; +import org.codeaurora.snapcam.R; + +public class MultiCaptureModule implements MultiCamera { + + private static final String TAG = "SnapCam_MultiCaptureModule"; + + private static final boolean DEBUG = + (PersistUtil.getCamera2Debug() == PersistUtil.CAMERA2_DEBUG_DUMP_LOG) || + (PersistUtil.getCamera2Debug() == PersistUtil.CAMERA2_DEBUG_DUMP_ALL); + + private static final int WAIT_SURFACE = 0; + private static final int OPEN_CAMERA = 1; + + private int mCameraListIndex = 0; + + private static final int MAX_NUM_CAM = 16; + + private CameraActivity mActivity; + private MultiCameraUI mMultiCameraUI; + private MultiCameraModule mMultiCameraModule; + private CameraDevice[] mCameraDevices = new CameraDevice[MAX_NUM_CAM]; + private SharedPreferences mLocalSharedPref; + private ArrayList<CameraCharacteristics> mCharacteristics; + + private ArrayList<String> mCameraIDList = new ArrayList<>(); + private CameraCaptureSession[] mCameraCaptureSessions = new CameraCaptureSession[MAX_NUM_CAM]; + private ImageReader[] mImageReaders = new ImageReader[MAX_NUM_CAM]; + + private Size mPreviewSizes[] = new Size[MAX_NUM_CAM]; + private Size mPictureSizes[] = new Size[MAX_NUM_CAM]; + private int[][] mMaxPreviewSize = new int[MAX_NUM_CAM][]; + + private Handler mCameraHandler; + private HandlerThread mCameraThread; + + private static final CaptureRequest.Key<Byte> override_resource_cost_validation = + new CaptureRequest.Key<>( + "org.codeaurora.qcamera3.sessionParameters.overrideResourceCostValidation", + byte.class); + + /** + * {@link CaptureRequest.Builder} for the camera preview + */ + private CaptureRequest.Builder[] mPreviewRequestBuilders = new CaptureRequest.Builder[MAX_NUM_CAM]; + /** + * {@link CaptureRequest} generated by {@link #mPreviewRequestBuilder} + */ + private CaptureRequest mPreviewRequest; + /** + * A {@link Semaphore} make sure the camera open callback happens first before closing the + * camera. + */ + private Semaphore mCameraOpenCloseLock = new Semaphore(3); + + /** + * Orientation of the camera sensor + */ + private int mSensorOrientation; + + private long mCaptureStartTime; + private NamedImages mNamedImages; + private ContentResolver mContentResolver; + private SoundClips.Player mSoundPlayer; + + public MultiCaptureModule(CameraActivity activity, MultiCameraUI ui, MultiCameraModule module) { + mActivity = activity; + mMultiCameraUI = ui; + mMultiCameraModule = module; + mContentResolver = mActivity.getContentResolver(); + mNamedImages = new NamedImages(); + mLocalSharedPref = mActivity.getSharedPreferences( + ComboPreferences.getLocalSharedPreferencesName(mActivity, + "multi" + mMultiCameraModule.getCurrenCameraMode()), Context.MODE_PRIVATE); + initCameraCharacteristics(); + startBackgroundThread(); + } + + @Override + public void onResume(String[] ids) { + for (String id : ids) { + mCameraIDList.add(id); + } + // Set up sound playback for shutter button, video record and video stop + if (mSoundPlayer == null) { + mSoundPlayer = SoundClips.getPlayer(mActivity); + } + startBackgroundThread(); + initCameraCharacteristics(); + for (String id : ids) { + int cameraId = Integer.parseInt(id); + updatePictureSize(cameraId); + int index = mCameraIDList.indexOf(id); + mMultiCameraUI.setPreviewSize(index, mPreviewSizes[cameraId].getWidth(), + mPreviewSizes[cameraId].getHeight()); + } + } + + @Override + public void onPause() { + if (mSoundPlayer != null) { + mSoundPlayer.release(); + mSoundPlayer = null; + } + closeCamera(); + if (mCameraIDList != null) { + mCameraIDList.clear(); + } + mCameraListIndex = 0; + stopBackgroundThread(); + } + + @Override + public boolean openCamera(String[] ids) { + Message msg = Message.obtain(); + msg.what = OPEN_CAMERA; + if (mCameraHandler != null) { + mCameraHandler.sendMessage(msg); + } + return true; + } + + @Override + public void startPreview() { + + } + + @Override + public void closeSession() { + + } + + @Override + public void closeCamera() { + Log.d(TAG, "closeCamera"); + /* 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() */ + try { + // Close camera starting with AUX first + for (int i = MAX_NUM_CAM - 1; i >= 0; i--) { + if (null != mCameraDevices[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: " + mCameraDevices[i].getId()); + mCameraDevices[i].close(); + mCameraDevices[i] = null; + mCameraCaptureSessions[i] = null; + } + if (null != mImageReaders[i]) { + mImageReaders[i].close(); + mImageReaders[i] = null; + } + } + } catch (InterruptedException e) { + mCameraOpenCloseLock.release(); + throw new RuntimeException("Interrupted while trying to lock camera closing.", e); + } catch (IllegalStateException e) { + e.printStackTrace(); + } finally { + mCameraOpenCloseLock.release(); + } + } + + private void openCameraInSequence(String id) { + CameraManager manager = (CameraManager) mActivity.getSystemService(Context.CAMERA_SERVICE); + CameraCharacteristics characteristics = null; + try { + characteristics = manager.getCameraCharacteristics(id); + } catch (CameraAccessException e) { + e.printStackTrace(); + } + mSensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION); + Log.d(TAG, "openCameraInSequence " + id + ", mSensorOrientation :" + mSensorOrientation); + try { + if (!mCameraOpenCloseLock.tryAcquire(5000, TimeUnit.MILLISECONDS)) { + Log.d(TAG, "Time out waiting to lock camera opening."); + throw new RuntimeException("Time out waiting to lock camera opening"); + } + manager.openCamera(id, mStateCallback, mMultiCameraModule.getMyCameraHandler()); + } catch (CameraAccessException e) { + e.printStackTrace(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + manager = null; + characteristics = null; + } + + private void startBackgroundThread() { + if (mCameraThread == null) { + mCameraThread = new HandlerThread("CameraBackground"); + mCameraThread.start(); + } + if (mCameraHandler == null) { + mCameraHandler = new MyCameraHandler(mCameraThread.getLooper()); + } + } + + private void stopBackgroundThread() { + mCameraThread.quitSafely(); + try { + mCameraThread.join(); + mCameraThread = null; + mCameraHandler = null; + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + private Size[] getSupportedOutputSize(int cameraId, Class cl) { + StreamConfigurationMap map = mCharacteristics.get(cameraId).get( + CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); + Size[] normal = map.getOutputSizes(cl); + Size[] high = map.getHighResolutionOutputSizes(ImageFormat.PRIVATE); + Size[] ret = new Size[normal.length + high.length]; + System.arraycopy(normal, 0, ret, 0, normal.length); + System.arraycopy(high, 0, ret, normal.length, high.length); + return ret; + } + + private Size getOptimalPreviewSize(int id, Size pictureSize, Size[] prevSizes) { + Point[] points = new Point[prevSizes.length]; + + double targetRatio = (double) pictureSize.getWidth() / pictureSize.getHeight(); + int index = 0; + int point_max[] = mMaxPreviewSize[id]; + int max_size = -1; + if (point_max != null){ + max_size = point_max[0] * point_max[1]; + } + for (Size s : prevSizes) { + if (max_size != -1){ + int size = s.getWidth() * s.getHeight(); + if (s.getWidth() == s.getHeight()){ + if (s.getWidth() > Math.max(point_max[0],point_max[1])) + continue; + } else if (size > max_size || size == 0) { + continue; + } + } + points[index++] = new Point(s.getWidth(), s.getHeight()); + } + + int optimalPickIndex = CameraUtil.getOptimalPreviewSize(mActivity, points, targetRatio); + return (optimalPickIndex == -1) ? null : + new Size(points[optimalPickIndex].x,points[optimalPickIndex].y); + } + + private class MyCameraHandler extends Handler { + + public MyCameraHandler(Looper looper) { + super(looper); + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case WAIT_SURFACE: + int id = msg.arg1; + int index = mCameraIDList.indexOf(String.valueOf(id)); + Log.v(TAG, "WAIT_SURFACE id :" + id + ", index :" + index); + if (index == -1) { + break; + } + Surface surface = mMultiCameraUI.getSurfaceViewList().get(index) + .getHolder().getSurface(); + if (surface.isValid()) { + createCaptureSessions(id, surface); + } else { + mCameraHandler.sendMessageDelayed(msg, 200); + Log.v(TAG, "Surface is invalid, wait more 200ms surfaceCreated"); + } + break; + case OPEN_CAMERA: + if (mCameraListIndex == mCameraIDList.size()) { + mCameraListIndex = 0; + } else { + String cameraId = mCameraIDList.get(mCameraListIndex); + openCameraInSequence(cameraId); + mCameraListIndex ++; + Log.v(TAG, " OPEN_CAMERA cameraId :" + cameraId + ", mCameraListIndex :" + + mCameraListIndex); + } + break; + } + } + } + + private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() { + + @Override + public void onOpened(CameraDevice cameraDevice) { + final int id = Integer.parseInt(cameraDevice.getId()); + mCameraDevices[id] = cameraDevice; + Log.d(TAG, "onOpened " + id); + mCameraOpenCloseLock.release(); + //updatePictureSize(id); + createCameraPreviewSession(id); + mActivity.runOnUiThread(new Runnable() { + @Override + public void run() { + mMultiCameraUI.onCameraOpened(id); + } + }); + } + + @Override + public void onDisconnected(CameraDevice cameraDevice) { + int id = Integer.parseInt(cameraDevice.getId()); + Log.d(TAG, "onDisconnected " + id); + mCameraOpenCloseLock.release(); + mCameraDevices[id] = null; + } + + @Override + public void onError(CameraDevice cameraDevice, int error) { + int id = Integer.parseInt(cameraDevice.getId()); + Log.e(TAG, "onError " + id + " " + error); + mCameraOpenCloseLock.release(); + + if (null != mActivity) { + Toast.makeText(mActivity,"open camera error id =" + id, + Toast.LENGTH_LONG).show(); + mActivity.finish(); + } + } + + @Override + public void onClosed(CameraDevice cameraDevice) { + int id = Integer.parseInt(cameraDevice.getId()); + Log.d(TAG, "onClosed " + id); + mCameraOpenCloseLock.release(); + mCameraDevices[id] = null; + } + }; + + /** + * Creates a new {@link CameraCaptureSession} for camera preview. + */ + private void createCameraPreviewSession(int id) { + // This is the output Surface we need to start preview. + int index = mCameraIDList.indexOf(String.valueOf(id)); + Log.v(TAG, "createCameraPreviewSession id :" + id + ", index :" + index); + Surface surface = mMultiCameraUI.getSurfaceViewList().get(index).getHolder().getSurface(); + if (surface.isValid()) { + createCaptureSessions(id, surface); + } else { + Message msg = Message.obtain(); + msg.what = WAIT_SURFACE; + msg.arg1 = id; + mCameraHandler.sendMessageDelayed(msg, 200); + Log.v(TAG, "Surface is invalid, wait 200ms surfaceCreated"); + } + } + + private void createCaptureSessions(final int id, Surface surface) { + try { + // We set up a CaptureRequest.Builder with the output Surface. + mPreviewRequestBuilders[id] + = mCameraDevices[id].createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); + mPreviewRequestBuilders[id].addTarget(surface); + mPreviewRequestBuilders[id].setTag(id); + CameraCaptureSession.StateCallback stateCallback = + new CameraCaptureSession.StateCallback() { + @Override + public void onConfigured(CameraCaptureSession cameraCaptureSession) { + // The camera is already closed + if (null == mCameraDevices[id]) { + return; + } + Log.v(TAG, " CameraCaptureSession onConfigured id :" + id); + // When the session is ready, we start displaying the preview. + mCameraCaptureSessions[id] = cameraCaptureSession; + try { + // Auto focus should be continuous for camera preview. + mPreviewRequestBuilders[id].set(CaptureRequest.CONTROL_AF_MODE, + CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); + // Finally, we start displaying the camera preview. + mCameraCaptureSessions[id].setRepeatingRequest( + mPreviewRequestBuilders[id].build(), + mCaptureCallback, mMultiCameraModule.getMyCameraHandler()); + } catch (CameraAccessException e) { + e.printStackTrace(); + } + + Message msg = Message.obtain(); + msg.what = OPEN_CAMERA; + if (mCameraHandler != null) { + mCameraHandler.sendMessage(msg); + } + } + + @Override + public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) { + showToast("onConfigureFailed"); + } + }; + + try { + final byte enable = 1; + mPreviewRequestBuilders[id].set(override_resource_cost_validation, enable); + Log.v(TAG, " set" + override_resource_cost_validation + " is 1"); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } + + List<OutputConfiguration> outConfigurations = new ArrayList<>(2); + outConfigurations.add(new OutputConfiguration(surface)); + outConfigurations.add(new OutputConfiguration(mImageReaders[id].getSurface())); + + SessionConfiguration sessionConfiguration = new SessionConfiguration( + SessionConfiguration.SESSION_REGULAR, outConfigurations, + new HandlerExecutor(mCameraHandler), stateCallback); + sessionConfiguration.setSessionParameters(mPreviewRequestBuilders[id].build()); + mCameraDevices[id].createCaptureSession(sessionConfiguration); + try { + CaptureRequest captureRequest = sessionConfiguration.getSessionParameters(); + Log.v(TAG, " override_resource_cost_validation result: " + + captureRequest.get(override_resource_cost_validation)); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } + } catch (CameraAccessException e) { + e.printStackTrace(); + } + } + + private class HandlerExecutor implements Executor { + private final Handler ihandler; + + public HandlerExecutor(Handler handler) { + ihandler = handler; + } + + @Override + public void execute(Runnable runCmd) { + ihandler.post(runCmd); + } + } + + /** + * A {@link CameraCaptureSession.CaptureCallback} that handles events related to JPEG capture. + */ + private CameraCaptureSession.CaptureCallback mCaptureCallback + = new CameraCaptureSession.CaptureCallback() { + + private void process(CaptureResult result) { + Integer afState = result.get(CaptureResult.CONTROL_AF_STATE); + Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE); + if (DEBUG) { + Log.v(TAG, "process afState :" + afState + ", aeState :" + aeState); + } + } + + @Override + public void onCaptureProgressed(CameraCaptureSession session, CaptureRequest request, + CaptureResult partialResult) { + process(partialResult); + } + + @Override + public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, + TotalCaptureResult result) { + process(result); + } + + }; + + private void updatePictureSize(int id) { + String defaultSize = mActivity.getString(R.string.pref_multi_camera_picturesize_default); + int index = mCameraIDList.indexOf(String.valueOf(id)); + String pictureSize = mLocalSharedPref.getString( + MultiSettingsActivity.KEY_PICTURE_SIZES.get(index), defaultSize); + mPictureSizes[id] = parsePictureSize(pictureSize); + Log.v(TAG, " updatePictureSize size :" + mPictureSizes[id].getWidth() + "x" + + mPictureSizes[id].getHeight()); + mImageReaders[id] = ImageReader.newInstance(mPictureSizes[id].getWidth(), + mPictureSizes[id].getHeight(), ImageFormat.JPEG, /*maxImages*/2); + mImageReaders[id].setOnImageAvailableListener( + mOnImageAvailableListener, mMultiCameraModule.getMyCameraHandler()); + + Size[] prevSizes = getSupportedOutputSize(id, SurfaceHolder.class); + mPreviewSizes[id] = getOptimalPreviewSize(id, mPictureSizes[id], prevSizes); + } + + private final ImageReader.OnImageAvailableListener mOnImageAvailableListener + = new ImageReader.OnImageAvailableListener() { + + @Override + public void onImageAvailable(ImageReader reader) { + Log.v(TAG, "onImageAvailable ..."); + Image image = reader.acquireNextImage(); + mCaptureStartTime = System.currentTimeMillis(); + mNamedImages.nameNewImage(mCaptureStartTime); + NamedEntity name = mNamedImages.getNextNameEntity(); + String title = (name == null) ? null : name.title; + long date = (name == null) ? -1 : name.date; + + byte[] bytes = getJpegData(image); + Log.i(TAG, "image format:" + image.getFormat()); + if (image.getFormat() == ImageFormat.RAW10) { + mActivity.getMediaSaveService().addRawImage(bytes, title, + "raw"); + image.close(); + } else if (image.getFormat() == ImageFormat.YUV_420_888) { + Log.d(TAG, "YUV buffer received" ); + image.close(); + } else { + int orientation = 0; + ExifInterface exif = null; + if (image.getFormat() != ImageFormat.HEIC) { + exif = Exif.getExif(bytes); + orientation = Exif.getOrientation(exif); + } + + String pictureFormat = "jpeg"; + if (image.getFormat() == ImageFormat.HEIC) { + pictureFormat = "heic"; + } + mActivity.getMediaSaveService().addImage(bytes, title, date, + null, image.getWidth(), image.getHeight(), orientation, exif, + mOnMediaSavedListener, mContentResolver, pictureFormat); + + if (image.getFormat() != ImageFormat.HEIC) { + mActivity.updateThumbnail(bytes); + } + image.close(); + mMultiCameraModule.updateTakingPicture(); + } + } + + }; + + /** + * Shows a {@link Toast} on the UI thread. + * @param text The message to show + */ + private void showToast(final String text) { + if (mActivity != null) { + mActivity.runOnUiThread(new Runnable() { + @Override + public void run() { + Toast.makeText(mActivity, text, Toast.LENGTH_SHORT).show(); + } + }); + } + } + + private byte[] getJpegData(Image image) { + ByteBuffer buffer = image.getPlanes()[0].getBuffer(); + byte[] bytes = new byte[buffer.remaining()]; + buffer.get(bytes); + return bytes; + } + + @Override + public void onShutterButtonClick(String[] ids) { + checkAndPlayShutterSound(); + mMultiCameraUI.enableShutter(false); + boolean halZSLCheck = mLocalSharedPref.getBoolean(MultiSettingsActivity.KEY_HAL_ZAL, false); + for (String id : ids) { + try { + int cameraId = Integer.parseInt(id); + final CaptureRequest.Builder captureBuilder = + mCameraDevices[cameraId].createCaptureRequest( + CameraDevice.TEMPLATE_STILL_CAPTURE); + captureBuilder.addTarget(mImageReaders[cameraId].getSurface()); + int index = mCameraIDList.indexOf(String.valueOf(cameraId)); + captureBuilder.addTarget(mMultiCameraUI.getSurfaceViewList().get( + index).getHolder().getSurface()); + applySettingsForCapture(captureBuilder, cameraId); + // Use the same AE and AF modes as the preview. + captureBuilder.set(CaptureRequest.CONTROL_AF_MODE, + CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); + if (halZSLCheck) { + captureBuilder.set(CaptureRequest.CONTROL_ENABLE_ZSL, true); + } else { + captureBuilder.set(CaptureRequest.CONTROL_ENABLE_ZSL, false); + } + setAutoFlash(captureBuilder); + // Orientation + int rotation = mActivity.getWindowManager().getDefaultDisplay().getRotation(); + captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, + CameraUtil.getJpegRotation(cameraId, rotation)); + mCameraCaptureSessions[cameraId].capture(captureBuilder.build(), + mCaptureStillCallback, mMultiCameraModule.getMyCameraHandler()); + Log.d(TAG, " cameraCaptureSession" + id + " captured "); + } catch (CameraAccessException e) { + e.printStackTrace(); + } + } + } + + @Override + public void onVideoButtonClick(String[] ids) { + + } + + @Override + public void onButtonPause(String[] ids) { + + } + + @Override + public void onButtonContinue(String[] ids) { + + } + + @Override + public void onOrientationChanged(int orientation) { + + } + + @Override + public boolean isRecordingVideo() { + return false; + } + + private void checkAndPlayShutterSound() { + boolean isPlay = mLocalSharedPref.getBoolean(MultiSettingsActivity.KEY_SHUTTER_SOUND, true); + if (mSoundPlayer != null && isPlay) { + mSoundPlayer.play(SoundClips.SHUTTER_CLICK); + } + } + + private MediaSaveService.OnMediaSavedListener mOnMediaSavedListener = + new MediaSaveService.OnMediaSavedListener() { + @Override + public void onMediaSaved(Uri uri) { + if (uri != null) { + mActivity.notifyNewMedia(uri); + } + } + }; + + private CameraCaptureSession.CaptureCallback mCaptureStillCallback + = new CameraCaptureSession.CaptureCallback() { + + @Override + public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, + TotalCaptureResult result) { + Log.d(TAG, " mCaptureCallback onCaptureCompleted "); + mMultiCameraModule.getMainHandler().post(new Runnable() { + @Override + public void run() { + Log.d(TAG, " enable Shutter " ); + mMultiCameraUI.enableShutter(true); + } + }); + } + + @Override + public void onCaptureFailed(CameraCaptureSession session, + CaptureRequest request, + CaptureFailure result) { + Log.d(TAG, " mCaptureCallback onCaptureFailed " ); + } + + + @Override + public void onCaptureSequenceCompleted(CameraCaptureSession session, int + sequenceId, long frameNumber) { + Log.d(TAG, " mCaptureCallback onCaptureSequenceCompleted "); + } + }; + + private void applySettingsForCapture(CaptureRequest.Builder builder, int id) { + builder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_IDLE); + applyJpegQuality(builder); + } + + private void applyJpegQuality(CaptureRequest.Builder request) { + String defaultValue = mActivity.getString(R.string.pref_camera_jpegquality_default); + String value = mLocalSharedPref.getString(MultiSettingsActivity.KEY_PICTURE_QUALITY, + defaultValue); + int jpegQuality = getQualityNumber(value); + request.set(CaptureRequest.JPEG_QUALITY, (byte) jpegQuality); + } + + private int getQualityNumber(String jpegQuality) { + if (jpegQuality == null) { + return 85; + } + try { + int qualityPercentile = Integer.parseInt(jpegQuality); + if (qualityPercentile >= 0 && qualityPercentile <= 100) + return qualityPercentile; + else + return 85; + } catch (NumberFormatException nfe) { + //chosen quality is not a number, continue + } + int value = 0; + switch (jpegQuality) { + case "superfine": + value = CameraProfile.QUALITY_HIGH; + break; + case "fine": + value = CameraProfile.QUALITY_MEDIUM; + break; + case "normal": + value = CameraProfile.QUALITY_LOW; + break; + default: + return 85; + } + return CameraProfile.getJpegEncodingQualityParameter(value); + } + + private Size parsePictureSize(String value) { + int indexX = value.indexOf('x'); + int width = Integer.parseInt(value.substring(0, indexX)); + int height = Integer.parseInt(value.substring(indexX + 1)); + return new Size(width, height); + } + + private void initCameraCharacteristics() { + mCharacteristics = new ArrayList<>(); + CameraManager manager = (CameraManager) mActivity.getSystemService(Context.CAMERA_SERVICE); + try { + String[] cameraIdList = manager.getCameraIdList(); + Log.d(TAG, "cameraIdList size =" + cameraIdList.length); + for (int i = 0; i < cameraIdList.length; i++) { + String cameraId = cameraIdList[i]; + CameraCharacteristics characteristics + = manager.getCameraCharacteristics(cameraId); + mCharacteristics.add(i, characteristics); + try { + mMaxPreviewSize[i] = characteristics.get(CaptureModule.max_preview_size); + } catch (IllegalArgumentException e) { + Log.e(TAG, "getMaxPreviewSize no vendorTag max_preview_size:"); + } + if (mMaxPreviewSize[i] != null) { + Log.d(TAG, " init cameraId :" + cameraId + ", i :" + i + + ", maxPreviewSize :" + mMaxPreviewSize[i][0]+ "x" + + mMaxPreviewSize[i][1]); + } + } + } catch (CameraAccessException e) { + e.printStackTrace(); + } + } + + private void setAutoFlash(CaptureRequest.Builder requestBuilder) { + if (true) { + requestBuilder.set(CaptureRequest.CONTROL_AE_MODE, + CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH); + } + } }
\ No newline at end of file |
