blob: aad9c2f6734ae6b2bca79837b62b53ef15f77e55 [file] [log] [blame]
/*
* Copyright (C) 2014 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.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCaptureSession.CaptureCallback;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CaptureFailure;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.TotalCaptureResult;
import android.util.Size;
import android.hardware.camera2.cts.CameraTestUtils.SimpleCaptureCallback;
import android.hardware.camera2.cts.testcases.Camera2SurfaceViewTestCase;
import android.util.Log;
import android.util.Range;
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatcher;
import static org.mockito.Mockito.*;
import java.util.Arrays;
import java.util.List;
/**
* CameraDevice preview test by using SurfaceView.
*/
public class SurfaceViewPreviewTest extends Camera2SurfaceViewTestCase {
private static final String TAG = "SurfaceViewPreviewTest";
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
private static final int FRAME_TIMEOUT_MS = 1000;
private static final int NUM_FRAMES_VERIFIED = 30;
private static final int NUM_TEST_PATTERN_FRAMES_VERIFIED = 60;
private static final float FRAME_DURATION_ERROR_MARGIN = 0.005f; // 0.5 percent error margin.
@Override
protected void setUp() throws Exception {
super.setUp();
}
@Override
protected void tearDown() throws Exception {
super.tearDown();
}
/**
* Test all supported preview sizes for each camera device.
* <p>
* For the first {@link #NUM_FRAMES_VERIFIED} of capture results,
* the {@link CaptureCallback} callback availability and the capture timestamp
* (monotonically increasing) ordering are verified.
* </p>
*/
public void testCameraPreview() throws Exception {
for (int i = 0; i < mCameraIds.length; i++) {
try {
Log.i(TAG, "Testing preview for Camera " + mCameraIds[i]);
openDevice(mCameraIds[i]);
previewTestByCamera();
} finally {
closeDevice();
}
}
}
/**
* Basic test pattern mode preview.
* <p>
* Only test the test pattern preview and capture result, the image buffer
* is not validated.
* </p>
*/
public void testBasicTestPatternPreview() throws Exception{
for (int i = 0; i < mCameraIds.length; i++) {
try {
Log.i(TAG, "Testing preview for Camera " + mCameraIds[i]);
openDevice(mCameraIds[i]);
previewTestPatternTestByCamera();
} finally {
closeDevice();
}
}
}
/**
* Test {@link CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE} for preview, validate the preview
* frame duration and exposure time.
*/
public void testPreviewFpsRange() throws Exception {
for (String id : mCameraIds) {
try {
openDevice(id);
previewFpsRangeTestByCamera();
} finally {
closeDevice();
}
}
}
/**
* Test preview fps range for all supported ranges. The exposure time are frame duration are
* validated.
*/
private void previewFpsRangeTestByCamera() throws Exception {
final int FPS_RANGE_SIZE = 2;
Size maxPreviewSz = mOrderedPreviewSizes.get(0);
Range<Integer>[] fpsRanges = mStaticInfo.getAeAvailableTargetFpsRangesChecked();
boolean antiBandingOffIsSupported = mStaticInfo.isAntiBandingOffModeSupported();
Range<Integer> fpsRange;
CaptureRequest.Builder requestBuilder =
mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
startPreview(requestBuilder, maxPreviewSz, resultListener);
for (int i = 0; i < fpsRanges.length; i += 1) {
fpsRange = fpsRanges[i];
requestBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, fpsRange);
// Turn off auto antibanding to avoid exposure time and frame duration interference
// from antibanding algorithm.
if (antiBandingOffIsSupported) {
requestBuilder.set(CaptureRequest.CONTROL_AE_ANTIBANDING_MODE,
CaptureRequest.CONTROL_AE_ANTIBANDING_MODE_OFF);
} else {
// The device doesn't implement the OFF mode, test continues. It need make sure
// that the antibanding algorithm doesn't interfere with the fps range control.
Log.i(TAG, "OFF antibanding mode is not supported, the camera device output must" +
" satisfy the specified fps range regardless of its current antibanding" +
" mode");
}
resultListener = new SimpleCaptureCallback();
mSession.setRepeatingRequest(requestBuilder.build(), resultListener, mHandler);
verifyPreviewTargetFpsRange(resultListener, NUM_FRAMES_VERIFIED, fpsRange,
maxPreviewSz);
}
stopPreview();
}
private void verifyPreviewTargetFpsRange(SimpleCaptureCallback resultListener,
int numFramesVerified, Range<Integer> fpsRange, Size previewSz) {
CaptureResult result = resultListener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
List<Integer> capabilities = mStaticInfo.getAvailableCapabilitiesChecked();
if (capabilities.contains(CaptureRequest.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
long frameDuration = getValueNotNull(result, CaptureResult.SENSOR_FRAME_DURATION);
long[] frameDurationRange =
new long[]{(long) (1e9 / fpsRange.getUpper()), (long) (1e9 / fpsRange.getLower())};
mCollector.expectInRange(
"Frame duration must be in the range of " + Arrays.toString(frameDurationRange),
frameDuration, (long) (frameDurationRange[0] * (1 - FRAME_DURATION_ERROR_MARGIN)),
(long) (frameDurationRange[1] * (1 + FRAME_DURATION_ERROR_MARGIN)));
long expTime = getValueNotNull(result, CaptureResult.SENSOR_EXPOSURE_TIME);
mCollector.expectTrue(String.format("Exposure time %d must be no larger than frame"
+ "duration %d", expTime, frameDuration), expTime <= frameDuration);
Long minFrameDuration = mMinPreviewFrameDurationMap.get(previewSz);
boolean findDuration = mCollector.expectTrue("Unable to find minFrameDuration for size "
+ previewSz.toString(), minFrameDuration != null);
if (findDuration) {
mCollector.expectTrue("Frame duration " + frameDuration + " must be no smaller than"
+ " minFrameDuration " + minFrameDuration, frameDuration >= minFrameDuration);
}
} else {
Log.i(TAG, "verifyPreviewTargetFpsRange - MANUAL_SENSOR control is not supported," +
" skipping duration and exposure time check.");
}
}
/**
* Test all supported preview sizes for a camera device
*
* @throws Exception
*/
private void previewTestByCamera() throws Exception {
List<Size> previewSizes = getSupportedPreviewSizes(
mCamera.getId(), mCameraManager, PREVIEW_SIZE_BOUND);
for (final Size sz : previewSizes) {
if (VERBOSE) {
Log.v(TAG, "Testing camera preview size: " + sz.toString());
}
// TODO: vary the different settings like crop region to cover more cases.
CaptureRequest.Builder requestBuilder =
mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
CaptureCallback mockCaptureCallback =
mock(CameraCaptureSession.CaptureCallback.class);
startPreview(requestBuilder, sz, mockCaptureCallback);
verifyCaptureResults(mSession, mockCaptureCallback, NUM_FRAMES_VERIFIED,
NUM_FRAMES_VERIFIED * FRAME_TIMEOUT_MS);
stopPreview();
}
}
private void previewTestPatternTestByCamera() throws Exception {
Size maxPreviewSize = mOrderedPreviewSizes.get(0);
int[] testPatternModes = mStaticInfo.getAvailableTestPatternModesChecked();
CaptureRequest.Builder requestBuilder =
mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
CaptureCallback mockCaptureCallback;
final int[] TEST_PATTERN_DATA = {0, 0xFFFFFFFF, 0xFFFFFFFF, 0}; // G:100%, RB:0.
for (int mode : testPatternModes) {
if (VERBOSE) {
Log.v(TAG, "Test pattern mode: " + mode);
}
requestBuilder.set(CaptureRequest.SENSOR_TEST_PATTERN_MODE, mode);
if (mode == CaptureRequest.SENSOR_TEST_PATTERN_MODE_SOLID_COLOR) {
// Assign color pattern to SENSOR_TEST_PATTERN_MODE_DATA
requestBuilder.set(CaptureRequest.SENSOR_TEST_PATTERN_DATA, TEST_PATTERN_DATA);
}
mockCaptureCallback = mock(CaptureCallback.class);
startPreview(requestBuilder, maxPreviewSize, mockCaptureCallback);
verifyCaptureResults(mSession, mockCaptureCallback, NUM_TEST_PATTERN_FRAMES_VERIFIED,
NUM_TEST_PATTERN_FRAMES_VERIFIED * FRAME_TIMEOUT_MS);
}
stopPreview();
}
private class IsCaptureResultValid extends ArgumentMatcher<TotalCaptureResult> {
@Override
public boolean matches(Object obj) {
TotalCaptureResult result = (TotalCaptureResult)obj;
Long timeStamp = result.get(CaptureResult.SENSOR_TIMESTAMP);
if (timeStamp != null && timeStamp.longValue() > 0L) {
return true;
}
return false;
}
}
private void verifyCaptureResults(
CameraCaptureSession session,
CaptureCallback mockListener,
int expectResultCount,
int timeOutMs) {
// Should receive expected number of onCaptureStarted callbacks.
ArgumentCaptor<Long> timestamps = ArgumentCaptor.forClass(Long.class);
verify(mockListener,
timeout(timeOutMs).atLeast(expectResultCount))
.onCaptureStarted(
eq(session),
isA(CaptureRequest.class),
timestamps.capture());
// Validate timestamps: all timestamps should be larger than 0 and monotonically increase.
long timestamp = 0;
for (Long nextTimestamp : timestamps.getAllValues()) {
assertNotNull("Next timestamp is null!", nextTimestamp);
assertTrue("Captures are out of order", timestamp < nextTimestamp);
timestamp = nextTimestamp;
}
// Should receive expected number of capture results.
verify(mockListener,
timeout(timeOutMs).atLeast(expectResultCount))
.onCaptureCompleted(
eq(session),
isA(CaptureRequest.class),
argThat(new IsCaptureResultValid()));
// Should not receive any capture failed callbacks.
verify(mockListener, never())
.onCaptureFailed(
eq(session),
isA(CaptureRequest.class),
isA(CaptureFailure.class));
}
}