[go: nahoru, domu]

Merge "Add more tests for VideoCapture.setTargetRotation" into androidx-main
diff --git a/camera/camera-video/src/androidTest/java/androidx/camera/video/VideoRecordingTest.kt b/camera/camera-video/src/androidTest/java/androidx/camera/video/VideoRecordingTest.kt
index f4f9d38..a7a8ed9 100644
--- a/camera/camera-video/src/androidTest/java/androidx/camera/video/VideoRecordingTest.kt
+++ b/camera/camera-video/src/androidTest/java/androidx/camera/video/VideoRecordingTest.kt
@@ -219,9 +219,11 @@
     @Test
     fun getMetadataRotation_when_setTargetRotation() {
         // Arrange.
-        // Just set one Surface.ROTATION_90 to verify the function work or not.
-        val targetRotation = Surface.ROTATION_90
-        videoCapture.targetRotation = targetRotation
+        // Set Surface.ROTATION_90 for the 1st recording and update to Surface.ROTATION_180
+        // for the 2nd recording.
+        val targetRotation1 = Surface.ROTATION_90
+        val targetRotation2 = Surface.ROTATION_180
+        videoCapture.targetRotation = targetRotation1
 
         val file = File.createTempFile("CameraX", ".tmp").apply { deleteOnExit() }
         latchForVideoSaved = CountDownLatch(1)
@@ -235,10 +237,32 @@
         completeVideoRecording(videoCapture, file)
 
         // Verify.
-        verifyMetadataRotation(getExpectedRotation(videoCapture).metadataRotation, file)
+        val (videoContentRotation, metadataRotation) = getExpectedRotation(videoCapture)
+        verifyMetadataRotation(metadataRotation, file)
 
         // Cleanup.
         file.delete()
+
+        // Arrange: Prepare for 2nd recording
+        val file2 = File.createTempFile("CameraX", ".tmp").apply { deleteOnExit() }
+        latchForVideoSaved = CountDownLatch(1)
+        latchForVideoRecording = CountDownLatch(5)
+
+        // Act: Update targetRotation.
+        videoCapture.targetRotation = targetRotation2
+        completeVideoRecording(videoCapture, file2)
+
+        // Verify.
+        val metadataRotation2 = cameraInfo.getSensorRotationDegrees(targetRotation2).let {
+            if (videoCapture.node != null) {
+                // If effect is enabled, the rotation should eliminate the video content rotation.
+                it - videoContentRotation
+            } else it
+        }
+        verifyMetadataRotation(metadataRotation2, file2)
+
+        // Cleanup.
+        file2.delete()
     }
 
     @Test
diff --git a/camera/camera-video/src/test/java/androidx/camera/video/VideoCaptureTest.kt b/camera/camera-video/src/test/java/androidx/camera/video/VideoCaptureTest.kt
index d0d5d70..c640507 100644
--- a/camera/camera-video/src/test/java/androidx/camera/video/VideoCaptureTest.kt
+++ b/camera/camera-video/src/test/java/androidx/camera/video/VideoCaptureTest.kt
@@ -36,8 +36,10 @@
 import androidx.camera.core.AspectRatio.RATIO_4_3
 import androidx.camera.core.CameraEffect
 import androidx.camera.core.CameraEffect.VIDEO_CAPTURE
-import androidx.camera.core.CameraSelector
+import androidx.camera.core.CameraSelector.DEFAULT_BACK_CAMERA
+import androidx.camera.core.CameraSelector.DEFAULT_FRONT_CAMERA
 import androidx.camera.core.CameraSelector.LENS_FACING_BACK
+import androidx.camera.core.CameraSelector.LENS_FACING_FRONT
 import androidx.camera.core.CameraXConfig
 import androidx.camera.core.SurfaceRequest
 import androidx.camera.core.UseCase
@@ -50,12 +52,16 @@
 import androidx.camera.core.impl.Observable
 import androidx.camera.core.impl.StreamSpec
 import androidx.camera.core.impl.Timebase
+import androidx.camera.core.impl.utils.CameraOrientationUtil.surfaceRotationToDegrees
 import androidx.camera.core.impl.utils.CompareSizesByArea
 import androidx.camera.core.impl.utils.TransformUtils.rectToSize
 import androidx.camera.core.impl.utils.TransformUtils.rotateSize
+import androidx.camera.core.impl.utils.TransformUtils.within360
 import androidx.camera.core.impl.utils.executor.CameraXExecutors.directExecutor
 import androidx.camera.core.impl.utils.executor.CameraXExecutors.mainThreadExecutor
 import androidx.camera.core.internal.CameraUseCaseAdapter
+import androidx.camera.testing.CameraUtil
+import androidx.camera.testing.CameraXUtil
 import androidx.camera.testing.EncoderProfilesUtil.PROFILES_1080P
 import androidx.camera.testing.EncoderProfilesUtil.PROFILES_2160P
 import androidx.camera.testing.EncoderProfilesUtil.PROFILES_480P
