1、获取CameraManager 相机管理器
CameraManager是通过获取系统服务方式获取CameraManager对象
// 获取CameraManager 相机设备管理器
mCameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
2、打开摄像头并展示预览画面
2.1 监听显示预览控件状态
activity被显示出来(执行到Activity的onResume方法)时,判断显示相机预览的控件TextTure是否可用,可用的话则打开相机;否则注册SurfaceTextureListener监听器对象,当可用是会回调onSurfaceTextureAvailable()函数,在此函数中进行打开相机:
1、创建activity时第一次走到OnResume()时,会先走else逻辑,因为此时TextTure还不可用,需要注册SurfaceTextureListener监听器,监听器中监听到TextTure可用时(即onSurfaceTextureAvailable)这时会首次打开摄像头;
2、后面再进入到activity的OnResume()时,则直接打开摄像头。
@Override
protected void onResume() {
super.onResume();
if (texture.isAvailable()) {
openCamera(texture.getWidth(), texture.getHeight(), mCameraId);
} else {
texture.setSurfaceTextureListener(textureListener);
}
startBackgroundThread();
}
/**
* TextureView 生命周期响应
*/
private final TextureView.SurfaceTextureListener textureListener = new TextureView.SurfaceTextureListener() {
@Override //创建
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
//当TextureView创建完成,打开指定摄像头相机
openCamera(width, height, mCameraId);
}
@Override //尺寸改变
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
}
@Override //销毁
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
return false;
}
@Override //更新
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
}
};
2.2 打开摄像头,展示相机预览
/**
* 打开指定摄像头ID的相机
*
* @param width
* @param height
* @param cameraId
*/
private void openCamera(int width, int height, int cameraId) {
if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
// TODO: Consider calling
//return;
requestPermission();
}
try {
mSurfaceWidth = width;
mSurfaceHeight = height;
getCameraId(cameraId);
CameraCharacteristics characteristics = mCameraManager.getCameraCharacteristics(mCameraId + "");
StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
// 获取设备方向
int rotation = getWindowManager().getDefaultDisplay().getRotation();
int totalRotation = sensorToDeviceRotation(characteristics, rotation);
boolean swapRotation = totalRotation == 90 || totalRotation == 270;
int rotatedWidth = mSurfaceWidth;
int rotatedHeight = mSurfaceHeight;
if (swapRotation) {
rotatedWidth = mSurfaceHeight;
rotatedHeight = mSurfaceWidth;
}
// 获取最佳的预览尺寸
mPreviewSize = getPreferredPreviewSize(map.getOutputSizes(SurfaceTexture.class), rotatedWidth, rotatedHeight);
if (swapRotation) {
texture.setAspectRatio(mPreviewSize.getWidth(), mPreviewSize.getHeight());
} else {
texture.setAspectRatio(mPreviewSize.getHeight(), mPreviewSize.getWidth());
}
if (mImageReader == null) {
// 创建一个ImageReader对象,用于获取摄像头的图像数据,maxImages是ImageReader一次可以访问的最大图片数量
mImageReader = ImageReader.newInstance(mPreviewSize.getWidth(), mPreviewSize.getHeight(),
ImageFormat.JPEG, 2);
mImageReader.setOnImageAvailableListener(mOnImageAvailableListener, mBackgroundHandler);
}
//检查是否支持闪光灯
Boolean available = characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE);
mFlashSupported = available == null ? false : available;
mCameraManager.openCamera(mCameraId + "", mStateCallback, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
1、要打开摄像头,需要先检测app的相机权限;
2、记录TextTure的宽高值;
3、通过cameramanager对象获取指定摄像头的特性信息CameraCharacteristics(下文详解);
4、通过characteristics对象获取相机支持的可用流配置;
5、获取android手机当前摆放的方向;
6、根据设备方向和摄像头方向,经过处理后得出最终的预览页控件的宽高大小(可能会发生旋转)。
7、从CameraCharacteristics中选择一个最匹配TextTure大小的宽高值;
8、然后重新设定TextTure的大小;
9、创建ImageReader图像读取流对象,ImageReader可以让应用直接访问到相机的image 数据,并渲染到Surface上,也就是TextTure控件上;创建ImageReader需要指定预览页的宽、高和图像的格式,和最大image数。
10、给ImageReader对象设置监听器OnImageAvailableListener; (ImageReader对象在打开摄像头显示预览这里其实不是必须的,可以在实际拍照的时候创建也可以)
11、检查摄像头是否支持开启闪光灯;
12、cameramanager对象调用openCamera()打开指定摄像头,并注册了状态回调对象StateCallback,因为openCamera()函数内部其实做了很多事情,是一个耗时动作,所以这里使用了回调函数异步执行。
13、执行打开摄像头动作后,当检测到摄像头已成功打开,则回调到onOpened(),可以获取到摄像头对象CameraDevice,这个类很重要。接下来就可以在该函数中创建预览了;
14、配置相机预览页大小,创建一个显示预览图像的缓冲区Surface。
15、通过CameraDevice构建一个预览请求构建器对象CaptureRequest.Builder,通过这个构建器设置预览请求对象的相关信息,比如指定预览数据显示位置,即Surface;
16、通过CameraDevice建立一个捕获会话CameraCaptureSession,有了这个会话对象后,后面的所有动作都是基于这个会话对象来操作了;创建会话时,也是注册了一个会话状态回调对象异步地执行接下来的动作;
17、当会话成功建立后,会回调onConfigured()函数;可以在该函数中继续设置预览请求对象的属性,比如设置自动对焦,设置闪光灯模式;
18、最后,预览请求构建器对象调用build()构建了一个捕获请求对象,并通过captureSession会话设置一个无休止的预览请求(即预览),即mCaptureSession.setRepeatingRequest(mPreviewRequest, null, mBackgroundHandler);
通过这种无休止地发生预览请求实现了相机的预览。
19、到这里TextTure对象中的Surface上就成功展示了相机预览画面数据了。