Skip to content

Commit

Permalink
feat(ui): add preview API for camerax CameraViewFinder
Browse files Browse the repository at this point in the history
  • Loading branch information
ThibaultBee committed Oct 3, 2024
1 parent 6cc3361 commit 50cf4c2
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ fun DefaultCameraStreamer(
* @param internalEndpoint the [IEndpointInternal] implementation
*/
open class DefaultCameraStreamer(
private val context: Context,
context: Context,
audioSourceInternal: IAudioSourceInternal?,
internalEndpoint: IEndpointInternal = DynamicEndpoint(context)
) : DefaultStreamer(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ import java.util.concurrent.Executors
* @param dispatcher the [CoroutineDispatcher] to execute suspendable methods. For test only. Do not change.
*/
open class DefaultStreamer(
private val context: Context,
protected val context: Context,
protected val audioSourceInternal: IAudioSourceInternal?,
protected val videoSourceInternal: IVideoSourceInternal?,
protected val endpointInternal: IEndpointInternal = DynamicEndpoint(context),
Expand All @@ -79,7 +79,7 @@ open class DefaultStreamer(
// Keep configurations
private var _audioConfig: AudioConfig? = null
private var _videoConfig: VideoConfig? = null

override val audioConfig: AudioConfig?
get() = _audioConfig

Expand Down
2 changes: 2 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
[versions]
agp = "8.5.2"
guava = "33.3.1-android"
videoApiClient = "1.5.7"
androidxActivity = "1.9.2"
androidxAppcompat = "1.7.0"
Expand All @@ -26,6 +27,7 @@ srtdroid = "1.8.3"
junitKtx = "1.2.1"

[libraries]
guava = { module = "com.google.guava:guava", version.ref = "guava" }
video-api-client = { module = "video.api:android-api-client", version.ref = "videoApiClient" }
android-gradle = { module = "com.android.tools.build:gradle", version.ref = "agp" }
android-material = { module = "com.google.android.material:material", version.ref = "material" }
Expand Down
1 change: 1 addition & 0 deletions ui/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,5 @@ dependencies {
implementation(libs.androidx.lifecycle.runtime)
implementation(libs.androidx.camera.viewfinder.view)
implementation(libs.androidx.core.ktx)
implementation(libs.guava)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package io.github.thibaultbee.streampack.ui.views

import android.content.Context
import android.hardware.camera2.CameraCharacteristics
import android.util.Size
import android.view.Surface
import androidx.camera.impl.utils.futures.FutureCallback
import androidx.camera.impl.utils.futures.Futures
import androidx.camera.viewfinder.CameraViewfinder
import androidx.camera.viewfinder.CameraViewfinderExt.requestSurface
import androidx.camera.viewfinder.surface.ViewfinderSurfaceRequest
import androidx.camera.viewfinder.surface.populateFromCharacteristics
import androidx.core.content.ContextCompat
import io.github.thibaultbee.streampack.core.streamers.interfaces.ICameraCallbackStreamer
import io.github.thibaultbee.streampack.core.streamers.interfaces.ICameraCoroutineStreamer
import io.github.thibaultbee.streampack.core.streamers.interfaces.startPreview
import io.github.thibaultbee.streampack.core.utils.extensions.getCameraCharacteristics

/**
* Start preview on a [CameraViewfinder]
*
* @param viewfinder The [CameraViewfinder] to set as preview
* @param previewSize The size of the preview
* @return The [ViewfinderSurfaceRequest] used to set the preview. Use it to call [ViewfinderSurfaceRequest.markSurfaceSafeToRelease] after [stopPreview].
*/
suspend fun ICameraCoroutineStreamer.startPreview(
viewfinder: CameraViewfinder,
previewSize: Size
): ViewfinderSurfaceRequest {
val request = setPreview(viewfinder, previewSize)
startPreview()
return request
}

/**
* Set preview on a [CameraViewfinder]
*
* @param viewfinder The [CameraViewfinder] to set as preview
* @param previewSize The size of the preview
* @return The [ViewfinderSurfaceRequest] used to set the preview. Use it to call [ViewfinderSurfaceRequest.markSurfaceSafeToRelease].
*/
suspend fun ICameraCoroutineStreamer.setPreview(
viewfinder: CameraViewfinder,
previewSize: Size
): ViewfinderSurfaceRequest {
val builder = ViewfinderSurfaceRequest.Builder(previewSize)
val cameraCharacteristics = viewfinder.context.getCameraCharacteristics(camera)
val request = builder.populateFromCharacteristics(cameraCharacteristics).build()
setPreview(viewfinder.requestSurface(request))
return request
}

/**
* Start preview on a [CameraViewfinder]
*
* @param viewfinder The [CameraViewfinder] to set as preview
* @param previewSize The size of the preview
* @return The [ViewfinderSurfaceRequest] used to set the preview. Use it to call [ViewfinderSurfaceRequest.markSurfaceSafeToRelease] after [stopPreview].
*/
fun ICameraCallbackStreamer.startPreview(
viewfinder: CameraViewfinder,
previewSize: Size
): ViewfinderSurfaceRequest {
val cameraCharacteristics = viewfinder.context.getCameraCharacteristics(camera)
return executeSurfaceRequest(
viewfinder.context,
viewfinder,
previewSize,
cameraCharacteristics
) {
startPreview(it)
}
}

/**
* Set preview on a [CameraViewfinder].
*
* @param viewfinder The [CameraViewfinder] to set as preview
* @param previewSize The size of the preview
* @return The [ViewfinderSurfaceRequest] used to set the preview. Use it to call [ViewfinderSurfaceRequest.markSurfaceSafeToRelease].
*/
fun ICameraCallbackStreamer.setPreview(
viewfinder: CameraViewfinder,
previewSize: Size
): ViewfinderSurfaceRequest {
val cameraCharacteristics = viewfinder.context.getCameraCharacteristics(camera)
return executeSurfaceRequest(
viewfinder.context,
viewfinder,
previewSize,
cameraCharacteristics
) { setPreview(it) }
}

private fun executeSurfaceRequest(
context: Context,
viewfinder: CameraViewfinder,
previewSize: Size,
cameraCharacteristics: CameraCharacteristics,
block: (Surface) -> Unit
): ViewfinderSurfaceRequest {
val builder = ViewfinderSurfaceRequest.Builder(previewSize)
val request = builder.populateFromCharacteristics(cameraCharacteristics).build()
val surfaceListenableFuture = viewfinder.requestSurfaceAsync(request)
Futures.addCallback(surfaceListenableFuture, object : FutureCallback<Surface> {
override fun onSuccess(result: Surface?) {
result?.let { block(it) }
}

override fun onFailure(t: Throwable) { /* something went wrong */
}
}, ContextCompat.getMainExecutor(context))

return request
}

0 comments on commit 50cf4c2

Please sign in to comment.