Add CaptureIntentPreviewQuirk and TemplateParamsOverride workaround in camera layer
This quirk is used to replace the workaround which changes UseCase's default template type.
For detail, see go/camerax-video-quality-enhancement-for-template-preview
Bug: 340385870
Test: ./gradlew camera:camera-camera2:testDebug; ./gradlew camera:camera-camera2-pipe-integration:testDebug
Change-Id: I4aac0800162d4e6d0bfa533fe487712f6d24a61e
diff --git a/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/testing/TestUseCaseCamera.kt b/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/testing/TestUseCaseCamera.kt
index 97a2497..70dba7c 100644
--- a/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/testing/TestUseCaseCamera.kt
+++ b/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/testing/TestUseCaseCamera.kt
@@ -31,6 +31,7 @@
import androidx.camera.camera2.pipe.integration.compat.StreamConfigurationMapCompat
import androidx.camera.camera2.pipe.integration.compat.quirk.CameraQuirks
import androidx.camera.camera2.pipe.integration.compat.workaround.NoOpInactiveSurfaceCloser
+import androidx.camera.camera2.pipe.integration.compat.workaround.NoOpTemplateParamsOverride
import androidx.camera.camera2.pipe.integration.compat.workaround.OutputSizesCorrector
import androidx.camera.camera2.pipe.integration.config.CameraConfig
import androidx.camera.camera2.pipe.integration.config.UseCaseCameraConfig
@@ -101,7 +102,8 @@
cameraConfig,
cameraQuirks,
null,
- ZslControlNoOpImpl()
+ ZslControlNoOpImpl(),
+ NoOpTemplateParamsOverride,
)
val cameraGraph = cameraPipe.create(cameraGraphConfig)
@@ -141,7 +143,8 @@
UseCaseCameraState(
useCaseCameraGraphConfig,
threads,
- sessionProcessorManager = null
+ sessionProcessorManager = null,
+ templateParamsOverride = NoOpTemplateParamsOverride,
),
useCaseGraphConfig = useCaseCameraGraphConfig,
)
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/CameraCompatModule.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/CameraCompatModule.kt
index 6472c76..157343e 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/CameraCompatModule.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/CameraCompatModule.kt
@@ -19,6 +19,7 @@
import androidx.camera.camera2.pipe.integration.compat.workaround.AutoFlashAEModeDisabler
import androidx.camera.camera2.pipe.integration.compat.workaround.InactiveSurfaceCloser
import androidx.camera.camera2.pipe.integration.compat.workaround.MeteringRegionCorrection
+import androidx.camera.camera2.pipe.integration.compat.workaround.TemplateParamsOverride
import androidx.camera.camera2.pipe.integration.compat.workaround.UseFlashModeTorchFor3aUpdate
import androidx.camera.camera2.pipe.integration.compat.workaround.UseTorchAsFlash
import dagger.Module
@@ -32,6 +33,7 @@
MeteringRegionCorrection.Bindings::class,
UseFlashModeTorchFor3aUpdate.Bindings::class,
UseTorchAsFlash.Bindings::class,
+ TemplateParamsOverride.Bindings::class,
],
)
abstract class CameraCompatModule
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CaptureIntentPreviewQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CaptureIntentPreviewQuirk.kt
new file mode 100644
index 0000000..9bf8a0f
--- /dev/null
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CaptureIntentPreviewQuirk.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2024 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.camera2.pipe.integration.compat.quirk
+
+import android.hardware.camera2.CaptureRequest
+import androidx.camera.core.impl.Quirk
+import androidx.camera.core.impl.Quirks
+
+/**
+ * A Quirk interface denotes devices have specific issue and can be workaround by using
+ * [CaptureRequest.CONTROL_CAPTURE_INTENT_PREVIEW] to replace
+ * [CaptureRequest.CONTROL_CAPTURE_INTENT_VIDEO_RECORD].
+ * - Subclasses of this quirk may contain device specific information.
+ */
+interface CaptureIntentPreviewQuirk : Quirk {
+ /**
+ * Returns if the device specific issue can be workaround by using
+ * [CaptureRequest.CONTROL_CAPTURE_INTENT_PREVIEW] to replace
+ * [CaptureRequest.CONTROL_CAPTURE_INTENT_VIDEO_RECORD].
+ */
+ fun workaroundByCaptureIntentPreview() = true
+
+ companion object {
+ /**
+ * Returns if input quirks contains at least one [CaptureIntentPreviewQuirk] which
+ * [CaptureIntentPreviewQuirk.workaroundByCaptureIntentPreview] is true.
+ */
+ fun workaroundByCaptureIntentPreview(quirks: Quirks): Boolean {
+ for (quirk in quirks.getAll(CaptureIntentPreviewQuirk::class.java)) {
+ if (quirk.workaroundByCaptureIntentPreview()) {
+ return true
+ }
+ }
+ return false
+ }
+ }
+}
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/TemplateParamsOverride.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/TemplateParamsOverride.kt
new file mode 100644
index 0000000..c7783a7
--- /dev/null
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/TemplateParamsOverride.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2024 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.camera2.pipe.integration.compat.workaround
+
+import android.hardware.camera2.CameraDevice
+import android.hardware.camera2.CameraMetadata.CONTROL_CAPTURE_INTENT_PREVIEW
+import android.hardware.camera2.CaptureRequest
+import android.hardware.camera2.CaptureRequest.CONTROL_CAPTURE_INTENT
+import androidx.camera.camera2.pipe.RequestTemplate
+import androidx.camera.camera2.pipe.integration.compat.quirk.CameraQuirks
+import androidx.camera.camera2.pipe.integration.compat.quirk.CaptureIntentPreviewQuirk.Companion.workaroundByCaptureIntentPreview
+import dagger.Module
+import dagger.Provides
+
+/**
+ * Workaround to get those capture parameters used to override the template default parameters.
+ * - This workaround should only be applied on repeating request but not on single request.
+ */
+interface TemplateParamsOverride {
+ /** Returns capture parameters used to override the default parameters of the input template. */
+ fun getOverrideParams(template: RequestTemplate?): Map<CaptureRequest.Key<*>, Any>
+
+ @Module
+ abstract class Bindings {
+ companion object {
+ @Provides
+ fun provideTemplateParamsOverride(quirks: CameraQuirks): TemplateParamsOverride {
+ return if (workaroundByCaptureIntentPreview(quirks.quirks))
+ TemplateParamsQuirkOverride
+ else NoOpTemplateParamsOverride
+ }
+ }
+ }
+}
+
+object TemplateParamsQuirkOverride : TemplateParamsOverride {
+ override fun getOverrideParams(template: RequestTemplate?): Map<CaptureRequest.Key<*>, Any> {
+ if (template?.value == CameraDevice.TEMPLATE_RECORD) {
+ return mapOf(CONTROL_CAPTURE_INTENT to CONTROL_CAPTURE_INTENT_PREVIEW)
+ }
+ return emptyMap()
+ }
+}
+
+object NoOpTemplateParamsOverride : TemplateParamsOverride {
+ override fun getOverrideParams(template: RequestTemplate?): Map<CaptureRequest.Key<*>, Any> {
+ return emptyMap()
+ }
+}
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraState.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraState.kt
index e5552c8..a508301 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraState.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraState.kt
@@ -33,6 +33,7 @@
import androidx.camera.camera2.pipe.RequestTemplate
import androidx.camera.camera2.pipe.StreamId
import androidx.camera.camera2.pipe.core.Log.debug
+import androidx.camera.camera2.pipe.integration.compat.workaround.TemplateParamsOverride
import androidx.camera.camera2.pipe.integration.config.UseCaseCameraScope
import androidx.camera.camera2.pipe.integration.config.UseCaseGraphConfig
import androidx.camera.core.Preview
@@ -64,6 +65,7 @@
useCaseGraphConfig: UseCaseGraphConfig,
private val threads: UseCaseThreads,
private val sessionProcessorManager: SessionProcessorManager?,
+ private val templateParamsOverride: TemplateParamsOverride,
) {
private val lock = Any()
@@ -276,7 +278,9 @@
Request(
template = currentTemplate,
streams = currentStreams.toList(),
- parameters = currentParameters.toMap(),
+ parameters =
+ templateParamsOverride.getOverrideParams(currentTemplate) +
+ currentParameters.toMap(),
extras =
currentInternalParameters.toMutableMap().also { parameters
->
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManager.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManager.kt
index 93319bd..1efebfd 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManager.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManager.kt
@@ -49,6 +49,7 @@
import androidx.camera.camera2.pipe.integration.compat.quirk.CloseCaptureSessionOnVideoQuirk
import androidx.camera.camera2.pipe.integration.compat.quirk.DeviceQuirks
import androidx.camera.camera2.pipe.integration.compat.quirk.DisableAbortCapturesOnStopWithSessionProcessorQuirk
+import androidx.camera.camera2.pipe.integration.compat.workaround.TemplateParamsOverride
import androidx.camera.camera2.pipe.integration.config.CameraConfig
import androidx.camera.camera2.pipe.integration.config.CameraScope
import androidx.camera.camera2.pipe.integration.config.UseCaseCameraComponent
@@ -124,6 +125,7 @@
private val cameraInternal: Provider<CameraInternal>,
private val useCaseThreads: Provider<UseCaseThreads>,
private val cameraInfoInternal: Provider<CameraInfoInternal>,
+ private val templateParamsOverride: TemplateParamsOverride,
context: Context,
cameraProperties: CameraProperties,
displayInfoManager: DisplayInfoManager,
@@ -588,6 +590,7 @@
cameraQuirks,
cameraGraphFlags,
zslControl,
+ templateParamsOverride,
isExtensions,
)
}
@@ -698,6 +701,7 @@
cameraQuirks: CameraQuirks,
cameraGraphFlags: CameraGraph.Flags?,
zslControl: ZslControl,
+ templateParamsOverride: TemplateParamsOverride,
isExtensions: Boolean = false,
): CameraGraph.Config {
var containsVideo = false
@@ -717,6 +721,7 @@
if (sessionConfig.templateType != CaptureConfig.TEMPLATE_TYPE_NONE) {
sessionTemplate = RequestTemplate(sessionConfig.templateType)
}
+ sessionParameters.putAll(templateParamsOverride.getOverrideParams(sessionTemplate))
sessionParameters.putAll(sessionConfig.implementationOptions.toParameters())
val physicalCameraIdForAllStreams =
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/compat/workaround/TemplateParamsOverrideTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/compat/workaround/TemplateParamsOverrideTest.kt
new file mode 100644
index 0000000..0fdd71e
--- /dev/null
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/compat/workaround/TemplateParamsOverrideTest.kt
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2024 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.camera2.pipe.integration.compat.workaround
+
+import android.hardware.camera2.CameraDevice.TEMPLATE_MANUAL
+import android.hardware.camera2.CameraDevice.TEMPLATE_PREVIEW
+import android.hardware.camera2.CameraDevice.TEMPLATE_RECORD
+import android.hardware.camera2.CameraDevice.TEMPLATE_STILL_CAPTURE
+import android.hardware.camera2.CameraDevice.TEMPLATE_VIDEO_SNAPSHOT
+import android.hardware.camera2.CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG
+import android.hardware.camera2.CameraMetadata.CONTROL_CAPTURE_INTENT_PREVIEW
+import android.hardware.camera2.CaptureRequest
+import android.hardware.camera2.CaptureRequest.CONTROL_CAPTURE_INTENT
+import androidx.camera.camera2.pipe.RequestTemplate
+import androidx.camera.camera2.pipe.integration.compat.StreamConfigurationMapCompat
+import androidx.camera.camera2.pipe.integration.compat.quirk.CameraQuirks
+import androidx.camera.camera2.pipe.integration.compat.quirk.CaptureIntentPreviewQuirk
+import androidx.camera.camera2.pipe.testing.FakeCameraMetadata
+import androidx.camera.core.impl.Quirk
+import com.google.common.truth.Truth.assertWithMessage
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.ParameterizedRobolectricTestRunner
+import org.robolectric.annotation.internal.DoNotInstrument
+import org.robolectric.shadows.StreamConfigurationMapBuilder
+
+/** Unit test for [TemplateParamsOverride]. */
+@RunWith(ParameterizedRobolectricTestRunner::class)
+@DoNotInstrument
+class TemplateParamsOverrideTest(
+ private val testName: String,
+ private val quirk: Quirk?,
+ private val expectedParamsMap: Map<Int, Map<CaptureRequest.Key<*>, Any>>,
+) {
+ companion object {
+ private val emptyParamsMap = emptyMap<CaptureRequest.Key<*>, Any>()
+ private val paramsMapForCaptureIntentPreviewQuirk =
+ mapOf(
+ TEMPLATE_RECORD to mapOf(CONTROL_CAPTURE_INTENT to CONTROL_CAPTURE_INTENT_PREVIEW)
+ )
+
+ @JvmStatic
+ @ParameterizedRobolectricTestRunner.Parameters(name = "testName={0}")
+ fun data() =
+ mutableListOf<Array<Any?>>().apply {
+ add(
+ arrayOf(
+ "no quirk",
+ null,
+ emptyParamsMap,
+ )
+ )
+ add(
+ arrayOf(
+ "CaptureIntentPreviewQuirk with false workaround flag",
+ object : CaptureIntentPreviewQuirk {
+ override fun workaroundByCaptureIntentPreview() = false
+ },
+ emptyParamsMap,
+ )
+ )
+ add(
+ arrayOf(
+ "CaptureIntentPreviewQuirk with true workaround flag",
+ object : CaptureIntentPreviewQuirk {
+ override fun workaroundByCaptureIntentPreview() = true
+ },
+ paramsMapForCaptureIntentPreviewQuirk,
+ )
+ )
+ }
+ }
+
+ private lateinit var cameraQuirks: CameraQuirks
+
+ @Before
+ fun setup() {
+ cameraQuirks =
+ CameraQuirks(
+ FakeCameraMetadata(),
+ StreamConfigurationMapCompat(
+ StreamConfigurationMapBuilder.newBuilder().build(),
+ OutputSizesCorrector(
+ FakeCameraMetadata(),
+ StreamConfigurationMapBuilder.newBuilder().build()
+ )
+ )
+ )
+ if (quirk != null) {
+ cameraQuirks.quirks.addQuirkForTesting(quirk)
+ }
+ }
+
+ @Test
+ fun getOverrideParams() {
+ for (template in
+ listOf(
+ TEMPLATE_PREVIEW,
+ TEMPLATE_RECORD,
+ TEMPLATE_STILL_CAPTURE,
+ TEMPLATE_MANUAL,
+ TEMPLATE_VIDEO_SNAPSHOT,
+ TEMPLATE_ZERO_SHUTTER_LAG
+ )) {
+ val templateParamsOverride =
+ TemplateParamsOverride_Bindings_Companion_ProvideTemplateParamsOverrideFactory
+ .provideTemplateParamsOverride(cameraQuirks)
+ val params = templateParamsOverride.getOverrideParams(RequestTemplate(template))
+ val expectedParams = expectedParamsMap[template] ?: emptyParamsMap
+ assertWithMessage("getOverrideParams with template: $template")
+ .that(params)
+ .isEqualTo(expectedParams)
+ }
+ }
+}
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/CapturePipelineTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/CapturePipelineTest.kt
index e4bc387..6991cef 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/CapturePipelineTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/CapturePipelineTest.kt
@@ -49,6 +49,7 @@
import androidx.camera.camera2.pipe.integration.compat.workaround.AeFpsRange
import androidx.camera.camera2.pipe.integration.compat.workaround.CapturePipelineTorchCorrection
import androidx.camera.camera2.pipe.integration.compat.workaround.NoOpAutoFlashAEModeDisabler
+import androidx.camera.camera2.pipe.integration.compat.workaround.NoOpTemplateParamsOverride
import androidx.camera.camera2.pipe.integration.compat.workaround.NotUseFlashModeTorchFor3aUpdate
import androidx.camera.camera2.pipe.integration.compat.workaround.NotUseTorchAsFlash
import androidx.camera.camera2.pipe.integration.compat.workaround.OutputSizesCorrector
@@ -355,6 +356,7 @@
fakeUseCaseGraphConfig,
fakeUseCaseThreads,
sessionProcessorManager = null,
+ templateParamsOverride = NoOpTemplateParamsOverride,
)
capturePipeline =
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/StillCaptureRequestTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/StillCaptureRequestTest.kt
index 6102b91..e9e7a29 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/StillCaptureRequestTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/StillCaptureRequestTest.kt
@@ -23,6 +23,7 @@
import androidx.camera.camera2.pipe.integration.adapter.CaptureConfigAdapter
import androidx.camera.camera2.pipe.integration.adapter.RobolectricCameraPipeTestRunner
import androidx.camera.camera2.pipe.integration.adapter.ZslControlNoOpImpl
+import androidx.camera.camera2.pipe.integration.compat.workaround.NoOpTemplateParamsOverride
import androidx.camera.camera2.pipe.integration.compat.workaround.NotUseFlashModeTorchFor3aUpdate
import androidx.camera.camera2.pipe.integration.compat.workaround.NotUseTorchAsFlash
import androidx.camera.camera2.pipe.integration.config.UseCaseGraphConfig
@@ -420,6 +421,7 @@
useCaseGraphConfig = fakeUseCaseGraphConfig,
threads = fakeUseCaseThreads,
sessionProcessorManager = null,
+ templateParamsOverride = NoOpTemplateParamsOverride,
)
val torchControl =
TorchControl(fakeCameraProperties, fakeState3AControl, fakeUseCaseThreads)
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraRequestControlTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraRequestControlTest.kt
index 3ffa532..a29dfa3 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraRequestControlTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraRequestControlTest.kt
@@ -27,6 +27,7 @@
import androidx.camera.camera2.pipe.StreamId
import androidx.camera.camera2.pipe.integration.adapter.CameraStateAdapter
import androidx.camera.camera2.pipe.integration.adapter.RobolectricCameraPipeTestRunner
+import androidx.camera.camera2.pipe.integration.compat.workaround.NoOpTemplateParamsOverride
import androidx.camera.camera2.pipe.integration.config.UseCaseGraphConfig
import androidx.camera.camera2.pipe.integration.testing.FakeCameraGraph
import androidx.camera.camera2.pipe.integration.testing.FakeCameraProperties
@@ -79,6 +80,7 @@
useCaseGraphConfig = fakeUseCaseGraphConfig,
threads = useCaseThreads,
sessionProcessorManager = null,
+ templateParamsOverride = NoOpTemplateParamsOverride,
)
private val requestControl =
UseCaseCameraRequestControlImpl(
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraStateTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraStateTest.kt
index 5f9c231..fc87955 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraStateTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraStateTest.kt
@@ -16,11 +16,17 @@
package androidx.camera.camera2.pipe.integration.impl
+import android.hardware.camera2.CameraDevice.TEMPLATE_RECORD
+import android.hardware.camera2.CaptureRequest.CONTROL_CAPTURE_INTENT
+import android.hardware.camera2.CaptureRequest.CONTROL_CAPTURE_INTENT_PREVIEW
import android.os.Build
+import androidx.camera.camera2.pipe.RequestTemplate
import androidx.camera.camera2.pipe.StreamId
import androidx.camera.camera2.pipe.integration.adapter.CameraStateAdapter
import androidx.camera.camera2.pipe.integration.adapter.RobolectricCameraPipeTestRunner
import androidx.camera.camera2.pipe.integration.adapter.asListenableFuture
+import androidx.camera.camera2.pipe.integration.compat.workaround.NoOpTemplateParamsOverride
+import androidx.camera.camera2.pipe.integration.compat.workaround.TemplateParamsQuirkOverride
import androidx.camera.camera2.pipe.integration.config.UseCaseGraphConfig
import androidx.camera.camera2.pipe.integration.testing.FakeCameraGraph
import androidx.camera.camera2.pipe.integration.testing.FakeCameraGraphSession
@@ -30,6 +36,7 @@
import androidx.camera.camera2.pipe.integration.testing.FakeSurface
import androidx.camera.core.impl.DeferrableSurface
import androidx.testutils.MainDispatcherRule
+import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
import com.google.common.util.concurrent.ListenableFuture
import java.util.concurrent.ExecutionException
@@ -81,6 +88,7 @@
useCaseGraphConfig = fakeUseCaseGraphConfig,
threads = useCaseThreads,
sessionProcessorManager = null,
+ templateParamsOverride = NoOpTemplateParamsOverride,
)
@Before
@@ -189,6 +197,37 @@
assertFutureCompletes(result)
}
+ @Test
+ fun updateAsync_overrideTemplateParams(): Unit = runBlocking {
+ val useCaseCameraState =
+ UseCaseCameraState(
+ useCaseGraphConfig = fakeUseCaseGraphConfig,
+ threads = useCaseThreads,
+ sessionProcessorManager = null,
+ templateParamsOverride = TemplateParamsQuirkOverride,
+ )
+
+ // startRepeating is called when there is at least one stream after updateAsync call
+ val template = RequestTemplate(TEMPLATE_RECORD)
+ val result =
+ useCaseCameraState
+ .updateAsync(
+ streams = setOf(StreamId(0)),
+ template = template,
+ )
+ .asListenableFuture()
+
+ // simulate startRepeating request being completed in camera
+ fakeCameraGraphSession.startRepeatingSignal.complete(TOTAL_CAPTURE_DONE)
+
+ assertFutureCompletes(result)
+
+ assertThat(fakeCameraGraphSession.repeatingRequests.size).isEqualTo(1)
+ val request = fakeCameraGraphSession.repeatingRequests[0]
+ assertThat(request.template).isEqualTo(template)
+ assertThat(request[CONTROL_CAPTURE_INTENT]).isEqualTo(CONTROL_CAPTURE_INTENT_PREVIEW)
+ }
+
private fun <T> assertFutureCompletes(future: ListenableFuture<T>) {
future[3, TimeUnit.SECONDS]
}
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraTest.kt
index 3bfb852..df4439a 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraTest.kt
@@ -24,6 +24,7 @@
import androidx.camera.camera2.pipe.integration.adapter.RobolectricCameraPipeTestRunner
import androidx.camera.camera2.pipe.integration.adapter.SessionConfigAdapter
import androidx.camera.camera2.pipe.integration.compat.workaround.NoOpInactiveSurfaceCloser
+import androidx.camera.camera2.pipe.integration.compat.workaround.NoOpTemplateParamsOverride
import androidx.camera.camera2.pipe.integration.config.UseCaseGraphConfig
import androidx.camera.camera2.pipe.integration.testing.FakeCameraGraph
import androidx.camera.camera2.pipe.integration.testing.FakeCameraProperties
@@ -72,6 +73,7 @@
useCaseGraphConfig = fakeUseCaseGraphConfig,
threads = useCaseThreads,
sessionProcessorManager = null,
+ templateParamsOverride = NoOpTemplateParamsOverride,
)
private val requestControl =
UseCaseCameraRequestControlImpl(
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManagerTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManagerTest.kt
index ecaf6bc..f13500f 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManagerTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManagerTest.kt
@@ -41,7 +41,10 @@
import androidx.camera.camera2.pipe.integration.adapter.ZslControlNoOpImpl
import androidx.camera.camera2.pipe.integration.compat.StreamConfigurationMapCompat
import androidx.camera.camera2.pipe.integration.compat.quirk.CameraQuirks
+import androidx.camera.camera2.pipe.integration.compat.workaround.NoOpTemplateParamsOverride
import androidx.camera.camera2.pipe.integration.compat.workaround.OutputSizesCorrector
+import androidx.camera.camera2.pipe.integration.compat.workaround.TemplateParamsOverride
+import androidx.camera.camera2.pipe.integration.compat.workaround.TemplateParamsQuirkOverride
import androidx.camera.camera2.pipe.integration.config.CameraConfig
import androidx.camera.camera2.pipe.integration.impl.UseCaseCamera.RunningUseCasesChangeListener
import androidx.camera.camera2.pipe.integration.interop.Camera2CameraControl
@@ -517,12 +520,40 @@
.isEqualTo(mapOf(CONTROL_CAPTURE_INTENT to CONTROL_CAPTURE_INTENT_PREVIEW))
}
+ @Test
+ fun overrideTemplateParams() = runTest {
+ // Arrange
+ initializeUseCaseThreads(this)
+ val useCaseManager =
+ createUseCaseManager(templateParamsOverride = TemplateParamsQuirkOverride)
+ val fakeUseCase =
+ FakeUseCase().apply {
+ updateSessionConfigForTesting(
+ SessionConfig.Builder().setTemplateType(TEMPLATE_RECORD).build()
+ )
+ }
+ val sessionConfigAdapter = SessionConfigAdapter(setOf(fakeUseCase))
+ val streamConfigMap = mutableMapOf<CameraStream.Config, DeferrableSurface>()
+
+ // Act.
+ val cameraGraphConfig =
+ useCaseManager.createCameraGraphConfig(
+ sessionConfigAdapter,
+ streamConfigMap,
+ )
+
+ // Assert
+ assertThat(cameraGraphConfig.sessionParameters[CONTROL_CAPTURE_INTENT])
+ .isEqualTo(CONTROL_CAPTURE_INTENT_PREVIEW)
+ }
+
@OptIn(ExperimentalCamera2Interop::class)
@Suppress("UNCHECKED_CAST", "PLATFORM_CLASS_MAPPED_TO_KOTLIN")
private fun createUseCaseManager(
controls: Set<UseCaseCameraControl> = emptySet(),
useCaseCameraComponentBuilder: FakeUseCaseCameraComponentBuilder =
FakeUseCaseCameraComponentBuilder(),
+ templateParamsOverride: TemplateParamsOverride = NoOpTemplateParamsOverride,
): UseCaseManager {
val cameraId = CameraId("0")
val characteristicsMap: Map<CameraCharacteristics.Key<*>, Any?> =
@@ -576,6 +607,7 @@
DisplayInfoManager(ApplicationProvider.getApplicationContext()),
context = ApplicationProvider.getApplicationContext(),
cameraInfoInternal = { fakeCamera.cameraInfoInternal },
+ templateParamsOverride = templateParamsOverride,
useCaseThreads = { useCaseThreads },
)
.also { useCaseManagerList.add(it) }
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CaptureRequestBuilder.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CaptureRequestBuilder.java
index 592ad30..4d36f185 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CaptureRequestBuilder.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CaptureRequestBuilder.java
@@ -29,6 +29,7 @@
import androidx.annotation.OptIn;
import androidx.annotation.RequiresApi;
import androidx.annotation.VisibleForTesting;
+import androidx.camera.camera2.internal.compat.workaround.TemplateParamsOverride;
import androidx.camera.camera2.interop.CaptureRequestOptions;
import androidx.camera.camera2.interop.ExperimentalCamera2Interop;
import androidx.camera.core.Logger;
@@ -77,6 +78,17 @@
return surfaceList;
}
+ private static void applyTemplateParamsOverrideWorkaround(
+ @NonNull CaptureRequest.Builder builder, int template,
+ @NonNull TemplateParamsOverride templateParamsOverride) {
+ for (Map.Entry<CaptureRequest.Key<?>, Object> entry :
+ templateParamsOverride.getOverrideParams(template).entrySet()) {
+ @SuppressWarnings("unchecked")
+ CaptureRequest.Key<Object> key = (CaptureRequest.Key<Object>) entry.getKey();
+ builder.set(key, entry.getValue());
+ }
+ }
+
@OptIn(markerClass = ExperimentalCamera2Interop.class)
private static void applyImplementationOptionToCaptureBuilder(
CaptureRequest.Builder builder, Config config) {
@@ -137,7 +149,7 @@
public static CaptureRequest build(@NonNull CaptureConfig captureConfig,
@Nullable CameraDevice device,
@NonNull Map<DeferrableSurface, Surface> configuredSurfaceMap,
- boolean isRepeatingRequest)
+ boolean isRepeatingRequest, @NonNull TemplateParamsOverride mTemplateParamsOverride)
throws CameraAccessException {
if (device == null) {
return null;
@@ -170,6 +182,11 @@
}
}
+ if (isRepeatingRequest) {
+ applyTemplateParamsOverrideWorkaround(builder, captureConfig.getTemplateType(),
+ mTemplateParamsOverride);
+ }
+
applyAeFpsRange(captureConfig, builder);
applyVideoStabilization(captureConfig, builder);
@@ -211,7 +228,7 @@
*/
@Nullable
public static CaptureRequest buildWithoutTarget(@NonNull CaptureConfig captureConfig,
- @Nullable CameraDevice device)
+ @Nullable CameraDevice device, @NonNull TemplateParamsOverride templateParamsOverride)
throws CameraAccessException {
if (device == null) {
return null;
@@ -219,6 +236,9 @@
CaptureRequest.Builder builder = device.createCaptureRequest(
captureConfig.getTemplateType());
+ applyTemplateParamsOverrideWorkaround(builder, captureConfig.getTemplateType(),
+ templateParamsOverride);
+
applyImplementationOptionToCaptureBuilder(builder,
captureConfig.getImplementationOptions());
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/CaptureSession.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/CaptureSession.java
index 9c5cb20..e785b48 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/CaptureSession.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/CaptureSession.java
@@ -40,6 +40,7 @@
import androidx.camera.camera2.internal.compat.quirk.CaptureNoResponseQuirk;
import androidx.camera.camera2.internal.compat.workaround.RequestMonitor;
import androidx.camera.camera2.internal.compat.workaround.StillCaptureFlow;
+import androidx.camera.camera2.internal.compat.workaround.TemplateParamsOverride;
import androidx.camera.camera2.internal.compat.workaround.TorchStateReset;
import androidx.camera.camera2.interop.ExperimentalCamera2Interop;
import androidx.camera.core.DynamicRange;
@@ -121,6 +122,7 @@
private final TorchStateReset mTorchStateReset = new TorchStateReset();
private final RequestMonitor mRequestMonitor;
private final DynamicRangesCompat mDynamicRangesCompat;
+ private final TemplateParamsOverride mTemplateParamsOverride;
/**
* Constructor for CaptureSession without CameraQuirk.
@@ -139,6 +141,7 @@
mCaptureSessionStateCallback = new StateCallback();
mRequestMonitor = new RequestMonitor(
cameraQuirks != null && cameraQuirks.contains(CaptureNoResponseQuirk.class));
+ mTemplateParamsOverride = new TemplateParamsOverride(cameraQuirks);
}
@Override
@@ -336,7 +339,8 @@
try {
CaptureRequest captureRequest =
Camera2CaptureRequestBuilder.buildWithoutTarget(
- sessionParameterConfigBuilder.build(), cameraDevice);
+ sessionParameterConfigBuilder.build(), cameraDevice,
+ mTemplateParamsOverride);
if (captureRequest != null) {
sessionConfigCompat.setSessionParameters(captureRequest);
}
@@ -647,7 +651,7 @@
Logger.d(TAG, "Issuing request for session.");
CaptureRequest captureRequest = Camera2CaptureRequestBuilder.build(
captureConfig, mSynchronizedCaptureSession.getDevice(),
- mConfiguredSurfaceMap, true);
+ mConfiguredSurfaceMap, true, mTemplateParamsOverride);
if (captureRequest == null) {
Logger.d(TAG, "Skipping issuing empty request for session.");
return -1;
@@ -757,7 +761,7 @@
CaptureRequest captureRequest = Camera2CaptureRequestBuilder.build(
captureConfigBuilder.build(), mSynchronizedCaptureSession.getDevice(),
- mConfiguredSurfaceMap, false);
+ mConfiguredSurfaceMap, false, mTemplateParamsOverride);
if (captureRequest == null) {
Logger.d(TAG, "Skipping issuing request without surface.");
return -1;
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/quirk/CaptureIntentPreviewQuirk.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/quirk/CaptureIntentPreviewQuirk.java
new file mode 100644
index 0000000..a8865c3
--- /dev/null
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/quirk/CaptureIntentPreviewQuirk.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2024 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.camera2.internal.compat.quirk;
+
+import android.hardware.camera2.CaptureRequest;
+
+import androidx.annotation.NonNull;
+import androidx.camera.core.impl.Quirk;
+import androidx.camera.core.impl.Quirks;
+
+/**
+ * A Quirk interface denotes devices have specific issue and can be workaround by using
+ * {@link CaptureRequest#CONTROL_CAPTURE_INTENT_PREVIEW} to replace
+ * {@link CaptureRequest#CONTROL_CAPTURE_INTENT_VIDEO_RECORD}.
+ *
+ * <p>Subclasses of this quirk may contain device specific information.
+ */
+public interface CaptureIntentPreviewQuirk extends Quirk {
+ /**
+ * Returns if the device specific issue can be workaround by using
+ * {@link CaptureRequest#CONTROL_CAPTURE_INTENT_PREVIEW} to replace
+ * {@link CaptureRequest#CONTROL_CAPTURE_INTENT_VIDEO_RECORD}.
+ */
+ default boolean workaroundByCaptureIntentPreview() {
+ return true;
+ }
+
+ /**
+ * Returns if input quirks contains at least one {@link CaptureIntentPreviewQuirk} which
+ * {@link CaptureIntentPreviewQuirk#workaroundByCaptureIntentPreview()} is true.
+ */
+ static boolean workaroundByCaptureIntentPreview(@NonNull Quirks quirks) {
+ for (CaptureIntentPreviewQuirk quirk : quirks.getAll(CaptureIntentPreviewQuirk.class)) {
+ if (quirk.workaroundByCaptureIntentPreview()) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/workaround/TemplateParamsOverride.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/workaround/TemplateParamsOverride.java
new file mode 100644
index 0000000..ce64101
--- /dev/null
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/workaround/TemplateParamsOverride.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2024 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.camera2.internal.compat.workaround;
+
+import static android.hardware.camera2.CameraDevice.TEMPLATE_RECORD;
+import static android.hardware.camera2.CameraMetadata.CONTROL_CAPTURE_INTENT_PREVIEW;
+import static android.hardware.camera2.CaptureRequest.CONTROL_CAPTURE_INTENT;
+
+import static androidx.camera.camera2.internal.compat.quirk.CaptureIntentPreviewQuirk.workaroundByCaptureIntentPreview;
+
+import static java.util.Collections.emptyMap;
+import static java.util.Collections.unmodifiableMap;
+
+import android.hardware.camera2.CaptureRequest;
+
+import androidx.annotation.NonNull;
+import androidx.camera.core.impl.Quirks;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Workaround to get those capture parameters used to override the template default parameters.
+ *
+ * <p>This workaround should only be applied on repeating request but not on single request.
+ */
+public class TemplateParamsOverride {
+ private final Quirks mQuirks;
+
+ public TemplateParamsOverride(@NonNull Quirks quirks) {
+ mQuirks = quirks;
+ }
+
+ /**
+ * Returns capture parameters used to override the default parameters of the input template.
+ */
+ @NonNull
+ public Map<CaptureRequest.Key<?>, Object> getOverrideParams(int template) {
+ if (template == TEMPLATE_RECORD && workaroundByCaptureIntentPreview(mQuirks)) {
+ Map<CaptureRequest.Key<?>, Object> params = new HashMap<>();
+ params.put(CONTROL_CAPTURE_INTENT, CONTROL_CAPTURE_INTENT_PREVIEW);
+ return unmodifiableMap(params);
+ }
+ return emptyMap();
+ }
+}
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/Camera2CaptureRequestBuilderTest.java b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/Camera2CaptureRequestBuilderTest.java
index 6f7d947..5fb8fd8 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/Camera2CaptureRequestBuilderTest.java
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/Camera2CaptureRequestBuilderTest.java
@@ -18,14 +18,16 @@
import static com.google.common.truth.Truth.assertThat;
+import static java.util.Collections.emptyList;
+
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CaptureRequest;
import android.os.Build;
-import android.view.Surface;
+import androidx.camera.camera2.internal.compat.workaround.TemplateParamsOverride;
import androidx.camera.core.impl.CaptureConfig;
-import androidx.camera.core.impl.DeferrableSurface;
+import androidx.camera.core.impl.Quirks;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -44,9 +46,11 @@
public void buildCaptureRequestWithNullCameraDevice() throws CameraAccessException {
CameraDevice cameraDevice = null;
CaptureConfig captureConfig = new CaptureConfig.Builder().build();
+ TemplateParamsOverride noOpTemplateParamsOverride = new TemplateParamsOverride(
+ new Quirks(emptyList()));
CaptureRequest captureRequest = Camera2CaptureRequestBuilder.build(captureConfig,
- cameraDevice, new HashMap<DeferrableSurface, Surface>(), true);
+ cameraDevice, new HashMap<>(), true, noOpTemplateParamsOverride);
assertThat(captureRequest).isNull();
}
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/compat/workaround/TemplateParamsOverrideTest.kt b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/compat/workaround/TemplateParamsOverrideTest.kt
new file mode 100644
index 0000000..0c42de6
--- /dev/null
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/compat/workaround/TemplateParamsOverrideTest.kt
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2024 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.camera2.internal.compat.workaround
+
+import android.hardware.camera2.CameraDevice.TEMPLATE_MANUAL
+import android.hardware.camera2.CameraDevice.TEMPLATE_PREVIEW
+import android.hardware.camera2.CameraDevice.TEMPLATE_RECORD
+import android.hardware.camera2.CameraDevice.TEMPLATE_STILL_CAPTURE
+import android.hardware.camera2.CameraDevice.TEMPLATE_VIDEO_SNAPSHOT
+import android.hardware.camera2.CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG
+import android.hardware.camera2.CameraMetadata.CONTROL_CAPTURE_INTENT_PREVIEW
+import android.hardware.camera2.CaptureRequest
+import android.hardware.camera2.CaptureRequest.CONTROL_CAPTURE_INTENT
+import androidx.camera.camera2.internal.compat.quirk.CaptureIntentPreviewQuirk
+import androidx.camera.core.impl.Quirk
+import androidx.camera.core.impl.Quirks
+import com.google.common.truth.Truth.assertWithMessage
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.ParameterizedRobolectricTestRunner
+import org.robolectric.annotation.internal.DoNotInstrument
+
+/** Unit test for [TemplateParamsOverride]. */
+@RunWith(ParameterizedRobolectricTestRunner::class)
+@DoNotInstrument
+class TemplateParamsOverrideTest(
+ private val testName: String,
+ quirk: Quirk?,
+ private val expectedParamsMap: Map<Int, Map<CaptureRequest.Key<*>, Any>>,
+) {
+ companion object {
+ private val emptyParamsMap = emptyMap<CaptureRequest.Key<*>, Any>()
+ private val paramsMapForCaptureIntentPreviewQuirk =
+ mapOf(
+ TEMPLATE_RECORD to mapOf(CONTROL_CAPTURE_INTENT to CONTROL_CAPTURE_INTENT_PREVIEW)
+ )
+
+ @JvmStatic
+ @ParameterizedRobolectricTestRunner.Parameters(name = "testName={0}")
+ fun data() =
+ mutableListOf<Array<Any?>>().apply {
+ add(
+ arrayOf(
+ "no quirk",
+ null,
+ emptyParamsMap,
+ )
+ )
+ add(
+ arrayOf(
+ "CaptureIntentPreviewQuirk with false workaround flag",
+ object : CaptureIntentPreviewQuirk {
+ override fun workaroundByCaptureIntentPreview() = false
+ },
+ emptyParamsMap,
+ )
+ )
+ add(
+ arrayOf(
+ "CaptureIntentPreviewQuirk with true workaround flag",
+ object : CaptureIntentPreviewQuirk {
+ override fun workaroundByCaptureIntentPreview() = true
+ },
+ paramsMapForCaptureIntentPreviewQuirk,
+ )
+ )
+ }
+ }
+
+ private val quirks = Quirks(if (quirk != null) listOf(quirk) else emptyList())
+
+ @Test
+ fun getOverrideParams() {
+ for (template in
+ listOf(
+ TEMPLATE_PREVIEW,
+ TEMPLATE_RECORD,
+ TEMPLATE_STILL_CAPTURE,
+ TEMPLATE_MANUAL,
+ TEMPLATE_VIDEO_SNAPSHOT,
+ TEMPLATE_ZERO_SHUTTER_LAG
+ )) {
+ val params = TemplateParamsOverride(quirks).getOverrideParams(template)
+ val expectedParams = expectedParamsMap[template] ?: emptyParamsMap
+ assertWithMessage("getOverrideParams with template: $template")
+ .that(params)
+ .isEqualTo(expectedParams)
+ }
+ }
+}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/Quirks.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/Quirks.java
index 81bafdb..baedf54 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/Quirks.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/Quirks.java
@@ -18,6 +18,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
import java.util.ArrayList;
import java.util.List;
@@ -97,4 +98,10 @@
return false;
}
+
+ /** Adds an extra quirk. */
+ @VisibleForTesting
+ public void addQuirkForTesting(@NonNull Quirk quirk) {
+ mQuirks.add(quirk);
+ }
}