輕鬆玩轉Camera,使用CameraView來拍照,修改CameraView 實現自定義拍照解析度
阿新 • • 發佈:2019-01-02
前言
對於拍照功能來說,Android 由於版本不同可以使用不同的書寫方式,光API就有Camera和Camera2兩套,其餘的區別還有
API Level | Camera API | Preview View |
---|---|---|
9-13 | Camera1 | SurfaceView |
14-20 | Camera1 | TextureView |
21-23 | Camera2 | TextureView |
24 | Camera2 | SurfaceView |
雖然Android能夠向下相容,但是誰不想使用更新的API和技術,另一方面,Camera API呼叫起來也不方便,所以對Camera呼叫的封裝勢在必行。
github上面有一些,但是使用時經常出現相容性,適配等問題,直到發現了Google自己封裝的CameraView,地址:
https://github.com/google/cameraview
但是使用的時候,你會發現一個問題我怎麼設定我拍出照片的解析度?
官方提供了Ratio,寬高比的屬性,但是沒有提供設定解析度的API和屬性。
下面我就會介紹怎麼修改程式碼來實現這個,當然你要是不想自己改,可以直接使用我修改好的,地址:
https://github.com/DiskyZhs/CameraView
直接作為lib匯入就可以了。
修改原始碼
檢視原始碼結構
你會發現找到CameraViewImp這個CameraView的介面封裝,這裡面封裝了CameraView對外的介面封裝,如下
abstract class CameraViewImpl {
protected final Callback mCallback;
protected final PreviewImpl mPreview;
CameraViewImpl(Callback callback, PreviewImpl preview) {
mCallback = callback;
mPreview = preview;
}
View getView() {
return mPreview.getView();
}
/**
* @return {@code true} if the implementation was able to start the camera session.
*/
abstract boolean start();
abstract void stop();
abstract boolean isCameraOpened();
abstract void setFacing(int facing);
abstract int getFacing();
abstract Set<AspectRatio> getSupportedAspectRatios();
/**
* @return {@code true} if the aspect ratio was changed.
*/
abstract boolean setAspectRatio(AspectRatio ratio);
abstract AspectRatio getAspectRatio();
abstract void setAutoFocus(boolean autoFocus);
abstract boolean getAutoFocus();
abstract void setFlash(int flash);
abstract int getFlash();
abstract void takePicture();
abstract void setDisplayOrientation(int displayOrientation);
interface Callback {
void onCameraOpened();
void onCameraClosed();
void onPictureTaken(byte[] data);
}
}
我們新增介面
/**
* 設定拍攝出來照片的解析度
*/
abstract void setPictureSize(int width,int height);
然後修改Camer1.java檔案,實現Camera1 API下面介面的實現 ,如下:
/*
* Copyright (C) 2016 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.google.android.cameraview;
import android.annotation.SuppressLint;
import android.graphics.SurfaceTexture;
import android.hardware.Camera;
import android.os.Build;
import android.support.v4.util.SparseArrayCompat;
import android.view.SurfaceHolder;
import java.io.IOException;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import java.util.concurrent.atomic.AtomicBoolean;
@SuppressWarnings("deprecation")
class Camera1 extends CameraViewImpl {
private static final int INVALID_CAMERA_ID = -1;
private static final SparseArrayCompat<String> FLASH_MODES = new SparseArrayCompat<>();
static {
FLASH_MODES.put(Constants.FLASH_OFF, Camera.Parameters.FLASH_MODE_OFF);
FLASH_MODES.put(Constants.FLASH_ON, Camera.Parameters.FLASH_MODE_ON);
FLASH_MODES.put(Constants.FLASH_TORCH, Camera.Parameters.FLASH_MODE_TORCH);
FLASH_MODES.put(Constants.FLASH_AUTO, Camera.Parameters.FLASH_MODE_AUTO);
FLASH_MODES.put(Constants.FLASH_RED_EYE, Camera.Parameters.FLASH_MODE_RED_EYE);
}
private int mCameraId;
private final AtomicBoolean isPictureCaptureInProgress = new AtomicBoolean(false);
Camera mCamera;
private Camera.Parameters mCameraParameters;
private final Camera.CameraInfo mCameraInfo = new Camera.CameraInfo();
private final SizeMap mPreviewSizes = new SizeMap();
private final SizeMap mPictureSizes = new SizeMap();
private AspectRatio mAspectRatio;
private boolean mShowingPreview;
private boolean mAutoFocus;
private int mFacing;
private int mFlash;
private int mDisplayOrientation;
/**
* 自定義的設定
*/
private Size mSettingPictureSize;
/**
* 自定義的預覽的解析度
*/
private Size mSettingPreviewSize;
Camera1(Callback callback, PreviewImpl preview) {
super(callback, preview);
preview.setCallback(new PreviewImpl.Callback() {
@Override
public void onSurfaceChanged() {
if (mCamera != null) {
setUpPreview();
adjustCameraParameters();
}
}
});
}
@Override
boolean start() {
chooseCamera();
openCamera();
if (mPreview.isReady()) {
setUpPreview();
}
mShowingPreview = true;
mCamera.startPreview();
return true;
}
@Override
void stop() {
if (mCamera != null) {
mCamera.stopPreview();
}
mShowingPreview = false;
releaseCamera();
}
// Suppresses Camera#setPreviewTexture
@SuppressLint("NewApi")
void setUpPreview() {
try {
if (mPreview.getOutputClass() == SurfaceHolder.class) {
final boolean needsToStopPreview = mShowingPreview && Build.VERSION.SDK_INT < 14;
if (needsToStopPreview) {
mCamera.stopPreview();
}
mCamera.setPreviewDisplay(mPreview.getSurfaceHolder());
if (needsToStopPreview) {
mCamera.startPreview();
}
} else {
mCamera.setPreviewTexture((SurfaceTexture) mPreview.getSurfaceTexture());
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
boolean isCameraOpened() {
return mCamera != null;
}
@Override
void setFacing(int facing) {
if (mFacing == facing) {
return;
}
mFacing = facing;
if (isCameraOpened()) {
stop();
start();
}
}
@Override
int getFacing() {
return mFacing;
}
@Override
Set<AspectRatio> getSupportedAspectRatios() {
return mPreviewSizes.ratios();
}
@Override
boolean setAspectRatio(AspectRatio ratio) {
if (mAspectRatio == null || !isCameraOpened()) {
// Handle this later when camera is opened
mAspectRatio = ratio;
return true;
} else if (!mAspectRatio.equals(ratio)) {
final Set<Size> sizes = mPreviewSizes.sizes(ratio);
if (sizes == null) {
throw new UnsupportedOperationException(ratio + " is not supported");
} else {
mAspectRatio = ratio;
adjustCameraParameters();
return true;
}
}
return false;
}
@Override
AspectRatio getAspectRatio() {
return mAspectRatio;
}
@Override
void setAutoFocus(boolean autoFocus) {
if (mAutoFocus == autoFocus) {
return;
}
if (setAutoFocusInternal(autoFocus)) {
mCamera.setParameters(mCameraParameters);
}
}
@Override
boolean getAutoFocus() {
if (!isCameraOpened()) {
return mAutoFocus;
}
String focusMode = mCameraParameters.getFocusMode();
return focusMode != null && focusMode.contains("continuous");
}
@Override
void setFlash(int flash) {
if (flash == mFlash) {
return;
}
if (setFlashInternal(flash)) {
mCamera.setParameters(mCameraParameters);
}
}
@Override
int getFlash() {
return mFlash;
}
@Override
void takePicture() {
if (!isCameraOpened()) {
throw new IllegalStateException(
"Camera is not ready. Call start() before takePicture().");
}
if (getAutoFocus()) {
mCamera.cancelAutoFocus();
mCamera.autoFocus(new Camera.AutoFocusCallback() {
@Override
public void onAutoFocus(boolean success, Camera camera) {
takePictureInternal();
}
});
} else {
takePictureInternal();
}
}
void takePictureInternal() {
if (!isPictureCaptureInProgress.getAndSet(true)) {
mCamera.takePicture(null, null, null, new Camera.PictureCallback() {
@Override
public void onPictureTaken(byte[] data, Camera camera) {
isPictureCaptureInProgress.set(false);
mCallback.onPictureTaken(data);
camera.cancelAutoFocus();
camera.startPreview();
}
});
}
}
@Override
void setDisplayOrientation(int displayOrientation) {
if (mDisplayOrientation == displayOrientation) {
return;
}
mDisplayOrientation = displayOrientation;
if (isCameraOpened()) {
int cameraRotation = calcCameraRotation(displayOrientation);
mCameraParameters.setRotation(cameraRotation);
mCamera.setParameters(mCameraParameters);
final boolean needsToStopPreview = mShowingPreview && Build.VERSION.SDK_INT < 14;
if (needsToStopPreview) {
mCamera.stopPreview();
}
mCamera.setDisplayOrientation(cameraRotation);
if (needsToStopPreview) {
mCamera.startPreview();
}
}
}
/**
* 設定拍攝出來照片的解析度
*
* @param width
* @param height
*/
@Override
void setPictureSize(int width, int height) {
mSettingPictureSize = new Size(width, height);
adjustCameraParameters();
}
/**
* This rewrites {@link #mCameraId} and {@link #mCameraInfo}.
*/
private void chooseCamera() {
for (int i = 0, count = Camera.getNumberOfCameras(); i < count; i++) {
Camera.getCameraInfo(i, mCameraInfo);
if (mCameraInfo.facing == mFacing) {
mCameraId = i;
return;
}
}
mCameraId = INVALID_CAMERA_ID;
}
private void openCamera() {
if (mCamera != null) {
releaseCamera();
}
mCamera = Camera.open(mCameraId);
mCameraParameters = mCamera.getParameters();
// Supported preview sizes
mPreviewSizes.clear();
for (Camera.Size size : mCameraParameters.getSupportedPreviewSizes()) {
mPreviewSizes.add(new Size(size.width, size.height));
}
// Supported picture sizes;
mPictureSizes.clear();
for (Camera.Size size : mCameraParameters.getSupportedPictureSizes()) {
mPictureSizes.add(new Size(size.width, size.height));
}
// AspectRatio
if (mAspectRatio == null) {
mAspectRatio = Constants.DEFAULT_ASPECT_RATIO;
}
adjustCameraParameters();
mCamera.setDisplayOrientation(calcCameraRotation(mDisplayOrientation));
mCallback.onCameraOpened();
}
private AspectRatio chooseAspectRatio() {
AspectRatio r = null;
for (AspectRatio ratio : mPreviewSizes.ratios()) {
r = ratio;
if (ratio.equals(Constants.DEFAULT_ASPECT_RATIO)) {
return ratio;
}
}
return r;
}
/**
* 設定相機引數
*/
void adjustCameraParameters() {
SortedSet<Size> sizes = mPreviewSizes.sizes(mAspectRatio);
//根據設定的寬高比來篩選出支援的PreviewSize
if (sizes == null) { // Not supported
mAspectRatio = chooseAspectRatio();
sizes = mPreviewSizes.sizes(mAspectRatio);
}
//選取最合適的PreviewSize
Size size = chooseOptimalSize(sizes);
final Camera.Size currentSize = mCameraParameters.getPictureSize();
if (currentSize.width != size.getWidth() || currentSize.height != size.getHeight()) {
// Largest picture size in this ratio
//Size pictureSize = mPictureSizes.sizes(mAspectRatio).last();
//根據寬高比設定實際拍照Size
Size pictureSize = null;
SortedSet<Size> pictureSizeS = mPictureSizes.sizes(mAspectRatio);
if (mSettingPictureSize == null) {
pictureSize = pictureSizeS.last(); //選擇最大的Size
} else {
pictureSize = chooseSuitableSize(pictureSizeS, mSettingPictureSize);
}
if (mShowingPreview) {
mCamera.stopPreview();
}
mCameraParameters.setPreviewSize(size.getWidth(), size.getHeight());
mCameraParameters.setPictureSize(pictureSize.getWidth(), pictureSize.getHeight());
mCameraParameters.setRotation(calcCameraRotation(mDisplayOrientation));
setAutoFocusInternal(mAutoFocus);
setFlashInternal(mFlash);
mCamera.setParameters(mCameraParameters);
if (mShowingPreview) {
mCamera.startPreview();
}
}
}
/**
* 選擇合適的pictureSize
*/
private Size chooseSuitableSize(SortedSet<Size> sizes, Size desiredSize) {
Size result = null;
for (Size size : sizes) { // Iterate from small to large
if (desiredSize.getWidth() <= size.getWidth() && desiredSize.getHeight() <= size.getHeight()) {
return size;
}
result = size;
}
return result;
}
@SuppressWarnings("SuspiciousNameCombination")
private Size chooseOptimalSize(SortedSet<Size> sizes) {
if (!mPreview.isReady()) { // Not yet laid out
return sizes.first(); // Return the smallest size
}
int desiredWidth;
int desiredHeight;
final int surfaceWidth = mPreview.getWidth();
final int surfaceHeight = mPreview.getHeight();
if (mDisplayOrientation == 90 || mDisplayOrientation == 270) {
desiredWidth = surfaceHeight;
desiredHeight = surfaceWidth;
} else {
desiredWidth = surfaceWidth;
desiredHeight = surfaceHeight;
}
Size result = null;
for (Size size : sizes) { // Iterate from small to large
if (desiredWidth <= size.getWidth() && desiredHeight <= size.getHeight()) {
return size;
}
result = size;
}
return result;
}
private void releaseCamera() {
if (mCamera != null) {
mCamera.release();
mCamera = null;
mCallback.onCameraClosed();
}
}
private int calcCameraRotation(int rotation) {
if (mCameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
return (360 - (mCameraInfo.orientation + rotation) % 360) % 360;
} else { // back-facing
return (mCameraInfo.orientation - rotation + 360) % 360;
}
}
/**
* @return {@code true} if {@link #mCameraParameters} was modified.
*/
private boolean setAutoFocusInternal(boolean autoFocus) {
mAutoFocus = autoFocus;
if (isCameraOpened()) {
final List<String> modes = mCameraParameters.getSupportedFocusModes();
if (autoFocus && modes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {
mCameraParameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
} else if (modes.contains(Camera.Parameters.FOCUS_MODE_FIXED)) {
mCameraParameters.setFocusMode(Camera.Parameters.FOCUS_MODE_FIXED);
} else if (modes.contains(Camera.Parameters.FOCUS_MODE_INFINITY)) {
mCameraParameters.setFocusMode(Camera.Parameters.FOCUS_MODE_INFINITY);
} else {
mCameraParameters.setFocusMode(modes.get(0));
}
return true;
} else {
return false;
}
}
/**
* @return {@code true} if {@link #mCameraParameters} was modified.
*/
private boolean setFlashInternal(int flash) {
if (isCameraOpened()) {
List<String> modes = mCameraParameters.getSupportedFlashModes();
String mode = FLASH_MODES.get(flash);
if (modes != null && modes.contains(mode)) {
mCameraParameters.setFlashMode(mode);
mFlash = flash;
return true;
}
String currentMode = FLASH_MODES.get(mFlash);
if (modes == null || !modes.contains(currentMode)) {
mCameraParameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
mFlash = Constants.FLASH_OFF;
return true;
}
return false;
} else {
mFlash = flash;
return false;
}
}
}
添加了setPictureSize()函式的實現,修改了adjustCameraParameters()函式,該函式就是在OpenCamera之前設定Camera引數的,其中包括PictureSize,拍照解析度
接下來完成Camera2中setPictureSize的實現,如下
/*
* Copyright (C) 2016 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.google.android.cameraview;
import android.Manifest;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.ImageFormat;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.Image;
import android.media.ImageReader;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.util.Log;
import android.util.SparseIntArray;
import android.view.Surface;
import android.widget.Toast;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
@TargetApi(21)
class Camera2 extends CameraViewImpl {
private static final String TAG = "Camera2";
private static final SparseIntArray INTERNAL_FACINGS = new SparseIntArray();
static {
INTERNAL_FACINGS.put(Constants.FACING_BACK, CameraCharacteristics.LENS_FACING_BACK);
INTERNAL_FACINGS.put(Constants.FACING_FRONT, CameraCharacteristics.LENS_FACING_FRONT);
}
/**
* Max preview width that is guaranteed by Camera2 API
*/
private static final int MAX_PREVIEW_WIDTH = 1920;
/**
* Max preview height that is guaranteed by Camera2 API
*/
private static final int MAX_PREVIEW_HEIGHT = 1080;
/**
* 自定義的設定
*/
private Size mSettingPictureSize;
/**
* 自定義的預覽的解析度
*/
private Size mSettingPreviewSize;
private final CameraManager mCameraManager;
private final CameraDevice.StateCallback mCameraDeviceCallback
= new CameraDevice.StateCallback() {
@Override
public void onOpened(@NonNull CameraDevice camera) {
mCamera = camera;
mCallback.onCameraOpened();
startCaptureSession();
}
@Override
public void onClosed(@NonNull CameraDevice camera) {
mCallback.onCameraClosed();
}
@Override
public void onDisconnected(@NonNull CameraDevice camera) {
mCamera = null;
}
@Override
public void onError(@NonNull CameraDevice camera, int error) {
Log.e(TAG, "onError: " + camera.getId() + " (" + error + ")");
mCamera = null;
}
};
private final CameraCaptureSession.StateCallback mSessionCallback
= new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(@NonNull CameraCaptureSession session) {
if (mCamera == null) {
return;
}
mCaptureSession = session;
updateAutoFocus();
updateFlash();
try {
mCaptureSession.setRepeatingRequest(mPreviewRequestBuilder.build(),
mCaptureCallback, null);
} catch (CameraAccessException e) {
Log.e(TAG, "Failed to start camera preview because it couldn't access camera", e);
} catch (IllegalStateException e) {
Log.e(TAG, "Failed to start camera preview.", e);
}
}
@Override
public void onConfigureFailed(@NonNull CameraCaptureSession session) {
Log.e(TAG, "Failed to configure capture session.");
}
@Override
public void onClosed(@NonNull CameraCaptureSession session) {
if (mCaptureSession != null && mCaptureSession.equals(session)) {
mCaptureSession = null;
}
}
};
PictureCaptureCallback mCaptureCallback = new PictureCaptureCallback() {
@Override
public void onPrecaptureRequired() {
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START);
setState(STATE_PRECAPTURE);
try {
mCaptureSession.capture(mPreviewRequestBuilder.build(), this, null);
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_IDLE);
} catch (CameraAccessException e) {
Log.e(TAG, "Failed to run precapture sequence.", e);
}
}
@Override
public void onReady() {
captureStillPicture();
}
};
private final ImageReader.OnImageAvailableListener mOnImageAvailableListener
= new ImageReader.OnImageAvailableListener() {
@Override
public void onImageAvailable(ImageReader reader) {
try (Image image = reader.acquireNextImage()) {
Image.Plane[] planes = image.getPlanes();
if (planes.length > 0) {
ByteBuffer buffer = planes[0].getBuffer();
byte[] data = new byte[buffer.remaining()];
buffer.get(data);
mCallback.onPictureTaken(data);
}
}
}
};
private String mCameraId;
private CameraCharacteristics mCameraCharacteristics;
CameraDevice mCamera;
CameraCaptureSession mCaptureSession;
CaptureRequest.Builder mPreviewRequestBuilder;
private ImageReader mImageReader;
private final SizeMap mPreviewSizes = new SizeMap();
private final SizeMap mPictureSizes = new SizeMap();
private int mFacing;
private AspectRatio mAspectRatio = Constants.DEFAULT_ASPECT_RATIO;
private boolean mAutoFocus;
private int mFlash;
private int mDisplayOrientation;
private Context mContext;
Camera2(Callback callback, PreviewImpl preview, Context context) {
super(callback, preview);
mContext = context;
mCameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
mPreview.setCallback(new PreviewImpl.Callback() {
@Override
public void onSurfaceChanged() {
startCaptureSession();
}
});
}
@Override
boolean start() {
if (!chooseCameraIdByFacing()) {
return false;
}
collectCameraInfo();
prepareImageReader();
startOpeningCamera();
return true;
}
@Override
void stop() {
if (mCaptureSession != null) {
mCaptureSession.close();
mCaptureSession = null;
}
if (mCamera != null) {
mCamera.close();
mCamera = null;
}
if (mImageReader != null) {
mImageReader.close();
mImageReader = null;
}
}
@Override
boolean isCameraOpened() {
return mCamera != null;
}
@Override
void setFacing(int facing) {
if (mFacing == facing) {
return;
}
mFacing = facing;
if (isCameraOpened()) {
stop();
start();
}
}
@Override
int getFacing() {
return mFacing;
}
@Override
Set<AspectRatio> getSupportedAspectRatios() {
return mPreviewSizes.ratios();
}
@Override
boolean setAspectRatio(AspectRatio ratio) {
if (ratio == null || ratio.equals(mAspectRatio) ||
!mPreviewSizes.ratios().contains(ratio)) {
// TODO: Better error handling
return false;
}
mAspectRatio = ratio;
if (mCaptureSession != null) {
mCaptureSession.close();
mCaptureSession = null;
startCaptureSession();
}
return true;
}
@Override
AspectRatio getAspectRatio() {
return mAspectRatio;
}
@Override
void setAutoFocus(boolean autoFocus) {
if (mAutoFocus == autoFocus) {
return;
}
mAutoFocus = autoFocus;
if (mPreviewRequestBuilder != null) {
updateAutoFocus();
if (mCaptureSession != null) {
try {
mCaptureSession.setRepeatingRequest(mPreviewRequestBuilder.build(),
mCaptureCallback, null);
} catch (CameraAccessException e) {
mAutoFocus = !mAutoFocus; // Revert
}
}
}
}
@Override
boolean getAutoFocus() {
return mAutoFocus;
}
@Override
void setFlash(int flash) {
if (mFlash == flash) {
return;
}
int saved = mFlash;
mFlash = flash;
if (mPreviewRequestBuilder != null) {
updateFlash();
if (mCaptureSession != null) {
try {
mCaptureSession.setRepeatingRequest(mPreviewRequestBuilder.build(),
mCaptureCallback, null);
} catch (CameraAccessException e) {
mFlash = saved; // Revert
}
}
}
}
@Override
int getFlash() {
return mFlash;
}
@Override
void takePicture() {
if (mAutoFocus) {
lockFocus();
} else {
captureStillPicture();
}
}
@Override
void setDisplayOrientation(int displayOrientation) {
mDisplayOrientation = displayOrientation;
mPreview.setDisplayOrientation(mDisplayOrientation);
}
/**
* 設定拍攝出來照片的解析度
*
* @param width
* @param height
*/
@Override
void setPictureSize(int width, int height) {
mSettingPictureSize = new Size(width, height);
Log.e(TAG, "camera setPictureSize width =" + width);
prepareImageReader();
}
/**
* <p>Chooses a camera ID by the specified camera facing ({@link #mFacing}).</p>
* <p>This rewrites {@link #mCameraId}, {@link #mCameraCharacteristics}, and optionally
* {@link #mFacing}.</p>
*/
private boolean chooseCameraIdByFacing() {
try {
int internalFacing = INTERNAL_FACINGS.get(mFacing);
final String[] ids = mCameraManager.getCameraIdList();
if (ids.length == 0) { // No camera
throw new RuntimeException("No camera available.");
}
for (String id : ids) {
CameraCharacteristics characteristics = mCameraManager.getCameraCharacteristics(id);
Integer level = characteristics.get(
CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
if (level == null ||
level == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) {
continue;
}
Integer internal = characteristics.get(CameraCharacteristics.LENS_FACING);
if (internal == null) {
throw new NullPointerException("Unexpected state: LENS_FACING null");
}
if (internal == internalFacing) {
mCameraId = id;
mCameraCharacteristics = characteristics;
return true;
}
}
// Not found
mCameraId = ids[0];
mCameraCharacteristics = mCameraManager.getCameraCharacteristics(mCameraId);
Integer level = mCameraCharacteristics.get(
CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
if (level == null ||
level == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) {
return false;
}
Integer internal = mCameraCharacteristics.get(CameraCharacteristics.LENS_FACING);
if (internal == null) {
throw new NullPointerException("Unexpected state: LENS_FACING null");
}
for (int i = 0, count = INTERNAL_FACINGS.size(); i < count; i++) {
if (INTERNAL_FACINGS.valueAt(i) == internal) {
mFacing = INTERNAL_FACINGS.keyAt(i);
return true;
}
}
// The operation can reach here when the only camera device is an external one.
// We treat it as facing back.
mFacing = Constants.FACING_BACK;
return true;
} catch (CameraAccessException e) {
throw new RuntimeException("Failed to get a list of camera devices", e);
}
}
/**
* 收集相機資訊
* <p>Collects some information from {@link #mCameraCharacteristics}.</p>
* <p>This rewrites {@link #mPreviewSizes}, {@link #mPictureSizes}, and optionally,
* {@link #mAspectRatio}.</p>
*/
private void collectCameraInfo() {
StreamConfigurationMap map = mCameraCharacteristics.get(
CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
if (map == null) {
throw new IllegalStateException("Failed to get configuration map: " + mCameraId);
}
mPreviewSizes.clear();
for (android.util.Size size : map.getOutputSizes(mPreview.getOutputClass())) {
mPreviewSizes.add(new Size(size.getWidth(), size.getHeight()));
}
mPictureSizes.clear();
collectPictureSizes(mPictureSizes, map);
if (!mPreviewSizes.ratios().contains(mAspectRatio)) {
mAspectRatio = mPreviewSizes.ratios().iterator().next();
}
}
protected void collectPictureSizes(SizeMap sizes, StreamConfigurationMap map) {
for (android.util.Size size : map.getOutputSizes(ImageFormat.JPEG)) {
mPictureSizes.add(new Size(size.getWidth(), size.getHeight()));
}
}
/**
* 設定ImageReader(拍照的Size)
*/
private void prepareImageReader() {
//根據寬高比設定實際拍照Size
Size pictureSize = null;
SortedSet<Size> pictureSizeS = mPictureSizes.sizes(mAspectRatio);
Log.e(TAG, "prepareImageReader mAspectRatio =" + mAspectRatio.toString());
Log.e(TAG, "prepareImageReader pictureSizeS =" + pictureSizeS.toString());
if (mSettingPictureSize == null) {
pictureSize = pictureSizeS.last(); //選擇最大的Size
} else {
pictureSize = chooseSuitableSize(pictureSizeS, mSettingPictureSize);
}
mImageReader = ImageReader.newInstance(pictureSize.getWidth(), pictureSize.getHeight(),
ImageFormat.JPEG, /* maxImages */ 2);
mImageReader.setOnImageAvailableListener(mOnImageAvailableListener, null);
}
/**
* 選擇合適的pictureSize
*/
private Size chooseSuitableSize(SortedSet<Size> sizes, Size desiredSize) {
Size result = null;
for (Size size : sizes) { // Iterate from small to large
if (desiredSize.getWidth() <= size.getWidth() && desiredSize.getHeight() <= size.getHeight()) {
return size;
}
result = size;
}
return result;
}
/**
* <p>Starts opening a camera device.</p>
* <p>The result will be processed in {@link #mCameraDeviceCallback}.</p>
*/
private void startOpeningCamera() {
try {
if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
//Todo處理提示
Toast.makeText(mContext, "相機許可權未設定", Toast.LENGTH_SHORT).show();
} else {
mCameraManager.openCamera(mCameraId, mCameraDeviceCallback, null);
}
} catch (CameraAccessException e) {
throw new RuntimeException("Failed to open camera: " + mCameraId, e);
}
}
/**
* <p>Starts a capture session for camera preview.</p>
* <p>This rewrites {@link #mPreviewRequestBuilder}.</p>
* <p>The result will be continuously processed in {@link #mSessionCallback}.</p>
*/
void startCaptureSession() {
if (!isCameraOpened() || !mPreview.isReady() || mImageReader == null) {
return;
}
Size previewSize = chooseOptimalSize();
mPreview.setBufferSize(previewSize.getWidth(), previewSize.getHeight());
Surface surface = mPreview.getSurface();
try {
mPreviewRequestBuilder = mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
mPreviewRequestBuilder.addTarget(surface);
mCamera.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()),
mSessionCallback, null);
} catch (CameraAccessException e) {
throw new RuntimeException("Failed to start camera session");
}
}
/**
* Chooses the optimal preview size based on {@link #mPreviewSizes} and the surface size.
*
* @return The picked size for camera preview.
*/
private Size chooseOptimalSize() {
int surfaceLonger, surfaceShorter;
final int surfaceWidth = mPreview.getWidth();
final int surfaceHeight = mPreview.getHeight();
if (surfaceWidth < surfaceHeight) {
surfaceLonger = surfaceHeight;
surfaceShorter = surfaceWidth;
} else {
surfaceLonger = surfaceWidth;
surfaceShorter = surfaceHeight;
}
SortedSet<Size> allCandidates = mPreviewSizes.sizes(mAspectRatio);
// Eliminate candidates that are bigger than Camera2 PREVIEW guarantees
SortedSet<Size> guaranteedCandidates = new TreeSet<>();
for (Size size : allCandidates) {
if (size.getWidth() <= MAX_PREVIEW_WIDTH && size.getHeight() <= MAX_PREVIEW_HEIGHT) {
guaranteedCandidates.add(size);
}
}
// Pick the smallest of those big enough
for (Size size : guaranteedCandidates) {
if (size.ge