1 /* 2 * Copyright (C) 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.testcases; 18 19 import static android.hardware.camera2.cts.CameraTestUtils.*; 20 import static com.android.ex.camera2.blocking.BlockingStateCallback.STATE_CLOSED; 21 22 import android.hardware.camera2.params.StreamConfigurationMap; 23 import android.media.ImageReader; 24 import android.os.Environment; 25 import android.os.Handler; 26 import android.os.HandlerThread; 27 import android.os.Looper; 28 import android.test.ActivityInstrumentationTestCase2; 29 import android.util.Log; 30 import android.view.Surface; 31 import android.view.SurfaceHolder; 32 import android.view.View; 33 import android.view.WindowManager; 34 import android.content.Context; 35 import android.graphics.ImageFormat; 36 import android.hardware.camera2.CameraAccessException; 37 import android.hardware.camera2.CameraCaptureSession; 38 import android.hardware.camera2.CameraCaptureSession.CaptureCallback; 39 import android.hardware.camera2.CameraCharacteristics; 40 import android.hardware.camera2.CameraDevice; 41 import android.hardware.camera2.CameraManager; 42 import android.hardware.camera2.CameraMetadata; 43 import android.hardware.camera2.CaptureRequest; 44 import android.hardware.camera2.CaptureResult; 45 import android.util.Size; 46 import android.util.Range; 47 import android.hardware.camera2.cts.Camera2SurfaceViewCtsActivity; 48 import android.hardware.camera2.cts.CameraTestUtils; 49 import android.hardware.camera2.cts.CameraTestUtils.SimpleCaptureCallback; 50 import android.hardware.camera2.cts.helpers.CameraErrorCollector; 51 import android.hardware.camera2.cts.helpers.StaticMetadata; 52 import android.hardware.camera2.cts.helpers.StaticMetadata.CheckLevel; 53 54 import com.android.ex.camera2.blocking.BlockingSessionCallback; 55 import com.android.ex.camera2.blocking.BlockingStateCallback; 56 import com.android.ex.camera2.exceptions.TimeoutRuntimeException; 57 58 import java.util.ArrayList; 59 import java.util.HashMap; 60 import java.util.List; 61 62 /** 63 * Camera2 Preview test case base class by using SurfaceView as rendering target. 64 * 65 * <p>This class encapsulates the SurfaceView based preview common functionalities. 66 * The setup and teardown of CameraManager, test HandlerThread, Activity, Camera IDs 67 * and CameraStateCallback are handled in this class. Some basic preview related utility 68 * functions are provided to facilitate the derived preview-based test classes. 69 * </p> 70 */ 71 72 public class Camera2SurfaceViewTestCase extends 73 ActivityInstrumentationTestCase2<Camera2SurfaceViewCtsActivity> { 74 private static final String TAG = "SurfaceViewTestCase"; 75 private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE); 76 private static final int WAIT_FOR_SURFACE_CHANGE_TIMEOUT_MS = 1000; 77 78 // TODO: Use internal storage for this to make sure the file is only visible to test. 79 protected static final String DEBUG_FILE_NAME_BASE = 80 Environment.getExternalStorageDirectory().getPath(); 81 protected static final int WAIT_FOR_RESULT_TIMEOUT_MS = 3000; 82 protected static final float FRAME_DURATION_ERROR_MARGIN = 0.005f; // 0.5 percent error margin. 83 protected static final int NUM_RESULTS_WAIT_TIMEOUT = 100; 84 protected static final int NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY = 8; 85 protected static final int MIN_FRAME_DURATION_ERROR_MARGIN = 100; // ns 86 87 protected Context mContext; 88 protected CameraManager mCameraManager; 89 protected String[] mCameraIds; 90 protected HandlerThread mHandlerThread; 91 protected Handler mHandler; 92 protected BlockingStateCallback mCameraListener; 93 protected BlockingSessionCallback mSessionListener; 94 protected CameraErrorCollector mCollector; 95 // Per device fields: 96 protected StaticMetadata mStaticInfo; 97 protected CameraDevice mCamera; 98 protected CameraCaptureSession mSession; 99 protected ImageReader mReader; 100 protected Surface mReaderSurface; 101 protected Surface mPreviewSurface; 102 protected Size mPreviewSize; 103 protected List<Size> mOrderedPreviewSizes; // In descending order. 104 protected List<Size> m1080pBoundedOrderedPreviewSizes; // In descending order. 105 protected List<Size> mOrderedVideoSizes; // In descending order. 106 protected List<Size> mOrderedStillSizes; // In descending order. 107 protected HashMap<Size, Long> mMinPreviewFrameDurationMap; 108 109 protected WindowManager mWindowManager; 110 Camera2SurfaceViewTestCase()111 public Camera2SurfaceViewTestCase() { 112 super(Camera2SurfaceViewCtsActivity.class); 113 } 114 115 @Override setUp()116 protected void setUp() throws Exception { 117 /** 118 * Set up the camera preview required environments, including activity, 119 * CameraManager, HandlerThread, Camera IDs, and CameraStateCallback. 120 */ 121 super.setUp(); 122 mContext = getActivity(); 123 /** 124 * Workaround for mockito and JB-MR2 incompatibility 125 * 126 * Avoid java.lang.IllegalArgumentException: dexcache == null 127 * https://code.google.com/p/dexmaker/issues/detail?id=2 128 */ 129 System.setProperty("dexmaker.dexcache", mContext.getCacheDir().toString()); 130 mCameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE); 131 assertNotNull("Unable to get CameraManager", mCameraManager); 132 mCameraIds = mCameraManager.getCameraIdList(); 133 assertNotNull("Unable to get camera ids", mCameraIds); 134 mHandlerThread = new HandlerThread(TAG); 135 mHandlerThread.start(); 136 mHandler = new Handler(mHandlerThread.getLooper()); 137 mCameraListener = new BlockingStateCallback(); 138 mCollector = new CameraErrorCollector(); 139 140 mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); 141 } 142 143 @Override tearDown()144 protected void tearDown() throws Exception { 145 // Teardown the camera preview required environments. 146 mHandlerThread.quitSafely(); 147 mHandler = null; 148 mCameraListener = null; 149 150 try { 151 mCollector.verify(); 152 } catch (Throwable e) { 153 // When new Exception(e) is used, exception info will be printed twice. 154 throw new Exception(e.getMessage()); 155 } finally { 156 super.tearDown(); 157 } 158 } 159 160 /** 161 * Start camera preview by using the given request, preview size and capture 162 * listener. 163 * <p> 164 * If preview is already started, calling this function will stop the 165 * current preview stream and start a new preview stream with given 166 * parameters. No need to call stopPreview between two startPreview calls. 167 * </p> 168 * 169 * @param request The request builder used to start the preview. 170 * @param previewSz The size of the camera device output preview stream. 171 * @param listener The callbacks the camera device will notify when preview 172 * capture is available. 173 */ startPreview(CaptureRequest.Builder request, Size previewSz, CaptureCallback listener)174 protected void startPreview(CaptureRequest.Builder request, Size previewSz, 175 CaptureCallback listener) throws Exception { 176 // Update preview size. 177 updatePreviewSurface(previewSz); 178 if (VERBOSE) { 179 Log.v(TAG, "start preview with size " + mPreviewSize.toString()); 180 } 181 182 configurePreviewOutput(request); 183 184 mSession.setRepeatingRequest(request.build(), listener, mHandler); 185 } 186 187 /** 188 * Configure the preview output stream. 189 * 190 * @param request The request to be configured with preview surface 191 */ configurePreviewOutput(CaptureRequest.Builder request)192 protected void configurePreviewOutput(CaptureRequest.Builder request) 193 throws CameraAccessException { 194 List<Surface> outputSurfaces = new ArrayList<Surface>(/*capacity*/1); 195 outputSurfaces.add(mPreviewSurface); 196 mSessionListener = new BlockingSessionCallback(); 197 mSession = configureCameraSession(mCamera, outputSurfaces, mSessionListener, mHandler); 198 199 request.addTarget(mPreviewSurface); 200 } 201 202 /** 203 * Create a {@link CaptureRequest#Builder} and add the default preview surface. 204 * 205 * @return The {@link CaptureRequest#Builder} to be created 206 * @throws CameraAccessException When create capture request from camera fails 207 */ createRequestForPreview()208 protected CaptureRequest.Builder createRequestForPreview() throws CameraAccessException { 209 if (mPreviewSurface == null) { 210 throw new IllegalStateException( 211 "Preview surface is not set yet, call updatePreviewSurface or startPreview" 212 + "first to set the preview surface properly."); 213 } 214 CaptureRequest.Builder requestBuilder = 215 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); 216 requestBuilder.addTarget(mPreviewSurface); 217 return requestBuilder; 218 } 219 220 /** 221 * Stop preview for current camera device by closing the session. 222 * Does _not_ wait for the device to go idle 223 */ stopPreview()224 protected void stopPreview() throws Exception { 225 if (VERBOSE) Log.v(TAG, "Stopping preview"); 226 // Stop repeat, wait for captures to complete, and disconnect from surfaces 227 mSession.close(); 228 } 229 230 /** 231 * Stop preview for current camera device by closing the session and waiting for it to close, 232 * resulting in an idle device. 233 */ stopPreviewAndDrain()234 protected void stopPreviewAndDrain() throws Exception { 235 if (VERBOSE) Log.v(TAG, "Stopping preview and waiting for idle"); 236 // Stop repeat, wait for captures to complete, and disconnect from surfaces 237 mSession.close(); 238 mSessionListener.getStateWaiter().waitForState(BlockingSessionCallback.SESSION_CLOSED, 239 /*timeoutMs*/WAIT_FOR_RESULT_TIMEOUT_MS); 240 } 241 242 /** 243 * Setup still (JPEG) capture configuration and start preview. 244 * <p> 245 * The default max number of image is set to image reader. 246 * </p> 247 * 248 * @param previewRequest The capture request to be used for preview 249 * @param stillRequest The capture request to be used for still capture 250 * @param previewSz Preview size 251 * @param stillSz The still capture size 252 * @param resultListener Capture result listener 253 * @param imageListener The still capture image listener 254 */ prepareStillCaptureAndStartPreview(CaptureRequest.Builder previewRequest, CaptureRequest.Builder stillRequest, Size previewSz, Size stillSz, CaptureCallback resultListener, ImageReader.OnImageAvailableListener imageListener)255 protected void prepareStillCaptureAndStartPreview(CaptureRequest.Builder previewRequest, 256 CaptureRequest.Builder stillRequest, Size previewSz, Size stillSz, 257 CaptureCallback resultListener, 258 ImageReader.OnImageAvailableListener imageListener) throws Exception { 259 prepareCaptureAndStartPreview(previewRequest, stillRequest, previewSz, stillSz, 260 ImageFormat.JPEG, resultListener, MAX_READER_IMAGES, imageListener); 261 } 262 263 /** 264 * Setup still (JPEG) capture configuration and start preview. 265 * 266 * @param previewRequest The capture request to be used for preview 267 * @param stillRequest The capture request to be used for still capture 268 * @param previewSz Preview size 269 * @param stillSz The still capture size 270 * @param resultListener Capture result listener 271 * @param maxNumImages The max number of images set to the image reader 272 * @param imageListener The still capture image listener 273 */ prepareStillCaptureAndStartPreview(CaptureRequest.Builder previewRequest, CaptureRequest.Builder stillRequest, Size previewSz, Size stillSz, CaptureCallback resultListener, int maxNumImages, ImageReader.OnImageAvailableListener imageListener)274 protected void prepareStillCaptureAndStartPreview(CaptureRequest.Builder previewRequest, 275 CaptureRequest.Builder stillRequest, Size previewSz, Size stillSz, 276 CaptureCallback resultListener, int maxNumImages, 277 ImageReader.OnImageAvailableListener imageListener) throws Exception { 278 prepareCaptureAndStartPreview(previewRequest, stillRequest, previewSz, stillSz, 279 ImageFormat.JPEG, resultListener, maxNumImages, imageListener); 280 } 281 282 /** 283 * Setup raw capture configuration and start preview. 284 * 285 * <p> 286 * The default max number of image is set to image reader. 287 * </p> 288 * 289 * @param previewRequest The capture request to be used for preview 290 * @param rawRequest The capture request to be used for raw capture 291 * @param previewSz Preview size 292 * @param rawSz The raw capture size 293 * @param resultListener Capture result listener 294 * @param imageListener The raw capture image listener 295 */ prepareRawCaptureAndStartPreview(CaptureRequest.Builder previewRequest, CaptureRequest.Builder rawRequest, Size previewSz, Size rawSz, CaptureCallback resultListener, ImageReader.OnImageAvailableListener imageListener)296 protected void prepareRawCaptureAndStartPreview(CaptureRequest.Builder previewRequest, 297 CaptureRequest.Builder rawRequest, Size previewSz, Size rawSz, 298 CaptureCallback resultListener, 299 ImageReader.OnImageAvailableListener imageListener) throws Exception { 300 prepareCaptureAndStartPreview(previewRequest, rawRequest, previewSz, rawSz, 301 ImageFormat.RAW_SENSOR, resultListener, MAX_READER_IMAGES, imageListener); 302 } 303 304 /** 305 * Wait for expected result key value available in a certain number of results. 306 * 307 * <p> 308 * Check the result immediately if numFramesWait is 0. 309 * </p> 310 * 311 * @param listener The capture listener to get capture result 312 * @param resultKey The capture result key associated with the result value 313 * @param expectedValue The result value need to be waited for 314 * @param numResultsWait Number of frame to wait before times out 315 * @throws TimeoutRuntimeException If more than numResultsWait results are 316 * seen before the result matching myRequest arrives, or each individual wait 317 * for result times out after {@value #WAIT_FOR_RESULT_TIMEOUT_MS}ms. 318 */ waitForResultValue(SimpleCaptureCallback listener, CaptureResult.Key<T> resultKey, T expectedValue, int numResultsWait)319 protected static <T> void waitForResultValue(SimpleCaptureCallback listener, 320 CaptureResult.Key<T> resultKey, 321 T expectedValue, int numResultsWait) { 322 List<T> expectedValues = new ArrayList<T>(); 323 expectedValues.add(expectedValue); 324 waitForAnyResultValue(listener, resultKey, expectedValues, numResultsWait); 325 } 326 327 /** 328 * Wait for any expected result key values available in a certain number of results. 329 * 330 * <p> 331 * Check the result immediately if numFramesWait is 0. 332 * </p> 333 * 334 * @param listener The capture listener to get capture result. 335 * @param resultKey The capture result key associated with the result value. 336 * @param expectedValues The list of result value need to be waited for, 337 * return immediately if the list is empty. 338 * @param numResultsWait Number of frame to wait before times out. 339 * @throws TimeoutRuntimeException If more than numResultsWait results are. 340 * seen before the result matching myRequest arrives, or each individual wait 341 * for result times out after {@value #WAIT_FOR_RESULT_TIMEOUT_MS}ms. 342 */ waitForAnyResultValue(SimpleCaptureCallback listener, CaptureResult.Key<T> resultKey, List<T> expectedValues, int numResultsWait)343 protected static <T> void waitForAnyResultValue(SimpleCaptureCallback listener, 344 CaptureResult.Key<T> resultKey, 345 List<T> expectedValues, int numResultsWait) { 346 if (numResultsWait < 0 || listener == null || expectedValues == null) { 347 throw new IllegalArgumentException( 348 "Input must be non-negative number and listener/expectedValues " 349 + "must be non-null"); 350 } 351 352 int i = 0; 353 CaptureResult result; 354 do { 355 result = listener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS); 356 T value = result.get(resultKey); 357 for ( T expectedValue : expectedValues) { 358 if (VERBOSE) { 359 Log.v(TAG, "Current result value for key " + resultKey.getName() + " is: " 360 + value.toString()); 361 } 362 if (value.equals(expectedValue)) { 363 return; 364 } 365 } 366 } while (i++ < numResultsWait); 367 368 throw new TimeoutRuntimeException( 369 "Unable to get the expected result value " + expectedValues + " for key " + 370 resultKey.getName() + " after waiting for " + numResultsWait + " results"); 371 } 372 373 /** 374 * Submit a capture once, then submit additional captures in order to ensure that 375 * the camera will be synchronized. 376 * 377 * <p> 378 * The additional capture count is determined by android.sync.maxLatency (or 379 * a fixed {@value #NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY}) captures if maxLatency is unknown). 380 * </p> 381 * 382 * <p>Returns the number of captures that were submitted (at least 1), which is useful 383 * with {@link #waitForNumResults}.</p> 384 * 385 * @param request capture request to forward to {@link CameraDevice#capture} 386 * @param listener request listener to forward to {@link CameraDevice#capture} 387 * @param handler handler to forward to {@link CameraDevice#capture} 388 * 389 * @return the number of captures that were submitted 390 * 391 * @throws CameraAccessException if capturing failed 392 */ captureRequestsSynchronized( CaptureRequest request, CaptureCallback listener, Handler handler)393 protected int captureRequestsSynchronized( 394 CaptureRequest request, CaptureCallback listener, Handler handler) 395 throws CameraAccessException { 396 return captureRequestsSynchronized(request, /*count*/1, listener, handler); 397 } 398 399 /** 400 * Submit a capture {@code count} times, then submit additional captures in order to ensure that 401 * the camera will be synchronized. 402 * 403 * <p> 404 * The additional capture count is determined by android.sync.maxLatency (or 405 * a fixed {@value #NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY}) captures if maxLatency is unknown). 406 * </p> 407 * 408 * <p>Returns the number of captures that were submitted (at least 1), which is useful 409 * with {@link #waitForNumResults}.</p> 410 * 411 * @param request capture request to forward to {@link CameraDevice#capture} 412 * @param count the number of times to submit the request (minimally), must be at least 1 413 * @param listener request listener to forward to {@link CameraDevice#capture} 414 * @param handler handler to forward to {@link CameraDevice#capture} 415 * 416 * @return the number of captures that were submitted 417 * 418 * @throws IllegalArgumentException if {@code count} was not at least 1 419 * @throws CameraAccessException if capturing failed 420 */ captureRequestsSynchronized( CaptureRequest request, int count, CaptureCallback listener, Handler handler)421 protected int captureRequestsSynchronized( 422 CaptureRequest request, int count, CaptureCallback listener, Handler handler) 423 throws CameraAccessException { 424 if (count < 1) { 425 throw new IllegalArgumentException("count must be positive"); 426 } 427 428 int maxLatency = mStaticInfo.getSyncMaxLatency(); 429 if (maxLatency == CameraMetadata.SYNC_MAX_LATENCY_UNKNOWN) { 430 maxLatency = NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY; 431 } 432 433 assertTrue("maxLatency is non-negative", maxLatency >= 0); 434 435 int numCaptures = maxLatency + count; 436 437 for (int i = 0; i < numCaptures; ++i) { 438 mSession.capture(request, listener, handler); 439 } 440 441 return numCaptures; 442 } 443 444 /** 445 * Wait for numResultWait frames 446 * 447 * @param resultListener The capture listener to get capture result back. 448 * @param numResultsWait Number of frame to wait 449 * 450 * @return the last result, or {@code null} if there was none 451 */ waitForNumResults(SimpleCaptureCallback resultListener, int numResultsWait)452 protected static CaptureResult waitForNumResults(SimpleCaptureCallback resultListener, 453 int numResultsWait) { 454 if (numResultsWait < 0 || resultListener == null) { 455 throw new IllegalArgumentException( 456 "Input must be positive number and listener must be non-null"); 457 } 458 459 CaptureResult result = null; 460 for (int i = 0; i < numResultsWait; i++) { 461 result = resultListener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS); 462 } 463 464 return result; 465 } 466 467 /** 468 * Wait for enough results for settings to be applied 469 * 470 * @param resultListener The capture listener to get capture result back. 471 * @param numResultWaitForUnknownLatency Number of frame to wait if camera device latency is 472 * unknown. 473 */ waitForSettingsApplied(SimpleCaptureCallback resultListener, int numResultWaitForUnknownLatency)474 protected void waitForSettingsApplied(SimpleCaptureCallback resultListener, 475 int numResultWaitForUnknownLatency) { 476 int maxLatency = mStaticInfo.getSyncMaxLatency(); 477 if (maxLatency == CameraMetadata.SYNC_MAX_LATENCY_UNKNOWN) { 478 maxLatency = numResultWaitForUnknownLatency; 479 } 480 // Wait for settings to take effect 481 waitForNumResults(resultListener, maxLatency); 482 } 483 484 485 /** 486 * Wait for AE to be stabilized before capture: CONVERGED or FLASH_REQUIRED. 487 * 488 * <p>Waits for {@code android.sync.maxLatency} number of results first, to make sure 489 * that the result is synchronized (or {@code numResultWaitForUnknownLatency} if the latency 490 * is unknown.</p> 491 * 492 * <p>This is a no-op for {@code LEGACY} devices since they don't report 493 * the {@code aeState} result.</p> 494 * 495 * @param resultListener The capture listener to get capture result back. 496 * @param numResultWaitForUnknownLatency Number of frame to wait if camera device latency is 497 * unknown. 498 */ waitForAeStable(SimpleCaptureCallback resultListener, int numResultWaitForUnknownLatency)499 protected void waitForAeStable(SimpleCaptureCallback resultListener, 500 int numResultWaitForUnknownLatency) { 501 waitForSettingsApplied(resultListener, numResultWaitForUnknownLatency); 502 503 if (!mStaticInfo.isHardwareLevelAtLeastLimited()) { 504 // No-op for metadata 505 return; 506 } 507 List<Integer> expectedAeStates = new ArrayList<Integer>(); 508 expectedAeStates.add(new Integer(CaptureResult.CONTROL_AE_STATE_CONVERGED)); 509 expectedAeStates.add(new Integer(CaptureResult.CONTROL_AE_STATE_FLASH_REQUIRED)); 510 waitForAnyResultValue(resultListener, CaptureResult.CONTROL_AE_STATE, expectedAeStates, 511 NUM_RESULTS_WAIT_TIMEOUT); 512 } 513 514 /** 515 * Wait for AE to be: LOCKED 516 * 517 * <p>Waits for {@code android.sync.maxLatency} number of results first, to make sure 518 * that the result is synchronized (or {@code numResultWaitForUnknownLatency} if the latency 519 * is unknown.</p> 520 * 521 * <p>This is a no-op for {@code LEGACY} devices since they don't report 522 * the {@code aeState} result.</p> 523 * 524 * @param resultListener The capture listener to get capture result back. 525 * @param numResultWaitForUnknownLatency Number of frame to wait if camera device latency is 526 * unknown. 527 */ waitForAeLocked(SimpleCaptureCallback resultListener, int numResultWaitForUnknownLatency)528 protected void waitForAeLocked(SimpleCaptureCallback resultListener, 529 int numResultWaitForUnknownLatency) { 530 531 waitForSettingsApplied(resultListener, numResultWaitForUnknownLatency); 532 533 if (!mStaticInfo.isHardwareLevelAtLeastLimited()) { 534 // No-op for legacy devices 535 return; 536 } 537 538 List<Integer> expectedAeStates = new ArrayList<Integer>(); 539 expectedAeStates.add(new Integer(CaptureResult.CONTROL_AE_STATE_LOCKED)); 540 waitForAnyResultValue(resultListener, CaptureResult.CONTROL_AE_STATE, expectedAeStates, 541 NUM_RESULTS_WAIT_TIMEOUT); 542 } 543 544 /** 545 * Create an {@link ImageReader} object and get the surface. 546 * 547 * @param size The size of this ImageReader to be created. 548 * @param format The format of this ImageReader to be created 549 * @param maxNumImages The max number of images that can be acquired simultaneously. 550 * @param listener The listener used by this ImageReader to notify callbacks. 551 */ createImageReader(Size size, int format, int maxNumImages, ImageReader.OnImageAvailableListener listener)552 protected void createImageReader(Size size, int format, int maxNumImages, 553 ImageReader.OnImageAvailableListener listener) throws Exception { 554 closeImageReader(); 555 556 ImageReader r = makeImageReader(size, format, maxNumImages, listener, 557 mHandler); 558 mReader = r; 559 mReaderSurface = r.getSurface(); 560 } 561 562 /** 563 * Close the pending images then close current active {@link ImageReader} object. 564 */ closeImageReader()565 protected void closeImageReader() { 566 CameraTestUtils.closeImageReader(mReader); 567 mReader = null; 568 mReaderSurface = null; 569 } 570 571 /** 572 * Open a camera device and get the StaticMetadata for a given camera id. 573 * 574 * @param cameraId The id of the camera device to be opened. 575 */ openDevice(String cameraId)576 protected void openDevice(String cameraId) throws Exception { 577 mCamera = CameraTestUtils.openCamera( 578 mCameraManager, cameraId, mCameraListener, mHandler); 579 mCollector.setCameraId(cameraId); 580 mStaticInfo = new StaticMetadata(mCameraManager.getCameraCharacteristics(cameraId), 581 CheckLevel.ASSERT, /*collector*/null); 582 if (mStaticInfo.isColorOutputSupported()) { 583 mOrderedPreviewSizes = getSupportedPreviewSizes(cameraId, mCameraManager, 584 getPreviewSizeBound(mWindowManager, PREVIEW_SIZE_BOUND)); 585 m1080pBoundedOrderedPreviewSizes = getSupportedPreviewSizes(cameraId, mCameraManager, 586 PREVIEW_SIZE_BOUND); 587 mOrderedVideoSizes = getSupportedVideoSizes(cameraId, mCameraManager, PREVIEW_SIZE_BOUND); 588 mOrderedStillSizes = getSupportedStillSizes(cameraId, mCameraManager, null); 589 // Use ImageFormat.YUV_420_888 for now. TODO: need figure out what's format for preview 590 // in public API side. 591 mMinPreviewFrameDurationMap = 592 mStaticInfo.getAvailableMinFrameDurationsForFormatChecked(ImageFormat.YUV_420_888); 593 } 594 } 595 596 /** 597 * Close the current actively used camera device. 598 */ closeDevice()599 protected void closeDevice() { 600 if (mCamera != null) { 601 mCamera.close(); 602 mCameraListener.waitForState(STATE_CLOSED, CAMERA_CLOSE_TIMEOUT_MS); 603 mCamera = null; 604 mSession = null; 605 mSessionListener = null; 606 mStaticInfo = null; 607 mOrderedPreviewSizes = null; 608 mOrderedVideoSizes = null; 609 mOrderedStillSizes = null; 610 } 611 } 612 613 /** 614 * Update the preview surface size. 615 * 616 * @param size The preview size to be updated. 617 */ updatePreviewSurface(Size size)618 protected void updatePreviewSurface(Size size) { 619 if (size.equals(mPreviewSize) && mPreviewSurface != null) { 620 Log.w(TAG, "Skipping update preview surface size..."); 621 return; 622 } 623 624 mPreviewSize = size; 625 Camera2SurfaceViewCtsActivity ctsActivity = getActivity(); 626 final SurfaceHolder holder = ctsActivity.getSurfaceView().getHolder(); 627 Handler handler = new Handler(Looper.getMainLooper()); 628 handler.post(new Runnable() { 629 @Override 630 public void run() { 631 holder.setFixedSize(mPreviewSize.getWidth(), mPreviewSize.getHeight()); 632 } 633 }); 634 635 boolean res = ctsActivity.waitForSurfaceSizeChanged( 636 WAIT_FOR_SURFACE_CHANGE_TIMEOUT_MS, mPreviewSize.getWidth(), 637 mPreviewSize.getHeight()); 638 assertTrue("wait for surface change to " + mPreviewSize.toString() + " timed out", res); 639 mPreviewSurface = holder.getSurface(); 640 assertNotNull("Preview surface is null", mPreviewSurface); 641 assertTrue("Preview surface is invalid", mPreviewSurface.isValid()); 642 } 643 644 /** 645 * Recreate the SurfaceView's Surface 646 * 647 * Hide and unhide the activity's preview SurfaceView, so that its backing Surface is 648 * recreated 649 */ recreatePreviewSurface()650 protected void recreatePreviewSurface() { 651 Camera2SurfaceViewCtsActivity ctsActivity = getActivity(); 652 setPreviewVisibility(View.GONE); 653 boolean res = ctsActivity.waitForSurfaceState( 654 WAIT_FOR_SURFACE_CHANGE_TIMEOUT_MS, /*valid*/ false); 655 assertTrue("wait for surface destroyed timed out", res); 656 setPreviewVisibility(View.VISIBLE); 657 res = ctsActivity.waitForSurfaceState( 658 WAIT_FOR_SURFACE_CHANGE_TIMEOUT_MS, /*valid*/ true); 659 assertTrue("wait for surface created timed out", res); 660 } 661 662 /** 663 * Show/hide the preview SurfaceView. 664 * 665 * If set to View.GONE, the surfaceDestroyed callback will fire 666 * @param visibility the new new visibility to set, one of View.VISIBLE / INVISIBLE / GONE 667 */ setPreviewVisibility(int visibility)668 protected void setPreviewVisibility(int visibility) { 669 final Camera2SurfaceViewCtsActivity ctsActivity = getActivity(); 670 Handler handler = new Handler(Looper.getMainLooper()); 671 handler.post(new Runnable() { 672 @Override 673 public void run() { 674 ctsActivity.getSurfaceView().setVisibility(visibility); 675 } 676 }); 677 } 678 679 /** 680 * Setup single capture configuration and start preview. 681 * 682 * @param previewRequest The capture request to be used for preview 683 * @param stillRequest The capture request to be used for still capture 684 * @param previewSz Preview size 685 * @param captureSz Still capture size 686 * @param format The single capture image format 687 * @param resultListener Capture result listener 688 * @param maxNumImages The max number of images set to the image reader 689 * @param imageListener The single capture capture image listener 690 */ prepareCaptureAndStartPreview(CaptureRequest.Builder previewRequest, CaptureRequest.Builder stillRequest, Size previewSz, Size captureSz, int format, CaptureCallback resultListener, int maxNumImages, ImageReader.OnImageAvailableListener imageListener)691 protected void prepareCaptureAndStartPreview(CaptureRequest.Builder previewRequest, 692 CaptureRequest.Builder stillRequest, Size previewSz, Size captureSz, int format, 693 CaptureCallback resultListener, int maxNumImages, 694 ImageReader.OnImageAvailableListener imageListener) throws Exception { 695 if (VERBOSE) { 696 Log.v(TAG, String.format("Prepare single capture (%s) and preview (%s)", 697 captureSz.toString(), previewSz.toString())); 698 } 699 700 // Update preview size. 701 updatePreviewSurface(previewSz); 702 703 // Create ImageReader. 704 createImageReader(captureSz, format, maxNumImages, imageListener); 705 706 // Configure output streams with preview and jpeg streams. 707 List<Surface> outputSurfaces = new ArrayList<Surface>(); 708 outputSurfaces.add(mPreviewSurface); 709 outputSurfaces.add(mReaderSurface); 710 mSessionListener = new BlockingSessionCallback(); 711 mSession = configureCameraSession(mCamera, outputSurfaces, mSessionListener, mHandler); 712 713 // Configure the requests. 714 previewRequest.addTarget(mPreviewSurface); 715 stillRequest.addTarget(mPreviewSurface); 716 stillRequest.addTarget(mReaderSurface); 717 718 // Start preview. 719 mSession.setRepeatingRequest(previewRequest.build(), resultListener, mHandler); 720 } 721 722 /** 723 * Get the max preview size that supports the given fpsRange. 724 * 725 * @param fpsRange The fps range the returned size must support. 726 * @return max size that support the given fps range. 727 */ getMaxPreviewSizeForFpsRange(Range<Integer> fpsRange)728 protected Size getMaxPreviewSizeForFpsRange(Range<Integer> fpsRange) { 729 if (fpsRange == null || fpsRange.getLower() <= 0 || fpsRange.getUpper() <= 0) { 730 throw new IllegalArgumentException("Invalid fps range argument"); 731 } 732 if (mOrderedPreviewSizes == null || mMinPreviewFrameDurationMap == null) { 733 throw new IllegalStateException("mOrderedPreviewSizes and mMinPreviewFrameDurationMap" 734 + " must be initialized"); 735 } 736 737 long[] frameDurationRange = 738 new long[]{(long) (1e9 / fpsRange.getUpper()), (long) (1e9 / fpsRange.getLower())}; 739 for (Size size : mOrderedPreviewSizes) { 740 Long minDuration = mMinPreviewFrameDurationMap.get(size); 741 if (minDuration == null || 742 minDuration == 0) { 743 if (mStaticInfo.isCapabilitySupported( 744 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) { 745 throw new IllegalArgumentException( 746 "No min frame duration available for the size " + size); 747 } 748 continue; 749 } 750 if (minDuration <= (frameDurationRange[0] + MIN_FRAME_DURATION_ERROR_MARGIN)) { 751 return size; 752 } 753 } 754 755 // Search again for sizes not bounded by display size 756 for (Size size : m1080pBoundedOrderedPreviewSizes) { 757 Long minDuration = mMinPreviewFrameDurationMap.get(size); 758 if (minDuration == null || 759 minDuration == 0) { 760 if (mStaticInfo.isCapabilitySupported( 761 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) { 762 throw new IllegalArgumentException( 763 "No min frame duration available for the size " + size); 764 } 765 continue; 766 } 767 if (minDuration <= (frameDurationRange[0] + MIN_FRAME_DURATION_ERROR_MARGIN)) { 768 return size; 769 } 770 } 771 return null; 772 } 773 isReprocessSupported(String cameraId, int format)774 protected boolean isReprocessSupported(String cameraId, int format) 775 throws CameraAccessException { 776 if (format != ImageFormat.YUV_420_888 && format != ImageFormat.PRIVATE) { 777 throw new IllegalArgumentException( 778 "format " + format + " is not supported for reprocessing"); 779 } 780 781 StaticMetadata info = 782 new StaticMetadata(mCameraManager.getCameraCharacteristics(cameraId), 783 CheckLevel.ASSERT, /*collector*/ null); 784 int cap = CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING; 785 if (format == ImageFormat.PRIVATE) { 786 cap = CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING; 787 } 788 return info.isCapabilitySupported(cap); 789 } 790 } 791