1 /*
2  * Copyright 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.hardware.camera2.cts;
18 
19 import android.graphics.ImageFormat;
20 import android.hardware.camera2.cts.CameraTestUtils;
21 import android.hardware.camera2.cts.helpers.StaticMetadata;
22 import android.hardware.camera2.cts.testcases.Camera2AndroidTestCase;
23 import android.hardware.camera2.params.OutputConfiguration;
24 import android.hardware.camera2.CameraDevice;
25 import android.hardware.camera2.CaptureRequest;
26 import android.hardware.camera2.CaptureResult;
27 import android.media.Image;
28 import android.media.ImageReader;
29 import android.os.Build;
30 import android.os.ConditionVariable;
31 import android.util.Log;
32 import android.util.Size;
33 
34 import java.util.ArrayList;
35 import java.util.HashSet;
36 import java.util.List;
37 import java.util.Set;
38 
39 import com.android.compatibility.common.util.PropertyUtil;
40 
41 import org.junit.runner.RunWith;
42 import org.junit.runners.Parameterized;
43 import org.junit.Test;
44 
45 import static android.hardware.camera2.cts.CameraTestUtils.CAPTURE_RESULT_TIMEOUT_MS;
46 import static android.hardware.camera2.cts.CameraTestUtils.SimpleCaptureCallback;
47 import static junit.framework.Assert.*;
48 
49 /**
50  * <p>Basic test for image capture using CONTROL_ZOOM_RATIO. It uses CameraDevice as
51  * producer, and camera sends image data to an imageReader. Image formats
52  * being tested are JPEG and RAW.</p>
53  */
54 @RunWith(Parameterized.class)
55 public class ZoomCaptureTest extends Camera2AndroidTestCase {
56     private static final String TAG = "ZoomCaptureTest";
57     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
58 
59     private SimpleImageListener mListener;
60 
61     @Override
setUp()62     public void setUp() throws Exception {
63         super.setUp();
64     }
65 
66     @Override
tearDown()67     public void tearDown() throws Exception {
68         super.tearDown();
69     }
70 
71     @Test
testJpegZoomCapture()72     public void testJpegZoomCapture() throws Exception {
73         for (String id : mCameraIdsUnderTest) {
74             try {
75                 Log.v(TAG, "Testing jpeg zoom capture for Camera " + id);
76                 openDevice(id);
77                 bufferFormatZoomTestByCamera(ImageFormat.JPEG);
78             } finally {
79                 closeDevice(id);
80             }
81         }
82     }
83 
84     @Test
testRawZoomCapture()85     public void testRawZoomCapture() throws Exception {
86         for (String id : mCameraIdsUnderTest) {
87             try {
88                 Log.v(TAG, "Testing raw zoom capture for camera " + id);
89                 openDevice(id);
90 
91                 bufferFormatZoomTestByCamera(ImageFormat.RAW_SENSOR);
92             } finally {
93                 closeDevice(id);
94             }
95         }
96     }
97 
bufferFormatZoomTestByCamera(int format)98     private void bufferFormatZoomTestByCamera(int format) throws Exception {
99         Size[] availableSizes = mStaticInfo.getAvailableSizesForFormatChecked(format,
100                 StaticMetadata.StreamDirection.Output);
101         if (availableSizes.length == 0) {
102             return;
103         }
104 
105         List<Float> candidateZoomRatios = CameraTestUtils.getCandidateZoomRatios(mStaticInfo);
106         Set<String> physicalCameraIds = null;
107         if (mStaticInfo.isLogicalMultiCamera()) {
108             physicalCameraIds = mStaticInfo.getCharacteristics().getPhysicalCameraIds();
109         }
110         try {
111             mListener  = new SimpleImageListener();
112             // Pick the largest image size:
113             Size maxSize = CameraTestUtils.getMaxSize(availableSizes);
114             createDefaultImageReader(maxSize, format, 1, mListener);
115 
116             checkImageReaderSessionConfiguration(
117                     "Camera capture session validation for format: " + format + "failed");
118 
119             ArrayList<OutputConfiguration> outputConfigs = new ArrayList<>();
120             OutputConfiguration config = new OutputConfiguration(mReader.getSurface());
121             outputConfigs.add(config);
122 
123             CaptureRequest.Builder requestBuilder = prepareCaptureRequestForConfigs(
124                     outputConfigs, CameraDevice.TEMPLATE_PREVIEW);
125 
126             Set<String> activePhysicalIdsSeen = new HashSet<String>();
127             boolean checkActivePhysicalIdConsistency =
128                     PropertyUtil.getFirstApiLevel() >= Build.VERSION_CODES.S;
129             for (Float zoomRatio : candidateZoomRatios) {
130                 if (VERBOSE) {
131                     Log.v(TAG, "Testing format " + format + " zoomRatio " + zoomRatio +
132                             " for camera " + mCamera.getId());
133                 }
134 
135                 requestBuilder.set(CaptureRequest.CONTROL_ZOOM_RATIO, zoomRatio);
136                 CaptureRequest request = requestBuilder.build();
137 
138                 SimpleCaptureCallback listener = new SimpleCaptureCallback();
139                 startCapture(request, false /*repeating*/, listener, mHandler);
140 
141                 // Validate images.
142                 mListener.waitForAnyImageAvailable(CAPTURE_WAIT_TIMEOUT_MS);
143                 Image img = mReader.acquireNextImage();
144                 assertNotNull("Unable to acquire the latest image", img);
145                 CameraTestUtils.validateImage(img, maxSize.getWidth(), maxSize.getHeight(), format,
146                         mDebugFileNameBase);
147                 img.close();
148 
149                 // Validate capture result.
150                 if (mStaticInfo.isActivePhysicalCameraIdSupported()) {
151                     CaptureResult result = listener.getCaptureResult(CAPTURE_RESULT_TIMEOUT_MS);
152                     String activePhysicalId = result.get(
153                             CaptureResult.LOGICAL_MULTI_CAMERA_ACTIVE_PHYSICAL_ID);
154                     if (checkActivePhysicalIdConsistency) {
155                         assertNotNull("Camera " + mCamera.getId() +
156                                 " result metadata must contain ACTIVE_PHYSICAL_ID",
157                                 activePhysicalId);
158                         assertTrue("Camera " + mCamera.getId() + " must be logical " +
159                                 "camera if activePhysicalId exists in capture result",
160                                 physicalCameraIds != null && physicalCameraIds.size() != 0);
161                         mCollector.expectTrue("Camera " + mCamera.getId() + "  activePhysicalId " +
162                                 activePhysicalId + "must be among valid physical Ids "  +
163                                 physicalCameraIds.toString(),
164                                 physicalCameraIds.contains(activePhysicalId));
165 
166                         activePhysicalIdsSeen.add(activePhysicalId);
167                     }
168                 }
169             }
170             // stop capture.
171             stopCapture(/*fast*/false);
172 
173             if (activePhysicalIdsSeen.size() > 0 && format == ImageFormat.RAW_SENSOR) {
174                 mCollector.expectTrue("Logical camera's activePhysicalCamera should not " +
175                         " change at different zoom levels.", activePhysicalIdsSeen.size() == 1);
176             }
177         } finally {
178             closeDefaultImageReader();
179         }
180     }
181 
182     private final class SimpleImageListener implements ImageReader.OnImageAvailableListener {
183         private final ConditionVariable imageAvailable = new ConditionVariable();
184         @Override
onImageAvailable(ImageReader reader)185         public void onImageAvailable(ImageReader reader) {
186             imageAvailable.open();
187         }
188 
waitForAnyImageAvailable(long timeout)189         public void waitForAnyImageAvailable(long timeout) {
190             if (imageAvailable.block(timeout)) {
191                 imageAvailable.close();
192             } else {
193                 fail("wait for image available timed out after " + timeout + "ms");
194             }
195         }
196     }
197 }
198