- 一、摘要
- 二、伪代码
- 2.1 方向
- 2.2 previewSize
- 2.3 pictureSize
- 2.4 DefaultFocusParameters
- 2.5 DefaultFlashParameters
- 三、代码整理
本篇文章阐述初始化流程中需要设置的默认参数包括如下:
- DisplayOrientation
- PreviewSize
- PictureSize
- DefaultFocusParameters
- DefaultFlashParameters
先把函数写出来
private void applyDefaultParameters(){ //1.apply方向 //2.apply previewSize //3.apply pictureSize //4.apply focus mode //5.apply flash mode }2.1 方向
参考【Camera1】Camera1源码分析【Java层】【2.4.2】,先贴上相关代码:
private int calcDisplayOrientation() { CameraInfo info = new Camera.CameraInfo(); android.hardware.Camera.getCameraInfo(cameraId, info); int rotation = activity.getWindowManager().getDefaultDisplay() .getRotation(); int degrees = 0; switch (rotation) { case Surface.ROTATION_0: degrees = 0; break; case Surface.ROTATION_90: degrees = 90; break; case Surface.ROTATION_180: degrees = 180; break; case Surface.ROTATION_270: degrees = 270; break; } int result; if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) { result = (info.orientation + degrees) % 360; result = (360 - result) % 360; // compensate the mirror } else { // back-facing result = (info.orientation - degrees + 360) % 360; } camera.setDisplayOrientation(result); }
分析:
- 这里我们已经在openCamera里获取到mFacing的信息了,可以省区代码里的getCameraInfo逻辑。
- 在某些兼容性手机上,方向可能有异常,需要做特殊的兼容逻辑处理
- setDisplayOrientation会抛异常,但是不是致命问题
因此可改造代码如下:
private int applyDefaultOrientation() { int displayOrientation; //1.问题机型兼容逻辑处理 if(CameraCompat.Camera1.isOrientationExcepDevice(Build.Model)){ displayOrientation = CameraCompat.Camera1.getOrientationForExcepDevices(); }else{ //2.逻辑计算 int rotation = activity.getWindowManager().getDefaultDisplay() .getRotation(); int degrees = 0; switch (rotation) { case Surface.ROTATION_0: degrees = 0; break; case Surface.ROTATION_90: degrees = 90; break; case Surface.ROTATION_180: degrees = 180; break; case Surface.ROTATION_270: degrees = 270; break; } //mOrientation为 CameraInfo里 orientation字段,在openCamera阶段保存为变量即可 if (mFacing == Camera.CameraInfo.CAMERA_FACING_FRONT) { displayOrientation = (mOrientation + degrees) % 360; displayOrientation = (360 - displayOrientation) % 360; // compensate the mirror } else { // back-facing displayOrientation = (mOrientation - degrees + 360) % 360; } } try{ //3.非致命异常 try-catch camera.setDisplayOrientation(displayOrientation); }catch(Exception e){ Log.e(TAG, "setDisplayOrientation exception info = "+e.getInfo()); } }2.2 previewSize
Camera.java源码如下:
分析
- 如果camera is preview,需要 stop preview first,and then setPreviewSize
- 和displayOrientation有关系,正常后置和手机竖屏有90度的夹角。所以一般计算得到的最终displayOrientation = 90。 因此preview正常为如下:1920 * 1080; width > height。和手机分辨率(1080 * 1920) 有90度夹角
- 会抛异常
官方Demo如下
void adjustCameraParameters() { SortedSetsizes = mPreviewSizes.sizes(mAspectRatio); if (sizes == null) { // Not supported mAspectRatio = chooseAspectRatio(); sizes = mPreviewSizes.sizes(mAspectRatio); } Size size = chooseOptimalSize(sizes); // Always re-apply camera parameters // Largest picture size in this ratio final Size pictureSize = mPictureSizes.sizes(mAspectRatio).last(); if (mShowingPreview) { mCamera.stopPreview(); } mCameraParameters.setPreviewSize(size.getWidth(), size.getHeight()); mCameraParameters.setPictureSize(pictureSize.getWidth(), pictureSize.getHeight()); } private AspectRatio chooseAspectRatio() { AspectRatio r = null; for (AspectRatio ratio : mPreviewSizes.ratios()) { r = ratio; if (ratio.equals(Constants.DEFAULT_ASPECT_RATIO)) { return ratio; } } return r; } 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 (isLandscape(mDisplayOrientation)) { 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; }
分析:
- 通过mShowingPreview字段来判断是否stopPreview
- 通过aspectRatio来筛选
- 计算和aspectRatio匹配的分辨率
previewSize是相机支持的默认分辨率,每种分辨率有不同的宽高比。常见的有
- 1:1
- 2:3
- 3:4
- 9:16
具体的根据用户体验层面来进行设定。
对应的UI默认分辨率的宽位1080,因此如果不考虑指定可选的分辨率。默认分辨率为:
- 1080 * 1080(1:1)
- 1080 * 1920 (9:16)
- 1080 * 1440 (3:4)
- 1080 * 1620 (2:3)
在Camera1源码分析一文中。【4.2 参数内容】里给出了preview-values值。如下:
preview-size-values -> 1920x1080,1600x960,1600x900,1600x736,1600x720,1440x1080,1440x720,1280x720,1080x1080,960x720,720x480,640x480,352x288,320x240,176x144
因此我们需要遍历preview size list。通过对比筛选,选择一个匹配的。如果都不匹配,选择最近匹配的最后设置在上面。
注意如下2个情况:
1.不设置preview-size camera会有默认设置,不影响功能使用
2. 设置的如果非supportPreviewSize会报错
Caused by: java.lang.RuntimeException: setParameters failed
at android.hardware.Camera.native_setParameters(Native Method)
at android.hardware.Camera.setParameters(Camera.java:2192)
at com.google.android.cameraview.Camera1.adjustCameraParameters(Camera1.java:330)
at com.google.android.cameraview.Camera1.openCamera(Camera1.java:295)
at com.google.android.cameraview.Camera1.start(Camera1.java:90)
at com.google.android.cameraview.CameraView.start(CameraView.java:245)
at com.google.android.cameraview.demo.MainActivity.onResume(MainActivity.java:131)
at android.app.Instrumentation.callActivityonResume(Instrumentation.java:1453)
at android.app.Activity.performResume(Activity.java:8050)
最终,我们可更改代码如下:
public void applyPreviewSize(){ if (mShowingPreview) { mCamera.stopPreview(); } Camera.Parameters params = null; try{ //mCamera.getParameters();需要try-catch住,不是致命bug params = mCamera.getParameters(); }catch(Exception e){ ... } if(params == null){ //不设置 return return; } List2.3 pictureSizepreviewSize = null; try{ //可参看源码,内部没有异常try-catch。可能会抛异常,也可能返回为null; previewSize = params.getSupportedPreviewSizes(); }catch(Exception e){ ... } if(previewSize == null || previewSize.size() == 0){ return; } float min = Integer.MAX_VALUE; Size selectSize = null; for(Size s : previewSize){ float ar = AspectRatio.cal(s); if(ar - mAspectRatio < min){ selectPreviewSize = s; min = Math.min(min,Math.abs(mAspectRatio - ar)); } } if(selectPreviewSize == null){ return; } try{ params.setPreviewSize(selectSize.getWidth(),selectSize.getHeight()); }catch(Exception e){ ... } }
pictureSize和previewSize是同样的逻辑,可参考2.2。只需:
- getSupportedPreviewSizes -> getSupportedPictureSizes
- setPreviewSize -> setPictureSize
即可。
官方代码如下:
private boolean setAutoFocusInternal(boolean autoFocus) { mAutoFocus = autoFocus; if (isCameraOpened()) { final Listmodes = 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; } }
具体到相机对焦细节,将在Camera1系列文章对焦里阐述,这里直接贴上相关代码:
public boolean applyDefaultFocusMode(){ if(!isCameraOpened()){ return false; } try{ final Listmodes = mCameraParameters.getSupportedFocusModes(); if(modes == null || modes.size() == 0){ return false; } if (isCurVideoTab() && modes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO)) { mCameraParameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO); } else if (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)); } }catch(Exception e){ ... } }
以上是伪代码mCameraParameters,即可全局变量保存更新,也可以临时获取和更新但一定要注意:
try-catch,读取和更新CameraParameters都存在抛异常的可能性
官方Demo如下:
public void applyDefaultFlashMode(){ private static final SparseArrayCompatFLASH_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 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; } } }
这里可直接使用官方Demo的方法,也可以自己做另外更改,只需要处理好异常情况即可。
三、代码整理最后再整理下代码如下:
private void applyDefaultParameters(){ //1.apply方向 applyDefaultOrientation(); //2.apply previewSize applyDefaultPreviewSize(); //3.apply pictureSize applyDefaultPictureSize(); //4.apply focus mode applyDefaultFocusMode(); //5.apply flash mode applyDefaultFlashMode(); }
下一篇将阐述
Camera1初始化销毁流程(六) —— Camera1Impl类之startPreview
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)