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.integration.compat 18 19 import android.hardware.camera2.CameraCharacteristics 20 import android.hardware.camera2.params.DynamicRangeProfiles 21 import android.os.Build 22 import androidx.annotation.RequiresApi 23 import androidx.camera.camera2.pipe.CameraMetadata 24 import androidx.camera.camera2.pipe.core.checkApi 25 import androidx.camera.core.DynamicRange 26 27 /** Helper for accessing features in DynamicRangeProfiles in a backwards compatible fashion. */ 28 public class DynamicRangeProfilesCompat 29 internal constructor(private val impl: DynamicRangeProfilesCompatImpl) { 30 /** The set of supported dynamic ranges. */ 31 public val supportedDynamicRanges: Set<DynamicRange> 32 get() = impl.supportedDynamicRanges 33 34 /** 35 * Returns a set of supported [DynamicRange] that can be referenced in a single capture request. 36 * 37 * For example if a particular 10-bit output capable device returns (STANDARD, HLG10, HDR10) as 38 * result from calling [supportedDynamicRanges] and 39 * [DynamicRangeProfiles.getProfileCaptureRequestConstraints] returns (STANDARD, HLG10) when 40 * given an argument of STANDARD. This means that the corresponding camera device will only 41 * accept and process capture requests that reference outputs configured using HDR10 dynamic 42 * range or alternatively some combination of STANDARD and HLG10. However trying to queue 43 * capture requests to outputs that reference both HDR10 and STANDARD/HLG10 will result in 44 * IllegalArgumentException. 45 * 46 * The list will be empty in case there are no constraints for the given dynamic range. 47 * 48 * @param dynamicRange The dynamic range that will be checked for constraints 49 * @return non-modifiable set of dynamic ranges 50 * @throws IllegalArgumentException If the dynamic range argument is not within the set returned 51 * by [supportedDynamicRanges]. 52 */ getDynamicRangeCaptureRequestConstraintsnull53 public fun getDynamicRangeCaptureRequestConstraints( 54 dynamicRange: DynamicRange 55 ): Set<DynamicRange> { 56 return impl.getDynamicRangeCaptureRequestConstraints(dynamicRange) 57 } 58 59 /** 60 * Checks whether a given dynamic range is suitable for latency sensitive use cases. 61 * 62 * Due to internal lookahead logic, camera outputs configured with some dynamic range profiles 63 * may experience additional latency greater than 3 buffers. Using camera outputs with such 64 * dynamic ranges for latency sensitive use cases such as camera preview is not recommended. 65 * Dynamic ranges that have such extra streaming delay are typically utilized for scenarios such 66 * as offscreen video recording. 67 * 68 * @param dynamicRange The dynamic range to check for extra latency 69 * @return `true` if the given profile is not suitable for latency sensitive use cases, `false` 70 * otherwise. 71 * @throws IllegalArgumentException If the dynamic range argument is not within the set returned 72 * by [supportedDynamicRanges]. 73 */ isExtraLatencyPresentnull74 public fun isExtraLatencyPresent(dynamicRange: DynamicRange): Boolean { 75 return impl.isExtraLatencyPresent(dynamicRange) 76 } 77 78 /** 79 * Returns the underlying framework [DynamicRangeProfiles]. 80 * 81 * @return the underlying [DynamicRangeProfiles] or `null` if the device doesn't support 10 bit 82 * dynamic range. 83 */ 84 @RequiresApi(33) toDynamicRangeProfilesnull85 public fun toDynamicRangeProfiles(): DynamicRangeProfiles? { 86 checkApi( 87 33, 88 "DynamicRangesCompat can only be " + 89 "converted to DynamicRangeProfiles on API 33 or higher." 90 ) 91 return impl.unwrap() 92 } 93 94 internal interface DynamicRangeProfilesCompatImpl { 95 val supportedDynamicRanges: Set<DynamicRange> 96 getDynamicRangeCaptureRequestConstraintsnull97 fun getDynamicRangeCaptureRequestConstraints(dynamicRange: DynamicRange): Set<DynamicRange> 98 99 fun isExtraLatencyPresent(dynamicRange: DynamicRange): Boolean 100 101 fun unwrap(): DynamicRangeProfiles? 102 } 103 104 public companion object { 105 /** 106 * Returns a [DynamicRangeProfilesCompat] using the capabilities derived from the provided 107 * characteristics. 108 * 109 * @param cameraMetadata the metaData used to derive dynamic range information. 110 * @return a [DynamicRangeProfilesCompat] object. 111 */ 112 public fun fromCameraMetaData(cameraMetadata: CameraMetadata): DynamicRangeProfilesCompat { 113 var rangesCompat: DynamicRangeProfilesCompat? = null 114 if (Build.VERSION.SDK_INT >= 33) { 115 rangesCompat = 116 toDynamicRangesCompat( 117 cameraMetadata[ 118 CameraCharacteristics.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES] 119 ) 120 } 121 return rangesCompat ?: DynamicRangeProfilesCompatBaseImpl.COMPAT_INSTANCE 122 } 123 124 /** 125 * Creates an instance from a framework [DynamicRangeProfiles] object. 126 * 127 * @param dynamicRangeProfiles a [DynamicRangeProfiles]. 128 * @return an equivalent [DynamicRangeProfilesCompat] object. 129 */ 130 @RequiresApi(33) 131 public fun toDynamicRangesCompat( 132 dynamicRangeProfiles: DynamicRangeProfiles? 133 ): DynamicRangeProfilesCompat? { 134 if (dynamicRangeProfiles == null) { 135 return null 136 } 137 checkApi( 138 33, 139 "DynamicRangeProfiles can only " + 140 "be converted to DynamicRangesCompat on API 33 or higher." 141 ) 142 return DynamicRangeProfilesCompat( 143 DynamicRangeProfilesCompatApi33Impl(dynamicRangeProfiles) 144 ) 145 } 146 } 147 } 148