Skip to content

Commit 81f5888

Browse files
committed
fix(*): fix few issues with the preview view and camera source for preview
1 parent 6e099e6 commit 81f5888

File tree

8 files changed

+109
-152
lines changed

8 files changed

+109
-152
lines changed

core/src/main/java/io/github/thibaultbee/streampack/core/elements/sources/video/camera/CameraSource.kt

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -132,16 +132,29 @@ internal class CameraSource(
132132
override suspend fun hasPreview() = controller.hasOutput(PREVIEW_NAME)
133133

134134
@RequiresPermission(Manifest.permission.CAMERA)
135-
override suspend fun setPreview(surface: Surface) {
135+
private suspend fun setPreviewUnsafe(surface: Surface) {
136136
if (isPreviewingFlow.value) {
137137
Logger.w(TAG, "Trying to set preview while previewing")
138138
}
139139
controller.addOutput(CameraSurface(PREVIEW_NAME, surface))
140140
}
141141

142+
@RequiresPermission(Manifest.permission.CAMERA)
143+
override suspend fun setPreview(surface: Surface) {
144+
withContext(defaultDispatcher) {
145+
previewMutex.withLock {
146+
setPreviewUnsafe(surface)
147+
}
148+
}
149+
}
150+
142151
@SuppressLint("MissingPermission")
143152
override suspend fun resetPreviewImpl() {
144-
controller.removeOutput(PREVIEW_NAME)
153+
withContext(defaultDispatcher) {
154+
previewMutex.withLock {
155+
controller.removeOutput(PREVIEW_NAME)
156+
}
157+
}
145158
}
146159

147160
override suspend fun getOutput() = controller.getOutput(STREAM_NAME)?.surface
@@ -192,7 +205,6 @@ internal class CameraSource(
192205
private suspend fun startPreviewUnsafe() {
193206
if (isPreviewingFlow.value) {
194207
Logger.w(TAG, "Camera is already previewing")
195-
return
196208
}
197209
controller.addTarget(PREVIEW_NAME)
198210
_isPreviewingFlow.emit(true)
@@ -214,11 +226,7 @@ internal class CameraSource(
214226
override suspend fun startPreview(previewSurface: Surface) {
215227
withContext(defaultDispatcher) {
216228
previewMutex.withLock {
217-
if (isPreviewingFlow.value) {
218-
Logger.w(TAG, "Camera is already previewing")
219-
return@withContext
220-
}
221-
setPreview(previewSurface)
229+
setPreviewUnsafe(previewSurface)
222230
startPreviewUnsafe()
223231
}
224232
}
@@ -228,7 +236,6 @@ internal class CameraSource(
228236
override suspend fun stopPreview() {
229237
withContext(defaultDispatcher) {
230238
previewMutex.withLock {
231-
Logger.d(TAG, "Stopping preview")
232239
if (!isPreviewingFlow.value) {
233240
Logger.w(TAG, "Camera is not previewing")
234241
return@withContext
@@ -253,15 +260,13 @@ internal class CameraSource(
253260
Logger.w(TAG, "Camera is already streaming")
254261
return
255262
}
256-
Logger.d(TAG, "startStream")
257263
controller.addTarget(STREAM_NAME)
258264
_isStreamingFlow.emit(true)
259265
controller.muteVibrationAndSound()
260266
}
261267

262268
@SuppressLint("MissingPermission")
263269
override suspend fun stopStream() = streamMutex.withLock {
264-
Logger.d(TAG, "stopStream")
265270
if (!isStreamingFlow.value) {
266271
Logger.w(TAG, "Camera is not streaming")
267272
return

core/src/main/java/io/github/thibaultbee/streampack/core/elements/sources/video/camera/controllers/CameraController.kt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,14 +104,18 @@ internal class CameraController(
104104
suspend fun addOutput(output: CameraSurface) {
105105
require(output.surface.isValid) { "Output is invalid: $output" }
106106
withContext(defaultDispatcher) {
107+
Logger.e(TAG, "addOutput $output")
107108
controllerMutex.withLock {
109+
Logger.e(TAG, "addOutput locked: $output")
108110
if (outputs.values.contains(output)) {
111+
Logger.w(TAG, "Output is already added: $output")
109112
return@withContext
110113
}
111114
outputs[output.name] = output
112115
if (isActiveFlow.value) {
113116
restartSessionUnsafe()
114117
}
118+
Logger.e(TAG, "addOutput end of locked: $output")
115119
}
116120
}
117121
}
@@ -125,8 +129,7 @@ internal class CameraController(
125129
suspend fun removeOutput(name: String) {
126130
withContext(defaultDispatcher) {
127131
controllerMutex.withLock {
128-
val needRestart = outputs.containsKey(name) && isActiveFlow.value
129-
outputs.remove(name) != null
132+
val needRestart = outputs.remove(name) != null && isActiveFlow.value
130133
if (needRestart) {
131134
restartSessionUnsafe()
132135
}

core/src/main/java/io/github/thibaultbee/streampack/core/elements/sources/video/camera/controllers/CameraDeviceController.kt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,9 +103,8 @@ internal class CameraDeviceController private constructor(
103103
return@withLock
104104
}
105105
cameraDevice.close()
106-
if (!isClosedFlow.value) {
107-
isClosedFlow.first { it }
108-
}
106+
isClosedFlow.first { it }
107+
Logger.d(TAG, "Camera $id closed")
109108
}
110109
}
111110

core/src/main/java/io/github/thibaultbee/streampack/core/elements/sources/video/camera/controllers/CameraSessionController.kt

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,13 @@ internal class CameraSessionController private constructor(
4747
private val captureSession: CameraCaptureSession,
4848
private val outputs: List<CameraSurface>,
4949
val dynamicRange: Long,
50+
val cameraIsClosedFlow: StateFlow<Boolean>,
5051
val isClosedFlow: StateFlow<Boolean>
5152
) {
5253
private val captureSessionMutex = Mutex()
5354

5455
val isClosed: Boolean
55-
get() = isClosedFlow.value
56+
get() = isClosedFlow.value || cameraIsClosedFlow.value
5657

5758
private val requestTargetMutex = Mutex()
5859

@@ -82,8 +83,6 @@ internal class CameraSessionController private constructor(
8283
/**
8384
* Whether the current capture request has a target
8485
*
85-
* The target must be in the current capture session, see [hasOutput].
86-
*
8786
* @param surface The target to check
8887
* @return true if the target is in the current capture request, false otherwise
8988
*/
@@ -96,8 +95,6 @@ internal class CameraSessionController private constructor(
9695
/**
9796
* Whether the current capture request has a target
9897
*
99-
* The target must be in the current capture session, see [hasOutput].
100-
*
10198
* @param cameraSurface The target to check
10299
* @return true if the target is in the current capture request, false otherwise
103100
*/
@@ -242,10 +239,8 @@ internal class CameraSessionController private constructor(
242239
}
243240
try {
244241
captureSession.close()
245-
246-
if (!isClosedFlow.value) {
247-
isClosedFlow.first { it }
248-
}
242+
isClosedFlow.first { it }
243+
Logger.d(TAG, "Camera session closed")
249244
} catch (t: Throwable) {
250245
Logger.w(TAG, "Error closing camera session: $t")
251246
}
@@ -385,6 +380,8 @@ internal class CameraSessionController private constructor(
385380
fpsRange: Range<Int>
386381
): CameraSessionController = withContext(coroutineScope.coroutineContext) {
387382
requestTargetMutex.withLock {
383+
Logger.d(TAG, "Recreating Session controller")
384+
388385
require(outputs.isNotEmpty()) { "At least one output is required" }
389386
require(outputs.all { it.surface.isValid }) { "All outputs $outputs must be valid but ${outputs.filter { !it.surface.isValid }} is invalid" }
390387

@@ -422,6 +419,7 @@ internal class CameraSessionController private constructor(
422419
newCaptureSession,
423420
outputs,
424421
dynamicRange,
422+
cameraDeviceController.isClosedFlow,
425423
isClosedFlow.asStateFlow()
426424
)
427425

@@ -472,6 +470,7 @@ internal class CameraSessionController private constructor(
472470
captureSession,
473471
outputs,
474472
dynamicRange,
473+
cameraDeviceController.isClosedFlow,
475474
isClosedFlow.asStateFlow()
476475
)
477476
}

core/src/main/java/io/github/thibaultbee/streampack/core/elements/sources/video/camera/utils/CameraUtils.kt

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,11 +98,15 @@ internal object CameraUtils {
9898
isClosedFlow: MutableStateFlow<Boolean>
9999
): CameraCaptureSession = suspendCoroutine { continuation ->
100100
val cameraId = cameraDeviceController.id
101+
Logger.i(
102+
TAG,
103+
"Camera session configuring for camera $cameraId and outputs $outputs"
104+
)
101105
val callbacks = object : CameraCaptureSession.StateCallback() {
102106
override fun onConfigured(session: CameraCaptureSession) {
103107
Logger.i(
104108
TAG,
105-
"Camera session configured for camera $cameraId and outputs $outputs"
109+
"Camera session $session configured for camera $cameraId and outputs $outputs"
106110
)
107111
continuation.resume(session)
108112
}
@@ -111,7 +115,7 @@ internal object CameraUtils {
111115
isClosedFlow.tryEmit(true)
112116
Logger.e(
113117
TAG,
114-
"Camera session configuration failed for camera $cameraId and outputs $outputs"
118+
"Camera session $session configuration failed for camera $cameraId and outputs $outputs"
115119
)
116120
try {
117121
continuation.resumeWithException(CameraException("Camera: failed to configure the capture session for camera $cameraId and outputs $outputs"))
@@ -124,7 +128,7 @@ internal object CameraUtils {
124128
isClosedFlow.tryEmit(true)
125129
Logger.e(
126130
TAG,
127-
"Camera capture session closed for camera $cameraId and outputs $outputs"
131+
"Camera capture session $session closed for camera $cameraId and outputs $outputs"
128132
)
129133
}
130134
}

core/src/main/java/io/github/thibaultbee/streampack/core/elements/sources/video/camera/utils/CaptureRequestWithTargetsBuilder.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import android.hardware.camera2.CaptureRequest
2020
import android.hardware.camera2.CaptureRequest.Builder
2121
import android.view.Surface
2222
import io.github.thibaultbee.streampack.core.elements.sources.video.camera.controllers.CameraDeviceController
23+
import io.github.thibaultbee.streampack.core.logger.Logger
2324
import kotlinx.coroutines.sync.Mutex
2425
import kotlinx.coroutines.sync.withLock
2526

@@ -59,6 +60,7 @@ internal class CaptureRequestWithTargetsBuilder private constructor(
5960
* Whether the [CaptureRequest.Builder] has a target
6061
*/
6162
suspend fun hasTarget(name: String): Boolean = mutex.withLock {
63+
Logger.e(">>>>>>>", "name = $name mutableTarget = $mutableTargets")
6264
mutableTargets.any { it.name == name }
6365
}
6466

demos/camera/src/main/java/io/github/thibaultbee/streampack/app/ui/main/PreviewFragment.kt

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -193,17 +193,8 @@ class PreviewFragment : Fragment(R.layout.main_fragment) {
193193
}
194194

195195
// Wait till streamer exists to set it to the SurfaceView.
196-
preview.streamer = streamer
197-
if (PermissionManager.hasPermissions(requireContext(), Manifest.permission.CAMERA)) {
198-
lifecycleScope.launch {
199-
try {
200-
preview.startPreview()
201-
} catch (t: Throwable) {
202-
Log.e(TAG, "Error starting preview", t)
203-
}
204-
}
205-
} else {
206-
Log.e(TAG, "Camera permission not granted. Preview will not start.")
196+
lifecycleScope.launch {
197+
preview.setVideoSourceProvider(streamer)
207198
}
208199
}
209200

0 commit comments

Comments
 (0)