blob: fb81b10770b1a7e4c98f9561b1e0ef2c6415441d [file] [log] [blame]
/*
* Copyright 2023 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.pipe.integration.internal
import android.hardware.camera2.CameraCharacteristics
import android.hardware.camera2.CameraDevice
import android.hardware.camera2.CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT
import android.hardware.camera2.CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW
import android.hardware.camera2.CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW_VIDEO_STILL
import android.hardware.camera2.CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_STILL_CAPTURE
import android.hardware.camera2.CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_VIDEO_RECORD
import android.os.Build
import androidx.annotation.RequiresApi
import androidx.annotation.VisibleForTesting
import androidx.camera.camera2.pipe.CameraMetadata
import androidx.camera.camera2.pipe.core.Log
import androidx.camera.camera2.pipe.integration.adapter.SupportedSurfaceCombination
import androidx.camera.camera2.pipe.integration.impl.Camera2ImplConfig
import androidx.camera.camera2.pipe.integration.impl.STREAM_USE_CASE_OPTION
import androidx.camera.core.DynamicRange
import androidx.camera.core.ExperimentalZeroShutterLag
import androidx.camera.core.ImageCapture
import androidx.camera.core.ImageCapture.CaptureMode
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.CaptureType
import androidx.camera.core.streamsharing.StreamSharingConfig
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
object StreamUseCaseUtil {
@VisibleForTesting
val STREAM_USE_CASE_STREAM_SPEC_OPTION: Config.Option<Long> = Config.Option.create(
"camera2.streamSpec.streamUseCase",
Long::class.javaPrimitiveType!!
)
private val STREAM_USE_CASE_TO_ELIGIBLE_CAPTURE_TYPES_MAP:
Map<Long, Set<CaptureType>> = buildMap {
if (Build.VERSION.SDK_INT >= 33) {
put(
SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW_VIDEO_STILL.toLong(),
setOf(CaptureType.PREVIEW)
)
put(
SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW.toLong(),
setOf(CaptureType.PREVIEW, CaptureType.IMAGE_ANALYSIS)
)
put(
SCALER_AVAILABLE_STREAM_USE_CASES_STILL_CAPTURE.toLong(),
setOf(CaptureType.IMAGE_CAPTURE)
)
put(
SCALER_AVAILABLE_STREAM_USE_CASES_VIDEO_RECORD.toLong(),
setOf(CaptureType.VIDEO_CAPTURE)
)
}
}
private val STREAM_USE_CASE_TO_ELIGIBLE_STREAM_SHARING_CHILDREN_TYPES_MAP:
Map<Long, Set<CaptureType>> = buildMap {
if (Build.VERSION.SDK_INT >= 33) {
put(
SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW_VIDEO_STILL.toLong(),
setOf(CaptureType.PREVIEW, CaptureType.IMAGE_CAPTURE, CaptureType.VIDEO_CAPTURE)
)
put(
SCALER_AVAILABLE_STREAM_USE_CASES_VIDEO_RECORD.toLong(),
setOf(CaptureType.PREVIEW, CaptureType.VIDEO_CAPTURE)
)
}
}
/**
* 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
*/
fun populateSurfaceToStreamUseCaseMapping(
sessionConfigs: Collection<SessionConfig>,
useCaseConfigs: Collection<UseCaseConfig<*>>,
streamUseCaseMap: MutableMap<DeferrableSurface, Long>
) {
var position = 0
var hasStreamUseCase = false
val useCaseConfigArrayList = ArrayList(useCaseConfigs)
for (sessionConfig: SessionConfig in sessionConfigs) {
if (sessionConfig.implementationOptions.containsOption(
STREAM_USE_CASE_STREAM_SPEC_OPTION
) && sessionConfig.surfaces.size != 1
) {
Log.error {
"StreamUseCaseUtil: SessionConfig has stream use case but also contains " +
"${sessionConfig.surfaces.size} surfaces, " +
"abort populateSurfaceToStreamUseCaseMapping()."
}
return
}
if (sessionConfig.implementationOptions.containsOption(
STREAM_USE_CASE_STREAM_SPEC_OPTION
)
) {
hasStreamUseCase = true
break
}
}
if (hasStreamUseCase) {
for (sessionConfig: SessionConfig in sessionConfigs) {
if ((useCaseConfigArrayList[position].captureType
== 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
streamUseCaseMap[sessionConfig.surfaces[0]] =
SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW.toLong()
} else if (sessionConfig.implementationOptions.containsOption(
STREAM_USE_CASE_STREAM_SPEC_OPTION
)
) {
streamUseCaseMap[sessionConfig.surfaces[0]] =
sessionConfig.implementationOptions.retrieveOption(
STREAM_USE_CASE_STREAM_SPEC_OPTION
)!!
}
position++
}
}
}
/**
* Populate all implementation options needed to determine the StreamUseCase option in the
* StreamSpec for this UseCaseConfig
*/
fun getStreamSpecImplementationOptions(
useCaseConfig: UseCaseConfig<*>
): Camera2ImplConfig {
val optionsBundle = MutableOptionsBundle.create()
if (useCaseConfig.containsOption(STREAM_USE_CASE_OPTION)) {
optionsBundle.insertOption(
STREAM_USE_CASE_OPTION,
useCaseConfig.retrieveOption(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 Camera2ImplConfig(optionsBundle)
}
/**
* Return true if the given camera characteristics support stream use case
*/
fun isStreamUseCaseSupported(
cameraMetadata: CameraMetadata
): Boolean {
if (Build.VERSION.SDK_INT < 33) {
return false
}
val availableStreamUseCases: LongArray? =
cameraMetadata[CameraCharacteristics.SCALER_AVAILABLE_STREAM_USE_CASES]
return !(availableStreamUseCases == null || availableStreamUseCases.isEmpty())
}
/**
* Return true if the given feature settings is appropriate for stream use case usage.
*/
fun shouldUseStreamUseCase(
featureSettings: SupportedSurfaceCombination.FeatureSettings
): Boolean {
return (featureSettings.cameraMode == CameraMode.DEFAULT &&
featureSettings.requiredMaxBitDepth == DynamicRange.BIT_DEPTH_8_BIT)
}
/**
* Populate the [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
* [android.hardware.camera2.params.OutputConfiguration.setStreamUseCase]
*
* @param cameraMetadata 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.
*/
fun populateStreamUseCaseStreamSpecOptionWithInteropOverride(
cameraMetadata: CameraMetadata,
attachedSurfaces: List<AttachedSurfaceInfo>,
suggestedStreamSpecMap: MutableMap<UseCaseConfig<*>, StreamSpec>,
attachedSurfaceStreamSpecMap: MutableMap<AttachedSurfaceInfo, StreamSpec>
): Boolean {
if (Build.VERSION.SDK_INT < 33) {
return false
}
val newUseCaseConfigs: List<UseCaseConfig<*>> =
java.util.ArrayList(suggestedStreamSpecMap.keys)
// All AttachedSurfaceInfo should have implementation options
for (attachedSurfaceInfo in attachedSurfaces) {
checkNotNull(attachedSurfaceInfo.implementationOptions)
}
// All StreamSpecs in the map should have implementation options
for (useCaseConfig in newUseCaseConfigs) {
checkNotNull(
checkNotNull(
suggestedStreamSpecMap[useCaseConfig]
).implementationOptions
)
}
val availableStreamUseCases: LongArray? =
cameraMetadata[CameraCharacteristics.SCALER_AVAILABLE_STREAM_USE_CASES]
if (availableStreamUseCases == null || availableStreamUseCases.isEmpty()) {
return false
}
val availableStreamUseCaseSet: MutableSet<Long> = HashSet()
for (availableStreamUseCase in availableStreamUseCases) {
availableStreamUseCaseSet.add(availableStreamUseCase)
}
if (isValidCamera2InteropOverride(
attachedSurfaces, newUseCaseConfigs,
availableStreamUseCaseSet
)
) {
for (attachedSurfaceInfo in attachedSurfaces) {
val oldImplementationOptions = attachedSurfaceInfo.implementationOptions
getUpdatedImplementationOptionsWithUseCaseStreamSpecOption(
oldImplementationOptions!!,
oldImplementationOptions.retrieveOption(STREAM_USE_CASE_OPTION)
)?.also {
attachedSurfaceStreamSpecMap[attachedSurfaceInfo] =
attachedSurfaceInfo.toStreamSpec(it)
}
}
for (newUseCaseConfig in newUseCaseConfigs) {
val oldStreamSpec = suggestedStreamSpecMap[newUseCaseConfig]
val oldImplementationOptions = oldStreamSpec!!.implementationOptions
getUpdatedImplementationOptionsWithUseCaseStreamSpecOption(
oldImplementationOptions!!,
oldImplementationOptions.retrieveOption(STREAM_USE_CASE_OPTION)
)?.also {
suggestedStreamSpecMap[newUseCaseConfig] =
oldStreamSpec.toBuilder().setImplementationOptions(it).build()
}
}
return true
}
return false
}
/**
* Return true if the stream use cases in the given surface configurations are available for
* the device.
*/
fun areStreamUseCasesAvailableForSurfaceConfigs(
cameraMetadata: CameraMetadata,
surfaceConfigs: List<SurfaceConfig>
): Boolean {
if (Build.VERSION.SDK_INT < 33) {
return false
}
val availableStreamUseCases: LongArray? =
cameraMetadata[CameraCharacteristics.SCALER_AVAILABLE_STREAM_USE_CASES]
if (availableStreamUseCases == null || availableStreamUseCases.isEmpty()) {
return false
}
val availableStreamUseCaseSet: MutableSet<Long> = java.util.HashSet()
for (availableStreamUseCase in availableStreamUseCases) {
availableStreamUseCaseSet.add(availableStreamUseCase)
}
for (surfaceConfig in surfaceConfigs) {
if (!availableStreamUseCaseSet.contains(surfaceConfig.streamUseCase)) {
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 fun isEligibleCaptureType(
captureType: CaptureType,
streamUseCase: Long,
streamSharingTypes: List<CaptureType>
): Boolean {
if (Build.VERSION.SDK_INT < 33) {
return false
}
return if (captureType == CaptureType.STREAM_SHARING) {
if (!STREAM_USE_CASE_TO_ELIGIBLE_STREAM_SHARING_CHILDREN_TYPES_MAP.containsKey(
streamUseCase
)
) {
return false
}
val captureTypes: Set<CaptureType> =
STREAM_USE_CASE_TO_ELIGIBLE_STREAM_SHARING_CHILDREN_TYPES_MAP[streamUseCase]!!
if (streamSharingTypes.size != captureTypes.size) {
return false
}
for (childType in streamSharingTypes) {
if (!captureTypes.contains(childType)) {
return false
}
}
true
} else {
STREAM_USE_CASE_TO_ELIGIBLE_CAPTURE_TYPES_MAP.containsKey(streamUseCase) &&
STREAM_USE_CASE_TO_ELIGIBLE_CAPTURE_TYPES_MAP[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
*/
fun areCaptureTypesEligible(
surfaceConfigIndexAttachedSurfaceInfoMap: Map<Int, AttachedSurfaceInfo?>,
surfaceConfigIndexUseCaseConfigMap: Map<Int, UseCaseConfig<*>>,
surfaceConfigsWithStreamUseCase: List<SurfaceConfig>
): Boolean {
for (i in surfaceConfigsWithStreamUseCase.indices) {
// Verify that the use case has the eligible capture type the given stream use case.
val streamUseCase = surfaceConfigsWithStreamUseCase[i].streamUseCase
if (surfaceConfigIndexAttachedSurfaceInfoMap.containsKey(i)) {
val attachedSurfaceInfo = surfaceConfigIndexAttachedSurfaceInfoMap[i]
if (!isEligibleCaptureType(
if (attachedSurfaceInfo!!.captureTypes.size == 1)
attachedSurfaceInfo.captureTypes[0] else CaptureType.STREAM_SHARING,
streamUseCase,
attachedSurfaceInfo.captureTypes
)
) {
return false
}
} else if (surfaceConfigIndexUseCaseConfigMap.containsKey(i)) {
val newUseCaseConfig = surfaceConfigIndexUseCaseConfigMap[i]!!
if (!isEligibleCaptureType(
newUseCaseConfig.captureType, streamUseCase,
if (newUseCaseConfig.captureType
== CaptureType.STREAM_SHARING
) (newUseCaseConfig as StreamSharingConfig).captureTypes else emptyList()
)
) {
return false
}
} else {
throw 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
*/
fun populateStreamUseCaseStreamSpecOptionWithSupportedSurfaceConfigs(
suggestedStreamSpecMap: MutableMap<UseCaseConfig<*>, StreamSpec>,
attachedSurfaceStreamSpecMap: MutableMap<AttachedSurfaceInfo, StreamSpec>,
surfaceConfigIndexAttachedSurfaceInfoMap: Map<Int, AttachedSurfaceInfo>,
surfaceConfigIndexUseCaseConfigMap: Map<Int, UseCaseConfig<*>>,
surfaceConfigsWithStreamUseCase: List<SurfaceConfig>
) {
// Populate StreamSpecs with stream use cases.
for (i in surfaceConfigsWithStreamUseCase.indices) {
val streamUseCase = surfaceConfigsWithStreamUseCase[i].streamUseCase
if (surfaceConfigIndexAttachedSurfaceInfoMap.containsKey(i)) {
val attachedSurfaceInfo = surfaceConfigIndexAttachedSurfaceInfoMap[i]
val oldImplementationOptions = attachedSurfaceInfo!!.implementationOptions
val newImplementationOptions: Config? =
getUpdatedImplementationOptionsWithUseCaseStreamSpecOption(
oldImplementationOptions!!, streamUseCase
)
if (newImplementationOptions != null) {
attachedSurfaceStreamSpecMap[attachedSurfaceInfo] =
attachedSurfaceInfo.toStreamSpec(newImplementationOptions)
}
} else if (surfaceConfigIndexUseCaseConfigMap.containsKey(i)) {
val newUseCaseConfig: UseCaseConfig<*> = surfaceConfigIndexUseCaseConfigMap[i]!!
val oldStreamSpec = suggestedStreamSpecMap[newUseCaseConfig]
val oldImplementationOptions = oldStreamSpec!!.implementationOptions
val newImplementationOptions: Config? =
getUpdatedImplementationOptionsWithUseCaseStreamSpecOption(
oldImplementationOptions!!, streamUseCase
)
if (newImplementationOptions != null) {
val newStreamSpec = oldStreamSpec.toBuilder().setImplementationOptions(
newImplementationOptions
).build()
suggestedStreamSpecMap[newUseCaseConfig] = newStreamSpec
}
} else {
throw 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
*/
private fun getUpdatedImplementationOptionsWithUseCaseStreamSpecOption(
oldImplementationOptions: Config,
streamUseCase: Long?
): Config? {
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
}
val optionsBundle = MutableOptionsBundle.from(oldImplementationOptions)
optionsBundle.insertOption(STREAM_USE_CASE_STREAM_SPEC_OPTION, streamUseCase)
return Camera2ImplConfig(optionsBundle)
}
/**
* Return true if any one of the existing or new UseCases is ZSL.
*/
fun containsZslUseCase(
attachedSurfaces: List<AttachedSurfaceInfo>,
newUseCaseConfigs: List<UseCaseConfig<*>>
): Boolean {
for (attachedSurfaceInfo: AttachedSurfaceInfo in attachedSurfaces) {
val captureTypes = attachedSurfaceInfo.captureTypes
val captureType = captureTypes[0]
if (isZslUseCase(
attachedSurfaceInfo.implementationOptions!!,
captureType
)
) {
return true
}
}
for (useCaseConfig: UseCaseConfig<*> in newUseCaseConfigs) {
if (isZslUseCase(useCaseConfig, useCaseConfig.captureType)) {
return true
}
}
return false
}
/**
* Check whether a UseCase is ZSL.
*/
private fun isZslUseCase(
config: Config,
captureType: CaptureType
): Boolean {
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
}
@CaptureMode val captureMode: Int =
config.retrieveOption(ImageCaptureConfig.OPTION_IMAGE_CAPTURE_MODE)!!
return (getSessionConfigTemplateType(captureType, captureMode)
== CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG)
}
/**
* Check whether the given StreamUseCases are available to the device.
*/
private fun areStreamUseCasesAvailable(
availableStreamUseCasesSet: Set<Long>,
streamUseCases: Set<Long>
): Boolean {
for (streamUseCase: Long in streamUseCases) {
if (!availableStreamUseCasesSet.contains(streamUseCase)) {
return false
}
}
return true
}
private fun throwInvalidCamera2InteropOverrideException() {
throw 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.
*/
private fun isValidCamera2InteropOverride(
attachedSurfaces: List<AttachedSurfaceInfo>,
newUseCaseConfigs: List<UseCaseConfig<*>>,
availableStreamUseCases: Set<Long>
): Boolean {
val streamUseCases: MutableSet<Long> = mutableSetOf()
var hasNonDefaultStreamUseCase = false
var hasDefaultOrNullStreamUseCase = false
for (attachedSurfaceInfo: AttachedSurfaceInfo in attachedSurfaces) {
if (!attachedSurfaceInfo.implementationOptions!!.containsOption(
STREAM_USE_CASE_OPTION
)
) {
hasDefaultOrNullStreamUseCase = true
break
}
val streamUseCaseOverride: Long = (attachedSurfaceInfo.implementationOptions!!
.retrieveOption(STREAM_USE_CASE_OPTION))!!
if ((streamUseCaseOverride == SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT.toLong())) {
hasDefaultOrNullStreamUseCase = true
break
}
hasNonDefaultStreamUseCase = true
break
}
for (useCaseConfig: UseCaseConfig<*> in newUseCaseConfigs) {
if (!useCaseConfig.containsOption(STREAM_USE_CASE_OPTION)) {
hasDefaultOrNullStreamUseCase = true
if (hasNonDefaultStreamUseCase) {
throwInvalidCamera2InteropOverrideException()
}
} else {
val streamUseCaseOverride: Long =
useCaseConfig.retrieveOption(STREAM_USE_CASE_OPTION)!!
if ((streamUseCaseOverride == SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT.toLong())
) {
hasDefaultOrNullStreamUseCase = true
if (hasNonDefaultStreamUseCase) {
throwInvalidCamera2InteropOverrideException()
}
} else {
hasNonDefaultStreamUseCase = true
if (hasDefaultOrNullStreamUseCase) {
throwInvalidCamera2InteropOverrideException()
}
streamUseCases.add(streamUseCaseOverride)
}
}
}
return !hasDefaultOrNullStreamUseCase && areStreamUseCasesAvailable(
availableStreamUseCases,
streamUseCases
)
}
@androidx.annotation.OptIn(markerClass = [ExperimentalZeroShutterLag::class])
private fun getSessionConfigTemplateType(
captureType: CaptureType,
@CaptureMode captureMode: Int
): Int {
return when (captureType) {
CaptureType.IMAGE_CAPTURE ->
if (captureMode == ImageCapture.CAPTURE_MODE_ZERO_SHUTTER_LAG)
CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG else CameraDevice.TEMPLATE_PREVIEW
CaptureType.VIDEO_CAPTURE, CaptureType.STREAM_SHARING -> CameraDevice.TEMPLATE_RECORD
CaptureType.PREVIEW, CaptureType.IMAGE_ANALYSIS -> CameraDevice.TEMPLATE_PREVIEW
else -> CameraDevice.TEMPLATE_PREVIEW
}
}
}