Kasem Mohamed
Committed by GitHub

Add use cases to createCameraProvider (#536)

  1 +---
  2 +"client-sdk-android": minor
  3 +---
  4 +
  5 +Add use cases to CameraX createCameraProvider
1 # CameraX support for LiveKit Android SDK 1 # CameraX support for LiveKit Android SDK
2 [![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.livekit/livekit-android-camerax/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.livekit/livekit-android-camerax) 2 [![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.livekit/livekit-android-camerax/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.livekit/livekit-android-camerax)
3 3
4 -This library provides an CameraX integration for use with the Android LiveKit SDK. This provides access to more camera functionality such as custom zoom and torch control. 4 +This library provides an CameraX integration for use with the Android LiveKit SDK. This provides access to more camera functionality such as custom zoom, torch control and taking a picture.
5 5
6 ## Installation 6 ## Installation
7 7
@@ -29,12 +29,13 @@ CameraXHelper.createCameraProvider(lifecycleOwner).let { @@ -29,12 +29,13 @@ CameraXHelper.createCameraProvider(lifecycleOwner).let {
29 } 29 }
30 ``` 30 ```
31 31
32 -Your activity can act as your `LifecycleOwner` for the camera provider. If you wish to use the camera beyond the lifecycle of a single activity, consider using 32 +Your activity can act as your `LifecycleOwner` for the camera provider. If you wish to use the camera beyond the lifecycle of a single activity, consider using
33 [viewmodel-lifecycle](https://github.com/skydoves/viewmodel-lifecycle) for use within a view model (useful if your activity wants to handle rotation or other configuration changes), 33 [viewmodel-lifecycle](https://github.com/skydoves/viewmodel-lifecycle) for use within a view model (useful if your activity wants to handle rotation or other configuration changes),
34 or `LifecycleService` from `androidx.lifecycle:lifecycle-service` to use in a service for backgrounded camera usage. 34 or `LifecycleService` from `androidx.lifecycle:lifecycle-service` to use in a service for backgrounded camera usage.
35 35
36 Once registered, LiveKit will default to using CameraX when creating a camera video track. 36 Once registered, LiveKit will default to using CameraX when creating a camera video track.
37 37
  38 +
38 ### Accessing the camera controls 39 ### Accessing the camera controls
39 40
40 ``` 41 ```
@@ -51,3 +52,42 @@ fun zoom(factor: Float) { @@ -51,3 +52,42 @@ fun zoom(factor: Float) {
51 ``` 52 ```
52 53
53 We provide a convenience `ScaleZoomHelper` class that can handle pinch-to-zoom functionality as well. 54 We provide a convenience `ScaleZoomHelper` class that can handle pinch-to-zoom functionality as well.
  55 +
  56 +### Taking a high quality picture
  57 +
  58 +This allows you to take a high picture without interrupting the video stream.
  59 +
  60 +```
  61 +// Pass in the imageCapture use case when creating the camera provider
  62 +val imageCapture = ImageCapture.Builder()
  63 + .setCaptureMode(ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY)
  64 + .build()
  65 +CameraXHelper.createCameraProvider(lifecycleOwner, arrayOf(imageCapture).let {
  66 + if (it.isSupported(application)) {
  67 + CameraCapturerUtils.registerCameraProvider(it)
  68 +
  69 + // Save cameraProvider for unregistration later.
  70 + cameraProvider = it
  71 + }
  72 +}
  73 +
  74 +fun takeHighQualityPicture() {
  75 + val outputOptions = ImageCapture.OutputFileOptions.Builder(
  76 + File(getApplication<Application>().filesDir, "high_quality_picture.jpg")
  77 + ).build()
  78 +
  79 + imageCapture.takePicture(
  80 + outputOptions,
  81 + ContextCompat.getMainExecutor(getApplication()),
  82 + object : ImageCapture.OnImageSavedCallback {
  83 + override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
  84 + Log.d(TAG, "Image saved successfully: ${outputFileResults.savedUri}")
  85 + }
  86 +
  87 + override fun onError(exception: ImageCaptureException) {
  88 + Log.e(TAG, "Error capturing image", exception)
  89 + }
  90 + }
  91 + )
  92 +}
  93 +```
@@ -21,6 +21,7 @@ import android.hardware.camera2.CameraManager @@ -21,6 +21,7 @@ import android.hardware.camera2.CameraManager
21 import androidx.annotation.OptIn 21 import androidx.annotation.OptIn
22 import androidx.camera.camera2.interop.ExperimentalCamera2Interop 22 import androidx.camera.camera2.interop.ExperimentalCamera2Interop
23 import androidx.camera.core.Camera 23 import androidx.camera.core.Camera
  24 +import androidx.camera.core.UseCase
24 import androidx.lifecycle.LifecycleOwner 25 import androidx.lifecycle.LifecycleOwner
25 import io.livekit.android.room.track.video.CameraCapturerWithSize 26 import io.livekit.android.room.track.video.CameraCapturerWithSize
26 import io.livekit.android.room.track.video.CameraEventsDispatchHandler 27 import io.livekit.android.room.track.video.CameraEventsDispatchHandler
@@ -35,6 +36,7 @@ internal class CameraXCapturer( @@ -35,6 +36,7 @@ internal class CameraXCapturer(
35 private val lifecycleOwner: LifecycleOwner, 36 private val lifecycleOwner: LifecycleOwner,
36 cameraName: String?, 37 cameraName: String?,
37 eventsHandler: CameraVideoCapturer.CameraEventsHandler?, 38 eventsHandler: CameraVideoCapturer.CameraEventsHandler?,
  39 + private val useCases: Array<out UseCase> = emptyArray(),
38 ) : CameraCapturer(cameraName, eventsHandler, CameraXEnumerator(context, lifecycleOwner)) { 40 ) : CameraCapturer(cameraName, eventsHandler, CameraXEnumerator(context, lifecycleOwner)) {
39 41
40 @FlowObservable 42 @FlowObservable
@@ -90,6 +92,7 @@ internal class CameraXCapturer( @@ -90,6 +92,7 @@ internal class CameraXCapturer(
90 width, 92 width,
91 height, 93 height,
92 framerate, 94 framerate,
  95 + useCases,
93 ) 96 )
94 } 97 }
95 } 98 }
@@ -24,6 +24,7 @@ import android.os.Build @@ -24,6 +24,7 @@ import android.os.Build
24 import android.os.Build.VERSION 24 import android.os.Build.VERSION
25 import androidx.camera.camera2.interop.Camera2CameraInfo 25 import androidx.camera.camera2.interop.Camera2CameraInfo
26 import androidx.camera.camera2.interop.ExperimentalCamera2Interop 26 import androidx.camera.camera2.interop.ExperimentalCamera2Interop
  27 +import androidx.camera.core.UseCase
27 import androidx.lifecycle.LifecycleOwner 28 import androidx.lifecycle.LifecycleOwner
28 29
29 /** 30 /**
@@ -33,13 +34,14 @@ import androidx.lifecycle.LifecycleOwner @@ -33,13 +34,14 @@ import androidx.lifecycle.LifecycleOwner
33 class CameraXEnumerator( 34 class CameraXEnumerator(
34 context: Context, 35 context: Context,
35 private val lifecycleOwner: LifecycleOwner, 36 private val lifecycleOwner: LifecycleOwner,
  37 + private val useCases: Array<out UseCase> = emptyArray(),
36 ) : Camera2Enumerator(context) { 38 ) : Camera2Enumerator(context) {
37 39
38 override fun createCapturer( 40 override fun createCapturer(
39 deviceName: String?, 41 deviceName: String?,
40 eventsHandler: CameraVideoCapturer.CameraEventsHandler?, 42 eventsHandler: CameraVideoCapturer.CameraEventsHandler?,
41 ): CameraVideoCapturer { 43 ): CameraVideoCapturer {
42 - return CameraXCapturer(context, lifecycleOwner, deviceName, eventsHandler) 44 + return CameraXCapturer(context, lifecycleOwner, deviceName, eventsHandler, useCases)
43 } 45 }
44 46
45 companion object { 47 companion object {
@@ -19,6 +19,7 @@ package livekit.org.webrtc @@ -19,6 +19,7 @@ package livekit.org.webrtc
19 import android.content.Context 19 import android.content.Context
20 import android.hardware.camera2.CameraManager 20 import android.hardware.camera2.CameraManager
21 import androidx.camera.camera2.interop.ExperimentalCamera2Interop 21 import androidx.camera.camera2.interop.ExperimentalCamera2Interop
  22 +import androidx.camera.core.UseCase
22 import androidx.lifecycle.Lifecycle 23 import androidx.lifecycle.Lifecycle
23 import androidx.lifecycle.LifecycleOwner 24 import androidx.lifecycle.LifecycleOwner
24 import io.livekit.android.room.track.LocalVideoTrackOptions 25 import io.livekit.android.room.track.LocalVideoTrackOptions
@@ -35,10 +36,14 @@ class CameraXHelper { @@ -35,10 +36,14 @@ class CameraXHelper {
35 * For use with [CameraCapturerUtils.registerCameraProvider]. 36 * For use with [CameraCapturerUtils.registerCameraProvider].
36 * Remember to unregister the provider when outside the lifecycle 37 * Remember to unregister the provider when outside the lifecycle
37 * of [lifecycleOwner]. 38 * of [lifecycleOwner].
  39 + *
  40 + * @param lifecycleOwner The lifecycleOwner which controls the lifecycle transitions of the use cases.
  41 + * @param useCases The use cases to bind to a lifecycle.
38 */ 42 */
39 @ExperimentalCamera2Interop 43 @ExperimentalCamera2Interop
40 fun createCameraProvider( 44 fun createCameraProvider(
41 lifecycleOwner: LifecycleOwner, 45 lifecycleOwner: LifecycleOwner,
  46 + useCases: Array<out UseCase> = emptyArray(),
42 ) = object : CameraCapturerUtils.CameraProvider { 47 ) = object : CameraCapturerUtils.CameraProvider {
43 48
44 private var enumerator: CameraXEnumerator? = null 49 private var enumerator: CameraXEnumerator? = null
@@ -46,7 +51,7 @@ class CameraXHelper { @@ -46,7 +51,7 @@ class CameraXHelper {
46 override val cameraVersion = 3 51 override val cameraVersion = 3
47 52
48 override fun provideEnumerator(context: Context): CameraXEnumerator = 53 override fun provideEnumerator(context: Context): CameraXEnumerator =
49 - enumerator ?: CameraXEnumerator(context, lifecycleOwner).also { 54 + enumerator ?: CameraXEnumerator(context, lifecycleOwner, useCases).also {
50 enumerator = it 55 enumerator = it
51 } 56 }
52 57
@@ -35,6 +35,7 @@ import androidx.camera.core.CameraSelector @@ -35,6 +35,7 @@ import androidx.camera.core.CameraSelector
35 import androidx.camera.core.ImageAnalysis 35 import androidx.camera.core.ImageAnalysis
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.lifecycle.ProcessCameraProvider 39 import androidx.camera.lifecycle.ProcessCameraProvider
39 import androidx.core.content.ContextCompat 40 import androidx.core.content.ContextCompat
40 import androidx.lifecycle.LifecycleOwner 41 import androidx.lifecycle.LifecycleOwner
@@ -57,6 +58,7 @@ internal constructor( @@ -57,6 +58,7 @@ internal constructor(
57 private val width: Int, 58 private val width: Int,
58 private val height: Int, 59 private val height: Int,
59 private val frameRate: Int, 60 private val frameRate: Int,
  61 + private val useCases: Array<out UseCase> = emptyArray(),
60 ) : CameraSession { 62 ) : CameraSession {
61 63
62 private var state = SessionState.RUNNING 64 private var state = SessionState.RUNNING
@@ -171,7 +173,13 @@ internal constructor( @@ -171,7 +173,13 @@ internal constructor(
171 cameraProvider.unbindAll() 173 cameraProvider.unbindAll()
172 174
173 // Bind use cases to camera 175 // Bind use cases to camera
174 - camera = cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, imageAnalysis, preview) 176 + camera = cameraProvider.bindToLifecycle(
  177 + lifecycleOwner,
  178 + cameraSelector,
  179 + imageAnalysis,
  180 + preview,
  181 + *useCases,
  182 + )
175 183
176 cameraThreadHandler.post { 184 cameraThreadHandler.post {
177 sessionCallback.onDone(this@CameraXSession) 185 sessionCallback.onDone(this@CameraXSession)