Merge changes I4e2a5704,If8ae0958 into androidx-main
* changes:
Enable testlib advanced type testing in camera-extensions
Implement the postview and captureProcessProgressed on testlib advanced extender
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ImageProcessingUtil.java b/camera/camera-core/src/main/java/androidx/camera/core/ImageProcessingUtil.java
index dc55aec..e448993 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ImageProcessingUtil.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ImageProcessingUtil.java
@@ -146,11 +146,25 @@
}
/**
- * Convert a YUV_420_888 ImageProxy to a JPEG bytes data as an Image into the Surface.
+ * Convert a YUV_420_888 Image to a JPEG bytes data as an Image into the Surface.
*
* <p>Returns true if it succeeds and false otherwise.
*/
public static boolean convertYuvToJpegBytesIntoSurface(
+ @NonNull Image image,
+ @IntRange(from = 1, to = 100) int jpegQuality,
+ @ImageOutputConfig.RotationDegreesValue int rotationDegrees,
+ @NonNull Surface outputSurface) {
+ return convertYuvToJpegBytesIntoSurface(new AndroidImageProxy(image), jpegQuality,
+ rotationDegrees, outputSurface);
+ }
+
+ /**
+ * Convert a YUV_420_888 ImageProxy to a JPEG bytes data as an Image into the Surface.
+ *
+ * <p>Returns true if it succeeds and false otherwise.
+ */
+ public static boolean convertYuvToJpegBytesIntoSurface(
@NonNull ImageProxy imageProxy,
@IntRange(from = 1, to = 100) int jpegQuality,
@ImageOutputConfig.RotationDegreesValue int rotationDegrees,
diff --git a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ExtensionsManagerTest.kt b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ExtensionsManagerTest.kt
index 926cad2..5dc613d 100644
--- a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ExtensionsManagerTest.kt
+++ b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ExtensionsManagerTest.kt
@@ -27,6 +27,7 @@
import androidx.camera.core.SurfaceRequest
import androidx.camera.core.impl.CameraInfoInternal
import androidx.camera.core.impl.MutableStateObservable
+import androidx.camera.extensions.impl.ExtensionsTestlibControl
import androidx.camera.extensions.internal.ClientVersion
import androidx.camera.extensions.internal.ExtensionVersion
import androidx.camera.extensions.internal.VendorExtender
@@ -60,6 +61,7 @@
@RunWith(Parameterized::class)
@SdkSuppress(minSdkVersion = 21)
class ExtensionsManagerTest(
+ private val implType: ExtensionsTestlibControl.ImplementationType,
@field:ExtensionMode.Mode @param:ExtensionMode.Mode private val extensionMode: Int,
@field:CameraSelector.LensFacing @param:CameraSelector.LensFacing private val lensFacing: Int
) {
@@ -92,6 +94,7 @@
)
baseCameraSelector = CameraSelector.Builder().requireLensFacing(lensFacing).build()
+ ExtensionsTestlibControl.getInstance().setImplementationType(implType)
}
@After
@@ -107,9 +110,9 @@
companion object {
@JvmStatic
- @get:Parameterized.Parameters(name = "extension = {0}, facing = {1}")
+ @get:Parameterized.Parameters(name = "implType = {0}, mode = {1}, facing = {2}")
val parameters: Collection<Array<Any>>
- get() = ExtensionsTestUtil.getAllExtensionsLensFacingCombinations()
+ get() = ExtensionsTestUtil.getAllImplExtensionsLensFacingCombinations()
}
@Test
diff --git a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ImageAnalysisTest.kt b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ImageAnalysisTest.kt
index 2fb9866..935c0e1 100644
--- a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ImageAnalysisTest.kt
+++ b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ImageAnalysisTest.kt
@@ -30,6 +30,7 @@
import androidx.camera.core.Preview
import androidx.camera.core.impl.ImageFormatConstants
import androidx.camera.core.impl.utils.executor.CameraXExecutors
+import androidx.camera.extensions.impl.ExtensionsTestlibControl
import androidx.camera.extensions.internal.VendorExtender
import androidx.camera.extensions.util.ExtensionsTestUtil
import androidx.camera.lifecycle.ProcessCameraProvider
@@ -58,14 +59,15 @@
@RunWith(Parameterized::class)
@SdkSuppress(minSdkVersion = 21)
class ImageAnalysisTest(
+ private val implType: ExtensionsTestlibControl.ImplementationType,
@ExtensionMode.Mode private val extensionMode: Int,
@CameraSelector.LensFacing private val lensFacing: Int
) {
companion object {
@JvmStatic
- @get:Parameterized.Parameters(name = "extension = {0}, facing = {1}")
+ @get:Parameterized.Parameters(name = "implType = {0}, mode = {1}, facing = {2}")
val parameters: Collection<Array<Any>>
- get() = ExtensionsTestUtil.getAllExtensionsLensFacingCombinations()
+ get() = ExtensionsTestUtil.getAllImplExtensionsLensFacingCombinations()
}
@get:Rule
@@ -91,6 +93,7 @@
)
cameraProvider = ProcessCameraProvider.getInstance(context)[10000, TimeUnit.MILLISECONDS]
+ ExtensionsTestlibControl.getInstance().setImplementationType(implType)
baseCameraSelector = CameraSelector.Builder().requireLensFacing(lensFacing).build()
extensionsManager = ExtensionsManager.getInstanceAsync(
context,
diff --git a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ImageCaptureTest.kt b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ImageCaptureTest.kt
index 0328edf..96616ad 100644
--- a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ImageCaptureTest.kt
+++ b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ImageCaptureTest.kt
@@ -31,6 +31,7 @@
import androidx.camera.core.Preview
import androidx.camera.core.impl.utils.executor.CameraXExecutors
import androidx.camera.core.internal.compat.workaround.ExifRotationAvailability
+import androidx.camera.extensions.impl.ExtensionsTestlibControl
import androidx.camera.extensions.util.ExtensionsTestUtil
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.camera.testing.impl.CameraUtil
@@ -66,6 +67,7 @@
@RunWith(Parameterized::class)
@SdkSuppress(minSdkVersion = 21)
class ImageCaptureTest(
+ private val implType: ExtensionsTestlibControl.ImplementationType,
@field:ExtensionMode.Mode @param:ExtensionMode.Mode private val extensionMode: Int,
@field:CameraSelector.LensFacing @param:CameraSelector.LensFacing private val lensFacing: Int
) {
@@ -102,6 +104,7 @@
cameraProvider = ProcessCameraProvider.getInstance(context)[10000, TimeUnit.MILLISECONDS]
baseCameraSelector = CameraSelector.Builder().requireLensFacing(lensFacing).build()
+ ExtensionsTestlibControl.getInstance().setImplementationType(implType)
extensionsManager = ExtensionsManager.getInstanceAsync(
context,
cameraProvider
@@ -132,9 +135,9 @@
companion object {
@JvmStatic
- @get:Parameterized.Parameters(name = "extension = {0}, facing = {1}")
+ @get:Parameterized.Parameters(name = "impl= {0}, mode = {1}, facing = {2}")
val parameters: Collection<Array<Any>>
- get() = ExtensionsTestUtil.getAllExtensionsLensFacingCombinations()
+ get() = ExtensionsTestUtil.getAllImplExtensionsLensFacingCombinations()
}
@Test
diff --git a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/PreviewTest.kt b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/PreviewTest.kt
index 7ba9407..ef828d0 100644
--- a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/PreviewTest.kt
+++ b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/PreviewTest.kt
@@ -24,6 +24,7 @@
import androidx.camera.camera2.Camera2Config
import androidx.camera.core.CameraSelector
import androidx.camera.core.Preview
+import androidx.camera.extensions.impl.ExtensionsTestlibControl
import androidx.camera.extensions.util.ExtensionsTestUtil
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.camera.testing.impl.CameraUtil
@@ -53,6 +54,7 @@
@RunWith(Parameterized::class)
@SdkSuppress(minSdkVersion = 21)
class PreviewTest(
+ private val implType: ExtensionsTestlibControl.ImplementationType,
@field:ExtensionMode.Mode @param:ExtensionMode.Mode private val extensionMode: Int,
@field:CameraSelector.LensFacing @param:CameraSelector.LensFacing private val lensFacing: Int
) {
@@ -119,6 +121,7 @@
)
cameraProvider = ProcessCameraProvider.getInstance(context)[10000, TimeUnit.MILLISECONDS]
+ ExtensionsTestlibControl.getInstance().setImplementationType(implType)
baseCameraSelector = CameraSelector.Builder().requireLensFacing(lensFacing).build()
extensionsManager = ExtensionsManager.getInstanceAsync(
context,
@@ -150,9 +153,9 @@
companion object {
@JvmStatic
- @get:Parameterized.Parameters(name = "extension = {0}, facing = {1}")
+ @get:Parameterized.Parameters(name = "implType = {0}, mode = {1}, facing = {2}")
val parameters: Collection<Array<Any>>
- get() = ExtensionsTestUtil.getAllExtensionsLensFacingCombinations()
+ get() = ExtensionsTestUtil.getAllImplExtensionsLensFacingCombinations()
}
@UiThreadTest
diff --git a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/VideoCaptureTest.kt b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/VideoCaptureTest.kt
index 8348eeb..69bcb78 100644
--- a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/VideoCaptureTest.kt
+++ b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/VideoCaptureTest.kt
@@ -29,6 +29,7 @@
import androidx.camera.core.Preview
import androidx.camera.core.Preview.SurfaceProvider
import androidx.camera.core.impl.utils.executor.CameraXExecutors
+import androidx.camera.extensions.impl.ExtensionsTestlibControl
import androidx.camera.extensions.util.ExtensionsTestUtil
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.camera.testing.impl.AndroidUtil.skipVideoRecordingTestIfNotSupportedByEmulator
@@ -66,6 +67,7 @@
@RunWith(Parameterized::class)
@SdkSuppress(minSdkVersion = 21)
class VideoCaptureTest(
+ private val implType: ExtensionsTestlibControl.ImplementationType,
@field:ExtensionMode.Mode @param:ExtensionMode.Mode private val extensionMode: Int,
@field:CameraSelector.LensFacing @param:CameraSelector.LensFacing private val lensFacing: Int
) {
@@ -130,6 +132,7 @@
skipVideoRecordingTestIfNotSupportedByEmulator()
cameraProvider = ProcessCameraProvider.getInstance(context)[10000, TimeUnit.MILLISECONDS]
+ ExtensionsTestlibControl.getInstance().setImplementationType(implType)
baseCameraSelector = CameraSelector.Builder().requireLensFacing(lensFacing).build()
extensionsManager = ExtensionsManager.getInstanceAsync(
context,
@@ -280,8 +283,8 @@
private const val TAG = "VideoCaptureTest"
@JvmStatic
- @get:Parameterized.Parameters(name = "extension = {0}, facing = {1}")
+ @get:Parameterized.Parameters(name = "implType = {0}, mode = {1}, facing = {2}")
val parameters: Collection<Array<Any>>
- get() = ExtensionsTestUtil.getAllExtensionsLensFacingCombinations()
+ get() = ExtensionsTestUtil.getAllImplExtensionsLensFacingCombinations()
}
}
diff --git a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/util/ExtensionsTestUtil.java b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/util/ExtensionsTestUtil.java
index a9f652d..8f92a27 100644
--- a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/util/ExtensionsTestUtil.java
+++ b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/util/ExtensionsTestUtil.java
@@ -21,6 +21,9 @@
import static androidx.camera.extensions.ExtensionMode.FACE_RETOUCH;
import static androidx.camera.extensions.ExtensionMode.HDR;
import static androidx.camera.extensions.ExtensionMode.NIGHT;
+import static androidx.camera.extensions.impl.ExtensionsTestlibControl.ImplementationType.OEM_IMPL;
+import static androidx.camera.extensions.impl.ExtensionsTestlibControl.ImplementationType.TESTLIB_ADVANCED;
+import static androidx.camera.extensions.impl.ExtensionsTestlibControl.ImplementationType.TESTLIB_BASIC;
import android.hardware.camera2.CameraCharacteristics;
import android.os.Build;
@@ -29,6 +32,7 @@
import androidx.annotation.RequiresApi;
import androidx.camera.core.CameraSelector;
import androidx.camera.extensions.ExtensionMode;
+import androidx.camera.extensions.impl.ExtensionsTestlibControl;
import androidx.camera.extensions.internal.AdvancedVendorExtender;
import androidx.camera.extensions.internal.BasicVendorExtender;
import androidx.camera.extensions.internal.ExtensionVersion;
@@ -37,28 +41,65 @@
import androidx.camera.extensions.internal.compat.workaround.ExtensionDisabledValidator;
import androidx.camera.testing.impl.CameraUtil;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.List;
/**
* Extension test util functions.
*/
@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
public class ExtensionsTestUtil {
+
+ /**
+ * Returns the parameters which contains the combination of implementationType, extensions
+ * mode and lens facing.
+ */
@NonNull
- public static Collection<Object[]> getAllExtensionsLensFacingCombinations() {
- return Arrays.asList(new Object[][]{
- {BOKEH, CameraSelector.LENS_FACING_FRONT},
- {BOKEH, CameraSelector.LENS_FACING_BACK},
- {HDR, CameraSelector.LENS_FACING_FRONT},
- {HDR, CameraSelector.LENS_FACING_BACK},
- {FACE_RETOUCH, CameraSelector.LENS_FACING_FRONT},
- {FACE_RETOUCH, CameraSelector.LENS_FACING_BACK},
- {NIGHT, CameraSelector.LENS_FACING_FRONT},
- {NIGHT, CameraSelector.LENS_FACING_BACK},
- {AUTO, CameraSelector.LENS_FACING_FRONT},
- {AUTO, CameraSelector.LENS_FACING_BACK}
+ public static Collection<Object[]> getAllImplExtensionsLensFacingCombinations() {
+ ExtensionsTestlibControl.ImplementationType implType =
+ ExtensionsTestlibControl.getInstance().getImplementationType();
+
+ if (implType == TESTLIB_ADVANCED) {
+ ExtensionsTestlibControl.getInstance().setImplementationType(TESTLIB_BASIC);
+ implType = TESTLIB_BASIC;
+ }
+
+ List<Object[]> basicOrOemImplList = Arrays.asList(new Object[][]{
+ {implType, BOKEH, CameraSelector.LENS_FACING_FRONT},
+ {implType, BOKEH, CameraSelector.LENS_FACING_BACK},
+ {implType, HDR, CameraSelector.LENS_FACING_FRONT},
+ {implType, HDR, CameraSelector.LENS_FACING_BACK},
+ {implType, FACE_RETOUCH, CameraSelector.LENS_FACING_FRONT},
+ {implType, FACE_RETOUCH, CameraSelector.LENS_FACING_BACK},
+ {implType, NIGHT, CameraSelector.LENS_FACING_FRONT},
+ {implType, NIGHT, CameraSelector.LENS_FACING_BACK},
+ {implType, AUTO, CameraSelector.LENS_FACING_FRONT},
+ {implType, AUTO, CameraSelector.LENS_FACING_BACK}
});
+
+ if (implType == OEM_IMPL) {
+ return basicOrOemImplList;
+ }
+
+ List<Object[]> advancedList = Arrays.asList(new Object[][]{
+ {TESTLIB_ADVANCED, BOKEH, CameraSelector.LENS_FACING_FRONT},
+ {TESTLIB_ADVANCED, BOKEH, CameraSelector.LENS_FACING_BACK},
+ {TESTLIB_ADVANCED, HDR, CameraSelector.LENS_FACING_FRONT},
+ {TESTLIB_ADVANCED, HDR, CameraSelector.LENS_FACING_BACK},
+ {TESTLIB_ADVANCED, FACE_RETOUCH, CameraSelector.LENS_FACING_FRONT},
+ {TESTLIB_ADVANCED, FACE_RETOUCH, CameraSelector.LENS_FACING_BACK},
+ {TESTLIB_ADVANCED, NIGHT, CameraSelector.LENS_FACING_FRONT},
+ {TESTLIB_ADVANCED, NIGHT, CameraSelector.LENS_FACING_BACK},
+ {TESTLIB_ADVANCED, AUTO, CameraSelector.LENS_FACING_FRONT},
+ {TESTLIB_ADVANCED, AUTO, CameraSelector.LENS_FACING_BACK}
+ });
+
+ List<Object[]> allList = new ArrayList<>();
+ allList.addAll(basicOrOemImplList);
+ allList.addAll(advancedList);
+ return allList;
}
/**
diff --git a/camera/camera-testlib-extensions/src/main/java/androidx/camera/extensions/impl/ExtensionVersionImpl.java b/camera/camera-testlib-extensions/src/main/java/androidx/camera/extensions/impl/ExtensionVersionImpl.java
index 5235e59..7df0fe4 100644
--- a/camera/camera-testlib-extensions/src/main/java/androidx/camera/extensions/impl/ExtensionVersionImpl.java
+++ b/camera/camera-testlib-extensions/src/main/java/androidx/camera/extensions/impl/ExtensionVersionImpl.java
@@ -87,6 +87,15 @@
* @since 1.2
*/
public boolean isAdvancedExtenderImplemented() {
- return false;
+ return ExtensionsTestlibControl.getInstance().getImplementationType()
+ == ExtensionsTestlibControl.ImplementationType.TESTLIB_ADVANCED;
+ }
+
+ /**
+ * This method is used to check if test lib is running. If OEM implementation exists, invoking
+ * this method will throw {@link NoSuchMethodError}. This can be used to determine if OEM
+ * implementation is used or not.
+ */
+ public void checkTestlibRunning() {
}
}
diff --git a/camera/camera-testlib-extensions/src/main/java/androidx/camera/extensions/impl/ExtensionsTestlibControl.java b/camera/camera-testlib-extensions/src/main/java/androidx/camera/extensions/impl/ExtensionsTestlibControl.java
index 6cf35b5..344d9ac 100644
--- a/camera/camera-testlib-extensions/src/main/java/androidx/camera/extensions/impl/ExtensionsTestlibControl.java
+++ b/camera/camera-testlib-extensions/src/main/java/androidx/camera/extensions/impl/ExtensionsTestlibControl.java
@@ -21,19 +21,25 @@
/**
* An internal utility class that allows tests to specify whether to enable basic extender or
- * advanced extender of this testlib.
+ * advanced extender of this testlib. If OEM implementation exists on the device, the
+ * implementation type is always {@link ImplementationType#OEM_IMPL} and can't be changed to
+ * other types.
*/
@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
public class ExtensionsTestlibControl {
public enum ImplementationType {
- ADVANCED_EXTENDER,
- BASIC_EXTENDER
+ OEM_IMPL,
+ TESTLIB_ADVANCED,
+ TESTLIB_BASIC
}
private static ExtensionsTestlibControl sInstance;
private static Object sLock = new Object();
+ private volatile ImplementationType mImplementationType = ImplementationType.TESTLIB_BASIC;
private ExtensionsTestlibControl() {
+ mImplementationType = doesOEMImplementationExist()
+ ? ImplementationType.OEM_IMPL : ImplementationType.TESTLIB_BASIC;
}
/**
@@ -49,15 +55,43 @@
}
}
- private ImplementationType mImplementationType = ImplementationType.BASIC_EXTENDER;
-
/**
* Set the implementation type.
+ *
+ * <p>When OEM implementation exists on the device, the only possible type is
+ * {@link ImplementationType#OEM_IMPL}. Setting the implementation type to
+ * {@link ImplementationType#TESTLIB_BASIC} or {@link ImplementationType#TESTLIB_ADVANCED}
+ * when OEM implementation exist will throw an {@link IllegalArgumentException}.
+ *
+ * <p>When OEM implementation doesn't exist on the device, it is allowed to set it to
+ * {@link ImplementationType#TESTLIB_BASIC} or {@link ImplementationType#TESTLIB_ADVANCED}.
+ * Setting it to {@link ImplementationType#OEM_IMPL} in this case will throw an
+ * {@link IllegalArgumentException}.
*/
public void setImplementationType(@NonNull ImplementationType type) {
+ if (mImplementationType != ImplementationType.OEM_IMPL) { // OEM impl doesn't exist
+ if (type == ImplementationType.OEM_IMPL) {
+ throw new IllegalArgumentException("OEM_IMPL is not supported on this device.");
+ }
+ } else { // OEM impl exists
+ if (type != ImplementationType.OEM_IMPL) {
+ throw new IllegalArgumentException("Can't change the implementation type because "
+ + "OEM implementation exists on the device");
+ }
+ }
+
mImplementationType = type;
}
+ private boolean doesOEMImplementationExist() {
+ try {
+ new ExtensionVersionImpl().checkTestlibRunning();
+ return false;
+ } catch (NoSuchMethodError e) { // checkTestlibRunning doesn't exist in OEM implementation.
+ return true;
+ }
+ }
+
/**
* Gets the implementation type;
*/
diff --git a/camera/camera-testlib-extensions/src/main/java/androidx/camera/extensions/impl/HdrImageCaptureExtenderImpl.java b/camera/camera-testlib-extensions/src/main/java/androidx/camera/extensions/impl/HdrImageCaptureExtenderImpl.java
index ac5fdce..0c0cf09 100644
--- a/camera/camera-testlib-extensions/src/main/java/androidx/camera/extensions/impl/HdrImageCaptureExtenderImpl.java
+++ b/camera/camera-testlib-extensions/src/main/java/androidx/camera/extensions/impl/HdrImageCaptureExtenderImpl.java
@@ -19,7 +19,6 @@
import android.annotation.SuppressLint;
import android.content.Context;
-import android.graphics.ImageFormat;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
@@ -40,7 +39,7 @@
import java.nio.ByteBuffer;
import java.util.ArrayList;
-import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
@@ -385,8 +384,7 @@
@Nullable
@Override
public List<Pair<Integer, Size[]>> getSupportedPostviewResolutions(@NonNull Size captureSize) {
- Pair<Integer, Size[]> pair = new Pair<>(ImageFormat.JPEG, new Size[] {captureSize});
- return Arrays.asList(pair);
+ return Collections.emptyList();
}
@Override
diff --git a/camera/camera-testlib-extensions/src/main/java/androidx/camera/extensions/impl/NightImageCaptureExtenderImpl.java b/camera/camera-testlib-extensions/src/main/java/androidx/camera/extensions/impl/NightImageCaptureExtenderImpl.java
index 093d409..0de93ff 100644
--- a/camera/camera-testlib-extensions/src/main/java/androidx/camera/extensions/impl/NightImageCaptureExtenderImpl.java
+++ b/camera/camera-testlib-extensions/src/main/java/androidx/camera/extensions/impl/NightImageCaptureExtenderImpl.java
@@ -179,11 +179,11 @@
@NonNull
@Override
public List<CaptureRequest.Key> getAvailableCaptureRequestKeys() {
- List<CaptureRequest.Key> keys = Arrays.asList(
+ List<CaptureRequest.Key> keys = new ArrayList<>(Arrays.asList(
CaptureRequest.CONTROL_AF_MODE,
CaptureRequest.CONTROL_AF_TRIGGER,
CaptureRequest.CONTROL_AF_REGIONS,
- CaptureRequest.CONTROL_AE_REGIONS);
+ CaptureRequest.CONTROL_AE_REGIONS));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
keys.add(CaptureRequest.CONTROL_ZOOM_RATIO);
} else {
@@ -195,12 +195,12 @@
@NonNull
@Override
public List<CaptureResult.Key> getAvailableCaptureResultKeys() {
- List<CaptureResult.Key> keys = Arrays.asList(
+ List<CaptureResult.Key> keys = new ArrayList<>(Arrays.asList(
CaptureResult.CONTROL_AF_MODE,
CaptureResult.CONTROL_AE_REGIONS,
CaptureResult.CONTROL_AF_REGIONS,
CaptureResult.CONTROL_AE_STATE,
- CaptureResult.CONTROL_AF_STATE);
+ CaptureResult.CONTROL_AF_STATE));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
keys.add(CaptureResult.CONTROL_ZOOM_RATIO);
} else {
diff --git a/camera/camera-testlib-extensions/src/main/java/androidx/camera/extensions/impl/advanced/AutoAdvancedExtenderImpl.java b/camera/camera-testlib-extensions/src/main/java/androidx/camera/extensions/impl/advanced/AutoAdvancedExtenderImpl.java
index 9f567ba..520ea90 100644
--- a/camera/camera-testlib-extensions/src/main/java/androidx/camera/extensions/impl/advanced/AutoAdvancedExtenderImpl.java
+++ b/camera/camera-testlib-extensions/src/main/java/androidx/camera/extensions/impl/advanced/AutoAdvancedExtenderImpl.java
@@ -42,13 +42,12 @@
@Override
public boolean isExtensionAvailable(@NonNull String cameraId,
@NonNull Map<String, CameraCharacteristics> characteristicsMap) {
- throw new RuntimeException("Stub, replace with implementation.");
+ return false;
}
@Override
public void init(@NonNull String cameraId,
@NonNull Map<String, CameraCharacteristics> characteristicsMap) {
- throw new RuntimeException("Stub, replace with implementation.");
}
@Override
diff --git a/camera/camera-testlib-extensions/src/main/java/androidx/camera/extensions/impl/advanced/BeautyAdvancedExtenderImpl.java b/camera/camera-testlib-extensions/src/main/java/androidx/camera/extensions/impl/advanced/BeautyAdvancedExtenderImpl.java
index 40bbb93..d99e44c 100644
--- a/camera/camera-testlib-extensions/src/main/java/androidx/camera/extensions/impl/advanced/BeautyAdvancedExtenderImpl.java
+++ b/camera/camera-testlib-extensions/src/main/java/androidx/camera/extensions/impl/advanced/BeautyAdvancedExtenderImpl.java
@@ -42,13 +42,12 @@
@Override
public boolean isExtensionAvailable(@NonNull String cameraId,
@NonNull Map<String, CameraCharacteristics> characteristicsMap) {
- throw new RuntimeException("Stub, replace with implementation.");
+ return false;
}
@Override
public void init(@NonNull String cameraId,
@NonNull Map<String, CameraCharacteristics> characteristicsMap) {
- throw new RuntimeException("Stub, replace with implementation.");
}
@Override
diff --git a/camera/camera-testlib-extensions/src/main/java/androidx/camera/extensions/impl/advanced/BokehAdvancedExtenderImpl.java b/camera/camera-testlib-extensions/src/main/java/androidx/camera/extensions/impl/advanced/BokehAdvancedExtenderImpl.java
index 4093211..743b8d9 100644
--- a/camera/camera-testlib-extensions/src/main/java/androidx/camera/extensions/impl/advanced/BokehAdvancedExtenderImpl.java
+++ b/camera/camera-testlib-extensions/src/main/java/androidx/camera/extensions/impl/advanced/BokehAdvancedExtenderImpl.java
@@ -42,13 +42,12 @@
@Override
public boolean isExtensionAvailable(@NonNull String cameraId,
@NonNull Map<String, CameraCharacteristics> characteristicsMap) {
- throw new RuntimeException("Stub, replace with implementation.");
+ return false;
}
@Override
public void init(@NonNull String cameraId,
@NonNull Map<String, CameraCharacteristics> characteristicsMap) {
- throw new RuntimeException("Stub, replace with implementation.");
}
@Override
diff --git a/camera/camera-testlib-extensions/src/main/java/androidx/camera/extensions/impl/advanced/DefaultRequestProcessorImpl.java b/camera/camera-testlib-extensions/src/main/java/androidx/camera/extensions/impl/advanced/DefaultRequestProcessorImpl.java
new file mode 100644
index 0000000..c6c58a3
--- /dev/null
+++ b/camera/camera-testlib-extensions/src/main/java/androidx/camera/extensions/impl/advanced/DefaultRequestProcessorImpl.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.extensions.impl.advanced;
+
+import android.hardware.camera2.CaptureFailure;
+import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.TotalCaptureResult;
+
+import androidx.annotation.NonNull;
+
+public class DefaultRequestProcessorImpl implements RequestProcessorImpl.Callback {
+ @Override
+ public void onCaptureStarted(@NonNull RequestProcessorImpl.Request request, long frameNumber,
+ long timestamp) {
+
+ }
+
+ @Override
+ public void onCaptureProgressed(@NonNull RequestProcessorImpl.Request request,
+ @NonNull CaptureResult partialResult) {
+
+ }
+
+ @Override
+ public void onCaptureCompleted(@NonNull RequestProcessorImpl.Request request,
+ @NonNull TotalCaptureResult totalCaptureResult) {
+
+ }
+
+ @Override
+ public void onCaptureFailed(@NonNull RequestProcessorImpl.Request request,
+ @NonNull CaptureFailure captureFailure) {
+
+ }
+
+ @Override
+ public void onCaptureBufferLost(@NonNull RequestProcessorImpl.Request request, long frameNumber,
+ int outputStreamId) {
+
+ }
+
+ @Override
+ public void onCaptureSequenceCompleted(int sequenceId, long frameNumber) {
+
+ }
+
+ @Override
+ public void onCaptureSequenceAborted(int sequenceId) {
+
+ }
+}
diff --git a/camera/camera-testlib-extensions/src/main/java/androidx/camera/extensions/impl/advanced/HdrAdvancedExtenderImpl.java b/camera/camera-testlib-extensions/src/main/java/androidx/camera/extensions/impl/advanced/HdrAdvancedExtenderImpl.java
index 3d682ec..e4b038c 100644
--- a/camera/camera-testlib-extensions/src/main/java/androidx/camera/extensions/impl/advanced/HdrAdvancedExtenderImpl.java
+++ b/camera/camera-testlib-extensions/src/main/java/androidx/camera/extensions/impl/advanced/HdrAdvancedExtenderImpl.java
@@ -16,101 +16,19 @@
package androidx.camera.extensions.impl.advanced;
-import android.hardware.camera2.CameraCharacteristics;
-import android.hardware.camera2.CaptureRequest;
-import android.hardware.camera2.CaptureResult;
-import android.util.Range;
-import android.util.Size;
+import android.graphics.ImageFormat;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import java.util.List;
-import java.util.Map;
+import androidx.annotation.RequiresApi;
/**
- * Stub advanced extender implementation for hdr.
- *
- * <p>This class should be implemented by OEM and deployed to the target devices.
+ * A sample HDR implementation for testing long processing capture. It is capable of outputting
+ * the postview(JPEG format) and the process progress event. ImageAnalysis is not supported.
*
* @since 1.2
*/
-public class HdrAdvancedExtenderImpl implements AdvancedExtenderImpl {
+@RequiresApi(21)
+public class HdrAdvancedExtenderImpl extends LongCaptureAdvancedExtenderImpl {
public HdrAdvancedExtenderImpl() {
- }
-
- @Override
- public boolean isExtensionAvailable(@NonNull String cameraId,
- @NonNull Map<String, CameraCharacteristics> characteristicsMap) {
- throw new RuntimeException("Stub, replace with implementation.");
- }
-
- @Override
- public void init(@NonNull String cameraId,
- @NonNull Map<String, CameraCharacteristics> characteristicsMap) {
- throw new RuntimeException("Stub, replace with implementation.");
- }
-
- @Override
- @Nullable
- public Range<Long> getEstimatedCaptureLatencyRange(
- @NonNull String cameraId, @Nullable Size size, int imageFormat) {
- throw new RuntimeException("Stub, replace with implementation.");
- }
-
- @Override
- @NonNull
- public Map<Integer, List<Size>> getSupportedPreviewOutputResolutions(
- @NonNull String cameraId) {
- throw new RuntimeException("Stub, replace with implementation.");
- }
-
-
- @Override
- @NonNull
- public Map<Integer, List<Size>> getSupportedCaptureOutputResolutions(
- @NonNull String cameraId) {
- throw new RuntimeException("Stub, replace with implementation.");
- }
-
- @Override
- @NonNull
- public Map<Integer, List<Size>> getSupportedPostviewResolutions(
- @NonNull Size captureSize) {
- throw new RuntimeException("Stub, replace with implementation.");
- }
-
- @Override
- @Nullable
- public List<Size> getSupportedYuvAnalysisResolutions(@NonNull String cameraId) {
- throw new RuntimeException("Stub, replace with implementation.");
- }
-
- @Override
- @NonNull
- public SessionProcessorImpl createSessionProcessor() {
- throw new RuntimeException("Stub, replace with implementation.");
- }
-
- @Override
- @NonNull
- public List<CaptureRequest.Key> getAvailableCaptureRequestKeys() {
- throw new RuntimeException("Stub, replace with implementation.");
- }
-
- @Override
- @NonNull
- public List<CaptureResult.Key> getAvailableCaptureResultKeys() {
- throw new RuntimeException("Stub, replace with implementation.");
- }
-
- @Override
- public boolean isCaptureProcessProgressAvailable() {
- throw new RuntimeException("Stub, replace with implementation.");
- }
-
- @Override
- public boolean isPostviewAvailable() {
- throw new RuntimeException("Stub, replace with implementation.");
+ super(/* postviewFormat */ ImageFormat.JPEG);
}
}
diff --git a/camera/camera-testlib-extensions/src/main/java/androidx/camera/extensions/impl/advanced/LongCaptureAdvancedExtenderImpl.java b/camera/camera-testlib-extensions/src/main/java/androidx/camera/extensions/impl/advanced/LongCaptureAdvancedExtenderImpl.java
new file mode 100644
index 0000000..d28067c
--- /dev/null
+++ b/camera/camera-testlib-extensions/src/main/java/androidx/camera/extensions/impl/advanced/LongCaptureAdvancedExtenderImpl.java
@@ -0,0 +1,593 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.extensions.impl.advanced;
+
+import android.content.Context;
+import android.graphics.ImageFormat;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CaptureFailure;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.TotalCaptureResult;
+import android.hardware.camera2.params.StreamConfigurationMap;
+import android.os.Build;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.util.Pair;
+import android.util.Range;
+import android.util.Size;
+import android.view.Surface;
+
+import androidx.annotation.GuardedBy;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+import androidx.camera.core.ImageProcessingUtil;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * An {@link AdvancedExtenderImpl} implementation that have long processing time for capture and
+ * is capable of outputting the postview and the process progress event.
+ */
+@RequiresApi(21)
+public class LongCaptureAdvancedExtenderImpl implements AdvancedExtenderImpl {
+ private static final int EV_INDEX = 10;
+
+ private CameraCharacteristics mCameraCharacteristics;
+ private final int mPostviewFormat;
+
+ public LongCaptureAdvancedExtenderImpl(int postviewFormat) {
+ mPostviewFormat = postviewFormat;
+ }
+
+ @Override
+ public boolean isExtensionAvailable(@NonNull String cameraId,
+ @NonNull Map<String, CameraCharacteristics> characteristicsMap) {
+ return true;
+ }
+
+ @Override
+ public void init(@NonNull String cameraId,
+ @NonNull Map<String, CameraCharacteristics> characteristicsMap) {
+ mCameraCharacteristics = characteristicsMap.get(cameraId);
+ }
+
+ @Override
+ @Nullable
+ public Range<Long> getEstimatedCaptureLatencyRange(
+ @NonNull String cameraId, @Nullable Size size, int imageFormat) {
+ return new Range<>(1000L, 4000L);
+ }
+
+ @Override
+ @NonNull
+ public Map<Integer, List<Size>> getSupportedPreviewOutputResolutions(
+ @NonNull String cameraId) {
+ HashMap<Integer, List<Size>> map = new HashMap<>();
+ map.put(ImageFormat.PRIVATE, getOutputSizes(ImageFormat.PRIVATE));
+ return map;
+ }
+
+ private List<Size> getOutputSizes(int imageFormat) {
+ StreamConfigurationMap map = mCameraCharacteristics
+ .get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+
+ return Arrays.asList(map.getOutputSizes(imageFormat));
+ }
+
+ @Override
+ @NonNull
+ public Map<Integer, List<Size>> getSupportedCaptureOutputResolutions(
+ @NonNull String cameraId) {
+ HashMap<Integer, List<Size>> map = new HashMap<>();
+ map.put(ImageFormat.JPEG, getOutputSizes(ImageFormat.JPEG));
+ return map;
+ }
+
+ @Override
+ @NonNull
+ public Map<Integer, List<Size>> getSupportedPostviewResolutions(
+ @NonNull Size captureSize) {
+ HashMap<Integer, List<Size>> map = new HashMap<>();
+ // Here it intentionally contains JPEG or YUV instead of both so that we can test
+ // the different path.
+ if (mPostviewFormat == ImageFormat.YUV_420_888) {
+ map.put(ImageFormat.YUV_420_888, getCompatibleYuvSizes(captureSize));
+ } else if (mPostviewFormat == ImageFormat.JPEG) {
+ // it will configure YUV stream and convert it to JPEG.
+ map.put(ImageFormat.JPEG, getCompatibleYuvSizes(captureSize));
+ }
+ return map;
+ }
+
+ private List<Size> getCompatibleYuvSizes(Size captureSize) {
+ List<Size> yuvSizes = getOutputSizes(ImageFormat.YUV_420_888);
+ List<Size> results = new ArrayList<>();
+
+ for (Size yuvSize : yuvSizes) {
+ int area = yuvSize.getWidth() * yuvSize.getHeight();
+ if (area <= captureSize.getWidth() * captureSize.getHeight()
+ && area <= 1920 * 1080 /* 1080P */) {
+ results.add(yuvSize);
+ }
+ }
+ return results;
+ }
+
+ @Override
+ @Nullable
+ public List<Size> getSupportedYuvAnalysisResolutions(
+ @NonNull String cameraId) {
+ return Collections.emptyList();
+ }
+
+ private LongCaptureSessionProcessor mNightSessionProcessor = new LongCaptureSessionProcessor();
+ @Override
+ @NonNull
+ public SessionProcessorImpl createSessionProcessor() {
+ return mNightSessionProcessor;
+ }
+
+ @Override
+ @NonNull
+ public List<CaptureRequest.Key> getAvailableCaptureRequestKeys() {
+ List<CaptureRequest.Key> keys = new ArrayList<>(Arrays.asList(
+ CaptureRequest.CONTROL_AF_MODE,
+ CaptureRequest.CONTROL_AF_TRIGGER,
+ CaptureRequest.CONTROL_AF_REGIONS,
+ CaptureRequest.CONTROL_AE_REGIONS)
+ );
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ keys.add(CaptureRequest.CONTROL_ZOOM_RATIO);
+ } else {
+ keys.add(CaptureRequest.SCALER_CROP_REGION);
+ }
+ return Collections.unmodifiableList(keys);
+ }
+
+ @Override
+ @NonNull
+ public List<CaptureResult.Key> getAvailableCaptureResultKeys() {
+ List<CaptureResult.Key> keys = new ArrayList<>(Arrays.asList(
+ CaptureResult.CONTROL_AF_MODE,
+ CaptureResult.CONTROL_AE_REGIONS,
+ CaptureResult.CONTROL_AF_REGIONS,
+ CaptureResult.CONTROL_AE_STATE,
+ CaptureResult.CONTROL_AF_STATE));
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ keys.add(CaptureResult.CONTROL_ZOOM_RATIO);
+ } else {
+ keys.add(CaptureResult.SCALER_CROP_REGION);
+ }
+ return Collections.unmodifiableList(keys);
+ }
+
+ @Override
+ public boolean isCaptureProcessProgressAvailable() {
+ return true;
+ }
+
+ @Override
+ public boolean isPostviewAvailable() {
+ return true;
+ }
+
+ private class LongCaptureSessionProcessor implements SessionProcessorImpl {
+ private HandlerThread mHandlerThread;
+ private Handler mBackgroundHandler;
+
+ private final Object mLock = new Object();
+ @GuardedBy("mLock")
+ private Map<CaptureRequest.Key<?>, Object> mParameters = new LinkedHashMap<>();
+
+ private Camera2OutputConfigImpl mPreviewOutputConfig;
+ private Camera2OutputConfigImpl mCaptureOutputConfig;
+ private Camera2OutputConfigImpl mPostviewOutputConfig;
+
+ private Surface mPostviewJpegOutputSurface;
+
+ private RequestProcessorImpl mRequestProcessor;
+ private AtomicInteger mNextCaptureSequenceId = new AtomicInteger(1);
+
+ @NonNull
+ @Override
+ public Camera2SessionConfigImpl initSession(@NonNull String cameraId,
+ @NonNull Map<String, CameraCharacteristics> cameraCharacteristicsMap,
+ @NonNull Context context, @NonNull OutputSurfaceConfigurationImpl surfaceConfigs) {
+ return initSessionInternal(
+ surfaceConfigs.getPreviewOutputSurface(),
+ surfaceConfigs.getImageCaptureOutputSurface(),
+ surfaceConfigs.getPostviewOutputSurface());
+ }
+
+ @NonNull
+ @Override
+ public Camera2SessionConfigImpl initSession(@NonNull String cameraId,
+ @NonNull Map<String, CameraCharacteristics> cameraCharacteristicsMap,
+ @NonNull Context context, @NonNull OutputSurfaceImpl previewSurfaceConfig,
+ @NonNull OutputSurfaceImpl imageCaptureSurfaceConfig,
+ @Nullable OutputSurfaceImpl imageAnalysisSurfaceConfig) {
+ return initSessionInternal(previewSurfaceConfig,
+ imageCaptureSurfaceConfig,
+ null);
+ }
+
+ private Camera2SessionConfigImpl initSessionInternal(
+ @NonNull OutputSurfaceImpl previewSurfaceConfig,
+ @NonNull OutputSurfaceImpl imageCaptureSurfaceConfig,
+ @Nullable OutputSurfaceImpl postviewSurfaceConfig) {
+
+ mHandlerThread = new HandlerThread("LongCapture advanced extender impl");
+ mHandlerThread.start();
+ mBackgroundHandler = new Handler(mHandlerThread.getLooper());
+ Camera2SessionConfigImplBuilder builder =
+ new Camera2SessionConfigImplBuilder()
+ .setSessionTemplateId(CameraDevice.TEMPLATE_PREVIEW);
+
+ // Preview
+ if (previewSurfaceConfig.getSurface() != null) {
+ mPreviewOutputConfig = Camera2OutputConfigImplBuilder
+ .newSurfaceConfig(previewSurfaceConfig.getSurface()).build();
+
+ builder.addOutputConfig(mPreviewOutputConfig);
+ }
+
+ // Image Capture
+ if (imageCaptureSurfaceConfig.getSurface() != null) {
+ mCaptureOutputConfig = Camera2OutputConfigImplBuilder.newSurfaceConfig(
+ imageCaptureSurfaceConfig.getSurface()).build();
+ builder.addOutputConfig(mCaptureOutputConfig);
+ }
+
+ // postview
+ if (postviewSurfaceConfig != null) {
+ if (postviewSurfaceConfig.getImageFormat() == ImageFormat.YUV_420_888) {
+ // For YUV format postview, it just configures the YUV surface into the camera.
+ mPostviewOutputConfig = Camera2OutputConfigImplBuilder.newSurfaceConfig(
+ postviewSurfaceConfig.getSurface()).build();
+ } else if (postviewSurfaceConfig.getImageFormat() == ImageFormat.JPEG) {
+ // For JPEG format postview, because we can't configure two JPEG streams on
+ // most devices, alternatively, we configure a YUV stream and convert it to
+ // JPEG to the postview output surface.
+ mPostviewOutputConfig = Camera2OutputConfigImplBuilder.newImageReaderConfig(
+ postviewSurfaceConfig.getSize(), ImageFormat.YUV_420_888, 2
+ ).build();
+ mPostviewJpegOutputSurface = postviewSurfaceConfig.getSurface();
+ }
+
+ builder.addOutputConfig(mPostviewOutputConfig);
+ }
+
+ return builder.build();
+ }
+
+ @Override
+ public void deInitSession() {
+ mHandlerThread.quitSafely();
+ }
+
+ @Override
+ public void setParameters(@NonNull Map<CaptureRequest.Key<?>, Object> parameters) {
+ synchronized (mLock) {
+ for (CaptureRequest.Key<?> key : parameters.keySet()) {
+ Object value = parameters.get(key);
+ if (value != null) {
+ mParameters.put(key, value);
+ }
+ }
+ }
+ }
+
+ private void applyParameters(@NonNull RequestBuilder requestBuilder) {
+ synchronized (mLock) {
+ for (CaptureRequest.Key<?> key : mParameters.keySet()) {
+ requestBuilder.setParameters(key, mParameters.get(key));
+ }
+ }
+ }
+
+ @Override
+ public int startTrigger(@NonNull Map<CaptureRequest.Key<?>, Object> triggers,
+ @NonNull CaptureCallback captureCallback) {
+ RequestBuilder builder = new RequestBuilder(mPreviewOutputConfig.getId(),
+ CameraDevice.TEMPLATE_PREVIEW);
+ applyParameters(builder);
+ builder.setParameters(
+ CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION, EV_INDEX);
+
+ List<CaptureRequest.Key> availableKeys = getAvailableCaptureRequestKeys();
+ for (CaptureRequest.Key<?> key : triggers.keySet()) {
+ if (availableKeys.contains(key)) {
+ builder.setParameters(key, triggers.get(key));
+ }
+ }
+
+ int seqId = mNextCaptureSequenceId.getAndIncrement();
+
+ RequestProcessorImpl.Callback callback = new RequestProcessorImpl.Callback() {
+ @Override
+ public void onCaptureStarted(@NonNull RequestProcessorImpl.Request request,
+ long frameNumber,
+ long timestamp) {
+ captureCallback.onCaptureStarted(seqId, timestamp);
+ }
+
+ @Override
+ public void onCaptureProgressed(@NonNull RequestProcessorImpl.Request request,
+ @NonNull CaptureResult partialResult) {
+
+ }
+
+ @Override
+ public void onCaptureCompleted(@NonNull RequestProcessorImpl.Request request,
+ @NonNull TotalCaptureResult totalCaptureResult) {
+ captureCallback.onCaptureProcessStarted(seqId);
+ }
+
+ @Override
+ public void onCaptureFailed(@NonNull RequestProcessorImpl.Request request,
+ @NonNull CaptureFailure captureFailure) {
+ captureCallback.onCaptureFailed(seqId);
+ }
+
+ @Override
+ public void onCaptureBufferLost(@NonNull RequestProcessorImpl.Request request,
+ long frameNumber, int outputStreamId) {
+ captureCallback.onCaptureFailed(seqId);
+ }
+
+ @Override
+ public void onCaptureSequenceCompleted(int sequenceId, long frameNumber) {
+ captureCallback.onCaptureSequenceCompleted(seqId);
+ }
+
+ @Override
+ public void onCaptureSequenceAborted(int sequenceId) {
+ captureCallback.onCaptureSequenceAborted(seqId);
+ }
+ };
+
+ mRequestProcessor.submit(builder.build(), callback);
+ return seqId;
+ }
+
+ private int getJpegOrientation() {
+ synchronized (mLock) {
+ if (mParameters.get(CaptureRequest.JPEG_ORIENTATION) != null) {
+ return (int) mParameters.get(CaptureRequest.JPEG_ORIENTATION);
+ }
+ }
+ return 0;
+ }
+
+ @RequiresApi(21)
+ @Override
+ public void onCaptureSessionStart(@NonNull RequestProcessorImpl requestProcessor) {
+ mRequestProcessor = requestProcessor;
+
+ if (mPostviewFormat == ImageFormat.JPEG && mPostviewJpegOutputSurface != null) {
+ requestProcessor.setImageProcessor(mPostviewOutputConfig.getId(),
+ new ImageProcessorImpl() {
+ @Override
+ public void onNextImageAvailable(int outputConfigId, long timestampNs,
+ ImageReferenceImpl imageReference, String physicalCameraId) {
+ ImageProcessingUtil.convertYuvToJpegBytesIntoSurface(
+ imageReference.get(),
+ 90,
+ getJpegOrientation(),
+ mPostviewJpegOutputSurface
+ );
+
+ imageReference.decrement();
+ }
+ }
+ );
+ }
+ }
+
+ @Override
+ public void onCaptureSessionEnd() {
+
+ }
+
+ @Override
+ public int startRepeating(@NonNull CaptureCallback captureCallback) {
+ RequestBuilder builder = new RequestBuilder(mPreviewOutputConfig.getId(),
+ CameraDevice.TEMPLATE_PREVIEW);
+ applyParameters(builder);
+ builder.setParameters(
+ CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION, EV_INDEX);
+ final int seqId = mNextCaptureSequenceId.getAndIncrement();
+
+ RequestProcessorImpl.Callback callback = new RequestProcessorImpl.Callback() {
+ @Override
+ public void onCaptureStarted(@NonNull RequestProcessorImpl.Request request,
+ long frameNumber,
+ long timestamp) {
+ captureCallback.onCaptureStarted(seqId, timestamp);
+ }
+
+ @Override
+ public void onCaptureProgressed(@NonNull RequestProcessorImpl.Request request,
+ @NonNull CaptureResult partialResult) {
+
+ }
+
+ @Override
+ public void onCaptureCompleted(@NonNull RequestProcessorImpl.Request request,
+ @NonNull TotalCaptureResult totalCaptureResult) {
+ captureCallback.onCaptureProcessStarted(seqId);
+ }
+
+ @Override
+ public void onCaptureFailed(@NonNull RequestProcessorImpl.Request request,
+ @NonNull CaptureFailure captureFailure) {
+ captureCallback.onCaptureFailed(seqId);
+ }
+
+ @Override
+ public void onCaptureBufferLost(@NonNull RequestProcessorImpl.Request request,
+ long frameNumber, int outputStreamId) {
+ captureCallback.onCaptureFailed(seqId);
+ }
+
+ @Override
+ public void onCaptureSequenceCompleted(int sequenceId, long frameNumber) {
+ captureCallback.onCaptureSequenceCompleted(seqId);
+ }
+
+ @Override
+ public void onCaptureSequenceAborted(int sequenceId) {
+ captureCallback.onCaptureSequenceAborted(seqId);
+ }
+ };
+
+ mRequestProcessor.setRepeating(builder.build(), callback);
+
+ return seqId;
+ }
+
+ @Override
+ public void stopRepeating() {
+ mRequestProcessor.stopRepeating();
+ }
+
+ @Override
+ public int startCapture(@NonNull CaptureCallback callback) {
+ return startCaptureInternal(false, callback);
+ }
+
+ @Override
+ public int startCaptureWithPostview(@NonNull CaptureCallback callback) {
+ return startCaptureInternal(true, callback);
+ }
+
+ private int startCaptureInternal(boolean enablePostivew,
+ @NonNull CaptureCallback captureCallback) {
+
+ // Send postview request
+ if (enablePostivew) {
+ RequestBuilder builderPostview = new RequestBuilder(mPostviewOutputConfig.getId(),
+ CameraDevice.TEMPLATE_PREVIEW);
+ applyParameters(builderPostview);
+ RequestProcessorImpl.Request postviewRequest = builderPostview.build();
+
+ mRequestProcessor.submit(postviewRequest, new DefaultRequestProcessorImpl());
+ }
+
+ captureCallback.onCaptureProcessProgressed(10);
+
+ // send still capture request
+ final int seqId = mNextCaptureSequenceId.getAndIncrement();
+ updateProcessProgressDelayed(captureCallback, 40, 1000);
+ updateProcessProgressDelayed(captureCallback, 70, 2000);
+ updateProcessProgressDelayed(captureCallback, 100, 3000);
+
+ mBackgroundHandler.postDelayed(() -> {
+ submitStillCapture(seqId, captureCallback);
+ }, 3000);
+ return seqId;
+ }
+
+ private void updateProcessProgressDelayed(CaptureCallback callback,
+ int progress, long delayInMs) {
+ mBackgroundHandler.postDelayed(() -> {
+ callback.onCaptureProcessProgressed(progress);
+ }, delayInMs);
+ }
+
+ private int submitStillCapture(int seqId, @NonNull CaptureCallback captureCallback) {
+ if (mRequestProcessor == null) {
+ return -1;
+ }
+ RequestBuilder builderStillCapture = new RequestBuilder(mCaptureOutputConfig.getId(),
+ CameraDevice.TEMPLATE_STILL_CAPTURE);
+ applyParameters(builderStillCapture);
+ RequestProcessorImpl.Request stillcaptureRequest = builderStillCapture.build();
+
+ RequestProcessorImpl.Callback callback = new RequestProcessorImpl.Callback() {
+ private boolean mOnCaptureStartedInvokded = false;
+
+ @Override
+ public void onCaptureStarted(@NonNull RequestProcessorImpl.Request request,
+ long frameNumber, long timestamp) {
+ if (!mOnCaptureStartedInvokded) {
+ mOnCaptureStartedInvokded = true;
+ captureCallback.onCaptureStarted(seqId, timestamp);
+ }
+ }
+
+ @Override
+ public void onCaptureProgressed(@NonNull RequestProcessorImpl.Request request,
+ @NonNull CaptureResult partialResult) {
+
+ }
+
+ @Override
+ public void onCaptureCompleted(@NonNull RequestProcessorImpl.Request request,
+ @NonNull TotalCaptureResult totalCaptureResult) {
+ }
+
+ @Override
+ public void onCaptureFailed(@NonNull RequestProcessorImpl.Request request,
+ @NonNull CaptureFailure captureFailure) {
+ captureCallback.onCaptureFailed(seqId);
+ }
+
+ @Override
+ public void onCaptureBufferLost(@NonNull RequestProcessorImpl.Request request,
+ long frameNumber, int outputStreamId) {
+ captureCallback.onCaptureFailed(seqId);
+ }
+
+ @Override
+ public void onCaptureSequenceCompleted(int sequenceId, long frameNumber) {
+ captureCallback.onCaptureSequenceCompleted(seqId);
+ }
+
+ @Override
+ public void onCaptureSequenceAborted(int sequenceId) {
+ captureCallback.onCaptureSequenceAborted(seqId);
+ }
+ };
+
+ mRequestProcessor.submit(stillcaptureRequest, callback);
+
+ return seqId;
+ }
+ @Override
+ public void abortCapture(int captureSequenceId) {
+
+ }
+
+ @Nullable
+ @Override
+ public Pair<Long, Long> getRealtimeCaptureLatency() {
+ return null;
+ }
+ }
+}
+
diff --git a/camera/camera-testlib-extensions/src/main/java/androidx/camera/extensions/impl/advanced/NightAdvancedExtenderImpl.java b/camera/camera-testlib-extensions/src/main/java/androidx/camera/extensions/impl/advanced/NightAdvancedExtenderImpl.java
index 5c32de8..415a8c5 100644
--- a/camera/camera-testlib-extensions/src/main/java/androidx/camera/extensions/impl/advanced/NightAdvancedExtenderImpl.java
+++ b/camera/camera-testlib-extensions/src/main/java/androidx/camera/extensions/impl/advanced/NightAdvancedExtenderImpl.java
@@ -16,101 +16,19 @@
package androidx.camera.extensions.impl.advanced;
-import android.hardware.camera2.CameraCharacteristics;
-import android.hardware.camera2.CaptureRequest;
-import android.hardware.camera2.CaptureResult;
-import android.util.Range;
-import android.util.Size;
+import android.graphics.ImageFormat;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import java.util.List;
-import java.util.Map;
+import androidx.annotation.RequiresApi;
/**
- * Stub advanced extender implementation for night.
- *
- * <p>This class should be implemented by OEM and deployed to the target devices.
+ * A sample night implementation for testing long processing capture. It is capable of outputting
+ * the postview(YUV format) and the process progress event. ImageAnalysis is not supported.
*
* @since 1.2
*/
-public class NightAdvancedExtenderImpl implements AdvancedExtenderImpl {
+@RequiresApi(21)
+public class NightAdvancedExtenderImpl extends LongCaptureAdvancedExtenderImpl {
public NightAdvancedExtenderImpl() {
- }
-
- @Override
- public boolean isExtensionAvailable(@NonNull String cameraId,
- @NonNull Map<String, CameraCharacteristics> characteristicsMap) {
- throw new RuntimeException("Stub, replace with implementation.");
- }
-
- @Override
- public void init(@NonNull String cameraId,
- @NonNull Map<String, CameraCharacteristics> characteristicsMap) {
- throw new RuntimeException("Stub, replace with implementation.");
- }
-
- @Override
- @Nullable
- public Range<Long> getEstimatedCaptureLatencyRange(
- @NonNull String cameraId, @Nullable Size size, int imageFormat) {
- throw new RuntimeException("Stub, replace with implementation.");
- }
-
- @Override
- @NonNull
- public Map<Integer, List<Size>> getSupportedPreviewOutputResolutions(
- @NonNull String cameraId) {
- throw new RuntimeException("Stub, replace with implementation.");
- }
-
- @Override
- @NonNull
- public Map<Integer, List<Size>> getSupportedCaptureOutputResolutions(
- @NonNull String cameraId) {
- throw new RuntimeException("Stub, replace with implementation.");
- }
-
- @Override
- @NonNull
- public Map<Integer, List<Size>> getSupportedPostviewResolutions(
- @NonNull Size captureSize) {
- throw new RuntimeException("Stub, replace with implementation.");
- }
-
- @Override
- @Nullable
- public List<Size> getSupportedYuvAnalysisResolutions(
- @NonNull String cameraId) {
- throw new RuntimeException("Stub, replace with implementation.");
- }
-
- @Override
- @NonNull
- public SessionProcessorImpl createSessionProcessor() {
- throw new RuntimeException("Stub, replace with implementation.");
- }
-
- @Override
- @NonNull
- public List<CaptureRequest.Key> getAvailableCaptureRequestKeys() {
- throw new RuntimeException("Stub, replace with implementation.");
- }
-
- @Override
- @NonNull
- public List<CaptureResult.Key> getAvailableCaptureResultKeys() {
- throw new RuntimeException("Stub, replace with implementation.");
- }
-
- @Override
- public boolean isCaptureProcessProgressAvailable() {
- throw new RuntimeException("Stub, replace with implementation.");
- }
-
- @Override
- public boolean isPostviewAvailable() {
- throw new RuntimeException("Stub, replace with implementation.");
+ super(/* postviewFormat */ ImageFormat.YUV_420_888);
}
}
diff --git a/camera/camera-testlib-extensions/src/main/java/androidx/camera/extensions/impl/advanced/RequestBuilder.java b/camera/camera-testlib-extensions/src/main/java/androidx/camera/extensions/impl/advanced/RequestBuilder.java
new file mode 100644
index 0000000..b83328b
--- /dev/null
+++ b/camera/camera-testlib-extensions/src/main/java/androidx/camera/extensions/impl/advanced/RequestBuilder.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.extensions.impl.advanced;
+
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CaptureRequest;
+
+import androidx.annotation.NonNull;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A builder to build the {@link RequestProcessorImpl.Request} instance.
+ */
+public class RequestBuilder {
+ List<Integer> mTargetOutputConfigIds = new ArrayList<>();
+ Map<CaptureRequest.Key<?>, Object> mParameters = new HashMap<>();
+ int mTemplateId = CameraDevice.TEMPLATE_PREVIEW;
+
+ /**
+ * Construct the builder with default settings.
+ */
+ public RequestBuilder() {
+ }
+
+ /**
+ * Construct the builder.
+ */
+ public RequestBuilder(int targetOutputConfigId, int templateId) {
+ addTargetOutputConfigIds(targetOutputConfigId);
+ setTemplateId(templateId);
+ }
+
+
+ /**
+ * Adds the target outputconfig ids.
+ */
+ @NonNull
+ public RequestBuilder addTargetOutputConfigIds(int targetOutputConfigId) {
+ mTargetOutputConfigIds.add(targetOutputConfigId);
+ return this;
+ }
+
+ /**
+ * Sets the parameters
+ */
+ @NonNull
+ public RequestBuilder setParameters(@NonNull CaptureRequest.Key<?> key,
+ @NonNull Object value) {
+ mParameters.put(key, value);
+ return this;
+ }
+
+ /**
+ * Sets the template id.
+ */
+ @NonNull
+ public RequestBuilder setTemplateId(int templateId) {
+ mTemplateId = templateId;
+ return this;
+ }
+
+ /**
+ * construct {@link RequestProcessorImpl.Request} instance.
+ */
+ @NonNull
+ public RequestProcessorImpl.Request build() {
+ return new RequestProcessorRequest(
+ mTargetOutputConfigIds, mParameters, mTemplateId);
+ }
+
+ static class RequestProcessorRequest implements RequestProcessorImpl.Request {
+ final List<Integer> mTargetOutputConfigIds;
+ final Map<CaptureRequest.Key<?>, Object> mParameters;
+ final int mTemplateId;
+
+ RequestProcessorRequest(List<Integer> targetOutputConfigIds,
+ Map<CaptureRequest.Key<?>, Object> parameters,
+ int templateId) {
+ mTargetOutputConfigIds = targetOutputConfigIds;
+ mParameters = parameters;
+ mTemplateId = templateId;
+ }
+
+ @Override
+ @NonNull
+ public List<Integer> getTargetOutputConfigIds() {
+ return mTargetOutputConfigIds;
+ }
+
+ @Override
+ @NonNull
+ public Map<CaptureRequest.Key<?>, Object> getParameters() {
+ return mParameters;
+ }
+
+ @Override
+ @NonNull
+ public Integer getTemplateId() {
+ return mTemplateId;
+ }
+ }
+}