blob: 1b59c3b98142fe532168209ee373401a882cf38f [file] [log] [blame]
/*
* Copyright 2020 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 org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import com.android.compatibility.common.util.DeviceReportLog;
import com.android.compatibility.common.util.ResultType;
import com.android.compatibility.common.util.ResultUnit;
import com.android.compatibility.common.util.Stat;
import com.android.ex.camera2.blocking.BlockingSessionCallback;
import com.android.ex.camera2.blocking.BlockingExtensionSessionCallback;
import com.android.ex.camera2.blocking.BlockingStateCallback;
import com.android.ex.camera2.exceptions.TimeoutRuntimeException;
import com.android.ex.camera2.pos.AutoFocusStateMachine;
import android.graphics.ImageFormat;
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
import android.hardware.HardwareBuffer;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraExtensionCharacteristics;
import android.hardware.camera2.CameraExtensionSession;
import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.cts.helpers.CameraErrorCollector;
import android.hardware.camera2.cts.helpers.StaticMetadata;
import android.hardware.camera2.cts.testcases.Camera2AndroidTestRule;
import android.hardware.camera2.params.ExtensionSessionConfiguration;
import android.hardware.camera2.params.MeteringRectangle;
import android.hardware.camera2.params.OutputConfiguration;
import android.hardware.camera2.params.SessionConfiguration;
import android.media.ExifInterface;
import android.media.Image;
import android.media.ImageReader;
import android.os.Build;
import android.os.SystemClock;
import android.util.Range;
import android.util.Size;
import static android.hardware.camera2.cts.CameraTestUtils.*;
import static android.hardware.cts.helpers.CameraUtils.*;
import android.util.Log;
import android.view.Surface;
import android.view.TextureView;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.rule.ActivityTestRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@RunWith(Parameterized.class)
public class CameraExtensionSessionTest extends Camera2ParameterizedTestCase {
private static final String TAG = "CameraExtensionSessionTest";
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
private static final long WAIT_FOR_COMMAND_TO_COMPLETE_MS = 5000;
private static final long REPEATING_REQUEST_TIMEOUT_MS = 5000;
private static final int MULTI_FRAME_CAPTURE_IMAGE_TIMEOUT_MS = 10000;
private static final float ZOOM_ERROR_MARGIN = 0.05f;
private SurfaceTexture mSurfaceTexture = null;
private Camera2AndroidTestRule mTestRule = null;
private CameraErrorCollector mCollector = null;
private DeviceReportLog mReportLog;
@Override
public void setUp() throws Exception {
super.setUp();
mTestRule = new Camera2AndroidTestRule(mContext);
mTestRule.before();
mCollector = new CameraErrorCollector();
}
@Override
public void tearDown() throws Exception {
if (mTestRule != null) {
mTestRule.after();
}
if (mSurfaceTexture != null) {
mSurfaceTexture.release();
mSurfaceTexture = null;
}
if (mCollector != null) {
try {
mCollector.verify();
} catch (Throwable e) {
throw new Exception(e.getMessage());
}
}
super.tearDown();
}
@Rule
public ActivityTestRule<CameraExtensionTestActivity> mActivityRule =
new ActivityTestRule<>(CameraExtensionTestActivity.class);
private void updatePreviewSurfaceTexture() {
if (mSurfaceTexture != null) {
return;
}
TextureView textureView = mActivityRule.getActivity().getTextureView();
mSurfaceTexture = getAvailableSurfaceTexture(WAIT_FOR_COMMAND_TO_COMPLETE_MS, textureView);
assertNotNull("Failed to acquire valid preview surface texture!", mSurfaceTexture);
}
// Verify that camera extension sessions can be created and closed as expected.
@Test
public void testBasicExtensionLifecycle() throws Exception {
for (String id : getCameraIdsUnderTest()) {
StaticMetadata staticMeta =
new StaticMetadata(mTestRule.getCameraManager().getCameraCharacteristics(id));
if (!staticMeta.isColorOutputSupported()) {
continue;
}
updatePreviewSurfaceTexture();
CameraExtensionCharacteristics extensionChars =
mTestRule.getCameraManager().getCameraExtensionCharacteristics(id);
List<Integer> supportedExtensions = extensionChars.getSupportedExtensions();
for (Integer extension : supportedExtensions) {
List<Size> extensionSizes = extensionChars.getExtensionSupportedSizes(extension,
mSurfaceTexture.getClass());
Size maxSize = CameraTestUtils.getMaxSize(extensionSizes.toArray(new Size[0]));
mSurfaceTexture.setDefaultBufferSize(maxSize.getWidth(), maxSize.getHeight());
OutputConfiguration outputConfig = new OutputConfiguration(
new Surface(mSurfaceTexture));
List<OutputConfiguration> outputConfigs = new ArrayList<>();
outputConfigs.add(outputConfig);
BlockingExtensionSessionCallback sessionListener =
new BlockingExtensionSessionCallback(
mock(CameraExtensionSession.StateCallback.class));
ExtensionSessionConfiguration configuration =
new ExtensionSessionConfiguration(extension, outputConfigs,
new HandlerExecutor(mTestRule.getHandler()), sessionListener);
try {
mTestRule.openDevice(id);
CameraDevice camera = mTestRule.getCamera();
camera.createExtensionSession(configuration);
CameraExtensionSession extensionSession = sessionListener.waitAndGetSession(
SESSION_CONFIGURE_TIMEOUT_MS);
extensionSession.close();
sessionListener.getStateWaiter().waitForState(
BlockingExtensionSessionCallback.SESSION_CLOSED,
SESSION_CLOSE_TIMEOUT_MS);
} finally {
mTestRule.closeDevice(id);
}
}
}
}
// Verify that regular camera sessions close as expected after creating a camera extension
// session.
@Test
public void testCloseCaptureSession() throws Exception {
for (String id : getCameraIdsUnderTest()) {
StaticMetadata staticMeta =
new StaticMetadata(mTestRule.getCameraManager().getCameraCharacteristics(id));
if (!staticMeta.isColorOutputSupported()) {
continue;
}
updatePreviewSurfaceTexture();
CameraExtensionCharacteristics extensionChars =
mTestRule.getCameraManager().getCameraExtensionCharacteristics(id);
List<Integer> supportedExtensions = extensionChars.getSupportedExtensions();
for (Integer extension : supportedExtensions) {
List<Size> extensionSizes = extensionChars.getExtensionSupportedSizes(extension,
mSurfaceTexture.getClass());
Size maxSize = CameraTestUtils.getMaxSize(extensionSizes.toArray(new Size[0]));
mSurfaceTexture.setDefaultBufferSize(maxSize.getWidth(), maxSize.getHeight());
Surface repeatingSurface = new Surface(mSurfaceTexture);
OutputConfiguration textureOutput = new OutputConfiguration(repeatingSurface);
List<OutputConfiguration> outputs = new ArrayList<>();
outputs.add(textureOutput);
BlockingSessionCallback regularSessionListener = new BlockingSessionCallback(
mock(CameraCaptureSession.StateCallback.class));
SessionConfiguration regularConfiguration = new SessionConfiguration(
SessionConfiguration.SESSION_REGULAR, outputs,
new HandlerExecutor(mTestRule.getHandler()), regularSessionListener);
BlockingExtensionSessionCallback sessionListener =
new BlockingExtensionSessionCallback(mock(
CameraExtensionSession.StateCallback.class));
ExtensionSessionConfiguration configuration =
new ExtensionSessionConfiguration(extension, outputs,
new HandlerExecutor(mTestRule.getHandler()), sessionListener);
try {
mTestRule.openDevice(id);
mTestRule.getCamera().createCaptureSession(regularConfiguration);
CameraCaptureSession session =
regularSessionListener
.waitAndGetSession(SESSION_CONFIGURE_TIMEOUT_MS);
assertNotNull(session);
CameraDevice camera = mTestRule.getCamera();
camera.createExtensionSession(configuration);
CameraExtensionSession extensionSession = sessionListener.waitAndGetSession(
SESSION_CONFIGURE_TIMEOUT_MS);
assertNotNull(extensionSession);
regularSessionListener.getStateWaiter().waitForState(
BlockingExtensionSessionCallback.SESSION_CLOSED,
SESSION_CLOSE_TIMEOUT_MS);
extensionSession.close();
sessionListener.getStateWaiter().waitForState(
BlockingExtensionSessionCallback.SESSION_CLOSED,
SESSION_CLOSE_TIMEOUT_MS);
} finally {
mTestRule.closeDevice(id);
}
}
}
}
// Verify that camera extension sessions close as expected when creating a regular capture
// session.
@Test
public void testCloseExtensionSession() throws Exception {
for (String id : getCameraIdsUnderTest()) {
StaticMetadata staticMeta =
new StaticMetadata(mTestRule.getCameraManager().getCameraCharacteristics(id));
if (!staticMeta.isColorOutputSupported()) {
continue;
}
updatePreviewSurfaceTexture();
CameraExtensionCharacteristics extensionChars =
mTestRule.getCameraManager().getCameraExtensionCharacteristics(id);
List<Integer> supportedExtensions = extensionChars.getSupportedExtensions();
for (Integer extension : supportedExtensions) {
List<Size> extensionSizes = extensionChars.getExtensionSupportedSizes(extension,
mSurfaceTexture.getClass());
Size maxSize = CameraTestUtils.getMaxSize(extensionSizes.toArray(new Size[0]));
mSurfaceTexture.setDefaultBufferSize(maxSize.getWidth(), maxSize.getHeight());
Surface surface = new Surface(mSurfaceTexture);
OutputConfiguration textureOutput = new OutputConfiguration(surface);
List<OutputConfiguration> outputs = new ArrayList<>();
outputs.add(textureOutput);
BlockingSessionCallback regularSessionListener = new BlockingSessionCallback(
mock(CameraCaptureSession.StateCallback.class));
SessionConfiguration regularConfiguration = new SessionConfiguration(
SessionConfiguration.SESSION_REGULAR, outputs,
new HandlerExecutor(mTestRule.getHandler()), regularSessionListener);
BlockingExtensionSessionCallback sessionListener =
new BlockingExtensionSessionCallback(mock(
CameraExtensionSession.StateCallback.class));
ExtensionSessionConfiguration configuration =
new ExtensionSessionConfiguration(extension, outputs,
new HandlerExecutor(mTestRule.getHandler()), sessionListener);
try {
mTestRule.openDevice(id);
CameraDevice camera = mTestRule.getCamera();
camera.createExtensionSession(configuration);
CameraExtensionSession extensionSession = sessionListener.waitAndGetSession(
SESSION_CONFIGURE_TIMEOUT_MS);
assertNotNull(extensionSession);
mTestRule.getCamera().createCaptureSession(regularConfiguration);
sessionListener.getStateWaiter().waitForState(
BlockingExtensionSessionCallback.SESSION_CLOSED,
SESSION_CLOSE_TIMEOUT_MS);
CameraCaptureSession session =
regularSessionListener.waitAndGetSession(
SESSION_CONFIGURE_TIMEOUT_MS);
session.close();
regularSessionListener.getStateWaiter().waitForState(
BlockingSessionCallback.SESSION_CLOSED, SESSION_CLOSE_TIMEOUT_MS);
} finally {
mTestRule.closeDevice(id);
}
}
}
}
// Verify camera device query
@Test
public void testGetDevice() throws Exception {
for (String id : getCameraIdsUnderTest()) {
StaticMetadata staticMeta =
new StaticMetadata(mTestRule.getCameraManager().getCameraCharacteristics(id));
if (!staticMeta.isColorOutputSupported()) {
continue;
}
updatePreviewSurfaceTexture();
CameraExtensionCharacteristics extensionChars =
mTestRule.getCameraManager().getCameraExtensionCharacteristics(id);
List<Integer> supportedExtensions = extensionChars.getSupportedExtensions();
for (Integer extension : supportedExtensions) {
List<Size> extensionSizes = extensionChars.getExtensionSupportedSizes(extension,
mSurfaceTexture.getClass());
Size maxSize = CameraTestUtils.getMaxSize(extensionSizes.toArray(new Size[0]));
mSurfaceTexture.setDefaultBufferSize(maxSize.getWidth(), maxSize.getHeight());
OutputConfiguration privateOutput = new OutputConfiguration(
new Surface(mSurfaceTexture));
List<OutputConfiguration> outputConfigs = new ArrayList<>();
outputConfigs.add(privateOutput);
BlockingExtensionSessionCallback sessionListener =
new BlockingExtensionSessionCallback(
mock(CameraExtensionSession.StateCallback.class));
ExtensionSessionConfiguration configuration =
new ExtensionSessionConfiguration(extension, outputConfigs,
new HandlerExecutor(mTestRule.getHandler()), sessionListener);
try {
mTestRule.openDevice(id);
CameraDevice camera = mTestRule.getCamera();
camera.createExtensionSession(configuration);
CameraExtensionSession extensionSession = sessionListener.waitAndGetSession(
SESSION_CONFIGURE_TIMEOUT_MS);
assertEquals("Unexpected/Invalid camera device", mTestRule.getCamera(),
extensionSession.getDevice());
} finally {
mTestRule.closeDevice(id);
}
try {
sessionListener.getStateWaiter().waitForState(
BlockingExtensionSessionCallback.SESSION_CLOSED,
SESSION_CLOSE_TIMEOUT_MS);
fail("should get TimeoutRuntimeException due to previously closed camera "
+ "device");
} catch (TimeoutRuntimeException e) {
// Expected, per API spec we should not receive any further session callbacks
// besides the device state 'onClosed' callback.
}
}
}
}
// Test case for repeating/stopRepeating on all supported extensions and expected state/capture
// callbacks.
@Test
public void testRepeatingCapture() throws Exception {
for (String id : getCameraIdsUnderTest()) {
StaticMetadata staticMeta =
new StaticMetadata(mTestRule.getCameraManager().getCameraCharacteristics(id));
if (!staticMeta.isColorOutputSupported()) {
continue;
}
updatePreviewSurfaceTexture();
CameraExtensionCharacteristics extensionChars =
mTestRule.getCameraManager().getCameraExtensionCharacteristics(id);
List<Integer> supportedExtensions = extensionChars.getSupportedExtensions();
for (Integer extension : supportedExtensions) {
List<Size> extensionSizes = extensionChars.getExtensionSupportedSizes(extension,
mSurfaceTexture.getClass());
Size maxSize =
CameraTestUtils.getMaxSize(extensionSizes.toArray(new Size[0]));
mSurfaceTexture.setDefaultBufferSize(maxSize.getWidth(),
maxSize.getHeight());
Surface texturedSurface = new Surface(mSurfaceTexture);
List<OutputConfiguration> outputConfigs = new ArrayList<>();
outputConfigs.add(new OutputConfiguration(texturedSurface));
BlockingExtensionSessionCallback sessionListener =
new BlockingExtensionSessionCallback(mock(
CameraExtensionSession.StateCallback.class));
ExtensionSessionConfiguration configuration =
new ExtensionSessionConfiguration(extension, outputConfigs,
new HandlerExecutor(mTestRule.getHandler()),
sessionListener);
boolean captureResultsSupported =
!extensionChars.getAvailableCaptureResultKeys(extension).isEmpty();
try {
mTestRule.openDevice(id);
CameraDevice camera = mTestRule.getCamera();
camera.createExtensionSession(configuration);
CameraExtensionSession extensionSession =
sessionListener.waitAndGetSession(
SESSION_CONFIGURE_TIMEOUT_MS);
assertNotNull(extensionSession);
CaptureRequest.Builder captureBuilder =
mTestRule.getCamera().createCaptureRequest(
android.hardware.camera2.CameraDevice.TEMPLATE_PREVIEW);
captureBuilder.addTarget(texturedSurface);
CameraExtensionSession.ExtensionCaptureCallback captureCallbackMock =
mock(CameraExtensionSession.ExtensionCaptureCallback.class);
SimpleCaptureCallback simpleCaptureCallback =
new SimpleCaptureCallback(extension, captureCallbackMock,
extensionChars.getAvailableCaptureResultKeys(extension),
mCollector);
CaptureRequest request = captureBuilder.build();
int sequenceId = extensionSession.setRepeatingRequest(request,
new HandlerExecutor(mTestRule.getHandler()), simpleCaptureCallback);
verify(captureCallbackMock,
timeout(REPEATING_REQUEST_TIMEOUT_MS).atLeastOnce())
.onCaptureStarted(eq(extensionSession), eq(request), anyLong());
verify(captureCallbackMock,
timeout(REPEATING_REQUEST_TIMEOUT_MS).atLeastOnce())
.onCaptureProcessStarted(extensionSession, request);
if (captureResultsSupported) {
verify(captureCallbackMock,
timeout(MULTI_FRAME_CAPTURE_IMAGE_TIMEOUT_MS).atLeastOnce())
.onCaptureResultAvailable(eq(extensionSession), eq(request),
any(TotalCaptureResult.class));
}
extensionSession.stopRepeating();
verify(captureCallbackMock,
timeout(MULTI_FRAME_CAPTURE_IMAGE_TIMEOUT_MS).times(1))
.onCaptureSequenceCompleted(extensionSession, sequenceId);
verify(captureCallbackMock, times(0))
.onCaptureSequenceAborted(any(CameraExtensionSession.class),
anyInt());
extensionSession.close();
sessionListener.getStateWaiter().waitForState(
BlockingExtensionSessionCallback.SESSION_CLOSED,
SESSION_CLOSE_TIMEOUT_MS);
assertTrue("The sum of onCaptureProcessStarted and onCaptureFailed" +
" callbacks must be greater or equal than the number of calls" +
" to onCaptureStarted!",
simpleCaptureCallback.getTotalFramesArrived() +
simpleCaptureCallback.getTotalFramesFailed() >=
simpleCaptureCallback.getTotalFramesStarted());
assertTrue(String.format("The last repeating request surface timestamp " +
"%d must be less than or equal to the last " +
"onCaptureStarted " +
"timestamp %d", mSurfaceTexture.getTimestamp(),
simpleCaptureCallback.getLastTimestamp()),
mSurfaceTexture.getTimestamp() <=
simpleCaptureCallback.getLastTimestamp());
} finally {
mTestRule.closeDevice(id);
texturedSurface.release();
}
}
}
}
// Test for postview of still capture on all supported extensions
@Test
public void testPostviewAndCapture() throws Exception {
final int IMAGE_COUNT = 10;
final int SUPPORTED_CAPTURE_OUTPUT_FORMATS[] = {
ImageFormat.YUV_420_888,
ImageFormat.JPEG
};
for (String id : getCameraIdsUnderTest()) {
StaticMetadata staticMeta =
new StaticMetadata(mTestRule.getCameraManager().getCameraCharacteristics(id));
if (!staticMeta.isColorOutputSupported()) {
continue;
}
updatePreviewSurfaceTexture();
CameraExtensionCharacteristics extensionChars =
mTestRule.getCameraManager().getCameraExtensionCharacteristics(id);
List<Integer> supportedExtensions = extensionChars.getSupportedExtensions();
for (Integer extension : supportedExtensions) {
if (!extensionChars.isPostviewAvailable(extension)) {
continue;
}
for (int captureFormat : SUPPORTED_CAPTURE_OUTPUT_FORMATS) {
boolean captureProgressSupported =
extensionChars.isCaptureProcessProgressAvailable(extension);
List<Size> extensionSizes = extensionChars.getExtensionSupportedSizes(extension,
captureFormat);
if (extensionSizes.isEmpty()) {
continue;
}
Size maxSize = CameraTestUtils.getMaxSize(extensionSizes.toArray(new Size[0]));
List<Size> postviewSizes = extensionChars.getPostviewSupportedSizes(extension,
maxSize, captureFormat);
if (postviewSizes.isEmpty()) {
continue;
}
SimpleImageReaderListener imageListener = new SimpleImageReaderListener(false,
1);
ImageReader extensionImageReader = CameraTestUtils.makeImageReader(maxSize,
captureFormat, /*maxImages*/ 1, imageListener,
mTestRule.getHandler());
Surface imageReaderSurface = extensionImageReader.getSurface();
OutputConfiguration readerOutput = new OutputConfiguration(imageReaderSurface);
List<OutputConfiguration> outputConfigs = new ArrayList<>();
outputConfigs.add(readerOutput);
Size postviewSize =
CameraTestUtils.getMaxSize(postviewSizes.toArray(new Size[0]));
SimpleImageReaderListener imageListenerPostview =
new SimpleImageReaderListener(false, 1);
ImageReader postviewImageReader = CameraTestUtils.makeImageReader(postviewSize,
captureFormat, /*maxImages*/ 1, imageListenerPostview,
mTestRule.getHandler());
Surface postviewImageReaderSurface = postviewImageReader.getSurface();
OutputConfiguration postviewReaderOutput =
new OutputConfiguration(postviewImageReaderSurface);
BlockingExtensionSessionCallback sessionListener =
new BlockingExtensionSessionCallback(mock(
CameraExtensionSession.StateCallback.class));
ExtensionSessionConfiguration configuration =
new ExtensionSessionConfiguration(extension, outputConfigs,
new HandlerExecutor(mTestRule.getHandler()),
sessionListener);
configuration.setPostviewOutputConfiguration(postviewReaderOutput);
assertNotNull(configuration.getPostviewOutputConfiguration());
String streamName = "test_extension_postview_capture";
mReportLog = new DeviceReportLog(REPORT_LOG_NAME, streamName);
mReportLog.addValue("camera_id", id, ResultType.NEUTRAL, ResultUnit.NONE);
mReportLog.addValue("extension_id", extension, ResultType.NEUTRAL,
ResultUnit.NONE);
double[] captureTimes = new double[IMAGE_COUNT];
double[] postviewCaptureTimes = new double[IMAGE_COUNT];
boolean captureResultsSupported =
!extensionChars.getAvailableCaptureResultKeys(extension).isEmpty();
try {
mTestRule.openDevice(id);
CameraDevice camera = mTestRule.getCamera();
camera.createExtensionSession(configuration);
CameraExtensionSession extensionSession =
sessionListener.waitAndGetSession(
SESSION_CONFIGURE_TIMEOUT_MS);
assertNotNull(extensionSession);
CaptureRequest.Builder captureBuilder =
mTestRule.getCamera().createCaptureRequest(
CameraDevice.TEMPLATE_STILL_CAPTURE);
captureBuilder.addTarget(imageReaderSurface);
captureBuilder.addTarget(postviewImageReaderSurface);
CameraExtensionSession.ExtensionCaptureCallback captureMockCallback =
mock(CameraExtensionSession.ExtensionCaptureCallback.class);
SimpleCaptureCallback captureCallback =
new SimpleCaptureCallback(extension, captureMockCallback,
extensionChars.getAvailableCaptureResultKeys(extension),
mCollector);
for (int i = 0; i < IMAGE_COUNT; i++) {
int jpegOrientation = (i * 90) % 360; // degrees [0..270]
if (captureFormat == ImageFormat.JPEG) {
captureBuilder.set(CaptureRequest.JPEG_ORIENTATION,
jpegOrientation);
}
CaptureRequest request = captureBuilder.build();
long startTimeMs = SystemClock.elapsedRealtime();
captureCallback.resetCaptureProgress();
int sequenceId = extensionSession.capture(request,
new HandlerExecutor(mTestRule.getHandler()), captureCallback);
Image imgPostview =
imageListenerPostview
.getImage(MULTI_FRAME_CAPTURE_IMAGE_TIMEOUT_MS);
postviewCaptureTimes[i] = SystemClock.elapsedRealtime() - startTimeMs;
if (captureFormat == ImageFormat.JPEG) {
verifyJpegOrientation(imgPostview, postviewSize, jpegOrientation);
} else {
validateImage(imgPostview, postviewSize.getWidth(),
postviewSize.getHeight(), captureFormat, null);
}
Long imgTsPostview = imgPostview.getTimestamp();
imgPostview.close();
Image img =
imageListener.getImage(MULTI_FRAME_CAPTURE_IMAGE_TIMEOUT_MS);
captureTimes[i] = SystemClock.elapsedRealtime() - startTimeMs;
if (captureFormat == ImageFormat.JPEG) {
verifyJpegOrientation(img, maxSize, jpegOrientation);
} else {
validateImage(img, maxSize.getWidth(), maxSize.getHeight(),
captureFormat, null);
}
Long imgTs = img.getTimestamp();
img.close();
assertEquals("Still capture timestamp does not match its "
+ "postview timestamp", imgTsPostview, imgTs);
verify(captureMockCallback,
timeout(MULTI_FRAME_CAPTURE_IMAGE_TIMEOUT_MS).times(1))
.onCaptureStarted(eq(extensionSession), eq(request), eq(imgTs));
verify(captureMockCallback, times(1))
.onCaptureStarted(eq(extensionSession), eq(request), anyLong());
verify(captureMockCallback,
timeout(MULTI_FRAME_CAPTURE_IMAGE_TIMEOUT_MS).times(1))
.onCaptureProcessStarted(extensionSession, request);
verify(captureMockCallback,
timeout(MULTI_FRAME_CAPTURE_IMAGE_TIMEOUT_MS).times(1))
.onCaptureSequenceCompleted(extensionSession, sequenceId);
if (captureResultsSupported) {
verify(captureMockCallback,
timeout(MULTI_FRAME_CAPTURE_IMAGE_TIMEOUT_MS).times(1))
.onCaptureResultAvailable(eq(extensionSession), eq(request),
any(TotalCaptureResult.class));
}
if (captureProgressSupported) {
verify(captureMockCallback,
timeout(MULTI_FRAME_CAPTURE_IMAGE_TIMEOUT_MS).times(1))
.onCaptureProcessProgressed(eq(extensionSession),
eq(request), eq(100));
}
}
mReportLog.addValue("width", maxSize.getWidth(), ResultType.NEUTRAL,
ResultUnit.NONE);
mReportLog.addValue("height", maxSize.getHeight(),
ResultType.NEUTRAL, ResultUnit.NONE);
mReportLog.addValue("format", captureFormat, ResultType.NEUTRAL,
ResultUnit.NONE);
long avgPostviewLatency = (long) Stat.getAverage(postviewCaptureTimes);
mReportLog.addValue("avg_postview_latency", avgPostviewLatency,
ResultType.LOWER_BETTER, ResultUnit.MS);
long avgCaptureLatency = (long) Stat.getAverage(captureTimes);
mReportLog.addValue("avg_capture_latency", avgCaptureLatency,
ResultType.LOWER_BETTER, ResultUnit.MS);
verify(captureMockCallback, times(0))
.onCaptureSequenceAborted(any(CameraExtensionSession.class),
anyInt());
verify(captureMockCallback, times(0))
.onCaptureFailed(any(CameraExtensionSession.class),
any(CaptureRequest.class));
Range<Long> latencyRange =
extensionChars.getEstimatedCaptureLatencyRangeMillis(extension,
maxSize, captureFormat);
if (latencyRange != null) {
String msg = String.format("Camera [%s]: The measured average "
+ "capture latency of %d ms. for extension type %d "
+ "with image format: %d and size: %dx%d must be "
+ "within the advertised range of [%d, %d] ms.",
id, avgCaptureLatency, extension, captureFormat,
maxSize.getWidth(), maxSize.getHeight(),
latencyRange.getLower(), latencyRange.getUpper());
assertTrue(msg, latencyRange.contains(avgCaptureLatency));
}
extensionSession.close();
sessionListener.getStateWaiter().waitForState(
BlockingExtensionSessionCallback.SESSION_CLOSED,
SESSION_CLOSE_TIMEOUT_MS);
} finally {
mTestRule.closeDevice(id);
extensionImageReader.close();
mReportLog.submit(InstrumentationRegistry.getInstrumentation());
}
}
}
}
}
// Test case for multi-frame only capture on all supported extensions and expected state
// callbacks. Verify still frame output, measure the average capture latency and if possible
// ensure that the value is within the reported range.
@Test
public void testMultiFrameCapture() throws Exception {
final int IMAGE_COUNT = 10;
final int SUPPORTED_CAPTURE_OUTPUT_FORMATS[] = {
ImageFormat.YUV_420_888,
ImageFormat.JPEG
};
for (String id : getCameraIdsUnderTest()) {
StaticMetadata staticMeta =
new StaticMetadata(mTestRule.getCameraManager().getCameraCharacteristics(id));
if (!staticMeta.isColorOutputSupported()) {
continue;
}
updatePreviewSurfaceTexture();
CameraExtensionCharacteristics extensionChars =
mTestRule.getCameraManager().getCameraExtensionCharacteristics(id);
List<Integer> supportedExtensions = extensionChars.getSupportedExtensions();
for (Integer extension : supportedExtensions) {
boolean captureProgressSupported = extensionChars.isCaptureProcessProgressAvailable(
extension);
for (int captureFormat : SUPPORTED_CAPTURE_OUTPUT_FORMATS) {
List<Size> extensionSizes = extensionChars.getExtensionSupportedSizes(extension,
captureFormat);
if (extensionSizes.isEmpty()) {
continue;
}
Size maxSize = CameraTestUtils.getMaxSize(extensionSizes.toArray(new Size[0]));
SimpleImageReaderListener imageListener = new SimpleImageReaderListener(false,
1);
ImageReader extensionImageReader = CameraTestUtils.makeImageReader(maxSize,
captureFormat, /*maxImages*/ 1, imageListener,
mTestRule.getHandler());
Surface imageReaderSurface = extensionImageReader.getSurface();
OutputConfiguration readerOutput = new OutputConfiguration(imageReaderSurface);
List<OutputConfiguration> outputConfigs = new ArrayList<>();
outputConfigs.add(readerOutput);
BlockingExtensionSessionCallback sessionListener =
new BlockingExtensionSessionCallback(mock(
CameraExtensionSession.StateCallback.class));
ExtensionSessionConfiguration configuration =
new ExtensionSessionConfiguration(extension, outputConfigs,
new HandlerExecutor(mTestRule.getHandler()),
sessionListener);
String streamName = "test_extension_capture";
mReportLog = new DeviceReportLog(REPORT_LOG_NAME, streamName);
mReportLog.addValue("camera_id", id, ResultType.NEUTRAL, ResultUnit.NONE);
mReportLog.addValue("extension_id", extension, ResultType.NEUTRAL,
ResultUnit.NONE);
double[] captureTimes = new double[IMAGE_COUNT];
boolean captureResultsSupported =
!extensionChars.getAvailableCaptureResultKeys(extension).isEmpty();
try {
mTestRule.openDevice(id);
CameraDevice camera = mTestRule.getCamera();
camera.createExtensionSession(configuration);
CameraExtensionSession extensionSession =
sessionListener.waitAndGetSession(
SESSION_CONFIGURE_TIMEOUT_MS);
assertNotNull(extensionSession);
CaptureRequest.Builder captureBuilder =
mTestRule.getCamera().createCaptureRequest(
CameraDevice.TEMPLATE_STILL_CAPTURE);
captureBuilder.addTarget(imageReaderSurface);
CameraExtensionSession.ExtensionCaptureCallback captureMockCallback =
mock(CameraExtensionSession.ExtensionCaptureCallback.class);
SimpleCaptureCallback captureCallback =
new SimpleCaptureCallback(extension, captureMockCallback,
extensionChars.getAvailableCaptureResultKeys(extension),
mCollector);
for (int i = 0; i < IMAGE_COUNT; i++) {
int jpegOrientation = (i * 90) % 360; // degrees [0..270]
if (captureFormat == ImageFormat.JPEG) {
captureBuilder.set(CaptureRequest.JPEG_ORIENTATION,
jpegOrientation);
}
CaptureRequest request = captureBuilder.build();
long startTimeMs = SystemClock.elapsedRealtime();
captureCallback.resetCaptureProgress();
int sequenceId = extensionSession.capture(request,
new HandlerExecutor(mTestRule.getHandler()), captureCallback);
Image img =
imageListener.getImage(MULTI_FRAME_CAPTURE_IMAGE_TIMEOUT_MS);
captureTimes[i] = SystemClock.elapsedRealtime() - startTimeMs;
if (captureFormat == ImageFormat.JPEG) {
verifyJpegOrientation(img, maxSize, jpegOrientation);
} else {
validateImage(img, maxSize.getWidth(), maxSize.getHeight(),
captureFormat, null);
}
Long imgTs = img.getTimestamp();
img.close();
verify(captureMockCallback,
timeout(MULTI_FRAME_CAPTURE_IMAGE_TIMEOUT_MS).times(1))
.onCaptureStarted(eq(extensionSession), eq(request), eq(imgTs));
verify(captureMockCallback, times(1))
.onCaptureStarted(eq(extensionSession), eq(request), anyLong());
verify(captureMockCallback,
timeout(MULTI_FRAME_CAPTURE_IMAGE_TIMEOUT_MS).times(1))
.onCaptureProcessStarted(extensionSession, request);
verify(captureMockCallback,
timeout(MULTI_FRAME_CAPTURE_IMAGE_TIMEOUT_MS).times(1))
.onCaptureSequenceCompleted(extensionSession, sequenceId);
if (captureResultsSupported) {
verify(captureMockCallback,
timeout(MULTI_FRAME_CAPTURE_IMAGE_TIMEOUT_MS).times(1))
.onCaptureResultAvailable(eq(extensionSession), eq(request),
any(TotalCaptureResult.class));
}
if (captureProgressSupported) {
verify(captureMockCallback,
timeout(MULTI_FRAME_CAPTURE_IMAGE_TIMEOUT_MS).times(1))
.onCaptureProcessProgressed(eq(extensionSession),
eq(request), eq(100));
}
}
mReportLog.addValue("width", maxSize.getWidth(), ResultType.NEUTRAL,
ResultUnit.NONE);
mReportLog.addValue("height", maxSize.getHeight(),
ResultType.NEUTRAL, ResultUnit.NONE);
mReportLog.addValue("format", captureFormat, ResultType.NEUTRAL,
ResultUnit.NONE);
long avgCaptureLatency = (long) Stat.getAverage(captureTimes);
mReportLog.addValue("avg_latency", avgCaptureLatency,
ResultType.LOWER_BETTER, ResultUnit.MS);
verify(captureMockCallback, times(0))
.onCaptureSequenceAborted(any(CameraExtensionSession.class),
anyInt());
verify(captureMockCallback, times(0))
.onCaptureFailed(any(CameraExtensionSession.class),
any(CaptureRequest.class));
Range<Long> latencyRange =
extensionChars.getEstimatedCaptureLatencyRangeMillis(extension,
maxSize, captureFormat);
if (latencyRange != null) {
String msg = String.format("Camera [%s]: The measured average "
+ "capture latency of %d ms. for extension type %d "
+ "with image format: %d and size: %dx%d must be "
+ "within the advertised range of [%d, %d] ms.",
id, avgCaptureLatency, extension, captureFormat,
maxSize.getWidth(), maxSize.getHeight(),
latencyRange.getLower(), latencyRange.getUpper());
assertTrue(msg, latencyRange.contains(avgCaptureLatency));
}
extensionSession.close();
sessionListener.getStateWaiter().waitForState(
BlockingExtensionSessionCallback.SESSION_CLOSED,
SESSION_CLOSE_TIMEOUT_MS);
} finally {
mTestRule.closeDevice(id);
extensionImageReader.close();
mReportLog.submit(InstrumentationRegistry.getInstrumentation());
}
}
}
}
}
// Verify concurrent extension sessions behavior
@Test
public void testConcurrentSessions() throws Exception {
Set<Set<String>> concurrentCameraIdSet =
mTestRule.getCameraManager().getConcurrentCameraIds();
if (concurrentCameraIdSet.isEmpty()) {
return;
}
for (String id : getCameraIdsUnderTest()) {
StaticMetadata staticMeta =
new StaticMetadata(mTestRule.getCameraManager().getCameraCharacteristics(id));
if (!staticMeta.isColorOutputSupported()) {
continue;
}
CameraExtensionCharacteristics extensionChars =
mTestRule.getCameraManager().getCameraExtensionCharacteristics(id);
List<Integer> supportedExtensions = extensionChars.getSupportedExtensions();
if (supportedExtensions.isEmpty()) {
continue;
}
Set<String> concurrentCameraIds = null;
for (Set<String> entry : concurrentCameraIdSet) {
if (entry.contains(id)) {
concurrentCameraIds = entry;
break;
}
}
if (concurrentCameraIds == null) {
continue;
}
String concurrentCameraId = null;
CameraExtensionCharacteristics concurrentExtensionChars = null;
for (String entry : concurrentCameraIds) {
if (entry.equals(id)) {
continue;
}
if (!(new StaticMetadata(mTestRule.getCameraManager().getCameraCharacteristics(
entry))).isColorOutputSupported()) {
continue;
}
CameraExtensionCharacteristics chars =
mTestRule.getCameraManager().getCameraExtensionCharacteristics(entry);
if (chars.getSupportedExtensions().isEmpty()) {
continue;
}
concurrentCameraId = entry;
concurrentExtensionChars = chars;
break;
}
if ((concurrentCameraId == null) || (concurrentExtensionChars == null)) {
continue;
}
updatePreviewSurfaceTexture();
int extensionId = supportedExtensions.get(0);
List<Size> extensionSizes = extensionChars.getExtensionSupportedSizes(extensionId,
mSurfaceTexture.getClass());
Size maxSize = CameraTestUtils.getMaxSize(extensionSizes.toArray(new Size[0]));
mSurfaceTexture.setDefaultBufferSize(maxSize.getWidth(), maxSize.getHeight());
OutputConfiguration outputConfig = new OutputConfiguration(
new Surface(mSurfaceTexture));
List<OutputConfiguration> outputConfigs = new ArrayList<>();
outputConfigs.add(outputConfig);
BlockingExtensionSessionCallback sessionListener =
new BlockingExtensionSessionCallback(
mock(CameraExtensionSession.StateCallback.class));
ExtensionSessionConfiguration configuration =
new ExtensionSessionConfiguration(extensionId, outputConfigs,
new HandlerExecutor(mTestRule.getHandler()), sessionListener);
CameraDevice concurrentCameraDevice = null;
ImageReader extensionImageReader = null;
try {
mTestRule.openDevice(id);
concurrentCameraDevice = CameraTestUtils.openCamera(mTestRule.getCameraManager(),
concurrentCameraId, new BlockingStateCallback(), mTestRule.getHandler());
CameraDevice camera = mTestRule.getCamera();
camera.createExtensionSession(configuration);
CameraExtensionSession extensionSession = sessionListener.waitAndGetSession(
SESSION_CONFIGURE_TIMEOUT_MS);
assertNotNull(extensionSession);
assertNotNull(concurrentCameraDevice);
int concurrentExtensionId =
concurrentExtensionChars.getSupportedExtensions().get(0);
List<Size> captureSizes = concurrentExtensionChars.getExtensionSupportedSizes(
concurrentExtensionId, mSurfaceTexture.getClass());
assertFalse("No SurfaceTexture output supported", captureSizes.isEmpty());
Size captureMaxSize =
CameraTestUtils.getMaxSize(captureSizes.toArray(new Size[0]));
extensionImageReader = ImageReader.newInstance(
captureMaxSize.getWidth(), captureMaxSize.getHeight(), ImageFormat.PRIVATE,
/*maxImages*/ 1, HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE);
Surface imageReaderSurface = extensionImageReader.getSurface();
OutputConfiguration readerOutput = new OutputConfiguration(imageReaderSurface);
outputConfigs = new ArrayList<>();
outputConfigs.add(readerOutput);
CameraExtensionSession.StateCallback mockSessionListener =
mock(CameraExtensionSession.StateCallback.class);
ExtensionSessionConfiguration concurrentConfiguration =
new ExtensionSessionConfiguration(concurrentExtensionId, outputConfigs,
new HandlerExecutor(mTestRule.getHandler()),
mockSessionListener);
concurrentCameraDevice.createExtensionSession(concurrentConfiguration);
// Trying to initialize multiple concurrent extension sessions is expected to fail
verify(mockSessionListener, timeout(SESSION_CONFIGURE_TIMEOUT_MS).times(1))
.onConfigureFailed(any(CameraExtensionSession.class));
verify(mockSessionListener, times(0)).onConfigured(
any(CameraExtensionSession.class));
extensionSession.close();
sessionListener.getStateWaiter().waitForState(
BlockingExtensionSessionCallback.SESSION_CLOSED,
SESSION_CLOSE_TIMEOUT_MS);
// Initialization of another extension session must now be possible
BlockingExtensionSessionCallback concurrentSessionListener =
new BlockingExtensionSessionCallback(
mock(CameraExtensionSession.StateCallback.class));
concurrentConfiguration = new ExtensionSessionConfiguration(concurrentExtensionId,
outputConfigs, new HandlerExecutor(mTestRule.getHandler()),
concurrentSessionListener);
concurrentCameraDevice.createExtensionSession(concurrentConfiguration);
extensionSession = concurrentSessionListener.waitAndGetSession(
SESSION_CONFIGURE_TIMEOUT_MS);
assertNotNull(extensionSession);
extensionSession.close();
concurrentSessionListener.getStateWaiter().waitForState(
BlockingExtensionSessionCallback.SESSION_CLOSED,
SESSION_CLOSE_TIMEOUT_MS);
} finally {
mTestRule.closeDevice(id);
if (concurrentCameraDevice != null) {
concurrentCameraDevice.close();
}
if (extensionImageReader != null) {
extensionImageReader.close();
}
}
}
}
// Test case combined repeating with multi frame capture on all supported extensions.
// Verify still frame output.
@Test
public void testRepeatingAndCaptureCombined() throws Exception {
final double LATENCY_MARGIN = .1f; // Account for system load, capture call duration etc.
for (String id : getCameraIdsUnderTest()) {
StaticMetadata staticMeta =
new StaticMetadata(mTestRule.getCameraManager().getCameraCharacteristics(id));
if (!staticMeta.isColorOutputSupported()) {
continue;
}
updatePreviewSurfaceTexture();
CameraExtensionCharacteristics extensionChars =
mTestRule.getCameraManager().getCameraExtensionCharacteristics(id);
List<Integer> supportedExtensions = extensionChars.getSupportedExtensions();
for (Integer extension : supportedExtensions) {
Set<CaptureRequest.Key> supportedRequestKeys =
extensionChars.getAvailableCaptureRequestKeys(extension);
boolean supportsStrengthControl = supportedRequestKeys.contains(
CaptureRequest.EXTENSION_STRENGTH);
if (supportsStrengthControl) {
Set<CaptureResult.Key> supportedResultKeys =
extensionChars.getAvailableCaptureResultKeys(extension);
assertTrue(supportedResultKeys.contains(CaptureResult.EXTENSION_STRENGTH));
}
int captureFormat = ImageFormat.JPEG;
List<Size> captureSizes = extensionChars.getExtensionSupportedSizes(extension,
captureFormat);
assertFalse("No Jpeg output supported", captureSizes.isEmpty());
Size captureMaxSize =
CameraTestUtils.getMaxSize(captureSizes.toArray(new Size[0]));
SimpleImageReaderListener imageListener = new SimpleImageReaderListener(false
, 1);
ImageReader extensionImageReader = CameraTestUtils.makeImageReader(
captureMaxSize, captureFormat, /*maxImages*/ 1, imageListener,
mTestRule.getHandler());
Surface imageReaderSurface = extensionImageReader.getSurface();
OutputConfiguration readerOutput = new OutputConfiguration(imageReaderSurface);
List<OutputConfiguration> outputConfigs = new ArrayList<>();
outputConfigs.add(readerOutput);
// Pick a supported preview/repeating size with aspect ratio close to the
// multi-frame capture size
List<Size> repeatingSizes = extensionChars.getExtensionSupportedSizes(extension,
mSurfaceTexture.getClass());
Size maxRepeatingSize =
CameraTestUtils.getMaxSize(repeatingSizes.toArray(new Size[0]));
List<Size> previewSizes = getSupportedPreviewSizes(id,
mTestRule.getCameraManager(),
getPreviewSizeBound(mTestRule.getWindowManager(), PREVIEW_SIZE_BOUND));
List<Size> supportedPreviewSizes =
previewSizes.stream().filter(repeatingSizes::contains).collect(
Collectors.toList());
if (!supportedPreviewSizes.isEmpty()) {
float targetAr =
((float) captureMaxSize.getWidth()) / captureMaxSize.getHeight();
for (Size s : supportedPreviewSizes) {
float currentAr = ((float) s.getWidth()) / s.getHeight();
if (Math.abs(targetAr - currentAr) < 0.01) {
maxRepeatingSize = s;
break;
}
}
}
boolean captureResultsSupported =
!extensionChars.getAvailableCaptureResultKeys(extension).isEmpty();
mSurfaceTexture.setDefaultBufferSize(maxRepeatingSize.getWidth(),
maxRepeatingSize.getHeight());
Surface texturedSurface = new Surface(mSurfaceTexture);
outputConfigs.add(new OutputConfiguration(texturedSurface));
BlockingExtensionSessionCallback sessionListener =
new BlockingExtensionSessionCallback(mock(
CameraExtensionSession.StateCallback.class));
ExtensionSessionConfiguration configuration =
new ExtensionSessionConfiguration(extension, outputConfigs,
new HandlerExecutor(mTestRule.getHandler()),
sessionListener);
try {
mTestRule.openDevice(id);
CameraDevice camera = mTestRule.getCamera();
camera.createExtensionSession(configuration);
CameraExtensionSession extensionSession =
sessionListener.waitAndGetSession(
SESSION_CONFIGURE_TIMEOUT_MS);
assertNotNull(extensionSession);
CaptureRequest.Builder captureBuilder =
mTestRule.getCamera().createCaptureRequest(
android.hardware.camera2.CameraDevice.TEMPLATE_PREVIEW);
captureBuilder.addTarget(texturedSurface);
CameraExtensionSession.ExtensionCaptureCallback repeatingCallbackMock =
mock(CameraExtensionSession.ExtensionCaptureCallback.class);
SimpleCaptureCallback repeatingCaptureCallback =
new SimpleCaptureCallback(extension, repeatingCallbackMock,
extensionChars.getAvailableCaptureResultKeys(extension),
mCollector);
if (supportsStrengthControl) {
captureBuilder.set(CaptureRequest.EXTENSION_STRENGTH, 100);
}
CaptureRequest repeatingRequest = captureBuilder.build();
int repeatingSequenceId =
extensionSession.setRepeatingRequest(repeatingRequest,
new HandlerExecutor(mTestRule.getHandler()),
repeatingCaptureCallback);
Thread.sleep(REPEATING_REQUEST_TIMEOUT_MS);
verify(repeatingCallbackMock, atLeastOnce())
.onCaptureStarted(eq(extensionSession), eq(repeatingRequest),
anyLong());
verify(repeatingCallbackMock, atLeastOnce())
.onCaptureProcessStarted(extensionSession, repeatingRequest);
if (captureResultsSupported) {
verify(repeatingCallbackMock,
timeout(MULTI_FRAME_CAPTURE_IMAGE_TIMEOUT_MS).atLeastOnce())
.onCaptureResultAvailable(eq(extensionSession),
eq(repeatingRequest), any(TotalCaptureResult.class));
}
verify(repeatingCallbackMock, times(0)).onCaptureProcessProgressed(
any(CameraExtensionSession.class), any(CaptureRequest.class), anyInt());
captureBuilder = mTestRule.getCamera().createCaptureRequest(
android.hardware.camera2.CameraDevice.TEMPLATE_STILL_CAPTURE);
captureBuilder.addTarget(imageReaderSurface);
CameraExtensionSession.ExtensionCaptureCallback captureCallback =
mock(CameraExtensionSession.ExtensionCaptureCallback.class);
CameraExtensionSession.StillCaptureLatency stillCaptureLatency =
extensionSession.getRealtimeStillCaptureLatency();
CaptureRequest captureRequest = captureBuilder.build();
long startTimeMs = SystemClock.elapsedRealtime();
int captureSequenceId = extensionSession.capture(captureRequest,
new HandlerExecutor(mTestRule.getHandler()), captureCallback);
Image img =
imageListener.getImage(MULTI_FRAME_CAPTURE_IMAGE_TIMEOUT_MS);
long captureTime = SystemClock.elapsedRealtime() - startTimeMs;
validateImage(img, captureMaxSize.getWidth(),
captureMaxSize.getHeight(), captureFormat, null);
Long imgTs = img.getTimestamp();
img.close();
if (stillCaptureLatency != null) {
assertTrue("Still capture frame latency must be positive!",
stillCaptureLatency.getCaptureLatency() > 0);
assertTrue("Processing capture latency must be non-negative!",
stillCaptureLatency.getProcessingLatency() >= 0);
long estimatedTotalLatency = stillCaptureLatency.getCaptureLatency() +
stillCaptureLatency.getProcessingLatency();
long estimatedTotalLatencyMin =
(long) (estimatedTotalLatency * (1.f - LATENCY_MARGIN));
long estimatedTotalLatencyMax =
(long) (estimatedTotalLatency * (1.f + LATENCY_MARGIN));
assertTrue(String.format("Camera %s: Measured still capture latency " +
"doesn't match: %d ms, expected [%d,%d]ms.", id,
captureTime, estimatedTotalLatencyMin,
estimatedTotalLatencyMax),
(captureTime <= estimatedTotalLatencyMax) &&
(captureTime >= estimatedTotalLatencyMin));
}
verify(captureCallback, times(1))
.onCaptureStarted(eq(extensionSession), eq(captureRequest), eq(imgTs));
verify(captureCallback, timeout(MULTI_FRAME_CAPTURE_IMAGE_TIMEOUT_MS).times(1))
.onCaptureProcessStarted(extensionSession, captureRequest);
if (captureResultsSupported) {
verify(captureCallback,
timeout(MULTI_FRAME_CAPTURE_IMAGE_TIMEOUT_MS).times(1))
.onCaptureResultAvailable(eq(extensionSession),
eq(captureRequest), any(TotalCaptureResult.class));
}
verify(captureCallback, timeout(MULTI_FRAME_CAPTURE_IMAGE_TIMEOUT_MS).times(1))
.onCaptureSequenceCompleted(extensionSession,
captureSequenceId);
verify(captureCallback, times(0))
.onCaptureSequenceAborted(any(CameraExtensionSession.class),
anyInt());
verify(captureCallback, times(0))
.onCaptureFailed(any(CameraExtensionSession.class),
any(CaptureRequest.class));
extensionSession.stopRepeating();
verify(repeatingCallbackMock,
timeout(MULTI_FRAME_CAPTURE_IMAGE_TIMEOUT_MS).times(1))
.onCaptureSequenceCompleted(extensionSession, repeatingSequenceId);
verify(repeatingCallbackMock, times(0))
.onCaptureSequenceAborted(any(CameraExtensionSession.class),
anyInt());
extensionSession.close();
sessionListener.getStateWaiter().waitForState(
BlockingExtensionSessionCallback.SESSION_CLOSED,
SESSION_CLOSE_TIMEOUT_MS);
assertTrue("The sum of onCaptureProcessStarted and onCaptureFailed" +
" callbacks must be greater or equal than the number of calls" +
" to onCaptureStarted!",
repeatingCaptureCallback.getTotalFramesArrived() +
repeatingCaptureCallback.getTotalFramesFailed() >=
repeatingCaptureCallback.getTotalFramesStarted());
assertTrue(String.format("The last repeating request surface timestamp " +
"%d must be less than or equal to the last " +
"onCaptureStarted " +
"timestamp %d", mSurfaceTexture.getTimestamp(),
repeatingCaptureCallback.getLastTimestamp()),
mSurfaceTexture.getTimestamp() <=
repeatingCaptureCallback.getLastTimestamp());
} finally {
mTestRule.closeDevice(id);
texturedSurface.release();
extensionImageReader.close();
}
}
}
}
private void verifyJpegOrientation(Image img, Size jpegSize, int requestedOrientation)
throws IOException {
byte[] blobBuffer = getDataFromImage(img);
String blobFilename = mTestRule.getDebugFileNameBase() + "/verifyJpegKeys.jpeg";
dumpFile(blobFilename, blobBuffer);
ExifInterface exif = new ExifInterface(blobFilename);
int exifWidth = exif.getAttributeInt(ExifInterface.TAG_IMAGE_WIDTH, /*defaultValue*/0);
int exifHeight = exif.getAttributeInt(ExifInterface.TAG_IMAGE_LENGTH, /*defaultValue*/0);
Size exifSize = new Size(exifWidth, exifHeight);
int exifOrientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION,
/*defaultValue*/ ExifInterface.ORIENTATION_UNDEFINED);
final int ORIENTATION_MIN = ExifInterface.ORIENTATION_UNDEFINED;
final int ORIENTATION_MAX = ExifInterface.ORIENTATION_ROTATE_270;
assertTrue(String.format("Exif orientation must be in range of [%d, %d]",
ORIENTATION_MIN, ORIENTATION_MAX),
exifOrientation >= ORIENTATION_MIN && exifOrientation <= ORIENTATION_MAX);
/**
* Device captured image doesn't respect the requested orientation,
* which means it rotates the image buffer physically. Then we
* should swap the exif width/height accordingly to compare.
*/
boolean deviceRotatedImage = exifOrientation == ExifInterface.ORIENTATION_UNDEFINED;
if (deviceRotatedImage) {
// Case 1.
boolean needSwap = (requestedOrientation % 180 == 90);
if (needSwap) {
exifSize = new Size(exifHeight, exifWidth);
}
} else {
// Case 2.
assertEquals("Exif orientation should match requested orientation",
requestedOrientation, getExifOrientationInDegree(exifOrientation));
}
assertEquals("Exif size should match jpeg capture size", jpegSize, exifSize);
}
private static int getExifOrientationInDegree(int exifOrientation) {
switch (exifOrientation) {
case ExifInterface.ORIENTATION_NORMAL:
return 0;
case ExifInterface.ORIENTATION_ROTATE_90:
return 90;
case ExifInterface.ORIENTATION_ROTATE_180:
return 180;
case ExifInterface.ORIENTATION_ROTATE_270:
return 270;
default:
fail("It is impossible to get non 0, 90, 180, 270 degress exif" +
"info based on the request orientation range");
return -1;
}
}
public static class SimpleCaptureCallback
extends CameraExtensionSession.ExtensionCaptureCallback {
private long mLastTimestamp = -1;
private int mNumFramesArrived = 0;
private int mNumFramesStarted = 0;
private int mNumFramesFailed = 0;
private int mLastProgressValue = -1;
private boolean mNonIncreasingTimestamps = false;
private HashSet<Long> mExpectedResultTimestamps = new HashSet<>();
private final CameraExtensionSession.ExtensionCaptureCallback mProxy;
private final AutoFocusStateMachine mAFStateMachine;
private final FlashStateListener mFlashStateListener;
private final AutoExposureStateListener mAEStateListener;
private final HashSet<CaptureResult.Key> mSupportedResultKeys;
private final CameraErrorCollector mCollector;
private final boolean mPerFrameControl;
private final int mExtensionType;
public SimpleCaptureCallback(int extensionType,
CameraExtensionSession.ExtensionCaptureCallback proxy,
Set<CaptureResult.Key> supportedResultKeys, CameraErrorCollector errorCollector) {
this(extensionType, proxy, supportedResultKeys, errorCollector, null /*afListener*/,
null /*flashState*/, null /*aeState*/, false /*perFrameControl*/);
}
public SimpleCaptureCallback(int extensionType,
CameraExtensionSession.ExtensionCaptureCallback proxy,
Set<CaptureResult.Key> supportedResultKeys, CameraErrorCollector errorCollector,
AutoFocusStateMachine afState, FlashStateListener flashState,
AutoExposureStateListener aeState, boolean perFrameControl) {
mProxy = proxy;
mSupportedResultKeys = new HashSet<>(supportedResultKeys);
mCollector = errorCollector;
mAFStateMachine = afState;
mFlashStateListener = flashState;
mAEStateListener = aeState;
mPerFrameControl = perFrameControl;
mExtensionType = extensionType;
}
public void resetCaptureProgress() {
mLastProgressValue = -1;
}
@Override
public void onCaptureStarted(CameraExtensionSession session,
CaptureRequest request, long timestamp) {
mExpectedResultTimestamps.add(timestamp);
if (timestamp < mLastTimestamp) {
mNonIncreasingTimestamps = true;
}
mLastTimestamp = timestamp;
mNumFramesStarted++;
if (mProxy != null) {
mProxy.onCaptureStarted(session, request, timestamp);
}
}
@Override
public void onCaptureProcessStarted(CameraExtensionSession session,
CaptureRequest request) {
mNumFramesArrived++;
if (mProxy != null) {
mProxy.onCaptureProcessStarted(session, request);
}
}
@Override
public void onCaptureFailed(CameraExtensionSession session,
CaptureRequest request) {
mNumFramesFailed++;
if (mProxy != null) {
mProxy.onCaptureFailed(session, request);
}
}
@Override
public void onCaptureSequenceAborted(CameraExtensionSession session,
int sequenceId) {
if (mProxy != null) {
mProxy.onCaptureSequenceAborted(session, sequenceId);
}
}
@Override
public void onCaptureSequenceCompleted(CameraExtensionSession session,
int sequenceId) {
if (mProxy != null) {
mProxy.onCaptureSequenceCompleted(session, sequenceId);
}
}
@Override
public void onCaptureProcessProgressed(CameraExtensionSession session,
CaptureRequest request, int progress) {
if ((progress < 0) || (progress > 100)) {
mCollector.addMessage("Capture progress invalid value: " + progress);
return;
}
if (mLastProgressValue >= progress) {
mCollector.addMessage("Unexpected progress value: " + progress +
" last progress value: " + mLastProgressValue);
return;
}
mLastProgressValue = progress;
if (mProxy != null) {
mProxy.onCaptureProcessProgressed(session, request, progress);
}
}
@Override
public void onCaptureResultAvailable(CameraExtensionSession session,
CaptureRequest request, TotalCaptureResult result) {
final float METERING_REGION_ERROR_PERCENT_DELTA = 0.05f;
if (mSupportedResultKeys.isEmpty()) {
mCollector.addMessage("Capture results not supported, but " +
"onCaptureResultAvailable still got triggered!");
return;
}
List<CaptureResult.Key<?>> resultKeys = result.getKeys();
for (CaptureResult.Key<?> resultKey : resultKeys) {
mCollector.expectTrue("Capture result " + resultKey + " is not among the"
+ " supported result keys!", mSupportedResultKeys.contains(resultKey));
}
Long timeStamp = result.get(CaptureResult.SENSOR_TIMESTAMP);
assertNotNull(timeStamp);
assertTrue("Capture result sensor timestamp: " + timeStamp + " must match "
+ " with the timestamp passed to onCaptureStarted!",
mExpectedResultTimestamps.contains(timeStamp));
Integer currentType = result.get(CaptureResult.EXTENSION_CURRENT_TYPE);
if (currentType != null) {
mCollector.expectNotEquals("The reported extension type cannot be set to AUTO!",
CameraExtensionCharacteristics.EXTENSION_AUTOMATIC, currentType);
if (mExtensionType == CameraExtensionCharacteristics.EXTENSION_AUTOMATIC) {
Integer expectedValues[] = {
CameraExtensionCharacteristics.EXTENSION_BOKEH,
CameraExtensionCharacteristics.EXTENSION_HDR,
CameraExtensionCharacteristics.EXTENSION_NIGHT,
CameraExtensionCharacteristics.EXTENSION_FACE_RETOUCH};
mCollector.expectContains("Unexpected extension type result: "
+ currentType, expectedValues, currentType);
} else {
mCollector.expectEquals("Unexpected extension type result: " + currentType
+ " expected: " + mExtensionType, mExtensionType, currentType);
}
}
Integer strength = request.get(CaptureRequest.EXTENSION_STRENGTH);
if (strength != null) {
Integer resultStrength = result.get(CaptureResult.EXTENSION_STRENGTH);
mCollector.expectTrue("Request extension strength: " + strength +
" doesn't match with result: " + resultStrength,
strength.equals(resultStrength));
}
Integer jpegOrientation = request.get(CaptureRequest.JPEG_ORIENTATION);
if (jpegOrientation != null) {
Integer resultJpegOrientation = result.get(CaptureResult.JPEG_ORIENTATION);
mCollector.expectTrue("Request Jpeg orientation: " + jpegOrientation +
" doesn't match with result: " + resultJpegOrientation,
jpegOrientation.equals(resultJpegOrientation));
}
Byte jpegQuality = request.get(CaptureRequest.JPEG_QUALITY);
if (jpegQuality != null) {
Byte resultJpegQuality = result.get(CaptureResult.JPEG_QUALITY);
mCollector.expectTrue("Request Jpeg quality: " + jpegQuality +
" doesn't match with result: " + resultJpegQuality,
jpegQuality.equals(resultJpegQuality));
}
if (resultKeys.contains(CaptureResult.CONTROL_ZOOM_RATIO) && mPerFrameControl) {
Float zoomRatio = request.get(CaptureRequest.CONTROL_ZOOM_RATIO);
if (zoomRatio != null) {
Float resultZoomRatio = result.get(CaptureResult.CONTROL_ZOOM_RATIO);
mCollector.expectTrue(
String.format("Request and result zoom ratio should be similar " +
"(requested = %f, result = %f", zoomRatio, resultZoomRatio),
Math.abs(zoomRatio - resultZoomRatio) / zoomRatio <= ZOOM_ERROR_MARGIN);
}
}
if (mFlashStateListener != null) {
Integer flashMode = request.get(CaptureRequest.FLASH_MODE);
if ((flashMode != null) && mPerFrameControl) {
Integer resultFlashMode = result.get(CaptureResult.FLASH_MODE);
mCollector.expectTrue("Request flash mode: " + flashMode +
" doesn't match with result: " + resultFlashMode,
flashMode.equals(resultFlashMode));
}
Integer flashState = result.get(CaptureResult.FLASH_STATE);
if (flashState != null) {
switch (flashState) {
case CaptureResult.FLASH_STATE_UNAVAILABLE:
mFlashStateListener.onUnavailable();
break;
case CaptureResult.FLASH_STATE_FIRED:
mFlashStateListener.onFired();
break;
case CaptureResult.FLASH_STATE_CHARGING:
mFlashStateListener.onCharging();
break;
case CaptureResult.FLASH_STATE_PARTIAL:
mFlashStateListener.onPartial();
break;
case CaptureResult.FLASH_STATE_READY:
mFlashStateListener.onReady();
break;
default:
mCollector.addMessage("Unexpected flash state: " + flashState);
}
}
}
if (mAEStateListener != null) {
Integer aeMode = request.get(CaptureRequest.CONTROL_AE_MODE);
if ((aeMode != null) && mPerFrameControl) {
Integer resultAeMode = result.get(CaptureResult.CONTROL_AE_MODE);
mCollector.expectTrue("Request AE mode: " + aeMode +
" doesn't match with result: " + resultAeMode,
aeMode.equals(resultAeMode));
}
Boolean aeLock = request.get(CaptureRequest.CONTROL_AE_LOCK);
if ((aeLock != null) && mPerFrameControl) {
Boolean resultAeLock = result.get(CaptureResult.CONTROL_AE_LOCK);
mCollector.expectTrue("Request AE lock: " + aeLock +
" doesn't match with result: " + resultAeLock,
aeLock.equals(resultAeLock));
}
Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
if (aeState != null) {
switch (aeState) {
case CaptureResult.CONTROL_AE_STATE_CONVERGED:
mAEStateListener.onConverged();
break;
case CaptureResult.CONTROL_AE_STATE_FLASH_REQUIRED:
mAEStateListener.onFlashRequired();
break;
case CaptureResult.CONTROL_AE_STATE_INACTIVE:
mAEStateListener.onInactive();
break;
case CaptureResult.CONTROL_AE_STATE_LOCKED:
mAEStateListener.onLocked();
break;
case CaptureResult.CONTROL_AE_STATE_PRECAPTURE:
mAEStateListener.onPrecapture();
break;
case CaptureResult.CONTROL_AE_STATE_SEARCHING:
mAEStateListener.onSearching();
break;
default:
mCollector.addMessage("Unexpected AE state: " + aeState);
}
}
}
if (mAFStateMachine != null) {
Integer afMode = request.get(CaptureRequest.CONTROL_AF_MODE);
if ((afMode != null) && mPerFrameControl) {
Integer resultAfMode = result.get(CaptureResult.CONTROL_AF_MODE);
mCollector.expectTrue("Request AF mode: " + afMode +
" doesn't match with result: " + resultAfMode,
afMode.equals(resultAfMode));
}
MeteringRectangle[] afRegions = request.get(CaptureRequest.CONTROL_AF_REGIONS);
if ((afRegions != null) && mPerFrameControl) {
MeteringRectangle[] resultAfRegions = result.get(
CaptureResult.CONTROL_AF_REGIONS);
mCollector.expectMeteringRegionsAreSimilar(
"AF regions in result and request should be similar",
afRegions, resultAfRegions, METERING_REGION_ERROR_PERCENT_DELTA);
}
Integer afTrigger = request.get(CaptureRequest.CONTROL_AF_TRIGGER);
if ((afTrigger != null) && mPerFrameControl) {
Integer resultAfTrigger = result.get(CaptureResult.CONTROL_AF_TRIGGER);
mCollector.expectTrue("Request AF trigger: " + afTrigger +
" doesn't match with result: " + resultAfTrigger,
afTrigger.equals(resultAfTrigger));
}
mAFStateMachine.onCaptureCompleted(result);
}
if (mProxy != null) {
mProxy.onCaptureResultAvailable(session, request, result);
}
}
public int getTotalFramesArrived() {
return mNumFramesArrived;
}
public int getTotalFramesStarted() {
return mNumFramesStarted;
}
public int getTotalFramesFailed() {
return mNumFramesFailed;
}
public long getLastTimestamp() throws IllegalStateException {
if (mNonIncreasingTimestamps) {
throw new IllegalStateException("Non-monotonically increasing timestamps!");
}
return mLastTimestamp;
}
}
public interface AutoFocusStateListener {
void onDone(boolean success);
void onScan();
void onInactive();
}
private class TestAutoFocusProxy implements AutoFocusStateMachine.AutoFocusStateListener {
private final AutoFocusStateListener mListener;
TestAutoFocusProxy(AutoFocusStateListener listener) {
mListener = listener;
}
@Override
public void onAutoFocusSuccess(CaptureResult result, boolean locked) {
mListener.onDone(true);
}
@Override
public void onAutoFocusFail(CaptureResult result, boolean locked) {
mListener.onDone(false);
}
@Override
public void onAutoFocusScan(CaptureResult result) {
mListener.onScan();
}
@Override
public void onAutoFocusInactive(CaptureResult result) {
mListener.onInactive();
}
}
// Verify that camera extension sessions can support AF and AF metering controls. The test
// goal is to check that AF related controls and results are supported and can be used to
// lock the AF state and not to do an exhaustive check of the AF state transitions or manual AF.
@Test
public void testAFMetering() throws Exception {
final CaptureRequest.Key[] FOCUS_CAPTURE_REQUEST_SET = {CaptureRequest.CONTROL_AF_MODE,
CaptureRequest.CONTROL_AF_REGIONS, CaptureRequest.CONTROL_AF_TRIGGER};
final CaptureResult.Key[] FOCUS_CAPTURE_RESULT_SET = {CaptureResult.CONTROL_AF_MODE,
CaptureResult.CONTROL_AF_REGIONS, CaptureResult.CONTROL_AF_TRIGGER,
CaptureResult.CONTROL_AF_STATE};
final int METERING_REGION_SCALE_RATIO = 8;
final int WAIT_FOR_FOCUS_DONE_TIMEOUT_MS = 6000;
for (String id : getCameraIdsUnderTest()) {
StaticMetadata staticMeta =
new StaticMetadata(mTestRule.getCameraManager().getCameraCharacteristics(id));
if (!staticMeta.isColorOutputSupported()) {
continue;
}
if (!staticMeta.hasFocuser()) {
continue;
}
boolean perFrameControl = staticMeta.isPerFrameControlSupported();
final Rect activeArraySize = staticMeta.getActiveArraySizeChecked();
int regionWidth = activeArraySize.width() / METERING_REGION_SCALE_RATIO - 1;
int regionHeight = activeArraySize.height() / METERING_REGION_SCALE_RATIO - 1;
int centerX = activeArraySize.width() / 2;
int centerY = activeArraySize.height() / 2;
// Center region
MeteringRectangle[] afMeteringRects = {new MeteringRectangle(
centerX - regionWidth / 2, centerY - regionHeight / 2,
regionWidth, regionHeight,
MeteringRectangle.METERING_WEIGHT_MAX)};
updatePreviewSurfaceTexture();
CameraExtensionCharacteristics extensionChars =
mTestRule.getCameraManager().getCameraExtensionCharacteristics(id);
List<Integer> supportedExtensions = extensionChars.getSupportedExtensions();
for (Integer extension : supportedExtensions) {
Set<CaptureRequest.Key> supportedRequestKeys =
extensionChars.getAvailableCaptureRequestKeys(extension);
// The night extension is required to support AF controls starting with Android V
if ((Build.VERSION.DEVICE_INITIAL_SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM)
&& (extension == CameraExtensionCharacteristics.EXTENSION_NIGHT)) {
assertTrue(supportedRequestKeys.containsAll(
Arrays.asList(FOCUS_CAPTURE_REQUEST_SET)));
} else if (!supportedRequestKeys.containsAll(
Arrays.asList(FOCUS_CAPTURE_REQUEST_SET))) {
continue;
}
Set<CaptureResult.Key> supportedResultKeys =
extensionChars.getAvailableCaptureResultKeys(extension);
assertTrue(supportedResultKeys.containsAll(
Arrays.asList(FOCUS_CAPTURE_RESULT_SET)));
List<Size> extensionSizes = extensionChars.getExtensionSupportedSizes(extension,
mSurfaceTexture.getClass());
Size maxSize =
CameraTestUtils.getMaxSize(extensionSizes.toArray(new Size[0]));
mSurfaceTexture.setDefaultBufferSize(maxSize.getWidth(),
maxSize.getHeight());
Surface texturedSurface = new Surface(mSurfaceTexture);
List<OutputConfiguration> outputConfigs = new ArrayList<>();
outputConfigs.add(new OutputConfiguration(texturedSurface));
BlockingExtensionSessionCallback sessionListener =
new BlockingExtensionSessionCallback(mock(
CameraExtensionSession.StateCallback.class));
ExtensionSessionConfiguration configuration =
new ExtensionSessionConfiguration(extension, outputConfigs,
new HandlerExecutor(mTestRule.getHandler()),
sessionListener);
try {
mTestRule.openDevice(id);
CameraDevice camera = mTestRule.getCamera();
camera.createExtensionSession(configuration);
CameraExtensionSession extensionSession = sessionListener.waitAndGetSession(
SESSION_CONFIGURE_TIMEOUT_MS);
assertNotNull(extensionSession);
// Check passive AF
AutoFocusStateListener mockAFListener =
mock(AutoFocusStateListener.class);
AutoFocusStateMachine afState = new AutoFocusStateMachine(
new TestAutoFocusProxy(mockAFListener));
CameraExtensionSession.ExtensionCaptureCallback repeatingCallbackMock =
mock(CameraExtensionSession.ExtensionCaptureCallback.class);
SimpleCaptureCallback repeatingCaptureCallback =
new SimpleCaptureCallback(extension, repeatingCallbackMock,
extensionChars.getAvailableCaptureResultKeys(extension),
mCollector, afState, null /*flashState*/,
null /*aeState*/, perFrameControl);
CaptureRequest.Builder captureBuilder =
mTestRule.getCamera().createCaptureRequest(
android.hardware.camera2.CameraDevice.TEMPLATE_PREVIEW);
captureBuilder.addTarget(texturedSurface);
afState.setPassiveAutoFocus(true /*picture*/, captureBuilder);
captureBuilder.set(CaptureRequest.CONTROL_AF_REGIONS, afMeteringRects);
CaptureRequest request = captureBuilder.build();
int passiveSequenceId = extensionSession.setRepeatingRequest(request,
new HandlerExecutor(mTestRule.getHandler()), repeatingCaptureCallback);
assertTrue(passiveSequenceId > 0);
verify(repeatingCallbackMock,
timeout(REPEATING_REQUEST_TIMEOUT_MS).atLeastOnce())
.onCaptureResultAvailable(eq(extensionSession), eq(request),
any(TotalCaptureResult.class));
verify(mockAFListener,
timeout(WAIT_FOR_FOCUS_DONE_TIMEOUT_MS).atLeastOnce())
.onDone(anyBoolean());
// Check active AF
mockAFListener = mock(AutoFocusStateListener.class);
CameraExtensionSession.ExtensionCaptureCallback callbackMock = mock(
CameraExtensionSession.ExtensionCaptureCallback.class);
AutoFocusStateMachine activeAFState = new AutoFocusStateMachine(
new TestAutoFocusProxy(mockAFListener));
CameraExtensionSession.ExtensionCaptureCallback captureCallback =
new SimpleCaptureCallback(extension, callbackMock,
extensionChars.getAvailableCaptureResultKeys(extension),
mCollector, activeAFState, null /*flashState*/,
null /*aeState*/, perFrameControl);
CaptureRequest.Builder triggerBuilder =
mTestRule.getCamera().createCaptureRequest(
android.hardware.camera2.CameraDevice.TEMPLATE_PREVIEW);
triggerBuilder.addTarget(texturedSurface);
afState.setActiveAutoFocus(captureBuilder, triggerBuilder);
afState.unlockAutoFocus(captureBuilder, triggerBuilder);
triggerBuilder.set(CaptureRequest.CONTROL_AF_REGIONS, afMeteringRects);
request = captureBuilder.build();
int activeSequenceId = extensionSession.setRepeatingRequest(request,
new HandlerExecutor(mTestRule.getHandler()), captureCallback);
assertTrue((activeSequenceId > 0) &&
(activeSequenceId != passiveSequenceId));
verify(callbackMock,
timeout(REPEATING_REQUEST_TIMEOUT_MS).atLeastOnce())
.onCaptureResultAvailable(eq(extensionSession), eq(request),
any(TotalCaptureResult.class));
CaptureRequest triggerRequest = triggerBuilder.build();
reset(mockAFListener);
int triggerSequenceId = extensionSession.capture(triggerRequest,
new HandlerExecutor(mTestRule.getHandler()), captureCallback);
assertTrue((triggerSequenceId > 0) &&
(activeSequenceId != triggerSequenceId) &&
(triggerSequenceId != passiveSequenceId));
verify(callbackMock,
timeout(REPEATING_REQUEST_TIMEOUT_MS).atLeastOnce())
.onCaptureResultAvailable(eq(extensionSession), eq(triggerRequest),
any(TotalCaptureResult.class));
afState.lockAutoFocus(captureBuilder, triggerBuilder);
triggerRequest = triggerBuilder.build();
reset(mockAFListener);
extensionSession.capture(triggerRequest,
new HandlerExecutor(mTestRule.getHandler()), captureCallback);
verify(callbackMock,
timeout(REPEATING_REQUEST_TIMEOUT_MS).atLeastOnce())
.onCaptureResultAvailable(eq(extensionSession), eq(triggerRequest),
any(TotalCaptureResult.class));
verify(mockAFListener, timeout(WAIT_FOR_FOCUS_DONE_TIMEOUT_MS)
.atLeast(1)).onDone(anyBoolean());
extensionSession.stopRepeating();
extensionSession.close();
sessionListener.getStateWaiter().waitForState(
BlockingExtensionSessionCallback.SESSION_CLOSED,
SESSION_CLOSE_TIMEOUT_MS);
} finally {
mTestRule.closeDevice(id);
texturedSurface.release();
}
}
}
}
// Verify that camera extension sessions can support the zoom ratio control.
@Test
public void testZoomRatio() throws Exception {
final CaptureRequest.Key[] ZOOM_CAPTURE_REQUEST_SET = {CaptureRequest.CONTROL_ZOOM_RATIO};
final CaptureResult.Key[] ZOOM_CAPTURE_RESULT_SET = {CaptureResult.CONTROL_ZOOM_RATIO};
final int ZOOM_RATIO_STEPS = 10;
for (String id : getCameraIdsUnderTest()) {
StaticMetadata staticMeta =
new StaticMetadata(mTestRule.getCameraManager().getCameraCharacteristics(id));
if (!staticMeta.isColorOutputSupported()) {
continue;
}
Range<Float> zoomRatioRange = staticMeta.getZoomRatioRangeChecked();
if (zoomRatioRange.getUpper().equals(zoomRatioRange.getLower())) {
continue;
}
final float maxZoom = staticMeta.getAvailableMaxDigitalZoomChecked();
if (Math.abs(maxZoom - 1.0f) < ZOOM_ERROR_MARGIN) {
return;
}
Float zoomStep =
(zoomRatioRange.getUpper() - zoomRatioRange.getLower()) / ZOOM_RATIO_STEPS;
if (zoomStep < ZOOM_ERROR_MARGIN) {
continue;
}
ArrayList<Float> candidateZoomRatios = new ArrayList<>(ZOOM_RATIO_STEPS);
for (int step = 0; step < (ZOOM_RATIO_STEPS - 1); step++) {
candidateZoomRatios.add(step, zoomRatioRange.getLower() + step * zoomStep);
}
candidateZoomRatios.add(ZOOM_RATIO_STEPS - 1, zoomRatioRange.getUpper());
updatePreviewSurfaceTexture();
CameraExtensionCharacteristics extensionChars =
mTestRule.getCameraManager().getCameraExtensionCharacteristics(id);
List<Integer> supportedExtensions = extensionChars.getSupportedExtensions();
for (Integer extension : supportedExtensions) {
Set<CaptureRequest.Key> supportedRequestKeys =
extensionChars.getAvailableCaptureRequestKeys(extension);
// The Night extension is required to support zoom controls start with Android V
if ((Build.VERSION.DEVICE_INITIAL_SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM)
&& (extension == CameraExtensionCharacteristics.EXTENSION_NIGHT)) {
assertTrue(supportedRequestKeys.containsAll(
Arrays.asList(ZOOM_CAPTURE_REQUEST_SET)));
} else if (!supportedRequestKeys.containsAll(
Arrays.asList(ZOOM_CAPTURE_REQUEST_SET))) {
continue;
}
Set<CaptureResult.Key> supportedResultKeys =
extensionChars.getAvailableCaptureResultKeys(extension);
assertTrue(supportedResultKeys.containsAll(
Arrays.asList(ZOOM_CAPTURE_RESULT_SET)));
List<Size> extensionSizes = extensionChars.getExtensionSupportedSizes(extension,
mSurfaceTexture.getClass());
Size maxSize =
CameraTestUtils.getMaxSize(extensionSizes.toArray(new Size[0]));
mSurfaceTexture.setDefaultBufferSize(maxSize.getWidth(),
maxSize.getHeight());
Surface texturedSurface = new Surface(mSurfaceTexture);
List<OutputConfiguration> outputConfigs = new ArrayList<>();
outputConfigs.add(new OutputConfiguration(texturedSurface));
BlockingExtensionSessionCallback sessionListener =
new BlockingExtensionSessionCallback(mock(
CameraExtensionSession.StateCallback.class));
ExtensionSessionConfiguration configuration =
new ExtensionSessionConfiguration(extension, outputConfigs,
new HandlerExecutor(mTestRule.getHandler()),
sessionListener);
try {
mTestRule.openDevice(id);
CameraDevice camera = mTestRule.getCamera();
camera.createExtensionSession(configuration);
CameraExtensionSession extensionSession = sessionListener.waitAndGetSession(
SESSION_CONFIGURE_TIMEOUT_MS);
assertNotNull(extensionSession);
CameraExtensionSession.ExtensionCaptureCallback repeatingCallbackMock =
mock(CameraExtensionSession.ExtensionCaptureCallback.class);
SimpleCaptureCallback repeatingCaptureCallback =
new SimpleCaptureCallback(extension, repeatingCallbackMock,
extensionChars.getAvailableCaptureResultKeys(extension),
mCollector);
CaptureRequest.Builder captureBuilder =
mTestRule.getCamera().createCaptureRequest(
android.hardware.camera2.CameraDevice.TEMPLATE_PREVIEW);
captureBuilder.addTarget(texturedSurface);
for (Float currentZoomRatio : candidateZoomRatios) {
captureBuilder.set(CaptureRequest.CONTROL_ZOOM_RATIO, currentZoomRatio);
CaptureRequest request = captureBuilder.build();
int seqId = extensionSession.setRepeatingRequest(request,
new HandlerExecutor(mTestRule.getHandler()),
repeatingCaptureCallback);
assertTrue(seqId > 0);
verify(repeatingCallbackMock,
timeout(REPEATING_REQUEST_TIMEOUT_MS).atLeastOnce())
.onCaptureResultAvailable(eq(extensionSession), eq(request),
any(TotalCaptureResult.class));
}
extensionSession.stopRepeating();
extensionSession.close();
sessionListener.getStateWaiter().waitForState(
BlockingExtensionSessionCallback.SESSION_CLOSED,
SESSION_CLOSE_TIMEOUT_MS);
} finally {
mTestRule.closeDevice(id);
texturedSurface.release();
}
}
}
}
public interface FlashStateListener {
public void onFired();
public void onReady();
public void onCharging();
public void onPartial();
public void onUnavailable();
}
public interface AutoExposureStateListener {
public void onInactive();
public void onSearching();
public void onConverged();
public void onLocked();
public void onFlashRequired();
public void onPrecapture();
}
// Verify that camera extension sessions can support Flash related controls. The test
// goal is to check that Flash controls and results are supported and can be used to
// turn on torch, run the pre-capture sequence and active the main flash.
@Test
public void testFlash() throws Exception {
final CaptureRequest.Key[] FLASH_CAPTURE_REQUEST_SET = {CaptureRequest.CONTROL_AE_MODE,
CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, CaptureRequest.CONTROL_AE_LOCK,
CaptureRequest.FLASH_MODE};
final CaptureResult.Key[] FLASH_CAPTURE_RESULT_SET = {CaptureResult.CONTROL_AE_MODE,
CaptureResult.CONTROL_AE_PRECAPTURE_TRIGGER, CaptureResult.CONTROL_AE_LOCK,
CaptureResult.CONTROL_AE_STATE, CaptureResult.FLASH_MODE,
CaptureResult.FLASH_STATE};
for (String id : getCameraIdsUnderTest()) {
StaticMetadata staticMeta =
new StaticMetadata(mTestRule.getCameraManager().getCameraCharacteristics(id));
if (!staticMeta.isColorOutputSupported()) {
continue;
}
if (!staticMeta.hasFlash()) {
continue;
}
boolean perFrameControl = staticMeta.isPerFrameControlSupported();
updatePreviewSurfaceTexture();
CameraExtensionCharacteristics extensionChars =
mTestRule.getCameraManager().getCameraExtensionCharacteristics(id);
List<Integer> supportedExtensions = extensionChars.getSupportedExtensions();
for (Integer extension : supportedExtensions) {
Set<CaptureRequest.Key> supportedRequestKeys =
extensionChars.getAvailableCaptureRequestKeys(extension);
if (!supportedRequestKeys.containsAll(Arrays.asList(FLASH_CAPTURE_REQUEST_SET))) {
continue;
}
Set<CaptureResult.Key> supportedResultKeys =
extensionChars.getAvailableCaptureResultKeys(extension);
assertTrue(supportedResultKeys.containsAll(
Arrays.asList(FLASH_CAPTURE_RESULT_SET)));
int captureFormat = ImageFormat.JPEG;
List<Size> captureSizes = extensionChars.getExtensionSupportedSizes(extension,
captureFormat);
assertFalse("No Jpeg output supported", captureSizes.isEmpty());
Size captureMaxSize = captureSizes.get(0);
SimpleImageReaderListener imageListener = new SimpleImageReaderListener(false, 1);
ImageReader extensionImageReader = CameraTestUtils.makeImageReader(
captureMaxSize, captureFormat, /*maxImages*/ 1, imageListener,
mTestRule.getHandler());
Surface imageReaderSurface = extensionImageReader.getSurface();
OutputConfiguration readerOutput = new OutputConfiguration(imageReaderSurface);
List<OutputConfiguration> outputConfigs = new ArrayList<>();
outputConfigs.add(readerOutput);
List<Size> repeatingSizes = extensionChars.getExtensionSupportedSizes(extension,
mSurfaceTexture.getClass());
Size previewSize = repeatingSizes.get(0);
mSurfaceTexture.setDefaultBufferSize(previewSize.getWidth(),
previewSize.getHeight());
Surface texturedSurface = new Surface(mSurfaceTexture);
outputConfigs.add(new OutputConfiguration(texturedSurface));
BlockingExtensionSessionCallback sessionListener =
new BlockingExtensionSessionCallback(mock(
CameraExtensionSession.StateCallback.class));
ExtensionSessionConfiguration configuration =
new ExtensionSessionConfiguration(extension, outputConfigs,
new HandlerExecutor(mTestRule.getHandler()),
sessionListener);
try {
mTestRule.openDevice(id);
CameraDevice camera = mTestRule.getCamera();
camera.createExtensionSession(configuration);
CameraExtensionSession extensionSession =
sessionListener.waitAndGetSession(
SESSION_CONFIGURE_TIMEOUT_MS);
assertNotNull(extensionSession);
// Test torch on and off
CaptureRequest.Builder captureBuilder =
mTestRule.getCamera().createCaptureRequest(
android.hardware.camera2.CameraDevice.TEMPLATE_PREVIEW);
captureBuilder.addTarget(texturedSurface);
captureBuilder.set(CaptureRequest.CONTROL_AE_MODE,
CameraMetadata.CONTROL_AE_MODE_ON);
captureBuilder.set(CaptureRequest.CONTROL_AE_LOCK, false);
captureBuilder.set(CaptureRequest.FLASH_MODE, CameraMetadata.FLASH_MODE_TORCH);
FlashStateListener mockFlashListener = mock(FlashStateListener.class);
AutoExposureStateListener mockAEStateListener =
mock(AutoExposureStateListener.class);
CameraExtensionSession.ExtensionCaptureCallback repeatingCallbackMock =
mock(CameraExtensionSession.ExtensionCaptureCallback.class);
SimpleCaptureCallback repeatingCaptureCallback =
new SimpleCaptureCallback(extension, repeatingCallbackMock,
extensionChars.getAvailableCaptureResultKeys(extension),
mCollector, null /*afState*/, mockFlashListener,
mockAEStateListener, perFrameControl);
CaptureRequest repeatingRequest = captureBuilder.build();
int repeatingSequenceId =
extensionSession.setRepeatingRequest(repeatingRequest,
new HandlerExecutor(mTestRule.getHandler()),
repeatingCaptureCallback);
assertTrue(repeatingSequenceId > 0);
verify(repeatingCallbackMock,
timeout(REPEATING_REQUEST_TIMEOUT_MS).atLeastOnce())
.onCaptureResultAvailable(eq(extensionSession),
eq(repeatingRequest), any(TotalCaptureResult.class));
verify(mockFlashListener,
timeout(REPEATING_REQUEST_TIMEOUT_MS).atLeastOnce()).onFired();
captureBuilder.set(CaptureRequest.CONTROL_AE_MODE,
CameraMetadata.CONTROL_AE_MODE_ON_ALWAYS_FLASH);
captureBuilder.set(CaptureRequest.FLASH_MODE, CameraMetadata.FLASH_MODE_OFF);
repeatingRequest = captureBuilder.build();
reset(mockFlashListener);
repeatingSequenceId = extensionSession.setRepeatingRequest(repeatingRequest,
new HandlerExecutor(mTestRule.getHandler()),
repeatingCaptureCallback);
assertTrue(repeatingSequenceId > 0);
verify(repeatingCallbackMock,
timeout(REPEATING_REQUEST_TIMEOUT_MS).atLeastOnce())
.onCaptureResultAvailable(eq(extensionSession),
eq(repeatingRequest), any(TotalCaptureResult.class));
verify(mockFlashListener,
timeout(REPEATING_REQUEST_TIMEOUT_MS).atLeastOnce()).onReady();
// Test AE pre-capture sequence
captureBuilder.set(CaptureRequest.FLASH_MODE, CameraMetadata.FLASH_MODE_OFF);
captureBuilder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
CameraMetadata.CONTROL_AE_PRECAPTURE_TRIGGER_START);
CaptureRequest triggerRequest = captureBuilder.build();
int triggerSeqId = extensionSession.capture(triggerRequest,
new HandlerExecutor(mTestRule.getHandler()), repeatingCaptureCallback);
assertTrue(triggerSeqId > 0);
verify(repeatingCallbackMock,
timeout(REPEATING_REQUEST_TIMEOUT_MS).atLeastOnce())
.onCaptureResultAvailable(eq(extensionSession),
eq(triggerRequest), any(TotalCaptureResult.class));
verify(mockAEStateListener,
timeout(REPEATING_REQUEST_TIMEOUT_MS).atLeastOnce()).onPrecapture();
verify(mockAEStateListener,
timeout(REPEATING_REQUEST_TIMEOUT_MS).atLeastOnce()).onConverged();
// Test main flash
captureBuilder = mTestRule.getCamera().createCaptureRequest(
android.hardware.camera2.CameraDevice.TEMPLATE_STILL_CAPTURE);
captureBuilder.addTarget(imageReaderSurface);
captureBuilder.set(CaptureRequest.CONTROL_AE_MODE,
CameraMetadata.CONTROL_AE_MODE_ON_ALWAYS_FLASH);
captureBuilder.set(CaptureRequest.CONTROL_AE_LOCK, true);
captureBuilder.set(CaptureRequest.FLASH_MODE, CameraMetadata.FLASH_MODE_SINGLE);
CameraExtensionSession.ExtensionCaptureCallback mockCaptureCallback =
mock(CameraExtensionSession.ExtensionCaptureCallback.class);
reset(mockFlashListener);
SimpleCaptureCallback captureCallback =
new SimpleCaptureCallback(extension, mockCaptureCallback,
extensionChars.getAvailableCaptureResultKeys(extension),
mCollector, null /*afState*/, mockFlashListener,
mockAEStateListener, perFrameControl);
CaptureRequest captureRequest = captureBuilder.build();
int captureSequenceId = extensionSession.capture(captureRequest,
new HandlerExecutor(mTestRule.getHandler()), captureCallback);
assertTrue(captureSequenceId > 0);
Image img =
imageListener.getImage(MULTI_FRAME_CAPTURE_IMAGE_TIMEOUT_MS);
validateImage(img, captureMaxSize.getWidth(),
captureMaxSize.getHeight(), captureFormat, null);
long imgTs = img.getTimestamp();
img.close();
verify(mockCaptureCallback,
timeout(MULTI_FRAME_CAPTURE_IMAGE_TIMEOUT_MS).times(1))
.onCaptureStarted(eq(extensionSession), eq(captureRequest), eq(imgTs));
verify(mockCaptureCallback,
timeout(MULTI_FRAME_CAPTURE_IMAGE_TIMEOUT_MS).times(1))
.onCaptureResultAvailable(eq(extensionSession),
eq(captureRequest), any(TotalCaptureResult.class));
verify(mockFlashListener,
timeout(REPEATING_REQUEST_TIMEOUT_MS).atLeastOnce()).onFired();
extensionSession.stopRepeating();
extensionSession.close();
sessionListener.getStateWaiter().waitForState(
BlockingExtensionSessionCallback.SESSION_CLOSED,
SESSION_CLOSE_TIMEOUT_MS);
} finally {
mTestRule.closeDevice(id);
texturedSurface.release();
extensionImageReader.close();
}
}
}
}
// Verify 'CameraExtensionSession.StillCaptureLatency' behavior
@Test
public void testSessionStillCaptureLatency() throws Exception {
final long CAPTURE_LATENCY_MS = 100;
final long PROCESSING_LATENCY_MS = 200;
final long DIFFERENT_PROCESSING_LATENCY_MS = 201;
CameraExtensionSession.StillCaptureLatency stillCaptureLatency =
new CameraExtensionSession.StillCaptureLatency(CAPTURE_LATENCY_MS,
PROCESSING_LATENCY_MS);
assertEquals(stillCaptureLatency.getCaptureLatency(), CAPTURE_LATENCY_MS);
assertEquals(stillCaptureLatency.getProcessingLatency(), PROCESSING_LATENCY_MS);
assertNotNull(stillCaptureLatency.toString());
CameraExtensionSession.StillCaptureLatency differentStillCaptureLatency =
new CameraExtensionSession.StillCaptureLatency(CAPTURE_LATENCY_MS,
DIFFERENT_PROCESSING_LATENCY_MS);
assertFalse(stillCaptureLatency.equals(differentStillCaptureLatency));
assertFalse(stillCaptureLatency.hashCode() ==
differentStillCaptureLatency.hashCode());
}
@Test
public void testIllegalArguments() throws Exception {
for (String id : getCameraIdsUnderTest()) {
StaticMetadata staticMeta =
new StaticMetadata(mTestRule.getCameraManager().getCameraCharacteristics(id));
if (!staticMeta.isColorOutputSupported()) {
continue;
}
updatePreviewSurfaceTexture();
CameraExtensionCharacteristics extensionChars =
mTestRule.getCameraManager().getCameraExtensionCharacteristics(id);
List<Integer> supportedExtensions = extensionChars.getSupportedExtensions();
for (Integer extension : supportedExtensions) {
List<OutputConfiguration> outputConfigs = new ArrayList<>();
BlockingExtensionSessionCallback sessionListener =
new BlockingExtensionSessionCallback(mock(
CameraExtensionSession.StateCallback.class));
ExtensionSessionConfiguration configuration =
new ExtensionSessionConfiguration(extension, outputConfigs,
new HandlerExecutor(mTestRule.getHandler()),
sessionListener);
try {
mTestRule.openDevice(id);
CameraDevice camera = mTestRule.getCamera();
try {
camera.createExtensionSession(configuration);
fail("should get IllegalArgumentException due to absent output surfaces");
} catch (IllegalArgumentException e) {
// Expected, we can proceed further
}
int captureFormat = ImageFormat.YUV_420_888;
List<Size> captureSizes = extensionChars.getExtensionSupportedSizes(extension,
captureFormat);
if (captureSizes.isEmpty()) {
captureFormat = ImageFormat.JPEG;
captureSizes = extensionChars.getExtensionSupportedSizes(extension,
captureFormat);
}
Size captureMaxSize =
CameraTestUtils.getMaxSize(captureSizes.toArray(new Size[0]));
mSurfaceTexture.setDefaultBufferSize(1, 1);
Surface texturedSurface = new Surface(mSurfaceTexture);
outputConfigs.add(new OutputConfiguration(texturedSurface));
configuration = new ExtensionSessionConfiguration(extension, outputConfigs,
new HandlerExecutor(mTestRule.getHandler()), sessionListener);
try {
camera.createExtensionSession(configuration);
fail("should get IllegalArgumentException due to illegal repeating request"
+ " output surface");
} catch (IllegalArgumentException e) {
// Expected, we can proceed further
} finally {
outputConfigs.clear();
}
SimpleImageReaderListener imageListener = new SimpleImageReaderListener(false,
1);
Size invalidCaptureSize = new Size(1, 1);
ImageReader extensionImageReader = CameraTestUtils.makeImageReader(
invalidCaptureSize, captureFormat, /*maxImages*/ 1,
imageListener, mTestRule.getHandler());
Surface imageReaderSurface = extensionImageReader.getSurface();
OutputConfiguration readerOutput = new OutputConfiguration(imageReaderSurface);
outputConfigs.add(readerOutput);
configuration = new ExtensionSessionConfiguration(extension, outputConfigs,
new HandlerExecutor(mTestRule.getHandler()), sessionListener);
try{
camera.createExtensionSession(configuration);
fail("should get IllegalArgumentException due to illegal multi-frame"
+ " request output surface");
} catch (IllegalArgumentException e) {
// Expected, we can proceed further
} finally {
outputConfigs.clear();
extensionImageReader.close();
}
// Pick a supported preview/repeating size with aspect ratio close to the
// multi-frame capture size
List<Size> repeatingSizes = extensionChars.getExtensionSupportedSizes(extension,
mSurfaceTexture.getClass());
Size maxRepeatingSize =
CameraTestUtils.getMaxSize(repeatingSizes.toArray(new Size[0]));
List<Size> previewSizes = getSupportedPreviewSizes(id,
mTestRule.getCameraManager(),
getPreviewSizeBound(mTestRule.getWindowManager(), PREVIEW_SIZE_BOUND));
List<Size> supportedPreviewSizes =
previewSizes.stream().filter(repeatingSizes::contains).collect(
Collectors.toList());
if (!supportedPreviewSizes.isEmpty()) {
float targetAr =
((float) captureMaxSize.getWidth()) / captureMaxSize.getHeight();
for (Size s : supportedPreviewSizes) {
float currentAr = ((float) s.getWidth()) / s.getHeight();
if (Math.abs(targetAr - currentAr) < 0.01) {
maxRepeatingSize = s;
break;
}
}
}
imageListener = new SimpleImageReaderListener(false, 1);
extensionImageReader = CameraTestUtils.makeImageReader(captureMaxSize,
captureFormat, /*maxImages*/ 1, imageListener, mTestRule.getHandler());
imageReaderSurface = extensionImageReader.getSurface();
readerOutput = new OutputConfiguration(imageReaderSurface);
outputConfigs.add(readerOutput);
mSurfaceTexture.setDefaultBufferSize(maxRepeatingSize.getWidth(),
maxRepeatingSize.getHeight());
texturedSurface = new Surface(mSurfaceTexture);
outputConfigs.add(new OutputConfiguration(texturedSurface));
configuration = new ExtensionSessionConfiguration(extension, outputConfigs,
new HandlerExecutor(mTestRule.getHandler()), sessionListener);
camera.createExtensionSession(configuration);
CameraExtensionSession extensionSession =
sessionListener.waitAndGetSession(
SESSION_CONFIGURE_TIMEOUT_MS);
assertNotNull(extensionSession);
CaptureRequest.Builder captureBuilder =
mTestRule.getCamera().createCaptureRequest(
android.hardware.camera2.CameraDevice.TEMPLATE_PREVIEW);
captureBuilder.addTarget(imageReaderSurface);
CameraExtensionSession.ExtensionCaptureCallback repeatingCallbackMock =
mock(CameraExtensionSession.ExtensionCaptureCallback.class);
SimpleCaptureCallback repeatingCaptureCallback =
new SimpleCaptureCallback(extension, repeatingCallbackMock,
extensionChars.getAvailableCaptureResultKeys(extension),
mCollector);
CaptureRequest repeatingRequest = captureBuilder.build();
try {
extensionSession.setRepeatingRequest(repeatingRequest,
new HandlerExecutor(mTestRule.getHandler()),
repeatingCaptureCallback);
fail("should get IllegalArgumentException due to illegal repeating request"
+ " output target");
} catch (IllegalArgumentException e) {
// Expected, we can proceed further
}
extensionSession.close();
sessionListener.getStateWaiter().waitForState(
BlockingExtensionSessionCallback.SESSION_CLOSED,
SESSION_CLOSE_TIMEOUT_MS);
texturedSurface.release();
extensionImageReader.close();
captureBuilder = mTestRule.getCamera().createCaptureRequest(
CameraDevice.TEMPLATE_PREVIEW);
captureBuilder.addTarget(texturedSurface);
CameraExtensionSession.ExtensionCaptureCallback captureCallback =
mock(CameraExtensionSession.ExtensionCaptureCallback.class);
CaptureRequest captureRequest = captureBuilder.build();
try {
extensionSession.setRepeatingRequest(captureRequest,
new HandlerExecutor(mTestRule.getHandler()), captureCallback);
fail("should get IllegalStateException due to closed session");
} catch (IllegalStateException e) {
// Expected, we can proceed further
}
try {
extensionSession.stopRepeating();
fail("should get IllegalStateException due to closed session");
} catch (IllegalStateException e) {
// Expected, we can proceed further
}
try {
extensionSession.capture(captureRequest,
new HandlerExecutor(mTestRule.getHandler()), captureCallback);
fail("should get IllegalStateException due to closed session");
} catch (IllegalStateException e) {
// Expected, we can proceed further
}
} finally {
mTestRule.closeDevice(id);
}
}
}
}
}