1引言
1.1編寫目的
Camera2 主要類的介紹,預覽,拍照,錄像的流程介紹。
1.2適用范圍
Camera API2的使用。
2 主要類的介紹
-
CameraManager
是一個用于檢測、連接和描述相機設備的系統(tǒng)服務,負責管理所有的CameraDevice相機設備。
可以通過調用Context.getSystemService(java.lang.String)方法來獲取一個CameraManager的實例:
CameraManager manager=(CameraManager)getSystemService(Context.CAMERA_SERVICE);
CameraDevice
連接到 Android 設備的單個攝像頭的表示,描述一個照相機設備,
一個 Android 設備可能會有多個攝像頭,通過 CameraId 可以進行區(qū)別,當相機
狀態(tài)回調函數(shù)執(zhí)行的時候,可以從回調中拿到當前 CameraDevice,并通過
CameraDevice 對當前相機進行一些列操作。
CameraCharacteristics
是描述相機設備的屬性類,其中的屬性都是固定的,繼承自 CameraMetadata 類。類比于舊 API 中的 CameraInfo 類。
包括:曝光補償(Exposure compensation)、自動曝光/自動對焦/自動白平衡模式(AE / AF / AWB mode)、自動曝光/自動白平衡鎖(AE / AWB lock)、自動對焦觸發(fā)器(AF trigger)、拍攝前自動曝光觸發(fā)器(Precapture AE trigger)、測量區(qū)域(Metering regions)、閃光燈觸發(fā)器(Flash trigger)、曝光時間(Exposure time)、感光度(ISO Sensitivity)、幀間隔(Frame duration)、鏡頭對焦距離(Lens focus distance)、色彩校正矩陣(Color correction matrix)、JPEG 元數(shù)據(jù)(JPEG metadata)、色調映射曲線(Tonemap curve)、裁剪區(qū)域(Crop region)、目標 FPS 范圍(Target FPS range)、拍攝意圖(Capture intent)、硬件視頻防抖(Video stabilization)等。
通過 CameraManager 的 getCameraCharacteristics(String cameraId) 方法獲取指定相機設備的 CameraCharacteristics 對象。
-
CameraCaptureSession
是一個事務,用來向相機設備發(fā)送獲取圖像的請求。
主要有 setRepeatingRequest()
和 capture()
方法。
setRepeatingRequest()
是重復請求獲取圖像數(shù)據(jù),常用于預覽或連拍,capture()
是獲取一次,常用于單張拍照。
CameraCaptureSession 類是一個抽象類.
-
CameraRequest
代表了一次捕獲請求,從相機設備捕獲單個圖像所需的不可變的設置和輸出包,當程序調用 setRepeatingRequest()方法進行預覽時,或調用 capture()方法進行拍照時,都需要傳入 CameraRequest 參數(shù)。
CameraRequest.Builder 則負責生成CameraRequest 對象
如:預覽前設置自動對焦
CaptureRequestBuilder.set(CaptureRequest.CONTORL_AF_MODE,CaptureRequest.CONTROL_AF_MOODE_CONTINUOUS_PICTURE);
CaptureRequest = CaptureRequestBuilder.build();
CameraCaptureSession.setRepeatingRequest(CaptureRequest,CaptureCallback,Handler);
CaptureResult
CaptureRequest描述是從圖像傳感器捕獲單個圖像的結果的子集的對象。(CaptureResults are produced by a CameraDevice after processing a CaptureRequest)當CaptureRequest被處理之后由CameraDevice生成。CameraMetadata
Camera API2/HAL3架構下使用了全新的CameraMetadata結構取代了之前的SetParameter/Paramters等操作,實現(xiàn)了Java到native到HAL3的參數(shù)傳遞。
3 app 層的主要流程
3.1 預覽的流程
3.1.1 先上圖,看圖講故事
3.1.2 預覽流程說明
- 獲取CameraManager
//獲取攝像頭的管理者CameraManager
CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
- openCamera 打開相應ID的相機
·String CameraId:傳入要打開的攝像頭的 Id
·CameraDevice.stateCallback:相機的狀態(tài)回調接口
·Handler handler:用來確定Callback在哪個線程執(zhí)行,為null的話就在當前線程執(zhí)行
public void openCamera( String cameraId,
final CameraDevice.StateCallback callback, Handler handler)
throws CameraAccessException {
openCameraForUid(cameraId, callback,
CameraDeviceImpl.checkAndWrapHandler(handler), USE_CALLING_UID);
}
- 實例化 CameraDevice.statCallback
private final CameraDevice.StateCallback stateCallback = new CameraDevice.StateCallback() {
@Override
public void onOpened(CameraDevice camera) {
mCameraDevice = camera;
//開啟預覽
startPreview();
}
@Override
public void onDisconnected(@NonNull CameraDevice camera) {
}
@Override
public void onError(@NonNull CameraDevice camera, int error) {
}
};
- 生成CaptureRequest.Builder
private CaptureRequest.Builder mPreviewRequestBuilder;
// 創(chuàng)建預覽請求的Builder(TEMPLATE_PREVIEW表示預覽請求)
private void getPreviewRequestBuilder() {
try {
mPreviewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
} catch (CameraAccessException e) {
e.printStackTrace();
}
//設置預覽的顯示界面,即將顯示預覽用的 surface 的實例,作為一個顯示層添加到該請求的目標列表中.
mPreviewRequestBuilder.addTarget(mPreviewSurface);
MeteringRectangle[] meteringRectangles = mPreviewRequestBuilder.get(CaptureRequest.CONTROL_AF_REGIONS);
if (meteringRectangles != null && meteringRectangles.length > 0) {
CamLog.d("PreviewRequestBuilder: AF_REGIONS=" + meteringRectangles[0].getRect().toString());
}
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO);
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_IDLE);
}
- mCameraDevice.createCaptureSession 創(chuàng)建CaptureSession
·List<Surface>:捕獲數(shù)據(jù)的輸出Surface列表,此處傳入用于顯
示預覽圖像的 Surface 即可,即 Arrays.asList(surface),
·CameraCaptureSession.stateCallback:CameraCaptureSession的狀態(tài)回調接口,當它創(chuàng)建好后會回調onConfigured方法.
·Handler:第三個參數(shù)用來確定Callback在哪個線程執(zhí)行,為null的話就在當前線程執(zhí)行
public abstract void createCaptureSession( List<Surface> outputs,
CameraCaptureSession.StateCallback callback, Handler handler)
throws CameraAccessException;
- CameraCaptureSession.StateCallback 回調
new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(CameraCaptureSession session) {
//該方法會從底層傳回一個CameraCaptureSession,該 session 可以開始處理捕獲請求,
mCaptureSession = session;
repeatPreview();
}
@Override
public void onConfigureFailed(CameraCaptureSession session) {
}
}
- CaptureRequest和mCaptureSession.setRepeatingRequest
private void repeatPreview() {
mPreviewRequestBuilder.setTag(TAG_PREVIEW);
mPreviewRequest = mPreviewRequestBuilder.build();
//設置反復捕獲數(shù)據(jù)的請求,這樣預覽界面就會一直有數(shù)據(jù)顯示
try {
mCaptureSession.setRepeatingRequest(mPreviewRequest,mPreviewCaptureCallback, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
·CaptureReqeust:無限重復的請求,
·CameraCaptureSession.CaptureCallback:即 callback 的回調,不過預覽中
該回調中不用進行任何處理
·Handler:第三個參數(shù)用來確定Callback在哪個線程執(zhí)行,為null的話就在當前線程執(zhí)行
public abstract int setRepeatingRequest( CaptureRequest request,
CaptureCallback listener, Handler handler)
throws CameraAccessException;
3.2 拍照的流程
3.2.1 先上圖,看圖講故事
3.2.2 拍照流程說明
- ImageReader 的使用
點擊拍照,使用 ImageReader 訪問呈現(xiàn)到 Surface 中的圖像,并進行保存。在預覽的 Surface 捕獲圖像的同時,我們也需要 ImageReader 來同時捕獲圖像數(shù)據(jù),所以在預覽的第五步中,CameraDevice.CreateCaptureSession()
方法中,我們將 ImageReader 的實例也傳入第一個參數(shù)中,即 Arrays.asList(surface,ImageReader.getSurface())
private ImageReader mImageReader;
private void setupImageReader() {
//前三個參數(shù)分別是需要的尺寸和格式,最后一個參數(shù)代表每次最多獲取幾幀數(shù)據(jù)
mImageReader = ImageReader.newInstance(mPreviewSize.getWidth(), mPreviewSize.getHeight(),
ImageFormat.JPEG, 1);
//監(jiān)聽ImageReader的事件,當有圖像流數(shù)據(jù)可用時會回調onImageAvailable方法,它的參數(shù)就是預覽幀數(shù)據(jù),可以對這幀數(shù)據(jù)進行處理
mImageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {
@Override
public void onImageAvailable(ImageReader reader) {
Image image = reader.acquireLatestImage();
// 開啟線程異步保存圖片
new Thread(new ImageSaver(image)).start();
}
}, null);
}
public static class ImageSaver implements Runnable {
private Image mImage;
private File mImageFile;
public ImageSaver(Image image) {
mImage = image;
}
@Override
public void run() {
ByteBuffer buffer = mImage.getPlanes()[0].getBuffer();
byte[] data = new byte[buffer.remaining()];
buffer.get(data);
mImageFile = new File(Environment.getExternalStorageDirectory() + "/DCIM/myPicture.jpg");
FileOutputStream fos = null;
try {
fos = new FileOutputStream(mImageFile);
fos.write(data, 0, data.length);
} catch (IOException e) {
e.printStackTrace();
} finally {
mImageFile = null;
if (fos != null) {
try {
fos.close();
fos = null;
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
//預覽第五步的代碼
mCameraDevice.createCaptureSession(
Arrays.asList(mPreviewSurface, mImageReader.getSurface()),
new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(CameraCaptureSession session) {
mCaptureSession = session;
repeatPreview();
}
@Override
public void onConfigureFailed(CameraCaptureSession session) {
}
}, null);
- 生成CaptureRequest
//首先我們創(chuàng)建請求拍照的CaptureRequest
final CaptureRequest.Builder mCaptureBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
mCaptureBuilder.addTarget(mPreviewSurface);
mCaptureBuilder.addTarget(mImageReader.getSurface());
//獲取屏幕方向
int rotation = getWindowManager().getDefaultDisplay().getRotation();
//設置拍照方向
mCaptureBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATION.get(rotation));
CaptureRequest captureRequest = mCaptureBuilder.build();
- CameraCaputureSession.capture()方法進行拍照
· CaptureRequest:此次拍照的參數(shù)設置
· CaptureCallback:callback 對象,當這個請求被處理的時候觸發(fā),如果
為 null,不會生成 matedate 信息,但是仍會生成圖像信息
· Handler:為一個句柄,代表執(zhí)行 callback 的 handler,如果程序希望直
接在當前線程中執(zhí)行 callback,則可以將 handler 參數(shù)設為 null
public abstract int capture( CaptureRequest request,
CaptureCallback listener, Handler handler)
throws CameraAccessException;
- CameraCaptureSession.CaptureCallback
//開始拍照,然后回調上面的接口重啟預覽,
// 因為mCaptureBuilder設置ImageReader作為target,所以會自動回調ImageReader的onImageAvailable()方法保存圖片
CameraCaptureSession.CaptureCallback captureCallback = new CameraCaptureSession.CaptureCallback() {
@Override
public void onCaptureCompleted(@NonNull CameraCaptureSession session,
@NonNull CaptureRequest request,
@NonNull TotalCaptureResult result) {
//恢復預覽狀態(tài)
repeatPreview();
}
};
3.3 錄像的流程
3.3.1 先上圖,看圖講故事
3.3.2 錄像流程說明
- MediaRecorder 的使用
MediaRecorder是安卓提供的一個用于音視頻采集的類.
mMediaRecorder = new MediaRecorder();
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
String mNextVideoAbsolutePath = Environment.getExternalStorageDirectory() + "/DCIM/videoBack.mp4";
mMediaRecorder.setOutputFile(mNextVideoAbsolutePath);
mMediaRecorder.setVideoEncodingBitRate(10000000);
mMediaRecorder.setVideoFrameRate(30);
mMediaRecorder.setVideoSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
mMediaRecorder.setOrientationHint(90);
try {
mMediaRecorder.prepare();
} catch (IOException e) {
e.printStackTrace();
}
- CaptureRequest.Builder的生成和添加Target
將 MedioRecorder 和預覽用的 Surface 的實例,添加到該請求的目標列表中。
mPreviewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
List<Surface> surfaces = new ArrayList<>();
SurfaceTexture texture = mTextureView.getSurfaceTexture();
assert texture != null;
texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
// Set up Surface for the camera preview
Surface previewSurface = new Surface(texture);
surfaces.add(previewSurface);
mPreviewRequestBuilder.addTarget(previewSurface);
// Set up Surface for the MediaRecorder
Surface recorderSurface = mMediaRecorder.getSurface();
surfaces.add(recorderSurface);
mPreviewRequestBuilder.addTarget(recorderSurface);
- 將預覽時創(chuàng)建的 session,close 掉
private void closePreviewSession() {
if (mCaptureSession != null) {
mCaptureSession.close();
mCaptureSession = null;
}
}
- createCaptureSession
·List<Surface>:新的用于捕獲圖像信息的 Surface 集合,此處為顯示預覽
信息的 surface 實例,以及記錄圖像信息用的 MediaRecorder 的實例
·CameraCaptureSession.StateCallback:用于通知新捕獲 session 的
callback
·Handler:為一個句柄,代表執(zhí)行 callback 的 handler,如果程序希望直
接在當前線程中執(zhí)行 callback,則可以將 handler 參數(shù)設為 null
public abstract void createCaptureSession( List<Surface> outputs,
CameraCaptureSession.StateCallback callback,
Handler handler)
throws CameraAccessException;
- CameraCaptureSession.StateCallback
new CameraCaptureSession.StateCallback()
{
@Override
public void onConfigured (@NonNull CameraCaptureSession session){
mCaptureSession = session;
try {
//調用CaptureRequestBuilder.set()方法,設置捕獲的參數(shù),此處設置3A算法
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);
CaptureRequest request = mPreviewRequestBuilder.build();
//調用CameraCaptureSession.setRepeatingReqest()方法,
//通過此捕獲session,持續(xù)重復捕獲圖像
mCaptureSession.setRepeatingRequest(request, null, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
runOnUiThread(new Runnable() {
@Override
public void run() {
// Start recording
mMediaRecorder.start();
}
});
}
@Override
public void onConfigureFailed (@NonNull CameraCaptureSession session){
}
}
- MediaRecorder 停止錄制
public void videoStop(View view) {
mMediaRecorder.stop();
mMediaRecorder.reset();
Toast.makeText(this, "Video saved: 保存",
Toast.LENGTH_SHORT).show();
//重新開啟預覽
startPreview();
}