diff --git a/.changeset/metal-lamps-act.md b/.changeset/metal-lamps-act.md new file mode 100644 index 00000000..2b69c54e --- /dev/null +++ b/.changeset/metal-lamps-act.md @@ -0,0 +1,5 @@ +--- +"client-sdk-android": minor +--- + +Add use cases to CameraX createCameraProvider diff --git a/livekit-android-camerax/README.md b/livekit-android-camerax/README.md index 25368ee7..f2c63ece 100644 --- a/livekit-android-camerax/README.md +++ b/livekit-android-camerax/README.md @@ -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 @@ -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().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) + } + } + ) +} +``` diff --git a/livekit-android-camerax/src/main/java/livekit/org/webrtc/CameraXCapturer.kt b/livekit-android-camerax/src/main/java/livekit/org/webrtc/CameraXCapturer.kt index 1700b084..7ec830e0 100644 --- a/livekit-android-camerax/src/main/java/livekit/org/webrtc/CameraXCapturer.kt +++ b/livekit-android-camerax/src/main/java/livekit/org/webrtc/CameraXCapturer.kt @@ -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 = emptyArray(), ) : CameraCapturer(cameraName, eventsHandler, CameraXEnumerator(context, lifecycleOwner)) { @FlowObservable @@ -90,6 +92,7 @@ internal class CameraXCapturer( width, height, framerate, + useCases, ) } } diff --git a/livekit-android-camerax/src/main/java/livekit/org/webrtc/CameraXEnumerator.kt b/livekit-android-camerax/src/main/java/livekit/org/webrtc/CameraXEnumerator.kt index 51434953..487ed7f4 100644 --- a/livekit-android-camerax/src/main/java/livekit/org/webrtc/CameraXEnumerator.kt +++ b/livekit-android-camerax/src/main/java/livekit/org/webrtc/CameraXEnumerator.kt @@ -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 = 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 { diff --git a/livekit-android-camerax/src/main/java/livekit/org/webrtc/CameraXHelper.kt b/livekit-android-camerax/src/main/java/livekit/org/webrtc/CameraXHelper.kt index 1a59e6cd..aafec319 100644 --- a/livekit-android-camerax/src/main/java/livekit/org/webrtc/CameraXHelper.kt +++ b/livekit-android-camerax/src/main/java/livekit/org/webrtc/CameraXHelper.kt @@ -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 = 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 } diff --git a/livekit-android-camerax/src/main/java/livekit/org/webrtc/CameraXSession.kt b/livekit-android-camerax/src/main/java/livekit/org/webrtc/CameraXSession.kt index c10044d5..5ccfb6c2 100644 --- a/livekit-android-camerax/src/main/java/livekit/org/webrtc/CameraXSession.kt +++ b/livekit-android-camerax/src/main/java/livekit/org/webrtc/CameraXSession.kt @@ -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 = 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)