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