1 /* 2 * Copyright 2013 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 static android.hardware.camera2.cts.CameraTestUtils.*; 20 21 import android.content.Context; 22 import android.graphics.ImageFormat; 23 import android.hardware.camera2.CameraDevice; 24 import android.hardware.camera2.CameraCharacteristics; 25 import android.hardware.camera2.CaptureRequest; 26 import android.hardware.camera2.CaptureResult; 27 import android.util.Size; 28 import android.hardware.camera2.cts.helpers.StaticMetadata; 29 import android.hardware.camera2.cts.testcases.Camera2AndroidTestCase; 30 import android.media.Image; 31 import android.media.ImageReader; 32 import android.os.ConditionVariable; 33 import android.util.Log; 34 import android.view.Surface; 35 36 import java.util.ArrayList; 37 import java.util.List; 38 39 /** 40 * <p>Basic test for ImageReader APIs. It uses CameraDevice as producer, camera 41 * sends the data to the surface provided by imageReader. Below image formats 42 * are tested:</p> 43 * 44 * <p>YUV_420_888: flexible YUV420, it is mandatory format for camera. </p> 45 * <p>JPEG: used for JPEG still capture, also mandatory format. </p> 46 * <p>Some invalid access test. </p> 47 * <p>TODO: Add more format tests? </p> 48 */ 49 public class ImageReaderTest extends Camera2AndroidTestCase { 50 private static final String TAG = "ImageReaderTest"; 51 private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE); 52 // number of frame (for streaming requests) to be verified. 53 private static final int NUM_FRAME_VERIFIED = 2; 54 // Max number of images can be accessed simultaneously from ImageReader. 55 private static final int MAX_NUM_IMAGES = 5; 56 57 private SimpleImageListener mListener; 58 59 @Override 60 public void setContext(Context context) { 61 super.setContext(context); 62 } 63 64 @Override 65 protected void setUp() throws Exception { 66 super.setUp(); 67 } 68 69 @Override 70 protected void tearDown() throws Exception { 71 super.tearDown(); 72 } 73 74 public void testFlexibleYuv() throws Exception { 75 for (String id : mCameraIds) { 76 try { 77 Log.i(TAG, "Testing Camera " + id); 78 openDevice(id); 79 bufferFormatTestByCamera(ImageFormat.YUV_420_888, /*repeating*/true); 80 } finally { 81 closeDevice(id); 82 } 83 } 84 } 85 86 public void testJpeg() throws Exception { 87 for (String id : mCameraIds) { 88 try { 89 Log.v(TAG, "Testing jpeg capture for Camera " + id); 90 openDevice(id); 91 if (mStaticInfo.isHardwareLevelLegacy()) { 92 Log.i(TAG, "Skipping test on legacy devices"); 93 continue; 94 } 95 bufferFormatTestByCamera(ImageFormat.JPEG, /*repeating*/false); 96 } finally { 97 closeDevice(id); 98 } 99 } 100 } 101 102 public void testRaw() throws Exception { 103 for (String id : mCameraIds) { 104 try { 105 Log.v(TAG, "Testing raw capture for camera " + id); 106 openDevice(id); 107 108 bufferFormatTestByCamera(ImageFormat.RAW_SENSOR, /*repeating*/false); 109 } finally { 110 closeDevice(id); 111 } 112 } 113 } 114 115 public void testRepeatingJpeg() throws Exception { 116 for (String id : mCameraIds) { 117 try { 118 Log.v(TAG, "Testing repeating jpeg capture for Camera " + id); 119 openDevice(id); 120 if (mStaticInfo.isHardwareLevelLegacy()) { 121 Log.i(TAG, "Skipping test on legacy devices"); 122 continue; 123 } 124 bufferFormatTestByCamera(ImageFormat.JPEG, /*repeating*/true); 125 } finally { 126 closeDevice(id); 127 } 128 } 129 } 130 131 public void testRepeatingRaw() throws Exception { 132 for (String id : mCameraIds) { 133 try { 134 Log.v(TAG, "Testing repeating raw capture for camera " + id); 135 openDevice(id); 136 137 bufferFormatTestByCamera(ImageFormat.RAW_SENSOR, /*repeating*/true); 138 } finally { 139 closeDevice(id); 140 } 141 } 142 } 143 144 public void testInvalidAccessTest() { 145 // TODO: test invalid access case, see if we can receive expected 146 // exceptions 147 } 148 149 /** 150 * Test two image stream (YUV420_888 and JPEG) capture by using ImageReader. 151 * 152 * <p>Both stream formats are mandatory for Camera2 API</p> 153 */ 154 public void testYuvAndJpeg() throws Exception { 155 for (String id : mCameraIds) { 156 try { 157 Log.v(TAG, "YUV and JPEG testing for camera " + id); 158 openDevice(id); 159 if (mStaticInfo.isHardwareLevelLegacy()) { 160 Log.i(TAG, "Skipping test on legacy devices"); 161 continue; 162 } 163 bufferFormatWithYuvTestByCamera(ImageFormat.JPEG); 164 } finally { 165 closeDevice(id); 166 } 167 } 168 } 169 170 /** 171 * Test two image stream (YUV420_888 and RAW_SENSOR) capture by using ImageReader. 172 * 173 */ 174 public void testImageReaderYuvAndRaw() throws Exception { 175 for (String id : mCameraIds) { 176 try { 177 Log.v(TAG, "YUV and RAW testing for camera " + id); 178 openDevice(id); 179 180 bufferFormatWithYuvTestByCamera(ImageFormat.RAW_SENSOR); 181 } finally { 182 closeDevice(id); 183 } 184 } 185 } 186 187 188 /** 189 * Test capture a given format stream with yuv stream simultaneously. 190 * 191 * <p>Use fixed yuv size, varies targeted format capture size. Single capture is tested.</p> 192 * 193 * @param format The capture format to be tested along with yuv format. 194 */ 195 private void bufferFormatWithYuvTestByCamera(int format) throws Exception { 196 if (format != ImageFormat.JPEG && format != ImageFormat.RAW_SENSOR 197 && format != ImageFormat.YUV_420_888) { 198 throw new IllegalArgumentException("Unsupported format: " + format); 199 } 200 201 final int NUM_SINGLE_CAPTURE_TESTED = MAX_NUM_IMAGES - 1; 202 Size maxYuvSz = mOrderedPreviewSizes.get(0); 203 Size[] targetCaptureSizes = mStaticInfo.getAvailableSizesForFormatChecked(format, 204 StaticMetadata.StreamDirection.Output); 205 206 for (Size captureSz : targetCaptureSizes) { 207 if (VERBOSE) { 208 Log.v(TAG, "Testing yuv size " + maxYuvSz.toString() + " and capture size " 209 + captureSz.toString() + " for camera " + mCamera.getId()); 210 } 211 212 ImageReader captureReader = null; 213 ImageReader yuvReader = null; 214 try { 215 // Create YUV image reader 216 SimpleImageReaderListener yuvListener = new SimpleImageReaderListener(); 217 yuvReader = createImageReader(maxYuvSz, ImageFormat.YUV_420_888, MAX_NUM_IMAGES, 218 yuvListener); 219 Surface yuvSurface = yuvReader.getSurface(); 220 221 // Create capture image reader 222 SimpleImageReaderListener captureListener = new SimpleImageReaderListener(); 223 captureReader = createImageReader(captureSz, format, MAX_NUM_IMAGES, 224 captureListener); 225 Surface captureSurface = captureReader.getSurface(); 226 227 // Capture images. 228 List<Surface> outputSurfaces = new ArrayList<Surface>(); 229 outputSurfaces.add(yuvSurface); 230 outputSurfaces.add(captureSurface); 231 CaptureRequest.Builder request = prepareCaptureRequestForSurfaces(outputSurfaces); 232 SimpleCaptureCallback resultListener = new SimpleCaptureCallback(); 233 234 for (int i = 0; i < NUM_SINGLE_CAPTURE_TESTED; i++) { 235 startCapture(request.build(), /*repeating*/false, resultListener, mHandler); 236 } 237 238 // Verify capture result and images 239 for (int i = 0; i < NUM_SINGLE_CAPTURE_TESTED; i++) { 240 resultListener.getCaptureResult(CAPTURE_WAIT_TIMEOUT_MS); 241 if (VERBOSE) { 242 Log.v(TAG, " Got the capture result back for " + i + "th capture"); 243 } 244 245 Image yuvImage = yuvListener.getImage(CAPTURE_WAIT_TIMEOUT_MS); 246 if (VERBOSE) { 247 Log.v(TAG, " Got the yuv image back for " + i + "th capture"); 248 } 249 250 Image captureImage = captureListener.getImage(CAPTURE_WAIT_TIMEOUT_MS); 251 if (VERBOSE) { 252 Log.v(TAG, " Got the capture image back for " + i + "th capture"); 253 } 254 255 //Validate captured images. 256 CameraTestUtils.validateImage(yuvImage, maxYuvSz.getWidth(), 257 maxYuvSz.getHeight(), ImageFormat.YUV_420_888, /*filePath*/null); 258 CameraTestUtils.validateImage(captureImage, captureSz.getWidth(), 259 captureSz.getHeight(), format, /*filePath*/null); 260 } 261 262 // Stop capture, delete the streams. 263 stopCapture(/*fast*/false); 264 } finally { 265 closeImageReader(captureReader); 266 captureReader = null; 267 closeImageReader(yuvReader); 268 yuvReader = null; 269 } 270 } 271 } 272 273 private void bufferFormatTestByCamera(int format, boolean repeating) throws Exception { 274 275 Size[] availableSizes = mStaticInfo.getAvailableSizesForFormatChecked(format, 276 StaticMetadata.StreamDirection.Output); 277 278 // for each resolution, test imageReader: 279 for (Size sz : availableSizes) { 280 try { 281 if (VERBOSE) { 282 Log.v(TAG, "Testing size " + sz.toString() + " format " + format 283 + " for camera " + mCamera.getId()); 284 } 285 286 // Create ImageReader. 287 mListener = new SimpleImageListener(); 288 createDefaultImageReader(sz, format, MAX_NUM_IMAGES, mListener); 289 290 // Start capture. 291 CaptureRequest request = prepareCaptureRequest(); 292 SimpleCaptureCallback listener = new SimpleCaptureCallback(); 293 startCapture(request, repeating, listener, mHandler); 294 295 int numFrameVerified = repeating ? NUM_FRAME_VERIFIED : 1; 296 297 // Validate images. 298 validateImage(sz, format, numFrameVerified, repeating); 299 300 // Validate capture result. 301 validateCaptureResult(format, sz, listener, numFrameVerified); 302 303 // stop capture. 304 stopCapture(/*fast*/false); 305 } finally { 306 closeDefaultImageReader(); 307 } 308 309 } 310 } 311 312 /** 313 * Validate capture results. 314 * 315 * @param format The format of this capture. 316 * @param size The capture size. 317 * @param listener The capture listener to get capture result callbacks. 318 */ 319 private void validateCaptureResult(int format, Size size, SimpleCaptureCallback listener, 320 int numFrameVerified) { 321 for (int i = 0; i < numFrameVerified; i++) { 322 CaptureResult result = listener.getCaptureResult(CAPTURE_RESULT_TIMEOUT_MS); 323 324 // TODO: Update this to use availableResultKeys once shim supports this. 325 if (mStaticInfo.isCapabilitySupported( 326 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) { 327 Long exposureTime = getValueNotNull(result, CaptureResult.SENSOR_EXPOSURE_TIME); 328 Integer sensitivity = getValueNotNull(result, CaptureResult.SENSOR_SENSITIVITY); 329 mCollector.expectInRange( 330 String.format( 331 "Capture for format %d, size %s exposure time is invalid.", 332 format, size.toString()), 333 exposureTime, 334 mStaticInfo.getExposureMinimumOrDefault(), 335 mStaticInfo.getExposureMaximumOrDefault() 336 ); 337 mCollector.expectInRange( 338 String.format("Capture for format %d, size %s sensitivity is invalid.", 339 format, size.toString()), 340 sensitivity, 341 mStaticInfo.getSensitivityMinimumOrDefault(), 342 mStaticInfo.getSensitivityMaximumOrDefault() 343 ); 344 } 345 // TODO: add more key validations. 346 } 347 } 348 349 private final class SimpleImageListener implements ImageReader.OnImageAvailableListener { 350 private final ConditionVariable imageAvailable = new ConditionVariable(); 351 @Override 352 public void onImageAvailable(ImageReader reader) { 353 if (mReader != reader) { 354 return; 355 } 356 357 if (VERBOSE) Log.v(TAG, "new image available"); 358 imageAvailable.open(); 359 } 360 361 public void waitForAnyImageAvailable(long timeout) { 362 if (imageAvailable.block(timeout)) { 363 imageAvailable.close(); 364 } else { 365 fail("wait for image available timed out after " + timeout + "ms"); 366 } 367 } 368 369 public void closePendingImages() { 370 Image image = mReader.acquireLatestImage(); 371 if (image != null) { 372 image.close(); 373 } 374 } 375 } 376 377 private CaptureRequest prepareCaptureRequest() throws Exception { 378 List<Surface> outputSurfaces = new ArrayList<Surface>(); 379 Surface surface = mReader.getSurface(); 380 assertNotNull("Fail to get surface from ImageReader", surface); 381 outputSurfaces.add(surface); 382 return prepareCaptureRequestForSurfaces(outputSurfaces).build(); 383 } 384 385 private CaptureRequest.Builder prepareCaptureRequestForSurfaces(List<Surface> surfaces) 386 throws Exception { 387 createSession(surfaces); 388 389 CaptureRequest.Builder captureBuilder = 390 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); 391 assertNotNull("Fail to get captureRequest", captureBuilder); 392 for (Surface surface : surfaces) { 393 captureBuilder.addTarget(surface); 394 } 395 396 return captureBuilder; 397 } 398 399 private void validateImage(Size sz, int format, int captureCount, boolean repeating) 400 throws Exception { 401 // TODO: Add more format here, and wrap each one as a function. 402 Image img; 403 final int MAX_RETRY_COUNT = 20; 404 int numImageVerified = 0; 405 int reTryCount = 0; 406 while (numImageVerified < captureCount) { 407 assertNotNull("Image listener is null", mListener); 408 if (VERBOSE) Log.v(TAG, "Waiting for an Image"); 409 mListener.waitForAnyImageAvailable(CAPTURE_WAIT_TIMEOUT_MS); 410 if (repeating) { 411 /** 412 * Acquire the latest image in case the validation is slower than 413 * the image producing rate. 414 */ 415 img = mReader.acquireLatestImage(); 416 /** 417 * Sometimes if multiple onImageAvailable callbacks being queued, 418 * acquireLatestImage will clear all buffer before corresponding callback is 419 * executed. Wait for a new frame in that case. 420 */ 421 if (img == null && reTryCount < MAX_RETRY_COUNT) { 422 reTryCount++; 423 continue; 424 } 425 } else { 426 img = mReader.acquireNextImage(); 427 } 428 assertNotNull("Unable to acquire the latest image", img); 429 if (VERBOSE) Log.v(TAG, "Got the latest image"); 430 CameraTestUtils.validateImage(img, sz.getWidth(), sz.getHeight(), format, 431 DEBUG_FILE_NAME_BASE); 432 if (VERBOSE) Log.v(TAG, "finish vaildation of image " + numImageVerified); 433 img.close(); 434 numImageVerified++; 435 reTryCount = 0; 436 } 437 438 // Return all pending images to the ImageReader as the validateImage may 439 // take a while to return and there could be many images pending. 440 mListener.closePendingImages(); 441 } 442 } 443