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