blob: 740ae6b337fc02d37b55f6551a01b5f38addf8bb [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.ImageDropperListener;
import static android.hardware.camera2.cts.CameraTestUtils.MAX_READER_IMAGES;
import static android.hardware.camera2.cts.CameraTestUtils.SimpleCaptureCallback;
import static android.hardware.camera2.cts.CameraTestUtils.assertEquals;
import static android.hardware.camera2.cts.CameraTestUtils.assertNotNull;
import static android.hardware.camera2.cts.CameraTestUtils.assertTrue;
import static android.hardware.camera2.cts.CameraTestUtils.fail;
import static android.hardware.camera2.cts.CameraTestUtils.makeImageReader;
import static android.hardware.cts.helpers.CameraUtils.getAvailableSurfaceTexture;
import static org.junit.Assert.assertArrayEquals;
import android.graphics.Bitmap;
import android.graphics.ImageFormat;
import android.graphics.SurfaceTexture;
import android.hardware.HardwareBuffer;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CaptureFailure;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.cts.CameraTestUtils.ImageVerifierListener;
import android.hardware.camera2.cts.helpers.StaticMetadata;
import android.hardware.camera2.cts.rs.BitmapUtils;
import android.hardware.camera2.cts.testcases.Camera2MultiViewTestCase;
import android.hardware.camera2.params.OutputConfiguration;
import android.media.Image;
import android.media.ImageReader;
import android.media.ImageWriter;
import android.opengl.Matrix;
import android.os.ConditionVariable;
import android.os.SystemClock;
import android.util.Log;
import android.util.Size;
import android.view.Surface;
import android.view.TextureView;
import com.android.ex.camera2.blocking.BlockingCameraManager.BlockingOpenException;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
/**
* CameraDevice test by using combination of SurfaceView, TextureView and ImageReader
*/
@RunWith(Parameterized.class)
public class MultiViewTest extends Camera2MultiViewTestCase {
private static final String TAG = "MultiViewTest";
private final static long WAIT_FOR_COMMAND_TO_COMPLETE = 5000; //ms
private final static long PREVIEW_TIME_MS = 2000;
private final static long PREVIEW_FLUSH_TIME_MS = 1000;
private final static int NUM_SURFACE_SWITCHES = 30;
private final static int IMG_READER_COUNT = 2;
private final static int YUV_IMG_READER_COUNT = 3;
private final static double BITMAP_DIFF_THRESHOLD = 0.1;
@Test
public void testTextureViewPreview() throws Exception {
for (String cameraId : getCameraIdsUnderTest()) {
Exception prior = null;
try {
openCamera(cameraId);
if (!getStaticInfo(cameraId).isColorOutputSupported()) {
Log.i(TAG, "Camera " + cameraId + " does not support color outputs, skipping");
continue;
}
List<TextureView> views = Arrays.asList(mTextureView[0]);
textureViewPreview(cameraId, views, /*ImageReader*/null);
} catch (Exception e) {
prior = e;
} finally {
try {
closeCamera(cameraId);
} catch (Exception e) {
if (prior != null) {
Log.e(TAG, "Prior exception received: " + prior);
}
prior = e;
}
if (prior != null) throw prior; // Rethrow last exception.
}
}
}
@Test
public void testTextureViewPreviewWithImageReader() throws Exception {
for (String cameraId : getCameraIdsUnderTest()) {
Exception prior = null;
ImageVerifierListener yuvListener;
ImageReader yuvReader = null;
try {
openCamera(cameraId);
if (!getStaticInfo(cameraId).isColorOutputSupported()) {
Log.i(TAG, "Camera " + cameraId + " does not support color outputs, skipping");
continue;
}
Size previewSize = getOrderedPreviewSizes(cameraId).get(0);
yuvListener =
new ImageVerifierListener(previewSize, ImageFormat.YUV_420_888);
yuvReader = makeImageReader(previewSize,
ImageFormat.YUV_420_888, MAX_READER_IMAGES, yuvListener, mHandler);
int maxNumStreamsProc =
getStaticInfo(cameraId).getMaxNumOutputStreamsProcessedChecked();
if (maxNumStreamsProc < 2) {
continue;
}
List<TextureView> views = Arrays.asList(mTextureView[0]);
textureViewPreview(cameraId, views, yuvReader);
} catch (Exception e) {
prior = e;
} finally {
try {
// Close camera device first. This will give some more time for
// ImageVerifierListener to finish the validation before yuvReader is closed
// (all image will be closed after that)
closeCamera(cameraId);
if (yuvReader != null) {
yuvReader.close();
}
} catch (Exception e) {
if (prior != null) {
Log.e(TAG, "Prior exception received: " + prior);
}
prior = e;
}
if (prior != null) throw prior; // Rethrow last exception.
}
}
}
@Test
public void testDualTextureViewPreview() throws Exception {
for (String cameraId : getCameraIdsUnderTest()) {
Exception prior = null;
try {
openCamera(cameraId);
if (!getStaticInfo(cameraId).isColorOutputSupported()) {
Log.i(TAG, "Camera " + cameraId + " does not support color outputs, skipping");
continue;
}
int maxNumStreamsProc =
getStaticInfo(cameraId).getMaxNumOutputStreamsProcessedChecked();
if (maxNumStreamsProc < 2) {
continue;
}
List<TextureView> views = Arrays.asList(mTextureView[0], mTextureView[1]);
textureViewPreview(cameraId, views, /*ImageReader*/null);
} catch (Exception e) {
prior = e;
} finally {
try {
closeCamera(cameraId);
} catch (Exception e) {
if (prior != null) {
Log.e(TAG, "Prior exception received: " + prior);
}
prior = e;
}
if (prior != null) throw prior; // Rethrow last exception.
}
}
}
@Test
public void testDualTextureViewAndImageReaderPreview() throws Exception {
for (String cameraId : getCameraIdsUnderTest()) {
Exception prior = null;
ImageVerifierListener yuvListener = null;
ImageReader yuvReader = null;
try {
openCamera(cameraId);
if (!getStaticInfo(cameraId).isColorOutputSupported()) {
Log.i(TAG, "Camera " + cameraId + " does not support color outputs, skipping");
continue;
}
Size previewSize = getOrderedPreviewSizes(cameraId).get(0);
yuvListener =
new ImageVerifierListener(previewSize, ImageFormat.YUV_420_888);
yuvReader = makeImageReader(previewSize,
ImageFormat.YUV_420_888, MAX_READER_IMAGES, yuvListener, mHandler);
int maxNumStreamsProc =
getStaticInfo(cameraId).getMaxNumOutputStreamsProcessedChecked();
if (maxNumStreamsProc < 3) {
continue;
}
List<TextureView> views = Arrays.asList(mTextureView[0], mTextureView[1]);
textureViewPreview(cameraId, views, yuvReader);
} catch (Exception e) {
prior = e;
} finally {
try {
if (yuvListener != null) {
yuvListener.onReaderDestroyed();
}
if (yuvReader != null) {
yuvReader.close();
}
closeCamera(cameraId);
} catch (Exception e) {
if (prior != null) {
Log.e(TAG, "Prior exception received: " + prior);
}
prior = e;
}
if (prior != null) throw prior; // Rethrow last exception.
}
}
}
@Test
public void testDualCameraPreview() throws Exception {
final int NUM_CAMERAS_TESTED = 2;
String[] cameraIdsUnderTest = getCameraIdsUnderTest();
if (cameraIdsUnderTest.length < NUM_CAMERAS_TESTED) {
return;
}
try {
for (int i = 0; i < NUM_CAMERAS_TESTED; i++) {
openCamera(cameraIdsUnderTest[i]);
if (!getStaticInfo(cameraIdsUnderTest[i]).isColorOutputSupported()) {
Log.i(TAG, "Camera " + cameraIdsUnderTest[i] +
" does not support color outputs, skipping");
continue;
}
List<TextureView> views = Arrays.asList(mTextureView[i]);
startTextureViewPreview(cameraIdsUnderTest[i], views, /*ImageReader*/null);
}
// TODO: check the framerate is correct
SystemClock.sleep(PREVIEW_TIME_MS);
for (int i = 0; i < NUM_CAMERAS_TESTED; i++) {
if (!getStaticInfo(cameraIdsUnderTest[i]).isColorOutputSupported()) {
Log.i(TAG, "Camera " + cameraIdsUnderTest[i] +
" does not support color outputs, skipping");
continue;
}
stopPreview(cameraIdsUnderTest[i]);
}
} catch (BlockingOpenException e) {
// The only error accepted is ERROR_MAX_CAMERAS_IN_USE, which means HAL doesn't support
// concurrent camera streaming
assertEquals("Camera device open failed",
CameraDevice.StateCallback.ERROR_MAX_CAMERAS_IN_USE, e.getCode());
Log.i(TAG, "Camera HAL does not support dual camera preview. Skip the test");
} finally {
for (int i = 0; i < NUM_CAMERAS_TESTED; i++) {
closeCamera(cameraIdsUnderTest[i]);
}
}
}
/*
* Verify dynamic shared surface behavior.
*/
@Test
public void testSharedSurfaceBasic() throws Exception {
for (String cameraId : getCameraIdsUnderTest()) {
try {
openCamera(cameraId);
if (getStaticInfo(cameraId).isHardwareLevelLegacy()) {
Log.i(TAG, "Camera " + cameraId + " is legacy, skipping");
continue;
}
if (!getStaticInfo(cameraId).isColorOutputSupported()) {
Log.i(TAG, "Camera " + cameraId +
" does not support color outputs, skipping");
continue;
}
testSharedSurfaceBasicByCamera(cameraId);
}
finally {
closeCamera(cameraId);
}
}
}
private void testSharedSurfaceBasicByCamera(String cameraId) throws Exception {
Size previewSize = getOrderedPreviewSizes(cameraId).get(0);
CameraPreviewListener[] previewListener = new CameraPreviewListener[2];
SurfaceTexture[] previewTexture = new SurfaceTexture[2];
Surface[] surfaces = new Surface[2];
OutputConfiguration[] outputConfigs = new OutputConfiguration[2];
List<OutputConfiguration> outputConfigurations = new ArrayList<>();
// Create surface textures with the same size
for (int i = 0; i < 2; i++) {
previewListener[i] = new CameraPreviewListener();
mTextureView[i].setSurfaceTextureListener(previewListener[i]);
previewTexture[i] = getAvailableSurfaceTexture(
WAIT_FOR_COMMAND_TO_COMPLETE, mTextureView[i]);
assertNotNull("Unable to get preview surface texture", previewTexture[i]);
previewTexture[i].setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight());
// Correct the preview display rotation.
updatePreviewDisplayRotation(previewSize, mTextureView[i]);
surfaces[i] = new Surface(previewTexture[i]);
outputConfigs[i] = new OutputConfiguration(surfaces[i]);
outputConfigs[i].enableSurfaceSharing();
outputConfigurations.add(outputConfigs[i]);
}
startPreviewWithConfigs(cameraId, outputConfigurations, null);
boolean previewDone =
previewListener[0].waitForPreviewDone(WAIT_FOR_COMMAND_TO_COMPLETE);
assertTrue("Unable to start preview", previewDone);
//try to dynamically add and remove any of the initial configured outputs
try {
outputConfigs[1].addSurface(surfaces[0]);
updateOutputConfiguration(cameraId, outputConfigs[1]);
fail("should get IllegalArgumentException due to invalid output");
} catch (IllegalArgumentException e) {
// expected exception
outputConfigs[1].removeSurface(surfaces[0]);
}
try {
outputConfigs[1].removeSurface(surfaces[1]);
fail("should get IllegalArgumentException due to invalid output");
} catch (IllegalArgumentException e) {
// expected exception
}
try {
outputConfigs[0].addSurface(surfaces[1]);
updateOutputConfiguration(cameraId, outputConfigs[0]);
fail("should get IllegalArgumentException due to invalid output");
} catch (IllegalArgumentException e) {
// expected exception
outputConfigs[0].removeSurface(surfaces[1]);
}
try {
outputConfigs[0].removeSurface(surfaces[0]);
fail("should get IllegalArgumentException due to invalid output");
} catch (IllegalArgumentException e) {
// expected exception
}
//Check that we are able to add a shared texture surface with different size
List<Size> orderedPreviewSizes = getOrderedPreviewSizes(cameraId);
Size textureSize = previewSize;
for (Size s : orderedPreviewSizes) {
if (!s.equals(previewSize)) {
textureSize = s;
break;
}
}
if (textureSize.equals(previewSize)) {
return;
}
SurfaceTexture outputTexture = new SurfaceTexture(/* random texture ID*/ 5);
outputTexture.setDefaultBufferSize(textureSize.getWidth(), textureSize.getHeight());
Surface outputSurface = new Surface(outputTexture);
//Add a valid output surface and then verify that it cannot be added any more
outputConfigs[1].addSurface(outputSurface);
updateOutputConfiguration(cameraId, outputConfigs[1]);
try {
outputConfigs[1].addSurface(outputSurface);
fail("should get IllegalStateException due to duplicate output");
} catch (IllegalStateException e) {
// expected exception
}
outputConfigs[0].addSurface(outputSurface);
try {
updateOutputConfiguration(cameraId, outputConfigs[0]);
fail("should get IllegalArgumentException due to duplicate output");
} catch (IllegalArgumentException e) {
// expected exception
outputConfigs[0].removeSurface(outputSurface);
}
//Verify that the same surface cannot be removed twice
outputConfigs[1].removeSurface(outputSurface);
updateOutputConfiguration(cameraId, outputConfigs[1]);
try {
outputConfigs[0].removeSurface(outputSurface);
fail("should get IllegalArgumentException due to invalid output");
} catch (IllegalArgumentException e) {
// expected exception
}
try {
outputConfigs[1].removeSurface(outputSurface);
fail("should get IllegalArgumentException due to invalid output");
} catch (IllegalArgumentException e) {
// expected exception
}
stopPreview(cameraId);
}
/*
* Verify dynamic shared surface behavior using multiple ImageReaders.
*/
@Test
public void testSharedSurfaceImageReaderSwitch() throws Exception {
for (String cameraId : getCameraIdsUnderTest()) {
try {
openCamera(cameraId);
if (getStaticInfo(cameraId).isHardwareLevelLegacy()) {
Log.i(TAG, "Camera " + cameraId + " is legacy, skipping");
continue;
}
if (!getStaticInfo(cameraId).isColorOutputSupported()) {
Log.i(TAG, "Camera " + cameraId +
" does not support color outputs, skipping");
continue;
}
testSharedSurfaceImageReaderSwitch(cameraId, NUM_SURFACE_SWITCHES);
}
finally {
closeCamera(cameraId);
}
}
}
private void testSharedSurfaceImageReaderSwitch(String cameraId, int switchCount)
throws Exception {
SimpleImageListener imageListeners[] = new SimpleImageListener[IMG_READER_COUNT];
SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
ImageReader imageReaders[] = new ImageReader[IMG_READER_COUNT];
Surface readerSurfaces[] = new Surface[IMG_READER_COUNT];
Size previewSize = getOrderedPreviewSizes(cameraId).get(0);
CameraPreviewListener previewListener = new CameraPreviewListener();
mTextureView[0].setSurfaceTextureListener(previewListener);
SurfaceTexture previewTexture = getAvailableSurfaceTexture(WAIT_FOR_COMMAND_TO_COMPLETE,
mTextureView[0]);
assertNotNull("Unable to get preview surface texture", previewTexture);
previewTexture.setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight());
updatePreviewDisplayRotation(previewSize, mTextureView[0]);
Surface previewSurface = new Surface(previewTexture);
OutputConfiguration outputConfig = new OutputConfiguration(previewSurface);
outputConfig.enableSurfaceSharing();
List<OutputConfiguration> outputConfigurations = new ArrayList<>();
outputConfigurations.add(outputConfig);
if (outputConfig.getMaxSharedSurfaceCount() < (IMG_READER_COUNT + 1)) {
return;
}
//Start regular preview streaming
startPreviewWithConfigs(cameraId, outputConfigurations, null);
boolean previewDone = previewListener.waitForPreviewDone(WAIT_FOR_COMMAND_TO_COMPLETE);
assertTrue("Unable to start preview", previewDone);
//Test shared image reader outputs
for (int i = 0; i < IMG_READER_COUNT; i++) {
imageListeners[i] = new SimpleImageListener();
imageReaders[i] = ImageReader.newInstance(previewSize.getWidth(),
previewSize.getHeight(), ImageFormat.PRIVATE, 2);
imageReaders[i].setOnImageAvailableListener(imageListeners[i], mHandler);
readerSurfaces[i] = imageReaders[i].getSurface();
}
for (int j = 0; j < switchCount; j++) {
for (int i = 0; i < IMG_READER_COUNT; i++) {
outputConfig.addSurface(readerSurfaces[i]);
updateOutputConfiguration(cameraId, outputConfig);
CaptureRequest.Builder imageReaderRequestBuilder = getCaptureBuilder(cameraId,
CameraDevice.TEMPLATE_PREVIEW);
imageReaderRequestBuilder.addTarget(readerSurfaces[i]);
capture(cameraId, imageReaderRequestBuilder.build(), resultListener);
imageListeners[i].waitForAnyImageAvailable(PREVIEW_TIME_MS);
Image img = imageReaders[i].acquireLatestImage();
assertNotNull("Invalid image acquired!", img);
img.close();
outputConfig.removeSurface(readerSurfaces[i]);
updateOutputConfiguration(cameraId, outputConfig);
}
}
for (int i = 0; i < IMG_READER_COUNT; i++) {
imageReaders[i].close();
}
stopPreview(cameraId);
}
/*
* Verify dynamic shared surface behavior using YUV ImageReaders.
*/
@Test
public void testSharedSurfaceYUVImageReaderSwitch() throws Exception {
int YUVFormats[] = {ImageFormat.YUV_420_888, ImageFormat.YUV_422_888,
ImageFormat.YUV_444_888, ImageFormat.YUY2, ImageFormat.YV12,
ImageFormat.NV16, ImageFormat.NV21};
for (String cameraId : getCameraIdsUnderTest()) {
try {
openCamera(cameraId);
if (getStaticInfo(cameraId).isHardwareLevelLegacy()) {
Log.i(TAG, "Camera " + cameraId + " is legacy, skipping");
continue;
}
if (!getStaticInfo(cameraId).isColorOutputSupported()) {
Log.i(TAG, "Camera " + cameraId +
" does not support color outputs, skipping");
continue;
}
Size frameSize = null;
int yuvFormat = -1;
for (int it : YUVFormats) {
Size yuvSizes[] = getStaticInfo(cameraId).getAvailableSizesForFormatChecked(
it, StaticMetadata.StreamDirection.Output);
if (yuvSizes != null) {
frameSize = yuvSizes[0];
yuvFormat = it;
break;
}
}
if ((yuvFormat != -1) && (frameSize.getWidth() > 0) &&
(frameSize.getHeight() > 0)) {
testSharedSurfaceYUVImageReaderSwitch(cameraId, NUM_SURFACE_SWITCHES, yuvFormat,
frameSize, /*blockMaxAcquired*/ false);
testSharedSurfaceYUVImageReaderSwitch(cameraId, NUM_SURFACE_SWITCHES, yuvFormat,
frameSize, /*blockMaxAcquired*/ true);
} else {
Log.i(TAG, "Camera " + cameraId +
" does not support YUV outputs, skipping");
}
}
finally {
closeCamera(cameraId);
}
}
}
private void testSharedSurfaceYUVImageReaderSwitch(String cameraId, int switchCount, int format,
Size frameSize, boolean blockMaxAcquired) throws Exception {
assertTrue("YUV_IMG_READER_COUNT should be equal or greater than 2",
(YUV_IMG_READER_COUNT >= 2));
SimpleImageListener imageListeners[] = new SimpleImageListener[YUV_IMG_READER_COUNT];
SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
ImageReader imageReaders[] = new ImageReader[YUV_IMG_READER_COUNT];
Surface readerSurfaces[] = new Surface[YUV_IMG_READER_COUNT];
for (int i = 0; i < YUV_IMG_READER_COUNT; i++) {
imageListeners[i] = new SimpleImageListener();
imageReaders[i] = ImageReader.newInstance(frameSize.getWidth(), frameSize.getHeight(),
format, 2);
imageReaders[i].setOnImageAvailableListener(imageListeners[i], mHandler);
readerSurfaces[i] = imageReaders[i].getSurface();
}
OutputConfiguration outputConfig = new OutputConfiguration(readerSurfaces[0]);
outputConfig.enableSurfaceSharing();
List<OutputConfiguration> outputConfigurations = new ArrayList<>();
outputConfigurations.add(outputConfig);
if (outputConfig.getMaxSharedSurfaceCount() < YUV_IMG_READER_COUNT) {
return;
}
createSessionWithConfigs(cameraId, outputConfigurations);
// Test YUV ImageReader surface sharing. The first ImageReader will
// always be part of the capture request, the rest will switch on each
// iteration.
// If 'blockMaxAcquired' is enabled, the first image reader will acquire
// the maximum possible amount of buffers and also block a few more.
int maxAcquiredImages = imageReaders[0].getMaxImages();
int acquiredCount = 0;
Image[] acquiredImages = new Image[maxAcquiredImages];
for (int j = 0; j < switchCount; j++) {
for (int i = 1; i < YUV_IMG_READER_COUNT; i++) {
outputConfig.addSurface(readerSurfaces[i]);
updateOutputConfiguration(cameraId, outputConfig);
CaptureRequest.Builder imageReaderRequestBuilder = getCaptureBuilder(cameraId,
CameraDevice.TEMPLATE_PREVIEW);
imageReaderRequestBuilder.addTarget(readerSurfaces[i]);
if (blockMaxAcquired) {
if (acquiredCount <= (maxAcquiredImages + 1)) {
// Camera should be able to handle cases where
// one output blocks more buffers than the respective
// maximum acquired count.
imageReaderRequestBuilder.addTarget(readerSurfaces[0]);
}
} else {
imageReaderRequestBuilder.addTarget(readerSurfaces[0]);
}
capture(cameraId, imageReaderRequestBuilder.build(), resultListener);
imageListeners[i].waitForAnyImageAvailable(PREVIEW_TIME_MS);
Image img = imageReaders[i].acquireLatestImage();
assertNotNull("Invalid image acquired!", img);
assertNotNull("Image planes are invalid!", img.getPlanes());
img.close();
if (blockMaxAcquired) {
if (acquiredCount < maxAcquiredImages) {
imageListeners[0].waitForAnyImageAvailable(PREVIEW_TIME_MS);
acquiredImages[acquiredCount] = imageReaders[0].acquireNextImage();
}
acquiredCount++;
} else {
imageListeners[0].waitForAnyImageAvailable(PREVIEW_TIME_MS);
img = imageReaders[0].acquireLatestImage();
assertNotNull("Invalid image acquired!", img);
img.close();
}
outputConfig.removeSurface(readerSurfaces[i]);
updateOutputConfiguration(cameraId, outputConfig);
}
}
for (int i = 0; i < YUV_IMG_READER_COUNT; i++) {
imageReaders[i].close();
}
}
/*
* Test the dynamic shared surface limit.
*/
@Test
public void testSharedSurfaceLimit() throws Exception {
for (String cameraId : getCameraIdsUnderTest()) {
try {
openCamera(cameraId);
if (getStaticInfo(cameraId).isHardwareLevelLegacy()) {
Log.i(TAG, "Camera " + cameraId + " is legacy, skipping");
continue;
}
if (!getStaticInfo(cameraId).isColorOutputSupported()) {
Log.i(TAG, "Camera " + cameraId +
" does not support color outputs, skipping");
continue;
}
testSharedSurfaceLimitByCamera(cameraId,
Camera2MultiViewCtsActivity.MAX_TEXTURE_VIEWS);
}
finally {
closeCamera(cameraId);
}
}
}
private void testSharedSurfaceLimitByCamera(String cameraId, int surfaceLimit)
throws Exception {
Size previewSize = getOrderedPreviewSizes(cameraId).get(0);
CameraPreviewListener[] previewListener = new CameraPreviewListener[surfaceLimit];
SurfaceTexture[] previewTexture = new SurfaceTexture[surfaceLimit];
Surface[] surfaces = new Surface[surfaceLimit];
int sequenceId = -1;
// Create surface textures with the same size
for (int i = 0; i < surfaceLimit; i++) {
previewListener[i] = new CameraPreviewListener();
mTextureView[i].setSurfaceTextureListener(previewListener[i]);
previewTexture[i] = getAvailableSurfaceTexture(
WAIT_FOR_COMMAND_TO_COMPLETE, mTextureView[i]);
assertNotNull("Unable to get preview surface texture", previewTexture[i]);
previewTexture[i].setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight());
// Correct the preview display rotation.
updatePreviewDisplayRotation(previewSize, mTextureView[i]);
surfaces[i] = new Surface(previewTexture[i]);
}
SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
// Create shared outputs for the two surface textures
OutputConfiguration surfaceSharedOutput = new OutputConfiguration(surfaces[0]);
surfaceSharedOutput.enableSurfaceSharing();
if ((surfaceLimit <= 1) ||
(surfaceLimit < surfaceSharedOutput.getMaxSharedSurfaceCount())) {
return;
}
List<OutputConfiguration> outputConfigurations = new ArrayList<>();
outputConfigurations.add(surfaceSharedOutput);
startPreviewWithConfigs(cameraId, outputConfigurations, null);
boolean previewDone =
previewListener[0].waitForPreviewDone(WAIT_FOR_COMMAND_TO_COMPLETE);
assertTrue("Unable to start preview", previewDone);
mTextureView[0].setSurfaceTextureListener(null);
SystemClock.sleep(PREVIEW_TIME_MS);
int i = 1;
for (; i < surfaceLimit; i++) {
//Add one more output surface while preview is streaming
if (i >= surfaceSharedOutput.getMaxSharedSurfaceCount()){
try {
surfaceSharedOutput.addSurface(surfaces[i]);
fail("should get IllegalArgumentException due to output surface limit");
} catch (IllegalArgumentException e) {
//expected
break;
}
} else {
surfaceSharedOutput.addSurface(surfaces[i]);
assertTrue("Session configuration should not fail",
isSessionConfigurationSupported(cameraId, outputConfigurations));
updateOutputConfiguration(cameraId, surfaceSharedOutput);
sequenceId = updateRepeatingRequest(cameraId, outputConfigurations, resultListener);
SystemClock.sleep(PREVIEW_TIME_MS);
}
}
for (; i > 0; i--) {
if (i >= surfaceSharedOutput.getMaxSharedSurfaceCount()) {
try {
surfaceSharedOutput.removeSurface(surfaces[i]);
fail("should get IllegalArgumentException due to output surface limit");
} catch (IllegalArgumentException e) {
// expected exception
}
} else {
surfaceSharedOutput.removeSurface(surfaces[i]);
assertTrue("Session configuration should not fail",
isSessionConfigurationSupported(cameraId, outputConfigurations));
}
}
//Remove all previously added shared outputs in one call
updateRepeatingRequest(cameraId, outputConfigurations, new SimpleCaptureCallback());
long lastSequenceFrameNumber = resultListener.getCaptureSequenceLastFrameNumber(
sequenceId, PREVIEW_TIME_MS);
checkForLastFrameInSequence(lastSequenceFrameNumber, resultListener);
updateOutputConfiguration(cameraId, surfaceSharedOutput);
SystemClock.sleep(PREVIEW_TIME_MS);
stopPreview(cameraId);
}
/*
* Test dynamic shared surface switch behavior.
*/
@Test(timeout=60*60*1000) // timeout = 60 mins for long running tests
public void testSharedSurfaceSwitch() throws Exception {
for (String cameraId : getCameraIdsUnderTest()) {
try {
openCamera(cameraId);
if (getStaticInfo(cameraId).isHardwareLevelLegacy()) {
Log.i(TAG, "Camera " + cameraId + " is legacy, skipping");
continue;
}
if (!getStaticInfo(cameraId).isColorOutputSupported()) {
Log.i(TAG, "Camera " + cameraId +
" does not support color outputs, skipping");
continue;
}
testSharedSurfaceSwitchByCamera(cameraId, NUM_SURFACE_SWITCHES);
}
finally {
closeCamera(cameraId);
}
}
}
private void testSharedSurfaceSwitchByCamera(String cameraId, int switchCount)
throws Exception {
Size previewSize = getOrderedPreviewSizes(cameraId).get(0);
CameraPreviewListener[] previewListener = new CameraPreviewListener[2];
SurfaceTexture[] previewTexture = new SurfaceTexture[2];
Surface[] surfaces = new Surface[2];
// Create surface textures with the same size
for (int i = 0; i < 2; i++) {
previewListener[i] = new CameraPreviewListener();
mTextureView[i].setSurfaceTextureListener(previewListener[i]);
previewTexture[i] = getAvailableSurfaceTexture(
WAIT_FOR_COMMAND_TO_COMPLETE, mTextureView[i]);
assertNotNull("Unable to get preview surface texture", previewTexture[i]);
previewTexture[i].setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight());
// Correct the preview display rotation.
updatePreviewDisplayRotation(previewSize, mTextureView[i]);
surfaces[i] = new Surface(previewTexture[i]);
}
SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
// Create shared outputs for the two surface textures
OutputConfiguration surfaceSharedOutput = new OutputConfiguration(surfaces[0]);
surfaceSharedOutput.enableSurfaceSharing();
List<OutputConfiguration> outputConfigurations = new ArrayList<>();
outputConfigurations.add(surfaceSharedOutput);
startPreviewWithConfigs(cameraId, outputConfigurations, null);
boolean previewDone =
previewListener[0].waitForPreviewDone(WAIT_FOR_COMMAND_TO_COMPLETE);
assertTrue("Unable to start preview", previewDone);
mTextureView[0].setSurfaceTextureListener(null);
for (int i = 0; i < switchCount; i++) {
//Add one more output surface while preview is streaming
surfaceSharedOutput.addSurface(surfaces[1]);
updateOutputConfiguration(cameraId, surfaceSharedOutput);
int sequenceId = updateRepeatingRequest(cameraId, outputConfigurations, resultListener);
SystemClock.sleep(100);
//Try to remove the shared surface while while we still have active requests that
//use it as output.
surfaceSharedOutput.removeSurface(surfaces[1]);
try {
updateOutputConfiguration(cameraId, surfaceSharedOutput);
fail("should get IllegalArgumentException due to pending requests");
} catch (IllegalArgumentException e) {
// expected exception
}
//Wait for all pending requests to arrive and remove the shared output during active
//streaming
updateRepeatingRequest(cameraId, outputConfigurations, new SimpleCaptureCallback());
long lastSequenceFrameNumber = resultListener.getCaptureSequenceLastFrameNumber(
sequenceId, PREVIEW_TIME_MS);
checkForLastFrameInSequence(lastSequenceFrameNumber, resultListener);
updateOutputConfiguration(cameraId, surfaceSharedOutput);
}
stopPreview(cameraId);
}
/*
* Two output Surface of the same size are configured: one from TextureView and
* the other is ImageReader with usage flag USAGE_GPU_SAMPLED_IMAGE. The
* ImageReader queues Image to a ImageWriter of the same usage flag, the
* ImageWriter then is connected to another TextureView. Verify the Bitmap
* from the first TextureView is identical to the second TextureView.
*/
@Test
public void testTextureImageWriterReaderOperation() throws Exception {
for (String id : getCameraIdsUnderTest()) {
ImageReader reader = null;
ImageWriter writer = null;
Surface writerOutput = null;
try {
Log.i(TAG, "Testing Camera " + id);
openCamera(id);
if (!getStaticInfo(id).isColorOutputSupported()) {
Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
continue;
}
int maxNumStreamsProc =
getStaticInfo(id).getMaxNumOutputStreamsProcessedChecked();
if (maxNumStreamsProc < 2) {
continue;
}
// mTextureView[0..2] each shared 1/3 of the horizontal space but their size can
// differ up to one pixel if the total width is not divisible by 3. Here we try to
// pick two of them that have matching size.
Size size0 = new Size(mTextureView[0].getWidth(), mTextureView[0].getHeight());
Size size1 = new Size(mTextureView[1].getWidth(), mTextureView[1].getHeight());
Size size2 = new Size(mTextureView[2].getWidth(), mTextureView[2].getHeight());
Log.v(TAG, "Size0: " + size0 + ", Size1: " + size1 + ", size2: " + size2);
int viewIdx0 = 0;
int viewIdx1 = 1;
if (!size0.equals(size1)) {
assertTrue("No matching view sizes! Size0: " + size0 +
", Size1: " + size1 + ", size2: " + size2,
size0.equals(size2) || size1.equals(size2));
if (size0.equals(size2)) {
viewIdx0 = 0;
viewIdx1 = 2;
} else {
viewIdx0 = 1;
viewIdx1 = 2;
}
}
Size previewSize = getOrderedPreviewSizes(id).get(0);
List<TextureView> views = Arrays.asList(mTextureView[viewIdx0]);
// view[0] is normal camera -> TextureView path
// view[1] is camera -> ImageReader -> TextureView path
SurfaceTexture surfaceTexture0 = getAvailableSurfaceTexture(
WAIT_FOR_COMMAND_TO_COMPLETE, mTextureView[viewIdx0]);
assertNotNull("Unable to get preview surface texture 0", surfaceTexture0);
surfaceTexture0.setDefaultBufferSize(
previewSize.getWidth(), previewSize.getHeight());
SurfaceTexture surfaceTexture1 = getAvailableSurfaceTexture(
WAIT_FOR_COMMAND_TO_COMPLETE, mTextureView[viewIdx1]);
assertNotNull("Unable to get preview surface texture 1", surfaceTexture1);
surfaceTexture1.setDefaultBufferSize(
previewSize.getWidth(), previewSize.getHeight());
updatePreviewDisplayRotation(previewSize, mTextureView[viewIdx1]);
reader = ImageReader.newInstance(
previewSize.getWidth(), previewSize.getHeight(),
ImageFormat.PRIVATE,
MAX_READER_IMAGES,
HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE);
writerOutput = new Surface(surfaceTexture1);
writer = ImageWriter.newInstance(
writerOutput, MAX_READER_IMAGES,
ImageFormat.PRIVATE);
ImageWriterQueuer writerInput = new ImageWriterQueuer(writer);
reader.setOnImageAvailableListener(writerInput, mHandler);
startTextureViewPreview(id, views, reader);
SystemClock.sleep(PREVIEW_TIME_MS);
stopRepeating(id);
// Extra sleep to make sure all previous preview frames are delivered to
// SurfaceTexture
SystemClock.sleep(PREVIEW_FLUSH_TIME_MS);
Surface preview = new Surface(surfaceTexture0);
CaptureRequest.Builder requestBuilder = getCaptureBuilder(id,
CameraDevice.TEMPLATE_PREVIEW);
requestBuilder.addTarget(reader.getSurface());
requestBuilder.addTarget(preview);
SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
CameraPreviewListener stListener0 = new CameraPreviewListener();
CameraPreviewListener stListener1 = new CameraPreviewListener();
mTextureView[viewIdx0].setSurfaceTextureListener(stListener0);
mTextureView[viewIdx1].setSurfaceTextureListener(stListener1);
// do a single capture
capture(id, requestBuilder.build(), resultListener);
// wait for capture done
boolean previewDone = stListener0.waitForPreviewDone(WAIT_FOR_COMMAND_TO_COMPLETE);
assertTrue("Unable to start preview", previewDone);
previewDone = stListener1.waitForPreviewDone(WAIT_FOR_COMMAND_TO_COMPLETE);
assertTrue("Unable to start preview", previewDone);
// get bitmap from both TextureView and compare
Bitmap bitmap0 = mTextureView[viewIdx0].getBitmap();
Bitmap bitmap1 = mTextureView[viewIdx1].getBitmap();
BitmapUtils.BitmapCompareResult result =
BitmapUtils.compareBitmap(bitmap0, bitmap1);
Log.i(TAG, "Bitmap difference is " + result.mDiff);
assertTrue(String.format(
"Bitmap difference exceeds threshold: diff %f > threshold %f",
result.mDiff, BITMAP_DIFF_THRESHOLD),
result.mDiff <= BITMAP_DIFF_THRESHOLD);
assertTrue(String.format(
"Bitmap from direct Textureview is flat. All pixels are (%f, %f, %f)",
result.mLhsAverage[0], result.mLhsAverage[1], result.mLhsAverage[2]),
!result.mLhsFlat);
assertTrue(String.format(
"Bitmap from ImageWriter Textureview is flat. All pixels are (%f, %f, %f)",
result.mRhsAverage[0], result.mRhsAverage[1], result.mRhsAverage[2]),
!result.mRhsFlat);
} finally {
if (reader != null) {
reader.close();
}
if (writer != null) {
writer.close();
}
if (writerOutput != null) {
writerOutput.release();
}
closeCamera(id);
}
}
}
public static class ImageWriterQueuer implements ImageReader.OnImageAvailableListener {
@Override
public void onImageAvailable(ImageReader reader) {
Image image = null;
try {
image = reader.acquireNextImage();
} finally {
if (image != null && mWriter != null) {
mWriter.queueInputImage(image);
}
}
}
public ImageWriterQueuer(ImageWriter writer) {
mWriter = writer;
}
private ImageWriter mWriter = null;
}
private void checkForLastFrameInSequence(long lastSequenceFrameNumber,
SimpleCaptureCallback listener) throws Exception {
// Find the last frame number received in results and failures.
long lastFrameNumber = -1;
while (listener.hasMoreResults()) {
TotalCaptureResult result = listener.getTotalCaptureResult(PREVIEW_TIME_MS);
if (lastFrameNumber < result.getFrameNumber()) {
lastFrameNumber = result.getFrameNumber();
}
}
while (listener.hasMoreFailures()) {
ArrayList<CaptureFailure> failures = listener.getCaptureFailures(
/*maxNumFailures*/ 1);
for (CaptureFailure failure : failures) {
if (lastFrameNumber < failure.getFrameNumber()) {
lastFrameNumber = failure.getFrameNumber();
}
}
}
// Verify the last frame number received from capture sequence completed matches the
// the last frame number of the results and failures.
assertEquals(String.format("Last frame number from onCaptureSequenceCompleted " +
"(%d) doesn't match the last frame number received from " +
"results/failures (%d)", lastSequenceFrameNumber, lastFrameNumber),
lastSequenceFrameNumber, lastFrameNumber);
}
/*
* Verify behavior of sharing surfaces within one OutputConfiguration
*/
@Test
public void testSharedSurfaces() throws Exception {
for (String cameraId : getCameraIdsUnderTest()) {
try {
openCamera(cameraId);
if (getStaticInfo(cameraId).isHardwareLevelLegacy()) {
Log.i(TAG, "Camera " + cameraId + " is legacy, skipping");
continue;
}
if (!getStaticInfo(cameraId).isColorOutputSupported()) {
Log.i(TAG, "Camera " + cameraId +
" does not support color outputs, skipping");
continue;
}
testSharedSurfacesConfigByCamera(cameraId);
testSharedSurfacesCaptureSessionByCamera(cameraId);
testSharedDeferredSurfacesByCamera(cameraId);
}
finally {
closeCamera(cameraId);
}
}
}
/*
* Verify behavior of mirroring mode
*/
@Test
public void testTextureViewPreviewWithMirroring() throws Exception {
// 4x4 GL-style matrices, as returned by SurfaceTexture#getTransformMatrix(). Note they're
// transforming texture coordinates ([0,1]^2), so the origin for the transforms is
// (0.5, 0.5), not (0,0).
final float[] MIRROR_HORIZONTAL_MATRIX = new float[] {
-1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
1.0f, 0.0f, 0.0f, 1.0f,
};
final float[] MIRROR_VERTICAL_MATRIX = new float[] {
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, -1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 1.0f, 0.0f, 1.0f,
};
for (String cameraId : getCameraIdsUnderTest()) {
Exception prior = null;
try {
openCamera(cameraId);
if (!getStaticInfo(cameraId).isColorOutputSupported()) {
Log.i(TAG, "Camera " + cameraId + " does not support color outputs, skipping");
continue;
}
int[] supportedModes = {
OutputConfiguration.MIRROR_MODE_AUTO,
OutputConfiguration.MIRROR_MODE_NONE,
OutputConfiguration.MIRROR_MODE_H,
OutputConfiguration.MIRROR_MODE_V };
HashMap<Integer, float[]> transformsPerMode = new HashMap<>();
for (int mode : supportedModes) {
float[] transform =
textureViewPreviewWithMirroring(cameraId, mTextureView[0], mode);
transformsPerMode.put(mode, transform);
}
int lensFacing = getStaticInfo(cameraId).getLensFacingChecked();
if (lensFacing == CameraCharacteristics.LENS_FACING_FRONT) {
assertArrayEquals("Front camera's transform matrix must be the same between "
+ "AUTO and horizontal mirroring mode",
transformsPerMode.get(OutputConfiguration.MIRROR_MODE_AUTO),
transformsPerMode.get(OutputConfiguration.MIRROR_MODE_H), 0.0f);
} else {
assertArrayEquals("Rear/external camera's transform matrix must be the same "
+ "between AUTO and NONE mirroring mode",
transformsPerMode.get(OutputConfiguration.MIRROR_MODE_AUTO),
transformsPerMode.get(OutputConfiguration.MIRROR_MODE_NONE), 0.0f);
}
float[] horizontalMirroredTransform = new float[16];
Matrix.multiplyMM(horizontalMirroredTransform, 0,
transformsPerMode.get(OutputConfiguration.MIRROR_MODE_NONE), 0,
MIRROR_HORIZONTAL_MATRIX, 0);
assertArrayEquals("Camera " + cameraId + " transform matrix in horizontal mode "
+ "contradicts with NONE mode",
transformsPerMode.get(OutputConfiguration.MIRROR_MODE_H),
horizontalMirroredTransform, 0.0f);
float[] verticalMirroredTransform = new float[16];
Matrix.multiplyMM(verticalMirroredTransform, 0,
transformsPerMode.get(OutputConfiguration.MIRROR_MODE_NONE), 0,
MIRROR_VERTICAL_MATRIX, 0);
assertArrayEquals("Camera " + cameraId + " transform matrix in vertical mode "
+ "contradicts with NONE mode",
transformsPerMode.get(OutputConfiguration.MIRROR_MODE_V),
verticalMirroredTransform, 0.0f);
} catch (Exception e) {
prior = e;
} finally {
try {
closeCamera(cameraId);
} catch (Exception e) {
if (prior != null) {
Log.e(TAG, "Prior exception received: " + prior);
}
prior = e;
}
if (prior != null) throw prior; // Rethrow last exception.
}
}
}
private float[] textureViewPreviewWithMirroring(String cameraId,
TextureView textureView, int mirrorMode) throws Exception {
Size previewSize = getOrderedPreviewSizes(cameraId).get(0);
CameraPreviewListener previewListener =
new CameraPreviewListener();
textureView.setSurfaceTextureListener(previewListener);
SurfaceTexture previewTexture = getAvailableSurfaceTexture(
WAIT_FOR_COMMAND_TO_COMPLETE, textureView);
assertNotNull("Unable to get preview surface texture", previewTexture);
previewTexture.setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight());
// Correct the preview display rotation.
updatePreviewDisplayRotation(previewSize, textureView);
OutputConfiguration outputConfig = new OutputConfiguration(new Surface(previewTexture));
outputConfig.setMirrorMode(mirrorMode);
List<OutputConfiguration> outputConfigs = new ArrayList<>();
outputConfigs.add(outputConfig);
startPreviewWithConfigs(cameraId, outputConfigs, null);
boolean previewDone =
previewListener.waitForPreviewDone(WAIT_FOR_COMMAND_TO_COMPLETE);
assertTrue("Unable to start preview", previewDone);
SystemClock.sleep(PREVIEW_TIME_MS);
float[] transform = previewListener.getPreviewTransform();
assertNotNull("Failed to get preview transform");
textureView.setSurfaceTextureListener(null);
stopPreview(cameraId);
return transform;
}
/**
* Start camera preview using input texture views and/or one image reader
*/
private void startTextureViewPreview(
String cameraId, List<TextureView> views, ImageReader imageReader)
throws Exception {
int numPreview = views.size();
Size previewSize = getOrderedPreviewSizes(cameraId).get(0);
CameraPreviewListener[] previewListener =
new CameraPreviewListener[numPreview];
SurfaceTexture[] previewTexture = new SurfaceTexture[numPreview];
List<Surface> surfaces = new ArrayList<Surface>();
// Prepare preview surface.
int i = 0;
for (TextureView view : views) {
previewListener[i] = new CameraPreviewListener();
view.setSurfaceTextureListener(previewListener[i]);
previewTexture[i] = getAvailableSurfaceTexture(WAIT_FOR_COMMAND_TO_COMPLETE, view);
assertNotNull("Unable to get preview surface texture", previewTexture[i]);
previewTexture[i].setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight());
// Correct the preview display rotation.
updatePreviewDisplayRotation(previewSize, view);
surfaces.add(new Surface(previewTexture[i]));
i++;
}
if (imageReader != null) {
surfaces.add(imageReader.getSurface());
}
startPreview(cameraId, surfaces, null);
i = 0;
for (TextureView view : views) {
boolean previewDone =
previewListener[i].waitForPreviewDone(WAIT_FOR_COMMAND_TO_COMPLETE);
assertTrue("Unable to start preview " + i, previewDone);
view.setSurfaceTextureListener(null);
i++;
}
}
/**
* Test camera preview using input texture views and/or one image reader
*/
private void textureViewPreview(
String cameraId, List<TextureView> views, ImageReader testImagerReader)
throws Exception {
startTextureViewPreview(cameraId, views, testImagerReader);
// TODO: check the framerate is correct
SystemClock.sleep(PREVIEW_TIME_MS);
stopPreview(cameraId);
}
/*
* Verify behavior of OutputConfiguration when sharing surfaces
*/
private void testSharedSurfacesConfigByCamera(String cameraId) throws Exception {
List<Size> orderedPreviewSizes = getOrderedPreviewSizes(cameraId);
Size previewSize = orderedPreviewSizes.get(0);
SurfaceTexture[] previewTexture = new SurfaceTexture[2];
Surface[] surfaces = new Surface[2];
// Create surface textures with the same size
for (int i = 0; i < 2; i++) {
previewTexture[i] = getAvailableSurfaceTexture(
WAIT_FOR_COMMAND_TO_COMPLETE, mTextureView[i]);
assertNotNull("Unable to get preview surface texture", previewTexture[i]);
previewTexture[i].setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight());
// Correct the preview display rotation.
updatePreviewDisplayRotation(previewSize, mTextureView[i]);
surfaces[i] = new Surface(previewTexture[i]);
}
// Verify that outputConfiguration can be created with 2 surfaces with the same setting.
OutputConfiguration previewConfiguration = new OutputConfiguration(
OutputConfiguration.SURFACE_GROUP_ID_NONE, surfaces[0]);
previewConfiguration.enableSurfaceSharing();
previewConfiguration.addSurface(surfaces[1]);
List<Surface> previewSurfaces = previewConfiguration.getSurfaces();
List<Surface> inputSurfaces = Arrays.asList(surfaces);
assertTrue(
String.format("Surfaces returned from getSurfaces() don't match those passed in"),
previewSurfaces.equals(inputSurfaces));
if (orderedPreviewSizes.size() > 1) {
// Verify that createCaptureSession fails if 2 surfaces are different size
SurfaceTexture outputTexture2 = new SurfaceTexture(/* random texture ID*/ 5);
Size previewSize2 = orderedPreviewSizes.get(1);
outputTexture2.setDefaultBufferSize(previewSize2.getWidth(),
previewSize2.getHeight());
Surface outputSurface2 = new Surface(outputTexture2);
OutputConfiguration configuration = new OutputConfiguration(
OutputConfiguration.SURFACE_GROUP_ID_NONE, surfaces[0]);
configuration.enableSurfaceSharing();
configuration.addSurface(outputSurface2);
List<OutputConfiguration> outputConfigurations = new ArrayList<>();
outputConfigurations.add(configuration);
verifyCreateSessionWithConfigsFailure(cameraId, outputConfigurations);
}
OutputConfiguration configuration;
// Verify that outputConfiguration throws exception if 2 surfaces are different format
ImageReader imageReader = makeImageReader(previewSize, ImageFormat.YUV_420_888,
MAX_READER_IMAGES, new ImageDropperListener(), mHandler);
try {
configuration = new OutputConfiguration(OutputConfiguration.SURFACE_GROUP_ID_NONE,
surfaces[0]);
configuration.enableSurfaceSharing();
configuration.addSurface(imageReader.getSurface());
fail("No error for invalid output config created from different format surfaces");
} catch (IllegalArgumentException e) {
// expected
}
// Verify that outputConfiguration can be created with deferred surface with the same
// setting.
OutputConfiguration deferredPreviewConfigure = new OutputConfiguration(
previewSize, SurfaceTexture.class);
deferredPreviewConfigure.addSurface(surfaces[0]);
assertTrue(String.format("Number of surfaces %d doesn't match expected value 1",
deferredPreviewConfigure.getSurfaces().size()),
deferredPreviewConfigure.getSurfaces().size() == 1);
assertEquals("Surface 0 in OutputConfiguration doesn't match input",
deferredPreviewConfigure.getSurfaces().get(0), surfaces[0]);
// Verify that outputConfiguration throws exception if deferred surface and non-deferred
// surface properties don't match
try {
configuration = new OutputConfiguration(previewSize, SurfaceTexture.class);
configuration.addSurface(imageReader.getSurface());
fail("No error for invalid output config created deferred class with different type");
} catch (IllegalArgumentException e) {
// expected;
}
}
private void testSharedSurfacesCaptureSessionByCamera(String cameraId) throws Exception {
Size previewSize = getOrderedPreviewSizes(cameraId).get(0);
CameraPreviewListener[] previewListener = new CameraPreviewListener[2];
SurfaceTexture[] previewTexture = new SurfaceTexture[2];
Surface[] surfaces = new Surface[2];
// Create surface textures with the same size
for (int i = 0; i < 2; i++) {
previewListener[i] = new CameraPreviewListener();
mTextureView[i].setSurfaceTextureListener(previewListener[i]);
previewTexture[i] = getAvailableSurfaceTexture(
WAIT_FOR_COMMAND_TO_COMPLETE, mTextureView[i]);
assertNotNull("Unable to get preview surface texture", previewTexture[i]);
previewTexture[i].setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight());
// Correct the preview display rotation.
updatePreviewDisplayRotation(previewSize, mTextureView[i]);
surfaces[i] = new Surface(previewTexture[i]);
}
// Create shared outputs for the two surface textures
OutputConfiguration surfaceSharedOutput = new OutputConfiguration(
OutputConfiguration.SURFACE_GROUP_ID_NONE, surfaces[0]);
surfaceSharedOutput.enableSurfaceSharing();
surfaceSharedOutput.addSurface(surfaces[1]);
List<OutputConfiguration> outputConfigurations = new ArrayList<>();
outputConfigurations.add(surfaceSharedOutput);
startPreviewWithConfigs(cameraId, outputConfigurations, null);
for (int i = 0; i < 2; i++) {
boolean previewDone =
previewListener[i].waitForPreviewDone(WAIT_FOR_COMMAND_TO_COMPLETE);
assertTrue("Unable to start preview " + i, previewDone);
mTextureView[i].setSurfaceTextureListener(null);
}
SystemClock.sleep(PREVIEW_TIME_MS);
stopPreview(cameraId);
}
private void testSharedDeferredSurfacesByCamera(String cameraId) throws Exception {
Size previewSize = getOrderedPreviewSizes(cameraId).get(0);
CameraPreviewListener[] previewListener = new CameraPreviewListener[2];
SurfaceTexture[] previewTexture = new SurfaceTexture[2];
Surface[] surfaces = new Surface[2];
// Create surface textures with the same size
for (int i = 0; i < 2; i++) {
previewListener[i] = new CameraPreviewListener();
mTextureView[i].setSurfaceTextureListener(previewListener[i]);
previewTexture[i] = getAvailableSurfaceTexture(
WAIT_FOR_COMMAND_TO_COMPLETE, mTextureView[i]);
assertNotNull("Unable to get preview surface texture", previewTexture[i]);
previewTexture[i].setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight());
// Correct the preview display rotation.
updatePreviewDisplayRotation(previewSize, mTextureView[i]);
surfaces[i] = new Surface(previewTexture[i]);
}
//
// Create deferred outputConfiguration, addSurface, createCaptureSession, addSurface, and
// finalizeOutputConfigurations.
//
OutputConfiguration surfaceSharedOutput = new OutputConfiguration(
previewSize, SurfaceTexture.class);
surfaceSharedOutput.enableSurfaceSharing();
surfaceSharedOutput.addSurface(surfaces[0]);
List<OutputConfiguration> outputConfigurations = new ArrayList<>();
outputConfigurations.add(surfaceSharedOutput);
// Run preview with one surface, and verify at least one frame is received.
startPreviewWithConfigs(cameraId, outputConfigurations, null);
boolean previewDone =
previewListener[0].waitForPreviewDone(WAIT_FOR_COMMAND_TO_COMPLETE);
assertTrue("Unable to start preview 0", previewDone);
SystemClock.sleep(PREVIEW_TIME_MS);
// Add deferred surface to the output configuration
surfaceSharedOutput.addSurface(surfaces[1]);
List<OutputConfiguration> deferredConfigs = new ArrayList<OutputConfiguration>();
deferredConfigs.add(surfaceSharedOutput);
// Run preview with both surfaces, and verify at least one frame is received for each
// surface.
finalizeOutputConfigs(cameraId, deferredConfigs, null);
previewDone =
previewListener[1].waitForPreviewDone(WAIT_FOR_COMMAND_TO_COMPLETE);
assertTrue("Unable to start preview 1", previewDone);
stopPreview(cameraId);
previewListener[0].reset();
previewListener[1].reset();
//
// Create outputConfiguration with a surface, createCaptureSession, addSurface, and
// finalizeOutputConfigurations.
//
surfaceSharedOutput = new OutputConfiguration(
OutputConfiguration.SURFACE_GROUP_ID_NONE, surfaces[0]);
surfaceSharedOutput.enableSurfaceSharing();
outputConfigurations.clear();
outputConfigurations.add(surfaceSharedOutput);
startPreviewWithConfigs(cameraId, outputConfigurations, null);
previewDone =
previewListener[0].waitForPreviewDone(WAIT_FOR_COMMAND_TO_COMPLETE);
assertTrue("Unable to start preview 0", previewDone);
// Add deferred surface to the output configuration, and continue running preview
surfaceSharedOutput.addSurface(surfaces[1]);
deferredConfigs.clear();
deferredConfigs.add(surfaceSharedOutput);
finalizeOutputConfigs(cameraId, deferredConfigs, null);
previewDone =
previewListener[1].waitForPreviewDone(WAIT_FOR_COMMAND_TO_COMPLETE);
assertTrue("Unable to start preview 1", previewDone);
SystemClock.sleep(PREVIEW_TIME_MS);
stopPreview(cameraId);
previewListener[0].reset();
previewListener[1].reset();
//
// Create deferred output configuration, createCaptureSession, addSurface, addSurface, and
// finalizeOutputConfigurations.
surfaceSharedOutput = new OutputConfiguration(
previewSize, SurfaceTexture.class);
surfaceSharedOutput.enableSurfaceSharing();
outputConfigurations.clear();
outputConfigurations.add(surfaceSharedOutput);
createSessionWithConfigs(cameraId, outputConfigurations);
// Add 2 surfaces to the output configuration, and run preview
surfaceSharedOutput.addSurface(surfaces[0]);
surfaceSharedOutput.addSurface(surfaces[1]);
deferredConfigs.clear();
deferredConfigs.add(surfaceSharedOutput);
finalizeOutputConfigs(cameraId, deferredConfigs, null);
for (int i = 0; i < 2; i++) {
previewDone =
previewListener[i].waitForPreviewDone(WAIT_FOR_COMMAND_TO_COMPLETE);
assertTrue("Unable to start preview " + i, previewDone);
}
SystemClock.sleep(PREVIEW_TIME_MS);
stopPreview(cameraId);
}
private final class SimpleImageListener implements ImageReader.OnImageAvailableListener {
private final ConditionVariable imageAvailable = new ConditionVariable();
@Override
public void onImageAvailable(ImageReader reader) {
imageAvailable.open();
}
public void waitForAnyImageAvailable(long timeout) {
if (imageAvailable.block(timeout)) {
imageAvailable.close();
} else {
fail("wait for image available timed out after " + timeout + "ms");
}
}
}
}