现在的工作需要用到camera模块,所以打算分析Zxing中的camera实现,来了解android的camera。zxing有完整的camera控制。
废话不多说,开始camera的分析之旅!
一.源码下载 https://github.com/zxing/zxing
二.CameraManager类,就是我们要分析的第一个类。
接下来看一下CameraManager这个类为我们提供了什么方法
1.openDriver(SurfaceHolder holder) 打开摄像头
2.isOpen() 判断摄像头是否打开
3.closeDriver() 关闭摄像头
4.startPreview() 启动预览
5.stopPreview() 关闭预览
6.setTorch(boolean newSetting) 设置对焦
7.requestPreviewFrame(Handler handler, int message) 获取预览的图片(重要的一个方法)
8.getFramingRect() 获取矩形框(根据屏幕分辨率然后按照一个固定的比例来设置方框大小)
9.getFramingRectInPreview() 获取扫描区域的矩形框(getFramingRect是UI的显示,这是扫描的区域,其实竖屏扫描的时候,形式是正方形的框,但是获取到的图片确实长方形的)
10.setManualCameraId(int cameraId) 设置camera 的Id
11.setManualFramingRect(int width, int height) 设置矩形框的宽高(UI)
12.buildLuminanceSource(byte[] data, int width, int height) 对byte[] Source进行统一的处理(不同设备,相机返回的Source是不一样的)
三.关键代码分析
1.打开摄像头OpenCamera
OpenCameraInterface打开打开摄像头的控制类,OpenCamera类存放打开摄像头的对象,CameraFacing类,枚举,两个参数,前置和后置摄像头。staticOpenCameraopen方法对各种摄像头的情况做了判断,例如相判断该设备有没有摄像头,然后判断打开的摄像头是不是空,空就启动后置摄像头等,具体看下面的代码。
public staticOpenCameraopen(intcameraId) {
intnumCameras = Camera.getNumberOfCameras();//获取摄像头的个数
if(numCameras ==0) {
return null;
}
boolean explicitRequest = cameraId >=0;
Camera.CameraInfo selectedCameraInfo =null;
intindex;
if(explicitRequest) {
index = cameraId;
selectedCameraInfo =newCamera.CameraInfo();
Camera.getCameraInfo(index,selectedCameraInfo);
}else{//启动后置摄像头
index =0;
while(index < numCameras) {//不同的设备,摄像头的个数不一样
Camera.CameraInfo cameraInfo =newCamera.CameraInfo();
Camera.getCameraInfo(index,cameraInfo);
CameraFacing reportedFacing = CameraFacing.values()[cameraInfo.facing];
if(reportedFacing == CameraFacing.BACK) {
selectedCameraInfo = cameraInfo;
break;
}
index++;
}
}
Camera camera;
if(index < numCameras) {
Log.i(TAG,"Opening camera #"+ index);
camera = Camera.open(index);//打开摄像头
}else{
if(explicitRequest) {
Log.w(TAG,"Requested camera does not exist: "+ cameraId);
camera =null;
}else{
Log.i(TAG,"No camera facing "+ CameraFacing.BACK+"; returning camera #0");
camera = Camera.open(0);
selectedCameraInfo =newCamera.CameraInfo();
Camera.getCameraInfo(0,selectedCameraInfo);
}
}
if(camera ==null) {
return null;
}
return newOpenCamera(index,
camera,
CameraFacing.values()[selectedCameraInfo.facing],
selectedCameraInfo.orientation);
}
2.CameraManager类获取预览图片setOneShotPreviewCallback这个接口就是获取预览图片,previewCallback是回调接口
public synchronized voidrequestPreviewFrame(Handler handler, intmessage) {
OpenCamera theCamera =camera;
if(theCamera !=null&&previewing) {
previewCallback.setHandler(handler,message);
theCamera.getCamera().setOneShotPreviewCallback(previewCallback);
}
}
3.CameraConfigurationManager类,相机的配置管理类initFromCameraParameters初始化方法。根据不同的设备,还有横竖屏的方向,给Camera计算出最好的size。然后通过setDesiredCameraParameters方法设置Camera的配置参数,例如设置聚焦模式,照片的大小,场景,特效等等。
(
一些常用的配置:
1、setPictureFormat()方法用于设置相机照片的格式,其参数是一个字符型参数,位于PixelFormat类中,我们在这里选择PixelFormat.JPEG。)
2、setSceneMode()方法用于设置相机场景类型,其参是是一个字符型参数,位于Parameters类中,以SCENE_MODE_开头。
3、setZoom()方法用于设置相机焦距,其参数是一个整型的参数,该参数的范围是0到Camera.getParameters().getMaxZoom()。
4、setPictureSize()方法用于设置相机照片的大小,参数为整型。
5、setWhiteBalance(),方法用于设置相机照片白平衡,其参数是一个字符型参数,位于Parameters类中,以WHITE_BALANCE开头。
6、setJpegQuality()方法用于设置相机照片的质量,其参数是一个整型参数,取值范围为1到100。
7、setFlashMode()方法用于设置闪光灯的类型,其参数是一个字符型参数,位于Parameters类中,以FLASH_MODE_开头。
8、setColorEffect()方法用于设置照片颜色特效的类型,其参数是一个字符型参数,位于Parameters类中,以EFFECT_开头。
)
接下来看一下代码:
void initFromCameraParameters(OpenCamera camera) {//初始化配置
Camera.Parameters parameters = camera.getCamera().getParameters();
WindowManager manager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
Display display = manager.getDefaultDisplay();
intdisplayRotation = display.getRotation();
intcwRotationFromNaturalToDisplay;
switch(displayRotation) { //手机的方向
caseSurface.ROTATION_0:
cwRotationFromNaturalToDisplay =0;
break;
caseSurface.ROTATION_90:
cwRotationFromNaturalToDisplay =90;
break;
caseSurface.ROTATION_180:
cwRotationFromNaturalToDisplay =180;
break;
......
cameraResolution= CameraConfigurationUtils.findBestPreviewSizeValue(parameters,screenResolution); //寻找最好的PreviewSize
Log.i(TAG,"Camera resolution: "+cameraResolution);
bestPreviewSize= CameraConfigurationUtils.findBestPreviewSizeValue(parameters,screenResolution);
......
if(isScreenPortrait == isPreviewSizePortrait) { //根据横竖屏给previewSizeOnScreen赋值
previewSizeOnScreen=bestPreviewSize;
}else{
previewSizeOnScreen=newPoint(bestPreviewSize.y,bestPreviewSize.x);
}
......
}
CameraConfigurationUtils.findBestPreviewSizeValue这个方法的实现逻辑:(代码不贴了)
首先,查找手机支持的预览尺寸集合,如果集合为空,就返回默认的尺寸;否则,对尺寸集合根据尺寸的像素从小到大进行排序;
其次,移除不满足最小像素要求的所有尺寸;
在者,在剩余的尺寸集合中,剔除预览宽高比与屏幕分辨率宽高比之差的绝对值大于0.15的所有尺寸;
最后,寻找能够精确的与屏幕宽高匹配上的预览尺寸,如果存在则返回该宽高比;如果不存在,则使用尺寸集合中最大的那个尺寸。如果说尺寸集合已经在前面的过滤中被全部排除,则返回相机默认的尺寸值。
附加:UI的简单分析
1.CaptureActivity是扫描的UI实现,数据的传递都是通过Handler实现的,设置都保存到Preference里面,PreferenceManager就是设置保存的管理类,ViewfinderView继承View,camera的UI显示就是通过这个View。
CaptureActivity类的部分代码
public voidonCreate(Bundle icicle) {
super.onCreate(icicle);
Window window = getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);//设置常亮
......
PreferenceManager.setDefaultValues(this,R.xml.preferences, false);//设置默认的配置,文件放在xml文件夹下
}
看一下onResume方法
protected void onResume() {
//... 省略初始化的操作
//根据配置文件来和传感器切换横竖屏的显示
if(prefs.getBoolean(PreferencesActivity.KEY_DISABLE_AUTO_ORIENTATION, true)) {
getCurrentOrientation();
}else{
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
}
resetStatusView();
//...
if(Intents.Scan.ACTION.equals(action)) {
// 判断跳转过来的时候是否带上了宽度,高度
if(intent.hasExtra(Intents.Scan.WIDTH) && intent.hasExtra(Intents.Scan.HEIGHT)) {
int width = intent.getIntExtra(Intents.Scan.WIDTH,0);
int height = intent.getIntExtra(Intents.Scan.HEIGHT,0);
if(width >0&& height >0) {
cameraManager.setManualFramingRect(width,height);
}
}
//... 省略一些传参和赋值
SurfaceView surfaceView = (SurfaceView) findViewById(R.id.preview_view);
SurfaceHolder surfaceHolder = surfaceView.getHolder();
if(hasSurface) {
initCamera(surfaceHolder);
//最后初始化相机(主要就是这两个方法,打开相机和初始化Handler
//cameraManager.openDriver(surfaceHolder);
//handler=new CaptureActivityHandler(this,decodeFormats,decodeHints,characterSet,cameraManager);)
}
2.其他还有对图片的处理类PlanarYUVLuminanceSource和HybridBinarizer类,不同的设备获取的照片是不一样的,所以必须要做处理
总结:camera部分就分析到这里,如果上述有错误,请指出,谢谢!
此文由暴风雨1024原创, 转载,请注明出处,谢谢!