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