Add/Remove MeteringRepeating in Camera2CameraImpl
The 2nd chain CL is about checking codes to add or remove MeteringRepeating
in Camera2CameraImpl.java and add related tests in ImageCaptureTest and
Camera2ImplCameraXTest.
Bug: 145326998
Test: Test: ./gradlew bOS && ./gradlew camera:camera-*:connectedCheck
Change-Id: I177820832ae13c8f5b0120c5eb02311302f0104b
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/ImageCaptureTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/ImageCaptureTest.java
index 7021fe5..9fd682d 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/ImageCaptureTest.java
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/ImageCaptureTest.java
@@ -303,6 +303,26 @@
}
@Test
+ public void canCaptureMultipleImagesWithMaxQualityNoRepeating() {
+ ImageCapture useCase = new ImageCapture.Builder()
+ .setCaptureMode(ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY)
+ .build();
+ mInstrumentation.runOnMainSync(
+ () -> {
+ CameraX.bindToLifecycle(mLifecycleOwner, BACK_SELECTOR, useCase);
+ mLifecycleOwner.startAndResume();
+ });
+
+ OnImageCapturedCallback callback = createMockOnImageCapturedCallback(null);
+ int numImages = 5;
+ for (int i = 0; i < numImages; ++i) {
+ useCase.takePicture(mMainExecutor, callback);
+ }
+
+ verify(callback, timeout(10000).times(numImages)).onCaptureSuccess(any(ImageProxy.class));
+ }
+
+ @Test
public void saveCanSucceed() throws IOException {
ImageCapture useCase = mDefaultBuilder.build();
mInstrumentation.runOnMainSync(
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2ImplCameraXTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2ImplCameraXTest.java
index 2098c32..adf56c7 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2ImplCameraXTest.java
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2ImplCameraXTest.java
@@ -627,4 +627,39 @@
assertThat(((CameraInternal) camera).getCameraInfoInternal().getCameraId()).isEqualTo(
CameraUtil.getCameraIdWithLensFacing(DEFAULT_LENS_FACING));
}
+
+ @Test
+ public void sequentialBindUnbindUseCases_closeCamera() throws InterruptedException {
+ ImageAnalysis imageAnalysis = new ImageAnalysis.Builder().build();
+
+ ImageCapture.Builder builder1 = new ImageCapture.Builder()
+ .setCaptureMode(ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY);
+ new Camera2Interop.Extender<>(builder1).setDeviceStateCallback(mDeviceStateCallback);
+ ImageCapture imageCapture = builder1.build();
+
+ mInstrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ mLifecycle.startAndResume();
+ // Bind ImageCapture only
+ CameraX.bindToLifecycle(mLifecycle, DEFAULT_SELECTOR, imageCapture);
+
+ // Then bind another ImageAnalysis
+ CameraX.bindToLifecycle(mLifecycle, DEFAULT_SELECTOR, imageAnalysis);
+ }
+ });
+
+ verify(mDeviceStateCallback, timeout(3000)).onOpened(any(CameraDevice.class));
+
+ mInstrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ CameraX.unbind(imageCapture);
+ CameraX.unbind(imageAnalysis);
+ }
+ });
+
+ verify(mDeviceStateCallback, timeout(3000)).onClosed(any(CameraDevice.class));
+ }
+
}
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraImpl.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraImpl.java
index d52a5a4..2b4b65b 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraImpl.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraImpl.java
@@ -63,6 +63,7 @@
import com.google.common.util.concurrent.ListenableFuture;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashMap;
@@ -160,6 +161,9 @@
@SuppressWarnings("WeakerAccess") /* synthetic accessor */
final Set<CaptureSession> mConfiguringForClose = new HashSet<>();
+ // The metering repeating use case for ImageCapture only case.
+ private MeteringRepeating mMeteringRepeating;
+
/**
* Constructor for a camera.
*
@@ -652,6 +656,7 @@
// where an unbound UseCase is silently ignored.
try {
mUseCaseAttachState.setUseCaseOnline(useCase);
+
useCasesChangedToOnline.add(useCase);
} catch (NullPointerException e) {
debugLog("Failed to set already detached use case online");
@@ -667,6 +672,9 @@
notifyStateOnlineToUseCases(useCasesChangedToOnline);
+ // Check if need to add or remove MeetingRepeatingUseCase.
+ addOrRemoveMeteringRepeatingUseCase();
+
updateCaptureSessionConfig();
resetCaptureSession(/*abortInFlightCaptures=*/false);
@@ -750,6 +758,9 @@
notifyStateOfflineToUseCases(useCasesChangedToOffline);
+ // Check if need to add or remove MeetingRepeatingUseCase.
+ addOrRemoveMeteringRepeatingUseCase();
+
boolean allUseCasesOffline = mUseCaseAttachState.getOnlineUseCases().isEmpty();
if (allUseCasesOffline) {
mCameraControlInternal.setActive(false);
@@ -768,6 +779,55 @@
}
}
+ // Check if it need the repeating surface for ImageCapture only use case.
+ private void addOrRemoveMeteringRepeatingUseCase() {
+ ValidatingBuilder validatingBuilder = mUseCaseAttachState.getOnlineBuilder();
+ SessionConfig sessionConfig = validatingBuilder.build();
+ CaptureConfig captureConfig = sessionConfig.getRepeatingCaptureConfig();
+ int sizeRepeatingSurfaces = captureConfig.getSurfaces().size();
+ int sizeSessionSurfaces = sessionConfig.getSurfaces().size();
+
+ if (!sessionConfig.getSurfaces().isEmpty()) {
+ if (captureConfig.getSurfaces().isEmpty()) {
+ // Create the MeteringRepeating UseCase
+ if (mMeteringRepeating == null) {
+ mMeteringRepeating = new MeteringRepeating(this);
+ }
+ addMeteringRepeating();
+ } else {
+ // There is mMeteringRepeating and online, check to remove it or not.
+ if (sizeSessionSurfaces == 1 && sizeRepeatingSurfaces == 1) {
+ // The only online use case is MeteringRepeating, directly remove it.
+ removeMeteringRepeating();
+ } else if (sizeRepeatingSurfaces >= 2) {
+ // There are other repeating UseCases, remove the MeteringRepeating.
+ removeMeteringRepeating();
+ } else {
+ // Other normal cases, do nothing.
+ Log.d(TAG, "mMeteringRepeating is online, "
+ + "SessionConfig Surfaces: " + sizeSessionSurfaces + ", "
+ + "CaptureConfig Surfaces: " + sizeRepeatingSurfaces);
+ }
+ }
+ }
+ }
+
+ private void removeMeteringRepeating() {
+ if (mMeteringRepeating != null) {
+ mUseCaseAttachState.setUseCaseOffline(mMeteringRepeating);
+ notifyStateOfflineToUseCases(Arrays.asList(mMeteringRepeating));
+ mMeteringRepeating.clear();
+ mMeteringRepeating = null;
+ }
+ }
+
+ private void addMeteringRepeating() {
+ if (mMeteringRepeating != null) {
+ mUseCaseAttachState.setUseCaseOnline(mMeteringRepeating);
+ notifyStateOnlineToUseCases(Arrays.asList(mMeteringRepeating));
+ }
+ }
+
/** Returns an interface to retrieve characteristics of the camera. */
@NonNull
@Override