blob: 10ac90ec10cd962ff05f2c8f715a3206777c7b9b [file] [log] [blame]
/*
* Copyright 2020 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.integration.impl
import android.hardware.camera2.CameraCaptureSession
import android.hardware.camera2.CaptureFailure
import android.hardware.camera2.CaptureRequest
import android.hardware.camera2.CaptureResult
import android.hardware.camera2.TotalCaptureResult
import android.os.Build
import android.view.Surface
import androidx.annotation.DoNotInline
import androidx.annotation.RequiresApi
import androidx.camera.camera2.pipe.CameraTimestamp
import androidx.camera.camera2.pipe.FrameInfo
import androidx.camera.camera2.pipe.FrameMetadata
import androidx.camera.camera2.pipe.FrameNumber
import androidx.camera.camera2.pipe.Request
import androidx.camera.camera2.pipe.RequestFailure
import androidx.camera.camera2.pipe.RequestMetadata
import androidx.camera.camera2.pipe.StreamId
import androidx.camera.camera2.pipe.integration.adapter.CameraUseCaseAdapter
import androidx.camera.camera2.pipe.integration.adapter.CaptureResultAdapter
import androidx.camera.camera2.pipe.integration.config.CameraScope
import androidx.camera.core.impl.CameraCaptureCallback
import androidx.camera.core.impl.CameraCaptureFailure
import androidx.camera.core.impl.CaptureConfig
import androidx.camera.core.impl.TagBundle
import java.util.concurrent.Executor
import javax.inject.Inject
/**
* A map of [CameraCaptureCallback] that are invoked on each [Request].
*/
@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
@CameraScope
class CameraCallbackMap @Inject constructor() : Request.Listener {
private val callbackMap = mutableMapOf<CameraCaptureCallback, Executor>()
@Volatile
private var callbacks: Map<CameraCaptureCallback, Executor> = mapOf()
fun addCaptureCallback(callback: CameraCaptureCallback, executor: Executor) {
check(!callbacks.contains(callback)) { "$callback was already registered!" }
synchronized(callbackMap) {
callbackMap[callback] = executor
callbacks = callbackMap.toMap()
}
}
fun removeCaptureCallback(callback: CameraCaptureCallback) {
synchronized(callbackMap) {
callbackMap.remove(callback)
callbacks = callbackMap.toMap()
}
}
override fun onBufferLost(
requestMetadata: RequestMetadata,
frameNumber: FrameNumber,
stream: StreamId
) {
for ((callback, executor) in callbacks) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N &&
callback is CameraUseCaseAdapter.CaptureCallbackContainer
) {
val session: CameraCaptureSession? =
requestMetadata.unwrapAs(CameraCaptureSession::class)
val request: CaptureRequest? = requestMetadata.unwrapAs(CaptureRequest::class)
val surface: Surface? = requestMetadata.streams[stream]
if (session != null && request != null && surface != null) {
executor.execute {
Api24CompatImpl.onCaptureBufferLost(
callback.captureCallback, session, request, surface, frameNumber.value
)
}
}
}
}
}
override fun onComplete(
requestMetadata: RequestMetadata,
frameNumber: FrameNumber,
result: FrameInfo
) {
for ((callback, executor) in callbacks) {
if (callback is CameraUseCaseAdapter.CaptureCallbackContainer) {
val session: CameraCaptureSession? =
requestMetadata.unwrapAs(CameraCaptureSession::class)
val request: CaptureRequest? = requestMetadata.unwrapAs(CaptureRequest::class)
val totalCaptureResult: TotalCaptureResult? =
result.unwrapAs(TotalCaptureResult::class)
if (session != null && request != null && totalCaptureResult != null) {
executor.execute {
callback.captureCallback.onCaptureCompleted(
session, request,
totalCaptureResult
)
}
}
} else {
val captureResult = CaptureResultAdapter(requestMetadata, frameNumber, result)
executor.execute {
callback.onCaptureCompleted(requestMetadata.getCaptureConfigId(), captureResult)
}
}
}
}
private fun RequestMetadata.getCaptureConfigId(): Int {
val tagBundle = this[CAMERAX_TAG_BUNDLE]
return tagBundle?.getTag(CaptureConfig.CAPTURE_CONFIG_ID_TAG_KEY)
as? Int ?: CaptureConfig.DEFAULT_ID
}
override fun onFailed(
requestMetadata: RequestMetadata,
frameNumber: FrameNumber,
requestFailure: RequestFailure
) {
for ((callback, executor) in callbacks) {
if (callback is CameraUseCaseAdapter.CaptureCallbackContainer) {
val session: CameraCaptureSession? =
requestMetadata.unwrapAs(CameraCaptureSession::class)
val request: CaptureRequest? = requestMetadata.unwrapAs(CaptureRequest::class)
val captureFailure = requestFailure.unwrapAs(CaptureFailure::class)
if (session != null && request != null && captureFailure != null) {
executor.execute {
callback.captureCallback.onCaptureFailed(
session, request,
captureFailure
)
}
}
} else {
val failure = CameraCaptureFailure(CameraCaptureFailure.Reason.ERROR)
executor.execute {
callback.onCaptureFailed(requestMetadata.getCaptureConfigId(), failure)
}
}
}
}
override fun onAborted(request: Request) {
for ((callback, executor) in callbacks) {
// TODO: get the correct requestId
val tagBundle = request.extras[CAMERAX_TAG_BUNDLE] as? TagBundle
val captureConfigId = tagBundle?.getTag(CaptureConfig.CAPTURE_CONFIG_ID_TAG_KEY)
as? Int ?: CaptureConfig.DEFAULT_ID
executor.execute { callback.onCaptureCancelled(captureConfigId) }
}
}
override fun onPartialCaptureResult(
requestMetadata: RequestMetadata,
frameNumber: FrameNumber,
captureResult: FrameMetadata
) {
for ((callback, executor) in callbacks) {
if (callback is CameraUseCaseAdapter.CaptureCallbackContainer) {
val session: CameraCaptureSession? =
requestMetadata.unwrapAs(CameraCaptureSession::class)
val request: CaptureRequest? = requestMetadata.unwrapAs(CaptureRequest::class)
val partialResult: CaptureResult? = captureResult.unwrapAs(CaptureResult::class)
if (session != null && request != null && partialResult != null) {
executor.execute {
callback.captureCallback.onCaptureProgressed(
session, request, partialResult
)
}
}
}
}
}
override fun onRequestSequenceAborted(requestMetadata: RequestMetadata) {
for ((callback, executor) in callbacks) {
if (callback is CameraUseCaseAdapter.CaptureCallbackContainer) {
val session: CameraCaptureSession? =
requestMetadata.unwrapAs(CameraCaptureSession::class)
val request: CaptureRequest? = requestMetadata.unwrapAs(CaptureRequest::class)
if (session != null && request != null) {
executor.execute {
callback.captureCallback.onCaptureSequenceAborted(
session, -1 /*sequenceId*/
)
}
}
} else {
executor.execute {
callback.onCaptureCancelled(requestMetadata.getCaptureConfigId())
}
}
}
}
override fun onRequestSequenceCompleted(
requestMetadata: RequestMetadata,
frameNumber: FrameNumber
) {
for ((callback, executor) in callbacks) {
if (callback is CameraUseCaseAdapter.CaptureCallbackContainer) {
val session: CameraCaptureSession? =
requestMetadata.unwrapAs(CameraCaptureSession::class)
val request: CaptureRequest? = requestMetadata.unwrapAs(CaptureRequest::class)
if (session != null && request != null) {
executor.execute {
callback.captureCallback.onCaptureSequenceCompleted(
session, -1 /*sequenceId*/, frameNumber.value
)
}
}
}
}
}
override fun onStarted(
requestMetadata: RequestMetadata,
frameNumber: FrameNumber,
timestamp: CameraTimestamp
) {
for ((callback, executor) in callbacks) {
if (callback is CameraUseCaseAdapter.CaptureCallbackContainer) {
val session: CameraCaptureSession? =
requestMetadata.unwrapAs(CameraCaptureSession::class)
val request: CaptureRequest? = requestMetadata.unwrapAs(CaptureRequest::class)
if (session != null && request != null) {
executor.execute {
callback.captureCallback.onCaptureStarted(
session, request, timestamp.value, frameNumber.value
)
}
}
} else {
executor.execute { callback.onCaptureStarted(requestMetadata.getCaptureConfigId()) }
}
}
}
@RequiresApi(24)
private object Api24CompatImpl {
@DoNotInline
@JvmStatic
fun onCaptureBufferLost(
callback: CameraCaptureSession.CaptureCallback,
session: CameraCaptureSession,
request: CaptureRequest,
surface: Surface,
frameNumber: Long
) {
callback.onCaptureBufferLost(
session, request, surface, frameNumber
)
}
}
companion object {
fun createFor(
callbacks: Collection<CameraCaptureCallback>,
executor: Executor
): CameraCallbackMap {
return CameraCallbackMap().apply {
callbacks.forEach { callback -> addCaptureCallback(callback, executor) }
}
}
}
}