1 /* 2 * Copyright 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.mediaframeworktest.Camera2SurfaceViewTestCase; 21 import com.android.mediaframeworktest.helpers.CameraTestUtils; 22 import com.android.mediaframeworktest.helpers.StaticMetadata; 23 24 import android.graphics.ImageFormat; 25 import android.hardware.camera2.CameraDevice; 26 import android.hardware.camera2.CaptureFailure; 27 import android.hardware.camera2.CaptureRequest; 28 import android.hardware.camera2.CaptureResult; 29 import android.hardware.camera2.TotalCaptureResult; 30 import android.hardware.camera2.params.InputConfiguration; 31 import android.media.Image; 32 import android.media.ImageReader; 33 import android.media.ImageWriter; 34 import android.util.Log; 35 import android.util.Size; 36 import android.view.Surface; 37 38 import java.util.ArrayList; 39 import java.util.Arrays; 40 import java.util.List; 41 42 import static com.android.mediaframeworktest.helpers.CameraTestUtils.EXIF_TEST_DATA; 43 import static com.android.mediaframeworktest.helpers.CameraTestUtils.SESSION_CLOSE_TIMEOUT_MS; 44 import static com.android.mediaframeworktest.helpers.CameraTestUtils.SimpleCaptureCallback; 45 import static com.android.mediaframeworktest.helpers.CameraTestUtils.SimpleImageReaderListener; 46 import static com.android.mediaframeworktest.helpers.CameraTestUtils.SimpleImageWriterListener; 47 import static com.android.mediaframeworktest.helpers.CameraTestUtils.configureReprocessableCameraSession; 48 import static com.android.mediaframeworktest.helpers.CameraTestUtils.dumpFile; 49 import static com.android.mediaframeworktest.helpers.CameraTestUtils.getAscendingOrderSizes; 50 import static com.android.mediaframeworktest.helpers.CameraTestUtils.getDataFromImage; 51 import static com.android.mediaframeworktest.helpers.CameraTestUtils.makeImageReader; 52 import static com.android.mediaframeworktest.helpers.CameraTestUtils.setJpegKeys; 53 import static com.android.mediaframeworktest.helpers.CameraTestUtils.verifyJpegKeys; 54 55 /** 56 * <p>Tests for Reprocess API.</p> 57 * 58 * adb shell am instrument \ 59 * -e class \ 60 * com.android.mediaframeworktest.stress.Camera2StillCaptureTest#Camera2ReprocessCaptureTest \ 61 * -e iterations 1 \ 62 * -e waitIntervalMs 1000 \ 63 * -e resultToFile false \ 64 * -r -w com.android.mediaframeworktest/.Camera2InstrumentationTestRunner 65 */ 66 public class Camera2ReprocessCaptureTest extends Camera2SurfaceViewTestCase { 67 private static final String TAG = "ReprocessCaptureTest"; 68 private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE); 69 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 70 private static final int CAPTURE_TIMEOUT_FRAMES = 100; 71 private static final int CAPTURE_TIMEOUT_MS = 3000; 72 private static final int WAIT_FOR_SURFACE_CHANGE_TIMEOUT_MS = 1000; 73 private static final int CAPTURE_TEMPLATE = CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG; 74 private static final int ZSL_TEMPLATE = CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG; 75 private static final int NUM_REPROCESS_TEST_LOOP = 3; 76 private static final int NUM_REPROCESS_CAPTURES = 3; 77 private static final int NUM_REPROCESS_BURST = 3; 78 private int mDumpFrameCount = 0; 79 80 // The image reader for the first regular capture 81 private ImageReader mFirstImageReader; 82 // The image reader for the reprocess capture 83 private ImageReader mSecondImageReader; 84 // A flag indicating whether the regular capture and the reprocess capture share the same image 85 // reader. If it's true, mFirstImageReader should be used for regular and reprocess outputs. 86 private boolean mShareOneImageReader; 87 private SimpleImageReaderListener mFirstImageReaderListener; 88 private SimpleImageReaderListener mSecondImageReaderListener; 89 private Surface mInputSurface; 90 private ImageWriter mImageWriter; 91 private SimpleImageWriterListener mImageWriterListener; 92 93 private enum CaptureTestCase { 94 SINGLE_SHOT, 95 BURST, 96 MIXED_BURST, 97 ABORT_CAPTURE, 98 TIMESTAMPS, 99 JPEG_EXIF, 100 REQUEST_KEYS, 101 } 102 103 /** 104 * Test YUV_420_888 -> JPEG with maximal supported sizes 105 */ testBasicYuvToJpegReprocessing()106 public void testBasicYuvToJpegReprocessing() throws Exception { 107 for (String id : mCameraIds) { 108 if (!isYuvReprocessSupported(id)) { 109 continue; 110 } 111 112 // Test iteration starts... 113 for (int iteration = 0; iteration < getIterationCount(); ++iteration) { 114 Log.v(TAG, String.format("Reprocessing YUV to JPEG: %d/%d", iteration + 1, 115 getIterationCount())); 116 // YUV_420_888 -> JPEG must be supported. 117 testBasicReprocessing(id, ImageFormat.YUV_420_888, ImageFormat.JPEG); 118 getResultPrinter().printStatus(getIterationCount(), iteration + 1, id); 119 Thread.sleep(getTestWaitIntervalMs()); 120 } 121 } 122 } 123 124 /** 125 * Test OPAQUE -> JPEG with maximal supported sizes 126 */ testBasicOpaqueToJpegReprocessing()127 public void testBasicOpaqueToJpegReprocessing() throws Exception { 128 for (String id : mCameraIds) { 129 if (!isOpaqueReprocessSupported(id)) { 130 continue; 131 } 132 133 // Test iteration starts... 134 for (int iteration = 0; iteration < getIterationCount(); ++iteration) { 135 Log.v(TAG, String.format("Reprocessing OPAQUE to JPEG: %d/%d", iteration + 1, 136 getIterationCount())); 137 // OPAQUE -> JPEG must be supported. 138 testBasicReprocessing(id, ImageFormat.PRIVATE, ImageFormat.JPEG); 139 getResultPrinter().printStatus(getIterationCount(), iteration + 1, id); 140 Thread.sleep(getTestWaitIntervalMs()); 141 } 142 143 } 144 } 145 146 /** 147 * Test all supported size and format combinations with preview. 148 */ testReprocessingSizeFormatWithPreview()149 public void testReprocessingSizeFormatWithPreview() throws Exception { 150 for (String id : mCameraIds) { 151 if (!isYuvReprocessSupported(id) && !isOpaqueReprocessSupported(id)) { 152 continue; 153 } 154 155 try { 156 // open Camera device 157 openDevice(id); 158 159 // Test iteration starts... 160 for (int iteration = 0; iteration < getIterationCount(); ++iteration) { 161 Log.v(TAG, String.format("Reprocessing size format with preview: %d/%d", 162 iteration + 1, getIterationCount())); 163 testReprocessingAllCombinations(id, mOrderedPreviewSizes.get(0), 164 CaptureTestCase.SINGLE_SHOT); 165 getResultPrinter().printStatus(getIterationCount(), iteration + 1, id); 166 Thread.sleep(getTestWaitIntervalMs()); 167 } 168 } finally { 169 closeDevice(); 170 } 171 } 172 } 173 174 /** 175 * Test burst captures mixed with regular and reprocess captures with and without preview. 176 */ testMixedBurstReprocessing()177 public void testMixedBurstReprocessing() throws Exception { 178 for (String id : mCameraIds) { 179 if (!isYuvReprocessSupported(id) && !isOpaqueReprocessSupported(id)) { 180 continue; 181 } 182 183 try { 184 // open Camera device 185 openDevice(id); 186 187 // Test iteration starts... 188 for (int iteration = 0; iteration < getIterationCount(); ++iteration) { 189 Log.v(TAG, String.format("Reprocessing mixed burst with or without preview: " 190 + "%d/%d", iteration + 1, getIterationCount())); 191 // no preview 192 testReprocessingAllCombinations(id, /*previewSize*/null, 193 CaptureTestCase.MIXED_BURST); 194 // with preview 195 testReprocessingAllCombinations(id, mOrderedPreviewSizes.get(0), 196 CaptureTestCase.MIXED_BURST); 197 getResultPrinter().printStatus(getIterationCount(), iteration + 1, id); 198 Thread.sleep(getTestWaitIntervalMs()); 199 } 200 } finally { 201 closeDevice(); 202 } 203 } 204 } 205 206 /** 207 * Test the input format and output format with the largest input and output sizes. 208 */ testBasicReprocessing(String cameraId, int inputFormat, int reprocessOutputFormat)209 private void testBasicReprocessing(String cameraId, int inputFormat, 210 int reprocessOutputFormat) throws Exception { 211 try { 212 openDevice(cameraId); 213 214 testReprocessingMaxSizes(cameraId, inputFormat, reprocessOutputFormat, 215 /* previewSize */null, CaptureTestCase.SINGLE_SHOT); 216 } finally { 217 closeDevice(); 218 } 219 } 220 221 /** 222 * Test the input format and output format with the largest input and output sizes for a 223 * certain test case. 224 */ testReprocessingMaxSizes(String cameraId, int inputFormat, int reprocessOutputFormat, Size previewSize, CaptureTestCase captureTestCase)225 private void testReprocessingMaxSizes(String cameraId, int inputFormat, 226 int reprocessOutputFormat, Size previewSize, CaptureTestCase captureTestCase) 227 throws Exception { 228 Size maxInputSize = getMaxSize(inputFormat, StaticMetadata.StreamDirection.Input); 229 Size maxReprocessOutputSize = 230 getMaxSize(reprocessOutputFormat, StaticMetadata.StreamDirection.Output); 231 232 switch (captureTestCase) { 233 case SINGLE_SHOT: 234 testReprocess(cameraId, maxInputSize, inputFormat, maxReprocessOutputSize, 235 reprocessOutputFormat, previewSize, NUM_REPROCESS_CAPTURES); 236 break; 237 case ABORT_CAPTURE: 238 testReprocessAbort(cameraId, maxInputSize, inputFormat, maxReprocessOutputSize, 239 reprocessOutputFormat); 240 break; 241 case TIMESTAMPS: 242 testReprocessTimestamps(cameraId, maxInputSize, inputFormat, maxReprocessOutputSize, 243 reprocessOutputFormat); 244 break; 245 case JPEG_EXIF: 246 testReprocessJpegExif(cameraId, maxInputSize, inputFormat, maxReprocessOutputSize); 247 break; 248 case REQUEST_KEYS: 249 testReprocessRequestKeys(cameraId, maxInputSize, inputFormat, 250 maxReprocessOutputSize, reprocessOutputFormat); 251 break; 252 default: 253 throw new IllegalArgumentException("Invalid test case"); 254 } 255 } 256 257 /** 258 * Test all input format, input size, output format, and output size combinations. 259 */ testReprocessingAllCombinations(String cameraId, Size previewSize, CaptureTestCase captureTestCase)260 private void testReprocessingAllCombinations(String cameraId, Size previewSize, 261 CaptureTestCase captureTestCase) throws Exception { 262 263 int[] supportedInputFormats = 264 mStaticInfo.getAvailableFormats(StaticMetadata.StreamDirection.Input); 265 for (int inputFormat : supportedInputFormats) { 266 Size[] supportedInputSizes = 267 mStaticInfo.getAvailableSizesForFormatChecked(inputFormat, 268 StaticMetadata.StreamDirection.Input); 269 270 for (Size inputSize : supportedInputSizes) { 271 int[] supportedReprocessOutputFormats = 272 mStaticInfo.getValidOutputFormatsForInput(inputFormat); 273 274 for (int reprocessOutputFormat : supportedReprocessOutputFormats) { 275 Size[] supportedReprocessOutputSizes = 276 mStaticInfo.getAvailableSizesForFormatChecked(reprocessOutputFormat, 277 StaticMetadata.StreamDirection.Output); 278 279 for (Size reprocessOutputSize : supportedReprocessOutputSizes) { 280 switch (captureTestCase) { 281 case SINGLE_SHOT: 282 testReprocess(cameraId, inputSize, inputFormat, 283 reprocessOutputSize, reprocessOutputFormat, previewSize, 284 NUM_REPROCESS_CAPTURES); 285 break; 286 case BURST: 287 testReprocessBurst(cameraId, inputSize, inputFormat, 288 reprocessOutputSize, reprocessOutputFormat, previewSize, 289 NUM_REPROCESS_BURST); 290 break; 291 case MIXED_BURST: 292 testReprocessMixedBurst(cameraId, inputSize, inputFormat, 293 reprocessOutputSize, reprocessOutputFormat, previewSize, 294 NUM_REPROCESS_BURST); 295 break; 296 default: 297 throw new IllegalArgumentException("Invalid test case"); 298 } 299 } 300 } 301 } 302 } 303 } 304 305 /** 306 * Test burst that is mixed with regular and reprocess capture requests. 307 */ testReprocessMixedBurst(String cameraId, Size inputSize, int inputFormat, Size reprocessOutputSize, int reprocessOutputFormat, Size previewSize, int numBurst)308 private void testReprocessMixedBurst(String cameraId, Size inputSize, int inputFormat, 309 Size reprocessOutputSize, int reprocessOutputFormat, Size previewSize, 310 int numBurst) throws Exception { 311 if (VERBOSE) { 312 Log.v(TAG, "testReprocessMixedBurst: cameraId: " + cameraId + " inputSize: " + 313 inputSize + " inputFormat: " + inputFormat + " reprocessOutputSize: " + 314 reprocessOutputSize + " reprocessOutputFormat: " + reprocessOutputFormat + 315 " previewSize: " + previewSize + " numBurst: " + numBurst); 316 } 317 318 boolean enablePreview = (previewSize != null); 319 ImageResultHolder[] imageResultHolders = new ImageResultHolder[0]; 320 321 try { 322 // totalNumBurst = number of regular burst + number of reprocess burst. 323 int totalNumBurst = numBurst * 2; 324 325 if (enablePreview) { 326 updatePreviewSurface(previewSize); 327 } else { 328 mPreviewSurface = null; 329 } 330 331 setupImageReaders(inputSize, inputFormat, reprocessOutputSize, reprocessOutputFormat, 332 totalNumBurst); 333 setupReprocessableSession(mPreviewSurface, /*numImageWriterImages*/numBurst); 334 335 if (enablePreview) { 336 startPreview(mPreviewSurface); 337 } 338 339 // Prepare an array of booleans indicating each capture's type (regular or reprocess) 340 boolean[] isReprocessCaptures = new boolean[totalNumBurst]; 341 for (int i = 0; i < totalNumBurst; i++) { 342 if ((i & 1) == 0) { 343 isReprocessCaptures[i] = true; 344 } else { 345 isReprocessCaptures[i] = false; 346 } 347 } 348 349 imageResultHolders = doMixedReprocessBurstCapture(isReprocessCaptures); 350 for (ImageResultHolder holder : imageResultHolders) { 351 Image reprocessedImage = holder.getImage(); 352 TotalCaptureResult result = holder.getTotalCaptureResult(); 353 354 mCollector.expectImageProperties("testReprocessMixedBurst", reprocessedImage, 355 reprocessOutputFormat, reprocessOutputSize, 356 result.get(CaptureResult.SENSOR_TIMESTAMP)); 357 358 if (DEBUG) { 359 Log.d(TAG, String.format("camera %s in %dx%d %d out %dx%d %d", 360 cameraId, inputSize.getWidth(), inputSize.getHeight(), inputFormat, 361 reprocessOutputSize.getWidth(), reprocessOutputSize.getHeight(), 362 reprocessOutputFormat)); 363 dumpImage(reprocessedImage, 364 "/testReprocessMixedBurst_camera" + cameraId + "_" + mDumpFrameCount); 365 mDumpFrameCount++; 366 } 367 } 368 } finally { 369 for (ImageResultHolder holder : imageResultHolders) { 370 holder.getImage().close(); 371 } 372 closeReprossibleSession(); 373 closeImageReaders(); 374 } 375 } 376 377 /** 378 * Test burst of reprocess capture requests. 379 */ testReprocessBurst(String cameraId, Size inputSize, int inputFormat, Size reprocessOutputSize, int reprocessOutputFormat, Size previewSize, int numBurst)380 private void testReprocessBurst(String cameraId, Size inputSize, int inputFormat, 381 Size reprocessOutputSize, int reprocessOutputFormat, Size previewSize, 382 int numBurst) throws Exception { 383 if (VERBOSE) { 384 Log.v(TAG, "testReprocessBurst: cameraId: " + cameraId + " inputSize: " + 385 inputSize + " inputFormat: " + inputFormat + " reprocessOutputSize: " + 386 reprocessOutputSize + " reprocessOutputFormat: " + reprocessOutputFormat + 387 " previewSize: " + previewSize + " numBurst: " + numBurst); 388 } 389 390 boolean enablePreview = (previewSize != null); 391 ImageResultHolder[] imageResultHolders = new ImageResultHolder[0]; 392 393 try { 394 if (enablePreview) { 395 updatePreviewSurface(previewSize); 396 } else { 397 mPreviewSurface = null; 398 } 399 400 setupImageReaders(inputSize, inputFormat, reprocessOutputSize, reprocessOutputFormat, 401 numBurst); 402 setupReprocessableSession(mPreviewSurface, numBurst); 403 404 if (enablePreview) { 405 startPreview(mPreviewSurface); 406 } 407 408 imageResultHolders = doReprocessBurstCapture(numBurst); 409 for (ImageResultHolder holder : imageResultHolders) { 410 Image reprocessedImage = holder.getImage(); 411 TotalCaptureResult result = holder.getTotalCaptureResult(); 412 413 mCollector.expectImageProperties("testReprocessBurst", reprocessedImage, 414 reprocessOutputFormat, reprocessOutputSize, 415 result.get(CaptureResult.SENSOR_TIMESTAMP)); 416 417 if (DEBUG) { 418 Log.d(TAG, String.format("camera %s in %dx%d %d out %dx%d %d", 419 cameraId, inputSize.getWidth(), inputSize.getHeight(), inputFormat, 420 reprocessOutputSize.getWidth(), reprocessOutputSize.getHeight(), 421 reprocessOutputFormat)); 422 dumpImage(reprocessedImage, 423 "/testReprocessBurst_camera" + cameraId + "_" + mDumpFrameCount); 424 mDumpFrameCount++; 425 } 426 } 427 } finally { 428 for (ImageResultHolder holder : imageResultHolders) { 429 holder.getImage().close(); 430 } 431 closeReprossibleSession(); 432 closeImageReaders(); 433 } 434 } 435 436 /** 437 * Test a sequences of reprocess capture requests. 438 */ testReprocess(String cameraId, Size inputSize, int inputFormat, Size reprocessOutputSize, int reprocessOutputFormat, Size previewSize, int numReprocessCaptures)439 private void testReprocess(String cameraId, Size inputSize, int inputFormat, 440 Size reprocessOutputSize, int reprocessOutputFormat, Size previewSize, 441 int numReprocessCaptures) throws Exception { 442 if (VERBOSE) { 443 Log.v(TAG, "testReprocess: cameraId: " + cameraId + " inputSize: " + 444 inputSize + " inputFormat: " + inputFormat + " reprocessOutputSize: " + 445 reprocessOutputSize + " reprocessOutputFormat: " + reprocessOutputFormat + 446 " previewSize: " + previewSize); 447 } 448 449 boolean enablePreview = (previewSize != null); 450 451 try { 452 if (enablePreview) { 453 updatePreviewSurface(previewSize); 454 } else { 455 mPreviewSurface = null; 456 } 457 458 setupImageReaders(inputSize, inputFormat, reprocessOutputSize, reprocessOutputFormat, 459 /*maxImages*/1); 460 setupReprocessableSession(mPreviewSurface, /*numImageWriterImages*/1); 461 462 if (enablePreview) { 463 startPreview(mPreviewSurface); 464 } 465 466 for (int i = 0; i < numReprocessCaptures; i++) { 467 ImageResultHolder imageResultHolder = null; 468 469 try { 470 imageResultHolder = doReprocessCapture(); 471 Image reprocessedImage = imageResultHolder.getImage(); 472 TotalCaptureResult result = imageResultHolder.getTotalCaptureResult(); 473 474 mCollector.expectImageProperties("testReprocess", reprocessedImage, 475 reprocessOutputFormat, reprocessOutputSize, 476 result.get(CaptureResult.SENSOR_TIMESTAMP)); 477 478 if (DEBUG) { 479 Log.d(TAG, String.format("camera %s in %dx%d %d out %dx%d %d", 480 cameraId, inputSize.getWidth(), inputSize.getHeight(), inputFormat, 481 reprocessOutputSize.getWidth(), reprocessOutputSize.getHeight(), 482 reprocessOutputFormat)); 483 484 dumpImage(reprocessedImage, 485 "/testReprocess_camera" + cameraId + "_" + mDumpFrameCount); 486 mDumpFrameCount++; 487 } 488 } finally { 489 if (imageResultHolder != null) { 490 imageResultHolder.getImage().close(); 491 } 492 } 493 } 494 } finally { 495 closeReprossibleSession(); 496 closeImageReaders(); 497 } 498 } 499 500 /** 501 * Test aborting a burst reprocess capture and multiple single reprocess captures. 502 */ testReprocessAbort(String cameraId, Size inputSize, int inputFormat, Size reprocessOutputSize, int reprocessOutputFormat)503 private void testReprocessAbort(String cameraId, Size inputSize, int inputFormat, 504 Size reprocessOutputSize, int reprocessOutputFormat) throws Exception { 505 if (VERBOSE) { 506 Log.v(TAG, "testReprocessAbort: cameraId: " + cameraId + " inputSize: " + 507 inputSize + " inputFormat: " + inputFormat + " reprocessOutputSize: " + 508 reprocessOutputSize + " reprocessOutputFormat: " + reprocessOutputFormat); 509 } 510 511 try { 512 setupImageReaders(inputSize, inputFormat, reprocessOutputSize, reprocessOutputFormat, 513 NUM_REPROCESS_CAPTURES); 514 setupReprocessableSession(/*previewSurface*/null, NUM_REPROCESS_CAPTURES); 515 516 // Test two cases: submitting reprocess requests one by one and in a burst. 517 boolean submitInBursts[] = {false, true}; 518 for (boolean submitInBurst : submitInBursts) { 519 // Prepare reprocess capture requests. 520 ArrayList<CaptureRequest> reprocessRequests = 521 new ArrayList<>(NUM_REPROCESS_CAPTURES); 522 523 for (int i = 0; i < NUM_REPROCESS_CAPTURES; i++) { 524 TotalCaptureResult result = submitCaptureRequest(mFirstImageReader.getSurface(), 525 /*inputResult*/null); 526 527 mImageWriter.queueInputImage( 528 mFirstImageReaderListener.getImage(CAPTURE_TIMEOUT_MS)); 529 CaptureRequest.Builder builder = mCamera.createReprocessCaptureRequest(result); 530 builder.addTarget(getReprocessOutputImageReader().getSurface()); 531 reprocessRequests.add(builder.build()); 532 } 533 534 SimpleCaptureCallback captureCallback = new SimpleCaptureCallback(); 535 536 // Submit reprocess capture requests. 537 if (submitInBurst) { 538 mSession.captureBurst(reprocessRequests, captureCallback, mHandler); 539 } else { 540 for (CaptureRequest request : reprocessRequests) { 541 mSession.capture(request, captureCallback, mHandler); 542 } 543 } 544 545 // Abort after getting the first result 546 TotalCaptureResult reprocessResult = 547 captureCallback.getTotalCaptureResultForRequest(reprocessRequests.get(0), 548 CAPTURE_TIMEOUT_FRAMES); 549 mSession.abortCaptures(); 550 551 // Wait until the session is ready again. 552 mSessionListener.getStateWaiter().waitForState( 553 BlockingSessionCallback.SESSION_READY, SESSION_CLOSE_TIMEOUT_MS); 554 555 // Gather all failed requests. 556 ArrayList<CaptureFailure> failures = 557 captureCallback.getCaptureFailures(NUM_REPROCESS_CAPTURES - 1); 558 ArrayList<CaptureRequest> failedRequests = new ArrayList<>(); 559 for (CaptureFailure failure : failures) { 560 failedRequests.add(failure.getRequest()); 561 } 562 563 // For each request that didn't fail must have a valid result. 564 for (int i = 1; i < reprocessRequests.size(); i++) { 565 CaptureRequest request = reprocessRequests.get(i); 566 if (!failedRequests.contains(request)) { 567 captureCallback.getTotalCaptureResultForRequest(request, 568 CAPTURE_TIMEOUT_FRAMES); 569 } 570 } 571 572 // Drain the image reader listeners. 573 mFirstImageReaderListener.drain(); 574 if (!mShareOneImageReader) { 575 mSecondImageReaderListener.drain(); 576 } 577 578 // Make sure all input surfaces are released. 579 for (int i = 0; i < NUM_REPROCESS_CAPTURES; i++) { 580 mImageWriterListener.waitForImageReleased(CAPTURE_TIMEOUT_MS); 581 } 582 } 583 } finally { 584 closeReprossibleSession(); 585 closeImageReaders(); 586 } 587 } 588 589 /** 590 * Test timestamps for reprocess requests. Reprocess request's shutter timestamp, result's 591 * sensor timestamp, and output image's timestamp should match the reprocess input's timestamp. 592 */ testReprocessTimestamps(String cameraId, Size inputSize, int inputFormat, Size reprocessOutputSize, int reprocessOutputFormat)593 private void testReprocessTimestamps(String cameraId, Size inputSize, int inputFormat, 594 Size reprocessOutputSize, int reprocessOutputFormat) throws Exception { 595 if (VERBOSE) { 596 Log.v(TAG, "testReprocessTimestamps: cameraId: " + cameraId + " inputSize: " + 597 inputSize + " inputFormat: " + inputFormat + " reprocessOutputSize: " + 598 reprocessOutputSize + " reprocessOutputFormat: " + reprocessOutputFormat); 599 } 600 601 try { 602 setupImageReaders(inputSize, inputFormat, reprocessOutputSize, reprocessOutputFormat, 603 NUM_REPROCESS_CAPTURES); 604 setupReprocessableSession(/*previewSurface*/null, NUM_REPROCESS_CAPTURES); 605 606 // Prepare reprocess capture requests. 607 ArrayList<CaptureRequest> reprocessRequests = new ArrayList<>(NUM_REPROCESS_CAPTURES); 608 ArrayList<Long> expectedTimestamps = new ArrayList<>(NUM_REPROCESS_CAPTURES); 609 610 for (int i = 0; i < NUM_REPROCESS_CAPTURES; i++) { 611 TotalCaptureResult result = submitCaptureRequest(mFirstImageReader.getSurface(), 612 /*inputResult*/null); 613 614 mImageWriter.queueInputImage( 615 mFirstImageReaderListener.getImage(CAPTURE_TIMEOUT_MS)); 616 CaptureRequest.Builder builder = mCamera.createReprocessCaptureRequest(result); 617 builder.addTarget(getReprocessOutputImageReader().getSurface()); 618 reprocessRequests.add(builder.build()); 619 // Reprocess result's timestamp should match input image's timestamp. 620 expectedTimestamps.add(result.get(CaptureResult.SENSOR_TIMESTAMP)); 621 } 622 623 // Submit reprocess requests. 624 SimpleCaptureCallback captureCallback = new SimpleCaptureCallback(); 625 mSession.captureBurst(reprocessRequests, captureCallback, mHandler); 626 627 // Verify we get the expected timestamps. 628 for (int i = 0; i < reprocessRequests.size(); i++) { 629 captureCallback.waitForCaptureStart(reprocessRequests.get(i), 630 expectedTimestamps.get(i), CAPTURE_TIMEOUT_FRAMES); 631 } 632 633 TotalCaptureResult[] reprocessResults = 634 captureCallback.getTotalCaptureResultsForRequests(reprocessRequests, 635 CAPTURE_TIMEOUT_FRAMES); 636 637 for (int i = 0; i < expectedTimestamps.size(); i++) { 638 // Verify the result timestamps match the input image's timestamps. 639 long expected = expectedTimestamps.get(i); 640 long timestamp = reprocessResults[i].get(CaptureResult.SENSOR_TIMESTAMP); 641 assertEquals("Reprocess result timestamp (" + timestamp + ") doesn't match input " + 642 "image's timestamp (" + expected + ")", expected, timestamp); 643 644 // Verify the reprocess output image timestamps match the input image's timestamps. 645 Image image = getReprocessOutputImageReaderListener().getImage(CAPTURE_TIMEOUT_MS); 646 timestamp = image.getTimestamp(); 647 image.close(); 648 649 assertEquals("Reprocess output timestamp (" + timestamp + ") doesn't match input " + 650 "image's timestamp (" + expected + ")", expected, timestamp); 651 } 652 653 // Make sure all input surfaces are released. 654 for (int i = 0; i < NUM_REPROCESS_CAPTURES; i++) { 655 mImageWriterListener.waitForImageReleased(CAPTURE_TIMEOUT_MS); 656 } 657 } finally { 658 closeReprossibleSession(); 659 closeImageReaders(); 660 } 661 } 662 663 /** 664 * Test JPEG tags for reprocess requests. Reprocess result's JPEG tags and JPEG image's tags 665 * match reprocess request's JPEG tags. 666 */ testReprocessJpegExif(String cameraId, Size inputSize, int inputFormat, Size reprocessOutputSize)667 private void testReprocessJpegExif(String cameraId, Size inputSize, int inputFormat, 668 Size reprocessOutputSize) throws Exception { 669 if (VERBOSE) { 670 Log.v(TAG, "testReprocessJpegExif: cameraId: " + cameraId + " inputSize: " + 671 inputSize + " inputFormat: " + inputFormat + " reprocessOutputSize: " + 672 reprocessOutputSize); 673 } 674 675 Size[] thumbnailSizes = mStaticInfo.getAvailableThumbnailSizesChecked(); 676 Size[] testThumbnailSizes = new Size[EXIF_TEST_DATA.length]; 677 Arrays.fill(testThumbnailSizes, thumbnailSizes[thumbnailSizes.length - 1]); 678 // Make sure thumbnail size (0, 0) is covered. 679 testThumbnailSizes[0] = new Size(0, 0); 680 681 try { 682 setupImageReaders(inputSize, inputFormat, reprocessOutputSize, ImageFormat.JPEG, 683 EXIF_TEST_DATA.length); 684 setupReprocessableSession(/*previewSurface*/null, EXIF_TEST_DATA.length); 685 686 // Prepare reprocess capture requests. 687 ArrayList<CaptureRequest> reprocessRequests = new ArrayList<>(EXIF_TEST_DATA.length); 688 689 for (int i = 0; i < EXIF_TEST_DATA.length; i++) { 690 TotalCaptureResult result = submitCaptureRequest(mFirstImageReader.getSurface(), 691 /*inputResult*/null); 692 mImageWriter.queueInputImage( 693 mFirstImageReaderListener.getImage(CAPTURE_TIMEOUT_MS)); 694 695 CaptureRequest.Builder builder = mCamera.createReprocessCaptureRequest(result); 696 builder.addTarget(getReprocessOutputImageReader().getSurface()); 697 698 // set jpeg keys 699 setJpegKeys(builder, EXIF_TEST_DATA[i], testThumbnailSizes[i], mCollector); 700 reprocessRequests.add(builder.build()); 701 } 702 703 // Submit reprocess requests. 704 SimpleCaptureCallback captureCallback = new SimpleCaptureCallback(); 705 mSession.captureBurst(reprocessRequests, captureCallback, mHandler); 706 707 TotalCaptureResult[] reprocessResults = 708 captureCallback.getTotalCaptureResultsForRequests(reprocessRequests, 709 CAPTURE_TIMEOUT_FRAMES); 710 711 for (int i = 0; i < EXIF_TEST_DATA.length; i++) { 712 // Verify output image's and result's JPEG EXIF data. 713 Image image = getReprocessOutputImageReaderListener().getImage(CAPTURE_TIMEOUT_MS); 714 verifyJpegKeys(image, reprocessResults[i], reprocessOutputSize, 715 testThumbnailSizes[i], EXIF_TEST_DATA[i], mStaticInfo, mCollector); 716 image.close(); 717 718 } 719 } finally { 720 closeReprossibleSession(); 721 closeImageReaders(); 722 } 723 } 724 725 726 727 /** 728 * Test the following keys in reprocess results match the keys in reprocess requests: 729 * 1. EDGE_MODE 730 * 2. NOISE_REDUCTION_MODE 731 * 3. REPROCESS_EFFECTIVE_EXPOSURE_FACTOR (only for YUV reprocess) 732 */ testReprocessRequestKeys(String cameraId, Size inputSize, int inputFormat, Size reprocessOutputSize, int reprocessOutputFormat)733 private void testReprocessRequestKeys(String cameraId, Size inputSize, int inputFormat, 734 Size reprocessOutputSize, int reprocessOutputFormat) throws Exception { 735 if (VERBOSE) { 736 Log.v(TAG, "testReprocessRequestKeys: cameraId: " + cameraId + " inputSize: " + 737 inputSize + " inputFormat: " + inputFormat + " reprocessOutputSize: " + 738 reprocessOutputSize + " reprocessOutputFormat: " + reprocessOutputFormat); 739 } 740 741 final Integer[] EDGE_MODES = {CaptureRequest.EDGE_MODE_FAST, 742 CaptureRequest.EDGE_MODE_HIGH_QUALITY, CaptureRequest.EDGE_MODE_OFF, 743 CaptureRequest.EDGE_MODE_ZERO_SHUTTER_LAG}; 744 final Integer[] NR_MODES = {CaptureRequest.NOISE_REDUCTION_MODE_HIGH_QUALITY, 745 CaptureRequest.NOISE_REDUCTION_MODE_OFF, 746 CaptureRequest.NOISE_REDUCTION_MODE_ZERO_SHUTTER_LAG, 747 CaptureRequest.NOISE_REDUCTION_MODE_FAST}; 748 final Float[] EFFECTIVE_EXP_FACTORS = {null, 1.0f, 2.5f, 4.0f}; 749 int numFrames = EDGE_MODES.length; 750 751 try { 752 setupImageReaders(inputSize, inputFormat, reprocessOutputSize, reprocessOutputFormat, 753 numFrames); 754 setupReprocessableSession(/*previewSurface*/null, numFrames); 755 756 // Prepare reprocess capture requests. 757 ArrayList<CaptureRequest> reprocessRequests = new ArrayList<>(numFrames); 758 759 for (int i = 0; i < numFrames; i++) { 760 TotalCaptureResult result = submitCaptureRequest(mFirstImageReader.getSurface(), 761 /*inputResult*/null); 762 mImageWriter.queueInputImage( 763 mFirstImageReaderListener.getImage(CAPTURE_TIMEOUT_MS)); 764 765 CaptureRequest.Builder builder = mCamera.createReprocessCaptureRequest(result); 766 builder.addTarget(getReprocessOutputImageReader().getSurface()); 767 768 // Set reprocess request keys 769 builder.set(CaptureRequest.EDGE_MODE, EDGE_MODES[i]); 770 builder.set(CaptureRequest.NOISE_REDUCTION_MODE, NR_MODES[i]); 771 if (inputFormat == ImageFormat.YUV_420_888) { 772 builder.set(CaptureRequest.REPROCESS_EFFECTIVE_EXPOSURE_FACTOR, 773 EFFECTIVE_EXP_FACTORS[i]); 774 } 775 reprocessRequests.add(builder.build()); 776 } 777 778 // Submit reprocess requests. 779 SimpleCaptureCallback captureCallback = new SimpleCaptureCallback(); 780 mSession.captureBurst(reprocessRequests, captureCallback, mHandler); 781 782 TotalCaptureResult[] reprocessResults = 783 captureCallback.getTotalCaptureResultsForRequests(reprocessRequests, 784 CAPTURE_TIMEOUT_FRAMES); 785 786 for (int i = 0; i < numFrames; i++) { 787 // Verify result's keys 788 Integer resultEdgeMode = reprocessResults[i].get(CaptureResult.EDGE_MODE); 789 Integer resultNoiseReductionMode = 790 reprocessResults[i].get(CaptureResult.NOISE_REDUCTION_MODE); 791 792 assertEquals("Reprocess result edge mode (" + resultEdgeMode + 793 ") doesn't match requested edge mode (" + EDGE_MODES[i] + ")", 794 resultEdgeMode, EDGE_MODES[i]); 795 assertEquals("Reprocess result noise reduction mode (" + resultNoiseReductionMode + 796 ") doesn't match requested noise reduction mode (" + 797 NR_MODES[i] + ")", resultNoiseReductionMode, 798 NR_MODES[i]); 799 800 if (inputFormat == ImageFormat.YUV_420_888) { 801 Float resultEffectiveExposureFactor = reprocessResults[i].get( 802 CaptureResult.REPROCESS_EFFECTIVE_EXPOSURE_FACTOR); 803 assertEquals("Reprocess effective exposure factor (" + 804 resultEffectiveExposureFactor + ") doesn't match requested " + 805 "effective exposure factor (" + EFFECTIVE_EXP_FACTORS[i] + ")", 806 resultEffectiveExposureFactor, EFFECTIVE_EXP_FACTORS[i]); 807 } 808 } 809 } finally { 810 closeReprossibleSession(); 811 closeImageReaders(); 812 } 813 } 814 815 /** 816 * Set up two image readers: one for regular capture (used for reprocess input) and one for 817 * reprocess capture. 818 */ setupImageReaders(Size inputSize, int inputFormat, Size reprocessOutputSize, int reprocessOutputFormat, int maxImages)819 private void setupImageReaders(Size inputSize, int inputFormat, Size reprocessOutputSize, 820 int reprocessOutputFormat, int maxImages) { 821 822 mShareOneImageReader = false; 823 // If the regular output and reprocess output have the same size and format, 824 // they can share one image reader. 825 if (inputFormat == reprocessOutputFormat && 826 inputSize.equals(reprocessOutputSize)) { 827 maxImages *= 2; 828 mShareOneImageReader = true; 829 } 830 // create an ImageReader for the regular capture 831 mFirstImageReaderListener = new SimpleImageReaderListener(); 832 mFirstImageReader = makeImageReader(inputSize, inputFormat, maxImages, 833 mFirstImageReaderListener, mHandler); 834 835 if (!mShareOneImageReader) { 836 // create an ImageReader for the reprocess capture 837 mSecondImageReaderListener = new SimpleImageReaderListener(); 838 mSecondImageReader = makeImageReader(reprocessOutputSize, reprocessOutputFormat, 839 maxImages, mSecondImageReaderListener, mHandler); 840 } 841 } 842 843 /** 844 * Close two image readers. 845 */ closeImageReaders()846 private void closeImageReaders() { 847 CameraTestUtils.closeImageReader(mFirstImageReader); 848 mFirstImageReader = null; 849 CameraTestUtils.closeImageReader(mSecondImageReader); 850 mSecondImageReader = null; 851 } 852 853 /** 854 * Get the ImageReader for reprocess output. 855 */ getReprocessOutputImageReader()856 private ImageReader getReprocessOutputImageReader() { 857 if (mShareOneImageReader) { 858 return mFirstImageReader; 859 } else { 860 return mSecondImageReader; 861 } 862 } 863 getReprocessOutputImageReaderListener()864 private SimpleImageReaderListener getReprocessOutputImageReaderListener() { 865 if (mShareOneImageReader) { 866 return mFirstImageReaderListener; 867 } else { 868 return mSecondImageReaderListener; 869 } 870 } 871 872 /** 873 * Set up a reprocessable session and create an ImageWriter with the sessoin's input surface. 874 */ setupReprocessableSession(Surface previewSurface, int numImageWriterImages)875 private void setupReprocessableSession(Surface previewSurface, int numImageWriterImages) 876 throws Exception { 877 // create a reprocessable capture session 878 List<Surface> outSurfaces = new ArrayList<Surface>(); 879 outSurfaces.add(mFirstImageReader.getSurface()); 880 if (!mShareOneImageReader) { 881 outSurfaces.add(mSecondImageReader.getSurface()); 882 } 883 if (previewSurface != null) { 884 outSurfaces.add(previewSurface); 885 } 886 887 InputConfiguration inputConfig = new InputConfiguration(mFirstImageReader.getWidth(), 888 mFirstImageReader.getHeight(), mFirstImageReader.getImageFormat()); 889 String inputConfigString = inputConfig.toString(); 890 if (VERBOSE) { 891 Log.v(TAG, "InputConfiguration: " + inputConfigString); 892 } 893 assertTrue(String.format("inputConfig is wrong: %dx%d format %d. Expect %dx%d format %d", 894 inputConfig.getWidth(), inputConfig.getHeight(), inputConfig.getFormat(), 895 mFirstImageReader.getWidth(), mFirstImageReader.getHeight(), 896 mFirstImageReader.getImageFormat()), 897 inputConfig.getWidth() == mFirstImageReader.getWidth() && 898 inputConfig.getHeight() == mFirstImageReader.getHeight() && 899 inputConfig.getFormat() == mFirstImageReader.getImageFormat()); 900 901 mSessionListener = new BlockingSessionCallback(); 902 mSession = configureReprocessableCameraSession(mCamera, inputConfig, outSurfaces, 903 mSessionListener, mHandler); 904 905 // create an ImageWriter 906 mInputSurface = mSession.getInputSurface(); 907 mImageWriter = ImageWriter.newInstance(mInputSurface, 908 numImageWriterImages); 909 910 mImageWriterListener = new SimpleImageWriterListener(mImageWriter); 911 mImageWriter.setOnImageReleasedListener(mImageWriterListener, mHandler); 912 } 913 914 /** 915 * Close the reprocessable session and ImageWriter. 916 */ closeReprossibleSession()917 private void closeReprossibleSession() { 918 mInputSurface = null; 919 920 if (mSession != null) { 921 mSession.close(); 922 mSession = null; 923 } 924 925 if (mImageWriter != null) { 926 mImageWriter.close(); 927 mImageWriter = null; 928 } 929 } 930 931 /** 932 * Do one reprocess capture. 933 */ doReprocessCapture()934 private ImageResultHolder doReprocessCapture() throws Exception { 935 return doReprocessBurstCapture(/*numBurst*/1)[0]; 936 } 937 938 /** 939 * Do a burst of reprocess captures. 940 */ doReprocessBurstCapture(int numBurst)941 private ImageResultHolder[] doReprocessBurstCapture(int numBurst) throws Exception { 942 boolean[] isReprocessCaptures = new boolean[numBurst]; 943 for (int i = 0; i < numBurst; i++) { 944 isReprocessCaptures[i] = true; 945 } 946 947 return doMixedReprocessBurstCapture(isReprocessCaptures); 948 } 949 950 /** 951 * Do a burst of captures that are mixed with regular and reprocess captures. 952 * 953 * @param isReprocessCaptures An array whose elements indicate whether it's a reprocess capture 954 * request. If the element is true, it represents a reprocess capture 955 * request. If the element is false, it represents a regular capture 956 * request. The size of the array is the number of capture requests 957 * in the burst. 958 */ doMixedReprocessBurstCapture(boolean[] isReprocessCaptures)959 private ImageResultHolder[] doMixedReprocessBurstCapture(boolean[] isReprocessCaptures) 960 throws Exception { 961 if (isReprocessCaptures == null || isReprocessCaptures.length <= 0) { 962 throw new IllegalArgumentException("isReprocessCaptures must have at least 1 capture."); 963 } 964 965 boolean hasReprocessRequest = false; 966 boolean hasRegularRequest = false; 967 968 TotalCaptureResult[] results = new TotalCaptureResult[isReprocessCaptures.length]; 969 for (int i = 0; i < isReprocessCaptures.length; i++) { 970 // submit a capture and get the result if this entry is a reprocess capture. 971 if (isReprocessCaptures[i]) { 972 results[i] = submitCaptureRequest(mFirstImageReader.getSurface(), 973 /*inputResult*/null); 974 mImageWriter.queueInputImage( 975 mFirstImageReaderListener.getImage(CAPTURE_TIMEOUT_MS)); 976 hasReprocessRequest = true; 977 } else { 978 hasRegularRequest = true; 979 } 980 } 981 982 Surface[] outputSurfaces = new Surface[isReprocessCaptures.length]; 983 for (int i = 0; i < isReprocessCaptures.length; i++) { 984 outputSurfaces[i] = getReprocessOutputImageReader().getSurface(); 985 } 986 987 TotalCaptureResult[] finalResults = submitMixedCaptureBurstRequest(outputSurfaces, results); 988 989 ImageResultHolder[] holders = new ImageResultHolder[isReprocessCaptures.length]; 990 for (int i = 0; i < isReprocessCaptures.length; i++) { 991 Image image = getReprocessOutputImageReaderListener().getImage(CAPTURE_TIMEOUT_MS); 992 if (hasReprocessRequest && hasRegularRequest) { 993 // If there are mixed requests, images and results may not be in the same order. 994 for (int j = 0; j < finalResults.length; j++) { 995 if (finalResults[j] != null && 996 finalResults[j].get(CaptureResult.SENSOR_TIMESTAMP) == 997 image.getTimestamp()) { 998 holders[i] = new ImageResultHolder(image, finalResults[j]); 999 finalResults[j] = null; 1000 break; 1001 } 1002 } 1003 1004 assertNotNull("Cannot find a result matching output image's timestamp: " + 1005 image.getTimestamp(), holders[i]); 1006 } else { 1007 // If no mixed requests, images and results should be in the same order. 1008 holders[i] = new ImageResultHolder(image, finalResults[i]); 1009 } 1010 } 1011 1012 return holders; 1013 } 1014 1015 /** 1016 * Start preview without a listener. 1017 */ startPreview(Surface previewSurface)1018 private void startPreview(Surface previewSurface) throws Exception { 1019 CaptureRequest.Builder builder = mCamera.createCaptureRequest(ZSL_TEMPLATE); 1020 builder.addTarget(previewSurface); 1021 mSession.setRepeatingRequest(builder.build(), null, mHandler); 1022 } 1023 1024 /** 1025 * Issue a capture request and return the result. If inputResult is null, it's a regular 1026 * request. Otherwise, it's a reprocess request. 1027 */ submitCaptureRequest(Surface output, TotalCaptureResult inputResult)1028 private TotalCaptureResult submitCaptureRequest(Surface output, 1029 TotalCaptureResult inputResult) throws Exception { 1030 Surface[] outputs = new Surface[1]; 1031 outputs[0] = output; 1032 TotalCaptureResult[] inputResults = new TotalCaptureResult[1]; 1033 inputResults[0] = inputResult; 1034 1035 return submitMixedCaptureBurstRequest(outputs, inputResults)[0]; 1036 } 1037 1038 /** 1039 * Submit a burst request mixed with regular and reprocess requests. 1040 * 1041 * @param outputs An array of output surfaces. One output surface will be used in one request 1042 * so the length of the array is the number of requests in a burst request. 1043 * @param inputResults An array of input results. If it's null, all requests are regular 1044 * requests. If an element is null, that element represents a regular 1045 * request. If an element if not null, that element represents a reprocess 1046 * request. 1047 * 1048 */ submitMixedCaptureBurstRequest(Surface[] outputs, TotalCaptureResult[] inputResults)1049 private TotalCaptureResult[] submitMixedCaptureBurstRequest(Surface[] outputs, 1050 TotalCaptureResult[] inputResults) throws Exception { 1051 if (outputs == null || outputs.length <= 0) { 1052 throw new IllegalArgumentException("outputs must have at least 1 surface"); 1053 } else if (inputResults != null && inputResults.length != outputs.length) { 1054 throw new IllegalArgumentException("The lengths of outputs and inputResults " + 1055 "don't match"); 1056 } 1057 1058 int numReprocessCaptures = 0; 1059 SimpleCaptureCallback captureCallback = new SimpleCaptureCallback(); 1060 ArrayList<CaptureRequest> captureRequests = new ArrayList<>(outputs.length); 1061 1062 // Prepare a list of capture requests. Whether it's a regular or reprocess capture request 1063 // is based on inputResults array. 1064 for (int i = 0; i < outputs.length; i++) { 1065 CaptureRequest.Builder builder; 1066 boolean isReprocess = (inputResults != null && inputResults[i] != null); 1067 if (isReprocess) { 1068 builder = mCamera.createReprocessCaptureRequest(inputResults[i]); 1069 numReprocessCaptures++; 1070 } else { 1071 builder = mCamera.createCaptureRequest(CAPTURE_TEMPLATE); 1072 } 1073 builder.addTarget(outputs[i]); 1074 CaptureRequest request = builder.build(); 1075 assertTrue("Capture request reprocess type " + request.isReprocess() + " is wrong.", 1076 request.isReprocess() == isReprocess); 1077 1078 captureRequests.add(request); 1079 } 1080 1081 if (captureRequests.size() == 1) { 1082 mSession.capture(captureRequests.get(0), captureCallback, mHandler); 1083 } else { 1084 mSession.captureBurst(captureRequests, captureCallback, mHandler); 1085 } 1086 1087 TotalCaptureResult[] results; 1088 if (numReprocessCaptures == 0 || numReprocessCaptures == outputs.length) { 1089 results = new TotalCaptureResult[outputs.length]; 1090 // If the requests are not mixed, they should come in order. 1091 for (int i = 0; i < results.length; i++){ 1092 results[i] = captureCallback.getTotalCaptureResultForRequest( 1093 captureRequests.get(i), CAPTURE_TIMEOUT_FRAMES); 1094 } 1095 } else { 1096 // If the requests are mixed, they may not come in order. 1097 results = captureCallback.getTotalCaptureResultsForRequests( 1098 captureRequests, CAPTURE_TIMEOUT_FRAMES * captureRequests.size()); 1099 } 1100 1101 // make sure all input surfaces are released. 1102 for (int i = 0; i < numReprocessCaptures; i++) { 1103 mImageWriterListener.waitForImageReleased(CAPTURE_TIMEOUT_MS); 1104 } 1105 1106 return results; 1107 } 1108 getMaxSize(int format, StaticMetadata.StreamDirection direction)1109 private Size getMaxSize(int format, StaticMetadata.StreamDirection direction) { 1110 Size[] sizes = mStaticInfo.getAvailableSizesForFormatChecked(format, direction); 1111 return getAscendingOrderSizes(Arrays.asList(sizes), /*ascending*/false).get(0); 1112 } 1113 isYuvReprocessSupported(String cameraId)1114 private boolean isYuvReprocessSupported(String cameraId) throws Exception { 1115 return isReprocessSupported(cameraId, ImageFormat.YUV_420_888); 1116 } 1117 isOpaqueReprocessSupported(String cameraId)1118 private boolean isOpaqueReprocessSupported(String cameraId) throws Exception { 1119 return isReprocessSupported(cameraId, ImageFormat.PRIVATE); 1120 } 1121 dumpImage(Image image, String name)1122 private void dumpImage(Image image, String name) { 1123 String filename = DEBUG_FILE_NAME_BASE + name; 1124 switch(image.getFormat()) { 1125 case ImageFormat.JPEG: 1126 filename += ".jpg"; 1127 break; 1128 case ImageFormat.NV16: 1129 case ImageFormat.NV21: 1130 case ImageFormat.YUV_420_888: 1131 filename += ".yuv"; 1132 break; 1133 default: 1134 filename += "." + image.getFormat(); 1135 break; 1136 } 1137 1138 Log.d(TAG, "dumping an image to " + filename); 1139 dumpFile(filename , getDataFromImage(image)); 1140 } 1141 1142 /** 1143 * A class that holds an Image and a TotalCaptureResult. 1144 */ 1145 private static class ImageResultHolder { 1146 private final Image mImage; 1147 private final TotalCaptureResult mResult; 1148 ImageResultHolder(Image image, TotalCaptureResult result)1149 public ImageResultHolder(Image image, TotalCaptureResult result) { 1150 mImage = image; 1151 mResult = result; 1152 } 1153 getImage()1154 public Image getImage() { 1155 return mImage; 1156 } 1157 getTotalCaptureResult()1158 public TotalCaptureResult getTotalCaptureResult() { 1159 return mResult; 1160 } 1161 } 1162 } 1163