2s相机 android6,Android Camera2 使用总结
最近在做自定義相機相關的項目,網上查了資料都是有關android.hardware.Camera的資料,開始使用的才發現這個類已經廢棄了。Android 5.0(21)之后android.hardware.Camera就被廢棄了,取而代之的是全新的android.hardware.Camera2 。Android 5.0對拍照API進行了全新的設計,新增了全新設計的Camera v2 API,這些API不僅大幅提高了Android系統拍照的功能,還能支持RAW照片輸出,甚至允許程序調整相機的對焦模式、曝光模式、快門等。
Camera2主要的類說明
CameraManager:攝像頭管理器。這是一個全新的系統管理器,專門用于檢測系統攝像頭、打開系統攝像頭。除此之外,調用CameraManager的getCameraCharacteristics(String)方法即可獲取指定攝像頭的相關特性。
CameraCharacteristics:攝像頭特性。該對象通過CameraManager來獲取,用于描述特定攝像頭所支持的各種特性。
CameraDevice:代表系統攝像頭。該類的功能類似于早期的Camera類。
CameraCaptureSession:這是一個非常重要的API,當程序需要預覽、拍照時,都需要先通過該類的實例創建Session。而且不管預覽還是拍照,也都是由該對象的方法進行控制的,其中控制預覽的方法為setRepeatingRequest();控制拍照的方法為capture()。
CameraRequest和CameraRequest.Builder:當程序調用setRepeatingRequest()方法進行預覽時,或調用capture()方法進行拍照時,都需要傳入CameraRequest參數。CameraRequest代表了一次捕獲請求,用于描述捕獲圖片的各種參數設置,比如對焦模式、曝光模式……總之,程序需要對照片所做的各種控制,都通過CameraRequest參數進行設置。CameraRequest.Builder則負責生成CameraRequest對象。
開啟相機預覽
開啟相機請一定添加相關的相機權限,判斷6.0以后添加動態權限的獲取。如果相機預覽出現黑屏多半就是因為沒有相機權限而導致的
首頁我們要設置相機相關的參數
CameraManager manager = (CameraManager) ctx.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;
}
// 檢查閃光燈是否支持。
Boolean available = characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE);
mFlashSupported = available == null ? false : available;
mCameraId = cameraId;
Log.e(TAG," 相機可用 ");
return;
}
} catch (CameraAccessException e) {
e.printStackTrace();
} catch (NullPointerException e) {
//不支持Camera2API
}
}
通過getSystemService(Context.CAMERA_SERVICE);拿到了CameraManager 返回當前可用的相機列表,在這里你可以選擇使用前置還是后置攝像頭。CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);可以拿到當前相機的相關參數,在這里你可以進行相關的參數檢查,例如檢查閃光燈是否支持等。在這里我們拿到當前相機的cameraId后面使用。
拿到cameraId我們就可以調用CameraManager的openCamera(String cameraId, CameraDevice.StateCallback callback, Handler handler)打開相機了
CameraManager manager = (CameraManager) ctx.getSystemService(Context.CAMERA_SERVICE);
try {
//打開相機預覽
manager.openCamera(mCameraId, mStateCallback, mBackgroundHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
} catch (InterruptedException e) {
throw new RuntimeException("Interrupted while trying to lock camera opening.", e);
}
添加 CameraDevice.StateCallback 監聽
我們需要對相機狀態就行監聽,以便在相機狀態發生改變的時候做相應的操作。openCamera(String cameraId, CameraDevice.StateCallback callback, Handler handler)中CameraDevice.StateCallback就是對相機的狀態改變的Callback
private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {
@Override
public void onOpened(@NonNull CameraDevice cameraDevice) {
mCameraDevice = cameraDevice;
//創建CameraPreviewSession
createCameraPreviewSession();
}
@Override
public void onDisconnected(@NonNull CameraDevice cameraDevice) {
cameraDevice.close();
mCameraDevice = null;
}
@Override
public void onError(@NonNull CameraDevice cameraDevice, int error) {
cameraDevice.close();
mCameraDevice = null;
}
};
創建 CameraCaptureSession
在onOpened()中我們可以拿到CameraDevice對象,在相機打開后需要創建CameraCaptureSession。
CameraCaptureSession是什么呢?由于Camera2是一套全新的API,所以它引用了管道的概念將安卓設備和攝像頭之間聯通起來,系統向攝像頭發送 Capture 請求,而攝像頭會返回 CameraMetadata。這一切建立在一個叫作 CameraCaptureSession的會話中。如下圖:
2086682-e68d187e1240bfc5.png
這里我們需要預覽相機的內容就需要創建CameraCaptureSession向相機發送Capture請求預覽相機內容
/**
* 為相機預覽創建新的CameraCaptureSession
*/
private void createCameraPreviewSession() {
try {
//設置了一個具有輸出Surface的CaptureRequest.Builder。
mPreviewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
mPreviewRequestBuilder.addTarget(mSurfaceHolder.getSurface());
//創建一個CameraCaptureSession來進行相機預覽。
mCameraDevice.createCaptureSession(Arrays.asList(mSurfaceHolder.getSurface()),
new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {
// 相機已經關閉
if (null == mCameraDevice) {
return;
}
// 會話準備好后,我們開始顯示預覽
mCaptureSession = cameraCaptureSession;
try {
// 自動對焦應
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,
CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
// 閃光燈
setAutoFlash(mPreviewRequestBuilder);
// 開啟相機預覽并添加事件
mPreviewRequest = mPreviewRequestBuilder.build();
//發送請求
mCaptureSession.setRepeatingRequest(mPreviewRequest,
null, mBackgroundHandler);
Log.e(TAG," 開啟相機預覽并添加事件");
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
@Override
public void onConfigureFailed(
@NonNull CameraCaptureSession cameraCaptureSession) {
Log.e(TAG," onConfigureFailed 開啟預覽失敗");
}
}, null);
} catch (CameraAccessException e) {
Log.e(TAG," CameraAccessException 開啟預覽失敗");
e.printStackTrace();
}
}
首先我們創建了一個CaptureRequest 上面說過了我們需要跟相機通信只有通過CameraCaptureSession。而要和CameraCaptureSession通信就是發送請求。這里我們相當于在創建請求的一些參數。
createCaptureRequest(int); 也有很多參數可選。這里我們發送的是CameraDevice.TEMPLATE_PREVIEW也就是告訴相機我們只需要預覽。更多參數如下
詳細信息可以參考官網的API文檔。
調用創建方法createCaptureSession(List outputs, CameraCaptureSession.StateCallback callback, Handler handler) 第一參數就是我們需要輸出到的Surface列表,這里我們可以輸出到一個SurfaceView中或者TextureView中。第二參數是對創建過程的一個回調方法,當onConfigured回調的時候說明CameraCaptureSession創建成功了。現在我們可以向CameraCaptureSession發送前面創建的好的預覽相機請求了。調用mCaptureSession.setRepeatingRequest(mPreviewRequest,null, mBackgroundHandler); 這樣我們就開啟的相機的預覽,在剛才添加的輸出Surface對應的控件中我們可以看到攝像頭的預覽內容了。
拍照
當我們需要拍照并且得到相應的照片數據的時候和開啟相機預覽相同的操作,我們只需要向CameraCaptureSession發送我們創建好的請求就行,就像我們請求網絡數據一樣,封裝好參數直接告訴CameraCaptureSession需要做什么由它去和相機建立通信并執行相應的操作。
對焦
/**
* 將焦點鎖定為靜態圖像捕獲的第一步。(對焦)
*/
private void lockFocus() {
try {
// 相機對焦
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
CameraMetadata.CONTROL_AF_TRIGGER_START);
// 修改狀態
mState = STATE_WAITING_LOCK;
mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback,
mBackgroundHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
向CameraCaptureSession發送對焦請求,并且對對焦是否成功進行監聽,在mCaptureCallback中對回調進行處理
/**
* 處理與JPEG捕獲有關的事件
*/
private CameraCaptureSession.CaptureCallback mCaptureCallback
= new CameraCaptureSession.CaptureCallback() {
//處理
private void process(CaptureResult result) {
switch (mState) {
case STATE_PREVIEW: {
//預覽狀態
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;
}
}
}
}
拍攝圖片
對焦完成后我們就可以向CameraCaptureSession發送請求可以拍照了
/**
*
* 拍攝靜態圖片。
*/
private void captureStillPicture() {
try {
if ( null == mCameraDevice) {
return;
}
// 這是用來拍攝照片的CaptureRequest.Builder。
final CaptureRequest.Builder captureBuilder =
mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
captureBuilder.addTarget(mImageReader.getSurface());
// 使用相同的AE和AF模式作為預覽。
captureBuilder.set(CaptureRequest.CONTROL_AF_MODE,
CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
setAutoFlash(captureBuilder);
// 方向
int rotation = this.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.capture(captureBuilder.build(), CaptureCallback, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
相信看到這里代碼已經不復雜了,組裝好我們的請求然后用CameraCaptureSession發送這個請求就可以了。這里需要注意的是我們怎么拿到圖片數據呢? 這里要說回在創建CameraCaptureSession時參數不是有一個輸出的Surface列表么,在列表中添加一個ImageReader的Surface用戶獲取圖片數據
mCameraDevice.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()),null).....
在ImageReader中對圖片獲取就行監聽
mImageReader.setOnImageAvailableListener(mOnImageAvailableListener, mBackgroundHandler);
private final ImageReader.OnImageAvailableListener mOnImageAvailableListener
= new ImageReader.OnImageAvailableListener() {
@Override
public void onImageAvailable(ImageReader reader) {
//當圖片可得到的時候獲取圖片并保存
mBackgroundHandler.post(new ImageSaver(reader.acquireNextImage(), mFile));
}
};
調用reader.acquireNextImage()我們就可以拿到當前的圖片數據了。拍完后我們需要解鎖焦點讓相機回到預覽狀態,同樣的我們發送請求就可以了
/**
* 解鎖焦點
*/
private void unlockFocus() {
try {
// 重置自動對焦
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
CameraMetadata.CONTROL_AF_TRIGGER_CANCEL);
setAutoFlash(mPreviewRequestBuilder);
mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback,
mBackgroundHandler);
// 將相機恢復正常的預覽狀態。
mState = STATE_PREVIEW;
// 打開連續取景模式
mCaptureSession.setRepeatingRequest(mPreviewRequest, mCaptureCallback,
mBackgroundHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
效果
GIF.gif
之前看了鴻神推送的Android 仿火螢視頻桌面 神奇的LiveWallPaper 一文中提到了用相機來做壁紙也就是透明屏幕,項目地址https://github.com/songixan/Wallpaper 查看源碼發現還是用的舊的CameraAPI,所以我在Demo中用Camera2API做了透明屏幕,有興趣的可以去看下。 PS:后來在同事的小米2S(5.1.1)中測試發現出錯了,初步猜測是分辨率的原因,目前正在解決中。有問題大家可以私信我 謝謝~
到此Camera2的學習就結束了,同樣的如果你想用相機就行拍攝視頻也是如此,用CameraCaptureSession發送相應的請求了就可以了,大家有興趣可以做一做視頻的拍攝。現在做微信的小10秒小視頻拍攝也很簡單了,思路很簡單當用戶按下拍攝按鈕用CameraCaptureSession發送拍攝視頻的請求,松開手指的時候相機恢復到預覽狀態。so easy~ 大家有興趣可以嘗試下。
作者:_小河馬
鏈接:http://www.jianshu.com/p/73fed068a795
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。
總結
以上是生活随笔為你收集整理的2s相机 android6,Android Camera2 使用总结的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: oracle应收模块核销点不上,详解EB
- 下一篇: Oracle数据库版本维护支持结束时间表