1 /* 2 * Copyright (C) 2016 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 com.android.mediaframeworktest.stress; 18 19 import com.android.ex.camera2.blocking.BlockingSessionCallback; 20 import com.android.ex.camera2.exceptions.TimeoutRuntimeException; 21 import com.android.mediaframeworktest.Camera2SurfaceViewTestCase; 22 import com.android.mediaframeworktest.helpers.Camera2Focuser; 23 import com.android.mediaframeworktest.helpers.CameraTestUtils; 24 import com.android.mediaframeworktest.helpers.CameraTestUtils.SimpleCaptureCallback; 25 26 import android.graphics.ImageFormat; 27 import android.graphics.Point; 28 import android.hardware.camera2.CameraCharacteristics; 29 import android.hardware.camera2.CameraCaptureSession.CaptureCallback; 30 import android.hardware.camera2.CameraConstrainedHighSpeedCaptureSession; 31 import android.hardware.camera2.CameraDevice; 32 import android.hardware.camera2.CameraAccessException; 33 import android.hardware.camera2.CameraCaptureSession; 34 import android.hardware.camera2.CaptureRequest; 35 import android.hardware.camera2.CaptureResult; 36 import android.hardware.camera2.DngCreator; 37 import android.hardware.camera2.params.MeteringRectangle; 38 import android.media.Image; 39 import android.media.ImageReader; 40 import android.media.CamcorderProfile; 41 import android.media.MediaExtractor; 42 import android.media.MediaFormat; 43 import android.media.MediaRecorder; 44 import android.os.ConditionVariable; 45 import android.os.Environment; 46 import android.util.Log; 47 import android.util.Pair; 48 import android.util.Rational; 49 import android.util.Size; 50 import android.view.Surface; 51 import android.hardware.camera2.params.StreamConfigurationMap; 52 import android.test.suitebuilder.annotation.LargeTest; 53 import android.util.Log; 54 import android.util.Range; 55 56 import java.io.ByteArrayOutputStream; 57 import java.util.ArrayList; 58 import java.util.List; 59 import java.io.File; 60 import java.util.Arrays; 61 import java.util.HashMap; 62 63 import static com.android.mediaframeworktest.helpers.CameraTestUtils.CAPTURE_IMAGE_TIMEOUT_MS; 64 import static com.android.mediaframeworktest.helpers.CameraTestUtils.MAX_READER_IMAGES; 65 import static com.android.mediaframeworktest.helpers.CameraTestUtils.SimpleImageReaderListener; 66 import static com.android.mediaframeworktest.helpers.CameraTestUtils.basicValidateJpegImage; 67 import static com.android.mediaframeworktest.helpers.CameraTestUtils.configureCameraSession; 68 import static com.android.mediaframeworktest.helpers.CameraTestUtils.dumpFile; 69 import static com.android.mediaframeworktest.helpers.CameraTestUtils.getDataFromImage; 70 import static com.android.mediaframeworktest.helpers.CameraTestUtils.getValueNotNull; 71 import static com.android.mediaframeworktest.helpers.CameraTestUtils.makeImageReader; 72 import static com.android.ex.camera2.blocking.BlockingSessionCallback.SESSION_CLOSED; 73 import static com.android.mediaframeworktest.helpers.CameraTestUtils.CAPTURE_IMAGE_TIMEOUT_MS; 74 import static com.android.mediaframeworktest.helpers.CameraTestUtils.SESSION_CLOSE_TIMEOUT_MS; 75 import static com.android.mediaframeworktest.helpers.CameraTestUtils.SIZE_BOUND_1080P; 76 import static com.android.mediaframeworktest.helpers.CameraTestUtils.SIZE_BOUND_2160P; 77 import static com.android.mediaframeworktest.helpers.CameraTestUtils.getSupportedVideoSizes; 78 79 import com.android.ex.camera2.blocking.BlockingSessionCallback; 80 import com.android.mediaframeworktest.Camera2SurfaceViewTestCase; 81 import com.android.mediaframeworktest.helpers.CameraTestUtils; 82 83 import junit.framework.AssertionFailedError; 84 85 /** 86 * <p>Tests Back/Front camera switching and Camera/Video modes witching.</p> 87 * 88 * adb shell am instrument \ 89 * -e class com.android.mediaframeworktest.stress.Camera2SwitchPreviewTest \ 90 * -e iterations 200 \ 91 * -e waitIntervalMs 1000 \ 92 * -e resultToFile false \ 93 * -r -w com.android.mediaframeworktest/.Camera2InstrumentationTestRunner 94 */ 95 public class Camera2SwitchPreviewTest extends Camera2SurfaceViewTestCase { 96 private static final String TAG = "SwitchPreviewTest"; 97 private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE); 98 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 99 // 60 second to accommodate the possible long exposure time. 100 private static final int MAX_REGIONS_AE_INDEX = 0; 101 private static final int MAX_REGIONS_AWB_INDEX = 1; 102 private static final int MAX_REGIONS_AF_INDEX = 2; 103 private static final int WAIT_FOR_FOCUS_DONE_TIMEOUT_MS = 6000; 104 private static final double AE_COMPENSATION_ERROR_TOLERANCE = 0.2; 105 // 5 percent error margin for resulting metering regions 106 private static final float METERING_REGION_ERROR_PERCENT_DELTA = 0.05f; 107 private final String VIDEO_FILE_PATH = Environment.getExternalStorageDirectory().getPath(); 108 109 private static final boolean DEBUG_DUMP = Log.isLoggable(TAG, Log.DEBUG); 110 private static final int RECORDING_DURATION_MS = 3000; 111 private static final float DURATION_MARGIN = 0.2f; 112 private static final double FRAME_DURATION_ERROR_TOLERANCE_MS = 3.0; 113 private static final int BIT_RATE_1080P = 16000000; 114 private static final int BIT_RATE_MIN = 64000; 115 private static final int BIT_RATE_MAX = 40000000; 116 private static final int VIDEO_FRAME_RATE = 30; 117 private static final int[] mCamcorderProfileList = { 118 CamcorderProfile.QUALITY_HIGH, 119 CamcorderProfile.QUALITY_2160P, 120 CamcorderProfile.QUALITY_1080P, 121 CamcorderProfile.QUALITY_720P, 122 CamcorderProfile.QUALITY_480P, 123 CamcorderProfile.QUALITY_CIF, 124 CamcorderProfile.QUALITY_QCIF, 125 CamcorderProfile.QUALITY_QVGA, 126 CamcorderProfile.QUALITY_LOW, 127 }; 128 private static final int MAX_VIDEO_SNAPSHOT_IMAGES = 5; 129 private static final int BURST_VIDEO_SNAPSHOT_NUM = 3; 130 private static final int SLOWMO_SLOW_FACTOR = 4; 131 private static final int MAX_NUM_FRAME_DROP_INTERVAL_ALLOWED = 4; 132 private List<Size> mSupportedVideoSizes; 133 private Surface mRecordingSurface; 134 private Surface mPersistentSurface; 135 private MediaRecorder mMediaRecorder; 136 private String mOutMediaFileName; 137 private int mVideoFrameRate; 138 private Size mVideoSize; 139 private long mRecordingStartTime; 140 141 @Override 142 protected void setUp() throws Exception { 143 super.setUp(); 144 } 145 146 @Override 147 protected void tearDown() throws Exception { 148 super.tearDown(); 149 } 150 151 /** 152 * Test normal still preview switch. 153 * <p> 154 * Preview jpeg output streams are configured. Max still capture 155 * size is used for jpeg capture. 156 * </p> 157 */ 158 public void testPreviewSwitchBackFrontCamera() throws Exception { 159 List<String> mCameraColorOutputIds = cameraColorOutputCheck(); 160 // Test iteration starts... 161 Log.i(TAG, "Testing preview switch back/front camera in still capture mode"); 162 for (int iteration = 0; iteration < getIterationCount(); ++iteration) { 163 for (String id : mCameraColorOutputIds) { 164 try { 165 openDevice(id); 166 // Preview for basic still capture: 167 Log.v(TAG, String.format("Preview pictures: %d/%d", iteration + 1, 168 getIterationCount())); 169 stillCapturePreviewPreparer(id); 170 getResultPrinter().printStatus(getIterationCount(), iteration + 1, id); 171 } finally { 172 closeDevice(); 173 closeImageReader(); 174 } 175 } 176 } 177 } 178 179 /** 180 * <p> 181 * Test basic video preview switch. 182 * </p> 183 * <p> 184 * This test covers the typical basic use case of video preview switch. 185 * MediaRecorder is used to record the audio and video, CamcorderProfile is 186 * used to configure the MediaRecorder. Preview is set to the video size. 187 * </p> 188 */ 189 public void testPreviewSwitchBackFrontVideo() throws Exception { 190 List<String> mCameraColorOutputIds = cameraColorOutputCheck(); 191 // Test iteration starts... 192 Log.i(TAG, "Testing preview switch back/front camera in video mode"); 193 for (int iteration = 0; iteration < getIterationCount(); ++iteration) { 194 for (String id : mCameraColorOutputIds) { 195 try { 196 openDevice(id); 197 // Preview for basic video recording: 198 Log.v(TAG, String.format("Preview for recording videos: %d/%d", iteration + 1, 199 getIterationCount())); 200 recordingPreviewPreparer(id); 201 getResultPrinter().printStatus(getIterationCount(), iteration + 1, id); 202 } finally { 203 closeDevice(); 204 releaseRecorder(); 205 } 206 } 207 } 208 } 209 210 211 /** 212 * Test back camera preview switch between still capture and recording mode. 213 * <p> 214 * This test covers the basic case of preview switch camera mode, between 215 * still capture (photo) and recording (video) mode. The preview settings 216 * are same with the settings in "testPreviewSwitchBackFrontCamera" and 217 * "testPreviewSwitchBackFrontVideo" 218 * </p> 219 */ 220 public void testPreviewSwitchBackCameraVideo() throws Exception { 221 String id = mCameraIds[0]; 222 openDevice(id); 223 if (!mStaticInfo.isColorOutputSupported()) { 224 Log.i(TAG, "Camera " + id + 225 " does not support color outputs, skipping"); 226 return; 227 } 228 closeDevice(); 229 // Test iteration starts... 230 Log.i(TAG, "Testing preview switch between still capture/video modes for back camera"); 231 for (int iteration = 0; iteration < getIterationCount(); ++iteration) { 232 try { 233 openDevice(id); 234 235 // Preview for basic still capture: 236 Log.v(TAG, String.format("Preview pictures: %d/%d", iteration + 1, 237 getIterationCount())); 238 stillCapturePreviewPreparer(id); 239 getResultPrinter().printStatus(getIterationCount(), iteration + 1, id); 240 241 // Preview for basic video recording: 242 Log.v(TAG, String.format("Preview for recording videos: %d/%d", iteration + 1, 243 getIterationCount())); 244 recordingPreviewPreparer(id); 245 getResultPrinter().printStatus(getIterationCount(), iteration + 1, id); 246 } finally { 247 closeDevice(); 248 closeImageReader(); 249 } 250 } 251 } 252 253 /** 254 * Test front camera preview switch between still capture and recording mode. 255 * <p> 256 * This test covers the basic case of preview switch camera mode, between 257 * still capture (photo) and recording (video) mode. The preview settings 258 * are same with the settings in "testPreviewSwitchBackFrontCamera" and 259 * "testPreviewSwitchBackFrontVideo" 260 * </p> 261 */ 262 public void testPreviewSwitchFrontCameraVideo() throws Exception{ 263 String id = mCameraIds[1]; 264 openDevice(id); 265 if (!mStaticInfo.isColorOutputSupported()) { 266 Log.i(TAG, "Camera " + id + 267 " does not support color outputs, skipping"); 268 return; 269 } 270 closeDevice(); 271 // Test iteration starts... 272 Log.i(TAG, "Testing preview switch between still capture/video modes for front camera"); 273 for (int iteration = 0; iteration < getIterationCount(); ++iteration) { 274 try { 275 openDevice(id); 276 277 // Preview for basic still capture: 278 Log.v(TAG, String.format("Preview pictures: %d/%d", iteration + 1, 279 getIterationCount())); 280 stillCapturePreviewPreparer(id); 281 getResultPrinter().printStatus(getIterationCount(), iteration + 1, id); 282 283 // Preview for basic video recording: 284 Log.v(TAG, String.format("Preview for recording videos: %d/%d", iteration + 1, 285 getIterationCount())); 286 recordingPreviewPreparer(id); 287 getResultPrinter().printStatus(getIterationCount(), iteration + 1, id); 288 } finally { 289 closeDevice(); 290 closeImageReader(); 291 } 292 } 293 } 294 295 private void stillCapturePreviewPreparer(String id) throws Exception{ 296 CaptureResult result; 297 SimpleCaptureCallback resultListener = new SimpleCaptureCallback(); 298 SimpleImageReaderListener imageListener = new SimpleImageReaderListener(); 299 CaptureRequest.Builder previewRequest = 300 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); 301 CaptureRequest.Builder stillRequest = 302 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); 303 // Preview Setup: 304 prepareCapturePreview(previewRequest, stillRequest, resultListener, imageListener); 305 306 Thread.sleep(getTestWaitIntervalMs()); 307 } 308 309 private void recordingPreviewPreparer(String id) throws Exception{ 310 // Re-use the MediaRecorder object for the same camera device. 311 mMediaRecorder = new MediaRecorder(); 312 initSupportedVideoSize(id); 313 // preview Setup: 314 basicRecordingPreviewTestByCamera(mCamcorderProfileList); 315 316 Thread.sleep(getTestWaitIntervalMs()); 317 } 318 319 320 /** 321 * Initialize the supported video sizes. 322 */ 323 private void initSupportedVideoSize(String cameraId) throws Exception { 324 Size maxVideoSize = SIZE_BOUND_1080P; 325 if (CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_2160P)) { 326 maxVideoSize = SIZE_BOUND_2160P; 327 } 328 mSupportedVideoSizes = 329 getSupportedVideoSizes(cameraId, mCameraManager, maxVideoSize); 330 } 331 332 333 /** 334 * Test camera recording preview by using each available CamcorderProfile for a 335 * given camera. preview size is set to the video size. 336 */ 337 private void basicRecordingPreviewTestByCamera(int[] camcorderProfileList) 338 throws Exception { 339 Size maxPreviewSize = mOrderedPreviewSizes.get(0); 340 List<Range<Integer> > fpsRanges = Arrays.asList( 341 mStaticInfo.getAeAvailableTargetFpsRangesChecked()); 342 int cameraId = Integer.parseInt(mCamera.getId()); 343 int maxVideoFrameRate = -1; 344 int profileId = camcorderProfileList[0]; 345 if (!CamcorderProfile.hasProfile(cameraId, profileId) || 346 allowedUnsupported(cameraId, profileId)) { 347 return; 348 } 349 350 CamcorderProfile profile = CamcorderProfile.get(cameraId, profileId); 351 Size videoSz = new Size(profile.videoFrameWidth, profile.videoFrameHeight); 352 Range<Integer> fpsRange = new Range(profile.videoFrameRate, profile.videoFrameRate); 353 if (maxVideoFrameRate < profile.videoFrameRate) { 354 maxVideoFrameRate = profile.videoFrameRate; 355 } 356 if (mStaticInfo.isHardwareLevelLegacy() && 357 (videoSz.getWidth() > maxPreviewSize.getWidth() || 358 videoSz.getHeight() > maxPreviewSize.getHeight())) { 359 // Skip. Legacy mode can only do recording up to max preview size 360 return; 361 } 362 assertTrue("Video size " + videoSz.toString() + " for profile ID " + profileId + 363 " must be one of the camera device supported video size!", 364 mSupportedVideoSizes.contains(videoSz)); 365 assertTrue("Frame rate range " + fpsRange + " (for profile ID " + profileId + 366 ") must be one of the camera device available FPS range!", 367 fpsRanges.contains(fpsRange)); 368 369 if (VERBOSE) { 370 Log.v(TAG, "Testing camera recording with video size " + videoSz.toString()); 371 } 372 373 // Configure preview and recording surfaces. 374 mOutMediaFileName = VIDEO_FILE_PATH + "/test_video.mp4"; 375 if (DEBUG_DUMP) { 376 mOutMediaFileName = VIDEO_FILE_PATH + "/test_video_" + cameraId + "_" 377 + videoSz.toString() + ".mp4"; 378 } 379 380 prepareRecordingWithProfile(profile); 381 382 // prepare preview surface by using video size. 383 updatePreviewSurfaceWithVideo(videoSz, profile.videoFrameRate); 384 385 CaptureRequest.Builder previewRequest = 386 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); 387 CaptureRequest.Builder recordingRequest = 388 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_RECORD); 389 390 SimpleCaptureCallback resultListener = new SimpleCaptureCallback(); 391 SimpleImageReaderListener imageListener = new SimpleImageReaderListener(); 392 393 prepareVideoPreview(previewRequest, recordingRequest, resultListener, imageListener); 394 395 // Can reuse the MediaRecorder object after reset. 396 mMediaRecorder.reset(); 397 398 if (maxVideoFrameRate != -1) { 399 // At least one CamcorderProfile is present, check FPS 400 assertTrue("At least one CamcorderProfile must support >= 24 FPS", 401 maxVideoFrameRate >= 24); 402 } 403 } 404 405 private void releaseRecorder() { 406 if (mMediaRecorder != null) { 407 mMediaRecorder.release(); 408 mMediaRecorder = null; 409 } 410 } 411 412 private List<String> cameraColorOutputCheck() throws Exception { 413 List<String> mCameraColorOutputIds = new ArrayList<String>(); 414 for (String id : mCameraIds) { 415 openDevice(id); 416 if (!mStaticInfo.isColorOutputSupported()) { 417 Log.i(TAG, "Camera " + id + 418 " does not support color outputs, skipping"); 419 continue; 420 } 421 mCameraColorOutputIds.add(id); 422 closeDevice(); 423 } 424 return mCameraColorOutputIds; 425 } 426 427 /** 428 * Returns {@code true} if the {@link CamcorderProfile} ID is allowed to be unsupported. 429 * 430 * <p>This only allows unsupported profiles when using the LEGACY mode of the Camera API.</p> 431 * 432 * @param profileId a {@link CamcorderProfile} ID to check. 433 * @return {@code true} if supported. 434 */ 435 private boolean allowedUnsupported(int cameraId, int profileId) { 436 if (!mStaticInfo.isHardwareLevelLegacy()) { 437 return false; 438 } 439 440 switch(profileId) { 441 case CamcorderProfile.QUALITY_2160P: 442 case CamcorderProfile.QUALITY_1080P: 443 case CamcorderProfile.QUALITY_HIGH: 444 return !CamcorderProfile.hasProfile(cameraId, profileId) || 445 CamcorderProfile.get(cameraId, profileId).videoFrameWidth >= 1080; 446 } 447 return false; 448 } 449 450 /** 451 * Configure MediaRecorder recording session with CamcorderProfile, prepare 452 * the recording surface. 453 */ 454 private void prepareRecordingWithProfile(CamcorderProfile profile) 455 throws Exception { 456 // Prepare MediaRecorder. 457 mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER); 458 mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE); 459 mMediaRecorder.setProfile(profile); 460 mMediaRecorder.setOutputFile(mOutMediaFileName); 461 if (mPersistentSurface != null) { 462 mMediaRecorder.setInputSurface(mPersistentSurface); 463 mRecordingSurface = mPersistentSurface; 464 } 465 mMediaRecorder.prepare(); 466 if (mPersistentSurface == null) { 467 mRecordingSurface = mMediaRecorder.getSurface(); 468 } 469 assertNotNull("Recording surface must be non-null!", mRecordingSurface); 470 mVideoFrameRate = profile.videoFrameRate; 471 mVideoSize = new Size(profile.videoFrameWidth, profile.videoFrameHeight); 472 } 473 474 /** 475 * Update preview size with video size. 476 * 477 * <p>Preview size will be capped with max preview size.</p> 478 * 479 * @param videoSize The video size used for preview. 480 * @param videoFrameRate The video frame rate 481 * 482 */ 483 private void updatePreviewSurfaceWithVideo(Size videoSize, int videoFrameRate) throws Exception { 484 if (mOrderedPreviewSizes == null) { 485 throw new IllegalStateException("supported preview size list is not initialized yet"); 486 } 487 final float FRAME_DURATION_TOLERANCE = 0.01f; 488 long videoFrameDuration = (long) (1e9 / videoFrameRate * 489 (1.0 + FRAME_DURATION_TOLERANCE)); 490 HashMap<Size, Long> minFrameDurationMap = mStaticInfo. 491 getAvailableMinFrameDurationsForFormatChecked(ImageFormat.PRIVATE); 492 Size maxPreviewSize = mOrderedPreviewSizes.get(0); 493 Size previewSize = null; 494 if (videoSize.getWidth() > maxPreviewSize.getWidth() || 495 videoSize.getHeight() > maxPreviewSize.getHeight()) { 496 for (Size s : mOrderedPreviewSizes) { 497 Long frameDuration = minFrameDurationMap.get(s); 498 if (mStaticInfo.isHardwareLevelLegacy()) { 499 // Legacy doesn't report min frame duration 500 frameDuration = new Long(0); 501 } 502 assertTrue("Cannot find minimum frame duration for private size" + s, 503 frameDuration != null); 504 if (frameDuration <= videoFrameDuration && 505 s.getWidth() <= videoSize.getWidth() && 506 s.getHeight() <= videoSize.getHeight()) { 507 Log.w(TAG, "Overwrite preview size from " + videoSize.toString() + 508 " to " + s.toString()); 509 previewSize = s; 510 break; 511 // If all preview size doesn't work then we fallback to video size 512 } 513 } 514 } 515 if (previewSize == null) { 516 previewSize = videoSize; 517 } 518 519 updatePreviewSurface(previewSize); 520 } 521 522 protected void prepareVideoPreview(CaptureRequest.Builder previewRequest, 523 CaptureRequest.Builder recordingRequest, 524 CaptureCallback resultListener, 525 ImageReader.OnImageAvailableListener imageListener) throws Exception { 526 527 // Configure output streams with preview and jpeg streams. 528 List<Surface> outputSurfaces = new ArrayList<Surface>(); 529 outputSurfaces.add(mPreviewSurface); 530 outputSurfaces.add(mRecordingSurface); 531 532 mSessionListener = new BlockingSessionCallback(); 533 mSession = configureCameraSession(mCamera, outputSurfaces, mSessionListener, mHandler); 534 535 previewRequest.addTarget(mPreviewSurface); 536 recordingRequest.addTarget(mPreviewSurface); 537 recordingRequest.addTarget(mRecordingSurface); 538 539 // Start preview. 540 mSession.setRepeatingRequest(previewRequest.build(), null, mHandler); 541 } 542 543 protected void prepareCapturePreview(CaptureRequest.Builder previewRequest, 544 CaptureRequest.Builder stillRequest, 545 CaptureCallback resultListener, 546 ImageReader.OnImageAvailableListener imageListener) throws Exception { 547 548 Size captureSz = mOrderedStillSizes.get(0); 549 Size previewSz = mOrderedPreviewSizes.get(1); 550 551 if (VERBOSE) { 552 Log.v(TAG, String.format("Prepare single capture (%s) and preview (%s)", 553 captureSz.toString(), previewSz.toString())); 554 } 555 556 // Update preview size. 557 updatePreviewSurface(previewSz); 558 559 // Create ImageReader. 560 createImageReader(captureSz, ImageFormat.JPEG, MAX_READER_IMAGES, imageListener); 561 562 // Configure output streams with preview and jpeg streams. 563 List<Surface> outputSurfaces = new ArrayList<Surface>(); 564 outputSurfaces.add(mPreviewSurface); 565 outputSurfaces.add(mReaderSurface); 566 mSessionListener = new BlockingSessionCallback(); 567 mSession = configureCameraSession(mCamera, outputSurfaces, mSessionListener, mHandler); 568 569 // Configure the requests. 570 previewRequest.addTarget(mPreviewSurface); 571 stillRequest.addTarget(mPreviewSurface); 572 stillRequest.addTarget(mReaderSurface); 573 574 // Start preview. 575 mSession.setRepeatingRequest(previewRequest.build(), resultListener, mHandler); 576 } 577 578 } 579