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