blob: 2029c95c351fa40d8355ce551e99bb9091ac5bad [file]
/*
* 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.adapter
import androidx.annotation.RequiresApi
import androidx.annotation.VisibleForTesting
import androidx.camera.camera2.pipe.CameraDevices
import androidx.camera.camera2.pipe.CameraId
import androidx.camera.camera2.pipe.CameraPipe
import androidx.camera.camera2.pipe.core.Log
import androidx.camera.camera2.pipe.integration.internal.CameraCompatibilityFilter.isBackwardCompatible
import androidx.camera.camera2.pipe.integration.interop.Camera2CameraInfo
import androidx.camera.camera2.pipe.integration.interop.ExperimentalCamera2Interop
import androidx.camera.core.CameraInfo
import androidx.camera.core.CameraSelector
import androidx.camera.core.InitializationException
import androidx.camera.core.concurrent.CameraCoordinator
import androidx.camera.core.concurrent.CameraCoordinator.CAMERA_OPERATING_MODE_UNSPECIFIED
import androidx.camera.core.concurrent.CameraCoordinator.CameraOperatingMode
import androidx.camera.core.impl.CameraInternal
@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
class CameraCoordinatorAdapter(
private var cameraPipe: CameraPipe?,
cameraDevices: CameraDevices,
) : CameraCoordinator {
@VisibleForTesting
val cameraInternalMap = mutableMapOf<CameraId, CameraInternalAdapter>()
@VisibleForTesting
var concurrentCameraIdsSet = mutableSetOf<Set<CameraId>>()
@VisibleForTesting
var concurrentCameraIdMap = mutableMapOf<String, MutableList<String>>()
@VisibleForTesting
var activeConcurrentCameraInfosList = mutableListOf<CameraInfo>()
@VisibleForTesting
var concurrentMode: Int = CAMERA_OPERATING_MODE_UNSPECIFIED
@VisibleForTesting
var concurrentModeOn = false
init {
val concurrentCameraIds = cameraDevices.awaitConcurrentCameraIds()!!.toMutableSet()
for (cameraIdSet in concurrentCameraIds) {
val cameraIdsList = cameraIdSet.toList()
if (cameraIdsList.size >= 2) {
val cameraId1: String = cameraIdsList[0].value
val cameraId2: String = cameraIdsList[1].value
var isBackwardCompatible = false
try {
isBackwardCompatible = isBackwardCompatible(cameraId1, cameraDevices) &&
isBackwardCompatible(cameraId2, cameraDevices)
} catch (e: InitializationException) {
Log.debug { "Concurrent camera id pair: " +
"($cameraId1, $cameraId2) is not backward compatible" }
}
if (!isBackwardCompatible) {
continue
}
concurrentCameraIdsSet.add(cameraIdSet)
if (!concurrentCameraIdMap.containsKey(cameraId1)) {
concurrentCameraIdMap[cameraId1] = mutableListOf()
}
if (!concurrentCameraIdMap.containsKey(cameraId2)) {
concurrentCameraIdMap[cameraId2] = mutableListOf()
}
concurrentCameraIdMap[cameraId1]?.add(cameraId2)
concurrentCameraIdMap[cameraId2]?.add(cameraId1)
}
}
}
fun registerCamera(cameraId: String, cameraInternal: CameraInternal) {
cameraInternalMap[CameraId.fromCamera2Id(cameraId)] =
cameraInternal as CameraInternalAdapter
}
@OptIn(ExperimentalCamera2Interop::class)
override fun getConcurrentCameraSelectors(): MutableList<MutableList<CameraSelector>> {
return concurrentCameraIdsSet.map { concurrentCameraIds ->
concurrentCameraIds.map { cameraId ->
CameraSelector.Builder().addCameraFilter { cameraInfos ->
cameraInfos.filter {
cameraId.value == Camera2CameraInfo.from(it).getCameraId()
}
}.build()
}.toMutableList()
}.toMutableList()
}
override fun getActiveConcurrentCameraInfos(): MutableList<CameraInfo> {
return activeConcurrentCameraInfosList
}
override fun setActiveConcurrentCameraInfos(cameraInfos: MutableList<CameraInfo>) {
activeConcurrentCameraInfosList = cameraInfos
val graphConfigs = cameraInternalMap.values.map {
checkNotNull(it.getDeferredCameraGraphConfig()) {
"Every CameraInternal instance is expected to have a deferred CameraGraph config " +
"when the active concurrent CameraInfos are set!"
}
}
val cameraGraphs = checkNotNull(cameraPipe).createCameraGraphs(graphConfigs)
check(cameraGraphs.size == cameraInternalMap.size)
for ((cameraInternalAdapter, cameraGraph) in cameraInternalMap.values.zip(cameraGraphs)) {
cameraInternalAdapter.resumeDeferredCameraGraphCreation(cameraGraph)
}
}
@OptIn(ExperimentalCamera2Interop::class)
override fun getPairedConcurrentCameraId(cameraId: String): String? {
if (!concurrentCameraIdMap.containsKey(cameraId)) {
return null
}
for (pairedCameraId in concurrentCameraIdMap[cameraId]!!) {
for (cameraInfo in activeConcurrentCameraInfos) {
if (pairedCameraId == Camera2CameraInfo.from(cameraInfo).getCameraId()) {
return pairedCameraId
}
}
}
return null
}
@CameraOperatingMode
override fun getCameraOperatingMode(): Int {
return concurrentMode
}
override fun setCameraOperatingMode(@CameraOperatingMode cameraOperatingMode: Int) {
concurrentMode = cameraOperatingMode
concurrentModeOn = cameraOperatingMode == CameraCoordinator.CAMERA_OPERATING_MODE_CONCURRENT
for (cameraInternalAdapter in cameraInternalMap.values) {
if (cameraOperatingMode == CameraCoordinator.CAMERA_OPERATING_MODE_CONCURRENT) {
cameraInternalAdapter.setCameraGraphCreationMode(createImmediately = false)
} else if (cameraOperatingMode == CameraCoordinator.CAMERA_OPERATING_MODE_SINGLE) {
cameraInternalAdapter.setCameraGraphCreationMode(createImmediately = true)
}
}
}
override fun addListener(listener: CameraCoordinator.ConcurrentCameraModeListener) {
}
override fun removeListener(listener: CameraCoordinator.ConcurrentCameraModeListener) {
}
override fun shutdown() {
cameraPipe = null
cameraInternalMap.clear()
concurrentCameraIdsSet.clear()
concurrentCameraIdMap.clear()
activeConcurrentCameraInfosList.clear()
concurrentMode = CAMERA_OPERATING_MODE_UNSPECIFIED
concurrentModeOn = false
}
}