1 /*
<lambda>null2  * Copyright (C) 2024 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 package com.google.jetpackcamera.core.camera
17 
18 import android.annotation.SuppressLint
19 import android.hardware.camera2.CameraCharacteristics
20 import androidx.annotation.OptIn
21 import androidx.camera.camera2.interop.Camera2CameraInfo
22 import androidx.camera.camera2.interop.ExperimentalCamera2Interop
23 import androidx.camera.core.CameraInfo
24 import androidx.camera.core.CameraSelector
25 import androidx.camera.core.DynamicRange as CXDynamicRange
26 import androidx.camera.core.ImageCapture
27 import androidx.camera.core.Preview
28 import androidx.camera.core.UseCase
29 import androidx.camera.core.UseCaseGroup
30 import androidx.camera.video.Recorder
31 import androidx.camera.video.VideoCapture
32 import com.google.jetpackcamera.settings.model.DynamicRange
33 import com.google.jetpackcamera.settings.model.ImageOutputFormat
34 import com.google.jetpackcamera.settings.model.LensFacing
35 
36 val CameraInfo.appLensFacing: LensFacing
37     get() = when (this.lensFacing) {
38         CameraSelector.LENS_FACING_FRONT -> LensFacing.FRONT
39         CameraSelector.LENS_FACING_BACK -> LensFacing.BACK
40         else -> throw IllegalArgumentException(
41             "Unknown CameraSelector.LensFacing -> LensFacing mapping. " +
42                 "[CameraSelector.LensFacing: ${this.lensFacing}]"
43         )
44     }
45 
toSupportedAppDynamicRangenull46 fun CXDynamicRange.toSupportedAppDynamicRange(): DynamicRange? {
47     return when (this) {
48         CXDynamicRange.SDR -> DynamicRange.SDR
49         CXDynamicRange.HLG_10_BIT -> DynamicRange.HLG10
50         // All other dynamic ranges unsupported. Return null.
51         else -> null
52     }
53 }
54 
toCXDynamicRangenull55 fun DynamicRange.toCXDynamicRange(): CXDynamicRange {
56     return when (this) {
57         com.google.jetpackcamera.settings.model.DynamicRange.SDR -> CXDynamicRange.SDR
58         com.google.jetpackcamera.settings.model.DynamicRange.HLG10 -> CXDynamicRange.HLG_10_BIT
59     }
60 }
61 
toCameraSelectornull62 fun LensFacing.toCameraSelector(): CameraSelector = when (this) {
63     LensFacing.FRONT -> CameraSelector.DEFAULT_FRONT_CAMERA
64     LensFacing.BACK -> CameraSelector.DEFAULT_BACK_CAMERA
65 }
66 
67 @SuppressLint("RestrictedApi")
CameraSelectornull68 fun CameraSelector.toAppLensFacing(): LensFacing = when (this.lensFacing) {
69     CameraSelector.LENS_FACING_FRONT -> LensFacing.FRONT
70     CameraSelector.LENS_FACING_BACK -> LensFacing.BACK
71     else -> throw IllegalArgumentException(
72         "Unknown CameraSelector -> LensFacing mapping. [CameraSelector: $this]"
73     )
74 }
75 
76 val CameraInfo.sensorLandscapeRatio: Float
77     @OptIn(ExperimentalCamera2Interop::class)
78     get() = Camera2CameraInfo.from(this)
79         .getCameraCharacteristic(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE)
sensorRectnull80         ?.let { sensorRect ->
81             if (sensorRect.width() > sensorRect.height()) {
82                 sensorRect.width().toFloat() / sensorRect.height()
83             } else {
84                 sensorRect.height().toFloat() / sensorRect.width()
85             }
86         } ?: Float.NaN
87 
toAppImageFormatnull88 fun Int.toAppImageFormat(): ImageOutputFormat? {
89     return when (this) {
90         ImageCapture.OUTPUT_FORMAT_JPEG -> ImageOutputFormat.JPEG
91         ImageCapture.OUTPUT_FORMAT_JPEG_ULTRA_HDR -> ImageOutputFormat.JPEG_ULTRA_HDR
92         // All other output formats unsupported. Return null.
93         else -> null
94     }
95 }
96 
97 /**
98  * Checks if preview stabilization is supported by the device.
99  *
100  */
101 val CameraInfo.isPreviewStabilizationSupported: Boolean
102     get() = Preview.getPreviewCapabilities(this).isStabilizationSupported
103 
104 /**
105  * Checks if video stabilization is supported by the device.
106  *
107  */
108 val CameraInfo.isVideoStabilizationSupported: Boolean
109     get() = Recorder.getVideoCapabilities(this).isStabilizationSupported
110 
CameraInfonull111 fun CameraInfo.filterSupportedFixedFrameRates(desired: Set<Int>): Set<Int> {
112     return buildSet {
113         this@filterSupportedFixedFrameRates.supportedFrameRateRanges.forEach { e ->
114             if (e.upper == e.lower && desired.contains(e.upper)) {
115                 add(e.upper)
116             }
117         }
118     }
119 }
120 
121 val CameraInfo.supportedImageFormats: Set<ImageOutputFormat>
122     get() = ImageCapture.getImageCaptureCapabilities(this).supportedOutputFormats
123         .mapNotNull(Int::toAppImageFormat)
124         .toSet()
125 
UseCaseGroupnull126 fun UseCaseGroup.getVideoCapture() = getUseCaseOrNull<VideoCapture<Recorder>>()
127 fun UseCaseGroup.getImageCapture() = getUseCaseOrNull<ImageCapture>()
128 
129 private inline fun <reified T : UseCase> UseCaseGroup.getUseCaseOrNull(): T? {
130     return useCases.filterIsInstance<T>().singleOrNull()
131 }
132