blob: 9cc329c0a93c7ba6ccdc6b2dfb16b84498c74492 [file] [log] [blame]
/*
* Copyright 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.camera.camera2.pipe
import android.os.Build
import androidx.annotation.RequiresApi
import androidx.annotation.RestrictTo
import androidx.camera.camera2.pipe.FrameReference.Companion.acquire
import androidx.camera.camera2.pipe.media.OutputImage
/**
* A [Frame] is a container for all of the data and outputs that are sent to, and produced from, a
* single request issued to the camera.
*
* A frame represents a single "exposure" and/or moment in time. Since many modern cameras operate
* multiple individual sub-cameras together as a larger "logical" camera, this means that the
* outputs produced by the frame may contain outputs from more than one individual sub camera.
*
* Frames allow a developer to reason about the outputs from the camera without having to do all of
* the timestamp correlation and internal error handling. In the simple case, a frame will have the
* original request, the fully resolved [RequestMetadata] (which includes any modifications due to
* required parameters, 3A state, etc), a unique FrameId, the camera provided timestamp, and
* accessors for getting images when they are available. Frames are created as soon as the camera
* indicates an exposure has started, and output images may not be immediately available.
*
* Since a Frame holds onto expensive objects (Images) it is very important to make sure each frame
* is ALWAYS closed as soon as it is no longer needed. Cameras can easily operate at 30-60 frames
* per second.
*
* Implementations of this interface are thread safe.
*
* **Warning**: All [AutoCloseable] resources, including the [Frame] itself, must be closed or it
* will result in resource leaks and/or camera stalls!
*
* Example:
* ```
* /** Process and save the jpeg output from a Frame */
* suspend fun processAndSaveFrame(frame: Frame): Boolean {
* var jpegImage: OutputImage? = null
* var frameMetadata: FrameMetadata? = null
*
* frame.use {
* jpegImage = frame[jpegStreamId].await()
* frameInfo = frame.frameInfo.await()?.metadata
* } // `frame` is closed here. jpegImage is not.
*
*
* if (jpegImage == null || frameMetadata == null) {
* jpegImage?.close() // Always close the image.
* return false
* }
*
* // save is responsible for closing jpegImage
* return save(jpegImage, frameMetadata)
* }
* ```
*/
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
interface Frame : FrameReference, AutoCloseable {
/**
* Return the [FrameInfo], if available or suspend until the FrameInfo has been resolved.
*
* Returns null if the frameInfo could not be produced for any reason, or if the frame is
* closed. If frameInfo is not available, [frameInfoStatus] can be used to understand why this
* metadata is not available.
*/
suspend fun awaitFrameInfo(): FrameInfo?
/**
* Return the [FrameInfo], if available, for this Frame. This method does not block and will
* return null if the Frame has been closed, or if the [FrameInfo] has not yet been produced.
*/
fun getFrameInfo(): FrameInfo?
/**
* Return the [OutputImage] for this [streamId], if available or suspend until the output for
* this stream has been resolved.
*
* Returns null if the image could not be produced for any reason, or if this frame is closed.
* If an image is not available, [imageStatus] can be used to understand the reason this image
* was not produced by the camera. Each call produces a unique [OutputImage] that *must* be
* closed to avoid memory leaks.
*/
suspend fun awaitImage(streamId: StreamId): OutputImage?
/**
* Return the [OutputImage] for this [streamId], if available.
*
* Returns null if the image could not be produced for any reason, or if this frame is closed.
* If an image is not available, [imageStatus] can be used to understand the reason this image
* was not produced by the camera. Each call produces a unique [OutputImage] that *must* be
* closed to avoid memory leaks.
*/
fun getImage(streamId: StreamId): OutputImage?
/**
* Listener for non-coroutine based applications that may need to be notified when the state
* of this [Frame] changes.
*/
fun addListener(listener: Listener)
/** Listener for events about an [Frame] */
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
interface Listener {
/**
* Invoked after an [Frame] has been created and has started.
*
* @param frameNumber is the camera-provided identifier for this Frame.
* @param frameTimestamp is the primary camera-provided timestamp for this Frame.
*/
fun onFrameStarted(
frameNumber: FrameNumber,
frameTimestamp: CameraTimestamp
)
/**
* Invoked after [FrameInfo] is available, or has failed to be produced.
*/
fun onFrameInfoAvailable()
/**
* Invoked after the output for a given [StreamId] has been produced.
*/
fun onImageAvailable(streamId: StreamId)
/**
* Invoked after *all* outputs for this [Frame] have been produced. This method will
* be invoked after [onImageAvailable] has been invoked for all relevant streams, and will
* be invoked immediately after [onFrameStarted] for frames that do not produce outputs.
*/
fun onImagesAvailable()
/**
* Invoked after the [FrameInfo] and all outputs have been completed for this [Frame].
*/
fun onFrameComplete()
}
companion object {
val Frame.request
get() = this.requestMetadata.request
val FrameReference.isFrameInfoAvailable
get() = this.frameInfoStatus == OutputStatus.AVAILABLE
fun FrameReference.isImageAvailable(streamId: StreamId) =
this.imageStatus(streamId) == OutputStatus.AVAILABLE
}
}
/**
* A [FrameId] a unique identifier that represents the order a [Frame] was produced in.
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@JvmInline
value class FrameId(val value: Long)
/**
* Represents the status of an output from the camera with enum-like values.
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@JvmInline
value class OutputStatus internal constructor(val value: Int) {
companion object {
/** Output is not yet available. */
val PENDING = OutputStatus(0)
/** Output has arrived and is available. */
val AVAILABLE = OutputStatus(1)
/**
* Output has been resolved, and is not available for some reason that is not due to Camera
* operation, error, or other internal behavior. For example, if the object holding an
* output is closed, the method to get the output may return [UNAVAILABLE].
*/
val UNAVAILABLE = OutputStatus(2)
/** Output is not available because the Camera reported an error for this output. */
val ERROR_OUTPUT_FAILED = OutputStatus(10)
/** Output is not available because it was intentionally aborted, or arrived after close. */
val ERROR_OUTPUT_ABORTED = OutputStatus(11)
/**
* Output is not available because it was unexpectedly dropped or failed to arrive from the
* camera without some other kind of explicit error.
*/
val ERROR_OUTPUT_MISSING = OutputStatus(12)
/**
* Output is not available because it was intentionally dropped due to rate limiting. This
* can happen when the configured output capacity has been exceeded. While this can happen
* under normal usage, it can also indicate that some bit of code is not correctly closing
* frames and/or images.
*/
val ERROR_OUTPUT_DROPPED = OutputStatus(13)
}
}
/**
* A FrameCapture represents a [Request] that has been sent to the Camera, but that has not yet
* started. This object serves as a placeholder until the Camera begins exposing the frame, at
* which point all interactions should happen on the provided [Frame].
*
* Closing this FrameCapture will *not* cancel or abort the [Request].
*
* **Warning**: This object *must* must be closed or it will result in resource leaks and/or camera
* stalls!
*
* Example:
*
* ```
* /** Capture, process, and save a jpeg from the camera. */
* suspend fun captureFrame(cameraGraphSession: CameraGraph.Session): Boolean {
* // Issue the request to the camera and return a deferred capture.
* val frameCapture = cameraGraphSession.capture(
* Request(
* streams = listOf(viewfinderStream, jpegStream)
* )
* )
*
* // Wait for the frame to start and then pass it to `processAndSaveFrame`
* return frameCapture.use {
* val frame = frameCapture.awaitFrame() // suspend
* frameCapture.close() // close the frameCapture early since we have the Frame
* if (frame != null) {
* processAndSaveFrame(frame) // responsible for closing frame
* } else {
* false // capture failed
* }
* } // .use causes frameCapture to close, even if there is an exception
* }
* ```
*/
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
interface FrameCapture : AutoCloseable {
/**
* The [Request] that was used to issue this [FrameCapture].
*/
val request: Request
/**
* Get the status of the pending [Frame].
*/
val status: OutputStatus
/**
* Get or suspend until the [Frame] that will be produced by the camera for this [request] is
* available, failed, or aborted, or until this object is closed.
*
* Invoking this multiple times will produce distinct Frame instances that will need to be
* individually closed.
*/
suspend fun awaitFrame(): Frame?
/**
* Get the [Frame] that will was produced by the camera for this [request] or null if the
* request failed, was aborted, or if this [FrameCapture] was closed.
*
* Invoking this multiple times will produce distinct Frame instances that will need to be
* individually closed.
*/
fun getFrame(): Frame?
/**
* Adds a [Frame.Listener] that will be invoked for each of the subsequent [Frame] events.
*/
fun addListener(listener: Frame.Listener)
}
/**
* A FrameReference is a weak reference to a [Frame]. It will not prevent the underlying frame
* from being closed or released unless the frame is acquired via [acquire] or [tryAcquire].
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
interface FrameReference {
/**
* Metadata about the request that produced this [Frame].
*
* [RequestMetadata] includes any modifications to the original request that were made due to
* 3A, Zoom, default and required parameters defined, and more.
*/
val requestMetadata: RequestMetadata
/**
* The unique, sequential identifier defined by CameraPipe for this Frame. This identifier is
* incremented each time a new exposure starts from the Camera.
*/
val frameId: FrameId
/** The original camera provided [FrameNumber] from this [Frame] */
val frameNumber: FrameNumber
/** The original camera provided [CameraTimestamp] from this [Frame] */
val frameTimestamp: CameraTimestamp
/**
* Get the current [OutputStatus] for the FrameInfo of this Frame.
*/
val frameInfoStatus: OutputStatus
/**
* Get the current [OutputStatus] of the output for a given [streamId].
*/
fun imageStatus(streamId: StreamId): OutputStatus
/**
* [StreamId]'s that can be used to access [OutputImage]s from this [Frame] via [Frame.getImage]
*
* **This may be different from the list of streams defined in the original [Request]!** since
* this list will only include streams that were internally created and managed by CameraPipe.
*/
val imageStreams: Set<StreamId>
/**
* Acquire a reference to a [Frame] that can be independently managed or closed. A filter can
* be provided to limit which outputs are available.
*/
fun tryAcquire(streamFilter: Set<StreamId>? = null): Frame?
companion object {
/**
* Acquire a [Frame] from a [FrameReference]. The outputs can be limited by specifying a
* filter to restrict which outputs are acquired.
*/
fun FrameReference.acquire(streamFilter: Set<StreamId>? = null): Frame {
return checkNotNull(tryAcquire(streamFilter)) {
"Failed to acquire a strong reference to $this!"
}
}
}
}