1 /*
2  * Copyright 2023 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 androidx.annotation.RestrictTo
20 import androidx.camera.camera2.pipe.FrameReference.Companion.acquire
21 import androidx.camera.camera2.pipe.media.OutputImage
22 
23 /**
24  * A [Frame] is a container for all of the data and outputs that are sent to, and produced from, a
25  * single request issued to the camera.
26  *
27  * A frame represents a single "exposure" and/or moment in time. Since many modern cameras operate
28  * multiple individual sub-cameras together as a larger "logical" camera, this means that the
29  * outputs produced by the frame may contain outputs from more than one individual sub camera.
30  *
31  * Frames allow a developer to reason about the outputs from the camera without having to do all of
32  * the timestamp correlation and internal error handling. In the simple case, a frame will have the
33  * original request, the fully resolved [RequestMetadata] (which includes any modifications due to
34  * required parameters, 3A state, etc), a unique FrameId, the camera provided timestamp, and
35  * accessors for getting images when they are available. Frames are created as soon as the camera
36  * indicates an exposure has started, and output images may not be immediately available.
37  *
38  * Since a Frame holds onto expensive objects (Images) it is very important to make sure each frame
39  * is ALWAYS closed as soon as it is no longer needed. Cameras can easily operate at 30-60 frames
40  * per second.
41  *
42  * Implementations of this interface are thread safe.
43  *
44  * **Warning**: All [AutoCloseable] resources, including the [Frame] itself, must be closed or it
45  * will result in resource leaks and/or camera stalls!
46  *
47  * Example:
48  * ```
49  * /** Process and save the jpeg output from a Frame */
50  * suspend fun processAndSaveFrame(frame: Frame): Boolean {
51  *     var jpegImage: OutputImage? = null
52  *     var frameMetadata: FrameMetadata? = null
53  *
54  *     frame.use {
55  *         jpegImage = frame[jpegStreamId].await()
56  *         frameInfo = frame.frameInfo.await()?.metadata
57  *     } // `frame` is closed here. jpegImage is not.
58  *
59  *
60  *     if (jpegImage == null || frameMetadata == null) {
61  *         jpegImage?.close() // Always close the image.
62  *         return false
63  *     }
64  *
65  *     // save is responsible for closing jpegImage
66  *     return save(jpegImage, frameMetadata)
67  * }
68  * ```
69  */
70 @JvmDefaultWithCompatibility
71 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
72 public interface Frame : FrameReference, AutoCloseable {
73     /**
74      * Return the [FrameInfo], if available or suspend until the FrameInfo has been resolved.
75      *
76      * Returns null if the frameInfo could not be produced for any reason, or if the frame is
77      * closed. If frameInfo is not available, [frameInfoStatus] can be used to understand why this
78      * metadata is not available.
79      */
awaitFrameInfonull80     public suspend fun awaitFrameInfo(): FrameInfo?
81 
82     /**
83      * Return the [FrameInfo], if available, for this Frame. This method does not block and will
84      * return null if the Frame has been closed, or if the [FrameInfo] has not yet been produced.
85      */
86     public fun getFrameInfo(): FrameInfo?
87 
88     /**
89      * Return the [OutputImage] for this [streamId], if available or suspend until the output for
90      * this stream has been resolved.
91      *
92      * Returns null if the image could not be produced for any reason, or if this frame is closed.
93      * If an image is not available, [imageStatus] can be used to understand the reason this image
94      * was not produced by the camera. Each call produces a unique [OutputImage] that *must* be
95      * closed to avoid memory leaks.
96      */
97     public suspend fun awaitImage(streamId: StreamId): OutputImage?
98 
99     /**
100      * Return the [OutputImage] for this [streamId], if available.
101      *
102      * Returns null if the image could not be produced for any reason, or if this frame is closed.
103      * If an image is not available, [imageStatus] can be used to understand the reason this image
104      * was not produced by the camera. Each call produces a unique [OutputImage] that *must* be
105      * closed to avoid memory leaks.
106      */
107     public fun getImage(streamId: StreamId): OutputImage?
108 
109     /**
110      * Listener for non-coroutine based applications that may need to be notified when the state of
111      * this [Frame] changes.
112      */
113     public fun addListener(listener: Listener)
114 
115     /** Listener for events about an [Frame] */
116     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
117     public interface Listener {
118         /**
119          * Invoked after an [Frame] has been created and has started.
120          *
121          * @param frameNumber is the camera-provided identifier for this Frame.
122          * @param frameTimestamp is the primary camera-provided timestamp for this Frame.
123          */
124         public fun onFrameStarted(frameNumber: FrameNumber, frameTimestamp: CameraTimestamp)
125 
126         /** Invoked after [FrameInfo] is available, or has failed to be produced. */
127         public fun onFrameInfoAvailable()
128 
129         /** Invoked after the output for a given [StreamId] has been produced. */
130         public fun onImageAvailable(streamId: StreamId)
131 
132         /**
133          * Invoked after *all* outputs for this [Frame] have been produced. This method will be
134          * invoked after [onImageAvailable] has been invoked for all relevant streams, and will be
135          * invoked immediately after [onFrameStarted] for frames that do not produce outputs.
136          */
137         public fun onImagesAvailable()
138 
139         /** Invoked after the [FrameInfo] and all outputs have been completed for this [Frame]. */
140         public fun onFrameComplete()
141     }
142 
143     public companion object {
144         public val Frame.request: Request
145             get() = this.requestMetadata.request
146 
147         public val FrameReference.isFrameInfoAvailable: Boolean
148             get() = this.frameInfoStatus == OutputStatus.AVAILABLE
149 
isImageAvailablenull150         public fun FrameReference.isImageAvailable(streamId: StreamId): Boolean =
151             this.imageStatus(streamId) == OutputStatus.AVAILABLE
152     }
153 }
154 
155 /** A [FrameId] a unique identifier that represents the order a [Frame] was produced in. */
156 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
157 @JvmInline
158 public value class FrameId(public val value: Long)
159 
160 /** Represents the status of an output from the camera with enum-like values. */
161 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
162 @JvmInline
163 public value class OutputStatus internal constructor(public val value: Int) {
164     public companion object {
165         /** Output is not yet available. */
166         public val PENDING: OutputStatus = OutputStatus(0)
167 
168         /** Output has arrived and is available. */
169         public val AVAILABLE: OutputStatus = OutputStatus(1)
170 
171         /**
172          * Output has been resolved, and is not available for some reason that is not due to Camera
173          * operation, error, or other internal behavior. For example, if the object holding an
174          * output is closed, the method to get the output may return [UNAVAILABLE].
175          */
176         public val UNAVAILABLE: OutputStatus = OutputStatus(2)
177 
178         /** Output is not available because the Camera reported an error for this output. */
179         public val ERROR_OUTPUT_FAILED: OutputStatus = OutputStatus(10)
180 
181         /** Output is not available because it was intentionally aborted, or arrived after close. */
182         public val ERROR_OUTPUT_ABORTED: OutputStatus = OutputStatus(11)
183 
184         /**
185          * Output is not available because it was unexpectedly dropped or failed to arrive from the
186          * camera without some other kind of explicit error.
187          */
188         public val ERROR_OUTPUT_MISSING: OutputStatus = OutputStatus(12)
189 
190         /**
191          * Output is not available because it was intentionally dropped due to rate limiting. This
192          * can happen when the configured output capacity has been exceeded. While this can happen
193          * under normal usage, it can also indicate that some bit of code is not correctly closing
194          * frames and/or images.
195          */
196         public val ERROR_OUTPUT_DROPPED: OutputStatus = OutputStatus(13)
197     }
198 }
199 
200 /**
201  * A FrameCapture represents a [Request] that has been sent to the Camera, but that has not yet
202  * started. This object serves as a placeholder until the Camera begins exposing the frame, at which
203  * point all interactions should happen on the provided [Frame].
204  *
205  * Closing this FrameCapture will *not* cancel or abort the [Request].
206  *
207  * **Warning**: This object *must* must be closed or it will result in resource leaks and/or camera
208  * stalls!
209  *
210  * Example:
211  * ```
212  * /** Capture, process, and save a jpeg from the camera.  */
213  * suspend fun captureFrame(cameraGraphSession: CameraGraph.Session): Boolean {
214  *     // Issue the request to the camera and return a deferred capture.
215  *     val frameCapture = cameraGraphSession.capture(
216  *         Request(
217  *             streams = listOf(viewfinderStream, jpegStream)
218  *         )
219  *     )
220  *
221  *     // Wait for the frame to start and then pass it to `processAndSaveFrame`
222  *     return frameCapture.use {
223  *         val frame = frameCapture.awaitFrame() // suspend
224  *         frameCapture.close() // close the frameCapture early since we have the Frame
225  *         if (frame != null) {
226  *             processAndSaveFrame(frame) // responsible for closing frame
227  *         } else {
228  *             false // capture failed
229  *         }
230  *     } // .use causes frameCapture to close, even if there is an exception
231  * }
232  * ```
233  */
234 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
235 public interface FrameCapture : AutoCloseable {
236     /** The [Request] that was used to issue this [FrameCapture]. */
237     public val request: Request
238 
239     /** Get the status of the pending [Frame]. */
240     public val status: OutputStatus
241 
242     /**
243      * Get or suspend until the [Frame] that will be produced by the camera for this [request] is
244      * available, failed, or aborted, or until this object is closed.
245      *
246      * Invoking this multiple times will produce distinct Frame instances that will need to be
247      * individually closed.
248      */
awaitFramenull249     public suspend fun awaitFrame(): Frame?
250 
251     /**
252      * Get the [Frame] that will was produced by the camera for this [request] or null if the
253      * request failed, was aborted, or if this [FrameCapture] was closed.
254      *
255      * Invoking this multiple times will produce distinct Frame instances that will need to be
256      * individually closed.
257      */
258     public fun getFrame(): Frame?
259 
260     /** Adds a [Frame.Listener] that will be invoked for each of the subsequent [Frame] events. */
261     public fun addListener(listener: Frame.Listener)
262 }
263 
264 /**
265  * A FrameReference is a weak reference to a [Frame]. It will not prevent the underlying frame from
266  * being closed or released unless the frame is acquired via [acquire] or [tryAcquire].
267  */
268 @JvmDefaultWithCompatibility
269 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
270 public interface FrameReference {
271     /**
272      * Metadata about the request that produced this [Frame].
273      *
274      * [RequestMetadata] includes any modifications to the original request that were made due to
275      * 3A, Zoom, default and required parameters defined, and more.
276      */
277     public val requestMetadata: RequestMetadata
278 
279     /**
280      * The unique, sequential identifier defined by CameraPipe for this Frame. This identifier is
281      * incremented each time a new exposure starts from the Camera.
282      */
283     public val frameId: FrameId
284 
285     /** The original camera provided [FrameNumber] from this [Frame] */
286     public val frameNumber: FrameNumber
287 
288     /** The original camera provided [CameraTimestamp] from this [Frame] */
289     public val frameTimestamp: CameraTimestamp
290 
291     /** Get the current [OutputStatus] for the FrameInfo of this Frame. */
292     public val frameInfoStatus: OutputStatus
293 
294     /** Get the current [OutputStatus] of the output for a given [streamId]. */
295     public fun imageStatus(streamId: StreamId): OutputStatus
296 
297     /**
298      * [StreamId]'s that can be used to access [OutputImage]s from this [Frame] via [Frame.getImage]
299      *
300      * **This may be different from the list of streams defined in the original [Request]!** since
301      * this list will only include streams that were internally created and managed by CameraPipe.
302      */
303     public val imageStreams: Set<StreamId>
304 
305     /**
306      * Acquire a reference to a [Frame] that can be independently managed or closed. A filter can be
307      * provided to limit which outputs are available.
308      */
309     public fun tryAcquire(streamFilter: Set<StreamId>? = null): Frame?
310 
311     public companion object {
312         /**
313          * Acquire a [Frame] from a [FrameReference]. The outputs can be limited by specifying a
314          * filter to restrict which outputs are acquired.
315          */
316         public fun FrameReference.acquire(streamFilter: Set<StreamId>? = null): Frame {
317             return checkNotNull(tryAcquire(streamFilter)) {
318                 "Failed to acquire a strong reference to $this!"
319             }
320         }
321     }
322 }
323