blob: 283bc90f583740782da8910aa396f899dcc6e8d7 [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 android.hardware.camera2.cts.CameraTestUtils.*;
import static android.hardware.camera2.cts.CameraTestUtils.MaxStreamSizes.*;
import android.graphics.ImageFormat;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.CaptureFailure;
import android.hardware.camera2.cts.helpers.StaticMetadata;
import android.hardware.camera2.cts.testcases.Camera2ConcurrentAndroidTestCase;
import android.hardware.camera2.params.OutputConfiguration;
import android.hardware.camera2.params.MandatoryStreamCombination;
import android.hardware.camera2.params.MandatoryStreamCombination.MandatoryStreamInformation;
import android.hardware.camera2.params.SessionConfiguration;
import android.media.ImageReader;
import android.util.Log;
import android.util.Pair;
import android.view.Surface;
import com.android.ex.camera2.blocking.BlockingSessionCallback;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import java.util.Set;
import org.junit.runners.Parameterized;
import org.junit.runner.RunWith;
import org.junit.Test;
import static junit.framework.Assert.assertTrue;
import static org.mockito.Mockito.*;
/**
* Tests exercising concurrent camera streaming mandatory stream combinations.
*/
@RunWith(Parameterized.class)
public class ConcurrentCameraTest extends Camera2ConcurrentAndroidTestCase {
private static final String TAG = "ConcurrentCameraTest";
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
/**
* Class representing a per camera id test sample.
*/
private static class TestSample {
public String cameraId;
public StaticMetadata staticInfo;
public MandatoryStreamCombination combination;
public boolean haveSession = false;
public boolean substituteY8;
public List<OutputConfiguration> outputConfigs = new ArrayList<OutputConfiguration>();
public List<Surface> outputSurfaces = new ArrayList<Surface>();
public StreamCombinationTargets targets = new StreamCombinationTargets();
public TestSample(String cameraId, StaticMetadata staticInfo,
MandatoryStreamCombination combination, boolean subY8) {
this.cameraId = cameraId;
this.staticInfo = staticInfo;
this.combination = combination;
this.substituteY8 = subY8;
}
}
/**
* Class representing the information needed to generate a unique combination per camera id
*/
private static class GeneratedEntry {
public String cameraId;
public boolean substituteY8;
MandatoryStreamCombination combination;
//Note: We don't have physical camera ids here since physical camera ids do not have
// guaranteed concurrent stream combinations.
GeneratedEntry(String cameraId, boolean substituteY8,
MandatoryStreamCombination combination) {
this.cameraId = cameraId;
this.substituteY8 = substituteY8;
this.combination = combination;
}
};
@Test
public void testMandatoryConcurrentStreamCombination() throws Exception {
for (Set<String> cameraIdCombinations : mConcurrentCameraIdCombinations) {
if (cameraIdCombinations.size() == 0) {
continue;
}
List<HashMap<String, GeneratedEntry>> streamCombinationPermutations =
generateStreamSelections(cameraIdCombinations);
for (HashMap<String, GeneratedEntry> streamCombinationPermutation :
streamCombinationPermutations) {
ArrayList<TestSample> testSamples = new ArrayList<TestSample>();
for (Map.Entry<String, GeneratedEntry> deviceSample :
streamCombinationPermutation.entrySet()) {
CameraTestInfo info = mCameraTestInfos.get(deviceSample.getKey());
assertTrue("CameraTestInfo not found for camera id " + deviceSample.getKey(),
info != null);
MandatoryStreamCombination chosenCombination =
deviceSample.getValue().combination;
boolean substituteY8 = deviceSample.getValue().substituteY8;
TestSample testSample = new TestSample(deviceSample.getKey(), info.mStaticInfo,
chosenCombination, substituteY8);
testSamples.add(testSample);
openDevice(deviceSample.getKey());
}
try {
testMandatoryConcurrentStreamCombination(testSamples);
} finally {
for (TestSample testSample : testSamples) {
closeDevice(testSample.cameraId);
}
}
}
}
}
// @return Generates a List of HashMaps<String, GeneratedEntry> which is a map:
// cameraId -> GeneratedEntry (represents a camera device's test configuration)
// @param perIdGeneratedEntries a per-camera id set of combinations to recursively generate
// GeneratedEntries for.
private List<HashMap<String, GeneratedEntry>> generateStreamSelectionsInternal(
HashMap<String, List<GeneratedEntry>> perIdGeneratedEntries) {
Set<String> cameraIds = perIdGeneratedEntries.keySet();
// Base condition for recursion.
if (cameraIds.size() == 1) {
String cameraId = cameraIds.toArray(new String[1])[0];
List<HashMap<String, GeneratedEntry>> ret =
new ArrayList<HashMap<String, GeneratedEntry>> ();
for (GeneratedEntry entry : perIdGeneratedEntries.get(cameraId)) {
HashMap<String, GeneratedEntry> retPut = new HashMap<String, GeneratedEntry>();
retPut.put(cameraId, entry);
ret.add(retPut);
}
return ret;
}
// Choose one camera id, create all combinations for the remaining set and then add each of
// the camera id's combinations to the returned list of maps.
String keyChosen = null;
for (String cameraId: cameraIds) {
keyChosen = cameraId;
break;
}
List<GeneratedEntry> selfGeneratedEntries = perIdGeneratedEntries.get(keyChosen);
perIdGeneratedEntries.remove(keyChosen);
List<HashMap<String, GeneratedEntry>> recResult =
generateStreamSelectionsInternal(perIdGeneratedEntries);
List<HashMap<String, GeneratedEntry>> res =
new ArrayList<HashMap<String, GeneratedEntry>>();
for (GeneratedEntry gen : selfGeneratedEntries) {
for (HashMap<String, GeneratedEntry> entryMap : recResult) {
// Make a copy of the HashMap, add the generated entry to it and add it to the final
// result (since we want to use the original for the other 'gen' entries)
HashMap<String, GeneratedEntry> copy = (HashMap)entryMap.clone();
copy.put(keyChosen, gen);
res.add(copy);
}
}
return res;
}
/**
* Generates a list of combinations used for mandatory stream combination testing.
* Each combination(GeneratedEntry) corresponds to a camera id advertised by
* getConcurrentCameraIds().
*/
private List<HashMap<String, GeneratedEntry>> generateStreamSelections(
Set<String> cameraIdCombination) {
// First artificially create lists of GeneratedEntries for each camera id in the passed
// set.
HashMap<String, List<GeneratedEntry>> perIdGeneratedEntries =
new HashMap<String, List<GeneratedEntry>>();
for (String cameraId : cameraIdCombination) {
List<GeneratedEntry> genEntries = getGeneratedEntriesFor(cameraId);
perIdGeneratedEntries.put(cameraId, genEntries);
}
return generateStreamSelectionsInternal(perIdGeneratedEntries);
}
// get GeneratedEntries for a particular camera id.
List<GeneratedEntry> getGeneratedEntriesFor(String cameraId) {
CameraTestInfo info = mCameraTestInfos.get(cameraId);
assertTrue("CameraTestInfo not found for camera id " + cameraId, info != null);
MandatoryStreamCombination[] combinations = info.mMandatoryStreamCombinations;
List<GeneratedEntry> generatedEntries = new ArrayList<GeneratedEntry>();
// Now generate entries on the camera's mandatory streams
for (MandatoryStreamCombination combination : combinations) {
generatedEntries.add(new GeneratedEntry(cameraId,/*substituteY8*/false, combination));
// Check whether substituting YUV_888 format with Y8 format
boolean substituteY8 = false;
if (info.mStaticInfo.isMonochromeWithY8()) {
List<MandatoryStreamInformation> streamsInfo = combination.getStreamsInformation();
for (MandatoryStreamInformation streamInfo : streamsInfo) {
if (streamInfo.getFormat() == ImageFormat.YUV_420_888) {
substituteY8 = true;
break;
}
}
}
if (substituteY8) {
generatedEntries.add(new GeneratedEntry(cameraId, /*substituteY8*/true,
combination));
}
}
return generatedEntries;
}
private void testMandatoryConcurrentStreamCombination(ArrayList<TestSample> testSamples)
throws Exception {
final int TIMEOUT_FOR_RESULT_MS = 1000;
final int MIN_RESULT_COUNT = 3;
HashMap<String, SessionConfiguration> testSessionMap =
new HashMap<String, SessionConfiguration>();
for (TestSample testSample : testSamples) {
CameraTestInfo info = mCameraTestInfos.get(testSample.cameraId);
assertTrue("CameraTestInfo not found for camera id " + testSample.cameraId,
info != null);
List<OutputConfiguration> outputConfigs = new ArrayList<>();
CameraTestUtils.setupConfigurationTargets(
testSample.combination.getStreamsInformation(), testSample.targets,
outputConfigs, testSample.outputSurfaces, MIN_RESULT_COUNT,
testSample.substituteY8, /*substituteHEIC*/false, /*physicalCameraId*/null,
/*multiResStreamConfig*/null, mHandler);
for (OutputConfiguration c : outputConfigs) {
testSample.outputConfigs.add(c);
}
try {
checkSessionConfigurationSupported(info.mCamera, mHandler, testSample.outputConfigs,
/*inputConfig*/ null, SessionConfiguration.SESSION_REGULAR,
true/*defaultSupport*/, String.format(
"Session configuration query from combination: %s failed",
testSample.combination.getDescription()));
testSessionMap.put(testSample.cameraId, new SessionConfiguration(
SessionConfiguration.SESSION_REGULAR,testSample.outputConfigs,
new HandlerExecutor(mHandler), new BlockingSessionCallback()));
} catch (Throwable e) {
mCollector.addMessage(String.format(
"Mandatory stream combination: %s for camera id %s failed due: %s",
testSample.combination.getDescription(), testSample.cameraId,
e.getMessage()));
}
}
// Mandatory stream combinations must be reported as supported.
assertTrue("Concurrent session configs not supported",
mCameraManager.isConcurrentSessionConfigurationSupported(testSessionMap));
for (TestSample testSample : testSamples) {
try {
CameraTestInfo info = mCameraTestInfos.get(testSample.cameraId);
assertTrue("CameraTestInfo not found for camera id " + testSample.cameraId,
info != null);
createSessionByConfigs(testSample.cameraId, testSample.outputConfigs);
testSample.haveSession = true;
CaptureRequest.Builder requestBuilder =
info.mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
for (OutputConfiguration c : testSample.outputConfigs) {
requestBuilder.addTarget(c.getSurface());
}
CaptureRequest request = requestBuilder.build();
CameraCaptureSession.CaptureCallback mockCaptureCallback =
mock(CameraCaptureSession.CaptureCallback.class);
info.mCameraSession.setRepeatingRequest(request, mockCaptureCallback, mHandler);
verify(mockCaptureCallback,
timeout(TIMEOUT_FOR_RESULT_MS * MIN_RESULT_COUNT).atLeast(MIN_RESULT_COUNT))
.onCaptureCompleted(
eq(info.mCameraSession),
eq(request),
isA(TotalCaptureResult.class));
verify(mockCaptureCallback, never()).
onCaptureFailed(
eq(info.mCameraSession),
eq(request),
isA(CaptureFailure.class));
} catch (Throwable e) {
mCollector.addMessage(String.format(
"Mandatory stream combination : %s for camera id %s failed due: %s",
testSample.combination.getDescription(),
testSample.cameraId, e.getMessage()));
}
}
for (TestSample testSample : testSamples) {
if (testSample.haveSession) {
try {
Log.i(TAG,
String.format("Done with camera %s, combination: %s, closing session",
testSample.cameraId, testSample.combination.getDescription()));
stopCapture(testSample.cameraId, /*fast*/false);
} catch (Throwable e) {
String closingDownFormat =
"Closing down for combination: %s for camera id %s failed due to: %s";
mCollector.addMessage(
String.format(closingDownFormat,
testSample.combination.getDescription(), testSample.cameraId,
e.getMessage()));
}
}
testSample.targets.close();
}
}
}