Set TagBundle to the CaptureRequest
The TagBundle can be set to the "extras" which is a
map of keys of type Metadata.Key<T>.
Update CaptureConfigAdapter to set the TagBundle from
CaptureConfig to the Request.
Test: manually run CaptureConfigAdapterDeviceTest on lab devices
Change-Id: I6ba724614879395939334dfe1b0375c0d64dbcca
diff --git a/camera/camera-camera2-pipe-integration/build.gradle b/camera/camera-camera2-pipe-integration/build.gradle
index 22e07ea..db18e12 100644
--- a/camera/camera-camera2-pipe-integration/build.gradle
+++ b/camera/camera-camera2-pipe-integration/build.gradle
@@ -75,6 +75,7 @@
androidTestImplementation(libs.truth)
androidTestImplementation(project(":annotation:annotation-experimental"))
androidTestImplementation(project(":camera:camera-lifecycle"))
+ androidTestImplementation(project(":camera:camera-testing"))
androidTestImplementation(project(":concurrent:concurrent-futures-ktx"))
}
diff --git a/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/integration/CaptureConfigAdapterDeviceTest.kt b/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/integration/CaptureConfigAdapterDeviceTest.kt
new file mode 100644
index 0000000..8ef8557
--- /dev/null
+++ b/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/integration/CaptureConfigAdapterDeviceTest.kt
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2021 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
+
+import android.content.Context
+import android.graphics.SurfaceTexture
+import android.hardware.camera2.CameraDevice
+import android.view.Surface
+import androidx.camera.camera2.pipe.integration.adapter.CameraControlAdapter
+import androidx.camera.core.CameraSelector
+import androidx.camera.core.CameraX
+import androidx.camera.core.impl.CameraCaptureCallback
+import androidx.camera.core.impl.CameraCaptureFailure
+import androidx.camera.core.impl.CameraCaptureResult
+import androidx.camera.core.impl.CaptureConfig
+import androidx.camera.core.impl.DeferrableSurface
+import androidx.camera.core.impl.SessionConfig
+import androidx.camera.core.impl.utils.futures.Futures
+import androidx.camera.core.internal.CameraUseCaseAdapter
+import androidx.camera.testing.CameraUtil
+import androidx.camera.testing.fakes.FakeUseCase
+import androidx.camera.testing.fakes.FakeUseCaseConfig
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import com.google.common.truth.Truth
+import com.google.common.util.concurrent.ListenableFuture
+import kotlinx.coroutines.CompletableDeferred
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.asExecutor
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.withContext
+import kotlinx.coroutines.withTimeoutOrNull
+import org.junit.After
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TestRule
+import org.junit.runner.RunWith
+import java.util.concurrent.TimeUnit
+
+private const val DEFAULT_LENS_FACING_SELECTOR = CameraSelector.LENS_FACING_BACK
+
+@LargeTest
+@RunWith(AndroidJUnit4::class)
+class CaptureConfigAdapterDeviceTest {
+
+ @get:Rule
+ val useCamera: TestRule = CameraUtil.grantCameraPermissionAndPreTest()
+
+ private var cameraControl: CameraControlAdapter? = null
+ private var camera: CameraUseCaseAdapter? = null
+ private val testDeferrableSurface = TestDeferrableSurface()
+ private val fakeUseCase = FakeTestUseCase(
+ FakeUseCaseConfig.Builder().setTargetName("UseCase").useCaseConfig
+ ).apply {
+ setupSessionConfig(
+ SessionConfig.Builder().also { sessionConfigBuilder ->
+ sessionConfigBuilder.setTemplateType(CameraDevice.TEMPLATE_PREVIEW)
+ sessionConfigBuilder.addSurface(testDeferrableSurface)
+ }
+ )
+ }
+
+ @Before
+ fun setUp() = runBlocking {
+ Assume.assumeTrue(CameraUtil.hasCameraWithLensFacing(DEFAULT_LENS_FACING_SELECTOR))
+
+ val context: Context = ApplicationProvider.getApplicationContext()
+ CameraX.initialize(context, CameraPipeConfig.defaultConfig())
+ camera = CameraUtil.createCameraUseCaseAdapter(
+ context,
+ CameraSelector.Builder().requireLensFacing(
+ DEFAULT_LENS_FACING_SELECTOR
+ ).build()
+ ).apply {
+ withContext(Dispatchers.Main) {
+ addUseCases(listOf(fakeUseCase))
+ }
+ }
+
+ cameraControl = camera!!.cameraControl as CameraControlAdapter
+ }
+
+ @After
+ fun tearDown() {
+ camera?.detachUseCases()
+ testDeferrableSurface.close()
+ CameraX.shutdown()[10000, TimeUnit.MILLISECONDS]
+ }
+
+ @Test
+ fun tagBundleTest() = runBlocking {
+ // Arrange
+ val deferred = CompletableDeferred<CameraCaptureResult>()
+ val tagKey = "TestTagBundleKey"
+ val tagValue = "testing"
+ val captureConfig = CaptureConfig.Builder()
+ .apply {
+ templateType = CameraDevice.TEMPLATE_PREVIEW
+ addTag(tagKey, tagValue)
+ addSurface(testDeferrableSurface)
+ addCameraCaptureCallback(object : CameraCaptureCallback() {
+ override fun onCaptureCompleted(cameraCaptureResult: CameraCaptureResult) {
+ deferred.complete(cameraCaptureResult)
+ }
+
+ override fun onCaptureFailed(failure: CameraCaptureFailure) {
+ deferred.completeExceptionally(Throwable(failure.reason.toString()))
+ }
+
+ override fun onCaptureCancelled() {
+ deferred.cancel()
+ }
+ })
+ }.build()
+
+ // Act
+ cameraControl!!.submitCaptureRequests(listOf(captureConfig))
+
+ // Assert
+ Truth.assertThat(
+ withTimeoutOrNull(timeMillis = 5000) {
+ deferred.await()
+ }!!.tagBundle.getTag(tagKey)
+ ).isEqualTo(tagValue)
+ }
+}
+
+private class FakeTestUseCase(
+ config: FakeUseCaseConfig,
+) : FakeUseCase(config) {
+
+ fun setupSessionConfig(sessionConfigBuilder: SessionConfig.Builder) {
+ updateSessionConfig(sessionConfigBuilder.build())
+ notifyActive()
+ }
+}
+
+private class TestDeferrableSurface : DeferrableSurface() {
+ init {
+ terminationFuture.addListener(
+ { cleanUp() },
+ Dispatchers.IO.asExecutor()
+ )
+ }
+
+ private val surfaceTexture = SurfaceTexture(0).also {
+ it.setDefaultBufferSize(640, 480)
+ }
+ val testSurface = Surface(surfaceTexture)
+
+ override fun provideSurface(): ListenableFuture<Surface> {
+ return Futures.immediateFuture(testSurface)
+ }
+
+ fun cleanUp() {
+ testSurface.release()
+ surfaceTexture.release()
+ }
+}
\ No newline at end of file
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CaptureConfigAdapter.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CaptureConfigAdapter.kt
index 4da13dc..1fea658 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CaptureConfigAdapter.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CaptureConfigAdapter.kt
@@ -20,6 +20,7 @@
import androidx.camera.camera2.pipe.Request
import androidx.camera.camera2.pipe.RequestTemplate
import androidx.camera.camera2.pipe.StreamId
+import androidx.camera.camera2.pipe.integration.impl.CAMERAX_TAG_BUNDLE
import androidx.camera.camera2.pipe.integration.impl.CameraCallbackMap
import androidx.camera.core.impl.CaptureConfig
import androidx.camera.core.impl.DeferrableSurface
@@ -74,12 +75,11 @@
configOptions.retrieveOption(CaptureConfig.OPTION_JPEG_QUALITY)!!.toByte()
}
- // TODO: When adding support for extensions, also add support for passing capture request
- // tags with each request, since extensions may rely on these tags.
return Request(
streams = listOf(streamId),
listeners = listOf(callbacks),
parameters = parameters,
+ extras = mapOf(CAMERAX_TAG_BUNDLE to captureConfig.tagBundle),
template = RequestTemplate(captureConfig.templateType)
)
}
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/CaptureConfigAdapterTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/CaptureConfigAdapterTest.kt
index 7e82479..69fcd62 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/CaptureConfigAdapterTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/CaptureConfigAdapterTest.kt
@@ -20,9 +20,11 @@
import android.os.Build
import android.view.Surface
import androidx.camera.camera2.pipe.StreamId
+import androidx.camera.camera2.pipe.integration.impl.CAMERAX_TAG_BUNDLE
import androidx.camera.core.impl.CameraCaptureCallback
import androidx.camera.core.impl.CaptureConfig
import androidx.camera.core.impl.DeferrableSurface
+import androidx.camera.core.impl.TagBundle
import androidx.camera.core.impl.utils.futures.Futures
import androidx.testutils.assertThrows
import com.google.common.truth.Truth.assertThat
@@ -129,6 +131,31 @@
val rotation = request.parameters[CaptureRequest.JPEG_ORIENTATION]
assertThat(rotation).isEqualTo(90)
}
+
+ @Test
+ fun shouldSetTagBundleToTheRequest() {
+ // Arrange
+ val surface = FakeSurface()
+ val configAdapter = CaptureConfigAdapter(
+ surfaceToStreamMap = mapOf(surface to StreamId(0)),
+ callbackExecutor = Executors.newSingleThreadExecutor()
+ )
+
+ val tagKey = "testTagKey"
+ val tagValue = "testTagValue"
+ val captureConfig = CaptureConfig.Builder().apply {
+ addSurface(surface)
+ addTag(tagKey, tagValue)
+ }.build()
+
+ // Act
+ val request = configAdapter.mapToRequest(captureConfig)
+
+ // Assert
+ assertThat(request.extras).containsKey(CAMERAX_TAG_BUNDLE)
+ val tagBundle = request.extras[CAMERAX_TAG_BUNDLE] as TagBundle
+ assertThat(tagBundle.getTag(tagKey)).isEqualTo(tagValue)
+ }
}
private class FakeSurface : DeferrableSurface() {