【Android Camera1】Camera1初始化销毁流程(五) —— Camera1Impl类之applyDefaultParameter伪代码

一、摘要

本篇文章阐述初始化流程中需要设置的默认参数包括如下:

  1. DisplayOrientation
  2. PreviewSize
  3. PictureSize
  4. DefaultFocusParameters
  5. 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】,先贴上相关代码:

 /**
     * Calculate display orientation
https://developer.android.com/reference/android/hardware/Camera.html#setDisplayOrientation(int)
     */
    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);
    }

分析:

  1. 这里我们已经在openCamera里获取到mFacing的信息了,可以省区代码里的getCameraInfo逻辑。
  2. 在某些兼容性手机上,方向可能有异常,需要做特殊的兼容逻辑处理
  3. 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源码如下:
在这里插入图片描述

分析

  1. 如果camera is preview,需要 stop preview first,and then setPreviewSize
  2. 和displayOrientation有关系,正常后置和手机竖屏有90度的夹角。所以一般计算得到的最终displayOrientation = 90。 因此preview正常为如下:1920 * 1080; width > height。和手机分辨率(1080 * 1920) 有90度夹角
  3. 会抛异常

官方Demo如下

    void adjustCameraParameters() {
    
    
        SortedSet<Size> sizes = 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<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 (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;
    }

分析:

扫描二维码关注公众号,回复: 14853003 查看本文章
  1. 通过mShowingPreview字段来判断是否stopPreview
  2. 通过aspectRatio来筛选
  3. 计算和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 aspectRatio
1920x1080 16:9
1600x960 5:3
1600x900 16:9
1600x736 50:23
1600x720 20:9
1440x1080 4:3
1440x720 2:1
1280x720 16:9
1080x1080 1:1
960x720 4:3
720x480 3:2
640x480 4:3
352x288 11:9
320x240 4:3
176x144 11:9

因此我们需要遍历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;
	}
	List<Size> previewSize = 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){
    
    
		...
	}
}

2.3 pictureSize

pictureSize和previewSize是同样的逻辑,可参考2.2。只需:

  1. getSupportedPreviewSizes -> getSupportedPictureSizes
  2. setPreviewSize -> setPictureSize
    即可。

2.4 DefaultFocusParameters

官方代码如下:

    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;
        }
    }

具体到相机对焦细节,将在Camera1系列文章对焦里阐述,这里直接贴上相关代码:

public boolean applyDefaultFocusMode(){
    
    
	if(!isCameraOpened()){
    
    
		return false;
	}
	try{
    
    
	    final List<String> modes = 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都存在抛异常的可能性

2.5 DefaultFlashParameters

官方Demo如下:

public void applyDefaultFlashMode(){
    
    
  	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 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;
        }
    }
}

这里可直接使用官方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

猜你喜欢

转载自blog.csdn.net/Scott_S/article/details/122275907