@@ -67,8 +73,6 @@
 import androidx.camera.testing.EncoderProfilesUtil.RESOLUTION_QHD
 import androidx.camera.testing.EncoderProfilesUtil.RESOLUTION_QVGA
 import androidx.camera.testing.EncoderProfilesUtil.RESOLUTION_VGA
-import androidx.camera.testing.CameraUtil
-import androidx.camera.testing.CameraXUtil
 import androidx.camera.testing.fakes.FakeAppConfig
 import androidx.camera.testing.fakes.FakeCamera
 import androidx.camera.testing.fakes.FakeCameraDeviceSurfaceManager
@@ -90,6 +94,7 @@
 import androidx.camera.video.internal.encoder.VideoEncoderInfo
 import androidx.test.core.app.ApplicationProvider
 import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
 import java.util.Collections
 import java.util.concurrent.TimeUnit
 import org.junit.After
@@ -707,6 +712,15 @@
     }
 
     @Test
+    fun setTargetRotationInBuilder_rotationIsChanged() {
+        // Act.
+        val videoCapture = createVideoCapture(targetRotation = Surface.ROTATION_180)
+
+        // Assert.
+        assertThat(videoCapture.targetRotation).isEqualTo(Surface.ROTATION_180)
+    }
+
+    @Test
     fun setTargetRotation_rotationIsChanged() {
         // Arrange.
         val videoCapture = createVideoCapture()
@@ -789,11 +803,92 @@
         verify(listener).onTransformationInfoUpdate(any())
     }
 
+    // Test setTargetRotation with common back and front camera properties and various conditions.
     @Test
-    fun setTargetRotation_transformationInfoUpdated() {
+    fun setTargetRotation_backCameraInitial0_transformationInfoUpdated() {
+        testSetTargetRotation_transformationInfoUpdated(
+            lensFacing = LENS_FACING_BACK,
+            sensorRotationDegrees = 90,
+            initialTargetRotation = Surface.ROTATION_0,
+        )
+    }
+
+    @Test
+    fun setTargetRotation_backCameraInitial90_transformationInfoUpdated() {
+        testSetTargetRotation_transformationInfoUpdated(
+            lensFacing = LENS_FACING_BACK,
+            sensorRotationDegrees = 90,
+            initialTargetRotation = Surface.ROTATION_90,
+        )
+    }
+
+    @Test
+    fun setTargetRotation_frontCameraInitial0_transformationInfoUpdated() {
+        testSetTargetRotation_transformationInfoUpdated(
+            lensFacing = LENS_FACING_FRONT,
+            sensorRotationDegrees = 270,
+            initialTargetRotation = Surface.ROTATION_0,
+        )
+    }
+
+    @Test
+    fun setTargetRotation_frontCameraInitial90_transformationInfoUpdated() {
+        testSetTargetRotation_transformationInfoUpdated(
+            lensFacing = LENS_FACING_FRONT,
+            sensorRotationDegrees = 270,
+            initialTargetRotation = Surface.ROTATION_90,
+        )
+    }
+
+    @Test
+    fun setTargetRotation_withEffectBackCameraInitial0_transformationInfoUpdated() {
+        testSetTargetRotation_transformationInfoUpdated(
+            lensFacing = LENS_FACING_BACK,
+            sensorRotationDegrees = 90,
+            effect = createFakeEffect(),
+            initialTargetRotation = Surface.ROTATION_0,
+        )
+    }
+
+    @Test
+    fun setTargetRotation_withEffectBackCameraInitial90_transformationInfoUpdated() {
+        testSetTargetRotation_transformationInfoUpdated(
+            lensFacing = LENS_FACING_BACK,
+            sensorRotationDegrees = 90,
+            effect = createFakeEffect(),
+            initialTargetRotation = Surface.ROTATION_90,
+        )
+    }
+
+    @Test
+    fun setTargetRotation_withEffectFrontCameraInitial0_transformationInfoUpdated() {
+        testSetTargetRotation_transformationInfoUpdated(
+            lensFacing = LENS_FACING_FRONT,
+            sensorRotationDegrees = 270,
+            effect = createFakeEffect(),
+            initialTargetRotation = Surface.ROTATION_90,
+        )
+    }
+
+    @Test
+    fun setTargetRotation_withEffectFrontCameraInitial90_transformationInfoUpdated() {
+        testSetTargetRotation_transformationInfoUpdated(
+            lensFacing = LENS_FACING_FRONT,
+            sensorRotationDegrees = 270,
+            effect = createFakeEffect(),
+            initialTargetRotation = Surface.ROTATION_90,
+        )
+    }
+
+    private fun testSetTargetRotation_transformationInfoUpdated(
+        lensFacing: Int = LENS_FACING_BACK,
+        sensorRotationDegrees: Int = 0,
+        effect: CameraEffect? = null,
+        initialTargetRotation: Int = Surface.ROTATION_0,
+    ) {
         // Arrange.
-        setupCamera()
-        createCameraUseCaseAdapter()
+        setupCamera(lensFacing = lensFacing, sensorRotation = sensorRotationDegrees)
+        createCameraUseCaseAdapter(lensFacing = lensFacing)
         var transformationInfo: SurfaceRequest.TransformationInfo? = null
         val videoOutput = createVideoOutput(
             surfaceRequestListener = { surfaceRequest, _ ->
@@ -804,19 +899,59 @@
                 }
             }
         )
-        val videoCapture = createVideoCapture(videoOutput, targetRotation = Surface.ROTATION_90)
+        val videoCapture = createVideoCapture(videoOutput, targetRotation = initialTargetRotation)
 
         // Act.
+        effect?.apply { cameraUseCaseAdapter.setEffects(listOf(this)) }
         addAndAttachUseCases(videoCapture)
+        shadowOf(Looper.getMainLooper()).idle()
 
         // Assert.
-        assertThat(transformationInfo!!.rotationDegrees).isEqualTo(270)
+        var videoContentDegrees: Int
+        var metadataDegrees: Int
+        cameraInfo.getSensorRotationDegrees(initialTargetRotation).let {
+            if (effect != null) {
+                // If effect is enabled, the rotation is applied on video content but not metadata.
+                videoContentDegrees = it
+                metadataDegrees = 0
+            } else {
+                videoContentDegrees = 0
+                metadataDegrees = it
+            }
+        }
+        assertThat(transformationInfo!!.rotationDegrees).isEqualTo(metadataDegrees)
 
-        // Act.
-        videoCapture.targetRotation = Surface.ROTATION_180
+        // Act: Test all 4 rotation degrees.
+        for (targetRotation in listOf(
+            Surface.ROTATION_0,
+            Surface.ROTATION_90,
+            Surface.ROTATION_180,
+            Surface.ROTATION_270
+        )) {
+            videoCapture.targetRotation = targetRotation
+            shadowOf(Looper.getMainLooper()).idle()
 
-        // Assert.
-        assertThat(transformationInfo!!.rotationDegrees).isEqualTo(180)
+            // Assert.
+            val requiredDegrees = cameraInfo.getSensorRotationDegrees(targetRotation)
+            val expectedDegrees = if (effect != null) {
+                // If effect is enabled, the rotation should eliminate the video content rotation.
+                within360(requiredDegrees - videoContentDegrees)
+            } else {
+                requiredDegrees
+            }
+            val message = "lensFacing = $lensFacing" +
+                ", sensorRotationDegrees = $sensorRotationDegrees" +
+                ", initialTargetRotation = $initialTargetRotation" +
+                ", targetRotation = ${surfaceRotationToDegrees(targetRotation)}" +
+                ", effect = ${effect != null}" +
+                ", videoContentDegrees = $videoContentDegrees" +
+                ", metadataDegrees = $metadataDegrees" +
+                ", requiredDegrees = $requiredDegrees" +
+                ", expectedDegrees = $expectedDegrees" +
+                ", transformationInfo.rotationDegrees = " + transformationInfo!!.rotationDegrees
+            assertWithMessage(message).that(transformationInfo!!.rotationDegrees)
+                .isEqualTo(expectedDegrees)
+        }
     }
 
     @Test
