1 /* 2 * Copyright 2014 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 import static android.hardware.camera2.cts.RobustnessTest.MaxStreamSizes.*; 21 22 import android.content.Context; 23 import android.graphics.ImageFormat; 24 import android.graphics.SurfaceTexture; 25 import android.hardware.camera2.CameraCaptureSession; 26 import android.hardware.camera2.CameraCharacteristics; 27 import android.hardware.camera2.CameraDevice; 28 import android.hardware.camera2.CameraManager; 29 import android.hardware.camera2.CameraMetadata; 30 import android.hardware.camera2.CaptureRequest; 31 import android.hardware.camera2.CaptureResult; 32 import android.hardware.camera2.TotalCaptureResult; 33 import android.hardware.camera2.CaptureFailure; 34 import android.hardware.camera2.cts.helpers.StaticMetadata; 35 import android.hardware.camera2.cts.testcases.Camera2AndroidTestCase; 36 import android.hardware.camera2.params.InputConfiguration; 37 import android.hardware.camera2.params.OisSample; 38 import android.hardware.camera2.params.OutputConfiguration; 39 import android.hardware.camera2.params.MandatoryStreamCombination; 40 import android.hardware.camera2.params.MandatoryStreamCombination.MandatoryStreamInformation; 41 import android.hardware.camera2.params.SessionConfiguration; 42 import android.hardware.camera2.params.StreamConfigurationMap; 43 import android.media.CamcorderProfile; 44 import android.media.Image; 45 import android.media.ImageReader; 46 import android.media.ImageWriter; 47 import android.util.Log; 48 import android.util.Pair; 49 import android.util.Size; 50 import android.view.Display; 51 import android.view.Surface; 52 import android.view.WindowManager; 53 54 import com.android.ex.camera2.blocking.BlockingSessionCallback; 55 56 import java.util.Arrays; 57 import java.util.ArrayList; 58 import java.util.Comparator; 59 import java.util.concurrent.LinkedBlockingQueue; 60 import java.util.HashSet; 61 import java.util.List; 62 import java.util.Map; 63 import java.util.Set; 64 65 import org.junit.runners.Parameterized; 66 import org.junit.runner.RunWith; 67 import org.junit.Test; 68 69 import static junit.framework.Assert.assertTrue; 70 import static org.mockito.Mockito.*; 71 72 /** 73 * Tests exercising edge cases in camera setup, configuration, and usage. 74 */ 75 76 @RunWith(Parameterized.class) 77 public class RobustnessTest extends Camera2AndroidTestCase { 78 private static final String TAG = "RobustnessTest"; 79 private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE); 80 81 private static final int CONFIGURE_TIMEOUT = 5000; //ms 82 private static final int CAPTURE_TIMEOUT = 1500; //ms 83 84 // For testTriggerInteractions 85 private static final int PREVIEW_WARMUP_FRAMES = 60; 86 private static final int MAX_RESULT_STATE_CHANGE_WAIT_FRAMES = 100; 87 private static final int MAX_TRIGGER_SEQUENCE_FRAMES = 180; // 6 sec at 30 fps 88 private static final int MAX_RESULT_STATE_POSTCHANGE_WAIT_FRAMES = 10; 89 90 /** 91 * Test that a {@link CameraCaptureSession} can be configured with a {@link Surface} containing 92 * a dimension other than one of the supported output dimensions. The buffers produced into 93 * this surface are expected have the dimensions of the closest possible buffer size in the 94 * available stream configurations for a surface with this format. 95 */ 96 @Test testBadSurfaceDimensions()97 public void testBadSurfaceDimensions() throws Exception { 98 for (String id : mCameraIdsUnderTest) { 99 try { 100 Log.i(TAG, "Testing Camera " + id); 101 openDevice(id); 102 103 List<Size> testSizes = null; 104 int format = mStaticInfo.isColorOutputSupported() ? 105 ImageFormat.YUV_420_888 : ImageFormat.DEPTH16; 106 107 testSizes = CameraTestUtils.getSortedSizesForFormat(id, mCameraManager, 108 format, null); 109 110 // Find some size not supported by the camera 111 Size weirdSize = new Size(643, 577); 112 int count = 0; 113 while(testSizes.contains(weirdSize)) { 114 // Really, they can't all be supported... 115 weirdSize = new Size(weirdSize.getWidth() + 1, weirdSize.getHeight() + 1); 116 count++; 117 assertTrue("Too many exotic YUV_420_888 resolutions supported.", count < 100); 118 } 119 120 // Setup imageReader with invalid dimension 121 ImageReader imageReader = ImageReader.newInstance(weirdSize.getWidth(), 122 weirdSize.getHeight(), format, 3); 123 124 // Setup ImageReaderListener 125 SimpleImageReaderListener imageListener = new SimpleImageReaderListener(); 126 imageReader.setOnImageAvailableListener(imageListener, mHandler); 127 128 Surface surface = imageReader.getSurface(); 129 List<Surface> surfaces = new ArrayList<>(); 130 surfaces.add(surface); 131 132 // Setup a capture request and listener 133 CaptureRequest.Builder request = 134 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); 135 request.addTarget(surface); 136 137 // Check that correct session callback is hit. 138 CameraCaptureSession.StateCallback sessionListener = 139 mock(CameraCaptureSession.StateCallback.class); 140 CameraCaptureSession session = CameraTestUtils.configureCameraSession(mCamera, 141 surfaces, sessionListener, mHandler); 142 143 verify(sessionListener, timeout(CONFIGURE_TIMEOUT).atLeastOnce()). 144 onConfigured(any(CameraCaptureSession.class)); 145 verify(sessionListener, timeout(CONFIGURE_TIMEOUT).atLeastOnce()). 146 onReady(any(CameraCaptureSession.class)); 147 verify(sessionListener, never()).onConfigureFailed(any(CameraCaptureSession.class)); 148 verify(sessionListener, never()).onActive(any(CameraCaptureSession.class)); 149 verify(sessionListener, never()).onClosed(any(CameraCaptureSession.class)); 150 151 CameraCaptureSession.CaptureCallback captureListener = 152 mock(CameraCaptureSession.CaptureCallback.class); 153 session.capture(request.build(), captureListener, mHandler); 154 155 verify(captureListener, timeout(CAPTURE_TIMEOUT).atLeastOnce()). 156 onCaptureCompleted(any(CameraCaptureSession.class), 157 any(CaptureRequest.class), any(TotalCaptureResult.class)); 158 verify(captureListener, never()).onCaptureFailed(any(CameraCaptureSession.class), 159 any(CaptureRequest.class), any(CaptureFailure.class)); 160 161 Image image = imageListener.getImage(CAPTURE_TIMEOUT); 162 int imageWidth = image.getWidth(); 163 int imageHeight = image.getHeight(); 164 Size actualSize = new Size(imageWidth, imageHeight); 165 166 assertTrue("Camera does not contain outputted image resolution " + actualSize, 167 testSizes.contains(actualSize)); 168 imageReader.close(); 169 } finally { 170 closeDevice(id); 171 } 172 } 173 } 174 175 /** 176 * Test for making sure the mandatory stream combinations work as expected. 177 */ 178 @Test testMandatoryOutputCombinations()179 public void testMandatoryOutputCombinations() throws Exception { 180 testMandatoryOutputCombinations(/*maxResolution*/false); 181 } 182 183 /** 184 * Test for making sure the mandatory stream combinations work as expected. 185 */ testMandatoryOutputCombinations(boolean maxResolution)186 private void testMandatoryOutputCombinations(boolean maxResolution) throws Exception { 187 final int AVAILABILITY_TIMEOUT_MS = 10; 188 final LinkedBlockingQueue<Pair<String, String>> unavailablePhysicalCamEventQueue = 189 new LinkedBlockingQueue<>(); 190 CameraManager.AvailabilityCallback ac = new CameraManager.AvailabilityCallback() { 191 @Override 192 public void onPhysicalCameraUnavailable(String cameraId, String physicalCameraId) { 193 unavailablePhysicalCamEventQueue.offer(new Pair<>(cameraId, physicalCameraId)); 194 } 195 }; 196 197 mCameraManager.registerAvailabilityCallback(ac, mHandler); 198 Set<Pair<String, String>> unavailablePhysicalCameras = new HashSet<Pair<String, String>>(); 199 Pair<String, String> candidatePhysicalIds = 200 unavailablePhysicalCamEventQueue.poll(AVAILABILITY_TIMEOUT_MS, 201 java.util.concurrent.TimeUnit.MILLISECONDS); 202 while (candidatePhysicalIds != null) { 203 unavailablePhysicalCameras.add(candidatePhysicalIds); 204 candidatePhysicalIds = 205 unavailablePhysicalCamEventQueue.poll(AVAILABILITY_TIMEOUT_MS, 206 java.util.concurrent.TimeUnit.MILLISECONDS); 207 } 208 mCameraManager.unregisterAvailabilityCallback(ac); 209 CameraCharacteristics.Key<MandatoryStreamCombination []> ck = 210 CameraCharacteristics.SCALER_MANDATORY_STREAM_COMBINATIONS; 211 212 if (maxResolution) { 213 ck = CameraCharacteristics.SCALER_MANDATORY_MAXIMUM_RESOLUTION_STREAM_COMBINATIONS; 214 } 215 for (String id : mCameraIdsUnderTest) { 216 openDevice(id); 217 MandatoryStreamCombination[] combinations = mStaticInfo.getCharacteristics().get(ck); 218 219 if (combinations == null) { 220 String maxResolutionStr = maxResolution ? " " : " maximum resolution "; 221 Log.i(TAG, "No mandatory" + maxResolutionStr + "stream combinations for camera: " + 222 id + " skip test"); 223 closeDevice(id); 224 continue; 225 } 226 227 try { 228 for (MandatoryStreamCombination combination : combinations) { 229 if (!combination.isReprocessable()) { 230 if (maxResolution) { 231 testMandatoryStreamCombination(id, mStaticInfo, 232 /*physicalCameraId*/ null, combination, /*substituteY8*/false, 233 /*substituteHeic*/false, /*maxResolution*/true); 234 } else { 235 testMandatoryStreamCombination(id, mStaticInfo, 236 null/*physicalCameraId*/, combination); 237 } 238 } 239 } 240 241 // Make sure mandatory stream combinations for each physical camera work 242 // as expected. 243 if (mStaticInfo.isLogicalMultiCamera()) { 244 Set<String> physicalCameraIds = 245 mStaticInfo.getCharacteristics().getPhysicalCameraIds(); 246 boolean skipTest = false; 247 for (String physicalId : physicalCameraIds) { 248 if (Arrays.asList(mCameraIdsUnderTest).contains(physicalId)) { 249 // If physicalId is advertised in camera ID list, do not need to test 250 // its stream combination through logical camera. 251 skipTest = true; 252 } 253 for (Pair<String, String> unavailPhysicalCam : unavailablePhysicalCameras) { 254 if (unavailPhysicalCam.first.equals(id) || 255 unavailPhysicalCam.second.equals(physicalId)) { 256 // This particular physical camera isn't available. Skip. 257 skipTest = true; 258 break; 259 } 260 } 261 if (skipTest) { 262 continue; 263 } 264 StaticMetadata physicalStaticInfo = mAllStaticInfo.get(physicalId); 265 266 MandatoryStreamCombination[] phyCombinations = 267 physicalStaticInfo.getCharacteristics().get(ck); 268 269 if (phyCombinations == null) { 270 Log.i(TAG, "No mandatory stream combinations for physical camera device: " + id + " skip test"); 271 continue; 272 } 273 274 for (MandatoryStreamCombination combination : phyCombinations) { 275 if (!combination.isReprocessable()) { 276 if (maxResolution) { 277 testMandatoryStreamCombination(id, physicalStaticInfo, 278 physicalId, combination, /*substituteY8*/false, 279 /*substituteHeic*/false, /*maxResolution*/true); 280 } else { 281 testMandatoryStreamCombination(id, physicalStaticInfo, 282 physicalId, combination); 283 } 284 } 285 } 286 } 287 } 288 289 } finally { 290 closeDevice(id); 291 } 292 } 293 } 294 295 296 /** 297 * Test for making sure the mandatory stream combinations work as expected. 298 */ 299 @Test testMandatoryMaximumResolutionOutputCombinations()300 public void testMandatoryMaximumResolutionOutputCombinations() throws Exception { 301 testMandatoryOutputCombinations(/*maxResolution*/ true); 302 } 303 testMandatoryStreamCombination(String cameraId, StaticMetadata staticInfo, String physicalCameraId, MandatoryStreamCombination combination)304 private void testMandatoryStreamCombination(String cameraId, StaticMetadata staticInfo, 305 String physicalCameraId, MandatoryStreamCombination combination) throws Exception { 306 // Check whether substituting YUV_888 format with Y8 format 307 boolean substituteY8 = false; 308 if (staticInfo.isMonochromeWithY8()) { 309 List<MandatoryStreamInformation> streamsInfo = combination.getStreamsInformation(); 310 for (MandatoryStreamInformation streamInfo : streamsInfo) { 311 if (streamInfo.getFormat() == ImageFormat.YUV_420_888) { 312 substituteY8 = true; 313 break; 314 } 315 } 316 } 317 318 // Check whether substituting JPEG format with HEIC format 319 boolean substituteHeic = false; 320 if (staticInfo.isHeicSupported()) { 321 List<MandatoryStreamInformation> streamsInfo = combination.getStreamsInformation(); 322 for (MandatoryStreamInformation streamInfo : streamsInfo) { 323 if (streamInfo.getFormat() == ImageFormat.JPEG) { 324 substituteHeic = true; 325 break; 326 } 327 } 328 } 329 330 // Test camera output combination 331 String log = "Testing mandatory stream combination: " + combination.getDescription() + 332 " on camera: " + cameraId; 333 if (physicalCameraId != null) { 334 log += ", physical sub-camera: " + physicalCameraId; 335 } 336 Log.i(TAG, log); 337 testMandatoryStreamCombination(cameraId, staticInfo, physicalCameraId, combination, 338 /*substituteY8*/false, /*substituteHeic*/false, /*maxResolution*/false); 339 340 if (substituteY8) { 341 Log.i(TAG, log + " with Y8"); 342 testMandatoryStreamCombination(cameraId, staticInfo, physicalCameraId, combination, 343 /*substituteY8*/true, /*substituteHeic*/false, /*maxResolution*/false); 344 } 345 346 if (substituteHeic) { 347 Log.i(TAG, log + " with HEIC"); 348 testMandatoryStreamCombination(cameraId, staticInfo, physicalCameraId, combination, 349 /*substituteY8*/false, /*substituteHeic*/true, /**maxResolution*/ false); 350 } 351 } 352 testMandatoryStreamCombination(String cameraId, StaticMetadata staticInfo, String physicalCameraId, MandatoryStreamCombination combination, boolean substituteY8, boolean substituteHeic, boolean ultraHighResolution)353 private void testMandatoryStreamCombination(String cameraId, 354 StaticMetadata staticInfo, String physicalCameraId, 355 MandatoryStreamCombination combination, 356 boolean substituteY8, boolean substituteHeic, boolean ultraHighResolution) 357 throws Exception { 358 359 // Timeout is relaxed by 1 second for LEGACY devices to reduce false positive rate in CTS 360 // TODO: This needs to be adjusted based on feedback 361 final int TIMEOUT_MULTIPLIER = ultraHighResolution ? 2 : 1; 362 final int TIMEOUT_FOR_RESULT_MS = 363 ((staticInfo.isHardwareLevelLegacy()) ? 2000 : 1000) * TIMEOUT_MULTIPLIER; 364 final int MIN_RESULT_COUNT = 3; 365 366 // Set up outputs 367 List<OutputConfiguration> outputConfigs = new ArrayList<>(); 368 List<Surface> outputSurfaces = new ArrayList<Surface>(); 369 List<Surface> uhOutputSurfaces = new ArrayList<Surface>(); 370 StreamCombinationTargets targets = new StreamCombinationTargets(); 371 372 CameraTestUtils.setupConfigurationTargets(combination.getStreamsInformation(), 373 targets, outputConfigs, outputSurfaces, uhOutputSurfaces, MIN_RESULT_COUNT, 374 substituteY8, substituteHeic, physicalCameraId, /*multiResStreamConfig*/null, 375 mHandler); 376 377 boolean haveSession = false; 378 try { 379 CaptureRequest.Builder requestBuilder = 380 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); 381 CaptureRequest.Builder uhRequestBuilder = 382 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); 383 384 for (Surface s : outputSurfaces) { 385 requestBuilder.addTarget(s); 386 } 387 388 for (Surface s : uhOutputSurfaces) { 389 uhRequestBuilder.addTarget(s); 390 } 391 // We need to explicitly set the sensor pixel mode to default since we're mixing default 392 // and max resolution requests in the same capture session. 393 requestBuilder.set(CaptureRequest.SENSOR_PIXEL_MODE, 394 CameraMetadata.SENSOR_PIXEL_MODE_DEFAULT); 395 if (ultraHighResolution) { 396 uhRequestBuilder.set(CaptureRequest.SENSOR_PIXEL_MODE, 397 CameraMetadata.SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION); 398 } 399 CameraCaptureSession.CaptureCallback mockCaptureCallback = 400 mock(CameraCaptureSession.CaptureCallback.class); 401 402 if (physicalCameraId == null) { 403 checkSessionConfigurationSupported(mCamera, mHandler, outputConfigs, 404 /*inputConfig*/ null, SessionConfiguration.SESSION_REGULAR, 405 true/*defaultSupport*/, String.format( 406 "Session configuration query from combination: %s failed", 407 combination.getDescription())); 408 } else { 409 SessionConfigSupport sessionConfigSupport = isSessionConfigSupported( 410 mCamera, mHandler, outputConfigs, /*inputConfig*/ null, 411 SessionConfiguration.SESSION_REGULAR, false/*defaultSupport*/); 412 assertTrue( 413 String.format("Session configuration query from combination: %s failed", 414 combination.getDescription()), !sessionConfigSupport.error); 415 if (!sessionConfigSupport.callSupported) { 416 return; 417 } 418 assertTrue( 419 String.format("Session configuration must be supported for combination: " + 420 "%s", combination.getDescription()), sessionConfigSupport.configSupported); 421 } 422 423 createSessionByConfigs(outputConfigs); 424 haveSession = true; 425 CaptureRequest request = requestBuilder.build(); 426 CaptureRequest uhRequest = uhRequestBuilder.build(); 427 mCameraSession.setRepeatingRequest(request, mockCaptureCallback, mHandler); 428 if (ultraHighResolution) { 429 mCameraSession.capture(uhRequest, mockCaptureCallback, mHandler); 430 } 431 verify(mockCaptureCallback, 432 timeout(TIMEOUT_FOR_RESULT_MS * MIN_RESULT_COUNT).atLeast(MIN_RESULT_COUNT)) 433 .onCaptureCompleted( 434 eq(mCameraSession), 435 eq(request), 436 isA(TotalCaptureResult.class)); 437 if (ultraHighResolution) { 438 verify(mockCaptureCallback, 439 timeout(TIMEOUT_FOR_RESULT_MS).atLeast(1)) 440 .onCaptureCompleted( 441 eq(mCameraSession), 442 eq(uhRequest), 443 isA(TotalCaptureResult.class)); 444 } 445 446 verify(mockCaptureCallback, never()). 447 onCaptureFailed( 448 eq(mCameraSession), 449 eq(request), 450 isA(CaptureFailure.class)); 451 452 } catch (Throwable e) { 453 mCollector.addMessage(String.format("Mandatory stream combination: %s failed due: %s", 454 combination.getDescription(), e.getMessage())); 455 } 456 if (haveSession) { 457 try { 458 Log.i(TAG, String.format("Done with camera %s, combination: %s, closing session", 459 cameraId, combination.getDescription())); 460 stopCapture(/*fast*/false); 461 } catch (Throwable e) { 462 mCollector.addMessage( 463 String.format("Closing down for combination: %s failed due to: %s", 464 combination.getDescription(), e.getMessage())); 465 } 466 } 467 468 targets.close(); 469 } 470 471 /** 472 * Test for making sure the required reprocess input/output combinations for each hardware 473 * level and capability work as expected. 474 */ 475 @Test testMandatoryReprocessConfigurations()476 public void testMandatoryReprocessConfigurations() throws Exception { 477 testMandatoryReprocessConfigurations(/*maxResolution*/false); 478 } 479 480 /** 481 * Test for making sure the required reprocess input/output combinations for each hardware 482 * level and capability work as expected. 483 */ 484 @Test testMandatoryMaximumResolutionReprocessConfigurations()485 public void testMandatoryMaximumResolutionReprocessConfigurations() throws Exception { 486 testMandatoryReprocessConfigurations(/*maxResolution*/true); 487 } 488 489 /** 490 * Test for making sure the required reprocess input/output combinations for each hardware 491 * level and capability work as expected. 492 */ testMandatoryReprocessConfigurations(boolean maxResolution)493 public void testMandatoryReprocessConfigurations(boolean maxResolution) throws Exception { 494 for (String id : mCameraIdsUnderTest) { 495 openDevice(id); 496 CameraCharacteristics chars = mStaticInfo.getCharacteristics(); 497 if (maxResolution && !CameraTestUtils.hasCapability( 498 chars, CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_REMOSAIC_REPROCESSING)) { 499 Log.i(TAG, "Camera id " + id + "doesn't support REMOSAIC_REPROCESSING, skip test"); 500 closeDevice(id); 501 continue; 502 } 503 CameraCharacteristics.Key<MandatoryStreamCombination []> ck = 504 CameraCharacteristics.SCALER_MANDATORY_STREAM_COMBINATIONS; 505 506 if (maxResolution) { 507 ck = CameraCharacteristics.SCALER_MANDATORY_MAXIMUM_RESOLUTION_STREAM_COMBINATIONS; 508 } 509 510 MandatoryStreamCombination[] combinations = chars.get(ck); 511 if (combinations == null) { 512 Log.i(TAG, "No mandatory stream combinations for camera: " + id + " skip test"); 513 closeDevice(id); 514 continue; 515 } 516 517 try { 518 for (MandatoryStreamCombination combination : combinations) { 519 if (combination.isReprocessable()) { 520 Log.i(TAG, "Testing mandatory reprocessable stream combination: " + 521 combination.getDescription() + " on camera: " + id); 522 testMandatoryReprocessableStreamCombination(id, combination, maxResolution); 523 } 524 } 525 } finally { 526 closeDevice(id); 527 } 528 } 529 } 530 testMandatoryReprocessableStreamCombination(String cameraId, MandatoryStreamCombination combination, boolean maxResolution)531 private void testMandatoryReprocessableStreamCombination(String cameraId, 532 MandatoryStreamCombination combination, boolean maxResolution) throws Exception { 533 // Test reprocess stream combination 534 testMandatoryReprocessableStreamCombination(cameraId, combination, 535 /*substituteY8*/false, /*substituteHeic*/false, maxResolution/*maxResolution*/); 536 if (maxResolution) { 537 // Maximum resolution mode doesn't guarantee HEIC and Y8 streams. 538 return; 539 } 540 541 // Test substituting YUV_888 format with Y8 format in reprocess stream combination. 542 if (mStaticInfo.isMonochromeWithY8()) { 543 List<MandatoryStreamInformation> streamsInfo = combination.getStreamsInformation(); 544 boolean substituteY8 = false; 545 for (MandatoryStreamInformation streamInfo : streamsInfo) { 546 if (streamInfo.getFormat() == ImageFormat.YUV_420_888) { 547 substituteY8 = true; 548 } 549 } 550 if (substituteY8) { 551 testMandatoryReprocessableStreamCombination(cameraId, combination, 552 /*substituteY8*/true, /*substituteHeic*/false, false/*maxResolution*/); 553 } 554 } 555 556 if (mStaticInfo.isHeicSupported()) { 557 List<MandatoryStreamInformation> streamsInfo = combination.getStreamsInformation(); 558 boolean substituteHeic = false; 559 for (MandatoryStreamInformation streamInfo : streamsInfo) { 560 if (streamInfo.getFormat() == ImageFormat.JPEG) { 561 substituteHeic = true; 562 } 563 } 564 if (substituteHeic) { 565 testMandatoryReprocessableStreamCombination(cameraId, combination, 566 /*substituteY8*/false, /*substituteHeic*/true, false/*maxResolution*/); 567 } 568 } 569 } 570 testMandatoryReprocessableStreamCombination(String cameraId, MandatoryStreamCombination combination, boolean substituteY8, boolean substituteHeic, boolean maxResolution)571 private void testMandatoryReprocessableStreamCombination(String cameraId, 572 MandatoryStreamCombination combination, boolean substituteY8, 573 boolean substituteHeic, boolean maxResolution) throws Exception { 574 575 final int TIMEOUT_MULTIPLIER = maxResolution ? 2 : 1; 576 final int TIMEOUT_FOR_RESULT_MS = 5000 * TIMEOUT_MULTIPLIER; 577 final int NUM_REPROCESS_CAPTURES_PER_CONFIG = 3; 578 579 StreamCombinationTargets targets = new StreamCombinationTargets(); 580 ArrayList<Surface> defaultOutputSurfaces = new ArrayList<>(); 581 ArrayList<Surface> allOutputSurfaces = new ArrayList<>(); 582 List<OutputConfiguration> outputConfigs = new ArrayList<>(); 583 List<Surface> uhOutputSurfaces = new ArrayList<Surface>(); 584 ImageReader inputReader = null; 585 ImageWriter inputWriter = null; 586 SimpleImageReaderListener inputReaderListener = new SimpleImageReaderListener(); 587 SimpleCaptureCallback inputCaptureListener = new SimpleCaptureCallback(); 588 SimpleCaptureCallback reprocessOutputCaptureListener = new SimpleCaptureCallback(); 589 590 List<MandatoryStreamInformation> streamInfo = combination.getStreamsInformation(); 591 assertTrue("Reprocessable stream combinations should have at least 3 or more streams", 592 (streamInfo != null) && (streamInfo.size() >= 3)); 593 594 assertTrue("The first mandatory stream information in a reprocessable combination must " + 595 "always be input", streamInfo.get(0).isInput()); 596 597 List<Size> inputSizes = streamInfo.get(0).getAvailableSizes(); 598 int inputFormat = streamInfo.get(0).getFormat(); 599 if (substituteY8 && (inputFormat == ImageFormat.YUV_420_888)) { 600 inputFormat = ImageFormat.Y8; 601 } 602 603 Log.i(TAG, "testMandatoryReprocessableStreamCombination: " + 604 combination.getDescription() + ", substituteY8 = " + substituteY8 + 605 ", substituteHeic = " + substituteHeic); 606 try { 607 // The second stream information entry is the ZSL stream, which is configured 608 // separately. 609 List<MandatoryStreamInformation> mandatoryStreamInfos = null; 610 mandatoryStreamInfos = new ArrayList<MandatoryStreamInformation>(); 611 mandatoryStreamInfos = streamInfo.subList(2, streamInfo.size()); 612 CameraTestUtils.setupConfigurationTargets(mandatoryStreamInfos, targets, 613 outputConfigs, defaultOutputSurfaces, uhOutputSurfaces, 614 NUM_REPROCESS_CAPTURES_PER_CONFIG, 615 substituteY8, substituteHeic, null/*overridePhysicalCameraId*/, 616 /*multiResStreamConfig*/null, mHandler); 617 allOutputSurfaces.addAll(defaultOutputSurfaces); 618 allOutputSurfaces.addAll(uhOutputSurfaces); 619 InputConfiguration inputConfig = new InputConfiguration(inputSizes.get(0).getWidth(), 620 inputSizes.get(0).getHeight(), inputFormat); 621 622 // For each config, YUV and JPEG outputs will be tested. (For YUV/Y8 reprocessing, 623 // the YUV/Y8 ImageReader for input is also used for output.) 624 final boolean inputIsYuv = inputConfig.getFormat() == ImageFormat.YUV_420_888; 625 final boolean inputIsY8 = inputConfig.getFormat() == ImageFormat.Y8; 626 final boolean useYuv = inputIsYuv || targets.mYuvTargets.size() > 0; 627 final boolean useY8 = inputIsY8 || targets.mY8Targets.size() > 0; 628 final int totalNumReprocessCaptures = NUM_REPROCESS_CAPTURES_PER_CONFIG * 629 (maxResolution ? 1 : (((inputIsYuv || inputIsY8) ? 1 : 0) + 630 (substituteHeic ? targets.mHeicTargets.size() : targets.mJpegTargets.size()) + 631 (useYuv ? targets.mYuvTargets.size() : targets.mY8Targets.size()))); 632 633 // It needs 1 input buffer for each reprocess capture + the number of buffers 634 // that will be used as outputs. 635 inputReader = ImageReader.newInstance(inputConfig.getWidth(), inputConfig.getHeight(), 636 inputConfig.getFormat(), 637 totalNumReprocessCaptures + NUM_REPROCESS_CAPTURES_PER_CONFIG); 638 inputReader.setOnImageAvailableListener(inputReaderListener, mHandler); 639 allOutputSurfaces.add(inputReader.getSurface()); 640 641 checkSessionConfigurationWithSurfaces(mCamera, mHandler, allOutputSurfaces, 642 inputConfig, SessionConfiguration.SESSION_REGULAR, /*defaultSupport*/ true, 643 String.format("Session configuration query %s failed", 644 combination.getDescription())); 645 646 // Verify we can create a reprocessable session with the input and all outputs. 647 BlockingSessionCallback sessionListener = new BlockingSessionCallback(); 648 CameraCaptureSession session = configureReprocessableCameraSession(mCamera, 649 inputConfig, allOutputSurfaces, sessionListener, mHandler); 650 inputWriter = ImageWriter.newInstance(session.getInputSurface(), 651 totalNumReprocessCaptures); 652 653 // Prepare a request for reprocess input 654 CaptureRequest.Builder builder = mCamera.createCaptureRequest( 655 CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG); 656 builder.addTarget(inputReader.getSurface()); 657 if (maxResolution) { 658 builder.set(CaptureRequest.SENSOR_PIXEL_MODE, 659 CameraMetadata.SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION); 660 } 661 662 for (int i = 0; i < totalNumReprocessCaptures; i++) { 663 session.capture(builder.build(), inputCaptureListener, mHandler); 664 } 665 666 List<CaptureRequest> reprocessRequests = new ArrayList<>(); 667 List<Surface> reprocessOutputs = new ArrayList<>(); 668 669 if (maxResolution) { 670 if (uhOutputSurfaces.size() == 0) { // RAW -> RAW reprocessing 671 reprocessOutputs.add(inputReader.getSurface()); 672 } else { 673 for (Surface surface : uhOutputSurfaces) { 674 reprocessOutputs.add(surface); 675 } 676 } 677 } else { 678 if (inputIsYuv || inputIsY8) { 679 reprocessOutputs.add(inputReader.getSurface()); 680 } 681 682 for (ImageReader reader : targets.mJpegTargets) { 683 reprocessOutputs.add(reader.getSurface()); 684 } 685 686 for (ImageReader reader : targets.mHeicTargets) { 687 reprocessOutputs.add(reader.getSurface()); 688 } 689 690 for (ImageReader reader : targets.mYuvTargets) { 691 reprocessOutputs.add(reader.getSurface()); 692 } 693 694 for (ImageReader reader : targets.mY8Targets) { 695 reprocessOutputs.add(reader.getSurface()); 696 } 697 } 698 699 for (int i = 0; i < NUM_REPROCESS_CAPTURES_PER_CONFIG; i++) { 700 for (Surface output : reprocessOutputs) { 701 TotalCaptureResult result = inputCaptureListener.getTotalCaptureResult( 702 TIMEOUT_FOR_RESULT_MS); 703 builder = mCamera.createReprocessCaptureRequest(result); 704 inputWriter.queueInputImage( 705 inputReaderListener.getImage(TIMEOUT_FOR_RESULT_MS)); 706 builder.addTarget(output); 707 reprocessRequests.add(builder.build()); 708 } 709 } 710 711 session.captureBurst(reprocessRequests, reprocessOutputCaptureListener, mHandler); 712 713 for (int i = 0; i < reprocessOutputs.size() * NUM_REPROCESS_CAPTURES_PER_CONFIG; i++) { 714 TotalCaptureResult result = reprocessOutputCaptureListener.getTotalCaptureResult( 715 TIMEOUT_FOR_RESULT_MS); 716 } 717 } catch (Throwable e) { 718 mCollector.addMessage(String.format("Reprocess stream combination %s failed due to: %s", 719 combination.getDescription(), e.getMessage())); 720 } finally { 721 inputReaderListener.drain(); 722 reprocessOutputCaptureListener.drain(); 723 targets.close(); 724 725 if (inputReader != null) { 726 inputReader.close(); 727 } 728 729 if (inputWriter != null) { 730 inputWriter.close(); 731 } 732 } 733 } 734 735 @Test testBasicTriggerSequence()736 public void testBasicTriggerSequence() throws Exception { 737 738 for (String id : mCameraIdsUnderTest) { 739 Log.i(TAG, String.format("Testing Camera %s", id)); 740 741 try { 742 // Legacy devices do not support precapture trigger; don't test devices that 743 // can't focus 744 StaticMetadata staticInfo = mAllStaticInfo.get(id); 745 if (staticInfo.isHardwareLevelLegacy() || !staticInfo.hasFocuser()) { 746 continue; 747 } 748 // Depth-only devices won't support AE 749 if (!staticInfo.isColorOutputSupported()) { 750 Log.i(TAG, "Camera " + id + " does not support color outputs, skipping"); 751 continue; 752 } 753 754 openDevice(id); 755 int[] availableAfModes = mStaticInfo.getAfAvailableModesChecked(); 756 int[] availableAeModes = mStaticInfo.getAeAvailableModesChecked(); 757 758 for (int afMode : availableAfModes) { 759 if (afMode == CameraCharacteristics.CONTROL_AF_MODE_OFF || 760 afMode == CameraCharacteristics.CONTROL_AF_MODE_EDOF) { 761 // Only test AF modes that have meaningful trigger behavior 762 continue; 763 } 764 765 for (int aeMode : availableAeModes) { 766 if (aeMode == CameraCharacteristics.CONTROL_AE_MODE_OFF) { 767 // Only test AE modes that have meaningful trigger behavior 768 continue; 769 } 770 771 SurfaceTexture preview = new SurfaceTexture(/*random int*/ 1); 772 773 CaptureRequest.Builder previewRequest = 774 prepareTriggerTestSession(preview, aeMode, afMode); 775 776 SimpleCaptureCallback captureListener = 777 new CameraTestUtils.SimpleCaptureCallback(); 778 779 mCameraSession.setRepeatingRequest(previewRequest.build(), captureListener, 780 mHandler); 781 782 // Cancel triggers 783 784 cancelTriggersAndWait(previewRequest, captureListener, afMode); 785 786 // 787 // Standard sequence - AF trigger then AE trigger 788 789 if (VERBOSE) { 790 Log.v(TAG, String.format("Triggering AF")); 791 } 792 793 previewRequest.set(CaptureRequest.CONTROL_AF_TRIGGER, 794 CaptureRequest.CONTROL_AF_TRIGGER_START); 795 previewRequest.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, 796 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_IDLE); 797 798 CaptureRequest triggerRequest = previewRequest.build(); 799 mCameraSession.capture(triggerRequest, captureListener, mHandler); 800 801 CaptureResult triggerResult = captureListener.getCaptureResultForRequest( 802 triggerRequest, MAX_RESULT_STATE_CHANGE_WAIT_FRAMES); 803 int afState = triggerResult.get(CaptureResult.CONTROL_AF_STATE); 804 boolean focusComplete = false; 805 806 for (int i = 0; 807 i < MAX_TRIGGER_SEQUENCE_FRAMES && !focusComplete; 808 i++) { 809 810 focusComplete = verifyAfSequence(afMode, afState, focusComplete); 811 812 CaptureResult focusResult = captureListener.getCaptureResult( 813 CameraTestUtils.CAPTURE_RESULT_TIMEOUT_MS); 814 afState = focusResult.get(CaptureResult.CONTROL_AF_STATE); 815 } 816 817 assertTrue("Focusing never completed!", focusComplete); 818 819 // Standard sequence - Part 2 AE trigger 820 821 if (VERBOSE) { 822 Log.v(TAG, String.format("Triggering AE")); 823 } 824 825 previewRequest.set(CaptureRequest.CONTROL_AF_TRIGGER, 826 CaptureRequest.CONTROL_AF_TRIGGER_IDLE); 827 previewRequest.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, 828 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START); 829 830 triggerRequest = previewRequest.build(); 831 mCameraSession.capture(triggerRequest, captureListener, mHandler); 832 833 triggerResult = captureListener.getCaptureResultForRequest( 834 triggerRequest, MAX_RESULT_STATE_CHANGE_WAIT_FRAMES); 835 836 int aeState = triggerResult.get(CaptureResult.CONTROL_AE_STATE); 837 838 boolean precaptureComplete = false; 839 840 for (int i = 0; 841 i < MAX_TRIGGER_SEQUENCE_FRAMES && !precaptureComplete; 842 i++) { 843 844 precaptureComplete = verifyAeSequence(aeState, precaptureComplete); 845 846 CaptureResult precaptureResult = captureListener.getCaptureResult( 847 CameraTestUtils.CAPTURE_RESULT_TIMEOUT_MS); 848 aeState = precaptureResult.get(CaptureResult.CONTROL_AE_STATE); 849 } 850 851 assertTrue("Precapture sequence never completed!", precaptureComplete); 852 853 for (int i = 0; i < MAX_RESULT_STATE_POSTCHANGE_WAIT_FRAMES; i++) { 854 CaptureResult postPrecaptureResult = captureListener.getCaptureResult( 855 CameraTestUtils.CAPTURE_RESULT_TIMEOUT_MS); 856 aeState = postPrecaptureResult.get(CaptureResult.CONTROL_AE_STATE); 857 assertTrue("Late transition to PRECAPTURE state seen", 858 aeState != CaptureResult.CONTROL_AE_STATE_PRECAPTURE); 859 } 860 861 // Done 862 863 stopCapture(/*fast*/ false); 864 preview.release(); 865 } 866 867 } 868 869 } finally { 870 closeDevice(id); 871 } 872 } 873 874 } 875 876 @Test testSimultaneousTriggers()877 public void testSimultaneousTriggers() throws Exception { 878 for (String id : mCameraIdsUnderTest) { 879 Log.i(TAG, String.format("Testing Camera %s", id)); 880 881 try { 882 // Legacy devices do not support precapture trigger; don't test devices that 883 // can't focus 884 StaticMetadata staticInfo = mAllStaticInfo.get(id); 885 if (staticInfo.isHardwareLevelLegacy() || !staticInfo.hasFocuser()) { 886 continue; 887 } 888 // Depth-only devices won't support AE 889 if (!staticInfo.isColorOutputSupported()) { 890 Log.i(TAG, "Camera " + id + " does not support color outputs, skipping"); 891 continue; 892 } 893 894 openDevice(id); 895 int[] availableAfModes = mStaticInfo.getAfAvailableModesChecked(); 896 int[] availableAeModes = mStaticInfo.getAeAvailableModesChecked(); 897 898 for (int afMode : availableAfModes) { 899 if (afMode == CameraCharacteristics.CONTROL_AF_MODE_OFF || 900 afMode == CameraCharacteristics.CONTROL_AF_MODE_EDOF) { 901 // Only test AF modes that have meaningful trigger behavior 902 continue; 903 } 904 905 for (int aeMode : availableAeModes) { 906 if (aeMode == CameraCharacteristics.CONTROL_AE_MODE_OFF) { 907 // Only test AE modes that have meaningful trigger behavior 908 continue; 909 } 910 911 SurfaceTexture preview = new SurfaceTexture(/*random int*/ 1); 912 913 CaptureRequest.Builder previewRequest = 914 prepareTriggerTestSession(preview, aeMode, afMode); 915 916 SimpleCaptureCallback captureListener = 917 new CameraTestUtils.SimpleCaptureCallback(); 918 919 mCameraSession.setRepeatingRequest(previewRequest.build(), captureListener, 920 mHandler); 921 922 // Cancel triggers 923 924 cancelTriggersAndWait(previewRequest, captureListener, afMode); 925 926 // 927 // Trigger AF and AE together 928 929 if (VERBOSE) { 930 Log.v(TAG, String.format("Triggering AF and AE together")); 931 } 932 933 previewRequest.set(CaptureRequest.CONTROL_AF_TRIGGER, 934 CaptureRequest.CONTROL_AF_TRIGGER_START); 935 previewRequest.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, 936 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START); 937 938 CaptureRequest triggerRequest = previewRequest.build(); 939 mCameraSession.capture(triggerRequest, captureListener, mHandler); 940 941 CaptureResult triggerResult = captureListener.getCaptureResultForRequest( 942 triggerRequest, MAX_RESULT_STATE_CHANGE_WAIT_FRAMES); 943 int aeState = triggerResult.get(CaptureResult.CONTROL_AE_STATE); 944 int afState = triggerResult.get(CaptureResult.CONTROL_AF_STATE); 945 946 boolean precaptureComplete = false; 947 boolean focusComplete = false; 948 949 for (int i = 0; 950 i < MAX_TRIGGER_SEQUENCE_FRAMES && 951 !(focusComplete && precaptureComplete); 952 i++) { 953 954 focusComplete = verifyAfSequence(afMode, afState, focusComplete); 955 precaptureComplete = verifyAeSequence(aeState, precaptureComplete); 956 957 CaptureResult sequenceResult = captureListener.getCaptureResult( 958 CameraTestUtils.CAPTURE_RESULT_TIMEOUT_MS); 959 afState = sequenceResult.get(CaptureResult.CONTROL_AF_STATE); 960 aeState = sequenceResult.get(CaptureResult.CONTROL_AE_STATE); 961 } 962 963 assertTrue("Precapture sequence never completed!", precaptureComplete); 964 assertTrue("Focus sequence never completed!", focusComplete); 965 966 // Done 967 968 stopCapture(/*fast*/ false); 969 preview.release(); 970 971 } 972 } 973 } finally { 974 closeDevice(id); 975 } 976 } 977 } 978 979 @Test testAfThenAeTrigger()980 public void testAfThenAeTrigger() throws Exception { 981 for (String id : mCameraIdsUnderTest) { 982 Log.i(TAG, String.format("Testing Camera %s", id)); 983 984 try { 985 // Legacy devices do not support precapture trigger; don't test devices that 986 // can't focus 987 StaticMetadata staticInfo = mAllStaticInfo.get(id); 988 if (staticInfo.isHardwareLevelLegacy() || !staticInfo.hasFocuser()) { 989 continue; 990 } 991 // Depth-only devices won't support AE 992 if (!staticInfo.isColorOutputSupported()) { 993 Log.i(TAG, "Camera " + id + " does not support color outputs, skipping"); 994 continue; 995 } 996 997 openDevice(id); 998 int[] availableAfModes = mStaticInfo.getAfAvailableModesChecked(); 999 int[] availableAeModes = mStaticInfo.getAeAvailableModesChecked(); 1000 1001 for (int afMode : availableAfModes) { 1002 if (afMode == CameraCharacteristics.CONTROL_AF_MODE_OFF || 1003 afMode == CameraCharacteristics.CONTROL_AF_MODE_EDOF) { 1004 // Only test AF modes that have meaningful trigger behavior 1005 continue; 1006 } 1007 1008 for (int aeMode : availableAeModes) { 1009 if (aeMode == CameraCharacteristics.CONTROL_AE_MODE_OFF) { 1010 // Only test AE modes that have meaningful trigger behavior 1011 continue; 1012 } 1013 1014 SurfaceTexture preview = new SurfaceTexture(/*random int*/ 1); 1015 1016 CaptureRequest.Builder previewRequest = 1017 prepareTriggerTestSession(preview, aeMode, afMode); 1018 1019 SimpleCaptureCallback captureListener = 1020 new CameraTestUtils.SimpleCaptureCallback(); 1021 1022 mCameraSession.setRepeatingRequest(previewRequest.build(), captureListener, 1023 mHandler); 1024 1025 // Cancel triggers 1026 1027 cancelTriggersAndWait(previewRequest, captureListener, afMode); 1028 1029 // 1030 // AF with AE a request later 1031 1032 if (VERBOSE) { 1033 Log.v(TAG, "Trigger AF, then AE trigger on next request"); 1034 } 1035 1036 previewRequest.set(CaptureRequest.CONTROL_AF_TRIGGER, 1037 CaptureRequest.CONTROL_AF_TRIGGER_START); 1038 previewRequest.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, 1039 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_IDLE); 1040 1041 CaptureRequest triggerRequest = previewRequest.build(); 1042 mCameraSession.capture(triggerRequest, captureListener, mHandler); 1043 1044 previewRequest.set(CaptureRequest.CONTROL_AF_TRIGGER, 1045 CaptureRequest.CONTROL_AF_TRIGGER_IDLE); 1046 previewRequest.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, 1047 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START); 1048 1049 CaptureRequest triggerRequest2 = previewRequest.build(); 1050 mCameraSession.capture(triggerRequest2, captureListener, mHandler); 1051 1052 CaptureResult triggerResult = captureListener.getCaptureResultForRequest( 1053 triggerRequest, MAX_RESULT_STATE_CHANGE_WAIT_FRAMES); 1054 int afState = triggerResult.get(CaptureResult.CONTROL_AF_STATE); 1055 1056 boolean precaptureComplete = false; 1057 boolean focusComplete = false; 1058 1059 focusComplete = verifyAfSequence(afMode, afState, focusComplete); 1060 1061 triggerResult = captureListener.getCaptureResultForRequest( 1062 triggerRequest2, MAX_RESULT_STATE_CHANGE_WAIT_FRAMES); 1063 afState = triggerResult.get(CaptureResult.CONTROL_AF_STATE); 1064 int aeState = triggerResult.get(CaptureResult.CONTROL_AE_STATE); 1065 1066 for (int i = 0; 1067 i < MAX_TRIGGER_SEQUENCE_FRAMES && 1068 !(focusComplete && precaptureComplete); 1069 i++) { 1070 1071 focusComplete = verifyAfSequence(afMode, afState, focusComplete); 1072 precaptureComplete = verifyAeSequence(aeState, precaptureComplete); 1073 1074 CaptureResult sequenceResult = captureListener.getCaptureResult( 1075 CameraTestUtils.CAPTURE_RESULT_TIMEOUT_MS); 1076 afState = sequenceResult.get(CaptureResult.CONTROL_AF_STATE); 1077 aeState = sequenceResult.get(CaptureResult.CONTROL_AE_STATE); 1078 } 1079 1080 assertTrue("Precapture sequence never completed!", precaptureComplete); 1081 assertTrue("Focus sequence never completed!", focusComplete); 1082 1083 // Done 1084 1085 stopCapture(/*fast*/ false); 1086 preview.release(); 1087 1088 } 1089 } 1090 } finally { 1091 closeDevice(id); 1092 } 1093 } 1094 } 1095 1096 @Test testAeThenAfTrigger()1097 public void testAeThenAfTrigger() throws Exception { 1098 for (String id : mCameraIdsUnderTest) { 1099 Log.i(TAG, String.format("Testing Camera %s", id)); 1100 1101 try { 1102 // Legacy devices do not support precapture trigger; don't test devices that 1103 // can't focus 1104 StaticMetadata staticInfo = mAllStaticInfo.get(id); 1105 if (staticInfo.isHardwareLevelLegacy() || !staticInfo.hasFocuser()) { 1106 continue; 1107 } 1108 // Depth-only devices won't support AE 1109 if (!staticInfo.isColorOutputSupported()) { 1110 Log.i(TAG, "Camera " + id + " does not support color outputs, skipping"); 1111 continue; 1112 } 1113 1114 openDevice(id); 1115 int[] availableAfModes = mStaticInfo.getAfAvailableModesChecked(); 1116 int[] availableAeModes = mStaticInfo.getAeAvailableModesChecked(); 1117 1118 for (int afMode : availableAfModes) { 1119 if (afMode == CameraCharacteristics.CONTROL_AF_MODE_OFF || 1120 afMode == CameraCharacteristics.CONTROL_AF_MODE_EDOF) { 1121 // Only test AF modes that have meaningful trigger behavior 1122 continue; 1123 } 1124 1125 for (int aeMode : availableAeModes) { 1126 if (aeMode == CameraCharacteristics.CONTROL_AE_MODE_OFF) { 1127 // Only test AE modes that have meaningful trigger behavior 1128 continue; 1129 } 1130 1131 SurfaceTexture preview = new SurfaceTexture(/*random int*/ 1); 1132 1133 CaptureRequest.Builder previewRequest = 1134 prepareTriggerTestSession(preview, aeMode, afMode); 1135 1136 SimpleCaptureCallback captureListener = 1137 new CameraTestUtils.SimpleCaptureCallback(); 1138 1139 mCameraSession.setRepeatingRequest(previewRequest.build(), captureListener, 1140 mHandler); 1141 1142 // Cancel triggers 1143 1144 cancelTriggersAndWait(previewRequest, captureListener, afMode); 1145 1146 // 1147 // AE with AF a request later 1148 1149 if (VERBOSE) { 1150 Log.v(TAG, "Trigger AE, then AF trigger on next request"); 1151 } 1152 1153 previewRequest.set(CaptureRequest.CONTROL_AF_TRIGGER, 1154 CaptureRequest.CONTROL_AF_TRIGGER_IDLE); 1155 previewRequest.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, 1156 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START); 1157 1158 CaptureRequest triggerRequest = previewRequest.build(); 1159 mCameraSession.capture(triggerRequest, captureListener, mHandler); 1160 1161 previewRequest.set(CaptureRequest.CONTROL_AF_TRIGGER, 1162 CaptureRequest.CONTROL_AF_TRIGGER_START); 1163 previewRequest.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, 1164 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_IDLE); 1165 1166 CaptureRequest triggerRequest2 = previewRequest.build(); 1167 mCameraSession.capture(triggerRequest2, captureListener, mHandler); 1168 1169 CaptureResult triggerResult = captureListener.getCaptureResultForRequest( 1170 triggerRequest, MAX_RESULT_STATE_CHANGE_WAIT_FRAMES); 1171 int aeState = triggerResult.get(CaptureResult.CONTROL_AE_STATE); 1172 1173 boolean precaptureComplete = false; 1174 boolean focusComplete = false; 1175 1176 precaptureComplete = verifyAeSequence(aeState, precaptureComplete); 1177 1178 triggerResult = captureListener.getCaptureResultForRequest( 1179 triggerRequest2, MAX_RESULT_STATE_CHANGE_WAIT_FRAMES); 1180 int afState = triggerResult.get(CaptureResult.CONTROL_AF_STATE); 1181 aeState = triggerResult.get(CaptureResult.CONTROL_AE_STATE); 1182 1183 for (int i = 0; 1184 i < MAX_TRIGGER_SEQUENCE_FRAMES && 1185 !(focusComplete && precaptureComplete); 1186 i++) { 1187 1188 focusComplete = verifyAfSequence(afMode, afState, focusComplete); 1189 precaptureComplete = verifyAeSequence(aeState, precaptureComplete); 1190 1191 CaptureResult sequenceResult = captureListener.getCaptureResult( 1192 CameraTestUtils.CAPTURE_RESULT_TIMEOUT_MS); 1193 afState = sequenceResult.get(CaptureResult.CONTROL_AF_STATE); 1194 aeState = sequenceResult.get(CaptureResult.CONTROL_AE_STATE); 1195 } 1196 1197 assertTrue("Precapture sequence never completed!", precaptureComplete); 1198 assertTrue("Focus sequence never completed!", focusComplete); 1199 1200 // Done 1201 1202 stopCapture(/*fast*/ false); 1203 preview.release(); 1204 1205 } 1206 } 1207 } finally { 1208 closeDevice(id); 1209 } 1210 } 1211 } 1212 1213 @Test testAeAndAfCausality()1214 public void testAeAndAfCausality() throws Exception { 1215 1216 for (String id : mCameraIdsUnderTest) { 1217 Log.i(TAG, String.format("Testing Camera %s", id)); 1218 1219 try { 1220 // Legacy devices do not support precapture trigger; don't test devices that 1221 // can't focus 1222 StaticMetadata staticInfo = mAllStaticInfo.get(id); 1223 if (staticInfo.isHardwareLevelLegacy() || !staticInfo.hasFocuser()) { 1224 continue; 1225 } 1226 // Depth-only devices won't support AE 1227 if (!staticInfo.isColorOutputSupported()) { 1228 Log.i(TAG, "Camera " + id + " does not support color outputs, skipping"); 1229 continue; 1230 } 1231 1232 openDevice(id); 1233 int[] availableAfModes = mStaticInfo.getAfAvailableModesChecked(); 1234 int[] availableAeModes = mStaticInfo.getAeAvailableModesChecked(); 1235 final int maxPipelineDepth = mStaticInfo.getCharacteristics().get( 1236 CameraCharacteristics.REQUEST_PIPELINE_MAX_DEPTH); 1237 1238 for (int afMode : availableAfModes) { 1239 if (afMode == CameraCharacteristics.CONTROL_AF_MODE_OFF || 1240 afMode == CameraCharacteristics.CONTROL_AF_MODE_EDOF) { 1241 // Only test AF modes that have meaningful trigger behavior 1242 continue; 1243 } 1244 for (int aeMode : availableAeModes) { 1245 if (aeMode == CameraCharacteristics.CONTROL_AE_MODE_OFF) { 1246 // Only test AE modes that have meaningful trigger behavior 1247 continue; 1248 } 1249 1250 SurfaceTexture preview = new SurfaceTexture(/*random int*/ 1); 1251 1252 CaptureRequest.Builder previewRequest = 1253 prepareTriggerTestSession(preview, aeMode, afMode); 1254 1255 SimpleCaptureCallback captureListener = 1256 new CameraTestUtils.SimpleCaptureCallback(); 1257 1258 mCameraSession.setRepeatingRequest(previewRequest.build(), captureListener, 1259 mHandler); 1260 1261 List<CaptureRequest> triggerRequests = 1262 new ArrayList<CaptureRequest>(maxPipelineDepth+1); 1263 for (int i = 0; i < maxPipelineDepth; i++) { 1264 triggerRequests.add(previewRequest.build()); 1265 } 1266 1267 // Cancel triggers 1268 cancelTriggersAndWait(previewRequest, captureListener, afMode); 1269 1270 // 1271 // Standard sequence - Part 1 AF trigger 1272 1273 if (VERBOSE) { 1274 Log.v(TAG, String.format("Triggering AF")); 1275 } 1276 1277 previewRequest.set(CaptureRequest.CONTROL_AF_TRIGGER, 1278 CaptureRequest.CONTROL_AF_TRIGGER_START); 1279 previewRequest.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, 1280 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_IDLE); 1281 triggerRequests.add(previewRequest.build()); 1282 1283 mCameraSession.captureBurst(triggerRequests, captureListener, mHandler); 1284 1285 TotalCaptureResult[] triggerResults = 1286 captureListener.getTotalCaptureResultsForRequests( 1287 triggerRequests, MAX_RESULT_STATE_CHANGE_WAIT_FRAMES); 1288 for (int i = 0; i < maxPipelineDepth; i++) { 1289 TotalCaptureResult triggerResult = triggerResults[i]; 1290 int afState = triggerResult.get(CaptureResult.CONTROL_AF_STATE); 1291 int afTrigger = triggerResult.get(CaptureResult.CONTROL_AF_TRIGGER); 1292 1293 verifyStartingAfState(afMode, afState); 1294 assertTrue(String.format("In AF mode %s, previous AF_TRIGGER must not " 1295 + "be START before TRIGGER_START", 1296 StaticMetadata.getAfModeName(afMode)), 1297 afTrigger != CaptureResult.CONTROL_AF_TRIGGER_START); 1298 } 1299 1300 int afState = 1301 triggerResults[maxPipelineDepth].get(CaptureResult.CONTROL_AF_STATE); 1302 boolean focusComplete = false; 1303 for (int i = 0; 1304 i < MAX_TRIGGER_SEQUENCE_FRAMES && !focusComplete; 1305 i++) { 1306 1307 focusComplete = verifyAfSequence(afMode, afState, focusComplete); 1308 1309 CaptureResult focusResult = captureListener.getCaptureResult( 1310 CameraTestUtils.CAPTURE_RESULT_TIMEOUT_MS); 1311 afState = focusResult.get(CaptureResult.CONTROL_AF_STATE); 1312 } 1313 1314 assertTrue("Focusing never completed!", focusComplete); 1315 1316 // Standard sequence - Part 2 AE trigger 1317 1318 if (VERBOSE) { 1319 Log.v(TAG, String.format("Triggering AE")); 1320 } 1321 // Remove AF trigger request 1322 triggerRequests.remove(maxPipelineDepth); 1323 1324 previewRequest.set(CaptureRequest.CONTROL_AF_TRIGGER, 1325 CaptureRequest.CONTROL_AF_TRIGGER_IDLE); 1326 previewRequest.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, 1327 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START); 1328 triggerRequests.add(previewRequest.build()); 1329 1330 mCameraSession.captureBurst(triggerRequests, captureListener, mHandler); 1331 1332 triggerResults = captureListener.getTotalCaptureResultsForRequests( 1333 triggerRequests, MAX_RESULT_STATE_CHANGE_WAIT_FRAMES); 1334 1335 for (int i = 0; i < maxPipelineDepth; i++) { 1336 TotalCaptureResult triggerResult = triggerResults[i]; 1337 int aeState = triggerResult.get(CaptureResult.CONTROL_AE_STATE); 1338 int aeTrigger = triggerResult.get( 1339 CaptureResult.CONTROL_AE_PRECAPTURE_TRIGGER); 1340 1341 assertTrue(String.format("In AE mode %s, previous AE_TRIGGER must not " 1342 + "be START before TRIGGER_START", 1343 StaticMetadata.getAeModeName(aeMode)), 1344 aeTrigger != CaptureResult.CONTROL_AE_PRECAPTURE_TRIGGER_START); 1345 assertTrue(String.format("In AE mode %s, previous AE_STATE must not be" 1346 + " PRECAPTURE_TRIGGER before TRIGGER_START", 1347 StaticMetadata.getAeModeName(aeMode)), 1348 aeState != CaptureResult.CONTROL_AE_STATE_PRECAPTURE); 1349 } 1350 1351 // Stand sequence - Part 3 Cancel AF trigger 1352 if (VERBOSE) { 1353 Log.v(TAG, String.format("Cancel AF trigger")); 1354 } 1355 // Remove AE trigger request 1356 triggerRequests.remove(maxPipelineDepth); 1357 previewRequest.set(CaptureRequest.CONTROL_AF_TRIGGER, 1358 CaptureRequest.CONTROL_AF_TRIGGER_CANCEL); 1359 triggerRequests.add(previewRequest.build()); 1360 1361 mCameraSession.captureBurst(triggerRequests, captureListener, mHandler); 1362 triggerResults = captureListener.getTotalCaptureResultsForRequests( 1363 triggerRequests, MAX_RESULT_STATE_CHANGE_WAIT_FRAMES); 1364 for (int i = 0; i < maxPipelineDepth; i++) { 1365 TotalCaptureResult triggerResult = triggerResults[i]; 1366 afState = triggerResult.get(CaptureResult.CONTROL_AF_STATE); 1367 int afTrigger = triggerResult.get(CaptureResult.CONTROL_AF_TRIGGER); 1368 1369 assertTrue( 1370 String.format("In AF mode %s, previous AF_TRIGGER must not " + 1371 "be CANCEL before TRIGGER_CANCEL", 1372 StaticMetadata.getAfModeName(afMode)), 1373 afTrigger != CaptureResult.CONTROL_AF_TRIGGER_CANCEL); 1374 assertTrue( 1375 String.format("In AF mode %s, previous AF_STATE must be LOCKED" 1376 + " before CANCEL, but is %s", 1377 StaticMetadata.getAfModeName(afMode), 1378 StaticMetadata.AF_STATE_NAMES[afState]), 1379 afState == CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED || 1380 afState == CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED); 1381 } 1382 1383 stopCapture(/*fast*/ false); 1384 preview.release(); 1385 } 1386 1387 } 1388 1389 } finally { 1390 closeDevice(id); 1391 } 1392 } 1393 1394 } 1395 1396 @Test testAbandonRepeatingRequestSurface()1397 public void testAbandonRepeatingRequestSurface() throws Exception { 1398 for (String id : mCameraIdsUnderTest) { 1399 Log.i(TAG, String.format( 1400 "Testing Camera %s for abandoning surface of a repeating request", id)); 1401 1402 StaticMetadata staticInfo = mAllStaticInfo.get(id); 1403 if (!staticInfo.isColorOutputSupported()) { 1404 Log.i(TAG, "Camera " + id + " does not support color output, skipping"); 1405 continue; 1406 } 1407 1408 openDevice(id); 1409 1410 try { 1411 1412 SurfaceTexture preview = new SurfaceTexture(/*random int*/ 1); 1413 Surface previewSurface = new Surface(preview); 1414 1415 CaptureRequest.Builder previewRequest = preparePreviewTestSession(preview); 1416 SimpleCaptureCallback captureListener = new CameraTestUtils.SimpleCaptureCallback(); 1417 1418 int sequenceId = mCameraSession.setRepeatingRequest(previewRequest.build(), 1419 captureListener, mHandler); 1420 1421 for (int i = 0; i < PREVIEW_WARMUP_FRAMES; i++) { 1422 captureListener.getTotalCaptureResult(CAPTURE_TIMEOUT); 1423 } 1424 1425 // Abandon preview surface. 1426 preview.release(); 1427 1428 // Check onCaptureSequenceCompleted is received. 1429 long sequenceLastFrameNumber = captureListener.getCaptureSequenceLastFrameNumber( 1430 sequenceId, CAPTURE_TIMEOUT); 1431 1432 mCameraSession.stopRepeating(); 1433 1434 // Find the last frame number received in results and failures. 1435 long lastFrameNumber = -1; 1436 while (captureListener.hasMoreResults()) { 1437 TotalCaptureResult result = captureListener.getTotalCaptureResult( 1438 CAPTURE_TIMEOUT); 1439 if (lastFrameNumber < result.getFrameNumber()) { 1440 lastFrameNumber = result.getFrameNumber(); 1441 } 1442 } 1443 1444 while (captureListener.hasMoreFailures()) { 1445 ArrayList<CaptureFailure> failures = captureListener.getCaptureFailures( 1446 /*maxNumFailures*/ 1); 1447 for (CaptureFailure failure : failures) { 1448 if (lastFrameNumber < failure.getFrameNumber()) { 1449 lastFrameNumber = failure.getFrameNumber(); 1450 } 1451 } 1452 } 1453 1454 // Verify the last frame number received from capture sequence completed matches the 1455 // the last frame number of the results and failures. 1456 assertEquals(String.format("Last frame number from onCaptureSequenceCompleted " + 1457 "(%d) doesn't match the last frame number received from " + 1458 "results/failures (%d)", sequenceLastFrameNumber, lastFrameNumber), 1459 sequenceLastFrameNumber, lastFrameNumber); 1460 } finally { 1461 closeDevice(id); 1462 } 1463 } 1464 } 1465 1466 @Test testConfigureInvalidSensorPixelModes()1467 public void testConfigureInvalidSensorPixelModes() throws Exception { 1468 for (String id : mCameraIdsUnderTest) { 1469 // Go through given, stream configuration map, add the incorrect sensor pixel mode 1470 // to an OutputConfiguration, make sure the session configuration fails. 1471 CameraCharacteristics chars = mCameraManager.getCameraCharacteristics(id); 1472 StreamConfigurationMap defaultStreamConfigMap = 1473 chars.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); 1474 StreamConfigurationMap maxStreamConfigMap = 1475 chars.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION); 1476 openDevice(id); 1477 try { 1478 verifyBasicSensorPixelModes(id, defaultStreamConfigMap, /*maxResolution*/ false); 1479 verifyBasicSensorPixelModes(id, maxStreamConfigMap, /*maxResolution*/ true); 1480 } finally { 1481 closeDevice(id); 1482 } 1483 } 1484 } 1485 1486 @Test testConfigureAbandonedSurface()1487 public void testConfigureAbandonedSurface() throws Exception { 1488 for (String id : mCameraIdsUnderTest) { 1489 Log.i(TAG, String.format( 1490 "Testing Camera %s for configuring abandoned surface", id)); 1491 1492 openDevice(id); 1493 try { 1494 SurfaceTexture preview = new SurfaceTexture(/*random int*/ 1); 1495 Surface previewSurface = new Surface(preview); 1496 1497 // Abandon preview SurfaceTexture. 1498 preview.release(); 1499 1500 try { 1501 CaptureRequest.Builder previewRequest = preparePreviewTestSession(preview); 1502 fail("Configuring abandoned surfaces must fail!"); 1503 } catch (IllegalArgumentException e) { 1504 // expected 1505 Log.i(TAG, "normal session check passed"); 1506 } 1507 1508 // Try constrained high speed session/requests 1509 if (!mStaticInfo.isConstrainedHighSpeedVideoSupported()) { 1510 continue; 1511 } 1512 1513 List<Surface> surfaces = new ArrayList<>(); 1514 surfaces.add(previewSurface); 1515 CameraCaptureSession.StateCallback sessionListener = 1516 mock(CameraCaptureSession.StateCallback.class); 1517 1518 try { 1519 mCamera.createConstrainedHighSpeedCaptureSession(surfaces, 1520 sessionListener, mHandler); 1521 fail("Configuring abandoned surfaces in high speed session must fail!"); 1522 } catch (IllegalArgumentException e) { 1523 // expected 1524 Log.i(TAG, "high speed session check 1 passed"); 1525 } 1526 1527 // Also try abandone the Surface directly 1528 previewSurface.release(); 1529 1530 try { 1531 mCamera.createConstrainedHighSpeedCaptureSession(surfaces, 1532 sessionListener, mHandler); 1533 fail("Configuring abandoned surfaces in high speed session must fail!"); 1534 } catch (IllegalArgumentException e) { 1535 // expected 1536 Log.i(TAG, "high speed session check 2 passed"); 1537 } 1538 } finally { 1539 closeDevice(id); 1540 } 1541 } 1542 } 1543 1544 @Test testAfSceneChange()1545 public void testAfSceneChange() throws Exception { 1546 final int NUM_FRAMES_VERIFIED = 3; 1547 1548 for (String id : mCameraIdsUnderTest) { 1549 Log.i(TAG, String.format("Testing Camera %s for AF scene change", id)); 1550 1551 StaticMetadata staticInfo = 1552 new StaticMetadata(mCameraManager.getCameraCharacteristics(id)); 1553 if (!staticInfo.isAfSceneChangeSupported()) { 1554 continue; 1555 } 1556 1557 openDevice(id); 1558 1559 try { 1560 SurfaceTexture preview = new SurfaceTexture(/*random int*/ 1); 1561 Surface previewSurface = new Surface(preview); 1562 1563 CaptureRequest.Builder previewRequest = preparePreviewTestSession(preview); 1564 SimpleCaptureCallback previewListener = new CameraTestUtils.SimpleCaptureCallback(); 1565 1566 int[] availableAfModes = mStaticInfo.getAfAvailableModesChecked(); 1567 1568 // Test AF scene change in each AF mode. 1569 for (int afMode : availableAfModes) { 1570 previewRequest.set(CaptureRequest.CONTROL_AF_MODE, afMode); 1571 1572 int sequenceId = mCameraSession.setRepeatingRequest(previewRequest.build(), 1573 previewListener, mHandler); 1574 1575 // Verify that AF scene change is NOT_DETECTED or DETECTED. 1576 for (int i = 0; i < NUM_FRAMES_VERIFIED; i++) { 1577 TotalCaptureResult result = 1578 previewListener.getTotalCaptureResult(CAPTURE_TIMEOUT); 1579 mCollector.expectKeyValueIsIn(result, 1580 CaptureResult.CONTROL_AF_SCENE_CHANGE, 1581 CaptureResult.CONTROL_AF_SCENE_CHANGE_DETECTED, 1582 CaptureResult.CONTROL_AF_SCENE_CHANGE_NOT_DETECTED); 1583 } 1584 1585 mCameraSession.stopRepeating(); 1586 previewListener.getCaptureSequenceLastFrameNumber(sequenceId, CAPTURE_TIMEOUT); 1587 previewListener.drain(); 1588 } 1589 } finally { 1590 closeDevice(id); 1591 } 1592 } 1593 } 1594 1595 @Test testOisDataMode()1596 public void testOisDataMode() throws Exception { 1597 final int NUM_FRAMES_VERIFIED = 3; 1598 1599 for (String id : mCameraIdsUnderTest) { 1600 Log.i(TAG, String.format("Testing Camera %s for OIS mode", id)); 1601 1602 StaticMetadata staticInfo = 1603 new StaticMetadata(mCameraManager.getCameraCharacteristics(id)); 1604 if (!staticInfo.isOisDataModeSupported()) { 1605 continue; 1606 } 1607 1608 openDevice(id); 1609 1610 try { 1611 SurfaceTexture preview = new SurfaceTexture(/*random int*/ 1); 1612 Surface previewSurface = new Surface(preview); 1613 1614 CaptureRequest.Builder previewRequest = preparePreviewTestSession(preview); 1615 SimpleCaptureCallback previewListener = new CameraTestUtils.SimpleCaptureCallback(); 1616 1617 int[] availableOisDataModes = staticInfo.getCharacteristics().get( 1618 CameraCharacteristics.STATISTICS_INFO_AVAILABLE_OIS_DATA_MODES); 1619 1620 // Test each OIS data mode 1621 for (int oisMode : availableOisDataModes) { 1622 previewRequest.set(CaptureRequest.STATISTICS_OIS_DATA_MODE, oisMode); 1623 1624 int sequenceId = mCameraSession.setRepeatingRequest(previewRequest.build(), 1625 previewListener, mHandler); 1626 1627 // Check OIS data in each mode. 1628 for (int i = 0; i < NUM_FRAMES_VERIFIED; i++) { 1629 TotalCaptureResult result = 1630 previewListener.getTotalCaptureResult(CAPTURE_TIMEOUT); 1631 1632 OisSample[] oisSamples = result.get(CaptureResult.STATISTICS_OIS_SAMPLES); 1633 1634 if (oisMode == CameraCharacteristics.STATISTICS_OIS_DATA_MODE_OFF) { 1635 mCollector.expectKeyValueEquals(result, 1636 CaptureResult.STATISTICS_OIS_DATA_MODE, 1637 CaptureResult.STATISTICS_OIS_DATA_MODE_OFF); 1638 mCollector.expectTrue("OIS samples reported in OIS_DATA_MODE_OFF", 1639 oisSamples == null || oisSamples.length == 0); 1640 1641 } else if (oisMode == CameraCharacteristics.STATISTICS_OIS_DATA_MODE_ON) { 1642 mCollector.expectKeyValueEquals(result, 1643 CaptureResult.STATISTICS_OIS_DATA_MODE, 1644 CaptureResult.STATISTICS_OIS_DATA_MODE_ON); 1645 mCollector.expectTrue("OIS samples not reported in OIS_DATA_MODE_ON", 1646 oisSamples != null && oisSamples.length != 0); 1647 } else { 1648 mCollector.addMessage(String.format("Invalid OIS mode: %d", oisMode)); 1649 } 1650 } 1651 1652 mCameraSession.stopRepeating(); 1653 previewListener.getCaptureSequenceLastFrameNumber(sequenceId, CAPTURE_TIMEOUT); 1654 previewListener.drain(); 1655 } 1656 } finally { 1657 closeDevice(id); 1658 } 1659 } 1660 } 1661 preparePreviewTestSession(SurfaceTexture preview)1662 private CaptureRequest.Builder preparePreviewTestSession(SurfaceTexture preview) 1663 throws Exception { 1664 Surface previewSurface = new Surface(preview); 1665 1666 preview.setDefaultBufferSize(640, 480); 1667 1668 ArrayList<Surface> sessionOutputs = new ArrayList<>(); 1669 sessionOutputs.add(previewSurface); 1670 1671 createSession(sessionOutputs); 1672 1673 CaptureRequest.Builder previewRequest = 1674 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); 1675 1676 previewRequest.addTarget(previewSurface); 1677 1678 return previewRequest; 1679 } 1680 prepareTriggerTestSession( SurfaceTexture preview, int aeMode, int afMode)1681 private CaptureRequest.Builder prepareTriggerTestSession( 1682 SurfaceTexture preview, int aeMode, int afMode) throws Exception { 1683 Log.i(TAG, String.format("Testing AE mode %s, AF mode %s", 1684 StaticMetadata.getAeModeName(aeMode), 1685 StaticMetadata.getAfModeName(afMode))); 1686 1687 CaptureRequest.Builder previewRequest = preparePreviewTestSession(preview); 1688 previewRequest.set(CaptureRequest.CONTROL_AE_MODE, aeMode); 1689 previewRequest.set(CaptureRequest.CONTROL_AF_MODE, afMode); 1690 1691 return previewRequest; 1692 } 1693 cancelTriggersAndWait(CaptureRequest.Builder previewRequest, SimpleCaptureCallback captureListener, int afMode)1694 private void cancelTriggersAndWait(CaptureRequest.Builder previewRequest, 1695 SimpleCaptureCallback captureListener, int afMode) throws Exception { 1696 previewRequest.set(CaptureRequest.CONTROL_AF_TRIGGER, 1697 CaptureRequest.CONTROL_AF_TRIGGER_CANCEL); 1698 previewRequest.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, 1699 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_CANCEL); 1700 1701 CaptureRequest triggerRequest = previewRequest.build(); 1702 mCameraSession.capture(triggerRequest, captureListener, mHandler); 1703 1704 // Wait for a few frames to initialize 3A 1705 1706 CaptureResult previewResult = null; 1707 int afState; 1708 int aeState; 1709 1710 for (int i = 0; i < PREVIEW_WARMUP_FRAMES; i++) { 1711 previewResult = captureListener.getCaptureResult( 1712 CameraTestUtils.CAPTURE_RESULT_TIMEOUT_MS); 1713 if (VERBOSE) { 1714 afState = previewResult.get(CaptureResult.CONTROL_AF_STATE); 1715 aeState = previewResult.get(CaptureResult.CONTROL_AE_STATE); 1716 Log.v(TAG, String.format("AF state: %s, AE state: %s", 1717 StaticMetadata.AF_STATE_NAMES[afState], 1718 StaticMetadata.AE_STATE_NAMES[aeState])); 1719 } 1720 } 1721 1722 // Verify starting states 1723 1724 afState = previewResult.get(CaptureResult.CONTROL_AF_STATE); 1725 aeState = previewResult.get(CaptureResult.CONTROL_AE_STATE); 1726 1727 verifyStartingAfState(afMode, afState); 1728 1729 // After several frames, AE must no longer be in INACTIVE state 1730 assertTrue(String.format("AE state must be SEARCHING, CONVERGED, " + 1731 "or FLASH_REQUIRED, is %s", StaticMetadata.AE_STATE_NAMES[aeState]), 1732 aeState == CaptureResult.CONTROL_AE_STATE_SEARCHING || 1733 aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED || 1734 aeState == CaptureResult.CONTROL_AE_STATE_FLASH_REQUIRED); 1735 } 1736 verifyBasicSensorPixelModes(String id, StreamConfigurationMap configs, boolean maxResolution)1737 private void verifyBasicSensorPixelModes(String id, StreamConfigurationMap configs, 1738 boolean maxResolution) throws Exception { 1739 // Go through StreamConfiguration map, set up OutputConfiguration and add the opposite 1740 // sensorPixelMode. 1741 final int MIN_RESULT_COUNT = 3; 1742 if (!maxResolution) { 1743 assertTrue("Default stream config map must be present for id: " + id, configs != null); 1744 } 1745 if (configs == null) { 1746 Log.i(TAG, "camera id " + id + " has no StreamConfigurationMap for max resolution " + 1747 ", skipping verifyBasicSensorPixelModes"); 1748 return; 1749 } 1750 OutputConfiguration outputConfig = null; 1751 for (int format : configs.getOutputFormats()) { 1752 Size targetSize = CameraTestUtils.getMaxSize(configs.getOutputSizes(format)); 1753 // Create outputConfiguration with this size and format 1754 SimpleImageReaderListener imageListener = new SimpleImageReaderListener(); 1755 SurfaceTexture textureTarget = null; 1756 ImageReader readerTarget = null; 1757 if (format == ImageFormat.PRIVATE) { 1758 textureTarget = new SurfaceTexture(1); 1759 textureTarget.setDefaultBufferSize(targetSize.getWidth(), targetSize.getHeight()); 1760 outputConfig = new OutputConfiguration(new Surface(textureTarget)); 1761 } else { 1762 readerTarget = ImageReader.newInstance(targetSize.getWidth(), 1763 targetSize.getHeight(), format, MIN_RESULT_COUNT); 1764 readerTarget.setOnImageAvailableListener(imageListener, mHandler); 1765 outputConfig = new OutputConfiguration(readerTarget.getSurface()); 1766 } 1767 try { 1768 int invalidSensorPixelMode = 1769 maxResolution ? CameraMetadata.SENSOR_PIXEL_MODE_DEFAULT : 1770 CameraMetadata.SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION; 1771 1772 outputConfig.addSensorPixelModeUsed(invalidSensorPixelMode); 1773 CameraCaptureSession.StateCallback sessionListener = 1774 mock(CameraCaptureSession.StateCallback.class); 1775 List<OutputConfiguration> outputs = new ArrayList<>(); 1776 outputs.add(outputConfig); 1777 CameraCaptureSession session = 1778 CameraTestUtils.configureCameraSessionWithConfig(mCamera, outputs, 1779 sessionListener, mHandler); 1780 1781 verify(sessionListener, timeout(CONFIGURE_TIMEOUT).atLeastOnce()). 1782 onConfigureFailed(any(CameraCaptureSession.class)); 1783 verify(sessionListener, never()).onConfigured(any(CameraCaptureSession.class)); 1784 1785 // Remove the invalid sensor pixel mode, session configuration should succeed 1786 sessionListener = mock(CameraCaptureSession.StateCallback.class); 1787 outputConfig.removeSensorPixelModeUsed(invalidSensorPixelMode); 1788 CameraTestUtils.configureCameraSessionWithConfig(mCamera, outputs, 1789 sessionListener, mHandler); 1790 verify(sessionListener, timeout(CONFIGURE_TIMEOUT).atLeastOnce()). 1791 onConfigured(any(CameraCaptureSession.class)); 1792 verify(sessionListener, never()).onConfigureFailed(any(CameraCaptureSession.class)); 1793 } finally { 1794 if (textureTarget != null) { 1795 textureTarget.release(); 1796 } 1797 1798 if (readerTarget != null) { 1799 readerTarget.close(); 1800 } 1801 } 1802 } 1803 } 1804 verifyStartingAfState(int afMode, int afState)1805 private void verifyStartingAfState(int afMode, int afState) { 1806 switch (afMode) { 1807 case CaptureResult.CONTROL_AF_MODE_AUTO: 1808 case CaptureResult.CONTROL_AF_MODE_MACRO: 1809 assertTrue(String.format("AF state not INACTIVE, is %s", 1810 StaticMetadata.AF_STATE_NAMES[afState]), 1811 afState == CaptureResult.CONTROL_AF_STATE_INACTIVE); 1812 break; 1813 case CaptureResult.CONTROL_AF_MODE_CONTINUOUS_PICTURE: 1814 case CaptureResult.CONTROL_AF_MODE_CONTINUOUS_VIDEO: 1815 // After several frames, AF must no longer be in INACTIVE state 1816 assertTrue(String.format("In AF mode %s, AF state not PASSIVE_SCAN" + 1817 ", PASSIVE_FOCUSED, or PASSIVE_UNFOCUSED, is %s", 1818 StaticMetadata.getAfModeName(afMode), 1819 StaticMetadata.AF_STATE_NAMES[afState]), 1820 afState == CaptureResult.CONTROL_AF_STATE_PASSIVE_SCAN || 1821 afState == CaptureResult.CONTROL_AF_STATE_PASSIVE_FOCUSED || 1822 afState == CaptureResult.CONTROL_AF_STATE_PASSIVE_UNFOCUSED); 1823 break; 1824 default: 1825 fail("unexpected af mode"); 1826 } 1827 } 1828 verifyAfSequence(int afMode, int afState, boolean focusComplete)1829 private boolean verifyAfSequence(int afMode, int afState, boolean focusComplete) { 1830 if (focusComplete) { 1831 assertTrue(String.format("AF Mode %s: Focus lock lost after convergence: AF state: %s", 1832 StaticMetadata.getAfModeName(afMode), 1833 StaticMetadata.AF_STATE_NAMES[afState]), 1834 afState == CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED || 1835 afState ==CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED); 1836 return focusComplete; 1837 } 1838 if (VERBOSE) { 1839 Log.v(TAG, String.format("AF mode: %s, AF state: %s", 1840 StaticMetadata.getAfModeName(afMode), 1841 StaticMetadata.AF_STATE_NAMES[afState])); 1842 } 1843 switch (afMode) { 1844 case CaptureResult.CONTROL_AF_MODE_AUTO: 1845 case CaptureResult.CONTROL_AF_MODE_MACRO: 1846 assertTrue(String.format("AF mode %s: Unexpected AF state %s", 1847 StaticMetadata.getAfModeName(afMode), 1848 StaticMetadata.AF_STATE_NAMES[afState]), 1849 afState == CaptureResult.CONTROL_AF_STATE_ACTIVE_SCAN || 1850 afState == CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED || 1851 afState == CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED); 1852 focusComplete = 1853 (afState != CaptureResult.CONTROL_AF_STATE_ACTIVE_SCAN); 1854 break; 1855 case CaptureResult.CONTROL_AF_MODE_CONTINUOUS_PICTURE: 1856 assertTrue(String.format("AF mode %s: Unexpected AF state %s", 1857 StaticMetadata.getAfModeName(afMode), 1858 StaticMetadata.AF_STATE_NAMES[afState]), 1859 afState == CaptureResult.CONTROL_AF_STATE_PASSIVE_SCAN || 1860 afState == CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED || 1861 afState == CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED); 1862 focusComplete = 1863 (afState != CaptureResult.CONTROL_AF_STATE_PASSIVE_SCAN); 1864 break; 1865 case CaptureResult.CONTROL_AF_MODE_CONTINUOUS_VIDEO: 1866 assertTrue(String.format("AF mode %s: Unexpected AF state %s", 1867 StaticMetadata.getAfModeName(afMode), 1868 StaticMetadata.AF_STATE_NAMES[afState]), 1869 afState == CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED || 1870 afState == CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED); 1871 focusComplete = true; 1872 break; 1873 default: 1874 fail("Unexpected AF mode: " + StaticMetadata.getAfModeName(afMode)); 1875 } 1876 return focusComplete; 1877 } 1878 verifyAeSequence(int aeState, boolean precaptureComplete)1879 private boolean verifyAeSequence(int aeState, boolean precaptureComplete) { 1880 if (precaptureComplete) { 1881 assertTrue("Precapture state seen after convergence", 1882 aeState != CaptureResult.CONTROL_AE_STATE_PRECAPTURE); 1883 return precaptureComplete; 1884 } 1885 if (VERBOSE) { 1886 Log.v(TAG, String.format("AE state: %s", StaticMetadata.AE_STATE_NAMES[aeState])); 1887 } 1888 switch (aeState) { 1889 case CaptureResult.CONTROL_AE_STATE_PRECAPTURE: 1890 // scan still continuing 1891 break; 1892 case CaptureResult.CONTROL_AE_STATE_CONVERGED: 1893 case CaptureResult.CONTROL_AE_STATE_FLASH_REQUIRED: 1894 // completed 1895 precaptureComplete = true; 1896 break; 1897 default: 1898 fail(String.format("Precapture sequence transitioned to " 1899 + "state %s incorrectly!", StaticMetadata.AE_STATE_NAMES[aeState])); 1900 break; 1901 } 1902 return precaptureComplete; 1903 } 1904 1905 /** 1906 * Test for making sure that all expected mandatory stream combinations are present and 1907 * advertised accordingly. 1908 */ 1909 @Test testVerifyMandatoryOutputCombinationTables()1910 public void testVerifyMandatoryOutputCombinationTables() throws Exception { 1911 final int[][] LEGACY_COMBINATIONS = { 1912 // Simple preview, GPU video processing, or no-preview video recording 1913 {PRIV, MAXIMUM}, 1914 // No-viewfinder still image capture 1915 {JPEG, MAXIMUM}, 1916 // In-application video/image processing 1917 {YUV, MAXIMUM}, 1918 // Standard still imaging. 1919 {PRIV, PREVIEW, JPEG, MAXIMUM}, 1920 // In-app processing plus still capture. 1921 {YUV, PREVIEW, JPEG, MAXIMUM}, 1922 // Standard recording. 1923 {PRIV, PREVIEW, PRIV, PREVIEW}, 1924 // Preview plus in-app processing. 1925 {PRIV, PREVIEW, YUV, PREVIEW}, 1926 // Still capture plus in-app processing. 1927 {PRIV, PREVIEW, YUV, PREVIEW, JPEG, MAXIMUM} 1928 }; 1929 1930 final int[][] LIMITED_COMBINATIONS = { 1931 // High-resolution video recording with preview. 1932 {PRIV, PREVIEW, PRIV, RECORD }, 1933 // High-resolution in-app video processing with preview. 1934 {PRIV, PREVIEW, YUV , RECORD }, 1935 // Two-input in-app video processing. 1936 {YUV , PREVIEW, YUV , RECORD }, 1937 // High-resolution recording with video snapshot. 1938 {PRIV, PREVIEW, PRIV, RECORD, JPEG, RECORD }, 1939 // High-resolution in-app processing with video snapshot. 1940 {PRIV, PREVIEW, YUV, RECORD, JPEG, RECORD }, 1941 // Two-input in-app processing with still capture. 1942 {YUV , PREVIEW, YUV, PREVIEW, JPEG, MAXIMUM } 1943 }; 1944 1945 final int[][] BURST_COMBINATIONS = { 1946 // Maximum-resolution GPU processing with preview. 1947 {PRIV, PREVIEW, PRIV, MAXIMUM }, 1948 // Maximum-resolution in-app processing with preview. 1949 {PRIV, PREVIEW, YUV, MAXIMUM }, 1950 // Maximum-resolution two-input in-app processing. 1951 {YUV, PREVIEW, YUV, MAXIMUM }, 1952 }; 1953 1954 final int[][] FULL_COMBINATIONS = { 1955 // Video recording with maximum-size video snapshot. 1956 {PRIV, PREVIEW, PRIV, PREVIEW, JPEG, MAXIMUM }, 1957 // Standard video recording plus maximum-resolution in-app processing. 1958 {YUV, VGA, PRIV, PREVIEW, YUV, MAXIMUM }, 1959 // Preview plus two-input maximum-resolution in-app processing. 1960 {YUV, VGA, YUV, PREVIEW, YUV, MAXIMUM } 1961 }; 1962 1963 final int[][] RAW_COMBINATIONS = { 1964 // No-preview DNG capture. 1965 {RAW, MAXIMUM }, 1966 // Standard DNG capture. 1967 {PRIV, PREVIEW, RAW, MAXIMUM }, 1968 // In-app processing plus DNG capture. 1969 {YUV, PREVIEW, RAW, MAXIMUM }, 1970 // Video recording with DNG capture. 1971 {PRIV, PREVIEW, PRIV, PREVIEW, RAW, MAXIMUM}, 1972 // Preview with in-app processing and DNG capture. 1973 {PRIV, PREVIEW, YUV, PREVIEW, RAW, MAXIMUM}, 1974 // Two-input in-app processing plus DNG capture. 1975 {YUV, PREVIEW, YUV, PREVIEW, RAW, MAXIMUM}, 1976 // Still capture with simultaneous JPEG and DNG. 1977 {PRIV, PREVIEW, JPEG, MAXIMUM, RAW, MAXIMUM}, 1978 // In-app processing with simultaneous JPEG and DNG. 1979 {YUV, PREVIEW, JPEG, MAXIMUM, RAW, MAXIMUM} 1980 }; 1981 1982 final int[][] LEVEL_3_COMBINATIONS = { 1983 // In-app viewfinder analysis with dynamic selection of output format 1984 {PRIV, PREVIEW, PRIV, VGA, YUV, MAXIMUM, RAW, MAXIMUM}, 1985 // In-app viewfinder analysis with dynamic selection of output format 1986 {PRIV, PREVIEW, PRIV, VGA, JPEG, MAXIMUM, RAW, MAXIMUM} 1987 }; 1988 1989 final int[][][] TABLES = 1990 { LEGACY_COMBINATIONS, LIMITED_COMBINATIONS, BURST_COMBINATIONS, FULL_COMBINATIONS, 1991 RAW_COMBINATIONS, LEVEL_3_COMBINATIONS }; 1992 1993 validityCheckConfigurationTables(TABLES); 1994 1995 for (String id : mCameraIdsUnderTest) { 1996 openDevice(id); 1997 MandatoryStreamCombination[] combinations = 1998 mStaticInfo.getCharacteristics().get( 1999 CameraCharacteristics.SCALER_MANDATORY_STREAM_COMBINATIONS); 2000 if ((combinations == null) || (combinations.length == 0)) { 2001 Log.i(TAG, "No mandatory stream combinations for camera: " + id + " skip test"); 2002 closeDevice(id); 2003 continue; 2004 } 2005 2006 MaxStreamSizes maxSizes = new MaxStreamSizes(mStaticInfo, id, mContext); 2007 try { 2008 if (mStaticInfo.isColorOutputSupported()) { 2009 for (int[] c : LEGACY_COMBINATIONS) { 2010 assertTrue(String.format("Expected static stream combination: %s not " + 2011 "found among the available mandatory combinations", 2012 maxSizes.combinationToString(c)), 2013 isMandatoryCombinationAvailable(c, maxSizes, combinations)); 2014 } 2015 } 2016 2017 if (!mStaticInfo.isHardwareLevelLegacy()) { 2018 if (mStaticInfo.isColorOutputSupported()) { 2019 for (int[] c : LIMITED_COMBINATIONS) { 2020 assertTrue(String.format("Expected static stream combination: %s not " + 2021 "found among the available mandatory combinations", 2022 maxSizes.combinationToString(c)), 2023 isMandatoryCombinationAvailable(c, maxSizes, combinations)); 2024 } 2025 } 2026 2027 if (mStaticInfo.isCapabilitySupported( 2028 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE)) { 2029 for (int[] c : BURST_COMBINATIONS) { 2030 assertTrue(String.format("Expected static stream combination: %s not " + 2031 "found among the available mandatory combinations", 2032 maxSizes.combinationToString(c)), 2033 isMandatoryCombinationAvailable(c, maxSizes, combinations)); 2034 } 2035 } 2036 2037 if (mStaticInfo.isHardwareLevelAtLeastFull()) { 2038 for (int[] c : FULL_COMBINATIONS) { 2039 assertTrue(String.format("Expected static stream combination: %s not " + 2040 "found among the available mandatory combinations", 2041 maxSizes.combinationToString(c)), 2042 isMandatoryCombinationAvailable(c, maxSizes, combinations)); 2043 } 2044 } 2045 2046 if (mStaticInfo.isCapabilitySupported( 2047 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) { 2048 for (int[] c : RAW_COMBINATIONS) { 2049 assertTrue(String.format("Expected static stream combination: %s not " + 2050 "found among the available mandatory combinations", 2051 maxSizes.combinationToString(c)), 2052 isMandatoryCombinationAvailable(c, maxSizes, combinations)); 2053 } 2054 } 2055 2056 if (mStaticInfo.isHardwareLevelAtLeast( 2057 CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3)) { 2058 for (int[] c: LEVEL_3_COMBINATIONS) { 2059 assertTrue(String.format("Expected static stream combination: %s not " + 2060 "found among the available mandatory combinations", 2061 maxSizes.combinationToString(c)), 2062 isMandatoryCombinationAvailable(c, maxSizes, combinations)); 2063 } 2064 } 2065 } 2066 } finally { 2067 closeDevice(id); 2068 } 2069 } 2070 } 2071 2072 /** 2073 * Test for making sure that all expected reprocessable mandatory stream combinations are 2074 * present and advertised accordingly. 2075 */ 2076 @Test testVerifyReprocessMandatoryOutputCombinationTables()2077 public void testVerifyReprocessMandatoryOutputCombinationTables() throws Exception { 2078 final int[][] LIMITED_COMBINATIONS = { 2079 // Input Outputs 2080 {PRIV, MAXIMUM, JPEG, MAXIMUM}, 2081 {YUV , MAXIMUM, JPEG, MAXIMUM}, 2082 {PRIV, MAXIMUM, PRIV, PREVIEW, JPEG, MAXIMUM}, 2083 {YUV , MAXIMUM, PRIV, PREVIEW, JPEG, MAXIMUM}, 2084 {PRIV, MAXIMUM, YUV , PREVIEW, JPEG, MAXIMUM}, 2085 {YUV , MAXIMUM, YUV , PREVIEW, JPEG, MAXIMUM}, 2086 {PRIV, MAXIMUM, YUV , PREVIEW, YUV , PREVIEW, JPEG, MAXIMUM}, 2087 {YUV, MAXIMUM, YUV , PREVIEW, YUV , PREVIEW, JPEG, MAXIMUM}, 2088 }; 2089 2090 final int[][] FULL_COMBINATIONS = { 2091 // Input Outputs 2092 {YUV , MAXIMUM, PRIV, PREVIEW}, 2093 {YUV , MAXIMUM, YUV , PREVIEW}, 2094 {PRIV, MAXIMUM, PRIV, PREVIEW, YUV , RECORD}, 2095 {YUV , MAXIMUM, PRIV, PREVIEW, YUV , RECORD}, 2096 {PRIV, MAXIMUM, PRIV, PREVIEW, YUV , MAXIMUM}, 2097 {PRIV, MAXIMUM, YUV , PREVIEW, YUV , MAXIMUM}, 2098 {PRIV, MAXIMUM, PRIV, PREVIEW, YUV , PREVIEW, JPEG, MAXIMUM}, 2099 {YUV , MAXIMUM, PRIV, PREVIEW, YUV , PREVIEW, JPEG, MAXIMUM}, 2100 }; 2101 2102 final int[][] RAW_COMBINATIONS = { 2103 // Input Outputs 2104 {PRIV, MAXIMUM, YUV , PREVIEW, RAW , MAXIMUM}, 2105 {YUV , MAXIMUM, YUV , PREVIEW, RAW , MAXIMUM}, 2106 {PRIV, MAXIMUM, PRIV, PREVIEW, YUV , PREVIEW, RAW , MAXIMUM}, 2107 {YUV , MAXIMUM, PRIV, PREVIEW, YUV , PREVIEW, RAW , MAXIMUM}, 2108 {PRIV, MAXIMUM, YUV , PREVIEW, YUV , PREVIEW, RAW , MAXIMUM}, 2109 {YUV , MAXIMUM, YUV , PREVIEW, YUV , PREVIEW, RAW , MAXIMUM}, 2110 {PRIV, MAXIMUM, PRIV, PREVIEW, JPEG, MAXIMUM, RAW , MAXIMUM}, 2111 {YUV , MAXIMUM, PRIV, PREVIEW, JPEG, MAXIMUM, RAW , MAXIMUM}, 2112 {PRIV, MAXIMUM, YUV , PREVIEW, JPEG, MAXIMUM, RAW , MAXIMUM}, 2113 {YUV , MAXIMUM, YUV , PREVIEW, JPEG, MAXIMUM, RAW , MAXIMUM}, 2114 }; 2115 2116 final int[][] LEVEL_3_COMBINATIONS = { 2117 // Input Outputs 2118 // In-app viewfinder analysis with YUV->YUV ZSL and RAW 2119 {YUV , MAXIMUM, PRIV, PREVIEW, PRIV, VGA, RAW, MAXIMUM}, 2120 // In-app viewfinder analysis with PRIV->JPEG ZSL and RAW 2121 {PRIV, MAXIMUM, PRIV, PREVIEW, PRIV, VGA, RAW, MAXIMUM, JPEG, MAXIMUM}, 2122 // In-app viewfinder analysis with YUV->JPEG ZSL and RAW 2123 {YUV , MAXIMUM, PRIV, PREVIEW, PRIV, VGA, RAW, MAXIMUM, JPEG, MAXIMUM}, 2124 }; 2125 2126 final int[][][] TABLES = 2127 { LIMITED_COMBINATIONS, FULL_COMBINATIONS, RAW_COMBINATIONS, LEVEL_3_COMBINATIONS }; 2128 2129 validityCheckConfigurationTables(TABLES); 2130 2131 for (String id : mCameraIdsUnderTest) { 2132 openDevice(id); 2133 MandatoryStreamCombination[] cs = mStaticInfo.getCharacteristics().get( 2134 CameraCharacteristics.SCALER_MANDATORY_STREAM_COMBINATIONS); 2135 if ((cs == null) || (cs.length == 0)) { 2136 Log.i(TAG, "No mandatory stream combinations for camera: " + id + " skip test"); 2137 closeDevice(id); 2138 continue; 2139 } 2140 2141 boolean supportYuvReprocess = mStaticInfo.isCapabilitySupported( 2142 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING); 2143 boolean supportOpaqueReprocess = mStaticInfo.isCapabilitySupported( 2144 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING); 2145 if (!supportYuvReprocess && !supportOpaqueReprocess) { 2146 Log.i(TAG, "No reprocess support for camera: " + id + " skip test"); 2147 closeDevice(id); 2148 continue; 2149 } 2150 2151 MaxStreamSizes maxSizes = new MaxStreamSizes(mStaticInfo, id, mContext); 2152 try { 2153 for (int[] c : LIMITED_COMBINATIONS) { 2154 assertTrue(String.format("Expected static reprocessable stream combination:" + 2155 "%s not found among the available mandatory combinations", 2156 maxSizes.reprocessCombinationToString(c)), 2157 isMandatoryCombinationAvailable(c, maxSizes, /*isInput*/ true, cs)); 2158 } 2159 2160 if (mStaticInfo.isHardwareLevelAtLeastFull()) { 2161 for (int[] c : FULL_COMBINATIONS) { 2162 assertTrue(String.format( 2163 "Expected static reprocessable stream combination:" + 2164 "%s not found among the available mandatory combinations", 2165 maxSizes.reprocessCombinationToString(c)), 2166 isMandatoryCombinationAvailable(c, maxSizes, /*isInput*/ true, cs)); 2167 } 2168 } 2169 2170 if (mStaticInfo.isCapabilitySupported( 2171 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) { 2172 for (int[] c : RAW_COMBINATIONS) { 2173 assertTrue(String.format( 2174 "Expected static reprocessable stream combination:" + 2175 "%s not found among the available mandatory combinations", 2176 maxSizes.reprocessCombinationToString(c)), 2177 isMandatoryCombinationAvailable(c, maxSizes, /*isInput*/ true, cs)); 2178 } 2179 } 2180 2181 if (mStaticInfo.isHardwareLevelAtLeast( 2182 CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3)) { 2183 for (int[] c : LEVEL_3_COMBINATIONS) { 2184 assertTrue(String.format( 2185 "Expected static reprocessable stream combination:" + 2186 "%s not found among the available mandatory combinations", 2187 maxSizes.reprocessCombinationToString(c)), 2188 isMandatoryCombinationAvailable(c, maxSizes, /*isInput*/ true, cs)); 2189 } 2190 } 2191 } finally { 2192 closeDevice(id); 2193 } 2194 } 2195 } 2196 isMandatoryCombinationAvailable(final int[] combination, final MaxStreamSizes maxSizes, final MandatoryStreamCombination[] availableCombinations)2197 private boolean isMandatoryCombinationAvailable(final int[] combination, 2198 final MaxStreamSizes maxSizes, 2199 final MandatoryStreamCombination[] availableCombinations) { 2200 return isMandatoryCombinationAvailable(combination, maxSizes, /*isInput*/ false, 2201 availableCombinations); 2202 } 2203 isMandatoryCombinationAvailable(final int[] combination, final MaxStreamSizes maxSizes, boolean isInput, final MandatoryStreamCombination[] availableCombinations)2204 private boolean isMandatoryCombinationAvailable(final int[] combination, 2205 final MaxStreamSizes maxSizes, boolean isInput, 2206 final MandatoryStreamCombination[] availableCombinations) { 2207 boolean supportYuvReprocess = mStaticInfo.isCapabilitySupported( 2208 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING); 2209 boolean supportOpaqueReprocess = mStaticInfo.isCapabilitySupported( 2210 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING); 2211 // Static combinations to be verified can be composed of multiple entries 2212 // that have the following layout (format, size). In case "isInput" is set, 2213 // the first stream configuration entry will contain the input format and size 2214 // as well as the first matching output. 2215 int streamCount = combination.length / 2; 2216 2217 List<Pair<Pair<Integer, Boolean>, Size>> currentCombination = 2218 new ArrayList<Pair<Pair<Integer, Boolean>, Size>>(streamCount); 2219 for (int i = 0; i < combination.length; i += 2) { 2220 if (isInput && (i == 0)) { 2221 // Skip the combination if the format is not supported for reprocessing. 2222 if ((combination[i] == YUV && !supportYuvReprocess) || 2223 (combination[i] == PRIV && !supportOpaqueReprocess)) { 2224 return true; 2225 } 2226 Size sz = maxSizes.getMaxInputSizeForFormat(combination[i]); 2227 currentCombination.add(Pair.create(Pair.create(new Integer(combination[i]), 2228 new Boolean(true)), sz)); 2229 currentCombination.add(Pair.create(Pair.create(new Integer(combination[i]), 2230 new Boolean(false)), sz)); 2231 } else { 2232 Size sz = maxSizes.getOutputSizeForFormat(combination[i], combination[i+1]); 2233 currentCombination.add(Pair.create(Pair.create(new Integer(combination[i]), 2234 new Boolean(false)), sz)); 2235 } 2236 } 2237 2238 for (MandatoryStreamCombination c : availableCombinations) { 2239 List<MandatoryStreamInformation> streamInfoList = c.getStreamsInformation(); 2240 if ((streamInfoList.size() == currentCombination.size()) && 2241 (isInput == c.isReprocessable())) { 2242 ArrayList<Pair<Pair<Integer, Boolean>, Size>> expected = 2243 new ArrayList<Pair<Pair<Integer, Boolean>, Size>>(currentCombination); 2244 2245 for (MandatoryStreamInformation streamInfo : streamInfoList) { 2246 Size maxSize = CameraTestUtils.getMaxSize( 2247 streamInfo.getAvailableSizes().toArray(new Size[0])); 2248 Pair p = Pair.create(Pair.create(new Integer(streamInfo.getFormat()), 2249 new Boolean(streamInfo.isInput())), maxSize); 2250 if (expected.contains(p)) { 2251 expected.remove(p); 2252 } 2253 } 2254 2255 if (expected.isEmpty()) { 2256 return true; 2257 } 2258 } 2259 } 2260 2261 return false; 2262 } 2263 2264 /** 2265 * Verify correctness of the configuration tables. 2266 */ validityCheckConfigurationTables(final int[][][] tables)2267 private void validityCheckConfigurationTables(final int[][][] tables) throws Exception { 2268 int tableIdx = 0; 2269 for (int[][] table : tables) { 2270 int rowIdx = 0; 2271 for (int[] row : table) { 2272 assertTrue(String.format("Odd number of entries for table %d row %d: %s ", 2273 tableIdx, rowIdx, Arrays.toString(row)), 2274 (row.length % 2) == 0); 2275 for (int i = 0; i < row.length; i += 2) { 2276 int format = row[i]; 2277 int maxSize = row[i + 1]; 2278 assertTrue(String.format("table %d row %d index %d format not valid: %d", 2279 tableIdx, rowIdx, i, format), 2280 format == PRIV || format == JPEG || format == YUV || format == RAW); 2281 assertTrue(String.format("table %d row %d index %d max size not valid: %d", 2282 tableIdx, rowIdx, i + 1, maxSize), 2283 maxSize == PREVIEW || maxSize == RECORD || 2284 maxSize == MAXIMUM || maxSize == VGA); 2285 } 2286 rowIdx++; 2287 } 2288 tableIdx++; 2289 } 2290 } 2291 2292 /** 2293 * Simple holder for resolutions to use for different camera outputs and size limits. 2294 */ 2295 static class MaxStreamSizes { 2296 // Format shorthands 2297 static final int PRIV = ImageFormat.PRIVATE; 2298 static final int JPEG = ImageFormat.JPEG; 2299 static final int YUV = ImageFormat.YUV_420_888; 2300 static final int RAW = ImageFormat.RAW_SENSOR; 2301 static final int Y8 = ImageFormat.Y8; 2302 static final int HEIC = ImageFormat.HEIC; 2303 2304 // Max resolution indices 2305 static final int PREVIEW = 0; 2306 static final int RECORD = 1; 2307 static final int MAXIMUM = 2; 2308 static final int VGA = 3; 2309 static final int VGA_FULL_FOV = 4; 2310 static final int MAX_30FPS = 5; 2311 static final int RESOLUTION_COUNT = 6; 2312 2313 static final long FRAME_DURATION_30FPS_NSEC = (long) 1e9 / 30; 2314 MaxStreamSizes(StaticMetadata sm, String cameraId, Context context)2315 public MaxStreamSizes(StaticMetadata sm, String cameraId, Context context) { 2316 Size[] privSizes = sm.getAvailableSizesForFormatChecked(ImageFormat.PRIVATE, 2317 StaticMetadata.StreamDirection.Output, /*fastSizes*/true, /*slowSizes*/false); 2318 Size[] yuvSizes = sm.getAvailableSizesForFormatChecked(ImageFormat.YUV_420_888, 2319 StaticMetadata.StreamDirection.Output, /*fastSizes*/true, /*slowSizes*/false); 2320 2321 Size[] y8Sizes = sm.getAvailableSizesForFormatChecked(ImageFormat.Y8, 2322 StaticMetadata.StreamDirection.Output, /*fastSizes*/true, /*slowSizes*/false); 2323 Size[] jpegSizes = sm.getAvailableSizesForFormatChecked(ImageFormat.JPEG, 2324 StaticMetadata.StreamDirection.Output, /*fastSizes*/true, /*slowSizes*/false); 2325 Size[] rawSizes = sm.getAvailableSizesForFormatChecked(ImageFormat.RAW_SENSOR, 2326 StaticMetadata.StreamDirection.Output, /*fastSizes*/true, /*slowSizes*/false); 2327 Size[] heicSizes = sm.getAvailableSizesForFormatChecked(ImageFormat.HEIC, 2328 StaticMetadata.StreamDirection.Output, /*fastSizes*/true, /*slowSizes*/false); 2329 2330 Size maxPreviewSize = getMaxPreviewSize(context, cameraId); 2331 2332 maxRawSize = (rawSizes.length != 0) ? CameraTestUtils.getMaxSize(rawSizes) : null; 2333 2334 StreamConfigurationMap configs = sm.getCharacteristics().get( 2335 CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); 2336 if (sm.isColorOutputSupported()) { 2337 maxPrivSizes[PREVIEW] = getMaxSize(privSizes, maxPreviewSize); 2338 maxYuvSizes[PREVIEW] = getMaxSize(yuvSizes, maxPreviewSize); 2339 maxJpegSizes[PREVIEW] = getMaxSize(jpegSizes, maxPreviewSize); 2340 2341 if (sm.isExternalCamera()) { 2342 maxPrivSizes[RECORD] = getMaxExternalRecordingSize(cameraId, configs); 2343 maxYuvSizes[RECORD] = getMaxExternalRecordingSize(cameraId, configs); 2344 maxJpegSizes[RECORD] = getMaxExternalRecordingSize(cameraId, configs); 2345 } else { 2346 maxPrivSizes[RECORD] = getMaxRecordingSize(cameraId); 2347 maxYuvSizes[RECORD] = getMaxRecordingSize(cameraId); 2348 maxJpegSizes[RECORD] = getMaxRecordingSize(cameraId); 2349 } 2350 2351 maxPrivSizes[MAXIMUM] = CameraTestUtils.getMaxSize(privSizes); 2352 maxYuvSizes[MAXIMUM] = CameraTestUtils.getMaxSize(yuvSizes); 2353 maxJpegSizes[MAXIMUM] = CameraTestUtils.getMaxSize(jpegSizes); 2354 2355 // Must always be supported, add unconditionally 2356 final Size vgaSize = new Size(640, 480); 2357 maxPrivSizes[VGA] = vgaSize; 2358 maxYuvSizes[VGA] = vgaSize; 2359 maxJpegSizes[VGA] = vgaSize; 2360 2361 if (sm.isMonochromeWithY8()) { 2362 maxY8Sizes[PREVIEW] = getMaxSize(y8Sizes, maxPreviewSize); 2363 if (sm.isExternalCamera()) { 2364 maxY8Sizes[RECORD] = getMaxExternalRecordingSize(cameraId, configs); 2365 } else { 2366 maxY8Sizes[RECORD] = getMaxRecordingSize(cameraId); 2367 } 2368 maxY8Sizes[MAXIMUM] = CameraTestUtils.getMaxSize(y8Sizes); 2369 maxY8Sizes[VGA] = vgaSize; 2370 } 2371 2372 if (sm.isHeicSupported()) { 2373 maxHeicSizes[PREVIEW] = getMaxSize(heicSizes, maxPreviewSize); 2374 maxHeicSizes[RECORD] = getMaxRecordingSize(cameraId); 2375 maxHeicSizes[MAXIMUM] = CameraTestUtils.getMaxSize(heicSizes); 2376 maxHeicSizes[VGA] = vgaSize; 2377 } 2378 } 2379 if (sm.isColorOutputSupported() && !sm.isHardwareLevelLegacy()) { 2380 // VGA resolution, but with aspect ratio matching full res FOV 2381 float fullFovAspect = maxYuvSizes[MAXIMUM].getWidth() / 2382 (float) maxYuvSizes[MAXIMUM].getHeight(); 2383 Size vgaFullFovSize = new Size(640, (int) (640 / fullFovAspect)); 2384 2385 maxPrivSizes[VGA_FULL_FOV] = vgaFullFovSize; 2386 maxYuvSizes[VGA_FULL_FOV] = vgaFullFovSize; 2387 maxJpegSizes[VGA_FULL_FOV] = vgaFullFovSize; 2388 if (sm.isMonochromeWithY8()) { 2389 maxY8Sizes[VGA_FULL_FOV] = vgaFullFovSize; 2390 } 2391 2392 // Max resolution that runs at 30fps 2393 2394 Size maxPriv30fpsSize = null; 2395 Size maxYuv30fpsSize = null; 2396 Size maxY830fpsSize = null; 2397 Size maxJpeg30fpsSize = null; 2398 Comparator<Size> comparator = new SizeComparator(); 2399 for (Map.Entry<Size, Long> e : 2400 sm.getAvailableMinFrameDurationsForFormatChecked(ImageFormat.PRIVATE). 2401 entrySet()) { 2402 Size s = e.getKey(); 2403 Long minDuration = e.getValue(); 2404 Log.d(TAG, String.format("Priv Size: %s, duration %d limit %d", s, minDuration, 2405 FRAME_DURATION_30FPS_NSEC)); 2406 if (minDuration <= FRAME_DURATION_30FPS_NSEC) { 2407 if (maxPriv30fpsSize == null || 2408 comparator.compare(maxPriv30fpsSize, s) < 0) { 2409 maxPriv30fpsSize = s; 2410 } 2411 } 2412 } 2413 assertTrue("No PRIVATE resolution available at 30fps!", maxPriv30fpsSize != null); 2414 2415 for (Map.Entry<Size, Long> e : 2416 sm.getAvailableMinFrameDurationsForFormatChecked( 2417 ImageFormat.YUV_420_888). 2418 entrySet()) { 2419 Size s = e.getKey(); 2420 Long minDuration = e.getValue(); 2421 Log.d(TAG, String.format("YUV Size: %s, duration %d limit %d", s, minDuration, 2422 FRAME_DURATION_30FPS_NSEC)); 2423 if (minDuration <= FRAME_DURATION_30FPS_NSEC) { 2424 if (maxYuv30fpsSize == null || 2425 comparator.compare(maxYuv30fpsSize, s) < 0) { 2426 maxYuv30fpsSize = s; 2427 } 2428 } 2429 } 2430 assertTrue("No YUV_420_888 resolution available at 30fps!", 2431 maxYuv30fpsSize != null); 2432 2433 if (sm.isMonochromeWithY8()) { 2434 for (Map.Entry<Size, Long> e : 2435 sm.getAvailableMinFrameDurationsForFormatChecked( 2436 ImageFormat.Y8). 2437 entrySet()) { 2438 Size s = e.getKey(); 2439 Long minDuration = e.getValue(); 2440 Log.d(TAG, String.format("Y8 Size: %s, duration %d limit %d", 2441 s, minDuration, FRAME_DURATION_30FPS_NSEC)); 2442 if (minDuration <= FRAME_DURATION_30FPS_NSEC) { 2443 if (maxY830fpsSize == null || 2444 comparator.compare(maxY830fpsSize, s) < 0) { 2445 maxY830fpsSize = s; 2446 } 2447 } 2448 } 2449 assertTrue("No Y8 resolution available at 30fps!", maxY830fpsSize != null); 2450 } 2451 2452 for (Map.Entry<Size, Long> e : 2453 sm.getAvailableMinFrameDurationsForFormatChecked(ImageFormat.JPEG). 2454 entrySet()) { 2455 Size s = e.getKey(); 2456 Long minDuration = e.getValue(); 2457 Log.d(TAG, String.format("JPEG Size: %s, duration %d limit %d", s, minDuration, 2458 FRAME_DURATION_30FPS_NSEC)); 2459 if (minDuration <= FRAME_DURATION_30FPS_NSEC) { 2460 if (maxJpeg30fpsSize == null || 2461 comparator.compare(maxJpeg30fpsSize, s) < 0) { 2462 maxJpeg30fpsSize = s; 2463 } 2464 } 2465 } 2466 assertTrue("No JPEG resolution available at 30fps!", maxJpeg30fpsSize != null); 2467 2468 maxPrivSizes[MAX_30FPS] = maxPriv30fpsSize; 2469 maxYuvSizes[MAX_30FPS] = maxYuv30fpsSize; 2470 maxY8Sizes[MAX_30FPS] = maxY830fpsSize; 2471 maxJpegSizes[MAX_30FPS] = maxJpeg30fpsSize; 2472 } 2473 2474 Size[] privInputSizes = configs.getInputSizes(ImageFormat.PRIVATE); 2475 maxInputPrivSize = privInputSizes != null ? 2476 CameraTestUtils.getMaxSize(privInputSizes) : null; 2477 Size[] yuvInputSizes = configs.getInputSizes(ImageFormat.YUV_420_888); 2478 maxInputYuvSize = yuvInputSizes != null ? 2479 CameraTestUtils.getMaxSize(yuvInputSizes) : null; 2480 Size[] y8InputSizes = configs.getInputSizes(ImageFormat.Y8); 2481 maxInputY8Size = y8InputSizes != null ? 2482 CameraTestUtils.getMaxSize(y8InputSizes) : null; 2483 } 2484 2485 private final Size[] maxPrivSizes = new Size[RESOLUTION_COUNT]; 2486 private final Size[] maxJpegSizes = new Size[RESOLUTION_COUNT]; 2487 private final Size[] maxYuvSizes = new Size[RESOLUTION_COUNT]; 2488 private final Size[] maxY8Sizes = new Size[RESOLUTION_COUNT]; 2489 private final Size[] maxHeicSizes = new Size[RESOLUTION_COUNT]; 2490 private final Size maxRawSize; 2491 // TODO: support non maximum reprocess input. 2492 private final Size maxInputPrivSize; 2493 private final Size maxInputYuvSize; 2494 private final Size maxInputY8Size; 2495 getOutputSizeForFormat(int format, int resolutionIndex)2496 public final Size getOutputSizeForFormat(int format, int resolutionIndex) { 2497 if (resolutionIndex >= RESOLUTION_COUNT) { 2498 return new Size(0, 0); 2499 } 2500 2501 switch (format) { 2502 case PRIV: 2503 return maxPrivSizes[resolutionIndex]; 2504 case YUV: 2505 return maxYuvSizes[resolutionIndex]; 2506 case JPEG: 2507 return maxJpegSizes[resolutionIndex]; 2508 case Y8: 2509 return maxY8Sizes[resolutionIndex]; 2510 case HEIC: 2511 return maxHeicSizes[resolutionIndex]; 2512 case RAW: 2513 return maxRawSize; 2514 default: 2515 return new Size(0, 0); 2516 } 2517 } 2518 getMaxInputSizeForFormat(int format)2519 public final Size getMaxInputSizeForFormat(int format) { 2520 switch (format) { 2521 case PRIV: 2522 return maxInputPrivSize; 2523 case YUV: 2524 return maxInputYuvSize; 2525 case Y8: 2526 return maxInputY8Size; 2527 default: 2528 return new Size(0, 0); 2529 } 2530 } 2531 combinationToString(int[] combination)2532 static public String combinationToString(int[] combination) { 2533 StringBuilder b = new StringBuilder("{ "); 2534 for (int i = 0; i < combination.length; i += 2) { 2535 int format = combination[i]; 2536 int sizeLimit = combination[i + 1]; 2537 2538 appendFormatSize(b, format, sizeLimit); 2539 b.append(" "); 2540 } 2541 b.append("}"); 2542 return b.toString(); 2543 } 2544 reprocessCombinationToString(int[] reprocessCombination)2545 static public String reprocessCombinationToString(int[] reprocessCombination) { 2546 // reprocessConfig[0..1] is the input configuration 2547 StringBuilder b = new StringBuilder("Input: "); 2548 appendFormatSize(b, reprocessCombination[0], reprocessCombination[1]); 2549 2550 // reprocessCombnation[0..1] is also output combination to be captured as reprocess 2551 // input. 2552 b.append(", Outputs: { "); 2553 for (int i = 0; i < reprocessCombination.length; i += 2) { 2554 int format = reprocessCombination[i]; 2555 int sizeLimit = reprocessCombination[i + 1]; 2556 2557 appendFormatSize(b, format, sizeLimit); 2558 b.append(" "); 2559 } 2560 b.append("}"); 2561 return b.toString(); 2562 } 2563 appendFormatSize(StringBuilder b, int format, int Size)2564 static private void appendFormatSize(StringBuilder b, int format, int Size) { 2565 switch (format) { 2566 case PRIV: 2567 b.append("[PRIV, "); 2568 break; 2569 case JPEG: 2570 b.append("[JPEG, "); 2571 break; 2572 case YUV: 2573 b.append("[YUV, "); 2574 break; 2575 case Y8: 2576 b.append("[Y8, "); 2577 break; 2578 case RAW: 2579 b.append("[RAW, "); 2580 break; 2581 default: 2582 b.append("[UNK, "); 2583 break; 2584 } 2585 2586 switch (Size) { 2587 case PREVIEW: 2588 b.append("PREVIEW]"); 2589 break; 2590 case RECORD: 2591 b.append("RECORD]"); 2592 break; 2593 case MAXIMUM: 2594 b.append("MAXIMUM]"); 2595 break; 2596 case VGA: 2597 b.append("VGA]"); 2598 break; 2599 case VGA_FULL_FOV: 2600 b.append("VGA_FULL_FOV]"); 2601 break; 2602 case MAX_30FPS: 2603 b.append("MAX_30FPS]"); 2604 break; 2605 default: 2606 b.append("UNK]"); 2607 break; 2608 } 2609 } 2610 } 2611 getMaxRecordingSize(String cameraId)2612 private static Size getMaxRecordingSize(String cameraId) { 2613 int id = Integer.valueOf(cameraId); 2614 2615 int quality = 2616 CamcorderProfile.hasProfile(id, CamcorderProfile.QUALITY_2160P) ? 2617 CamcorderProfile.QUALITY_2160P : 2618 CamcorderProfile.hasProfile(id, CamcorderProfile.QUALITY_1080P) ? 2619 CamcorderProfile.QUALITY_1080P : 2620 CamcorderProfile.hasProfile(id, CamcorderProfile.QUALITY_720P) ? 2621 CamcorderProfile.QUALITY_720P : 2622 CamcorderProfile.hasProfile(id, CamcorderProfile.QUALITY_480P) ? 2623 CamcorderProfile.QUALITY_480P : 2624 CamcorderProfile.hasProfile(id, CamcorderProfile.QUALITY_QVGA) ? 2625 CamcorderProfile.QUALITY_QVGA : 2626 CamcorderProfile.hasProfile(id, CamcorderProfile.QUALITY_CIF) ? 2627 CamcorderProfile.QUALITY_CIF : 2628 CamcorderProfile.hasProfile(id, CamcorderProfile.QUALITY_QCIF) ? 2629 CamcorderProfile.QUALITY_QCIF : 2630 -1; 2631 2632 assertTrue("No recording supported for camera id " + cameraId, quality != -1); 2633 2634 CamcorderProfile maxProfile = CamcorderProfile.get(id, quality); 2635 return new Size(maxProfile.videoFrameWidth, maxProfile.videoFrameHeight); 2636 } 2637 getMaxExternalRecordingSize( String cameraId, StreamConfigurationMap config)2638 private static Size getMaxExternalRecordingSize( 2639 String cameraId, StreamConfigurationMap config) { 2640 final Size FULLHD = new Size(1920, 1080); 2641 2642 Size[] videoSizeArr = config.getOutputSizes(android.media.MediaRecorder.class); 2643 List<Size> sizes = new ArrayList<Size>(); 2644 for (Size sz: videoSizeArr) { 2645 if (sz.getWidth() <= FULLHD.getWidth() && sz.getHeight() <= FULLHD.getHeight()) { 2646 sizes.add(sz); 2647 } 2648 } 2649 List<Size> videoSizes = getAscendingOrderSizes(sizes, /*ascending*/false); 2650 for (Size sz : videoSizes) { 2651 long minFrameDuration = config.getOutputMinFrameDuration( 2652 android.media.MediaRecorder.class, sz); 2653 // Give some margin for rounding error 2654 if (minFrameDuration > (1e9 / 30.1)) { 2655 Log.i(TAG, "External camera " + cameraId + " has max video size:" + sz); 2656 return sz; 2657 } 2658 } 2659 fail("Camera " + cameraId + " does not support any 30fps video output"); 2660 return FULLHD; // doesn't matter what size is returned here 2661 } 2662 2663 /** 2664 * Get maximum size in list that's equal or smaller to than the bound. 2665 * Returns null if no size is smaller than or equal to the bound. 2666 */ getMaxSize(Size[] sizes, Size bound)2667 private static Size getMaxSize(Size[] sizes, Size bound) { 2668 if (sizes == null || sizes.length == 0) { 2669 throw new IllegalArgumentException("sizes was empty"); 2670 } 2671 2672 Size sz = null; 2673 for (Size size : sizes) { 2674 if (size.getWidth() <= bound.getWidth() && size.getHeight() <= bound.getHeight()) { 2675 2676 if (sz == null) { 2677 sz = size; 2678 } else { 2679 long curArea = sz.getWidth() * (long) sz.getHeight(); 2680 long newArea = size.getWidth() * (long) size.getHeight(); 2681 if ( newArea > curArea ) { 2682 sz = size; 2683 } 2684 } 2685 } 2686 } 2687 2688 assertTrue("No size under bound found: " + Arrays.toString(sizes) + " bound " + bound, 2689 sz != null); 2690 2691 return sz; 2692 } 2693 getMaxPreviewSize(Context context, String cameraId)2694 private static Size getMaxPreviewSize(Context context, String cameraId) { 2695 try { 2696 WindowManager windowManager = 2697 (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); 2698 Display display = windowManager.getDefaultDisplay(); 2699 2700 int width = display.getWidth(); 2701 int height = display.getHeight(); 2702 2703 if (height > width) { 2704 height = width; 2705 width = display.getHeight(); 2706 } 2707 2708 CameraManager camMgr = 2709 (CameraManager) context.getSystemService(Context.CAMERA_SERVICE); 2710 List<Size> orderedPreviewSizes = CameraTestUtils.getSupportedPreviewSizes( 2711 cameraId, camMgr, PREVIEW_SIZE_BOUND); 2712 2713 if (orderedPreviewSizes != null) { 2714 for (Size size : orderedPreviewSizes) { 2715 if (width >= size.getWidth() && 2716 height >= size.getHeight()) 2717 return size; 2718 } 2719 } 2720 } catch (Exception e) { 2721 Log.e(TAG, "getMaxPreviewSize Failed. "+e.toString()); 2722 } 2723 return PREVIEW_SIZE_BOUND; 2724 } 2725 } 2726