1 /* 2 * Copyright 2025 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.core.internal 18 19 import android.media.MediaCodec 20 import android.util.Range 21 import androidx.camera.core.Logger 22 import androidx.camera.core.impl.CaptureConfig 23 import androidx.camera.core.impl.DeferrableSurface 24 import androidx.camera.core.impl.SessionConfig.OutputConfig 25 26 /** 27 * Modifier for the FPS range for preview-only scenarios in high-speed camera sessions. 28 * 29 * This class adjusts the FPS range of a repeating capture request when only a preview surface is 30 * active in a high-speed camera session. This is done to improve device compatibility by ensuring a 31 * lower bound of 30 FPS before recording starts. 32 * 33 * High-speed sessions typically allow only one preview and one video surface. This modifier ensures 34 * that when only the preview is active, the FPS range is adjusted to [30, high-speed fps], which is 35 * guaranteed to be supported. 36 * 37 * See b/405045641 for more detail. 38 */ 39 public class HighSpeedFpsModifier { 40 41 private companion object { 42 private const val TAG = "HighSpeedFpsModifier" 43 private const val PREVIEW_ONLY_FPS_LOWER = 30 44 } 45 46 /** 47 * Modifies the FPS range for a preview-only repeating capture request in a high-speed camera 48 * session. 49 * 50 * This method checks if the current output configuration includes a video surface and if the 51 * repeating capture request does not. If these conditions are met, and the FPS range is a fixed 52 * high-speed range (e.g., [120, 120]), it adjusts the range to [30, high-speed fps]. 53 * 54 * @param outputConfigs The collection of output configurations. 55 * @param repeatingConfigBuilder The builder for the repeating capture configuration. 56 */ modifyFpsForPreviewOnlyRepeatingnull57 public fun modifyFpsForPreviewOnlyRepeating( 58 outputConfigs: Collection<OutputConfig>, 59 repeatingConfigBuilder: CaptureConfig.Builder 60 ) { 61 if ( 62 outputConfigs.size == 2 && 63 outputConfigs.hasVideoSurface() && 64 !repeatingConfigBuilder.hasVideoSurface() 65 ) { 66 repeatingConfigBuilder.expectedFrameRateRange 67 ?.takeIf { it.isHighSpeedFixedFps() } 68 ?.let { repeatingConfigBuilder.setExpectedFrameRateRange(it.toPreviewOnlyRange()) } 69 } 70 } 71 Rangenull72 private fun Range<Int>.isHighSpeedFixedFps(): Boolean = upper >= 120 && lower == upper 73 74 private fun Range<Int>.toPreviewOnlyRange(): Range<Int> { 75 return Range(PREVIEW_ONLY_FPS_LOWER, upper).also { 76 Logger.d(TAG, "Modified high-speed FPS range from $this to $it") 77 } 78 } 79 <lambda>null80 private fun Collection<OutputConfig>.hasVideoSurface(): Boolean = any { 81 it.surface.isVideoSurface() 82 } 83 hasVideoSurfacenull84 private fun CaptureConfig.Builder.hasVideoSurface(): Boolean = 85 surfaces.any { it.isVideoSurface() } 86 DeferrableSurfacenull87 private fun DeferrableSurface.isVideoSurface(): Boolean = 88 containerClass == MediaCodec::class.java 89 } 90