@@ -1063,9 +1198,11 @@
         shadowOf(Looper.getMainLooper()).idle()
     }
 
-    private fun createCameraUseCaseAdapter() {
+    private fun createCameraUseCaseAdapter(lensFacing: Int = LENS_FACING_BACK) {
+        val cameraSelector = if (lensFacing == LENS_FACING_FRONT) DEFAULT_FRONT_CAMERA
+        else DEFAULT_BACK_CAMERA
         cameraUseCaseAdapter =
-            CameraUtil.createCameraUseCaseAdapter(context, CameraSelector.DEFAULT_BACK_CAMERA)
+            CameraUtil.createCameraUseCaseAdapter(context, cameraSelector)
     }
 
     private fun createVideoCapture(
@@ -1115,13 +1252,14 @@
 
     private fun setupCamera(
         cameraId: String = CAMERA_ID_0,
+        lensFacing: Int = LENS_FACING_BACK,
         sensorRotation: Int = 0,
         hasTransform: Boolean = true,
         supportedResolutions: Map<Int, List<Size>> = CAMERA_0_SUPPORTED_RESOLUTION_MAP,
         profiles: Map<Int, EncoderProfilesProxy> = CAMERA_0_PROFILES,
         timebase: Timebase = Timebase.UPTIME,
     ) {
-        cameraInfo = FakeCameraInfoInternal(cameraId, sensorRotation, LENS_FACING_BACK).apply {
+        cameraInfo = FakeCameraInfoInternal(cameraId, sensorRotation, lensFacing).apply {
             supportedResolutions.forEach { (format, resolutions) ->
                 setSupportedResolutions(format, resolutions)
             }