1 /*
2 * 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 package androidx.camera.camera2.pipe
18
19 import android.hardware.camera2.CameraCaptureSession
20 import android.hardware.camera2.CameraDevice
21 import android.hardware.camera2.CameraExtensionCharacteristics
22 import android.hardware.camera2.CameraExtensionSession
23 import android.hardware.camera2.CaptureFailure
24 import android.hardware.camera2.CaptureRequest
25 import android.view.Surface
26 import androidx.annotation.RestrictTo
27 import androidx.camera.camera2.pipe.core.Debug
28 import androidx.camera.camera2.pipe.core.Log
29 import androidx.camera.camera2.pipe.media.ImageWrapper
30
31 /**
32 * A [RequestNumber] is an artificial identifier that is created for each request that is submitted
33 * to the Camera.
34 */
35 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
36 @JvmInline
37 public value class RequestNumber(public val value: Long)
38
39 /**
40 * A [Request] is an immutable package of outputs and parameters needed to issue a [CaptureRequest]
41 * to a Camera2 [CameraCaptureSession].
42 *
43 * [Request] objects are handled by camera2 via the [RequestProcessor] interface, and will translate
44 * each [Request] object into a corresponding [CaptureRequest] object using the active
45 * [CameraDevice], [CameraCaptureSession], and [CameraGraph.Config]. Requests may be queued up and
46 * submitted after a delay, or reused (in the case of repeating requests) if the
47 * [CameraCaptureSession] is reconfigured or recreated.
48 *
49 * Depending on the [CameraGraph.Config], it is possible that not all parameters that are set on the
50 * [Request] will be honored when a [Request] is sent to the camera. Specifically, Camera2
51 * parameters related to 3A State and any required parameters specified on the [CameraGraph.Config]
52 * will override parameters specified in a [Request]
53 *
54 * @param streams The list of streams to submit. Each request *must* have 1 or more valid streams.
55 */
56 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
57 public class Request(
58 public val streams: List<StreamId>,
59 public val parameters: Map<CaptureRequest.Key<*>, Any> = emptyMap(),
60 public val extras: Map<Metadata.Key<*>, Any> = emptyMap(),
61 public val listeners: List<Listener> = emptyList(),
62 public val template: RequestTemplate? = null,
63 public val inputRequest: InputRequest? = null
64 ) {
getnull65 public operator fun <T> get(key: CaptureRequest.Key<T>): T? = getUnchecked(key)
66
67 public operator fun <T> get(key: Metadata.Key<T>): T? = getUnchecked(key)
68
69 /**
70 * This listener is used to observe the state and progress of a [Request] that has been issued
71 * to the [CameraGraph]. Listeners will be invoked on background threads at high speed, and
72 * should avoid blocking work or accessing synchronized resources if possible. [Listener]s used
73 * in a repeating request may be issued multiple times within the same session, and should not
74 * rely on [onRequestSequenceSubmitted] from being invoked only once.
75 */
76 @JvmDefaultWithCompatibility
77 public interface Listener {
78 /**
79 * This event indicates that the camera sensor has started exposing the frame associated
80 * with this Request. The timestamp will either be the beginning or end of the sensors
81 * exposure time depending on the device, and may be in a different timebase from the
82 * timestamps that are returned from the underlying buffers.
83 *
84 * @param requestMetadata the data about the camera2 request that was sent to the camera.
85 * @param frameNumber the android frame number for this exposure
86 * @param timestamp the android timestamp in nanos for this exposure
87 * @see android.hardware.camera2.CameraCaptureSession.CaptureCallback.onCaptureStarted
88 */
89 public fun onStarted(
90 requestMetadata: RequestMetadata,
91 frameNumber: FrameNumber,
92 timestamp: CameraTimestamp
93 ) {}
94
95 /**
96 * This event indicates that the camera sensor has additional information about the frame
97 * associated with this Request. This method may be invoked 0 or more times before the frame
98 * receives onComplete.
99 *
100 * @param requestMetadata the data about the camera2 request that was sent to the camera.
101 * @param frameNumber the android frame number for this exposure
102 * @param captureResult the current android capture result for this exposure
103 * @see android.hardware.camera2.CameraCaptureSession.CaptureCallback.onCaptureStarted
104 */
105 public fun onPartialCaptureResult(
106 requestMetadata: RequestMetadata,
107 frameNumber: FrameNumber,
108 captureResult: FrameMetadata
109 ) {}
110
111 /**
112 * This event provides clients with an estimate of the post-processing progress of a capture
113 * which could take significantly more time relative to the rest of the
114 * [CameraExtensionSession.capture] sequence. The callback will be triggered only by
115 * extensions that return true from calls
116 * [CameraExtensionCharacteristics.isCaptureProcessProgressAvailable]. If support for this
117 * callback is present, then clients will be notified at least once with progress value 100.
118 * The callback will be triggered only for still capture requests
119 * [CameraExtensionSession.capture] and is not supported for repeating requests
120 * [CameraExtensionSession.setRepeatingRequest].
121 *
122 * @param requestMetadata the data about the camera2 request that was sent to the camera.
123 * @param progress the value indicating the current post-processing progress (between 0 and
124 * 100 inclusive)
125 * @see
126 * android.hardware.camera2.CameraExtensionSession.ExtensionCaptureCallback.onCaptureProcessProgressed
127 */
128 public fun onCaptureProgress(requestMetadata: RequestMetadata, progress: Int) {}
129
130 /**
131 * This event indicates that all of the metadata associated with this frame has been
132 * produced. If [onPartialCaptureResult] was invoked, the values returned in the
133 * totalCaptureResult map be a superset of the values produced from the
134 * [onPartialCaptureResult] calls.
135 *
136 * @param requestMetadata the data about the camera2 request that was sent to the camera.
137 * @param frameNumber the android frame number for this exposure
138 * @param totalCaptureResult the final android capture result for this exposure
139 * @see android.hardware.camera2.CameraCaptureSession.CaptureCallback.onCaptureStarted
140 */
141 public fun onTotalCaptureResult(
142 requestMetadata: RequestMetadata,
143 frameNumber: FrameNumber,
144 totalCaptureResult: FrameInfo
145 ) {}
146
147 /**
148 * This is an artificial event that will be invoked after onTotalCaptureResult. This may be
149 * invoked several frames after onTotalCaptureResult due to incorrect HAL implementations
150 * that return metadata that get shifted several frames in the future. See b/154568653 for
151 * real examples of this. The actual amount of shifting and required transformations may
152 * vary per device.
153 *
154 * @param requestMetadata the data about the camera2 request that was sent to the camera.
155 * @param frameNumber the android frame number for this exposure
156 * @param result the package of metadata associated with this result.
157 */
158 public fun onComplete(
159 requestMetadata: RequestMetadata,
160 frameNumber: FrameNumber,
161 result: FrameInfo
162 ) {}
163
164 /**
165 * onFailed occurs when a CaptureRequest failed in some way and the frame will not receive
166 * the [onTotalCaptureResult] callback.
167 *
168 * Surfaces may not received images if "wasImagesCaptured" is set to false.
169 *
170 * @param requestMetadata the data about the camera2 request that was sent to the camera.
171 * @param frameNumber the android frame number for this exposure
172 * @param requestFailure the android [RequestFailure] data wrapper
173 * @see android.hardware.camera2.CameraCaptureSession.CaptureCallback.onCaptureFailed
174 */
175 public fun onFailed(
176 requestMetadata: RequestMetadata,
177 frameNumber: FrameNumber,
178 requestFailure: RequestFailure
179 ) {}
180
181 /**
182 * onReadoutStarted occurs when the camera device has started reading out the output image
183 * for the request, at the beginning of the sensor image readout. Concretely, it is invoked
184 * right after onCaptureStarted.
185 *
186 * @param requestMetadata the data about the camera2 request that was sent to the camera.
187 * @param frameNumber the android frame number for this capture.
188 * @param timestamp the android timestamp in nanos at the start of camera data readout.
189 * @see android.hardware.camera2.CameraCaptureSession.CaptureCallback.onReadoutStarted
190 */
191 public fun onReadoutStarted(
192 requestMetadata: RequestMetadata,
193 frameNumber: FrameNumber,
194 timestamp: SensorTimestamp
195 ) {}
196
197 /**
198 * onBufferLost occurs when a CaptureRequest failed to create an image for a given output
199 * stream. This method may be invoked multiple times per frame if multiple buffers were
200 * lost. This method may not be invoked when an image is lost in some situations.
201 *
202 * @param requestMetadata the data about the camera2 request that was sent to the camera.
203 * @param frameNumber the android frame number for this exposure
204 * @param stream the internal stream that will not receive a buffer for this frame.
205 * @see android.hardware.camera2.CameraCaptureSession.CaptureCallback.onCaptureBufferLost
206 */
207 public fun onBufferLost(
208 requestMetadata: RequestMetadata,
209 frameNumber: FrameNumber,
210 stream: StreamId
211 ) {}
212
213 /**
214 * This is an artificial callback that will be invoked if a specific request was pending or
215 * had already been submitted to when an abort was requested. The behavior of the request is
216 * undefined if this method is invoked and images or metadata may or may not be produced for
217 * this request. Repeating requests will not receive onAborted. Failed reprocessing requests
218 * will be aborted and removed from the queue.
219 *
220 * @param request information about this specific request.
221 */
222 public fun onAborted(request: Request) {}
223
224 /**
225 * Invoked after the CaptureRequest(s) have been created, but before the request is
226 * submitted to the Camera. This method may be invoked multiple times if the request fails
227 * to submit or if this is a repeating request.
228 *
229 * @param requestMetadata information about this specific request.
230 */
231 public fun onRequestSequenceCreated(requestMetadata: RequestMetadata) {}
232
233 /**
234 * Invoked after the CaptureRequest(s) has been submitted. This method may be invoked
235 * multiple times if the request was submitted as a repeating request.
236 *
237 * @param requestMetadata the data about the camera2 request that was sent to the camera.
238 */
239 public fun onRequestSequenceSubmitted(requestMetadata: RequestMetadata) {}
240
241 /**
242 * Invoked by Camera2 if the request was aborted after having been submitted. This method is
243 * distinct from onAborted, which is directly invoked when aborting captures.
244 *
245 * @param requestMetadata the data about the camera2 request that was sent to the camera.
246 * @see
247 * android.hardware.camera2.CameraCaptureSession.CaptureCallback.onCaptureSequenceAborted
248 */
249 public fun onRequestSequenceAborted(requestMetadata: RequestMetadata) {}
250
251 /**
252 * Invoked by Camera2 if the request was completed after having been submitted. This method
253 * is distinct from onCompleted which is invoked for each frame when used with a repeating
254 * request.
255 *
256 * @param requestMetadata the data about the camera2 request that was sent to the camera.
257 * @param frameNumber the final frame number of this sequence.
258 * @see
259 * android.hardware.camera2.CameraCaptureSession.CaptureCallback.onCaptureSequenceCompleted
260 */
261 public fun onRequestSequenceCompleted(
262 requestMetadata: RequestMetadata,
263 frameNumber: FrameNumber
264 ) {}
265 }
266
267 @Suppress("UNCHECKED_CAST")
getUncheckednull268 private fun <T> getUnchecked(key: Metadata.Key<T>): T? = this.extras[key] as T?
269
270 @Suppress("UNCHECKED_CAST")
271 private fun <T> getUnchecked(key: CaptureRequest.Key<T>): T? = this.parameters[key] as T?
272
273 override fun toString(): String = toStringInternal(verbose = false)
274
275 public fun toStringVerbose(): String = toStringInternal(verbose = true)
276
277 private fun toStringInternal(verbose: Boolean): String {
278 val templateString = if (template == null) "" else ", template=$template"
279 // Ignore listener count, always include stream list (required).
280 val parametersString =
281 if (verbose && parameters.isNotEmpty()) {
282 ", parameters=${Debug.formatParameterMap(parameters, limit = 5)}"
283 } else {
284 ""
285 }
286 val extrasString =
287 if (verbose && extras.isNotEmpty()) {
288 ", extras=${Debug.formatParameterMap(extras, limit = 5)}"
289 } else {
290 ""
291 }
292 return "Request(streams=$streams$templateString$parametersString$extrasString)" +
293 "@${Integer.toHexString(hashCode())}"
294 }
295 }
296
297 /**
298 * Interface wrapper for [CaptureFailure].
299 *
300 * This interface should be used instead of [CaptureFailure] because its package-private constructor
301 * prevents directly creating an instance of it.
302 */
303 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
304 public interface RequestFailure : UnsafeWrapper {
305 /** Metadata about the request that has failed. */
306 public val requestMetadata: RequestMetadata
307
308 /** The Camera [FrameNumber] for the request that has failed. */
309 public val frameNumber: FrameNumber
310
311 /** Indicates the reason the particular request failed, see [CaptureFailure] for details. */
312 public val reason: Int
313
314 /**
315 * Indicates if images were still captured for this request. If this is true, the camera should
316 * invoke [Request.Listener.onBufferLost] individually for each output that failed. If this is
317 * false, these outputs will never arrive, and the individual callbacks will not be invoked.
318 */
319 public val wasImageCaptured: Boolean
320 }
321
322 /**
323 * A [RequestTemplate] indicates which preset set list of parameters will be applied to a request by
324 * default. These values are defined by camera2.
325 */
326 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
327 @JvmInline
328 public value class RequestTemplate(public val value: Int) {
329 public val name: String
330 get() {
331 return when (value) {
332 1 -> "TEMPLATE_PREVIEW"
333 2 -> "TEMPLATE_STILL_CAPTURE"
334 3 -> "TEMPLATE_RECORD"
335 4 -> "TEMPLATE_VIDEO_SNAPSHOT"
336 5 -> "TEMPLATE_ZERO_SHUTTER_LAG"
337 6 -> "TEMPLATE_MANUAL"
338 else -> "UNKNOWN-$value"
339 }
340 }
341 }
342
343 /**
344 * The intended use for this class is to submit the input needed for a reprocessing request, the
345 * [ImageWrapper] and [FrameInfo]. Both values are non-nullable because both values are needed for
346 * reprocessing.
347 */
348 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
349 public data class InputRequest(val image: ImageWrapper, val frameInfo: FrameInfo)
350
351 /**
352 * RequestMetadata wraps together all of the information about a specific CaptureRequest that was
353 * submitted to Camera2.
354 *
355 * <p> This class is distinct from [Request] which is used to configure and issue a request to the
356 * [CameraGraph]. This class will report the actual keys / values that were sent to camera2 (if
357 * different) from the request that was used to create the Camera2 [CaptureRequest].
358 */
359 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
360 public interface RequestMetadata : Metadata, UnsafeWrapper {
getnull361 public operator fun <T> get(key: CaptureRequest.Key<T>): T?
362
363 public fun <T> getOrDefault(key: CaptureRequest.Key<T>, default: T): T
364
365 /** The actual Camera2 template that was used when creating this [CaptureRequest] */
366 public val template: RequestTemplate
367
368 /**
369 * A Map of StreamId(s) that were submitted with this CaptureRequest and the Surface(s) used for
370 * this request. It's possible that not all of the streamId's specified in the [Request] are
371 * present in the [CaptureRequest].
372 */
373 public val streams: Map<StreamId, Surface>
374
375 /** Returns true if this is used in a repeating request. */
376 public val repeating: Boolean
377
378 /** The request object that was used to create this [CaptureRequest] */
379 public val request: Request
380
381 /** An internal number used to identify a specific [CaptureRequest] */
382 public val requestNumber: RequestNumber
383 }
384
385 /**
386 * This is a timestamp from the Camera, and corresponds to the nanosecond exposure time of a Frame.
387 * While the value is expressed in nano-seconds, the precision may be much lower. In addition, the
388 * time-base of the Camera is undefined, although it's common for it to be in either Monotonic or
389 * Realtime.
390 *
391 * <p> Timestamp may differ from timestamps that are obtained from other parts of the Camera and
392 * media systems within the same device. For example, it's common for high frequency sensors to
393 * operate based on a real-time clock, while audio/visual systems commonly operate based on a
394 * monotonic clock.
395 */
396 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
397 @JvmInline
398 public value class CameraTimestamp(public val value: Long)
399
400 /**
401 * This is a timestamp happen at start of readout for a regular request, or the timestamp at the
402 * input image's start of readout for a reprocess request, in nanoseconds.
403 */
404 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
405 @JvmInline
406 public value class SensorTimestamp(public val value: Long)
407
408 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
409 public fun <T> Request.getOrDefault(key: Metadata.Key<T>, default: T): T = this[key] ?: default
410
411 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
412 public fun <T> Request.getOrDefault(key: CaptureRequest.Key<T>, default: T): T =
413 this[key] ?: default
414
415 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
416 public fun Request.formatForLogs(): String = "Request($streams)@${Integer.toHexString(hashCode())}"
417
418 /** Utility function to help deal with the unsafe nature of the typed Key/Value pairs. */
419 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
420 public fun CaptureRequest.Builder.writeParameters(parameters: Map<*, Any?>) {
421 for ((key, value) in parameters) {
422 writeParameter(key, value)
423 }
424 }
425
426 /** Utility function to help deal with the unsafe nature of the typed Key/Value pairs. */
427 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
writeParameternull428 public fun CaptureRequest.Builder.writeParameter(key: Any?, value: Any?) {
429 if (key != null && key is CaptureRequest.Key<*>) {
430 try {
431 @Suppress("UNCHECKED_CAST") this.set(key as CaptureRequest.Key<Any>, value)
432 } catch (e: IllegalArgumentException) {
433 // Setting keys on CaptureRequest.Builder can fail if the key is defined on some
434 // OS versions, but not on others. Log and ignore these kinds of failures.
435 //
436 // See b/309518353 for an example failure.
437 Log.warn(e) { "Failed to set [${key.name}: $value] on CaptureRequest.Builder" }
438 }
439 }
440 }
441
442 /**
443 * Utility function to put all metadata in the current map through an unchecked cast. The unchecked
444 * cast is necessary since CameraGraph.Config uses Map<*, Any?> as the standard type for parameters.
445 */
446 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
putAllMetadatanull447 public fun MutableMap<Any, Any?>.putAllMetadata(metadata: Map<*, Any?>) {
448 @Suppress("UNCHECKED_CAST") this.putAll(metadata as Map<Any, Any?>)
449 }
450