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

分析:

  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-sizeaspectRatio
1920x108016:9
1600x9605:3
1600x90016:9
1600x73650:23
1600x72020:9
1440x10804:3
1440x7202:1
1280x72016:9
1080x10801:1
960x7204:3
720x4803:2
640x4804:3
352x28811:9
320x2404:3
176x14411: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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是使用HttpClient调用海康威视/ISAPI/Streaming/channels/1/picture接口的示例代码: ```java import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.util.Base64; import org.apache.http.HttpEntity; import org.apache.http.HttpHost; import org.apache.http.HttpResponse; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.util.EntityUtils; public class HttpTest { public static void main(String[] args) throws ClientProtocolException, IOException { String url = "http://192.168.1.1/ISAPI/Streaming/channels/1/picture"; String username = "admin"; String password = "admin123"; String auth = "Basic " + Base64.getEncoder().encodeToString((username + ":" + password).getBytes(StandardCharsets.UTF_8)); HttpHost proxy = new HttpHost("127.0.0.1", 8888); RequestConfig requestConfig = RequestConfig.custom() .setProxy(proxy) .build(); HttpUriRequest request = new HttpGet(url); request.setHeader("Authorization", auth); request.setConfig(requestConfig); try (CloseableHttpClient httpClient = HttpClientBuilder.create().build()) { HttpResponse response = httpClient.execute(request); HttpEntity entity = response.getEntity(); if (entity != null) { byte[] data = EntityUtils.toByteArray(entity); try (FileOutputStream fos = new FileOutputStream("image.jpg")) { fos.write(data); } } } } } ``` 注意,这里使用了Basic认证,需要将用户名和密码进行Base64编码后添加到请求头中。另外,如果需要使用代理,可以通过设置RequestConfig来指定代理。最后,将图片数据保存到本地文件中。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值