Kasem Mohamed
Committed by GitHub

Add use cases to createCameraProvider (#536)

---
"client-sdk-android": minor
---
Add use cases to CameraX createCameraProvider
... ...
# CameraX support for LiveKit Android SDK
[![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)
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.
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.
## Installation
... ... @@ -29,12 +29,13 @@ CameraXHelper.createCameraProvider(lifecycleOwner).let {
}
```
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
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
[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),
or `LifecycleService` from `androidx.lifecycle:lifecycle-service` to use in a service for backgrounded camera usage.
Once registered, LiveKit will default to using CameraX when creating a camera video track.
### Accessing the camera controls
```
... ... @@ -51,3 +52,42 @@ fun zoom(factor: Float) {
```
We provide a convenience `ScaleZoomHelper` class that can handle pinch-to-zoom functionality as well.
### Taking a high quality picture
This allows you to take a high picture without interrupting the video stream.
```
// Pass in the imageCapture use case when creating the camera provider
val imageCapture = ImageCapture.Builder()
.setCaptureMode(ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY)
.build()
CameraXHelper.createCameraProvider(lifecycleOwner, arrayOf(imageCapture).let {
if (it.isSupported(application)) {
CameraCapturerUtils.registerCameraProvider(it)
// Save cameraProvider for unregistration later.
cameraProvider = it
}
}
fun takeHighQualityPicture() {
val outputOptions = ImageCapture.OutputFileOptions.Builder(
File(getApplication<Application>().filesDir, "high_quality_picture.jpg")
).build()
imageCapture.takePicture(
outputOptions,
ContextCompat.getMainExecutor(getApplication()),
object : ImageCapture.OnImageSavedCallback {
override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
Log.d(TAG, "Image saved successfully: ${outputFileResults.savedUri}")
}
override fun onError(exception: ImageCaptureException) {
Log.e(TAG, "Error capturing image", exception)
}
}
)
}
```
... ...
... ... @@ -21,6 +21,7 @@ import android.hardware.camera2.CameraManager
import androidx.annotation.OptIn
import androidx.camera.camera2.interop.ExperimentalCamera2Interop
import androidx.camera.core.Camera
import androidx.camera.core.UseCase
import androidx.lifecycle.LifecycleOwner
import io.livekit.android.room.track.video.CameraCapturerWithSize
import io.livekit.android.room.track.video.CameraEventsDispatchHandler
... ... @@ -35,6 +36,7 @@ internal class CameraXCapturer(
private val lifecycleOwner: LifecycleOwner,
cameraName: String?,
eventsHandler: CameraVideoCapturer.CameraEventsHandler?,
private val useCases: Array<out UseCase> = emptyArray(),
) : CameraCapturer(cameraName, eventsHandler, CameraXEnumerator(context, lifecycleOwner)) {
@FlowObservable
... ... @@ -90,6 +92,7 @@ internal class CameraXCapturer(
width,
height,
framerate,
useCases,
)
}
}
... ...
... ... @@ -24,6 +24,7 @@ import android.os.Build
import android.os.Build.VERSION
import androidx.camera.camera2.interop.Camera2CameraInfo
import androidx.camera.camera2.interop.ExperimentalCamera2Interop
import androidx.camera.core.UseCase
import androidx.lifecycle.LifecycleOwner
/**
... ... @@ -33,13 +34,14 @@ import androidx.lifecycle.LifecycleOwner
class CameraXEnumerator(
context: Context,
private val lifecycleOwner: LifecycleOwner,
private val useCases: Array<out UseCase> = emptyArray(),
) : Camera2Enumerator(context) {
override fun createCapturer(
deviceName: String?,
eventsHandler: CameraVideoCapturer.CameraEventsHandler?,
): CameraVideoCapturer {
return CameraXCapturer(context, lifecycleOwner, deviceName, eventsHandler)
return CameraXCapturer(context, lifecycleOwner, deviceName, eventsHandler, useCases)
}
companion object {
... ...
... ... @@ -19,6 +19,7 @@ package livekit.org.webrtc
import android.content.Context
import android.hardware.camera2.CameraManager
import androidx.camera.camera2.interop.ExperimentalCamera2Interop
import androidx.camera.core.UseCase
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import io.livekit.android.room.track.LocalVideoTrackOptions
... ... @@ -35,10 +36,14 @@ class CameraXHelper {
* For use with [CameraCapturerUtils.registerCameraProvider].
* Remember to unregister the provider when outside the lifecycle
* of [lifecycleOwner].
*
* @param lifecycleOwner The lifecycleOwner which controls the lifecycle transitions of the use cases.
* @param useCases The use cases to bind to a lifecycle.
*/
@ExperimentalCamera2Interop
fun createCameraProvider(
lifecycleOwner: LifecycleOwner,
useCases: Array<out UseCase> = emptyArray(),
) = object : CameraCapturerUtils.CameraProvider {
private var enumerator: CameraXEnumerator? = null
... ... @@ -46,7 +51,7 @@ class CameraXHelper {
override val cameraVersion = 3
override fun provideEnumerator(context: Context): CameraXEnumerator =
enumerator ?: CameraXEnumerator(context, lifecycleOwner).also {
enumerator ?: CameraXEnumerator(context, lifecycleOwner, useCases).also {
enumerator = it
}
... ...
... ... @@ -35,6 +35,7 @@ import androidx.camera.core.CameraSelector
import androidx.camera.core.ImageAnalysis
import androidx.camera.core.Preview
import androidx.camera.core.Preview.SurfaceProvider
import androidx.camera.core.UseCase
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.core.content.ContextCompat
import androidx.lifecycle.LifecycleOwner
... ... @@ -57,6 +58,7 @@ internal constructor(
private val width: Int,
private val height: Int,
private val frameRate: Int,
private val useCases: Array<out UseCase> = emptyArray(),
) : CameraSession {
private var state = SessionState.RUNNING
... ... @@ -171,7 +173,13 @@ internal constructor(
cameraProvider.unbindAll()
// Bind use cases to camera
camera = cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, imageAnalysis, preview)
camera = cameraProvider.bindToLifecycle(
lifecycleOwner,
cameraSelector,
imageAnalysis,
preview,
*useCases,
)
cameraThreadHandler.post {
sessionCallback.onDone(this@CameraXSession)
... ...