java.lang.Object
↳androidx.camera.camera2.internal.StreamUseCaseUtil
Gradle dependencies
compile group: 'androidx.camera', name: 'camera-camera2', version: '1.5.0-alpha01'
- groupId: androidx.camera
- artifactId: camera-camera2
- version: 1.5.0-alpha01
Artifact androidx.camera:camera-camera2:1.5.0-alpha01 it located at Google repository (https://maven.google.com/)
Overview
A class that contains utility methods for stream use case.
Summary
Methods |
---|
public static boolean | areCaptureTypesEligible(java.util.Map<java.lang.Integer, AttachedSurfaceInfo> surfaceConfigIndexAttachedSurfaceInfoMap, java.util.Map<java.lang.Integer, UseCaseConfig> surfaceConfigIndexUseCaseConfigMap, java.util.List<SurfaceConfig> surfaceConfigsWithStreamUseCase)
Return true if the stream use cases contained in surfaceConfigsWithStreamUseCases all have
eligible capture type pairing with the use cases that these surfaceConfigs are constructed
from. |
public static boolean | areStreamUseCasesAvailableForSurfaceConfigs(CameraCharacteristicsCompat characteristicsCompat, java.util.List<SurfaceConfig> surfaceConfigs)
Return true if the stream use cases in the given surface configurations are available for
the device. |
public static boolean | containsZslUseCase(java.util.List<AttachedSurfaceInfo> attachedSurfaces, java.util.List<UseCaseConfig> newUseCaseConfigs)
Return true if any one of the existing or new UseCases is ZSL. |
public static Camera2ImplConfig | getStreamSpecImplementationOptions(UseCaseConfig<UseCase> useCaseConfig)
Populate all implementation options needed to determine the StreamUseCase option in the
StreamSpec for this UseCaseConfig |
public static boolean | isStreamUseCaseSupported(CameraCharacteristicsCompat characteristicsCompat)
Return true if the given camera characteristics support stream use case |
public static boolean | populateStreamUseCaseStreamSpecOptionWithInteropOverride(CameraCharacteristicsCompat characteristicsCompat, java.util.List<AttachedSurfaceInfo> attachedSurfaces, java.util.Map<UseCaseConfig, StreamSpec> suggestedStreamSpecMap, java.util.Map<AttachedSurfaceInfo, StreamSpec> attachedSurfaceStreamSpecMap)
Populate the option in StreamSpecs for both
existing UseCases and new UseCases to be attached. |
public static void | populateStreamUseCaseStreamSpecOptionWithSupportedSurfaceConfigs(java.util.Map<UseCaseConfig, StreamSpec> suggestedStreamSpecMap, java.util.Map<AttachedSurfaceInfo, StreamSpec> attachedSurfaceStreamSpecMap, java.util.Map<java.lang.Integer, AttachedSurfaceInfo> surfaceConfigIndexAttachedSurfaceInfoMap, java.util.Map<java.lang.Integer, UseCaseConfig> surfaceConfigIndexUseCaseConfigMap, java.util.List<SurfaceConfig> surfaceConfigsWithStreamUseCase)
|
public static void | populateSurfaceToStreamUseCaseMapping(java.util.Collection<SessionConfig> sessionConfigs, java.util.Collection<UseCaseConfig> useCaseConfigs, java.util.Map<DeferrableSurface, java.lang.Long> streamUseCaseMap)
Populates the mapping between surfaces of a capture session and the Stream Use Case of their
associated stream. |
public static boolean | shouldUseStreamUseCase(androidx.camera.camera2.internal.SupportedSurfaceCombination.FeatureSettings featureSettings)
Return true if the given feature settings is appropriate for stream use case usage. |
from java.lang.Object | clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait |
Fields
public static final
Config.Option<java.lang.Long>
STREAM_USE_CASE_STREAM_SPEC_OPTIONMethods
public static void
populateSurfaceToStreamUseCaseMapping(java.util.Collection<SessionConfig> sessionConfigs, java.util.Collection<UseCaseConfig> useCaseConfigs, java.util.Map<DeferrableSurface, java.lang.Long> streamUseCaseMap)
Populates the mapping between surfaces of a capture session and the Stream Use Case of their
associated stream.
Parameters:
sessionConfigs: collection of all session configs for this capture session
streamUseCaseMap: the mapping between surfaces and Stream Use Case flag
Populate all implementation options needed to determine the StreamUseCase option in the
StreamSpec for this UseCaseConfig
Return true if the given camera characteristics support stream use case
public static boolean
shouldUseStreamUseCase(androidx.camera.camera2.internal.SupportedSurfaceCombination.FeatureSettings featureSettings)
Return true if the given feature settings is appropriate for stream use case usage.
public static boolean
populateStreamUseCaseStreamSpecOptionWithInteropOverride(
CameraCharacteristicsCompat characteristicsCompat, java.util.List<AttachedSurfaceInfo> attachedSurfaces, java.util.Map<UseCaseConfig, StreamSpec> suggestedStreamSpecMap, java.util.Map<AttachedSurfaceInfo, StreamSpec> attachedSurfaceStreamSpecMap)
Populate the option in StreamSpecs for both
existing UseCases and new UseCases to be attached. This option will be written into the
session configurations of the UseCases. When creating a new capture session during
downstream, it will be used to set the StreamUseCase flag via
Parameters:
characteristicsCompat: the camera characteristics of the device
attachedSurfaces: surface info of the already attached use cases
suggestedStreamSpecMap: the UseCaseConfig-to-StreamSpec map for new use cases
attachedSurfaceStreamSpecMap: the SurfaceInfo-to-StreamSpec map for attached use cases
whose StreamSpecs needs to be updated
Returns:
true if StreamSpec options are populated. False if not.
public static boolean
areStreamUseCasesAvailableForSurfaceConfigs(
CameraCharacteristicsCompat characteristicsCompat, java.util.List<SurfaceConfig> surfaceConfigs)
Return true if the stream use cases in the given surface configurations are available for
the device.
public static boolean
areCaptureTypesEligible(java.util.Map<java.lang.Integer, AttachedSurfaceInfo> surfaceConfigIndexAttachedSurfaceInfoMap, java.util.Map<java.lang.Integer, UseCaseConfig> surfaceConfigIndexUseCaseConfigMap, java.util.List<SurfaceConfig> surfaceConfigsWithStreamUseCase)
Return true if the stream use cases contained in surfaceConfigsWithStreamUseCases all have
eligible capture type pairing with the use cases that these surfaceConfigs are constructed
from.
Parameters:
surfaceConfigIndexAttachedSurfaceInfoMap: mapping between an surfaceConfig's index
in the list and the attachedSurfaceInfo it
is constructed from
surfaceConfigIndexUseCaseConfigMap: mapping between an surfaceConfig's index
* in the list and the useCaseConfig it is
constructed from
surfaceConfigsWithStreamUseCase: the supported surfaceConfigs that contains
accurate streamUseCases
public static void
populateStreamUseCaseStreamSpecOptionWithSupportedSurfaceConfigs(java.util.Map<UseCaseConfig, StreamSpec> suggestedStreamSpecMap, java.util.Map<AttachedSurfaceInfo, StreamSpec> attachedSurfaceStreamSpecMap, java.util.Map<java.lang.Integer, AttachedSurfaceInfo> surfaceConfigIndexAttachedSurfaceInfoMap, java.util.Map<java.lang.Integer, UseCaseConfig> surfaceConfigIndexUseCaseConfigMap, java.util.List<SurfaceConfig> surfaceConfigsWithStreamUseCase)
Parameters:
suggestedStreamSpecMap: mapping between useCaseConfig and its
streamSpecs
attachedSurfaceStreamSpecMap: mapping between attachedSurfaceInfo and its
streamSpecs that contains streamUseCases.
All streamSpecs in this map has
streamUseCases
surfaceConfigIndexAttachedSurfaceInfoMap: mapping between an surfaceConfig's index
in the list and the
attachedSurfaceInfo it
is constructed from
surfaceConfigIndexUseCaseConfigMap: mapping between an surfaceConfig's
index in the list and the useCaseConfig
it is constructed from
surfaceConfigsWithStreamUseCase: the supported surfaceConfigs that contains
accurate streamUseCases
public static boolean
containsZslUseCase(java.util.List<AttachedSurfaceInfo> attachedSurfaces, java.util.List<UseCaseConfig> newUseCaseConfigs)
Return true if any one of the existing or new UseCases is ZSL.
Source
/*
* Copyright 2022 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 androidx.camera.camera2.internal;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraMetadata;
import android.os.Build;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.OptIn;
import androidx.camera.camera2.impl.Camera2ImplConfig;
import androidx.camera.camera2.internal.compat.CameraCharacteristicsCompat;
import androidx.camera.camera2.interop.ExperimentalCamera2Interop;
import androidx.camera.core.DynamicRange;
import androidx.camera.core.ImageCapture;
import androidx.camera.core.Logger;
import androidx.camera.core.impl.AttachedSurfaceInfo;
import androidx.camera.core.impl.CameraMode;
import androidx.camera.core.impl.Config;
import androidx.camera.core.impl.DeferrableSurface;
import androidx.camera.core.impl.ImageCaptureConfig;
import androidx.camera.core.impl.MutableOptionsBundle;
import androidx.camera.core.impl.SessionConfig;
import androidx.camera.core.impl.StreamSpec;
import androidx.camera.core.impl.SurfaceConfig;
import androidx.camera.core.impl.UseCaseConfig;
import androidx.camera.core.impl.UseCaseConfigFactory;
import androidx.camera.core.streamsharing.StreamSharingConfig;
import androidx.core.util.Preconditions;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* A class that contains utility methods for stream use case.
*/
public final class StreamUseCaseUtil {
private static final String TAG = "StreamUseCaseUtil";
public static final Config.Option<Long> STREAM_USE_CASE_STREAM_SPEC_OPTION =
Config.Option.create("camera2.streamSpec.streamUseCase", long.class);
private static final Map<Long, Set<UseCaseConfigFactory.CaptureType>>
STREAM_USE_CASE_TO_ELIGIBLE_CAPTURE_TYPES_MAP = new HashMap<>();
private static final Map<Long, Set<UseCaseConfigFactory.CaptureType>>
STREAM_USE_CASE_TO_ELIGIBLE_STREAM_SHARING_CHILDREN_TYPES_MAP = new HashMap<>();
static {
if (Build.VERSION.SDK_INT >= 33) {
Set<UseCaseConfigFactory.CaptureType> captureTypes = new HashSet<>();
captureTypes.add(UseCaseConfigFactory.CaptureType.PREVIEW);
captureTypes.add(UseCaseConfigFactory.CaptureType.METERING_REPEATING);
STREAM_USE_CASE_TO_ELIGIBLE_CAPTURE_TYPES_MAP.put(
Long.valueOf(
CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW_VIDEO_STILL),
captureTypes);
captureTypes = new HashSet<>();
captureTypes.add(UseCaseConfigFactory.CaptureType.PREVIEW);
captureTypes.add(UseCaseConfigFactory.CaptureType.METERING_REPEATING);
captureTypes.add(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS);
STREAM_USE_CASE_TO_ELIGIBLE_CAPTURE_TYPES_MAP.put(
Long.valueOf(CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW),
captureTypes);
captureTypes = new HashSet<>();
captureTypes.add(UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE);
STREAM_USE_CASE_TO_ELIGIBLE_CAPTURE_TYPES_MAP.put(
Long.valueOf(CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_STILL_CAPTURE),
captureTypes);
captureTypes = new HashSet<>();
captureTypes.add(UseCaseConfigFactory.CaptureType.VIDEO_CAPTURE);
STREAM_USE_CASE_TO_ELIGIBLE_CAPTURE_TYPES_MAP.put(
Long.valueOf(CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_VIDEO_RECORD),
captureTypes);
captureTypes = new HashSet<>();
captureTypes.add(UseCaseConfigFactory.CaptureType.PREVIEW);
captureTypes.add(UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE);
captureTypes.add(UseCaseConfigFactory.CaptureType.VIDEO_CAPTURE);
STREAM_USE_CASE_TO_ELIGIBLE_STREAM_SHARING_CHILDREN_TYPES_MAP.put(Long.valueOf(
CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW_VIDEO_STILL),
captureTypes);
captureTypes = new HashSet<>();
captureTypes.add(UseCaseConfigFactory.CaptureType.PREVIEW);
captureTypes.add(UseCaseConfigFactory.CaptureType.VIDEO_CAPTURE);
STREAM_USE_CASE_TO_ELIGIBLE_STREAM_SHARING_CHILDREN_TYPES_MAP.put(Long.valueOf(
CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_VIDEO_RECORD),
captureTypes);
}
}
private StreamUseCaseUtil() {
}
/**
* Populates the mapping between surfaces of a capture session and the Stream Use Case of their
* associated stream.
*
* @param sessionConfigs collection of all session configs for this capture session
* @param streamUseCaseMap the mapping between surfaces and Stream Use Case flag
*/
@OptIn(markerClass = ExperimentalCamera2Interop.class)
public static void populateSurfaceToStreamUseCaseMapping(
@NonNull Collection<SessionConfig> sessionConfigs,
@NonNull Collection<UseCaseConfig<?>> useCaseConfigs,
@NonNull Map<DeferrableSurface, Long> streamUseCaseMap) {
int position = 0;
boolean hasStreamUseCase = false;
ArrayList<UseCaseConfig<?>> useCaseConfigArrayList = new ArrayList<>(useCaseConfigs);
for (SessionConfig sessionConfig : sessionConfigs) {
if (sessionConfig.getImplementationOptions().containsOption(
STREAM_USE_CASE_STREAM_SPEC_OPTION)
&& sessionConfig.getSurfaces().size() != 1) {
Logger.e(TAG, String.format("SessionConfig has stream use case but also contains "
+ "%d surfaces, abort populateSurfaceToStreamUseCaseMapping().",
sessionConfig.getSurfaces().size()));
return;
}
if (sessionConfig.getImplementationOptions().containsOption(
STREAM_USE_CASE_STREAM_SPEC_OPTION)) {
hasStreamUseCase = true;
break;
}
}
if (hasStreamUseCase) {
for (SessionConfig sessionConfig : sessionConfigs) {
if (useCaseConfigArrayList.get(position).getCaptureType()
== UseCaseConfigFactory.CaptureType.METERING_REPEATING) {
// MeteringRepeating is attached after the StreamUseCase population logic and
// therefore won't have the StreamUseCase option. It should always have
// SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW
Preconditions.checkState(!sessionConfig.getSurfaces().isEmpty(),
"MeteringRepeating should contain a surface");
streamUseCaseMap.put(sessionConfig.getSurfaces().get(0),
Long.valueOf(CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW));
} else if (sessionConfig.getImplementationOptions().containsOption(
STREAM_USE_CASE_STREAM_SPEC_OPTION)) {
if (!sessionConfig.getSurfaces().isEmpty()) {
streamUseCaseMap.put(sessionConfig.getSurfaces().get(0),
sessionConfig.getImplementationOptions().retrieveOption(
STREAM_USE_CASE_STREAM_SPEC_OPTION));
}
}
position++;
}
}
}
/**
* Populate all implementation options needed to determine the StreamUseCase option in the
* StreamSpec for this UseCaseConfig
*/
@OptIn(markerClass = ExperimentalCamera2Interop.class)
@NonNull
public static Camera2ImplConfig getStreamSpecImplementationOptions(
@NonNull UseCaseConfig<?> useCaseConfig
) {
MutableOptionsBundle optionsBundle = MutableOptionsBundle.create();
if (useCaseConfig.containsOption(Camera2ImplConfig.STREAM_USE_CASE_OPTION)) {
optionsBundle.insertOption(
Camera2ImplConfig.STREAM_USE_CASE_OPTION,
useCaseConfig.retrieveOption(Camera2ImplConfig.STREAM_USE_CASE_OPTION)
);
}
if (useCaseConfig.containsOption(UseCaseConfig.OPTION_ZSL_DISABLED)) {
optionsBundle.insertOption(
UseCaseConfig.OPTION_ZSL_DISABLED,
useCaseConfig.retrieveOption(UseCaseConfig.OPTION_ZSL_DISABLED)
);
}
if (useCaseConfig.containsOption(ImageCaptureConfig.OPTION_IMAGE_CAPTURE_MODE)) {
optionsBundle.insertOption(
ImageCaptureConfig.OPTION_IMAGE_CAPTURE_MODE,
useCaseConfig
.retrieveOption(ImageCaptureConfig.OPTION_IMAGE_CAPTURE_MODE)
);
}
if (useCaseConfig.containsOption(UseCaseConfig.OPTION_INPUT_FORMAT)) {
optionsBundle.insertOption(
UseCaseConfig.OPTION_INPUT_FORMAT,
useCaseConfig
.retrieveOption(UseCaseConfig.OPTION_INPUT_FORMAT)
);
}
return new Camera2ImplConfig(optionsBundle);
}
/**
* Return true if the given camera characteristics support stream use case
*/
@OptIn(markerClass = ExperimentalCamera2Interop.class)
public static boolean isStreamUseCaseSupported(
@NonNull CameraCharacteristicsCompat characteristicsCompat) {
if (Build.VERSION.SDK_INT < 33) {
return false;
}
long[] availableStreamUseCases = characteristicsCompat.get(
CameraCharacteristics.SCALER_AVAILABLE_STREAM_USE_CASES);
if (availableStreamUseCases == null || availableStreamUseCases.length == 0) {
return false;
}
return true;
}
/**
* Return true if the given feature settings is appropriate for stream use case usage.
*/
public static boolean shouldUseStreamUseCase(
@NonNull SupportedSurfaceCombination.FeatureSettings featureSettings) {
return featureSettings.getCameraMode() == CameraMode.DEFAULT
&& featureSettings.getRequiredMaxBitDepth() == DynamicRange.BIT_DEPTH_8_BIT;
}
/**
* Populate the {@link STREAM_USE_CASE_STREAM_SPEC_OPTION} option in StreamSpecs for both
* existing UseCases and new UseCases to be attached. This option will be written into the
* session configurations of the UseCases. When creating a new capture session during
* downstream, it will be used to set the StreamUseCase flag via
* {@link android.hardware.camera2.params.OutputConfiguration#setStreamUseCase(long)}
*
* @param characteristicsCompat the camera characteristics of the device
* @param attachedSurfaces surface info of the already attached use cases
* @param suggestedStreamSpecMap the UseCaseConfig-to-StreamSpec map for new use cases
* @param attachedSurfaceStreamSpecMap the SurfaceInfo-to-StreamSpec map for attached use cases
* whose StreamSpecs needs to be updated
* @return true if StreamSpec options are populated. False if not.
*/
@OptIn(markerClass = ExperimentalCamera2Interop.class)
public static boolean populateStreamUseCaseStreamSpecOptionWithInteropOverride(
@NonNull CameraCharacteristicsCompat characteristicsCompat,
@NonNull List<AttachedSurfaceInfo> attachedSurfaces,
@NonNull Map<UseCaseConfig<?>, StreamSpec> suggestedStreamSpecMap,
@NonNull Map<AttachedSurfaceInfo, StreamSpec> attachedSurfaceStreamSpecMap) {
if (Build.VERSION.SDK_INT < 33) {
return false;
}
List<UseCaseConfig<?>> newUseCaseConfigs = new ArrayList<>(suggestedStreamSpecMap.keySet());
// All AttachedSurfaceInfo should have implementation options
for (AttachedSurfaceInfo attachedSurfaceInfo : attachedSurfaces) {
Preconditions.checkNotNull(attachedSurfaceInfo.getImplementationOptions());
}
// All StreamSpecs in the map should have implementation options
for (UseCaseConfig<?> useCaseConfig : newUseCaseConfigs) {
Preconditions.checkNotNull(Preconditions.checkNotNull(
suggestedStreamSpecMap.get(useCaseConfig)).getImplementationOptions());
}
long[] availableStreamUseCases = characteristicsCompat.get(
CameraCharacteristics.SCALER_AVAILABLE_STREAM_USE_CASES);
if (availableStreamUseCases == null || availableStreamUseCases.length == 0) {
return false;
}
Set<Long> availableStreamUseCaseSet = new HashSet<>();
for (Long availableStreamUseCase : availableStreamUseCases) {
availableStreamUseCaseSet.add(availableStreamUseCase);
}
if (isValidCamera2InteropOverride(attachedSurfaces, newUseCaseConfigs,
availableStreamUseCaseSet)) {
for (AttachedSurfaceInfo attachedSurfaceInfo : attachedSurfaces) {
Config oldImplementationOptions = attachedSurfaceInfo.getImplementationOptions();
Config newImplementationOptions =
getUpdatedImplementationOptionsWithUseCaseStreamSpecOption(
oldImplementationOptions,
oldImplementationOptions.retrieveOption(
Camera2ImplConfig.STREAM_USE_CASE_OPTION));
if (newImplementationOptions != null) {
attachedSurfaceStreamSpecMap.put(attachedSurfaceInfo,
attachedSurfaceInfo.toStreamSpec(newImplementationOptions));
}
}
for (UseCaseConfig<?> newUseCaseConfig : newUseCaseConfigs) {
StreamSpec oldStreamSpec = suggestedStreamSpecMap.get(newUseCaseConfig);
Config oldImplementationOptions = oldStreamSpec.getImplementationOptions();
Config newImplementationOptions =
getUpdatedImplementationOptionsWithUseCaseStreamSpecOption(
oldImplementationOptions, oldImplementationOptions.retrieveOption(
Camera2ImplConfig.STREAM_USE_CASE_OPTION));
if (newImplementationOptions != null) {
StreamSpec newStreamSpec =
oldStreamSpec.toBuilder().setImplementationOptions(
newImplementationOptions).build();
suggestedStreamSpecMap.put(newUseCaseConfig, newStreamSpec);
}
}
return true;
}
return false;
}
/**
* Return true if the stream use cases in the given surface configurations are available for
* the device.
*/
public static boolean areStreamUseCasesAvailableForSurfaceConfigs(
@NonNull CameraCharacteristicsCompat characteristicsCompat,
@NonNull List<SurfaceConfig> surfaceConfigs) {
if (Build.VERSION.SDK_INT < 33) {
return false;
}
long[] availableStreamUseCases = characteristicsCompat.get(
CameraCharacteristics.SCALER_AVAILABLE_STREAM_USE_CASES);
if (availableStreamUseCases == null || availableStreamUseCases.length == 0) {
return false;
}
Set<Long> availableStreamUseCaseSet = new HashSet<>();
for (Long availableStreamUseCase : availableStreamUseCases) {
availableStreamUseCaseSet.add(availableStreamUseCase);
}
for (SurfaceConfig surfaceConfig : surfaceConfigs) {
if (!availableStreamUseCaseSet.contains(surfaceConfig.getStreamUseCase())) {
return false;
}
}
return true;
}
/**
* Return true if the given capture type and stream use case are a eligible pair. If the
* given captureType is STREAM_SHARING, checks the streamSharingTypes, which are the capture
* types of the children, are eligible with the stream use case.
*/
private static boolean isEligibleCaptureType(UseCaseConfigFactory.CaptureType captureType,
long streamUseCase, List<UseCaseConfigFactory.CaptureType> streamSharingTypes) {
if (Build.VERSION.SDK_INT < 33) {
return false;
}
if (captureType == UseCaseConfigFactory.CaptureType.STREAM_SHARING) {
if (!STREAM_USE_CASE_TO_ELIGIBLE_STREAM_SHARING_CHILDREN_TYPES_MAP.containsKey(
streamUseCase)) {
return false;
}
Set<UseCaseConfigFactory.CaptureType> captureTypes =
STREAM_USE_CASE_TO_ELIGIBLE_STREAM_SHARING_CHILDREN_TYPES_MAP.get(
streamUseCase);
if (streamSharingTypes.size() != captureTypes.size()) {
return false;
}
for (UseCaseConfigFactory.CaptureType childType : streamSharingTypes) {
if (!captureTypes.contains(childType)) {
return false;
}
}
return true;
} else {
return STREAM_USE_CASE_TO_ELIGIBLE_CAPTURE_TYPES_MAP.containsKey(streamUseCase)
&& STREAM_USE_CASE_TO_ELIGIBLE_CAPTURE_TYPES_MAP.get(streamUseCase).contains(
captureType);
}
}
/**
* Return true if the stream use cases contained in surfaceConfigsWithStreamUseCases all have
* eligible capture type pairing with the use cases that these surfaceConfigs are constructed
* from.
*
* @param surfaceConfigIndexAttachedSurfaceInfoMap mapping between an surfaceConfig's index
* in the list and the attachedSurfaceInfo it
* is constructed from
* @param surfaceConfigIndexUseCaseConfigMap mapping between an surfaceConfig's index
* * in the list and the useCaseConfig it is
* constructed from
* @param surfaceConfigsWithStreamUseCase the supported surfaceConfigs that contains
* accurate streamUseCases
*/
public static boolean areCaptureTypesEligible(
@NonNull Map<Integer, AttachedSurfaceInfo> surfaceConfigIndexAttachedSurfaceInfoMap,
@NonNull Map<Integer, UseCaseConfig<?>> surfaceConfigIndexUseCaseConfigMap,
@NonNull List<SurfaceConfig> surfaceConfigsWithStreamUseCase) {
for (int i = 0; i < surfaceConfigsWithStreamUseCase.size(); i++) {
// Verify that the use case has the eligible capture type the given stream use case.
long streamUseCase = surfaceConfigsWithStreamUseCase.get(i).getStreamUseCase();
if (surfaceConfigIndexAttachedSurfaceInfoMap.containsKey(i)) {
AttachedSurfaceInfo attachedSurfaceInfo =
surfaceConfigIndexAttachedSurfaceInfoMap.get(i);
if (!isEligibleCaptureType(attachedSurfaceInfo.getCaptureTypes().size() == 1
? attachedSurfaceInfo.getCaptureTypes().get(0) :
UseCaseConfigFactory.CaptureType.STREAM_SHARING, streamUseCase,
attachedSurfaceInfo.getCaptureTypes())) {
return false;
}
} else if (surfaceConfigIndexUseCaseConfigMap.containsKey(i)) {
UseCaseConfig<?> newUseCaseConfig =
surfaceConfigIndexUseCaseConfigMap.get(i);
if (!isEligibleCaptureType(newUseCaseConfig.getCaptureType(), streamUseCase,
newUseCaseConfig.getCaptureType()
== UseCaseConfigFactory.CaptureType.STREAM_SHARING
? ((StreamSharingConfig) newUseCaseConfig).getCaptureTypes()
: Collections.emptyList())) {
return false;
}
} else {
throw new AssertionError("SurfaceConfig does not map to any use case");
}
}
return true;
}
/**
* @param suggestedStreamSpecMap mapping between useCaseConfig and its
* streamSpecs
* @param attachedSurfaceStreamSpecMap mapping between attachedSurfaceInfo and its
* streamSpecs that contains streamUseCases.
* All streamSpecs in this map has
* streamUseCases
* @param surfaceConfigIndexAttachedSurfaceInfoMap mapping between an surfaceConfig's index
* in the list and the
* attachedSurfaceInfo it
* is constructed from
*@param surfaceConfigIndexUseCaseConfigMap mapping between an surfaceConfig's
* index in the list and the useCaseConfig
* it is constructed from
* @param surfaceConfigsWithStreamUseCase the supported surfaceConfigs that contains
* accurate streamUseCases
*/
public static void populateStreamUseCaseStreamSpecOptionWithSupportedSurfaceConfigs(
@NonNull Map<UseCaseConfig<?>, StreamSpec> suggestedStreamSpecMap,
@NonNull Map<AttachedSurfaceInfo, StreamSpec> attachedSurfaceStreamSpecMap,
@NonNull Map<Integer, AttachedSurfaceInfo> surfaceConfigIndexAttachedSurfaceInfoMap,
@NonNull Map<Integer, UseCaseConfig<?>> surfaceConfigIndexUseCaseConfigMap,
@NonNull List<SurfaceConfig> surfaceConfigsWithStreamUseCase) {
// Populate StreamSpecs with stream use cases.
for (int i = 0; i < surfaceConfigsWithStreamUseCase.size(); i++) {
long streamUseCase = surfaceConfigsWithStreamUseCase.get(i).getStreamUseCase();
if (surfaceConfigIndexAttachedSurfaceInfoMap.containsKey(i)) {
AttachedSurfaceInfo attachedSurfaceInfo =
surfaceConfigIndexAttachedSurfaceInfoMap.get(i);
Config oldImplementationOptions = attachedSurfaceInfo.getImplementationOptions();
Config newImplementationOptions =
getUpdatedImplementationOptionsWithUseCaseStreamSpecOption(
oldImplementationOptions, streamUseCase);
if (newImplementationOptions != null) {
attachedSurfaceStreamSpecMap.put(attachedSurfaceInfo,
attachedSurfaceInfo.toStreamSpec(newImplementationOptions));
}
} else if (surfaceConfigIndexUseCaseConfigMap.containsKey(i)) {
UseCaseConfig<?> newUseCaseConfig =
surfaceConfigIndexUseCaseConfigMap.get(i);
StreamSpec oldStreamSpec = suggestedStreamSpecMap.get(newUseCaseConfig);
Config oldImplementationOptions = oldStreamSpec.getImplementationOptions();
Config newImplementationOptions =
getUpdatedImplementationOptionsWithUseCaseStreamSpecOption(
oldImplementationOptions, streamUseCase);
if (newImplementationOptions != null) {
StreamSpec newStreamSpec =
oldStreamSpec.toBuilder().setImplementationOptions(
newImplementationOptions).build();
suggestedStreamSpecMap.put(newUseCaseConfig, newStreamSpec);
}
} else {
throw new AssertionError("SurfaceConfig does not map to any use case");
}
}
}
/**
* Given an old options, return a new option with stream use case stream spec option inserted
*/
@Nullable
@OptIn(markerClass = ExperimentalCamera2Interop.class)
private static Config getUpdatedImplementationOptionsWithUseCaseStreamSpecOption(
Config oldImplementationOptions,
long streamUseCase
) {
if (oldImplementationOptions.containsOption(STREAM_USE_CASE_STREAM_SPEC_OPTION)
&& oldImplementationOptions.retrieveOption(STREAM_USE_CASE_STREAM_SPEC_OPTION)
== streamUseCase) {
// The old options already has the same stream use case. No need to update
return null;
}
MutableOptionsBundle optionsBundle =
MutableOptionsBundle.from(oldImplementationOptions);
optionsBundle.insertOption(STREAM_USE_CASE_STREAM_SPEC_OPTION, streamUseCase);
return new Camera2ImplConfig(optionsBundle);
}
/**
* Return true if any one of the existing or new UseCases is ZSL.
*/
public static boolean containsZslUseCase(
@NonNull List<AttachedSurfaceInfo> attachedSurfaces,
@NonNull List<UseCaseConfig<?>> newUseCaseConfigs) {
for (AttachedSurfaceInfo attachedSurfaceInfo : attachedSurfaces) {
List<UseCaseConfigFactory.CaptureType> captureTypes =
attachedSurfaceInfo.getCaptureTypes();
UseCaseConfigFactory.CaptureType captureType = captureTypes.get(0);
if (isZslUseCase(
attachedSurfaceInfo.getImplementationOptions(),
captureType)) {
return true;
}
}
for (UseCaseConfig<?> useCaseConfig : newUseCaseConfigs) {
if (isZslUseCase(useCaseConfig, useCaseConfig.getCaptureType())) {
return true;
}
}
return false;
}
/**
* Check whether a UseCase is ZSL.
*/
private static boolean isZslUseCase(Config config,
UseCaseConfigFactory.CaptureType captureType) {
if (config.retrieveOption(UseCaseConfig.OPTION_ZSL_DISABLED, false)) {
return false;
}
// Skip if capture mode doesn't exist in the options
if (!config.containsOption(ImageCaptureConfig.OPTION_IMAGE_CAPTURE_MODE)) {
return false;
}
@ImageCapture.CaptureMode int captureMode =
config.retrieveOption(ImageCaptureConfig.OPTION_IMAGE_CAPTURE_MODE);
return TemplateTypeUtil.getSessionConfigTemplateType(captureType, captureMode)
== CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG;
}
/**
* Check whether the given StreamUseCases are available to the device.
*/
private static boolean areStreamUseCasesAvailable(Set<Long> availableStreamUseCasesSet,
Set<Long> streamUseCases) {
for (Long streamUseCase : streamUseCases) {
if (!availableStreamUseCasesSet.contains(streamUseCase)) {
return false;
}
}
return true;
}
private static void throwInvalidCamera2InteropOverrideException() {
throw new IllegalArgumentException("Either all use cases must have non-default stream use "
+ "case assigned or none should have it");
}
/**
* Return true if all existing UseCases and new UseCases have Camera2Interop override and
* these StreamUseCases are all available to the device.
*/
@OptIn(markerClass = ExperimentalCamera2Interop.class)
private static boolean isValidCamera2InteropOverride(
List<AttachedSurfaceInfo> attachedSurfaces,
List<UseCaseConfig<?>> newUseCaseConfigs,
Set<Long> availableStreamUseCases) {
Set<Long> streamUseCases = new HashSet<>();
boolean hasNonDefaultStreamUseCase = false;
boolean hasDefaultOrNullStreamUseCase = false;
for (AttachedSurfaceInfo attachedSurfaceInfo : attachedSurfaces) {
if (!attachedSurfaceInfo.getImplementationOptions().containsOption(
Camera2ImplConfig.STREAM_USE_CASE_OPTION)) {
hasDefaultOrNullStreamUseCase = true;
break;
}
long streamUseCaseOverride =
attachedSurfaceInfo.getImplementationOptions()
.retrieveOption(Camera2ImplConfig.STREAM_USE_CASE_OPTION);
if (streamUseCaseOverride
== CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT) {
hasDefaultOrNullStreamUseCase = true;
break;
}
hasNonDefaultStreamUseCase = true;
break;
}
for (UseCaseConfig<?> useCaseConfig : newUseCaseConfigs) {
if (!useCaseConfig.containsOption(Camera2ImplConfig.STREAM_USE_CASE_OPTION)) {
hasDefaultOrNullStreamUseCase = true;
if (hasNonDefaultStreamUseCase) {
throwInvalidCamera2InteropOverrideException();
}
} else {
long streamUseCaseOverride =
useCaseConfig.retrieveOption(Camera2ImplConfig.STREAM_USE_CASE_OPTION);
if (streamUseCaseOverride
== CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT) {
hasDefaultOrNullStreamUseCase = true;
if (hasNonDefaultStreamUseCase) {
throwInvalidCamera2InteropOverrideException();
}
} else {
hasNonDefaultStreamUseCase = true;
if (hasDefaultOrNullStreamUseCase) {
throwInvalidCamera2InteropOverrideException();
}
streamUseCases.add(streamUseCaseOverride);
}
}
}
return !hasDefaultOrNullStreamUseCase && areStreamUseCasesAvailable(availableStreamUseCases,
streamUseCases);
}
}