Android Camera2入門
Android Camera2入門系列1 - Camera2在textureView預(yù)覽
Android Camera2入門系列2 - ImageReader獲得預(yù)覽數(shù)據(jù)
Android Camera2入門系列3 - Image中獲得YUV數(shù)據(jù)及YUV格式理解
Android Camera2入門系列4 - libyuv的編譯和使用
本文提供最easy的Camera2的入門,供入門查看,意在精簡荤傲,深入內(nèi)容看后續(xù)文章呐伞。
The android.hardware.camera2 package provides an interface to individual camera devices connected to an Android device. It replaces the deprecated
[Camera]
class.
camera2給Android的給個(gè)Camera設(shè)備提供了接口废酷,并且deprecated掉了Camera類
下面是翻譯了部分Camera2的API描述:基本上涉及了我們使用camera2的所有的API:
通過CameraManager能查詢本設(shè)備有多少個(gè)available的Camera設(shè)備瘟檩。
每個(gè)CameraDevice設(shè)備提供了一系列靜態(tài)參數(shù)去描述當(dāng)前的Camera設(shè)備,比如設(shè)置或者輸出參數(shù)澈蟆,這些參數(shù)通過[CameraCharacteristics]
提供出來墨辛,通過[getCameraCharacteristics(cameraId)]
獲取。
從相機(jī)設(shè)備獲取一個(gè)或者多個(gè)image丰介,首先必須創(chuàng)建一個(gè)CameraCaptureSession
并輸出到一個(gè)或多個(gè)目標(biāo)Surface上背蟆。每個(gè)Surface必須預(yù)先設(shè)置合適的預(yù)覽尺寸,這個(gè)尺寸必須是Camera支持的尺寸哮幢。目標(biāo)Surface可以被一系列的類所持有带膀,比如SurfaceView
,SurfaceTexture
,MediaCodec
,MediaRecorder
,Allocation
和ImageReader
。也就是說Camera的輸出可以被分發(fā)到多個(gè)Surface上面橙垢。
通常垛叨,相機(jī)預(yù)覽圖像可以被發(fā)送到SurfaceView或者TextureView上面,像拍照的時(shí)候去單獨(dú)獲取某一幀或者特效相機(jī)類的App獲得要處理的RAW數(shù)據(jù)流可以通過ImageReader
來獲取JPEG格式或者YUV格式的圖像數(shù)據(jù)柜某。比如要用RenderScript, OpenGL ES或者直接在native處理的數(shù)據(jù)就推薦使用YUV_420_888數(shù)據(jù)格式來承載嗽元。
如果相機(jī)設(shè)備要獲取Image(也就是獲取圖像的raw數(shù)據(jù):JPEG或者YUV數(shù)據(jù)),我們需要創(chuàng)建一個(gè)定義了相機(jī)需要的參數(shù)的CaptureRequest
喂击,CameraDevice有工廠方法去創(chuàng)建一個(gè)request builder
剂癌。
一旦request被創(chuàng)建出來,它可以被一個(gè)active狀態(tài)的session拿去得到一個(gè)Image(one-shot)或者多個(gè)Image(endless)翰绊,也就是說session通過request去得到一張圖或者多張圖佩谷。
//得到一個(gè)Image
session.capture(request, null, mCameraHandler);
//一直回調(diào)返回Image
session.setRepeatingRequest(request, null, mCameraHandler);
API使用流程大體如下:
- 通過context.getSystemService(Context.CAMERA_SERVICE) 獲取CameraManager.
- 調(diào)用CameraManager .open()方法在回調(diào)中得到CameraDevice.
- 通過CameraDevice.createCaptureSession() 在回調(diào)中獲取CameraCaptureSession.
- 構(gòu)建CaptureRequest, 有三種模式可選 預(yù)覽/拍照/錄像.
- 通過 CameraCaptureSession發(fā)送CaptureRequest, capture表示只發(fā)一次請求, setRepeatingRequest表示不斷發(fā)送請求.
- 拍照數(shù)據(jù)可以在ImageReader.OnImageAvailableListener回調(diào)中獲取, CaptureCallback中則可獲取拍照實(shí)際的參數(shù)和Camera當(dāng)前狀態(tài).
上文提到了Camera2的某些必要的API。其實(shí)單純的去看某個(gè)類或某些類的職責(zé)监嗜,只是看這些API的功能描述只會看的頭大谐檀,因?yàn)閏amera2確實(shí)提供了很多API來控制。還不如直接來看一個(gè)demo來的更加直觀裁奇,推薦大家不要拷貝桐猬,哪怕看著自己敲一遍,印象會深刻很多刽肠。
public class Camera2Provider {
private Activity mContext;
private String mCameraId;
private Handler mCameraHandler;
private CameraDevice mCameraDevice;
private TextureView mTextureView;
private CaptureRequest.Builder mPreviewBuilder;
private Size previewSize;
public Camera2Provider(Activity mContext) {
this.mContext = mContext;
//創(chuàng)建了一個(gè)Thread來供Camera運(yùn)行使用溃肪,使用HandlerThread而不使用Thread是因?yàn)镠andlerThread給我們創(chuàng)建了Looper,不用我們自己創(chuàng)建了音五。
HandlerThread handlerThread = new HandlerThread("camera");
handlerThread.start();
mCameraHandler = new Handler(handlerThread.getLooper());
}
/**
* 設(shè)置預(yù)覽view
*
* @param textureView 需要預(yù)覽的TextureView
*/
public void initTexture(TextureView textureView) {
mTextureView = textureView;
textureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
openCamera(width, height);
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {return false;}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {}
});
}
/**
* surface ready的時(shí)候開啟Camera
*
* @param width surface的寬
* @param height surface的高
*/
private void openCamera(int width, int height) {
CameraManager cameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
try {
for (String cameraId : cameraManager.getCameraIdList()) {
//描述相機(jī)設(shè)備的屬性類
CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(cameraId);
//獲取是前置還是后置攝像頭
Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING);
//使用后置攝像頭
if (facing != null && facing == CameraCharacteristics.LENS_FACING_BACK) {
StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
if (map != null) {
previewSize = CameraUtil.getOptimalSize(map.getOutputSizes(SurfaceTexture.class), width, height);
mCameraId = cameraId;
}
}
}
String[] params = new String[]{Manifest.permission.CAMERA};
if (!PermissionUtil.checkPermission(mContext, params)) {
PermissionUtil.requestPermission(mContext, "", 0, params);
}
cameraManager.openCamera(mCameraId, mStateCallback, mCameraHandler);
} catch (CameraAccessException r) {}
}
/**
* 狀態(tài)回調(diào)
*/
private CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {
@Override
public void onOpened(CameraDevice camera) {
mCameraDevice = camera;
SurfaceTexture surfaceTexture = mTextureView.getSurfaceTexture();
surfaceTexture.setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight());
Surface previewSurface = new Surface(surfaceTexture);
try {
mPreviewBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
//如果需要多個(gè)surface可以add多個(gè)
mPreviewBuilder.addTarget(previewSurface);
mCameraDevice.createCaptureSession(Arrays.asList(previewSurface), mStateCallBack, mCameraHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
@Override
public void onDisconnected(CameraDevice camera) {
camera.close();
}
@Override
public void onError(CameraDevice camera, int error) {
camera.close();
}
};
private CameraCaptureSession.StateCallback mStateCallBack = new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(CameraCaptureSession session) {
CaptureRequest request = mPreviewBuilder.build();
try {
//獲取一個(gè)Image乍惊,one-shot
// session.capture(request, null, mCameraHandler);
//開啟獲取Image,repeat模式
session.setRepeatingRequest(request, null, mCameraHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
@Override
public void onConfigureFailed(CameraCaptureSession session) {}
};
/**
* 記得關(guān)掉Camera
*/
public void closeCamera() {
mCameraDevice.close();
}
}
需要注意的幾個(gè)問題:
- 記得添加權(quán)限放仗,并自己實(shí)現(xiàn)動態(tài)請求權(quán)限。
- 創(chuàng)建一個(gè)Thread供Camera2使用撬碟。
- 注意預(yù)覽尺寸和Camera能提供的尺寸诞挨。
github代碼:Camera2Provider.java 歡迎star/follow
接著看下一篇如何獲取回調(diào)的數(shù)據(jù)Android Camera系列2 - ImageReader獲得預(yù)覽數(shù)據(jù)