1 /*
<lambda>null2  * Copyright 2020 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 @file:Suppress("NOTHING_TO_INLINE")
18 
19 package androidx.camera.camera2.pipe.core
20 
21 import android.hardware.camera2.CameraCharacteristics
22 import android.hardware.camera2.CameraCharacteristics.LENS_FACING
23 import android.hardware.camera2.CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES
24 import android.hardware.camera2.CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA
25 import android.hardware.camera2.CaptureRequest
26 import android.hardware.camera2.CaptureResult
27 import android.hardware.camera2.params.MeteringRectangle
28 import android.os.Build
29 import android.os.Trace
30 import androidx.camera.camera2.pipe.CameraGraph
31 import androidx.camera.camera2.pipe.CameraMetadata
32 import androidx.camera.camera2.pipe.core.Timestamps.formatMs
33 
34 /** Internal debug utilities, constants, and checks. */
35 public object Debug {
36     internal val systemTimeSource = SystemTimeSource()
37     public const val ENABLE_LOGGING: Boolean = true
38     public const val ENABLE_TRACING: Boolean = true
39 
40     /**
41      * Wrap the specified [block] in calls to [Trace.beginSection] (with the supplied [label]) and
42      * [Trace.endSection].
43      *
44      * @param label A name of the code section to appear in the trace.
45      * @param block A block of code which is being traced.
46      */
47     public inline fun <T> trace(label: String, crossinline block: () -> T): T {
48         try {
49             traceStart { label }
50             return block()
51         } finally {
52             traceStop()
53         }
54     }
55 
56     /** Wrap the specified [block] in a trace and timing calls. */
57     internal inline fun <T> instrument(label: String, crossinline block: () -> T): T {
58         val start = systemTimeSource.now()
59         try {
60             traceStart { label }
61             return block()
62         } finally {
63             traceStop()
64             val duration = systemTimeSource.now() - start
65             Log.debug { "$label - ${duration.formatMs()}" }
66         }
67     }
68 
69     /** Forwarding call to [Trace.beginSection] that can be statically disabled at compile time. */
70     public inline fun traceStart(crossinline label: () -> String) {
71         if (ENABLE_TRACING) {
72             Trace.beginSection(label())
73         }
74     }
75 
76     /** Forwarding call to [Trace.endSection] that can be statically disabled at compile time. */
77     public inline fun traceStop() {
78         if (ENABLE_TRACING) {
79             Trace.endSection()
80         }
81     }
82 
83     private fun appendParameters(builder: StringBuilder, name: String, parameters: Map<*, Any?>) {
84         builder.apply {
85             if (parameters.isEmpty()) {
86                 append("$name: (None)\n")
87             } else {
88                 append("${name}\n")
89                 parametersToSortedStringPairs(parameters).forEach {
90                     append("  ${it.first.padEnd(50, ' ')} ${it.second}\n")
91                 }
92             }
93         }
94     }
95 
96     /**
97      * Format a map of parameters as a comma separated list.
98      *
99      * Example: `[abc.xyz=1, abc.zyx=something]`
100      */
101     public fun formatParameterMap(parameters: Map<*, Any?>, limit: Int = -1): String {
102         return parametersToSortedStringPairs(parameters).joinToString(
103             prefix = "{",
104             postfix = "}",
105             limit = limit
106         ) {
107             "${it.first}=${it.second}"
108         }
109     }
110 
111     private fun parametersToSortedStringPairs(
112         parameters: Map<*, Any?>
113     ): List<Pair<String, String>> =
114         parameters.map { keyNameToString(it.key) to valueToString(it.value) }.sortedBy { it.first }
115 
116     private fun keyNameToString(key: Any?): String =
117         when (key) {
118             is CameraCharacteristics.Key<*> -> key.name
119             is CaptureRequest.Key<*> -> key.name
120             is CaptureResult.Key<*> -> key.name
121             else -> key.toString()
122         }
123 
124     /* Utility for cleaning up some verbose value types for logs */
125     private fun valueToString(value: Any?): String =
126         when (value) {
127             is MeteringRectangle ->
128                 "[x=${value.x}, y=${value.y}, " +
129                     "w=${value.width}, h=${value.height}, weight=${value.meteringWeight}"
130             else -> value.toString()
131         }
132 
133     public fun formatCameraGraphProperties(
134         metadata: CameraMetadata,
135         graphConfig: CameraGraph.Config,
136         cameraGraph: CameraGraph
137     ): String {
138         val sharedCameraIds = graphConfig.sharedCameraIds.joinToString()
139 
140         val lensFacing =
141             when (metadata[LENS_FACING]) {
142                 CameraCharacteristics.LENS_FACING_FRONT -> "Front"
143                 CameraCharacteristics.LENS_FACING_BACK -> "Back"
144                 CameraCharacteristics.LENS_FACING_EXTERNAL -> "External"
145                 else -> "Unknown"
146             }
147 
148         val operatingMode =
149             when (graphConfig.sessionMode) {
150                 CameraGraph.OperatingMode.HIGH_SPEED -> "High Speed"
151                 CameraGraph.OperatingMode.NORMAL -> "Normal"
152                 CameraGraph.OperatingMode.EXTENSION -> "Extension"
153                 else -> "Unknown"
154             }
155 
156         val capabilities = metadata[REQUEST_AVAILABLE_CAPABILITIES]
157         val cameraType =
158             if (
159                 capabilities != null &&
160                     capabilities.contains(REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA)
161             ) {
162                 "Logical"
163             } else {
164                 "Physical"
165             }
166 
167         return StringBuilder()
168             .apply {
169                 append("$cameraGraph (Camera ${graphConfig.camera.value})\n")
170                 if (sharedCameraIds.isNotEmpty()) {
171                     append("  Shared:    $sharedCameraIds\n")
172                 }
173                 append("  Facing:    $lensFacing ($cameraType)\n")
174                 append("  Mode:      $operatingMode\n")
175                 append("Outputs:\n")
176                 for (stream in cameraGraph.streams.streams) {
177                     stream.outputs.forEachIndexed { i, output ->
178                         append("  ")
179                         val streamId = if (i == 0) output.stream.id.toString() else ""
180                         append(streamId.padEnd(12, ' '))
181                         append(output.id.toString().padEnd(12, ' '))
182                         append(output.size.toString().padEnd(12, ' '))
183                         append(output.format.name.padEnd(16, ' '))
184                         output.mirrorMode?.let { append(" [$it]") }
185                         output.timestampBase?.let { append(" [$it]") }
186                         output.dynamicRangeProfile?.let { append(" [$it]") }
187                         output.streamUseCase?.let { append(" [$it]") }
188                         output.streamUseHint?.let { append(" [$it]") }
189                         if (output.camera != graphConfig.camera) {
190                             append(" [")
191                             append(output.camera)
192                             append("]")
193                         }
194                         append("\n")
195                     }
196                 }
197                 if (cameraGraph.streams.inputs.isNotEmpty()) {
198                     append("Inputs:\n")
199                     for (stream in cameraGraph.streams.inputs) {
200                         append(" ")
201                         append(stream.id.toString().padEnd(12, ' '))
202                         append(stream.format.toString().padEnd(12, ' '))
203                         append(stream.maxImages.toString().padEnd(12, ' '))
204                         append("\n")
205                     }
206                 }
207 
208                 append("Session Template: ${graphConfig.sessionTemplate.name}\n")
209                 appendParameters(this, "Session Parameters", graphConfig.sessionParameters)
210 
211                 append("Default Template: ${graphConfig.defaultTemplate.name}\n")
212                 appendParameters(this, "Default Parameters", graphConfig.defaultParameters)
213 
214                 appendParameters(this, "Required Parameters", graphConfig.requiredParameters)
215             }
216             .toString()
217     }
218 }
219 
220 /**
221  * Asserts that the method was invoked on a specific API version or higher.
222  *
223  * Example: checkApi(Build.VERSION_CODES.LOLLIPOP, "createCameraDevice")
224  */
checkApinull225 public inline fun checkApi(requiredApi: Int, methodName: String) {
226     check(Build.VERSION.SDK_INT >= requiredApi) {
227         "$methodName is not supported on API ${Build.VERSION.SDK_INT} (requires API $requiredApi)"
228     }
229 }
230 
231 /** Asserts that this method was invoked on Android L (API 21) or higher. */
checkLOrHighernull232 public inline fun checkLOrHigher(methodName: String): Unit =
233     checkApi(Build.VERSION_CODES.LOLLIPOP, methodName)
234 
235 /** Asserts that this method was invoked on Android M (API 23) or higher. */
236 public inline fun checkMOrHigher(methodName: String): Unit =
237     checkApi(Build.VERSION_CODES.M, methodName)
238 
239 /** Asserts that this method was invoked on Android N (API 24) or higher. */
240 public inline fun checkNOrHigher(methodName: String): Unit =
241     checkApi(Build.VERSION_CODES.N, methodName)
242 
243 /** Asserts that this method was invoked on Android O (API 26) or higher. */
244 public inline fun checkOOrHigher(methodName: String): Unit =
245     checkApi(Build.VERSION_CODES.O, methodName)
246 
247 /** Asserts that this method was invoked on Android P (API 28) or higher. */
248 public inline fun checkPOrHigher(methodName: String): Unit =
249     checkApi(Build.VERSION_CODES.P, methodName)
250 
251 /** Asserts that this method was invoked on Android Q (API 29) or higher. */
252 public inline fun checkQOrHigher(methodName: String): Unit =
253     checkApi(Build.VERSION_CODES.Q, methodName)
254