davidliu
Committed by GitHub

Fix CameraXSession not setting the target capture format correctly (#652)

* Fix CameraXSession not setting the target capture format correctly, and remove unused usecase

* changeset and spotless
  1 +---
  2 +"client-sdk-android": patch
  3 +---
  4 +
  5 +Fix CameraXSession not setting the target capture format correctly
1 /* 1 /*
2 - * Copyright 2024 LiveKit, Inc. 2 + * Copyright 2024-2025 LiveKit, Inc.
3 * 3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License. 5 * you may not use this file except in compliance with the License.
@@ -32,10 +32,12 @@ import androidx.camera.camera2.interop.Camera2CameraInfo @@ -32,10 +32,12 @@ import androidx.camera.camera2.interop.Camera2CameraInfo
32 import androidx.camera.camera2.interop.Camera2Interop 32 import androidx.camera.camera2.interop.Camera2Interop
33 import androidx.camera.core.Camera 33 import androidx.camera.core.Camera
34 import androidx.camera.core.CameraSelector 34 import androidx.camera.core.CameraSelector
35 -import androidx.camera.core.ImageAnalysis 35 +import androidx.camera.core.ExtendableBuilder
36 import androidx.camera.core.Preview 36 import androidx.camera.core.Preview
37 import androidx.camera.core.Preview.SurfaceProvider 37 import androidx.camera.core.Preview.SurfaceProvider
38 import androidx.camera.core.UseCase 38 import androidx.camera.core.UseCase
  39 +import androidx.camera.core.resolutionselector.ResolutionSelector
  40 +import androidx.camera.core.resolutionselector.ResolutionStrategy
39 import androidx.camera.lifecycle.ProcessCameraProvider 41 import androidx.camera.lifecycle.ProcessCameraProvider
40 import androidx.core.content.ContextCompat 42 import androidx.core.content.ContextCompat
41 import androidx.lifecycle.LifecycleOwner 43 import androidx.lifecycle.LifecycleOwner
@@ -154,9 +156,6 @@ internal constructor( @@ -154,9 +156,6 @@ internal constructor(
154 } ?: request.willNotProvideSurface() 156 } ?: request.willNotProvideSurface()
155 } 157 }
156 158
157 - // Set image analysis - camera params  
158 - val imageAnalysis = setImageAnalysis()  
159 -  
160 // Select camera by ID 159 // Select camera by ID
161 val cameraSelector = CameraSelector.Builder() 160 val cameraSelector = CameraSelector.Builder()
162 .addCameraFilter { cameraInfo -> cameraInfo.filter { Camera2CameraInfo.from(it).cameraId == cameraId } } 161 .addCameraFilter { cameraInfo -> cameraInfo.filter { Camera2CameraInfo.from(it).cameraId == cameraId } }
@@ -166,6 +165,17 @@ internal constructor( @@ -166,6 +165,17 @@ internal constructor(
166 ContextCompat.getMainExecutor(context).execute { 165 ContextCompat.getMainExecutor(context).execute {
167 // Preview 166 // Preview
168 val preview = Preview.Builder() 167 val preview = Preview.Builder()
  168 + .setResolutionSelector(
  169 + ResolutionSelector.Builder()
  170 + .setResolutionStrategy(
  171 + ResolutionStrategy(
  172 + Size(captureFormat?.width ?: width, captureFormat?.height ?: height),
  173 + ResolutionStrategy.FALLBACK_RULE_CLOSEST_HIGHER_THEN_LOWER,
  174 + ),
  175 + )
  176 + .build(),
  177 + )
  178 + .applyCameraSettings()
169 .build() 179 .build()
170 .also { 180 .also {
171 it.setSurfaceProvider(surfaceProvider) 181 it.setSurfaceProvider(surfaceProvider)
@@ -178,7 +188,6 @@ internal constructor( @@ -178,7 +188,6 @@ internal constructor(
178 camera = cameraProvider.bindToLifecycle( 188 camera = cameraProvider.bindToLifecycle(
179 lifecycleOwner, 189 lifecycleOwner,
180 cameraSelector, 190 cameraSelector,
181 - imageAnalysis,  
182 preview, 191 preview,
183 *useCases, 192 *useCases,
184 ) 193 )
@@ -195,11 +204,9 @@ internal constructor( @@ -195,11 +204,9 @@ internal constructor(
195 ) 204 )
196 } 205 }
197 206
198 - private fun setImageAnalysis() = ImageAnalysis.Builder()  
199 - .setTargetResolution(Size(captureFormat?.width ?: width, captureFormat?.height ?: height))  
200 - .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST).apply { 207 + private fun <T> ExtendableBuilder<T>.applyCameraSettings(): ExtendableBuilder<T> {
201 val cameraExtender = Camera2Interop.Extender(this) 208 val cameraExtender = Camera2Interop.Extender(this)
202 - captureFormat?.let { captureFormat -> 209 + val captureFormat = this@CameraXSession.captureFormat ?: return this
203 cameraExtender.setCaptureRequestOption( 210 cameraExtender.setCaptureRequestOption(
204 CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, 211 CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE,
205 Range( 212 Range(
@@ -220,11 +227,11 @@ internal constructor( @@ -220,11 +227,11 @@ internal constructor(
220 cameraExtender.setCaptureRequestOption(CaptureRequest.LENS_OPTICAL_STABILIZATION_MODE, LENS_OPTICAL_STABILIZATION_MODE_OFF) 227 cameraExtender.setCaptureRequestOption(CaptureRequest.LENS_OPTICAL_STABILIZATION_MODE, LENS_OPTICAL_STABILIZATION_MODE_OFF)
221 } 228 }
222 229
223 - else -> Unit 230 + else -> {
224 } 231 }
225 } 232 }
  233 + return this
226 } 234 }
227 - .build()  
228 235
229 private fun stopInternal() { 236 private fun stopInternal() {
230 Logging.d(TAG, "Stop internal") 237 Logging.d(TAG, "Stop internal")
@@ -25,7 +25,15 @@ data class LocalVideoTrackOptions( @@ -25,7 +25,15 @@ data class LocalVideoTrackOptions(
25 * will prefer a camera according to [position] 25 * will prefer a camera according to [position]
26 */ 26 */
27 val deviceId: String? = null, 27 val deviceId: String? = null,
  28 + /**
  29 + * The camera position to use.
  30 + */
28 val position: CameraPosition? = CameraPosition.FRONT, 31 val position: CameraPosition? = CameraPosition.FRONT,
  32 + /**
  33 + * Video capture options such as resolution and framerate.
  34 + *
  35 + * See [VideoPreset169] or [VideoPreset43] for preset parameters for common resolutions.
  36 + */
29 val captureParams: VideoCaptureParameter = VideoPreset169.H720.capture, 37 val captureParams: VideoCaptureParameter = VideoPreset169.H720.capture,
30 ) 38 )
31 39