從5.0開始(API Level 21),可以完全控制安卓設(shè)備相機(jī)的新api Camera2(android.hardware.Camera2)被引入了進(jìn)來。在以前的Camera api(android.hardware.Camera)中,對相機(jī)的手動控制需要更改系統(tǒng)才能實現(xiàn),而且api也不友好。不過老的Camera API在5.0上已經(jīng)過時,如今Android推薦使用Camera2采集視頻,借著寫這篇記錄的過程,熟悉和理解Camera2流程。
專業(yè)性名詞
YUV
一種顏色編碼的方法,在舊Camera API 常用的是NV21和YV12,可以轉(zhuǎn)成RGB編碼。
//獲取Camera2 支持的顏色編碼
StreamConfigurationMap map = characteristics.get(
CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
map.getOutputFormats();
CameraManager
Camera2中負(fù)責(zé)管理、查詢攝像頭信息、打開可用的攝像頭;
- 可以通過調(diào)用Context.getSystemService(java.lang.String)方法來獲取一個CameraManager的實例;
- cameraId 通過 getCameraIdList() 枚舉得到,代表選擇使用哪個攝像頭;
- 設(shè)備信息通過 CameraCharacteristics getCameraCharacteristics(String cameraId) 可拿到;
- 打開攝像頭 openCamera(String cameraId, CameraManager.StateCallback callback, Handler handler),StateCallback是接收設(shè)備狀態(tài)的更新的回調(diào),比如后面的cameraDevice就是通過stateCallback的onOpen()回調(diào)中拿到,Handler 表示打開攝像頭的工作在具體哪個handler的looper中,也就是在哪個線程中執(zhí)行,若為null則在當(dāng)前線程;
CameraDevice
具體的攝像頭,提供一組屬性信息,描述硬件設(shè)備以及設(shè)備的可用設(shè)置和參數(shù)。
- CameraDevice是在CameraManager打開攝像頭后,通過CameraDevice.StateCallback的回調(diào)中拿到的,是個異步的過程;
- createCaptureRequest() 創(chuàng)建CaptureRequest.Builder,CaptureRequest.Builder負(fù)責(zé)創(chuàng)建各種捕獲圖像的請求 CaptureRequest;
- createCaptureSession() 負(fù)責(zé)創(chuàng)建捕獲圖像的會話
CameraCaptureSession;
CaptureRequest
一次捕獲請求,通過CaptureRequest.Builder的build()創(chuàng)建,其實請求參數(shù)也是通過Buider來設(shè)置:
CaptureRequest.Builder常用的方法:
- addTarget(Surface outputTarget) 將surface添加到輸出列表中,才可以顯示在SurfaceView、TextureView或者輸出到ImageReader中;
- set(Key< T> key, T value) 設(shè)置其他屬性;
CameraCaptureSession
捕獲的會話Session,預(yù)覽、拍照,都由該它進(jìn)行控制的。
- cameraCaptureSession是通過CameraDevice的 createCaptureSession(List< Surface>, CameraCaptureSession.StateCallback, Handler) 創(chuàng)建;
- 拍照 capture(CaptureRequest, CameraCaptureSession.CaptureCallback, Handler);
- 預(yù)覽 setRepeatingRequest(CaptureRequest, CameraCaptureSession.CaptureCallback, Handler);
camera結(jié)構(gòu)

CameraManager結(jié)構(gòu)
在寫代碼的時候發(fā)現(xiàn)上面幾個callback弄不清楚
- openCamera中的CameraManager.StateCallback 是 camra 創(chuàng)建過程中狀態(tài)回調(diào);
- createCaptureSession中的CameraCaptureSession.StateCallback 是session創(chuàng)建過程中的狀態(tài)回調(diào);
- capture 或 setRepeatingRequest的CameraCaptureSession.CaptureCallback 是在預(yù)覽或者拍照request請求之后的回調(diào);
流程
初始化執(zhí)行camera動作的線程和handler
private void startCameraThread() {
mCameraThread = new HandlerThread("CameraThread");
mCameraThread.start();
mCameraHandler = new Handler(mCameraThread.getLooper());
}
設(shè)置攝像頭屬性
private void setupCamera(int width, int height) {
//獲取攝像頭的管理者CameraManager
CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
try {
//遍歷所有攝像頭
for (String cameraId : manager.getCameraIdList()) {
CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING);
//此處默認(rèn)打開后置攝像頭
if (facing != null && facing == CameraCharacteristics.LENS_FACING_FRONT)
continue;
//獲取StreamConfigurationMap,它是管理攝像頭支持的所有輸出格式和尺寸
StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
assert map != null;
//根據(jù)TextureView的尺寸設(shè)置預(yù)覽尺寸
mPreviewSize = getOptimalSize(map.getOutputSizes(SurfaceTexture.class), width, height);
mCameraId = cameraId;
break;
}
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
//選擇sizeMap中大于并且最接近width和height的size
private Size getOptimalSize(Size[] sizeMap, int width, int height) {
List<Size> sizeList = new ArrayList<>();
for (Size option : sizeMap) {
if (width > height) {
if (option.getWidth() > width && option.getHeight() > height) {
sizeList.add(option);
}
} else {
if (option.getWidth() > height && option.getHeight() > width) {
sizeList.add(option);
}
}
}
if (sizeList.size() > 0) {
return Collections.min(sizeList, new Comparator<Size>() {
@Override
public int compare(Size lhs, Size rhs) {
return Long.signum(lhs.getWidth() * lhs.getHeight() - rhs.getWidth() * rhs.getHeight());
}
});
}
return sizeMap[0];
}
打開攝像頭
private void openCamera() {
CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
try {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
return;
}
manager.openCamera(mCameraId, mStateCallback, mCameraHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
開始預(yù)覽
在mStateCallback中預(yù)覽。
TextureView預(yù)覽
private void startPreview() {
SurfaceTexture mSurfaceTexture = mTextureView.getSurfaceTexture();
mSurfaceTexture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
Surface previewSurface = new Surface(mSurfaceTexture);
try {
mCaptureRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
mCaptureRequestBuilder.addTarget(previewSurface);
mCameraDevice.createCaptureSession(Arrays.asList(previewSurface, mImageReader.getSurface()), new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(CameraCaptureSession session) {
try {
mCaptureRequest = mCaptureRequestBuilder.build();
mCameraCaptureSession = session;
mCameraCaptureSession.setRepeatingRequest(mCaptureRequest, null, mCameraHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
@Override
public void onConfigureFailed(CameraCaptureSession session) {
}
}, mCameraHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
SurfaceView預(yù)覽
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private void startPreView(){
try {
Surface surface = mSurfaceHolder.getSurface();
mCaptureRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
if (surface!=null){
Log.d(TAG,"SURFACE不為空");
mCaptureRequestBuilder.addTarget(surface);
}else {
Log.d(TAG,"SURFACE為空");
}
mCameraDevice.createCaptureSession(Arrays.asList(surface), new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(@NonNull CameraCaptureSession session) {
mCaptureRequest = mCaptureRequestBuilder.build();
mCameraCaptureSession = session;
try {
mCameraCaptureSession.setRepeatingRequest(mCaptureRequest,null,mCameraHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
@Override
public void onConfigureFailed(@NonNull CameraCaptureSession session) {
}
},mCameraHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
停止預(yù)覽
@Override
protected void onPause() {
super.onPause();
if (mCameraCaptureSession != null) {
mCameraCaptureSession.close();
mCameraCaptureSession = null;
}
if (mCameraDevice != null) {
mCameraDevice.close();
mCameraDevice = null;
}
}