对于拍照功能来说,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 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 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 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 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 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 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 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 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 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 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 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();
}
/**
* Chooses a camera ID by the specified camera facing ({@link #mFacing}).
* This rewrites {@link #mCameraId}, {@link #mCameraCharacteristics}, and optionally
* {@link #mFacing}.
*/
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);
}
}
/**
* 收集相机信息
* Collects some information from {@link #mCameraCharacteristics}.
* This rewrites {@link #mPreviewSizes}, {@link #mPictureSizes}, and optionally,
* {@link #mAspectRatio}.
*/
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 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 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;
}
/**
* Starts opening a camera device.
* The result will be processed in {@link #mCameraDeviceCallback}.
*/
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);
}
}
/**
* Starts a capture session for camera preview.
* This rewrites {@link #mPreviewRequestBuilder}.
* The result will be continuously processed in {@link #mSessionCallback}.
*/
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 allCandidates = mPreviewSizes.sizes(mAspectRatio);
// Eliminate candidates that are bigger than Camera2 PREVIEW guarantees
SortedSet 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.getWidth() >= surfaceLonger && size.getHeight() >= surfaceShorter) {
return size;
}
}
// If no size is big enough, pick the largest one.
return guaranteedCandidates.last();
}
/**
* Updates the internal state of auto-focus to {@link #mAutoFocus}.
*/
void updateAutoFocus() {
if (mAutoFocus) {
int[] modes = mCameraCharacteristics.get(
CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES);
// Auto focus is not supported
if (modes == null || modes.length == 0 ||
(modes.length == 1 && modes[0] == CameraCharacteristics.CONTROL_AF_MODE_OFF)) {
mAutoFocus = false;
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,
CaptureRequest.CONTROL_AF_MODE_OFF);
} else {
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,
CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
}
} else {
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,
CaptureRequest.CONTROL_AF_MODE_OFF);
}
}
/**
* Updates the internal state of flash to {@link #mFlash}.
*/
void updateFlash() {
switch (mFlash) {
case Constants.FLASH_OFF:
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
CaptureRequest.CONTROL_AE_MODE_ON);
mPreviewRequestBuilder.set(CaptureRequest.FLASH_MODE,
CaptureRequest.FLASH_MODE_OFF);
break;
case Constants.FLASH_ON:
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
CaptureRequest.CONTROL_AE_MODE_ON_ALWAYS_FLASH);
mPreviewRequestBuilder.set(CaptureRequest.FLASH_MODE,
CaptureRequest.FLASH_MODE_OFF);
break;
case Constants.FLASH_TORCH:
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
CaptureRequest.CONTROL_AE_MODE_ON);
mPreviewRequestBuilder.set(CaptureRequest.FLASH_MODE,
CaptureRequest.FLASH_MODE_TORCH);
break;
case Constants.FLASH_AUTO:
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
mPreviewRequestBuilder.set(CaptureRequest.FLASH_MODE,
CaptureRequest.FLASH_MODE_OFF);
break;
case Constants.FLASH_RED_EYE:
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE);
mPreviewRequestBuilder.set(CaptureRequest.FLASH_MODE,
CaptureRequest.FLASH_MODE_OFF);
break;
}
}
void updatePictureSize() {
}
/**
* Locks the focus as the first step for a still image capture.
*/
private void lockFocus() {
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
CaptureRequest.CONTROL_AF_TRIGGER_START);
try {
mCaptureCallback.setState(PictureCaptureCallback.STATE_LOCKING);
mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback, null);
} catch (CameraAccessException e) {
Log.e(TAG, "Failed to lock focus.", e);
}
}
/**
* Captures a still picture.(并且设置拍摄参数)
*/
void captureStillPicture() {
try {
CaptureRequest.Builder captureRequestBuilder = mCamera.createCaptureRequest(
CameraDevice.TEMPLATE_STILL_CAPTURE);
captureRequestBuilder.addTarget(mImageReader.getSurface());
captureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,
mPreviewRequestBuilder.get(CaptureRequest.CONTROL_AF_MODE));
switch (mFlash) {
case Constants.FLASH_OFF:
captureRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
CaptureRequest.CONTROL_AE_MODE_ON);
captureRequestBuilder.set(CaptureRequest.FLASH_MODE,
CaptureRequest.FLASH_MODE_OFF);
break;
case Constants.FLASH_ON:
captureRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
CaptureRequest.CONTROL_AE_MODE_ON_ALWAYS_FLASH);
break;
case Constants.FLASH_TORCH:
captureRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
CaptureRequest.CONTROL_AE_MODE_ON);
captureRequestBuilder.set(CaptureRequest.FLASH_MODE,
CaptureRequest.FLASH_MODE_TORCH);
break;
case Constants.FLASH_AUTO:
captureRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
break;
case Constants.FLASH_RED_EYE:
captureRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
break;
}
// Calculate JPEG orientation.
@SuppressWarnings("ConstantConditions")
int sensorOrientation = mCameraCharacteristics.get(
CameraCharacteristics.SENSOR_ORIENTATION);
captureRequestBuilder.set(CaptureRequest.JPEG_ORIENTATION,
(sensorOrientation +
mDisplayOrientation * (mFacing == Constants.FACING_FRONT ? 1 : -1) +
360) % 360);
// Stop preview and capture a still picture.
mCaptureSession.stopRepeating();
mCaptureSession.capture(captureRequestBuilder.build(),
new CameraCaptureSession.CaptureCallback() {
@Override
public void onCaptureCompleted(@NonNull CameraCaptureSession session,
@NonNull CaptureRequest request,
@NonNull TotalCaptureResult result) {
unlockFocus();
}
}, null);
} catch (CameraAccessException e) {
Log.e(TAG, "Cannot capture a still picture.", e);
}
}
/**
* Unlocks the auto-focus and restart camera preview. This is supposed to be called after
* capturing a still picture.
*/
void unlockFocus() {
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
CaptureRequest.CONTROL_AF_TRIGGER_CANCEL);
try {
mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback, null);
updateAutoFocus();
updateFlash();
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
CaptureRequest.CONTROL_AF_TRIGGER_IDLE);
mCaptureSession.setRepeatingRequest(mPreviewRequestBuilder.build(), mCaptureCallback,
null);
mCaptureCallback.setState(PictureCaptureCallback.STATE_PREVIEW);
} catch (CameraAccessException e) {
Log.e(TAG, "Failed to restart camera preview.", e);
}
}
/**
* A {@link CameraCaptureSession.CaptureCallback} for capturing a still picture.
*/
private static abstract class PictureCaptureCallback
extends CameraCaptureSession.CaptureCallback {
static final int STATE_PREVIEW = 0;
static final int STATE_LOCKING = 1;
static final int STATE_LOCKED = 2;
static final int STATE_PRECAPTURE = 3;
static final int STATE_WAITING = 4;
static final int STATE_CAPTURING = 5;
private int mState;
PictureCaptureCallback() {
}
void setState(int state) {
mState = state;
}
@Override
public void onCaptureProgressed(@NonNull CameraCaptureSession session,
@NonNull CaptureRequest request, @NonNull CaptureResult partialResult) {
process(partialResult);
}
@Override
public void onCaptureCompleted(@NonNull CameraCaptureSession session,
@NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {
process(result);
}
private void process(@NonNull CaptureResult result) {
switch (mState) {
case STATE_LOCKING: {
Integer af = result.get(CaptureResult.CONTROL_AF_STATE);
if (af == null) {
break;
}
if (af == CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED ||
af == CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED) {
Integer ae = result.get(CaptureResult.CONTROL_AE_STATE);
if (ae == null || ae == CaptureResult.CONTROL_AE_STATE_CONVERGED) {
setState(STATE_CAPTURING);
onReady();
} else {
setState(STATE_LOCKED);
onPrecaptureRequired();
}
}
break;
}
case STATE_PRECAPTURE: {
Integer ae = result.get(CaptureResult.CONTROL_AE_STATE);
if (ae == null || ae == CaptureResult.CONTROL_AE_STATE_PRECAPTURE ||
ae == CaptureRequest.CONTROL_AE_STATE_FLASH_REQUIRED ||
ae == CaptureResult.CONTROL_AE_STATE_CONVERGED) {
setState(STATE_WAITING);
}
break;
}
case STATE_WAITING: {
Integer ae = result.get(CaptureResult.CONTROL_AE_STATE);
if (ae == null || ae != CaptureResult.CONTROL_AE_STATE_PRECAPTURE) {
setState(STATE_CAPTURING);
onReady();
}
break;
}
}
}
/**
* Called when it is ready to take a still picture.
*/
public abstract void onReady();
/**
* Called when it is necessary to run the precapture sequence.
*/
public abstract void onPrecaptureRequired();
}
}
如上,自己封装了prepareImageReader()函数并在,onStart的时候调用,来进行pictureSize设置
综上,修改完毕,你只需要重新编译就可以使用了,进行设置排除照片的分辨率。
PS:了解原理了之后其实你可以按此来添加一些列其他属性的设置。
如下,附上怎么使用
mCameraView.addCallback(new Callback() {
@Override
public void onCameraOpened(CameraView cameraView) {
super.onCameraOpened(cameraView);
mCameraView.setAspectRatio(AspectRatio.of(1920, 1080));
mCameraView.setPictureSize(1920, 1080);
mCameraView.setAutoFocus(true);
}
@Override
public void onCameraClosed(CameraView cameraView) {
super.onCameraClosed(cameraView);
}
@Override
public void onPictureTaken(CameraView cameraView, byte[] data) {
super.onPictureTaken(cameraView, data);
}
项目lib地址:
https://github.com/DiskyZhs/CameraView
直接导入lib使用