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