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