/* * Copyright 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.hardware.camera2.cts; import android.content.Context; import android.graphics.ImageFormat; import android.hardware.camera2.CameraCaptureSession; import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CameraDevice; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.CaptureResult; import android.hardware.camera2.TotalCaptureResult; import android.media.Image; import android.media.ImageReader; import android.os.SystemClock; import android.util.Pair; import android.util.Size; import android.hardware.camera2.cts.helpers.StaticMetadata; import android.hardware.camera2.cts.testcases.Camera2AndroidTestCase; import static android.hardware.camera2.cts.CameraTestUtils.*; import static android.hardware.camera2.cts.helpers.CameraSessionUtils.*; import android.util.Log; import android.view.Surface; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; public class CaptureResultTest extends Camera2AndroidTestCase { private static final String TAG = "CaptureResultTest"; private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE); private static final int MAX_NUM_IMAGES = MAX_READER_IMAGES; private static final int NUM_FRAMES_VERIFIED = 30; private static final long WAIT_FOR_RESULT_TIMEOUT_MS = 3000; // List that includes all public keys from CaptureResult List> mAllKeys; // List tracking the failed test keys. @Override public void setContext(Context context) { mAllKeys = getAllCaptureResultKeys(); super.setContext(context); /** * Workaround for mockito and JB-MR2 incompatibility * * Avoid java.lang.IllegalArgumentException: dexcache == null * https://code.google.com/p/dexmaker/issues/detail?id=2 */ System.setProperty("dexmaker.dexcache", getContext().getCacheDir().toString()); } @Override protected void setUp() throws Exception { super.setUp(); } @Override protected void tearDown() throws Exception { super.tearDown(); } /** *

* Basic non-null check test for multiple capture results. *

*

* When capturing many frames, some camera devices may return some results that have null keys * randomly, which is an API violation and could cause application crash randomly. This test * runs a typical flexible yuv capture many times, and checks if there is any null entries in * a capture result. *

*/ public void testCameraCaptureResultAllKeys() throws Exception { for (String id : mCameraIds) { try { openDevice(id); if (mStaticInfo.isColorOutputSupported()) { // Create image reader and surface. Size size = mOrderedPreviewSizes.get(0); createDefaultImageReader(size, ImageFormat.YUV_420_888, MAX_NUM_IMAGES, new ImageDropperListener()); } else { Size size = getMaxDepthSize(id, mCameraManager); createDefaultImageReader(size, ImageFormat.DEPTH16, MAX_NUM_IMAGES, new ImageDropperListener()); } // Configure output streams. List outputSurfaces = new ArrayList(1); outputSurfaces.add(mReaderSurface); createSession(outputSurfaces); CaptureRequest.Builder requestBuilder = mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); assertNotNull("Failed to create capture request", requestBuilder); requestBuilder.addTarget(mReaderSurface); // Start capture SimpleCaptureCallback captureListener = new SimpleCaptureCallback(); startCapture(requestBuilder.build(), /*repeating*/true, captureListener, mHandler); // Get the waived keys for current camera device List> waiverkeys = getWaiverKeysForCamera(); // Verify results validateCaptureResult(captureListener, waiverkeys, requestBuilder, NUM_FRAMES_VERIFIED); stopCapture(/*fast*/false); } finally { closeDevice(id); closeDefaultImageReader(); } } } /** * Check partial results conform to its specification. *

* The test is skipped if partial result is not supported on device.

*

Test summary:

    *
  • 1. Number of partial results is less than or equal to * {@link CameraCharacteristics#REQUEST_PARTIAL_RESULT_COUNT}. *
  • 2. Each key appeared in partial results must be unique across all partial results. *
  • 3. All keys appeared in partial results must be present in TotalCaptureResult *
  • 4. Also test onCaptureComplete callback always happen after onCaptureStart or * onCaptureProgressed callbacks. *

*/ public void testPartialResult() throws Exception { final int NUM_FRAMES_TESTED = 30; final int WAIT_FOR_RESULT_TIMOUT_MS = 2000; for (String id : mCameraIds) { try { openDevice(id); // Skip the test if partial result is not supported int partialResultCount = mStaticInfo.getPartialResultCount(); if (partialResultCount == 1) { continue; } // Create image reader and surface. if (mStaticInfo.isColorOutputSupported()) { Size size = mOrderedPreviewSizes.get(0); createDefaultImageReader(size, ImageFormat.YUV_420_888, MAX_NUM_IMAGES, new ImageDropperListener()); } else { Size size = getMaxDepthSize(id, mCameraManager); createDefaultImageReader(size, ImageFormat.DEPTH16, MAX_NUM_IMAGES, new ImageDropperListener()); } // Configure output streams. List outputSurfaces = new ArrayList(1); outputSurfaces.add(mReaderSurface); createSession(outputSurfaces); CaptureRequest.Builder requestBuilder = mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); assertNotNull("Failed to create capture request", requestBuilder); requestBuilder.addTarget(mReaderSurface); TotalAndPartialResultListener listener = new TotalAndPartialResultListener(); // Start capture for (Integer frame = 0; frame < NUM_FRAMES_TESTED; frame++) { // Set a different tag for each request so the listener can group // partial results by each request requestBuilder.setTag(frame); startCapture( requestBuilder.build(), /*repeating*/false, listener, mHandler); } // Verify capture results for (int frame = 0; frame < NUM_FRAMES_TESTED; frame++) { Pair> resultPair = listener.getCaptureResultPairs(WAIT_FOR_RESULT_TIMOUT_MS); List partialResults = resultPair.second; if (partialResults == null) { // HAL only sends total result is legal partialResults = new ArrayList<>(); } TotalCaptureResult totalResult = resultPair.first; mCollector.expectLessOrEqual("Too many partial results", partialResultCount, partialResults.size()); Set> appearedPartialKeys = new HashSet>(); for (CaptureResult partialResult : partialResults) { List> partialKeys = partialResult.getKeys(); mCollector.expectValuesUnique("Partial result keys: ", partialKeys); for (CaptureResult.Key key : partialKeys) { mCollector.expectTrue( String.format("Key %s appears in multiple partial results", key.getName()), !appearedPartialKeys.contains(key)); } appearedPartialKeys.addAll(partialKeys); } // Test total result against the partial results List> totalResultKeys = totalResult.getKeys(); mCollector.expectTrue( "TotalCaptureResult must be a super set of partial capture results", totalResultKeys.containsAll(appearedPartialKeys)); List totalResultPartials = totalResult.getPartialResults(); mCollector.expectEquals("TotalCaptureResult's partial results must match " + "the ones observed by #onCaptureProgressed", partialResults, totalResultPartials); if (VERBOSE) { Log.v(TAG, "testPartialResult - Observed " + partialResults.size() + "; queried for " + totalResultPartials.size()); } } int errorCode = listener.getErrorCode(); if ((errorCode & TotalAndPartialResultListener.ERROR_DUPLICATED_REQUEST) != 0) { mCollector.addMessage("Listener received multiple onCaptureComplete" + " callback for the same request"); } if ((errorCode & TotalAndPartialResultListener.ERROR_WRONG_CALLBACK_ORDER) != 0) { mCollector.addMessage("Listener received onCaptureStart or" + " onCaptureProgressed after onCaptureComplete"); } stopCapture(/*fast*/false); } finally { closeDevice(id); closeDefaultImageReader(); } } } /** * Check that the timestamps passed in the results, buffers, and capture callbacks match for * a single request, and increase monotonically */ public void testResultTimestamps() throws Exception { for (String id : mCameraIds) { ImageReader previewReader = null; ImageReader jpegReader = null; SimpleImageReaderListener jpegListener = new SimpleImageReaderListener(); SimpleImageReaderListener prevListener = new SimpleImageReaderListener(); try { openDevice(id); if (!mStaticInfo.isColorOutputSupported()) { Log.i(TAG, "Camera " + id + " does not support color outputs, skipping"); continue; } CaptureRequest.Builder previewBuilder = mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); CaptureRequest.Builder multiBuilder = mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); // Create image reader and surface. Size previewSize = mOrderedPreviewSizes.get(0); Size jpegSize = mOrderedStillSizes.get(0); // Create ImageReaders. previewReader = makeImageReader(previewSize, ImageFormat.YUV_420_888, MAX_NUM_IMAGES, prevListener, mHandler); jpegReader = makeImageReader(jpegSize, ImageFormat.JPEG, MAX_NUM_IMAGES, jpegListener, mHandler); // Configure output streams with preview and jpeg streams. List outputSurfaces = new ArrayList<>(Arrays.asList( previewReader.getSurface(), jpegReader.getSurface())); SessionListener mockSessionListener = getMockSessionListener(); CameraCaptureSession session = configureAndVerifySession(mockSessionListener, mCamera, outputSurfaces, mHandler); // Configure the requests. previewBuilder.addTarget(previewReader.getSurface()); multiBuilder.addTarget(previewReader.getSurface()); multiBuilder.addTarget(jpegReader.getSurface()); CaptureCallback mockCaptureCallback = getMockCaptureListener(); // Capture targeting only preview Pair result = captureAndVerifyResult(mockCaptureCallback, session, previewBuilder.build(), mHandler); // Check if all timestamps are the same Image prevImage = prevListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS); validateTimestamps("Result 1", result.first, prevImage, result.second); prevImage.close(); // Capture targeting both jpeg and preview Pair result2 = captureAndVerifyResult(mockCaptureCallback, session, multiBuilder.build(), mHandler); // Check if all timestamps are the same prevImage = prevListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS); Image jpegImage = jpegListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS); validateTimestamps("Result 2 Preview", result2.first, prevImage, result2.second); validateTimestamps("Result 2 Jpeg", result2.first, jpegImage, result2.second); prevImage.close(); jpegImage.close(); // Check if timestamps are increasing mCollector.expectGreater("Timestamps must be increasing.", result.second, result2.second); // Capture two preview frames long startTime = SystemClock.elapsedRealtimeNanos(); Pair result3 = captureAndVerifyResult(mockCaptureCallback, session, previewBuilder.build(), mHandler); Pair result4 = captureAndVerifyResult(mockCaptureCallback, session, previewBuilder.build(), mHandler); long clockDiff = SystemClock.elapsedRealtimeNanos() - startTime; long resultDiff = result4.second - result3.second; // Check if all timestamps are the same prevImage = prevListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS); validateTimestamps("Result 3", result3.first, prevImage, result3.second); prevImage.close(); prevImage = prevListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS); validateTimestamps("Result 4", result4.first, prevImage, result4.second); prevImage.close(); // Check that the timestamps monotonically increase at a reasonable rate mCollector.expectGreaterOrEqual("Timestamps increase faster than system clock.", resultDiff, clockDiff); mCollector.expectGreater("Timestamps must be increasing.", result3.second, result4.second); } finally { closeDevice(id); closeImageReader(previewReader); closeImageReader(jpegReader); } } } private void validateTimestamps(String msg, TotalCaptureResult result, Image resultImage, long captureTime) { mCollector.expectKeyValueEquals(result, CaptureResult.SENSOR_TIMESTAMP, captureTime); mCollector.expectEquals(msg + ": Capture timestamp must be same as resultImage timestamp", resultImage.getTimestamp(), captureTime); } private void validateCaptureResult(SimpleCaptureCallback captureListener, List> skippedKeys, CaptureRequest.Builder requestBuilder, int numFramesVerified) throws Exception { CaptureResult result = null; for (int i = 0; i < numFramesVerified; i++) { String failMsg = "Failed capture result " + i + " test "; result = captureListener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS); for (CaptureResult.Key key : mAllKeys) { if (!skippedKeys.contains(key)) { /** * Check the critical tags here. * TODO: Can use the same key for request and result when request/result * becomes symmetric (b/14059883). Then below check can be wrapped into * a generic function. */ String msg = failMsg + "for key " + key.getName(); if (key.equals(CaptureResult.CONTROL_AE_MODE)) { mCollector.expectEquals(msg, requestBuilder.get(CaptureRequest.CONTROL_AE_MODE), result.get(CaptureResult.CONTROL_AE_MODE)); } else if (key.equals(CaptureResult.CONTROL_AF_MODE)) { mCollector.expectEquals(msg, requestBuilder.get(CaptureRequest.CONTROL_AF_MODE), result.get(CaptureResult.CONTROL_AF_MODE)); } else if (key.equals(CaptureResult.CONTROL_AWB_MODE)) { mCollector.expectEquals(msg, requestBuilder.get(CaptureRequest.CONTROL_AWB_MODE), result.get(CaptureResult.CONTROL_AWB_MODE)); } else if (key.equals(CaptureResult.CONTROL_MODE)) { mCollector.expectEquals(msg, requestBuilder.get(CaptureRequest.CONTROL_MODE), result.get(CaptureResult.CONTROL_MODE)); } else if (key.equals(CaptureResult.STATISTICS_FACE_DETECT_MODE)) { mCollector.expectEquals(msg, requestBuilder.get(CaptureRequest.STATISTICS_FACE_DETECT_MODE), result.get(CaptureResult.STATISTICS_FACE_DETECT_MODE)); } else if (key.equals(CaptureResult.NOISE_REDUCTION_MODE)) { mCollector.expectEquals(msg, requestBuilder.get(CaptureRequest.NOISE_REDUCTION_MODE), result.get(CaptureResult.NOISE_REDUCTION_MODE)); } else if (key.equals(CaptureResult.NOISE_REDUCTION_MODE)) { mCollector.expectEquals(msg, requestBuilder.get(CaptureRequest.NOISE_REDUCTION_MODE), result.get(CaptureResult.NOISE_REDUCTION_MODE)); } else if (key.equals(CaptureResult.REQUEST_PIPELINE_DEPTH)) { } else { // Only do non-null check for the rest of keys. mCollector.expectKeyValueNotNull(failMsg, result, key); } } else { // These keys should always be null if (key.equals(CaptureResult.CONTROL_AE_REGIONS)) { mCollector.expectNull( "Capture result contains AE regions but aeMaxRegions is 0", result.get(CaptureResult.CONTROL_AE_REGIONS)); } else if (key.equals(CaptureResult.CONTROL_AWB_REGIONS)) { mCollector.expectNull( "Capture result contains AWB regions but awbMaxRegions is 0", result.get(CaptureResult.CONTROL_AWB_REGIONS)); } else if (key.equals(CaptureResult.CONTROL_AF_REGIONS)) { mCollector.expectNull( "Capture result contains AF regions but afMaxRegions is 0", result.get(CaptureResult.CONTROL_AF_REGIONS)); } } } } } /* * Add waiver keys per camera device hardware level and capability. * * Must be called after camera device is opened. */ private List> getWaiverKeysForCamera() { List> waiverKeys = new ArrayList<>(); // Global waiver keys waiverKeys.add(CaptureResult.JPEG_GPS_LOCATION); waiverKeys.add(CaptureResult.JPEG_ORIENTATION); waiverKeys.add(CaptureResult.JPEG_QUALITY); waiverKeys.add(CaptureResult.JPEG_THUMBNAIL_QUALITY); waiverKeys.add(CaptureResult.JPEG_THUMBNAIL_SIZE); // Keys only present when corresponding control is on are being // verified in its own functional test // Only present in certain tonemap mode. Test in CaptureRequestTest. waiverKeys.add(CaptureResult.TONEMAP_CURVE); waiverKeys.add(CaptureResult.TONEMAP_GAMMA); waiverKeys.add(CaptureResult.TONEMAP_PRESET_CURVE); // Only present when test pattern mode is SOLID_COLOR. // TODO: verify this key in test pattern test later waiverKeys.add(CaptureResult.SENSOR_TEST_PATTERN_DATA); // Only present when STATISTICS_LENS_SHADING_MAP_MODE is ON waiverKeys.add(CaptureResult.STATISTICS_LENS_SHADING_CORRECTION_MAP); // Only present when STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES is ON waiverKeys.add(CaptureResult.STATISTICS_HOT_PIXEL_MAP); // Only present when face detection is on waiverKeys.add(CaptureResult.STATISTICS_FACES); // Only present in reprocessing capture result. waiverKeys.add(CaptureResult.REPROCESS_EFFECTIVE_EXPOSURE_FACTOR); //Keys not required if RAW is not supported if (!mStaticInfo.isCapabilitySupported( CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) { waiverKeys.add(CaptureResult.SENSOR_NEUTRAL_COLOR_POINT); waiverKeys.add(CaptureResult.SENSOR_GREEN_SPLIT); waiverKeys.add(CaptureResult.SENSOR_NOISE_PROFILE); } //Keys for depth output capability if (!mStaticInfo.isCapabilitySupported( CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT)) { waiverKeys.add(CaptureResult.LENS_POSE_ROTATION); waiverKeys.add(CaptureResult.LENS_POSE_TRANSLATION); waiverKeys.add(CaptureResult.LENS_INTRINSIC_CALIBRATION); waiverKeys.add(CaptureResult.LENS_RADIAL_DISTORTION); } // Waived if RAW output is not supported int[] outputFormats = mStaticInfo.getAvailableFormats( StaticMetadata.StreamDirection.Output); boolean supportRaw = false; for (int format : outputFormats) { if (format == ImageFormat.RAW_SENSOR || format == ImageFormat.RAW10 || format == ImageFormat.RAW12 || format == ImageFormat.RAW_PRIVATE) { supportRaw = true; break; } } if (!supportRaw) { waiverKeys.add(CaptureResult.CONTROL_POST_RAW_SENSITIVITY_BOOST); } if (mStaticInfo.getAeMaxRegionsChecked() == 0) { waiverKeys.add(CaptureResult.CONTROL_AE_REGIONS); } if (mStaticInfo.getAwbMaxRegionsChecked() == 0) { waiverKeys.add(CaptureResult.CONTROL_AWB_REGIONS); } if (mStaticInfo.getAfMaxRegionsChecked() == 0) { waiverKeys.add(CaptureResult.CONTROL_AF_REGIONS); } // Keys for dynamic black/white levels if (!mStaticInfo.isOpticalBlackRegionSupported()) { waiverKeys.add(CaptureResult.SENSOR_DYNAMIC_BLACK_LEVEL); waiverKeys.add(CaptureResult.SENSOR_DYNAMIC_WHITE_LEVEL); } if (mStaticInfo.isHardwareLevelAtLeastFull()) { return waiverKeys; } /* * Hardware Level = LIMITED or LEGACY */ // Key not present if certain control is not supported if (!mStaticInfo.isColorCorrectionSupported()) { waiverKeys.add(CaptureResult.COLOR_CORRECTION_GAINS); waiverKeys.add(CaptureResult.COLOR_CORRECTION_MODE); waiverKeys.add(CaptureResult.COLOR_CORRECTION_TRANSFORM); } if (!mStaticInfo.isManualColorAberrationControlSupported()) { waiverKeys.add(CaptureResult.COLOR_CORRECTION_ABERRATION_MODE); } if (!mStaticInfo.isManualToneMapSupported()) { waiverKeys.add(CaptureResult.TONEMAP_MODE); } if (!mStaticInfo.isEdgeModeControlSupported()) { waiverKeys.add(CaptureResult.EDGE_MODE); } if (!mStaticInfo.isHotPixelMapModeControlSupported()) { waiverKeys.add(CaptureResult.HOT_PIXEL_MODE); } if (!mStaticInfo.isNoiseReductionModeControlSupported()) { waiverKeys.add(CaptureResult.NOISE_REDUCTION_MODE); } if (!mStaticInfo.isManualLensShadingMapSupported()) { waiverKeys.add(CaptureResult.SHADING_MODE); } //Keys not required if neither MANUAL_SENSOR nor READ_SENSOR_SETTINGS is supported if (!mStaticInfo.isCapabilitySupported( CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR) && !mStaticInfo.isCapabilitySupported( CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS)) { waiverKeys.add(CaptureResult.SENSOR_EXPOSURE_TIME); waiverKeys.add(CaptureResult.SENSOR_SENSITIVITY); waiverKeys.add(CaptureResult.LENS_FOCUS_DISTANCE); waiverKeys.add(CaptureResult.LENS_APERTURE); } if (!mStaticInfo.isCapabilitySupported( CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) { waiverKeys.add(CaptureResult.SENSOR_FRAME_DURATION); waiverKeys.add(CaptureResult.BLACK_LEVEL_LOCK); waiverKeys.add(CaptureResult.LENS_FOCUS_RANGE); waiverKeys.add(CaptureResult.LENS_STATE); waiverKeys.add(CaptureResult.LENS_FILTER_DENSITY); } if (mStaticInfo.isHardwareLevelLimited() && mStaticInfo.isColorOutputSupported()) { return waiverKeys; } /* * Hardware Level = LEGACY or no regular output is supported */ waiverKeys.add(CaptureResult.CONTROL_AE_PRECAPTURE_TRIGGER); waiverKeys.add(CaptureResult.CONTROL_AE_STATE); waiverKeys.add(CaptureResult.CONTROL_AWB_STATE); waiverKeys.add(CaptureResult.FLASH_STATE); waiverKeys.add(CaptureResult.LENS_OPTICAL_STABILIZATION_MODE); waiverKeys.add(CaptureResult.SENSOR_ROLLING_SHUTTER_SKEW); waiverKeys.add(CaptureResult.STATISTICS_LENS_SHADING_MAP_MODE); waiverKeys.add(CaptureResult.STATISTICS_SCENE_FLICKER); waiverKeys.add(CaptureResult.STATISTICS_HOT_PIXEL_MAP_MODE); waiverKeys.add(CaptureResult.CONTROL_AE_TARGET_FPS_RANGE); waiverKeys.add(CaptureResult.CONTROL_AF_TRIGGER); if (mStaticInfo.isHardwareLevelLegacy()) { return waiverKeys; } /* * Regular output not supported, only depth, waive color-output-related keys */ waiverKeys.add(CaptureResult.CONTROL_SCENE_MODE); waiverKeys.add(CaptureResult.CONTROL_EFFECT_MODE); waiverKeys.add(CaptureResult.CONTROL_VIDEO_STABILIZATION_MODE); waiverKeys.add(CaptureResult.SENSOR_TEST_PATTERN_MODE); waiverKeys.add(CaptureResult.NOISE_REDUCTION_MODE); waiverKeys.add(CaptureResult.COLOR_CORRECTION_ABERRATION_MODE); waiverKeys.add(CaptureResult.CONTROL_AE_ANTIBANDING_MODE); waiverKeys.add(CaptureResult.CONTROL_AE_EXPOSURE_COMPENSATION); waiverKeys.add(CaptureResult.CONTROL_AE_LOCK); waiverKeys.add(CaptureResult.CONTROL_AE_MODE); waiverKeys.add(CaptureResult.CONTROL_AF_MODE); waiverKeys.add(CaptureResult.CONTROL_AWB_MODE); waiverKeys.add(CaptureResult.CONTROL_AWB_LOCK); waiverKeys.add(CaptureResult.STATISTICS_FACE_DETECT_MODE); waiverKeys.add(CaptureResult.FLASH_MODE); waiverKeys.add(CaptureResult.SCALER_CROP_REGION); return waiverKeys; } /** * A capture listener implementation for collecting both partial and total results. * *

This is not a full-blown class and has some implicit assumptions. The class groups * capture results by capture request, so the user must guarantee each request this listener * is listening is unique. This class is not thread safe, so don't attach an instance object * with multiple handlers.

* */ private static class TotalAndPartialResultListener extends CameraCaptureSession.CaptureCallback { static final int ERROR_DUPLICATED_REQUEST = 1 << 0; static final int ERROR_WRONG_CALLBACK_ORDER = 1 << 1; private final LinkedBlockingQueue> > mQueue = new LinkedBlockingQueue<>(); private final HashMap> mPartialResultsMap = new HashMap>(); private final HashSet completedRequests = new HashSet<>(); private int errorCode = 0; @Override public void onCaptureStarted( CameraCaptureSession session, CaptureRequest request, long timestamp, long frameNumber) { checkCallbackOrder(request); createMapEntryIfNecessary(request); } @Override public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) { try { List partialResultsList = mPartialResultsMap.get(request); if (partialResultsList == null) { Log.w(TAG, "onCaptureCompleted: unknown request"); } mQueue.put(new Pair>( result, partialResultsList)); mPartialResultsMap.remove(request); boolean newEntryAdded = completedRequests.add(request); if (!newEntryAdded) { Integer frame = (Integer) request.getTag(); Log.e(TAG, "Frame " + frame + "ERROR_DUPLICATED_REQUEST"); errorCode |= ERROR_DUPLICATED_REQUEST; } } catch (InterruptedException e) { throw new UnsupportedOperationException( "Can't handle InterruptedException in onCaptureCompleted"); } } @Override public void onCaptureProgressed(CameraCaptureSession session, CaptureRequest request, CaptureResult partialResult) { createMapEntryIfNecessary(request); List partialResultsList = mPartialResultsMap.get(request); partialResultsList.add(partialResult); } private void createMapEntryIfNecessary(CaptureRequest request) { if (!mPartialResultsMap.containsKey(request)) { // create a new entry in the map mPartialResultsMap.put(request, new ArrayList()); } } private void checkCallbackOrder(CaptureRequest request) { if (completedRequests.contains(request)) { Integer frame = (Integer) request.getTag(); Log.e(TAG, "Frame " + frame + "ERROR_WRONG_CALLBACK_ORDER"); errorCode |= ERROR_WRONG_CALLBACK_ORDER; } } public Pair> getCaptureResultPairs(long timeout) { try { Pair> result = mQueue.poll(timeout, TimeUnit.MILLISECONDS); assertNotNull("Wait for a capture result timed out in " + timeout + "ms", result); return result; } catch (InterruptedException e) { throw new UnsupportedOperationException("Unhandled interrupted exception", e); } } public int getErrorCode() { return errorCode; } } /** * TODO: Use CameraCharacteristics.getAvailableCaptureResultKeys() once we can filter out * @hide keys. * */ /*@O~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~ * The key entries below this point are generated from metadata * definitions in /system/media/camera/docs. Do not modify by hand or * modify the comment blocks at the start or end. *~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~*/ private static List> getAllCaptureResultKeys() { ArrayList> resultKeys = new ArrayList>(); resultKeys.add(CaptureResult.COLOR_CORRECTION_MODE); resultKeys.add(CaptureResult.COLOR_CORRECTION_TRANSFORM); resultKeys.add(CaptureResult.COLOR_CORRECTION_GAINS); resultKeys.add(CaptureResult.COLOR_CORRECTION_ABERRATION_MODE); resultKeys.add(CaptureResult.CONTROL_AE_ANTIBANDING_MODE); resultKeys.add(CaptureResult.CONTROL_AE_EXPOSURE_COMPENSATION); resultKeys.add(CaptureResult.CONTROL_AE_LOCK); resultKeys.add(CaptureResult.CONTROL_AE_MODE); resultKeys.add(CaptureResult.CONTROL_AE_REGIONS); resultKeys.add(CaptureResult.CONTROL_AE_TARGET_FPS_RANGE); resultKeys.add(CaptureResult.CONTROL_AE_PRECAPTURE_TRIGGER); resultKeys.add(CaptureResult.CONTROL_AF_MODE); resultKeys.add(CaptureResult.CONTROL_AF_REGIONS); resultKeys.add(CaptureResult.CONTROL_AF_TRIGGER); resultKeys.add(CaptureResult.CONTROL_AWB_LOCK); resultKeys.add(CaptureResult.CONTROL_AWB_MODE); resultKeys.add(CaptureResult.CONTROL_AWB_REGIONS); resultKeys.add(CaptureResult.CONTROL_CAPTURE_INTENT); resultKeys.add(CaptureResult.CONTROL_EFFECT_MODE); resultKeys.add(CaptureResult.CONTROL_MODE); resultKeys.add(CaptureResult.CONTROL_SCENE_MODE); resultKeys.add(CaptureResult.CONTROL_VIDEO_STABILIZATION_MODE); resultKeys.add(CaptureResult.CONTROL_AE_STATE); resultKeys.add(CaptureResult.CONTROL_AF_STATE); resultKeys.add(CaptureResult.CONTROL_AWB_STATE); resultKeys.add(CaptureResult.CONTROL_POST_RAW_SENSITIVITY_BOOST); resultKeys.add(CaptureResult.EDGE_MODE); resultKeys.add(CaptureResult.FLASH_MODE); resultKeys.add(CaptureResult.FLASH_STATE); resultKeys.add(CaptureResult.HOT_PIXEL_MODE); resultKeys.add(CaptureResult.JPEG_GPS_LOCATION); resultKeys.add(CaptureResult.JPEG_ORIENTATION); resultKeys.add(CaptureResult.JPEG_QUALITY); resultKeys.add(CaptureResult.JPEG_THUMBNAIL_QUALITY); resultKeys.add(CaptureResult.JPEG_THUMBNAIL_SIZE); resultKeys.add(CaptureResult.LENS_APERTURE); resultKeys.add(CaptureResult.LENS_FILTER_DENSITY); resultKeys.add(CaptureResult.LENS_FOCAL_LENGTH); resultKeys.add(CaptureResult.LENS_FOCUS_DISTANCE); resultKeys.add(CaptureResult.LENS_OPTICAL_STABILIZATION_MODE); resultKeys.add(CaptureResult.LENS_POSE_ROTATION); resultKeys.add(CaptureResult.LENS_POSE_TRANSLATION); resultKeys.add(CaptureResult.LENS_FOCUS_RANGE); resultKeys.add(CaptureResult.LENS_STATE); resultKeys.add(CaptureResult.LENS_INTRINSIC_CALIBRATION); resultKeys.add(CaptureResult.LENS_RADIAL_DISTORTION); resultKeys.add(CaptureResult.NOISE_REDUCTION_MODE); resultKeys.add(CaptureResult.REQUEST_PIPELINE_DEPTH); resultKeys.add(CaptureResult.SCALER_CROP_REGION); resultKeys.add(CaptureResult.SENSOR_EXPOSURE_TIME); resultKeys.add(CaptureResult.SENSOR_FRAME_DURATION); resultKeys.add(CaptureResult.SENSOR_SENSITIVITY); resultKeys.add(CaptureResult.SENSOR_TIMESTAMP); resultKeys.add(CaptureResult.SENSOR_NEUTRAL_COLOR_POINT); resultKeys.add(CaptureResult.SENSOR_NOISE_PROFILE); resultKeys.add(CaptureResult.SENSOR_GREEN_SPLIT); resultKeys.add(CaptureResult.SENSOR_TEST_PATTERN_DATA); resultKeys.add(CaptureResult.SENSOR_TEST_PATTERN_MODE); resultKeys.add(CaptureResult.SENSOR_ROLLING_SHUTTER_SKEW); resultKeys.add(CaptureResult.SENSOR_DYNAMIC_BLACK_LEVEL); resultKeys.add(CaptureResult.SENSOR_DYNAMIC_WHITE_LEVEL); resultKeys.add(CaptureResult.SHADING_MODE); resultKeys.add(CaptureResult.STATISTICS_FACE_DETECT_MODE); resultKeys.add(CaptureResult.STATISTICS_HOT_PIXEL_MAP_MODE); resultKeys.add(CaptureResult.STATISTICS_FACES); resultKeys.add(CaptureResult.STATISTICS_LENS_SHADING_CORRECTION_MAP); resultKeys.add(CaptureResult.STATISTICS_SCENE_FLICKER); resultKeys.add(CaptureResult.STATISTICS_HOT_PIXEL_MAP); resultKeys.add(CaptureResult.STATISTICS_LENS_SHADING_MAP_MODE); resultKeys.add(CaptureResult.TONEMAP_CURVE); resultKeys.add(CaptureResult.TONEMAP_MODE); resultKeys.add(CaptureResult.TONEMAP_GAMMA); resultKeys.add(CaptureResult.TONEMAP_PRESET_CURVE); resultKeys.add(CaptureResult.BLACK_LEVEL_LOCK); resultKeys.add(CaptureResult.REPROCESS_EFFECTIVE_EXPOSURE_FACTOR); return resultKeys; } /*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~ * End generated code *~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~O@*/ }