最近学习Android的camera模块,本文先介绍一下camera2的api,然后给出android camera拍照的例子,讲解一下camera 拍照的原因知识,与大家共勉。
- camera2 介绍
- android camera拍照功能介绍
一、camera2 介绍
- Camera api部分:
frameworks/base/core/java/android/hardware/camera2- Camera JNI部分:
frameworks/base/core/jni/android_hardware_Camera.cpp
编译选项在目录下的Android.bp
make libandroid_runtime -j1- Camera UI库部分:
frameworks/av/camera/
编译选项在目录下的Android.bp
make libcamera_client -j1- Camera服务部分:
frameworks/av/services/camera/libcameraservice/
编译选项在目录下的Android.mk
make libcameraservice -j1- Camera HAL部分:
hardware/qcom/camera/
android.hardware.camera2开发包给开发者提供了一个操作相机的开发包,是api-21提供的,用于替代之前的Camera操作控类。该软件包将摄像机设备建模为管道,它接收捕获单个帧的输入请求,根据请求捕获单个图像,然后输出一个捕获结果元数据包,以及一组请求的输出图像缓冲区。请求按顺序处理,多个请求可以立即进行。由于相机设备是具有多个阶段的管道,因此需要在移动中处理多个捕捉请求以便在大多数Android设备上保持完全帧率。
- 如果要操作相机设备,需要获取CameraManager实例。CameraDevices提供了一系列静态属性集合来描述camera设备,提供camera可供设置的属性和设备的输出参数。描述这些属性的是CameraCharacteristics实例,就是CameraManager的getCameraCharacteristics(String)方法。
- 为了捕捉或者流式话camera设备捕捉到的图片信息,应用开发者必须创建一个CameraCaptureSession,这个camera session中包含了一系列相机设备的输出surface集合。目标的surface一般通过SurfaceView, SurfaceTexture via Surface(SurfaceTexture), MediaCodec, MediaRecorder, Allocation, and ImageReader.
- camera 预览界面一般使用SurfaceView或者TextureView,,捕获的图片数据buffers可以通过ImageReader读取。
- TextureView可用于显示内容流。这样的内容流可以例如是视频或OpenGL场景。内容流可以来自应用程序的进程以及远程进程。
- TextureView只能在硬件加速窗口中使用。在软件中渲染时,TextureView将不会绘制任何内容。
- TextureView不会创建单独的窗口,但表现为常规视图。这一关键差异允许TextureView移动,转换和使用动画
- 之后,应用程序需要构建CaptureRequest,在捕获单个图片的时候,这些request请求需要定义一些请求的参数。
- 一旦设置了请求,就可以将其传递到活动捕获会话,以进行一次捕获或无休止地重复使用。两种方法还具有接受用作突发捕获/重复突发的请求列表的变体。重复请求的优先级低于捕获,因此在配置重复请求时通过capture()提交的请求将在当前重复(突发)捕获的任何新实例开始捕获之前捕获。
- 处理完请求后,摄像机设备将生成一个TotalCaptureResult
对象,该对象包含有关捕获时摄像机设备状态的信息以及使用的最终设置。如果需要舍入或解决相互矛盾的参数,这些可能与请求有所不同。相机设备还将一帧图像数据发送到请求中包括的每个输出表面。这些是相对于输出CaptureResult异步生成的,有时基本上稍晚。
根据camera2的工作示意图,画出下面的camera类关系图。下面使用camera功能的时候会详细介绍一下camera2 的api的主要功能。
二、android camera拍照功能介绍
2.1 设置camera preview预览页面
我们打开android camera一般会出现一个预览页面,通过这个预览页面,我么调用摄像头,将前方的景放在这个预览界面中,然后移动camera,预览界面会随之出现变化,实现这个预览界面的view,一般有两种选择,SurfaceView或者TextureView,上面我们也介绍了两种view之间的区别,本文我们选择TextureView,因为TextureView设置动画比较方便,我们移动或者旋转手机的时候,TextureView也应用相应的旋转,这样符合用户的体验。
可以通过设置TextureView.SurfaceTextureListener来对TextureView代表的surface监听。
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
public boolean onSurfaceTextureDestroyed(SurfaceTexture texture) {
return true;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture texture) {
}
};
几个监听事件监听当前surface的状态。onSurfaceTextureAvailable表示当前的surface状态可用,onSurfaceTextureSizeChanged表示当前的surface大小正在调整。
2.2 打开相机
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);
}
}
- 检查当前camera权限。
- 设置相机当前属性和输出图片等等。
- 设置TextureView 选装和移动的动画属性等等。
- 使用CameraManager实例打开相机
2.3 相机打开设置回调
在执行manager.openCamera(mCameraId, mStateCallback, mBackgroundHandler);方法的时候,传入了三个参数:
- mCameraId表示当前摄像头的标识,我们手机中有好多个摄像头,最新版的Mate20手机有3个后置摄像头和1个前置摄像,可以通过manager.getCameraIdList()来获取当前的cameraId集合。
- StateCallback是CameraDevice.StateCallback,这是表示相机设备当前状态的回调。
下面StateCallback的众多回调表示当前相机的状态,相机如果打开的话应该执行什么操作,相机如果断开连接的话应该执行什么操作等等。- 传入的Handler处理camera当前的消息。
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();
}
}
};
mCameraOpenCloseLock是一个信号量,一旦相机在openCamera到真正打开这段时间,相机必须被独占,其他线程不能介入处理,不然会出现线程错乱。一旦相机呈现出下一个状态,就可以释放这个信号量了。
private Semaphore mCameraOpenCloseLock = new Semaphore(1);
2.4 创建camera预览session
private void createCameraPreviewSession() {
try {
SurfaceTexture texture = mTextureView.getSurfaceTexture();
assert texture != null;
// We configure the size of default buffer to be the size of camera preview we want.
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();
}
}
- 设置SurfaceTexture缓存大小。
- 通过mCameraDevice.createCaptureRequest获取CaptureResult.Builder对象,这个对象要和之前的Surface绑定,表示当前的capture请求是输出到这个surface上的。
- mCameraDevice.createCaptureSession 创建capture session,在CameraCaptureSession.StateCallback的onConfigured回调函数中,就是对当前camera移动的回调,一旦移动,会触发这个回调,然后在回调中作出相应的改变。
2.5 拍照
private void takePicture() {
lockFocus();
}
/**
* Lock the focus as the first step for a still image capture.
*/
private void lockFocus() {
try {
// This is how to tell the camera to lock focus.
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
CameraMetadata.CONTROL_AF_TRIGGER_START);
// Tell #mCaptureCallback to wait for the lock.
mState = STATE_WAITING_LOCK;
mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback,
mBackgroundHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
Capture image的过程主要在 mCaptureSession.capture函数中,下面会从这个函数着手阐释一下camera capture的原理。
2.6 关闭相机
private void closeCamera() {
try {
mCameraOpenCloseLock.acquire();
if (null != mCaptureSession) {
mCaptureSession.close();
mCaptureSession = null;
}
if (null != mCameraDevice) {
mCameraDevice.close();
mCameraDevice = null;
}
if (null != mImageReader) {
mImageReader.close();
mImageReader = null;
}
} catch (InterruptedException e) {
throw new RuntimeException("Interrupted while trying to lock camera closing.", e);
} finally {
mCameraOpenCloseLock.release();
}
}
主要释放camera相关的资源,手机中camera的资源具有独占性,如果在用完了不释放的话,会造成别人使用的时候camera被占用,session不关闭的,会造成严重的内存泄露。
大家也可以参考具体的源码https://github.com/googlesamples/android-Camera2Basic/