您开发相机相关的功能,可以参考相机开发进行开发。
7.2.1 Camera API
使用流程:
1. 检查相机权限(android.permission.CAMERA);
2. Camera.getNumberOfCameras():获取相机硬件数量IdeaHub默认1个,cameraId 为“0”;
3. Camera.getCameraInfo():获取指定相机信息;
4. Camera.open():打开指定相机;
5. camera.getParameters():获取相机参数并设置;(ImageFormat.NV21始终受支 持);
6. camera.setPreviewTexture(SurfaceTexture surfaceTexture):设置用于相机预览的 surfaceTexutre;
7. camera.setDisplayOrientation(degrees):设置预览旋转角度;
8. camera.startPreview():开始预览;
关键类:
1. Camera 相机类,定义了open、startPreview、takePicture、AutoFocus、
setDisplayOrientation、setPreviewTexture等方法;
2. 实例方式:mCamera = Camera.open(cameraId);
3. Camera.CameraInfo 相机信息类,元数据获取;
4. Camera.Parameters 相机参数类,参数设置,定义了setFocusMode,
setPreviewSize、setFlashMode等方法,通过mCamera.getParameters()获取;
以下为关键函数的具体调用方法:
(1)初始化SurfaceviewHolder,并添加回调方法;获取摄像头,开始预览。
private void init() {
previewSurfaceHolder = previewSurfaceview.getHolder();
previewSurfaceHolder.addCallback(this);
if (camera == null) { camera = Camera.open(0);
startCamera(camera);
}}
(2) 给SurfaceViewHolder对象添加回调函数;
@Override
public void surfaceCreated(SurfaceHolder holder) { if (holder == previewSurfaceHolder) {
this.previewSurfaceInit = true;
}if (this.previewSurfaceInit) { camera = Camera.open(0);
startCamera(camera);
}}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { if (!checkPermissionAllGranted(PERMISSIONS_STORAGE)) {
return;
}}
@Override
public void surfaceDestroyed(SurfaceHolder holder) { if (holder == previewSurfaceHolder && null != camera) { camera.setPreviewCallback(null);
camera.stopPreview();
camera.release();
camera = null;
}}
说明
首先对mSurfaceHolder添加了一个回调,这个回调会告诉我们SurfaceView中surface的变化;
在surfaceCreated 回调中打开相机。因为相机开始预览的时候,如果SurfaceView中的surface还 没有创建,就会抛出异常,所以我们在surface创建后再对相机进行操作。
(3) 开始预览
/*** 开始预览相机内容
*/private void startCamera(Camera mCamera) { if (mCamera == null) {
return;
}try {
parameters = mCamera.getParameters();
parameters.setWhiteBalance(Camera.Parameters.WHITE_BALANCE_AUTO); // 自动白平衡 parameters.setPreviewFormat(ImageFormat.NV21); // 预览格式
parameters.setPreviewSize(width, height); // 预览大小
mCamera.setPreviewCallback(this);
mCamera.setParameters(parameters);
mCamera.setPreviewDisplay(previewSurfaceHolder);
mCamera.startPreview();
} catch (IOException e) { e.printStackTrace();
}}
说明
预览前可以设置预览相关的一些参数,目前IdeaHub不支持自动对焦,不支持白平衡。
7.2.2 Camera2 API
使用流程:
1. 检查相机权限(android.permission.CAMERA);
2. cameraManager = (CameraManager) getSystemService(CAMERA_SERVICE);
3. cameraManager.getCameraIdList():获取相机硬件id集合;
4. cameraManager.getCameraCharacteristics(cameraId):获取相机信息;
5. cameraManager.openCamera(cameraId,cameraCallback,null); 开启相机;
6. CameraDevice.StateCallback回调中获取cameraDevice;
7. cameraDevice.createCaptureSession(surfaces,new
CameraCaptureSession.StateCallback(),null) 创建捕获会话,surfaces作为捕获的图 像数据的输出的目标;
8. CaptureRequest.Builder 创建请求,通过键值对的方式设置参数;
9. cameraCaptureSession.setRepeatingRequest(previewRequest, captureCallback, null);发送请求;
关键类:
1. CameraManager 相机管理器,在Camera打开之前主要操作CameraManager,打 开后主要操作CameraCaptureSession;
2. getSystemService(Context.CAMERA_SERVICE);
3. CameraCharacteristics 摄像头属性,相当于Camera1的CameraInfo。 通过获取 Camera属性信息,配置Camera输出,如FPS,大小,旋转等;
4. mCameraManager.getCameraCharacteristics(currentCameraId);
5. CameraDevice 用于创建CameraCaptureSession和关闭摄像头;
6. cameraManager.openCamera(cameraId,cameraCallback,null);cameraCallback 的回调中获取到实例对象;
7. CameraCaptureSession 相机捕获会话,CameraCaptureSession建立了一个和 Camera硬件设备的会话通道,可以通过这个会话向Camera发送请求获取图像;
setRepeatingRequest():控制预览;capture():控制拍照;
8. CaptureRequest.Builder 请求构造器,Map,以键值对的方式设置请求参数;
9. CaptureRequest 预览或拍照,都需要CaptureRequest参数。CaptureRequest代表 了一次捕获请求,其中包含了捕获图片的参数设置,比如对焦模式、曝光模式等;
以下为关键函数的具体调用方法:
界面显示的时候调用onResume方法,在onResume方法里开启相机操作线程,添加 TextureViewSurface的监听方法,在TextureView准备好的时候,打开相机。
private final TextureView.SurfaceTextureListener mSurfaceTextureListener
= new TextureView.SurfaceTextureListener() {
@Override
public void onSurfaceTextureAvailable(SurfaceTexture texture, int width, int height) { openCamera(width, height);
}@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture texture, int width, int height) { configureTransform(width, height);
}@Override
publicboolean onSurfaceTextureDestroyed(SurfaceTexture texture) { return true;
}@Override
public void onSurfaceTextureUpdated(SurfaceTexture texture) { }};
(1) 初始化相机配置,打开相机。
private void openCamera(int width, int height) {
if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) { requestCameraPermission();
return;
}setUpCameraOutputs(width, height);
configureTransform(width, height);
Activity activity = getActivity();
CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE);
try {
if (!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) { throw new RuntimeException("Time out waiting to lock camera opening.");
}manager.openCamera(mCameraId, mStateCallback, mBackgroundHandler);
} catch (CameraAccessException e) { e.printStackTrace();
} catch (InterruptedException e) {
throw new RuntimeException("Interrupted while trying to lock camera opening.", e);
}}
说明
1、 首先,获取权限;
2、 配置相机参数时,要获取到 CameraManager 实例;
3、 通过循环遍历设备中可用的相机,通过 mCameraManager.getCameraCharacteristics(id) 获取到相机的各种信息;
4、 mCameraCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION) 获取到相机 传感器的方向;
5、 通过 configurationMap.getOutputSizes(ImageFormat.JPEG) 和
configurationMap.getOutputSizes(SurfaceTexture::class.java) 获取到相机支持的预览尺寸和保 存图片的尺寸;
6、 exchangeWidthAndHeight(displayRotation: Int, sensorOrientation: Int)方法的作用是根据 屏幕方向和摄像头方向确定是否需要交换宽高;
7、 比如我们手机竖屏放置,设置的预览宽高是 720 * 1280 ,我们希望设置的是宽为 720,高 为 1280 。 而后置摄像头相对于竖直方向是 90°,也就说 720 相对于是摄像头来说是它的高 度,1280 是它的宽度,这跟我们想要设置的刚好相反。所以,我们通过
exchangeWidthAndHeight这个方法得出来是否需要交换宽高值,如果需要,那变成了把 1280
* 720 设置给摄像头,即它的宽为 720,高为 1280 。这样就与我们预期的宽高值一样了;
8、 通过 getBestSize(targetWidth: Int, targetHeight: Int, maxWidth: Int, maxHeight: Int, sizeList: List<Size>) 方法获取到最优的宽和高。 根据传入的 目标宽高值、最大宽高值(即屏幕 大小)和 相机支持的尺寸列表,从相机支持的尺寸列表中得到一个最优值;
mImageReader = ImageReader.newInstance(largest.getWidth(), largest.getHeight(), ImageFormat.JPEG, /*maxImages*/
2);mImageReader.setOnImageAvailableListener(mOnImageAvailableListener, mBackgroundHandler);
创建一个ImageReader对象,并设置回调函数。前两个参数代表保存图片的宽高,第三个参数是 保存图片的格式,第四个参数代表用户同时可以得到的图片最大数。
(2) 打开相机后触发回调方法stateCallBack,回调中onOpen方法中开启预览。
private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {
@Override
public void onOpened(@NonNull CameraDevice cameraDevice) {
// This method is called when the camera is opened. We start camera preview here.
mCameraOpenCloseLock.release();
mCameraDevice = cameraDevice;
createCameraPreviewSession();
}@Override
public void onDisconnected(@NonNull CameraDevice cameraDevice) { mCameraOpenCloseLock.release();
cameraDevice.close();
mCameraDevice = null;
}@Override
public void onError(@NonNull CameraDevice cameraDevice, int error) { mCameraOpenCloseLock.release();
cameraDevice.close();
mCameraDevice = null;
Activity activity = getActivity();
if (null != activity) { activity.finish();
}} };
(3) 创建预览会话。
private void createCameraPreviewSession() { try {
SurfaceTexture texture = mTextureView.getSurfaceTexture();
assert texture != null;
texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
// This is the output Surface we need to start preview.
Surface surface = new Surface(texture);
// We set up a CaptureRequest.Builder with the output Surface.
mPreviewRequestBuilder
= mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
mPreviewRequestBuilder.addTarget(surface);
// Here, we create a CameraCaptureSession for camera preview.
mCameraDevice.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()), new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) { // The camera is already closed
if (null == mCameraDevice) { return;
}// When the session is ready, we start displaying the preview.
mCaptureSession = cameraCaptureSession;
try {
// Auto focus should be continuous for camera preview.
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
// Flash is automatically enabled when necessary.
setAutoFlash(mPreviewRequestBuilder);
// Finally, we start displaying the camera preview.
mPreviewRequest = mPreviewRequestBuilder.build();
mCaptureSession.setRepeatingRequest(mPreviewRequest, mCaptureCallback, mBackgroundHandler);
} catch (CameraAccessException e) { e.printStackTrace();
}}
@Override
public void onConfigureFailed(
@NonNull CameraCaptureSession cameraCaptureSession) { showToast("Failed");
}}, null
);} catch(CameraAccessException e) { e.printStackTrace();
}}
说明
1、 通过cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW) 创建一个 用于预览的Builder对象;
2、 为该Builder对象添加一个Surface对象,并设置各种相关参数;
3、 通过cameraDevice.createCaptureSession创建一个会话,第一个参数中传了一个 surface 和 mImageReader.getSurface()。这表明了这次会话的图像数据的输出到这两个对象;
4、 当会话创建成功时,通过 session.setRepeatingRequest(captureRequestBuilder.build(), mCaptureCallBack, mCameraHandler) 发起预览请求。