1 /* 2 * Copyright 2021 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.compat 18 19 import android.graphics.SurfaceTexture 20 import android.hardware.camera2.params.OutputConfiguration 21 import android.os.Build 22 import android.util.Size 23 import android.view.Surface 24 import android.view.SurfaceHolder 25 import androidx.annotation.RequiresApi 26 import androidx.camera.camera2.pipe.CameraId 27 import androidx.camera.camera2.pipe.OutputStream.DynamicRangeProfile 28 import androidx.camera.camera2.pipe.OutputStream.MirrorMode 29 import androidx.camera.camera2.pipe.OutputStream.OutputType 30 import androidx.camera.camera2.pipe.OutputStream.SensorPixelMode 31 import androidx.camera.camera2.pipe.OutputStream.StreamUseCase 32 import androidx.camera.camera2.pipe.OutputStream.TimestampBase 33 import androidx.camera.camera2.pipe.UnsafeWrapper 34 import androidx.camera.camera2.pipe.compat.OutputConfigurationWrapper.Companion.SURFACE_GROUP_ID_NONE 35 import androidx.camera.camera2.pipe.core.Log 36 import androidx.camera.camera2.pipe.core.checkNOrHigher 37 import androidx.camera.camera2.pipe.core.checkOOrHigher 38 import androidx.camera.camera2.pipe.core.checkPOrHigher 39 import java.util.concurrent.Executor 40 import kotlin.reflect.KClass 41 42 /** 43 * A data class that mirrors the fields in [android.hardware.camera2.params.SessionConfiguration] so 44 * that a real instance can be constructed when creating a 45 * [android.hardware.camera2.CameraCaptureSession] on newer versions of the OS. 46 */ 47 internal data class SessionConfigData( 48 val sessionType: Int, 49 val inputConfiguration: List<InputConfigData>?, 50 val outputConfigurations: List<OutputConfigurationWrapper>, 51 val executor: Executor, 52 val stateCallback: CameraCaptureSessionWrapper.StateCallback, 53 val sessionTemplateId: Int, 54 val sessionParameters: Map<*, Any?>, 55 ) 56 57 /** 58 * A data class that mirrors the fields in 59 * [android.hardware.camera2.params.ExtensionSessionConfiguration] so that a real instance can be 60 * constructed when creating a [android.hardware.camera2.CameraExtensionSession] on newer versions 61 * of the OS. 62 */ 63 internal data class ExtensionSessionConfigData( 64 val sessionType: Int, 65 val outputConfigurations: List<OutputConfigurationWrapper>, 66 val executor: Executor, 67 val stateCallback: CameraCaptureSessionWrapper.StateCallback, 68 val sessionTemplateId: Int, 69 val sessionParameters: Map<*, Any?>, 70 val extensionMode: Int? = null, 71 val extensionStateCallback: CameraExtensionSessionWrapper.StateCallback? = null, 72 val postviewOutputConfiguration: OutputConfigurationWrapper? = null 73 ) 74 75 internal object Camera2SessionTypes { 76 const val SESSION_TYPE_REGULAR = 0 77 const val SESSION_TYPE_HIGH_SPEED = 1 78 const val SESSION_TYPE_EXTENSION = 2 79 } 80 81 /** 82 * A data class that mirrors the fields in [android.hardware.camera2.params.InputConfiguration] so 83 * that a real instance can be constructed when creating a 84 * [android.hardware.camera2.CameraCaptureSession] on newer versions of the OS. 85 */ 86 internal data class InputConfigData(val width: Int, val height: Int, val format: Int) 87 88 /** 89 * An interface for [OutputConfiguration] with minor modifications. 90 * 91 * The primary modifications to this class are to make it harder to accidentally changes things that 92 * cannot be modified after the [android.hardware.camera2.CameraCaptureSession] has been created. 93 * 94 * [OutputConfiguration]'s are NOT immutable, and changing state of an [OutputConfiguration] may 95 * require the CameraCaptureSession to be finalized or updated. 96 */ 97 internal interface OutputConfigurationWrapper : UnsafeWrapper { 98 /** 99 * This method will return null if the output configuration was created without a Surface, and 100 * until addSurface is called for the first time. 101 * 102 * @see OutputConfiguration.getSurface 103 */ 104 val surface: Surface? 105 106 /** 107 * This method returns the current list of surfaces for this [OutputConfiguration]. Since the 108 * [OutputConfiguration] is stateful, this value may change as a result of calling addSurface or 109 * removeSurface. 110 * 111 * @see OutputConfiguration.getSurfaces 112 */ 113 val surfaces: List<Surface> 114 115 /** @see OutputConfiguration.addSurface */ addSurfacenull116 fun addSurface(surface: Surface) 117 118 /** @see OutputConfiguration.removeSurface */ 119 fun removeSurface(surface: Surface) 120 121 /** @see OutputConfiguration.setPhysicalCameraId */ 122 val physicalCameraId: CameraId? 123 124 /** @see OutputConfiguration.enableSurfaceSharing */ 125 val surfaceSharing: Boolean 126 127 /** @see OutputConfiguration.getMaxSharedSurfaceCount */ 128 val maxSharedSurfaceCount: Int 129 130 /** @see OutputConfiguration.getSurfaceGroupId */ 131 val surfaceGroupId: Int 132 133 companion object { 134 const val SURFACE_GROUP_ID_NONE = -1 135 } 136 } 137 138 @RequiresApi(24) 139 internal class AndroidOutputConfiguration( 140 private val output: OutputConfiguration, 141 override val surfaceSharing: Boolean, 142 override val maxSharedSurfaceCount: Int, 143 override val physicalCameraId: CameraId? 144 ) : OutputConfigurationWrapper { 145 146 @RequiresApi(24) 147 companion object { 148 /** 149 * Create and validate an OutputConfiguration for Camera2. null is returned when a 150 * non-exceptional error is encountered when creating the OutputConfiguration. 151 */ createnull152 fun create( 153 surface: Surface?, 154 outputType: OutputType = OutputType.SURFACE, 155 mirrorMode: MirrorMode? = null, 156 timestampBase: TimestampBase? = null, 157 dynamicRangeProfile: DynamicRangeProfile? = null, 158 streamUseCase: StreamUseCase? = null, 159 sensorPixelModes: List<SensorPixelMode> = emptyList(), 160 size: Size? = null, 161 surfaceSharing: Boolean = false, 162 surfaceGroupId: Int = SURFACE_GROUP_ID_NONE, 163 physicalCameraId: CameraId? = null, 164 ): OutputConfigurationWrapper? { 165 check(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) 166 167 // Create the OutputConfiguration using the groupId via the constructor (if set) 168 val configuration: OutputConfiguration 169 if (outputType == OutputType.SURFACE) { 170 check(surface != null) { 171 "OutputConfigurations defined with ${OutputType.SURFACE} must provide a" 172 "non-null surface!" 173 } 174 // OutputConfiguration will, on some OS versions, attempt to read the surface size 175 // from the Surface object. If the Surface has been destroyed, this check will fail. 176 // Because there's no way to cleanly synchronize and check the value, we catch the 177 // exception for these cases. 178 try { 179 configuration = 180 if (surfaceGroupId != SURFACE_GROUP_ID_NONE) { 181 OutputConfiguration(surfaceGroupId, surface) 182 } else { 183 OutputConfiguration(surface) 184 } 185 } catch (e: Throwable) { 186 Log.warn(e) { "Failed to create an OutputConfiguration for $surface!" } 187 return null 188 } 189 } else { 190 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { 191 throw IllegalStateException( 192 "Deferred OutputConfigurations are not supported on API " + 193 "${Build.VERSION.SDK_INT} (requires API ${Build.VERSION_CODES.O})" 194 ) 195 } 196 197 check(size != null) { 198 "Size must defined when creating a deferred OutputConfiguration." 199 } 200 val outputKlass = 201 when (outputType) { 202 OutputType.SURFACE_TEXTURE -> SurfaceTexture::class.java 203 OutputType.SURFACE_VIEW -> SurfaceHolder::class.java 204 else -> throw IllegalStateException("Unsupported OutputType: $outputType") 205 } 206 configuration = Api26Compat.newOutputConfiguration(size, outputKlass) 207 } 208 209 // Enable surface sharing, if set. 210 if (surfaceSharing) { 211 configuration.enableSurfaceSharingCompat() 212 } 213 214 // Pass along the physicalCameraId, if set. 215 if (physicalCameraId != null) { 216 configuration.setPhysicalCameraIdCompat(physicalCameraId) 217 } 218 219 if (mirrorMode != null) { 220 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { 221 Api33Compat.setMirrorMode(configuration, mirrorMode.value) 222 } else { 223 if (mirrorMode != MirrorMode.MIRROR_MODE_AUTO) { 224 Log.warn { 225 "Cannot set mirrorMode to a non-default value on " + 226 "API ${Build.VERSION.SDK_INT}. This may result in unexpected " + 227 "behavior. Requested $mirrorMode" 228 } 229 } 230 } 231 } 232 233 if (timestampBase != null) { 234 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { 235 Api33Compat.setTimestampBase(configuration, timestampBase.value) 236 } else { 237 if (timestampBase != TimestampBase.TIMESTAMP_BASE_DEFAULT) { 238 Log.info { 239 "The timestamp base on API ${Build.VERSION.SDK_INT} will " + 240 "default to TIMESTAMP_BASE_DEFAULT, with which the camera device" + 241 " adjusts timestamps based on the output target. " + 242 "Requested $timestampBase" 243 } 244 } 245 } 246 } 247 248 if (dynamicRangeProfile != null) { 249 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { 250 Api33Compat.setDynamicRangeProfile(configuration, dynamicRangeProfile.value) 251 } else { 252 if (dynamicRangeProfile != DynamicRangeProfile.STANDARD) { 253 Log.warn { 254 "Cannot set dynamicRangeProfile to a non-default value on API " + 255 "${Build.VERSION.SDK_INT}. This may result in unexpected " + 256 "behavior. Requested $dynamicRangeProfile" 257 } 258 } 259 } 260 } 261 262 if (streamUseCase != null) { 263 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { 264 Api33Compat.setStreamUseCase(configuration, streamUseCase.value) 265 } 266 } 267 268 if (sensorPixelModes.isNotEmpty()) { 269 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { 270 for (sensorPixelMode in sensorPixelModes) { 271 Api31Compat.addSensorPixelModeUsed(configuration, sensorPixelMode.value) 272 } 273 } else { 274 Log.warn { 275 "Cannot add sensorPixelModeUsed value on API ${Build.VERSION.SDK_INT}. " + 276 "This may result in unexpected behavior. Requested $sensorPixelModes" 277 } 278 } 279 } 280 281 // Create and return the Android 282 return AndroidOutputConfiguration( 283 configuration, 284 surfaceSharing, 285 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { 286 Api28Compat.getMaxSharedSurfaceCount(configuration) 287 } else { 288 1 289 }, 290 physicalCameraId 291 ) 292 } 293 OutputConfigurationnull294 private fun OutputConfiguration.enableSurfaceSharingCompat() { 295 checkNOrHigher("surfaceSharing") 296 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 297 Api26Compat.enableSurfaceSharing(this) 298 } 299 } 300 OutputConfigurationnull301 private fun OutputConfiguration.setPhysicalCameraIdCompat(physicalCameraId: CameraId) { 302 checkPOrHigher("physicalCameraId") 303 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { 304 Api28Compat.setPhysicalCameraId(this, physicalCameraId.value) 305 } 306 } 307 } 308 309 override val surface: Surface? = output.surface 310 override val surfaces: List<Surface> 311 get() { 312 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { 313 return Api26Compat.getSurfaces(output) 314 } 315 316 // On older versions of the OS, only one surface is allowed, and if an output 317 // configuration is in a deferred state it may not have a surface when it's first 318 // created. 319 return listOfNotNull(output.surface) 320 } 321 addSurfacenull322 override fun addSurface(surface: Surface) { 323 checkOOrHigher("addSurface") 324 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 325 Api26Compat.addSurfaces(output, surface) 326 } 327 } 328 removeSurfacenull329 override fun removeSurface(surface: Surface) { 330 checkPOrHigher("removeSurface") 331 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { 332 Api28Compat.removeSurface(output, surface) 333 } 334 } 335 336 override val surfaceGroupId: Int 337 get() = output.surfaceGroupId 338 339 @Suppress("UNCHECKED_CAST") unwrapAsnull340 override fun <T : Any> unwrapAs(type: KClass<T>): T? = 341 when (type) { 342 OutputConfiguration::class -> output as T 343 else -> null 344 } 345 toStringnull346 override fun toString(): String = output.toString() 347 } 348