В данный момент я пытаюсь создать фрагмент камеры на основе примера Camera2Basic. Основные отличия заключаются в том, что пользователь может переключаться между режимами вспышки (авто, вкл., Выкл.) И что отображается предварительный просмотр снятого снимка, поэтому пользователь может решить, сохранять или снимать снятый снимок.
Проблемы возникают при использовании вспышки в постоянно включенном режиме. Иногда последовательность предварительного захвата не завершается и приложение зависает. Предварительный просмотр все еще работает, а пользовательский интерфейс отзывчив, но обработка достигает «бесконечного цикла».
Согласно Camera2Basic, я использую этот CaptureCallback.
private CameraCaptureSession.CaptureCallback mPreviewCaptureCallback =
new CameraCaptureSession.CaptureCallback() {
private int counter = 0; // Counts failed attempts to gain AF or complete precapture
@Override
public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {
process(result);
}
@Override
public void onCaptureProgressed(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull CaptureResult partialResult) {
process(partialResult);
}
private void process(CaptureResult result) {
Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
Integer afState = result.get(CaptureResult.CONTROL_AF_STATE);
switch (mState) {
case STATE_PREVIEW:
// Do nothing
break;
case STATE_WAIT_LOCK:
if (afState == null) {
counter = 0;
captureStillImage();
} else if (afState == CaptureRequest.CONTROL_AF_STATE_FOCUSED_LOCKED ||
afState == CaptureRequest.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED) {
if (aeState == null ||
aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED) {
mState = STATE_PICTURE_CAPTURED;
captureStillImage();
} else {
runPrecaptureSequence();
}
counter = 0;
} else if(counter > 50) {
counter = 0;
restartFocus();
} else {
counter++;
Log.d(TAG, "STATE_WAIT_LOCK - Counter : " + counter);
}
break;
case STATE_WAIT_PRECAPTURE:
if (aeState == null ||
aeState == CaptureResult.CONTROL_AE_STATE_PRECAPTURE ||
aeState == CaptureRequest.CONTROL_AE_STATE_FLASH_REQUIRED) {
mState = STATE_WAIT_NON_PRECAPTURE;
}
break;
case STATE_WAIT_NON_PRECAPTURE:
if (aeState == null || aeState != CaptureResult.CONTROL_AE_STATE_PRECAPTURE) {
mState = STATE_PICTURE_CAPTURED;
captureStillImage();
} else if (counter > 50) {
Log.d(TAG, "STATE_WAIT_NON_PRECAPTURE - Restart");
counter = 0;
restartPrecapture();
} else {
counter++;
Log.d(TAG, "STATE_WAIT_NON_PRECAPTURE - Counter : " + counter);
}
break;
}
}
};
Поскольку в прошлом я сталкивался с похожей проблемой, когда AF не блокировался, я создал метод перезапуска AF.
private void restartFocus() {
try {
mPreviewRequestBuilder.set(
CaptureRequest.CONTROL_AF_TRIGGER,
CaptureRequest.CONTROL_AF_TRIGGER_CANCEL);
mCaptureSession.capture(
mPreviewRequestBuilder.build(),
mPreviewCaptureCallback,
mBackgroundHandler);
lockFocus();
} catch (CameraAccessException cae) {
cae.printStackTrace();
}
}
private void lockFocus() {
try {
mPreviewRequestBuilder.set(
CaptureRequest.CONTROL_AF_TRIGGER,
CaptureRequest.CONTROL_AF_TRIGGER_START);
mState = STATE_WAIT_LOCK;
mCaptureSession.capture(
mPreviewRequestBuilder.build(),
mPreviewCaptureCallback,
mBackgroundHandler);
} catch (CameraAccessException cae) {
cae.printStackTrace();
}
}
Так что я решил использовать эту идею во второй раз и перезапустить последовательность предзахвата.
private void restartPrecapture() {
try {
mPreviewRequestBuilder.set(
CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_CANCEL);
mCaptureSession.capture(mPreviewRequestBuilder.build(),
mPreviewCaptureCallback,
mBackgroundHandler);
runPrecaptureSequence();
} catch (CameraAccessException cae) {
cae.printStackTrace();
}
}
private void runPrecaptureSequence() {
try {
setFlash(mPreviewRequestBuilder);
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START);
mState = STATE_WAIT_PRECAPTURE;
mCaptureSession.capture(mPreviewRequestBuilder.build(),
mPreviewCaptureCallback,
mBackgroundHandler);
} catch (CameraAccessException cae) {
cae.printStackTrace();
}
}
Проверка Logcat показала, что приложение иногда застревает в STATE_WAIT_NON_PRECAPTURE, тогда как aeState остается CaptureResult.CONTROL_AE_STATE_PRECAPTURE. Если в этом состоянии счетчик достигает 50, то restartPrecapture () вызывается, как и предполагалось.
В настоящее время я сталкиваюсь с этой проблемой только при использовании CONTROL_AE_MODE_ON_ALWAYS_FLASH, но иногда я делаю десятки снимков, прежде чем это произойдет, поэтому, возможно, с автоматической или отключенной вспышкой шансы намного меньше.
private void setFlash(CaptureRequest.Builder requestBuilder) {
switch(mFlashMode) {
case FLASH_AUTO:
setFlashAuto(requestBuilder);
break;
case FLASH_ON:
setFlashOn(requestBuilder);
break;
case FLASH_OFF:
setFlashOff(requestBuilder);
break;
}
}
private void setFlashAuto(CaptureRequest.Builder requestBuilder) {
requestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
CameraMetadata.CONTROL_AE_MODE_ON_AUTO_FLASH);
}
private void setFlashOn(CaptureRequest.Builder requestBuilder) {
requestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
CameraMetadata.CONTROL_AE_MODE_ON_ALWAYS_FLASH);
}
private void setFlashOff(CaptureRequest.Builder requestBuilder) {
requestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
CameraMetadata.CONTROL_AE_MODE_ON);
requestBuilder.set(CaptureRequest.FLASH_MODE,
CameraMetadata.FLASH_MODE_OFF);
}
Как я могу справиться с такой ситуацией, когда последовательность предварительного захвата, по-видимому, не может закончиться?