一堂飞、使用奋蔚。關于Camera2的API使用屑柔,參考Google官方的例子:
Camera2Basic
Camera2Raw
Camera2Video
這是一手資料饲漾,配合官方的資料理解Camera2 API的底層原理:
3A 模式和狀態(tài)轉換
二、關于Camera2 API 的一些坑钓辆。
本人應公司要求剪验,預研Camera2 相關API以及封裝。在參考Camera2Basic 編寫相機應用時前联,本人發(fā)現(xiàn)了Camera2 API 的關于自動對焦的一個非常嚴重的BUG功戚。在此記錄下來,希望后來者在使用Camera2 API時似嗤,慎重選擇啸臀。
Camera2Basic 中出現(xiàn)問題的代碼如下:
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;
}
}
}
@Override
public void onCaptureProgressed(@NonNull CameraCaptureSession session,
@NonNull CaptureRequest request,
@NonNull CaptureResult partialResult) {
process(partialResult);
}
@Override
public void onCaptureCompleted(@NonNull CameraCaptureSession session,
@NonNull CaptureRequest request,
@NonNull TotalCaptureResult result) {
process(result);
}
};
出現(xiàn)問題的代碼如下:
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;
}
調用拍照方法后,會進入STATE_WAITING_LOCK狀態(tài)双谆,此時獲取Integer afState = result.get(CaptureResult.CONTROL_AF_STATE);的對焦狀態(tài)afState 在某些機器上面壳咕,連續(xù)拍了幾張圖片之后,afState 會一直處于CaptureResult.CONTROL_AF_STATE_PASSIVE_SCAN狀態(tài)顽馋,表示一個持續(xù)聚焦的算法正在做掃描谓厘。鏡頭正在移動中。然而實際上你并沒有移動鏡頭寸谜。這里會導致后續(xù)的對焦完成ImageReader取出對焦完成的圖像數(shù)據(jù)無法進行竟稳。也就是無法再拍照了。這是我測試得到的log:
afState 的狀態(tài)為1熊痴,即CONTROL_AF_STATE_PASSIVE_SCAN他爸。無法再繼續(xù)走到后續(xù)的CONTROL_AF_STATE_FOCUSED_LOCKED 和 CONTROL_AF_STATE_NOT_FOCUSED_LOCKED 狀態(tài),導致無法取出圖像數(shù)據(jù)果善,進而完成拍照功能诊笤。
測試設備:紅米5 Plus
而且這個狀態(tài)出錯的情況一旦出現(xiàn),就只能關掉相機重新打開才能恢復正常巾陕。由于手上的設備有限讨跟,無法做更多的測試纪他。但至少這個情況在MIUI系統(tǒng)上非常大概率出現(xiàn),基于MIUI國內的市場份額晾匠,對于這種情況茶袒,只有兩種解決方案,要么放棄Camera2在拍照時的自動對焦凉馆,要么放棄使用Camera2 API薪寓。暫時沒有找到滿意的解決方法。
關于放棄拍照時自動對焦方案是:
// 等待對焦被鎖定
case STATE_WAITING_LOCK: {
Integer afState = result.get(CaptureResult.CONTROL_AF_STATE);
if (afState == null) {
Log.d(TAG, "STATE_WAITING_LOCK: mState = STATE_WAITING_LOCK;");
captureStillPicture();
} else if (
CaptureResult.CONTROL_AF_STATE_PASSIVE_SCAN == afState ||
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 || (CaptureResult.CONTROL_AF_STATE_PASSIVE_SCAN != afState
&& aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED)) {
mState = STATE_PICTURE_TAKEN;
captureStillPicture();
} else {
runPrecaptureSequence();
}
}
Log.d(TAG, "process: afState = " + afState);
break;
}
加入CONTROL_AF_STATE_PASSIVE_SCAN 狀態(tài)的判斷澜共,對于出現(xiàn)一直出現(xiàn)CONTROL_AF_STATE_PASSIVE_SCAN的情況時向叉,直接走下一層的AE曝光處理runPrecaptureSequence()流程,此時可能會因為無法對焦咳胃,畫面層次感丟失的情況植康。
如果有人有更好的解決方案。希望能夠分享一下展懈。個人感覺目前Camera2 API的坑相當?shù)亩啵辽僭谑褂玫臅r候供璧,注意一些深坑存崖。