blob: 2bc08b91e0c32be88a5ace89684d6cebcd7f30bd [file] [log] [blame]
/*
* Copyright 2021 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.
*/
@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
package androidx.camera.camera2.pipe.integration.impl
import android.graphics.ImageFormat
import android.graphics.SurfaceTexture
import android.hardware.camera2.CameraCharacteristics
import android.hardware.camera2.CameraDevice
import android.os.Build
import android.util.Size
import android.view.Surface
import androidx.annotation.GuardedBy
import androidx.annotation.RequiresApi
import androidx.camera.camera2.pipe.core.Log.error
import androidx.camera.camera2.pipe.core.Log.warn
import androidx.camera.camera2.pipe.integration.adapter.CameraUseCaseAdapter
import androidx.camera.camera2.pipe.integration.compat.workaround.getSupportedRepeatingSurfaceSizes
import androidx.camera.core.UseCase
import androidx.camera.core.impl.CaptureConfig
import androidx.camera.core.impl.Config
import androidx.camera.core.impl.DeferrableSurface
import androidx.camera.core.impl.ImageFormatConstants
import androidx.camera.core.impl.ImageInputConfig
import androidx.camera.core.impl.ImmediateSurface
import androidx.camera.core.impl.MutableOptionsBundle
import androidx.camera.core.impl.SessionConfig
import androidx.camera.core.impl.StreamSpec
import androidx.camera.core.impl.UseCaseConfig
import androidx.camera.core.impl.UseCaseConfig.OPTION_CAPTURE_TYPE
import androidx.camera.core.impl.UseCaseConfig.OPTION_SESSION_CONFIG_UNPACKER
import androidx.camera.core.impl.UseCaseConfigFactory
import androidx.camera.core.impl.UseCaseConfigFactory.CaptureType
import androidx.camera.core.impl.utils.executor.CameraXExecutors
import kotlin.math.min
private val DEFAULT_PREVIEW_SIZE = Size(0, 0)
/**
* A [UseCase] used to issue repeating requests when only [androidx.camera.core.ImageCapture] is
* enabled, since taking a picture may require a repeating surface to perform pre-capture checks,
* mainly around 3A.
*/
@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
class MeteringRepeating(
private val cameraProperties: CameraProperties,
config: MeteringRepeatingConfig,
private val displayInfoManager: DisplayInfoManager
) : UseCase(config) {
private val meteringSurfaceSize = getProperPreviewSize()
private val deferrableSurfaceLock = Any()
@GuardedBy("deferrableSurfaceLock")
private var deferrableSurface: DeferrableSurface? = null
override fun getDefaultConfig(applyDefaultConfig: Boolean, factory: UseCaseConfigFactory) =
Builder(cameraProperties, displayInfoManager).useCaseConfig
override fun getUseCaseConfigBuilder(config: Config) =
Builder(cameraProperties, displayInfoManager)
override fun onSuggestedStreamSpecUpdated(suggestedStreamSpec: StreamSpec): StreamSpec {
updateSessionConfig(createPipeline(meteringSurfaceSize).build())
notifyActive()
return suggestedStreamSpec.toBuilder().setResolution(meteringSurfaceSize).build()
}
override fun onUnbind() {
synchronized(deferrableSurfaceLock) {
deferrableSurface?.close()
deferrableSurface = null
}
}
/** Sets up the use case's session configuration, mainly its [DeferrableSurface]. */
fun setupSession() {
// The suggested stream spec passed to `updateSuggestedStreamSpec` doesn't matter since
// this use case uses the min preview size.
updateSuggestedStreamSpec(StreamSpec.builder(DEFAULT_PREVIEW_SIZE).build())
}
private fun createPipeline(resolution: Size): SessionConfig.Builder {
synchronized(deferrableSurfaceLock) {
val surfaceTexture = SurfaceTexture(0).apply {
setDefaultBufferSize(resolution.width, resolution.height)
}
val surface = Surface(surfaceTexture)
deferrableSurface?.close()
deferrableSurface = ImmediateSurface(surface, resolution, imageFormat)
deferrableSurface!!.terminationFuture
.addListener(
{
surface.release()
surfaceTexture.release()
},
CameraXExecutors.directExecutor()
)
}
return SessionConfig.Builder
.createFrom(MeteringRepeatingConfig(), resolution)
.apply {
setTemplateType(CameraDevice.TEMPLATE_PREVIEW)
addSurface(deferrableSurface!!)
addErrorListener { _, _ ->
updateSessionConfig(createPipeline(resolution).build())
notifyReset()
}
}
}
private fun CameraProperties.getOutputSizes(): Array<Size>? {
val map = metadata[CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP] ?: run {
error { "Can not retrieve SCALER_STREAM_CONFIGURATION_MAP." }
return null
}
return if (Build.VERSION.SDK_INT < 23) {
// ImageFormat.PRIVATE is only public after Android level 23. Therefore, use
// SurfaceTexture.class to get the supported output sizes before Android level 23.
map.getOutputSizes(SurfaceTexture::class.java)
} else {
map.getOutputSizes(ImageFormat.PRIVATE)
}
}
private fun getProperPreviewSize(): Size {
var outputSizes = cameraProperties.getOutputSizes()
if (outputSizes == null) {
error { "Can not get output size list." }
return DEFAULT_PREVIEW_SIZE
}
if (outputSizes.isEmpty()) {
error { "Output sizes empty" }
return DEFAULT_PREVIEW_SIZE
}
val supportedOutputSizes = outputSizes.getSupportedRepeatingSurfaceSizes()
if (supportedOutputSizes.isNotEmpty()) {
outputSizes = supportedOutputSizes
} else {
warn { "No supported output size list, fallback to current list" }
}
outputSizes.sortBy { size -> size.width.toLong() * size.height.toLong() }
// Find maximum supported resolution that is <= min(VGA, display resolution)
// Using minimum supported size could cause some issue on certain devices.
val previewSize = displayInfoManager.getPreviewSize()
val maxSizeProduct =
min(640L * 480L, previewSize.width.toLong() * previewSize.height.toLong())
var previousSize: Size? = null
for (outputSize in outputSizes) {
val product = outputSize.width.toLong() * outputSize.height.toLong()
if (product == maxSizeProduct) {
return outputSize
} else if (product > maxSizeProduct) {
return previousSize ?: break // fallback to minimum size.
}
previousSize = outputSize
}
// If not found, return the minimum size.
return outputSizes[0]
}
class MeteringRepeatingConfig : UseCaseConfig<MeteringRepeating>, ImageInputConfig {
private val config = MutableOptionsBundle.create().apply {
insertOption(
OPTION_SESSION_CONFIG_UNPACKER,
CameraUseCaseAdapter.DefaultSessionOptionsUnpacker
)
insertOption(OPTION_CAPTURE_TYPE, CaptureType.METERING_REPEATING)
}
override fun getCaptureType() = UseCaseConfigFactory.CaptureType.METERING_REPEATING
override fun getConfig() = config
override fun getInputFormat() = ImageFormatConstants.INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE
}
class Builder(
private val cameraProperties: CameraProperties,
private val displayInfoManager: DisplayInfoManager
) :
UseCaseConfig.Builder<MeteringRepeating, MeteringRepeatingConfig, Builder> {
override fun getMutableConfig() = MutableOptionsBundle.create()
override fun getUseCaseConfig() = MeteringRepeatingConfig()
override fun setTargetClass(targetClass: Class<MeteringRepeating>) = this
override fun setTargetName(targetName: String) = this
override fun setUseCaseEventCallback(eventCallback: EventCallback) = this
override fun setDefaultSessionConfig(sessionConfig: SessionConfig) = this
override fun setDefaultCaptureConfig(captureConfig: CaptureConfig) = this
override fun setSessionOptionUnpacker(optionUnpacker: SessionConfig.OptionUnpacker) = this
override fun setCaptureOptionUnpacker(optionUnpacker: CaptureConfig.OptionUnpacker) = this
override fun setSurfaceOccupancyPriority(priority: Int) = this
override fun setZslDisabled(disabled: Boolean) = this
override fun setHighResolutionDisabled(disabled: Boolean) = this
override fun setCaptureType(captureType: UseCaseConfigFactory.CaptureType) = this
override fun build(): MeteringRepeating {
return MeteringRepeating(cameraProperties, useCaseConfig, displayInfoManager)
}
}
}