android-Camera2Basic 解析

Android Camera2介绍

Android 5.0(API Level 21) 重新设计了 Camera,废弃了之前的 Camera,改用现在的 Camera2 API,在Camera2上引入了Session/Request的概念,使用的复杂度远超之前的 Camera。

Camera2主要类简介

  • CameraManager:摄像头管理类,用于检测,打开系统摄像头,可以通过getCameraCharacteristics(cameraId)获取摄像头的特征
  • CameraCharacteristics:相机的特性类,可以得到相机是否支持自动对焦,是否支持闪光灯等特征
  • CameraDevice:相机设备类
  • CameraCaptureSession:用于创建预览,拍照的Session类。
  • CaptureRequest:相机的请求类,可以通过这个类获取一次捕获的请求,可以设置一些参数等。
  • CameraRequestCameraRequest.Builder:当程序调用setRepeatingRequest()方法进行预览时,或调用capture()方法进行拍照时,都需要传入CameraRequest参数。CameraRequest代表了一次捕获请求,用于描述捕获图片的各种参数设置,比如对焦模式、曝光模式……总之,程序需要对照片所做的各种控制,都通过CameraRequest参数进行设置。CameraRequest.Builder则负责生成CameraRequest对象。

Camera2 API 基本架构

  • APP通过创建一个CaptureRequest向CameraDevice发起Capture请求
  • CameraDevice收到请求后返回对应数据的预览数据
  • 点击拍照后,会从ImageReader中读取数据
  • CaptureRequest代表请求控制的Camera参数, CameraMetadata(CaptureResult)则表示当前返回帧中Camera使用的参数以及当前状态.

使用流程

  • 初始化,打开Camera

    1. 通过

      1
      2
      3
      4
      5
      if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.CAMERA)
      != PackageManager.PERMISSION_GRANTED) {
      requestCameraPermission();
      return;
      }

      获取权限

    2. 通过 context.getSystemService(Context.CAMERA_SERVICE) 获取CameraManager
    3. 通过CameraManager.getCameraCharacteristics(cameraId)获取相机的摄像头的特性

      • SENSOR_ORIENTATION:获取摄像头的拍照的方向
      • LENS_FACING:获取摄像头的方向。LENS_FACING_FRONT是前摄像头,LENS_FACING_BACK是后摄像头。
      • 获取FPS的范围
      • 获取大小
    4. 调用CameraManager .open()方法在回调中得到CameraDevice.
  • Create Session
    通过CameraDevice.createCaptureSession()在回调中获取CameraCaptureSession.
  • Config Session
    构建CaptureRequest, 有三种模式可选 预览/拍照/录像.
  • Capture
    拍照数据可以在ImageReader.OnImageAvailableListener回调中获取, CaptureCallback中则可获取拍照实际的参数和Camera当前状态.

android-Camera2Basic介绍

android-Camera2Basic是谷歌官方给的Camera2的demo,演示了Camrea2 API的基本功能,通过这个Demo,我们可以掌握连接设备,显示预览,以及拍照的基本使用。

项目结构

1
2
3
4
5
├── .DS_Store
└── camera2basic
├── AutoFitTextureView.java
├── Camera2BasicFragment.java
└── CameraActivity.java

代码详解

打开相机,创建预览

openCamera

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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);
}
}
setUpCameraOutputs(width, height)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
@SuppressWarnings("SuspiciousNameCombination")
private void setUpCameraOutputs(int width, int height) {
Activity activity = getActivity();
CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE);
try {
//获取摄像头可用列表
for (String cameraId : manager.getCameraIdList()) {
//获取相机的特性
CameraCharacteristics characteristics
= manager.getCameraCharacteristics(cameraId);

// 不使用前置摄像头
Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING);
if (facing != null && facing == CameraCharacteristics.LENS_FACING_FRONT) {
continue;
}

StreamConfigurationMap map = characteristics.get(
CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
if (map == null) {
continue;
}

// For still image captures, we use the largest available size.
Size largest = Collections.max(
Arrays.asList(map.getOutputSizes(ImageFormat.JPEG)),
new CompareSizesByArea());
//设置ImageReader接收的图片格式,以及允许接收的最大图片数目
mImageReader = ImageReader.newInstance(largest.getWidth(), largest.getHeight(),
ImageFormat.JPEG, /*maxImages*/2);
//设置图片存储的监听
mImageReader.setOnImageAvailableListener(
mOnImageAvailableListener, mBackgroundHandler);

...
}

CameraDevice.StateCallback监听

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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;
//创建PreviewSession
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();
}
}

};

