一、摘要
本篇文章阐述初始化流程中需要设置的默认参数包括如下:
- DisplayOrientation
- PreviewSize
- PictureSize
- DefaultFocusParameters
- DefaultFlashParameters
二、伪代码
先把函数写出来
private void applyDefaultParameters(){
}
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;
} else {
result = (info.orientation - degrees + 360) % 360;
}
camera.setDisplayOrientation(result);
}
分析:
- 这里我们已经在openCamera里获取到mFacing的信息了,可以省区代码里的getCameraInfo逻辑。
- 在某些兼容性手机上,方向可能有异常,需要做特殊的兼容逻辑处理
- setDisplayOrientation会抛异常,但是不是致命问题
因此可改造代码如下:
private int applyDefaultOrientation() {
int displayOrientation;
if(CameraCompat.Camera1.isOrientationExcepDevice(Build.Model)){
displayOrientation = CameraCompat.Camera1.getOrientationForExcepDevices();
}else{
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;
}
if (mFacing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
displayOrientation = (mOrientation + degrees) % 360;
displayOrientation = (360 - displayOrientation) % 360;
} else {
displayOrientation = (mOrientation - degrees + 360) % 360;
}
}
try{
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() {
SortedSet<Size> sizes = mPreviewSizes.sizes(mAspectRatio);
if (sizes == null) {
mAspectRatio = chooseAspectRatio();
sizes = mPreviewSizes.sizes(mAspectRatio);
}
Size size = chooseOptimalSize(sizes);
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()) {
return sizes.first();
}
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) {
if (desiredWidth <= size.getWidth() && desiredHeight <= size.getHeight()) {
return size;
}
result = size;
}
return result;
}
分析:
- 通过mShowingPreview字段来判断是否stopPreview
- 通过aspectRatio来筛选
- 计算和aspectRatio匹配的分辨率
previewSize是相机支持的默认分辨率,每种分辨率有不同的宽高比。常见的有
具体的根据用户体验层面来进行设定。
对应的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{
params = mCamera.getParameters();
}catch(Exception e){
...
}
if(params == null){
return;
}
List<Size> previewSize = null;
try{
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 。只需:
- getSupportedPreviewSizes -> getSupportedPictureSizes
- 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(){
applyDefaultOrientation();
applyDefaultPreviewSize();
applyDefaultPictureSize();
applyDefaultFocusMode();
applyDefaultFlashMode();
}
下一篇将阐述 Camera1初始化销毁流程(六) —— Camera1Impl类之startPreview
|