Skip to content

Commit

Permalink
Add use cases to createCameraProvider (#536)
Browse files Browse the repository at this point in the history
  • Loading branch information
KasemJaffer authored Nov 14, 2024
1 parent b20bf88 commit db34adb
Show file tree
Hide file tree
Showing 6 changed files with 68 additions and 5 deletions.
5 changes: 5 additions & 0 deletions .changeset/metal-lamps-act.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"client-sdk-android": minor
---

Add use cases to CameraX createCameraProvider
44 changes: 42 additions & 2 deletions livekit-android-camerax/README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# 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

Expand Down Expand Up @@ -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

```
Expand All @@ -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)
}
}
)
}
```
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -90,6 +92,7 @@ internal class CameraXCapturer(
width,
height,
framerate,
useCases,
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

/**
Expand All @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -35,18 +36,22 @@ 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

override val cameraVersion = 3

override fun provideEnumerator(context: Context): CameraXEnumerator =
enumerator ?: CameraXEnumerator(context, lifecycleOwner).also {
enumerator ?: CameraXEnumerator(context, lifecycleOwner, useCases).also {
enumerator = it
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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)
Expand Down

0 comments on commit db34adb

Please sign in to comment.