createCaptureSession

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
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();
}
}

项目中有注释,不做过多说明

拍照

拍照的过程就是我们向已经打开好的相机获取静态预览帧的过程。

对焦

1
2
3
4
5
6
7
8
9
10
11
12
13
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();
}
}

CameraCaptureSession发送对焦请求,并且对对焦是否成功进行监听,在mCaptureCallback中对回调进行处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
private CameraCaptureSession.CaptureCallback mCaptureCallback
= new CameraCaptureSession.CaptureCallback() {

private void process(CaptureResult result) {
switch (mState) {
case STATE_PREVIEW: {
// We have nothing to do when the camera preview is working normally.
break;
}
//等待对焦
case STATE_WAITING_LOCK: {
Integer afState = result.get(CaptureResult.CONTROL_AF_STATE);
if (afState == null) {
captureStillPicture();
} else if (CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED == afState ||
CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED == afState) {
// CONTROL_AE_STATE can be null on some devices
Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
if (aeState == null ||
aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED) {
mState = STATE_PICTURE_TAKEN;
//对焦完成
captureStillPicture();
} else {
runPrecaptureSequence();
}
}
break;
}
case STATE_WAITING_PRECAPTURE: {
// CONTROL_AE_STATE can be null on some devices
Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
if (aeState == null ||
aeState == CaptureResult.CONTROL_AE_STATE_PRECAPTURE ||
aeState == CaptureRequest.CONTROL_AE_STATE_FLASH_REQUIRED) {
mState = STATE_WAITING_NON_PRECAPTURE;
}
break;
}
case STATE_WAITING_NON_PRECAPTURE: {
// CONTROL_AE_STATE can be null on some devices
Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
if (aeState == null || aeState != CaptureResult.CONTROL_AE_STATE_PRECAPTURE) {
mState = STATE_PICTURE_TAKEN;
captureStillPicture();
}
break;
}
}
}

拍摄图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
private void captureStillPicture() {
try {
final Activity activity = getActivity();
if (null == activity || null == mCameraDevice) {
return;
}
// This is the CaptureRequest.Builder that we use to take a picture.
final CaptureRequest.Builder captureBuilder =
mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
captureBuilder.addTarget(mImageReader.getSurface());

// Use the same AE and AF modes as the preview.
captureBuilder.set(CaptureRequest.CONTROL_AF_MODE,
CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
setAutoFlash(captureBuilder);

// Orientation
int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, getOrientation(rotation));

CameraCaptureSession.CaptureCallback CaptureCallback
= new CameraCaptureSession.CaptureCallback() {

@Override
public void onCaptureCompleted(@NonNull CameraCaptureSession session,
@NonNull CaptureRequest request,
@NonNull TotalCaptureResult result) {
showToast("Saved: " + mFile);
Log.d(TAG, mFile.toString());
unlockFocus();
}
};

mCaptureSession.stopRepeating();
mCaptureSession.abortCaptures();
mCaptureSession.capture(captureBuilder.build(), CaptureCallback, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}

代码中有相应的注释。
注意Demo中的onCaptureCompleted完成后,又调用unlockFocus()解锁了焦点。是因为Demo做的是连续拍照,如果实现的是只拍一张照片,需要将unlockFocus()去掉。

存图片

存图片的操作是在之前设置ImageReader的监听方法里面进行存储的。

1
2
3
4
5
6
7
8
9
private final ImageReader.OnImageAvailableListener mOnImageAvailableListener
= new ImageReader.OnImageAvailableListener() {

@Override
public void onImageAvailable(ImageReader reader) {
mBackgroundHandler.post(new ImageSaver(reader.acquireNextImage(), mFile));
}

};

参考资料

Android Camera2 使用总结

Android Camera2 简介

×

纯属好玩

扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

文章目录
  1. 1. Android Camera2介绍
    1. 1.1. Camera2主要类简介
    2. 1.2. Camera2 API 基本架构
    3. 1.3. 使用流程
  2. 2. android-Camera2Basic介绍
    1. 2.1. 项目结构
    2. 2.2. 代码详解
      1. 2.2.1. 打开相机,创建预览
        1. 2.2.1.1. openCamera
          1. 2.2.1.1.1. setUpCameraOutputs(width, height)
        2. 2.2.1.2. CameraDevice.StateCallback监听
        3. 2.2.1.3. createCaptureSession
      2. 2.2.2. 拍照
        1. 2.2.2.1. 对焦
        2. 2.2.2.2. 拍摄图片
        3. 2.2.2.3. 存图片
  3. 3. 参考资料
,