blob: 33365b25831a5b7b13adff6580a259051e4595cd [file] [log] [blame]
/*
* Copyright 2018 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 static android.hardware.camera2.cts.CameraTestUtils.*;
import android.content.Context;
import android.graphics.ImageFormat;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.CaptureFailure;
import android.hardware.camera2.cts.CaptureResultTest;
import android.hardware.camera2.cts.helpers.StaticMetadata;
import android.hardware.camera2.cts.helpers.StaticMetadata.CheckLevel;
import android.hardware.camera2.cts.testcases.Camera2SurfaceViewTestCase;
import android.hardware.camera2.params.OutputConfiguration;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.CamcorderProfile;
import android.media.Image;
import android.media.ImageReader;
import android.platform.test.annotations.AppModeFull;
import android.util.ArraySet;
import android.util.Log;
import android.util.Range;
import android.util.Size;
import android.view.Display;
import android.view.Surface;
import android.view.WindowManager;
import com.android.compatibility.common.util.Stat;
import com.android.ex.camera2.blocking.BlockingSessionCallback;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static org.mockito.Mockito.*;
/**
* Tests exercising logical camera setup, configuration, and usage.
*/
@AppModeFull
public final class LogicalCameraDeviceTest extends Camera2SurfaceViewTestCase {
private static final String TAG = "LogicalCameraTest";
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
private static final int CONFIGURE_TIMEOUT = 5000; //ms
private static final double NS_PER_MS = 1000000.0;
private static final int MAX_IMAGE_COUNT = 3;
private static final int NUM_FRAMES_CHECKED = 30;
private static final double FRAME_DURATION_THRESHOLD = 0.03;
private HashMap<String, StaticMetadata> mAllStaticInfo;
@Override
protected void setUp() throws Exception {
super.setUp();
mAllStaticInfo = new HashMap<String, StaticMetadata>();
for (String cameraId : mCameraIds) {
StaticMetadata staticMetadata = new StaticMetadata(
mCameraManager.getCameraCharacteristics(cameraId),
CheckLevel.ASSERT, /*collector*/null);
mAllStaticInfo.put(cameraId, staticMetadata);
}
}
/**
* Test that passing in invalid physical camera ids in OutputConfiguragtion behaves as expected
* for logical multi-camera and non-logical multi-camera.
*/
public void testInvalidPhysicalCameraIdInOutputConfiguration() throws Exception {
for (String id : mCameraIds) {
try {
Log.i(TAG, "Testing Camera " + id);
openDevice(id);
if (mStaticInfo.isHardwareLevelLegacy()) {
Log.i(TAG, "Camera " + id + " is legacy, skipping");
continue;
}
Size yuvSize = mOrderedPreviewSizes.get(0);
// Create a YUV image reader.
ImageReader imageReader = ImageReader.newInstance(yuvSize.getWidth(),
yuvSize.getHeight(), ImageFormat.YUV_420_888, /*maxImages*/1);
CameraCaptureSession.StateCallback sessionListener =
mock(CameraCaptureSession.StateCallback.class);
List<OutputConfiguration> outputs = new ArrayList<>();
OutputConfiguration outputConfig = new OutputConfiguration(
imageReader.getSurface());
outputConfig.setPhysicalCameraId(id);
// Regardless of logical camera or non-logical camera, create a session of an
// output configuration with invalid physical camera id, verify that the
// createCaptureSession fails.
outputs.add(outputConfig);
CameraCaptureSession session =
CameraTestUtils.configureCameraSessionWithConfig(mCamera, outputs,
sessionListener, mHandler);
verify(sessionListener, timeout(CONFIGURE_TIMEOUT).atLeastOnce()).
onConfigureFailed(any(CameraCaptureSession.class));
verify(sessionListener, never()).onConfigured(any(CameraCaptureSession.class));
verify(sessionListener, never()).onReady(any(CameraCaptureSession.class));
verify(sessionListener, never()).onActive(any(CameraCaptureSession.class));
verify(sessionListener, never()).onClosed(any(CameraCaptureSession.class));
} finally {
closeDevice();
}
}
}
/**
* Test for making sure that streaming from physical streams work as expected, and
* FPS isn't slowed down.
*/
public void testBasicPhysicalStreaming() throws Exception {
for (String id : mCameraIds) {
try {
Log.i(TAG, "Testing Camera " + id);
openDevice(id);
if (!mStaticInfo.isColorOutputSupported()) {
Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
continue;
}
if (!mStaticInfo.isLogicalMultiCamera()) {
Log.i(TAG, "Camera " + id + " is not a logical multi-camera, skipping");
continue;
}
assertTrue("Logical multi-camera must be LIMITED or higher",
mStaticInfo.isHardwareLevelAtLeastLimited());
// Figure out preview size and physical cameras to use.
ArrayList<String> dualPhysicalCameraIds = new ArrayList<String>();
Size previewSize= findCommonPreviewSize(id, dualPhysicalCameraIds);
if (previewSize == null) {
Log.i(TAG, "Camera " + id + ": No matching physical preview streams, skipping");
}
testBasicPhysicalStreamingForCamera(
id, dualPhysicalCameraIds, previewSize);
} finally {
closeDevice();
}
}
}
/**
* Test for making sure that logical/physical stream requests work when both logical stream
* and physical stream are configured.
*/
public void testBasicLogicalPhysicalStreamCombination() throws Exception {
for (String id : mCameraIds) {
try {
Log.i(TAG, "Testing Camera " + id);
openDevice(id);
if (!mStaticInfo.isColorOutputSupported()) {
Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
continue;
}
if (!mStaticInfo.isLogicalMultiCamera()) {
Log.i(TAG, "Camera " + id + " is not a logical multi-camera, skipping");
continue;
}
assertTrue("Logical multi-camera must be LIMITED or higher",
mStaticInfo.isHardwareLevelAtLeastLimited());
// Figure out yuv size and physical cameras to use.
List<String> dualPhysicalCameraIds = new ArrayList<String>();
Size yuvSize= findCommonPreviewSize(id, dualPhysicalCameraIds);
if (yuvSize == null) {
Log.i(TAG, "Camera " + id + ": No matching physical YUV streams, skipping");
continue;
}
if (VERBOSE) {
Log.v(TAG, "Camera " + id + ": Testing YUV size of " + yuvSize.getWidth() +
" x " + yuvSize.getHeight());
}
List<OutputConfiguration> outputConfigs = new ArrayList<>();
List<SimpleImageReaderListener> imageReaderListeners = new ArrayList<>();
SimpleImageReaderListener readerListenerPhysical = new SimpleImageReaderListener();
ImageReader yuvTargetPhysical = CameraTestUtils.makeImageReader(yuvSize,
ImageFormat.YUV_420_888, MAX_IMAGE_COUNT,
readerListenerPhysical, mHandler);
OutputConfiguration config = new OutputConfiguration(yuvTargetPhysical.getSurface());
config.setPhysicalCameraId(dualPhysicalCameraIds.get(0));
outputConfigs.add(config);
SimpleImageReaderListener readerListenerLogical = new SimpleImageReaderListener();
ImageReader yuvTargetLogical = CameraTestUtils.makeImageReader(yuvSize,
ImageFormat.YUV_420_888, MAX_IMAGE_COUNT,
readerListenerLogical, mHandler);
outputConfigs.add(new OutputConfiguration(yuvTargetLogical.getSurface()));
imageReaderListeners.add(readerListenerLogical);
mSessionListener = new BlockingSessionCallback();
mSession = configureCameraSessionWithConfig(mCamera, outputConfigs,
mSessionListener, mHandler);
// Test request logical stream with an idle physical stream.
CaptureRequest.Builder requestBuilder =
mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
requestBuilder.addTarget(yuvTargetLogical.getSurface());
for (int i = 0; i < MAX_IMAGE_COUNT; i++) {
mSession.capture(requestBuilder.build(), new SimpleCaptureCallback(), mHandler);
for (SimpleImageReaderListener readerListener : imageReaderListeners) {
Image image = readerListener.getImage(WAIT_FOR_RESULT_TIMEOUT_MS);
image.close();
}
}
// Test request physical stream with an idle logical stream.
imageReaderListeners.clear();
imageReaderListeners.add(readerListenerPhysical);
requestBuilder.removeTarget(yuvTargetLogical.getSurface());
requestBuilder.addTarget(yuvTargetPhysical.getSurface());
for (int i = 0; i < MAX_IMAGE_COUNT; i++) {
mSession.capture(requestBuilder.build(), new SimpleCaptureCallback(), mHandler);
for (SimpleImageReaderListener readerListener : imageReaderListeners) {
Image image = readerListener.getImage(WAIT_FOR_RESULT_TIMEOUT_MS);
image.close();
}
}
// Test request logical and physical streams at the same time
imageReaderListeners.clear();
readerListenerLogical.drain();
imageReaderListeners.add(readerListenerLogical);
readerListenerPhysical.drain();
imageReaderListeners.add(readerListenerPhysical);
requestBuilder.addTarget(yuvTargetLogical.getSurface());
for (int i = 0; i < MAX_IMAGE_COUNT; i++) {
mSession.capture(requestBuilder.build(), new SimpleCaptureCallback(), mHandler);
for (SimpleImageReaderListener readerListener : imageReaderListeners) {
Image image = readerListener.getImage(WAIT_FOR_RESULT_TIMEOUT_MS);
image.close();
}
}
if (mSession != null) {
mSession.close();
}
} finally {
closeDevice();
}
}
}
/**
* Test for making sure that multiple requests for physical cameras work as expected.
*/
public void testBasicPhysicalRequests() throws Exception {
for (String id : mCameraIds) {
try {
Log.i(TAG, "Testing Camera " + id);
openDevice(id);
if (!mStaticInfo.isColorOutputSupported()) {
Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
continue;
}
if (!mStaticInfo.isLogicalMultiCamera()) {
Log.i(TAG, "Camera " + id + " is not a logical multi-camera, skipping");
continue;
}
assertTrue("Logical multi-camera must be LIMITED or higher",
mStaticInfo.isHardwareLevelAtLeastLimited());
// Figure out yuv size and physical cameras to use.
List<String> dualPhysicalCameraIds = new ArrayList<String>();
Size yuvSize= findCommonPreviewSize(id, dualPhysicalCameraIds);
if (yuvSize == null) {
Log.i(TAG, "Camera " + id + ": No matching physical YUV streams, skipping");
continue;
}
ArraySet<String> physicalIdSet = new ArraySet<String>(dualPhysicalCameraIds.size());
physicalIdSet.addAll(dualPhysicalCameraIds);
if (VERBOSE) {
Log.v(TAG, "Camera " + id + ": Testing YUV size of " + yuvSize.getWidth() +
" x " + yuvSize.getHeight());
}
List<CaptureRequest.Key<?>> physicalRequestKeys =
mStaticInfo.getCharacteristics().getAvailablePhysicalCameraRequestKeys();
if (physicalRequestKeys == null) {
Log.i(TAG, "Camera " + id + ": no available physical request keys, skipping");
continue;
}
List<OutputConfiguration> outputConfigs = new ArrayList<>();
List<ImageReader> imageReaders = new ArrayList<>();
List<SimpleImageReaderListener> imageReaderListeners = new ArrayList<>();
for (String physicalCameraId : dualPhysicalCameraIds) {
SimpleImageReaderListener readerListener = new SimpleImageReaderListener();
ImageReader yuvTarget = CameraTestUtils.makeImageReader(yuvSize,
ImageFormat.YUV_420_888, MAX_IMAGE_COUNT,
readerListener, mHandler);
OutputConfiguration config = new OutputConfiguration(yuvTarget.getSurface());
config.setPhysicalCameraId(physicalCameraId);
outputConfigs.add(config);
imageReaders.add(yuvTarget);
imageReaderListeners.add(readerListener);
}
CaptureRequest.Builder requestBuilder =
mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW, physicalIdSet);
for (OutputConfiguration c : outputConfigs) {
requestBuilder.addTarget(c.getSurface());
}
mSessionListener = new BlockingSessionCallback();
mSession = configureCameraSessionWithConfig(mCamera, outputConfigs,
mSessionListener, mHandler);
for (int i = 0; i < MAX_IMAGE_COUNT; i++) {
mSession.capture(requestBuilder.build(), new SimpleCaptureCallback(), mHandler);
for (SimpleImageReaderListener readerListener : imageReaderListeners) {
readerListener.getImage(WAIT_FOR_RESULT_TIMEOUT_MS);
}
}
// Test streaming requests
imageReaders.clear();
outputConfigs.clear();
// Add physical YUV streams
List<ImageReader> physicalTargets = new ArrayList<>();
for (String physicalCameraId : dualPhysicalCameraIds) {
ImageReader physicalTarget = CameraTestUtils.makeImageReader(yuvSize,
ImageFormat.YUV_420_888, MAX_IMAGE_COUNT,
new ImageDropperListener(), mHandler);
OutputConfiguration config = new OutputConfiguration(
physicalTarget.getSurface());
config.setPhysicalCameraId(physicalCameraId);
outputConfigs.add(config);
physicalTargets.add(physicalTarget);
}
mSessionListener = new BlockingSessionCallback();
mSession = configureCameraSessionWithConfig(mCamera, outputConfigs,
mSessionListener, mHandler);
requestBuilder = mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW,
physicalIdSet);
for (OutputConfiguration c : outputConfigs) {
requestBuilder.addTarget(c.getSurface());
}
SimpleCaptureCallback simpleResultListener = new SimpleCaptureCallback();
mSession.setRepeatingRequest(requestBuilder.build(), simpleResultListener,
mHandler);
// Converge AE
waitForAeStable(simpleResultListener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
if (mSession != null) {
mSession.close();
}
} finally {
closeDevice();
}
}
}
/**
* Tests invalid/incorrect multiple physical capture request cases.
*/
public void testInvalidPhysicalCameraRequests() throws Exception {
for (String id : mCameraIds) {
try {
Log.i(TAG, "Testing Camera " + id);
openDevice(id);
if (mStaticInfo.isHardwareLevelLegacy()) {
Log.i(TAG, "Camera " + id + " is legacy, skipping");
continue;
}
if (!mStaticInfo.isColorOutputSupported()) {
Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
continue;
}
Size yuvSize = mOrderedPreviewSizes.get(0);
List<OutputConfiguration> outputConfigs = new ArrayList<>();
List<ImageReader> imageReaders = new ArrayList<>();
SimpleImageReaderListener readerListener = new SimpleImageReaderListener();
ImageReader yuvTarget = CameraTestUtils.makeImageReader(yuvSize,
ImageFormat.YUV_420_888, MAX_IMAGE_COUNT,
readerListener, mHandler);
imageReaders.add(yuvTarget);
OutputConfiguration config = new OutputConfiguration(yuvTarget.getSurface());
outputConfigs.add(new OutputConfiguration(yuvTarget.getSurface()));
ArraySet<String> physicalIdSet = new ArraySet<String>();
// Invalid physical id
physicalIdSet.add("-2");
CaptureRequest.Builder requestBuilder =
mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW, physicalIdSet);
requestBuilder.addTarget(config.getSurface());
// Check for invalid setting get/set
try {
requestBuilder.getPhysicalCameraKey(CaptureRequest.CONTROL_CAPTURE_INTENT, "-1");
fail("No exception for invalid physical camera id");
} catch (IllegalArgumentException e) {
//expected
}
try {
requestBuilder.setPhysicalCameraKey(CaptureRequest.CONTROL_CAPTURE_INTENT,
new Integer(0), "-1");
fail("No exception for invalid physical camera id");
} catch (IllegalArgumentException e) {
//expected
}
mSessionListener = new BlockingSessionCallback();
mSession = configureCameraSessionWithConfig(mCamera, outputConfigs,
mSessionListener, mHandler);
try {
mSession.capture(requestBuilder.build(), new SimpleCaptureCallback(),
mHandler);
fail("No exception for invalid physical camera id");
} catch (IllegalArgumentException e) {
//expected
}
if (mStaticInfo.isLogicalMultiCamera()) {
// Figure out yuv size to use.
List<String> dualPhysicalCameraIds = new ArrayList<String>();
Size sharedSize= findCommonPreviewSize(id, dualPhysicalCameraIds);
if (sharedSize == null) {
Log.i(TAG, "Camera " + id + ": No matching physical YUV streams, skipping");
continue;
}
physicalIdSet.clear();
physicalIdSet.addAll(dualPhysicalCameraIds);
requestBuilder =
mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW, physicalIdSet);
requestBuilder.addTarget(config.getSurface());
CaptureRequest request = requestBuilder.build();
try {
mSession.capture(request, new SimpleCaptureCallback(), mHandler);
fail("Capture requests that don't configure outputs with corresponding " +
"physical camera id should fail");
} catch (IllegalArgumentException e) {
//expected
}
}
if (mSession != null) {
mSession.close();
}
} finally {
closeDevice();
}
}
}
/**
* Find a common preview size that's supported by both the logical camera and
* two of the underlying physical cameras.
*/
private Size findCommonPreviewSize(String cameraId,
List<String> dualPhysicalCameraIds) throws Exception {
Set<String> physicalCameraIds =
mStaticInfo.getCharacteristics().getPhysicalCameraIds();
assertTrue("Logical camera must contain at least 2 physical camera ids",
physicalCameraIds.size() >= 2);
List<Size> previewSizes = getSupportedPreviewSizes(
cameraId, mCameraManager, PREVIEW_SIZE_BOUND);
HashMap<String, List<Size>> physicalPreviewSizesMap = new HashMap<String, List<Size>>();
HashMap<String, StreamConfigurationMap> physicalConfigs = new HashMap<>();
for (String physicalCameraId : physicalCameraIds) {
CameraCharacteristics properties =
mCameraManager.getCameraCharacteristics(physicalCameraId);
assertNotNull("Can't get camera characteristics!", properties);
StreamConfigurationMap configMap =
properties.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
physicalConfigs.put(physicalCameraId, configMap);
physicalPreviewSizesMap.put(physicalCameraId,
getSupportedPreviewSizes(physicalCameraId, mCameraManager, PREVIEW_SIZE_BOUND));
}
// Find display size from window service.
Context context = getInstrumentation().getTargetContext();
WindowManager windowManager =
(WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = windowManager.getDefaultDisplay();
int displayWidth = display.getWidth();
int displayHeight = display.getHeight();
if (displayHeight > displayWidth) {
displayHeight = displayWidth;
displayWidth = display.getHeight();
}
StreamConfigurationMap config = mStaticInfo.getCharacteristics().get(
CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
for (Size previewSize : previewSizes) {
dualPhysicalCameraIds.clear();
// Skip preview sizes larger than screen size
if (previewSize.getWidth() > displayWidth ||
previewSize.getHeight() > displayHeight) {
continue;
}
final long minFrameDuration = config.getOutputMinFrameDuration(
ImageFormat.YUV_420_888, previewSize);
ArrayList<String> supportedPhysicalCameras = new ArrayList<String>();
for (String physicalCameraId : physicalCameraIds) {
List<Size> physicalPreviewSizes = physicalPreviewSizesMap.get(physicalCameraId);
if (physicalPreviewSizes.contains(previewSize)) {
long minDurationPhysical =
physicalConfigs.get(physicalCameraId).getOutputMinFrameDuration(
ImageFormat.YUV_420_888, previewSize);
if (minDurationPhysical <= minFrameDuration) {
dualPhysicalCameraIds.add(physicalCameraId);
if (dualPhysicalCameraIds.size() == 2) {
return previewSize;
}
}
}
}
}
return null;
}
/**
* Test physical camera YUV streaming within a particular logical camera.
*
* Use 2 YUV streams with PREVIEW or smaller size, which is guaranteed for LIMITED device level.
*/
private void testBasicPhysicalStreamingForCamera(String logicalCameraId,
List<String> physicalCameraIds, Size previewSize) throws Exception {
List<OutputConfiguration> outputConfigs = new ArrayList<>();
List<ImageReader> imageReaders = new ArrayList<>();
// Add 1 logical YUV stream
ImageReader logicalTarget = CameraTestUtils.makeImageReader(previewSize,
ImageFormat.YUV_420_888, MAX_IMAGE_COUNT,
new ImageDropperListener(), mHandler);
imageReaders.add(logicalTarget);
outputConfigs.add(new OutputConfiguration(logicalTarget.getSurface()));
// Add physical YUV streams
if (physicalCameraIds.size() != 2) {
throw new IllegalArgumentException("phyiscalCameraIds must contain 2 camera ids");
}
List<ImageReader> physicalTargets = new ArrayList<>();
for (String physicalCameraId : physicalCameraIds) {
ImageReader physicalTarget = CameraTestUtils.makeImageReader(previewSize,
ImageFormat.YUV_420_888, MAX_IMAGE_COUNT,
new ImageDropperListener(), mHandler);
OutputConfiguration config = new OutputConfiguration(physicalTarget.getSurface());
config.setPhysicalCameraId(physicalCameraId);
outputConfigs.add(config);
physicalTargets.add(physicalTarget);
}
mSessionListener = new BlockingSessionCallback();
mSession = configureCameraSessionWithConfig(mCamera, outputConfigs,
mSessionListener, mHandler);
// Stream logical YUV stream and note down the FPS
CaptureRequest.Builder requestBuilder =
mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
requestBuilder.addTarget(logicalTarget.getSurface());
SimpleCaptureCallback simpleResultListener =
new SimpleCaptureCallback();
StreamConfigurationMap config = mStaticInfo.getCharacteristics().get(
CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
final long minFrameDuration = config.getOutputMinFrameDuration(
ImageFormat.YUV_420_888, previewSize);
if (minFrameDuration > 0) {
Range<Integer> targetRange = getSuitableFpsRangeForDuration(logicalCameraId,
minFrameDuration);
requestBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, targetRange);
}
mSession.setRepeatingRequest(requestBuilder.build(),
simpleResultListener, mHandler);
// Converge AE
waitForAeStable(simpleResultListener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
if (mStaticInfo.isAeLockSupported()) {
// Lock AE if supported.
requestBuilder.set(CaptureRequest.CONTROL_AE_LOCK, true);
mSession.setRepeatingRequest(requestBuilder.build(), simpleResultListener,
mHandler);
waitForResultValue(simpleResultListener, CaptureResult.CONTROL_AE_STATE,
CaptureResult.CONTROL_AE_STATE_LOCKED, NUM_RESULTS_WAIT_TIMEOUT);
}
// Verify results
CaptureResultTest.validateCaptureResult(mCollector, simpleResultListener,
mStaticInfo, mAllStaticInfo, null/*requestedPhysicalIds*/,
requestBuilder, NUM_FRAMES_CHECKED);
// Collect timestamps for one logical stream only.
long prevTimestamp = -1;
long[] logicalTimestamps = new long[NUM_FRAMES_CHECKED];
for (int i = 0; i < NUM_FRAMES_CHECKED; i++) {
TotalCaptureResult totalCaptureResult =
simpleResultListener.getTotalCaptureResult(
CameraTestUtils.CAPTURE_RESULT_TIMEOUT_MS);
logicalTimestamps[i] = totalCaptureResult.get(CaptureResult.SENSOR_TIMESTAMP);
}
double logicalAvgDurationMs = (logicalTimestamps[NUM_FRAMES_CHECKED-1] -
logicalTimestamps[0])/(NS_PER_MS*(NUM_FRAMES_CHECKED-1));
// Request one logical stream and one physical stream
simpleResultListener = new SimpleCaptureCallback();
requestBuilder.addTarget(physicalTargets.get(1).getSurface());
mSession.setRepeatingRequest(requestBuilder.build(), simpleResultListener,
mHandler);
// Verify results for physical streams request.
CaptureResultTest.validateCaptureResult(mCollector, simpleResultListener,
mStaticInfo, mAllStaticInfo, physicalCameraIds.subList(1, 2), requestBuilder,
NUM_FRAMES_CHECKED);
// Start requesting on both logical and physical streams
SimpleCaptureCallback simpleResultListenerDual =
new SimpleCaptureCallback();
for (ImageReader physicalTarget : physicalTargets) {
requestBuilder.addTarget(physicalTarget.getSurface());
}
mSession.setRepeatingRequest(requestBuilder.build(), simpleResultListenerDual,
mHandler);
// Verify results for physical streams request.
CaptureResultTest.validateCaptureResult(mCollector, simpleResultListenerDual,
mStaticInfo, mAllStaticInfo, physicalCameraIds, requestBuilder,
NUM_FRAMES_CHECKED);
// Acquire the timestamps of the physical camera.
long[] logicalTimestamps2 = new long[NUM_FRAMES_CHECKED];
long [][] physicalTimestamps = new long[physicalTargets.size()][];
for (int i = 0; i < physicalTargets.size(); i++) {
physicalTimestamps[i] = new long[NUM_FRAMES_CHECKED];
}
for (int i = 0; i < NUM_FRAMES_CHECKED; i++) {
TotalCaptureResult totalCaptureResultDual =
simpleResultListenerDual.getTotalCaptureResult(
CameraTestUtils.CAPTURE_RESULT_TIMEOUT_MS);
logicalTimestamps2[i] = totalCaptureResultDual.get(CaptureResult.SENSOR_TIMESTAMP);
int index = 0;
Map<String, CaptureResult> physicalResultsDual =
totalCaptureResultDual.getPhysicalCameraResults();
for (String physicalId : physicalCameraIds) {
physicalTimestamps[index][i] = physicalResultsDual.get(physicalId).get(
CaptureResult.SENSOR_TIMESTAMP);
index++;
}
}
// Check timestamp monolithity for individual camera and across cameras
for (int i = 0; i < NUM_FRAMES_CHECKED-1; i++) {
assertTrue("Logical camera timestamp must monolithically increase",
logicalTimestamps2[i] < logicalTimestamps2[i+1]);
}
for (int i = 0; i < physicalCameraIds.size(); i++) {
for (int j = 0 ; j < NUM_FRAMES_CHECKED-1; j++) {
assertTrue("Physical camera timestamp must monolithically increase",
physicalTimestamps[i][j] < physicalTimestamps[i][j+1]);
if (j > 0) {
assertTrue("Physical camera's timestamp N must be greater than logical " +
"camera's timestamp N-1",
physicalTimestamps[i][j] > logicalTimestamps[j-1]);
}
assertTrue("Physical camera's timestamp N must be less than logical camera's " +
"timestamp N+1", physicalTimestamps[i][j] > logicalTimestamps[j+1]);
}
}
double logicalAvgDurationMs2 = (logicalTimestamps2[NUM_FRAMES_CHECKED-1] -
logicalTimestamps2[0])/(NS_PER_MS*(NUM_FRAMES_CHECKED-1));
mCollector.expectLessOrEqual("The average frame duration increase of all physical "
+ "streams is larger than threshold: "
+ String.format("increase = %.2f, threshold = %.2f",
(logicalAvgDurationMs2 - logicalAvgDurationMs)/logicalAvgDurationMs,
FRAME_DURATION_THRESHOLD),
logicalAvgDurationMs*(1+FRAME_DURATION_THRESHOLD),
logicalAvgDurationMs2);
// Stop preview
if (mSession != null) {
mSession.close();
}
}
}