1 /* <lambda>null2 * Copyright 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package androidx.camera.camera2.pipe.graph 18 19 import android.hardware.camera2.CameraCharacteristics 20 import android.hardware.camera2.params.OutputConfiguration 21 import android.os.Build 22 import android.util.Size 23 import android.view.Surface 24 import androidx.camera.camera2.pipe.CameraController 25 import androidx.camera.camera2.pipe.CameraGraph 26 import androidx.camera.camera2.pipe.CameraId 27 import androidx.camera.camera2.pipe.CameraMetadata 28 import androidx.camera.camera2.pipe.CameraMetadata.Companion.isHardwareLevelExternal 29 import androidx.camera.camera2.pipe.CameraMetadata.Companion.isHardwareLevelLegacy 30 import androidx.camera.camera2.pipe.CameraMetadata.Companion.isHardwareLevelLimited 31 import androidx.camera.camera2.pipe.CameraStream 32 import androidx.camera.camera2.pipe.InputStream 33 import androidx.camera.camera2.pipe.InputStreamId 34 import androidx.camera.camera2.pipe.OutputId 35 import androidx.camera.camera2.pipe.OutputStream 36 import androidx.camera.camera2.pipe.StreamFormat 37 import androidx.camera.camera2.pipe.StreamGraph 38 import androidx.camera.camera2.pipe.StreamId 39 import androidx.camera.camera2.pipe.compat.Api24Compat 40 import androidx.camera.camera2.pipe.config.CameraGraphScope 41 import javax.inject.Inject 42 import javax.inject.Provider 43 import kotlinx.atomicfu.atomic 44 45 /** 46 * This object builds an internal graph of inputs and outputs from a graphConfig. It is responsible 47 * for defining the identifiers for each input and output stream, and for building an abstract 48 * representation of the internal camera output configuration(s). 49 */ 50 @CameraGraphScope 51 internal class StreamGraphImpl 52 @Inject 53 constructor( 54 val cameraMetadata: CameraMetadata, 55 val graphConfig: CameraGraph.Config, 56 private val cameraControllerProvider: Provider<CameraController>, 57 ) : StreamGraph { 58 private val _streamMap: Map<CameraStream.Config, CameraStream> 59 60 internal val outputConfigs: List<OutputConfig> 61 62 // TODO: Build InputStream(s) 63 override val inputs: List<InputStream> 64 override val streams: List<CameraStream> 65 override val streamIds: Set<StreamId> 66 override val outputs: List<OutputStream> 67 68 override fun get(config: CameraStream.Config): CameraStream? = _streamMap[config] 69 70 override fun getOutputLatency( 71 streamId: StreamId, 72 outputId: OutputId? 73 ): StreamGraph.OutputLatency? { 74 val cameraController = cameraControllerProvider.get() 75 val outputLatency = cameraController.getOutputLatency(streamId) 76 if (outputLatency != null) { 77 return outputLatency 78 } 79 val stream = this[streamId] 80 var output = outputId?.let { get(it) } 81 checkNotNull(stream) { "No stream found for given streamId $streamId" } 82 if (stream.outputs.size == 1) { 83 output = stream.outputs.single() 84 } else { 85 checkNotNull(output) { 86 "Output must be specified for MultiResolution use case. " + 87 "No output found for given outputId $outputId" 88 } 89 } 90 val streamConfigurationMap = 91 cameraMetadata[CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP] 92 val stallDuration = 93 streamConfigurationMap?.getOutputStallDuration(output.format.value, output.size) 94 return stallDuration?.let { StreamGraph.OutputLatency(it, 0) } 95 } 96 97 init { 98 val outputConfigListBuilder = mutableListOf<OutputConfig>() 99 val outputConfigMap = mutableMapOf<OutputStream.Config, OutputConfig>() 100 101 val streamListBuilder = mutableListOf<CameraStream>() 102 val streamMapBuilder = mutableMapOf<CameraStream.Config, CameraStream>() 103 104 val deferredOutputsAllowed = 105 computeIfDeferredStreamsAreSupported(cameraMetadata, graphConfig) 106 107 // Compute groupNumbers for buffer sharing. 108 val groupNumbers = mutableMapOf<CameraStream.Config, Int>() 109 for (group in graphConfig.exclusiveStreamGroups) { 110 check(group.isNotEmpty()) 111 val surfaceGroupId = computeNextSurfaceGroupId(graphConfig) 112 for (config in group) { 113 check(!groupNumbers.containsKey(config)) 114 groupNumbers[config] = surfaceGroupId 115 } 116 } 117 118 // Create outputConfigs. If outputs are shared there can be fewer entries in map than there 119 // are streams. 120 for (streamConfig in graphConfig.streams) { 121 for (output in streamConfig.outputs) { 122 if (outputConfigMap.containsKey(output)) { 123 continue 124 } 125 126 val outputConfig = 127 OutputConfig( 128 nextConfigId(), 129 output.size, 130 output.format, 131 output.camera ?: graphConfig.camera, 132 groupNumber = groupNumbers[streamConfig], 133 deferredOutputType = 134 if (deferredOutputsAllowed) { 135 (output as? OutputStream.Config.LazyOutputConfig)?.outputType 136 } else { 137 null 138 }, 139 mirrorMode = output.mirrorMode, 140 timestampBase = output.timestampBase, 141 dynamicRangeProfile = output.dynamicRangeProfile, 142 streamUseCase = output.streamUseCase, 143 streamUseHint = output.streamUseHint, 144 sensorPixelModes = output.sensorPixelModes, 145 externalOutputConfig = getOutputConfigurationOrNull(output), 146 ) 147 outputConfigMap[output] = outputConfig 148 outputConfigListBuilder.add(outputConfig) 149 } 150 } 151 152 // Build the streams 153 for (streamConfigIdx in graphConfig.streams.indices) { 154 val streamConfig = graphConfig.streams[streamConfigIdx] 155 156 val outputs = 157 streamConfig.outputs.map { 158 val outputConfig = outputConfigMap[it]!! 159 160 val outputStream = 161 OutputStreamImpl( 162 nextOutputId(), 163 outputConfig.size, 164 outputConfig.format, 165 outputConfig.camera, 166 outputConfig.mirrorMode, 167 outputConfig.timestampBase, 168 outputConfig.dynamicRangeProfile, 169 outputConfig.streamUseCase, 170 outputConfig.deferredOutputType, 171 outputConfig.streamUseHint 172 ) 173 outputStream 174 } 175 176 val stream = CameraStream(nextStreamId(), outputs) 177 streamMapBuilder[streamConfig] = stream 178 streamListBuilder.add(stream) 179 for (output in outputs) { 180 output.stream = stream 181 } 182 for (cameraOutputConfig in streamConfig.outputs) { 183 outputConfigMap[cameraOutputConfig]!!.streamBuilder.add(stream) 184 } 185 } 186 inputs = 187 graphConfig.input?.map { 188 InputStreamImpl( 189 nextInputId(), 190 it.maxImages, 191 it.streamFormat, 192 ) 193 } ?: emptyList() 194 195 val streamSortedByPreview = sortOutputsByPreviewStream(streamListBuilder) 196 val streamSortedByVideo = sortOutputsByVideoStream(streamSortedByPreview) 197 198 streams = streamSortedByVideo 199 streamIds = streams.map { it.id }.toSet() 200 _streamMap = streamMapBuilder 201 outputConfigs = 202 outputConfigListBuilder.sortedBy { 203 it.streams.minOf { stream -> streams.indexOf(stream) } 204 } 205 outputs = streams.flatMap { it.outputs } 206 } 207 208 class OutputConfig( 209 val id: OutputConfigId, 210 val size: Size, 211 val format: StreamFormat, 212 val camera: CameraId, 213 val groupNumber: Int?, 214 val externalOutputConfig: OutputConfiguration?, 215 val deferredOutputType: OutputStream.OutputType?, 216 val mirrorMode: OutputStream.MirrorMode?, 217 val timestampBase: OutputStream.TimestampBase?, 218 val dynamicRangeProfile: OutputStream.DynamicRangeProfile?, 219 val streamUseCase: OutputStream.StreamUseCase?, 220 val streamUseHint: OutputStream.StreamUseHint?, 221 val sensorPixelModes: List<OutputStream.SensorPixelMode>, 222 ) { 223 internal val streamBuilder = mutableListOf<CameraStream>() 224 val streams: List<CameraStream> 225 get() = streamBuilder 226 227 val deferrable: Boolean 228 get() = deferredOutputType != null 229 230 val surfaceSharing: Boolean 231 get() = streamBuilder.size > 1 232 233 override fun toString(): String = id.toString() 234 } 235 236 private class OutputStreamImpl( 237 override val id: OutputId, 238 override val size: Size, 239 override val format: StreamFormat, 240 override val camera: CameraId, 241 override val mirrorMode: OutputStream.MirrorMode?, 242 override val timestampBase: OutputStream.TimestampBase?, 243 override val dynamicRangeProfile: OutputStream.DynamicRangeProfile?, 244 override val streamUseCase: OutputStream.StreamUseCase?, 245 override val outputType: OutputStream.OutputType?, 246 override val streamUseHint: OutputStream.StreamUseHint? 247 ) : OutputStream { 248 override lateinit var stream: CameraStream 249 250 override fun toString(): String = id.toString() 251 } 252 253 private class InputStreamImpl( 254 override val id: InputStreamId, 255 override val maxImages: Int, 256 override val format: StreamFormat 257 ) : InputStream 258 259 interface SurfaceListener { 260 fun onSurfaceMapUpdated(surfaces: Map<StreamId, Surface>) 261 } 262 263 private fun getOutputConfigurationOrNull( 264 outputConfig: OutputStream.Config 265 ): OutputConfiguration? { 266 if (Build.VERSION.SDK_INT >= 33) { 267 return (outputConfig as? OutputStream.Config.ExternalOutputConfig)?.output 268 } 269 return null 270 } 271 272 private fun computeNextSurfaceGroupId(graphConfig: CameraGraph.Config): Int { 273 // If there are any existing surfaceGroups, make sure the groups we define do not overlap 274 // with any existing values. 275 val existingGroupNumbers: List<Int> = readExistingGroupNumbers(graphConfig.streams) 276 277 // Loop until we produce a groupId that was not already used. 278 var number = nextGroupId() 279 while (existingGroupNumbers.contains(number)) { 280 number = nextGroupId() 281 } 282 return number 283 } 284 285 private fun readExistingGroupNumbers(outputs: List<CameraStream.Config>): List<Int> { 286 return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { 287 outputs 288 .flatMap { it.outputs } 289 .filterIsInstance<OutputStream.Config.ExternalOutputConfig>() 290 .fold(mutableListOf()) { values, config -> 291 val groupId = Api24Compat.getSurfaceGroupId(config.output) 292 if (!values.contains(groupId)) { 293 values.add(groupId) 294 } 295 values 296 } 297 } else { 298 emptyList() 299 } 300 } 301 302 private fun computeIfDeferredStreamsAreSupported( 303 cameraMetadata: CameraMetadata, 304 graphConfig: CameraGraph.Config 305 ): Boolean { 306 return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && 307 graphConfig.sessionMode == CameraGraph.OperatingMode.NORMAL && 308 !cameraMetadata.isHardwareLevelLegacy && 309 !cameraMetadata.isHardwareLevelLimited && 310 (Build.VERSION.SDK_INT < Build.VERSION_CODES.P || 311 !cameraMetadata.isHardwareLevelExternal) 312 } 313 314 override fun toString(): String { 315 return "StreamGraph($_streamMap)" 316 } 317 318 /** 319 * Sort the output streams to move preview streams to the head of the list. The order of the 320 * outputs is determined by the following: 321 * 1. StreamUseCase: Check if any streams have PREVIEW StreamUseCase set and move them to the 322 * head of the list. Otherwise, go to step 2. 323 * 2. OutputType: Check if any streams have SURFACE_VIEW and SURFACE_TEXTURE OutputType and move 324 * them in respective order to the head of the list. Otherwise, go to step 3. 325 * 3. StreamFormat: Check if any streams have UNKNOWN and PRIVATE StreamFormats and move them in 326 * respective order to the head of the list. Otherwise, return list in original order. 327 */ 328 private fun sortOutputsByPreviewStream( 329 unsortedStreams: List<CameraStream> 330 ): List<CameraStream> { 331 332 // If any stream explicitly specifies "PREVIEW" for its use case, prioritize those streams 333 val (previewStreamPartition, nonPreviewStreamPartition) = 334 unsortedStreams.partition { 335 it.outputs.any { output -> 336 output.streamUseCase == OutputStream.StreamUseCase.PREVIEW 337 } 338 } 339 if (previewStreamPartition.isNotEmpty()) { 340 return previewStreamPartition + nonPreviewStreamPartition 341 } 342 343 // If no streams explicitly specify the PREVIEW UseCase, fall back to ordering by 344 // SURFACE_VIEW / SURFACE_TEXTURE output types. 345 val (previewTypePartition, nonPreviewTypePartition) = 346 unsortedStreams.partition { 347 it.outputs.any { output -> output.outputType in previewOutputTypes } 348 } 349 if (previewTypePartition.isNotEmpty()) { 350 return previewTypePartition.sortedWith(previewOutputTypesComparator) + 351 nonPreviewTypePartition 352 } 353 354 // Check if any streams have UNKNOWN and PRIVATE StreamFormats 355 val (previewFormatPartition, nonPreviewFormatPartition) = 356 unsortedStreams.partition { 357 it.outputs.any { output -> output.format in previewFormats } 358 } 359 // Move streams with UNKNOWN and PRIVATE StreamFormats to head of list 360 if (previewFormatPartition.isNotEmpty()) { 361 return previewFormatPartition.sortedWith(previewFormatComparator) + 362 nonPreviewFormatPartition 363 } 364 365 // Return outputs in original order if no preview streams found 366 return unsortedStreams 367 } 368 369 /** 370 * Sort the output streams to move video streams to the bottom of the list. The order of the 371 * outputs is determined by the following: 372 * 1. StreamUseCase: Check if any streams have StreamUseCase.VIDEO_RECORD and move these to the 373 * bottom of the list. Otherwise, go to step 2. 374 * 2. StreamUseHint: Check if any streams have StreamUseHint.VIDEO_RECORD and move these to the 375 * bottom of the list. 376 */ 377 private fun sortOutputsByVideoStream(unsortedOutputs: List<CameraStream>): List<CameraStream> { 378 379 // Check if any streams have VIDEO StreamUseCase set 380 val (videoStreamPartition, nonVideoStreamPartition) = 381 unsortedOutputs.partition { 382 it.outputs.any { output -> 383 output.streamUseCase == OutputStream.StreamUseCase.VIDEO_RECORD 384 } 385 } 386 // Move streams with VIDEO StreamUseCase to end of list 387 if (videoStreamPartition.isNotEmpty()) { 388 return nonVideoStreamPartition + videoStreamPartition 389 } 390 391 // Check if any streams have VIDEO StreamUseCaseHint set 392 val (videoStreamHintPartition, nonVideoStreamHintPartition) = 393 unsortedOutputs.partition { 394 it.outputs.any { output -> 395 output.streamUseHint == OutputStream.StreamUseHint.VIDEO_RECORD 396 } 397 } 398 399 // Move streams with VIDEO StreamUseCaseHint to end of list 400 if (videoStreamHintPartition.isNotEmpty()) { 401 return nonVideoStreamHintPartition + videoStreamHintPartition 402 } 403 404 // Return outputs in original order if no video streams found 405 return unsortedOutputs 406 } 407 408 companion object { 409 private val streamIds = atomic(0) 410 411 internal fun nextStreamId(): StreamId = StreamId(streamIds.incrementAndGet()) 412 413 private val outputIds = atomic(0) 414 415 internal fun nextOutputId(): OutputId = OutputId(outputIds.incrementAndGet()) 416 417 private val inputIds = atomic(0) 418 419 internal fun nextInputId(): InputStreamId = InputStreamId(inputIds.incrementAndGet()) 420 421 private val configIds = atomic(0) 422 423 internal fun nextConfigId(): OutputConfigId = OutputConfigId(configIds.incrementAndGet()) 424 425 private val groupIds = atomic(0) 426 427 internal fun nextGroupId(): Int = groupIds.incrementAndGet() 428 429 private val previewOutputTypes = 430 listOf(OutputStream.OutputType.SURFACE_VIEW, OutputStream.OutputType.SURFACE_TEXTURE) 431 432 private val previewOutputTypesComparator = 433 compareBy<CameraStream> { 434 it.outputs.maxOf { output -> previewOutputTypes.indexOf(output.outputType) } 435 } 436 437 private val previewFormats = listOf(StreamFormat.UNKNOWN, StreamFormat.PRIVATE) 438 439 private val previewFormatComparator = 440 compareBy<CameraStream> { 441 it.outputs.maxOf { output -> previewFormats.indexOf(output.format) } 442 } 443 } 444 } 445 446 @JvmInline 447 internal value class OutputConfigId(val value: Int) { toStringnull448 override fun toString(): String = "OutputConfig-$value" 449 } 450