Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 6 additions & 7 deletions app/src/main/java/com/haishinkit/app/CameraViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.application
import androidx.lifecycle.viewModelScope
import com.haishinkit.device.CameraDevice
import com.haishinkit.device.DeviceManager
import com.haishinkit.device.CameraDeviceManager
import com.haishinkit.graphics.effect.MonochromeVideoEffect
import com.haishinkit.graphics.effect.MosaicVideoEffect
import com.haishinkit.graphics.effect.SepiaVideoEffect
Expand All @@ -41,16 +41,16 @@ import java.io.ByteArrayOutputStream
class CameraViewModel(
application: Application,
) : AndroidViewModel(application), DefaultLifecycleObserver {
private var deviceManager: DeviceManager =
DeviceManager(context = application.applicationContext)
private var cameraDeviceManager: CameraDeviceManager =
CameraDeviceManager(context = application.applicationContext)
private var mixer: MediaMixer = MediaMixer(application.applicationContext)
val session: StreamSession =
StreamSession
.Builder(application.applicationContext, Preference.shared.toRtmpUrl())
.build()
val cameraList: StateFlow<List<CameraDevice>>
get() {
return deviceManager.cameraList
return cameraDeviceManager.deviceList
}

val recorder: MediaRecorder = MediaRecorder(application.applicationContext)
Expand All @@ -73,7 +73,7 @@ class CameraViewModel(
_videoEffectItems.add(VideoEffectItem("Mosaic", MosaicVideoEffect()))
_videoEffectItems.add(VideoEffectItem("Sepia", SepiaVideoEffect()))
this.videoEffectItems = _videoEffectItems
_selectedCamera.value = deviceManager.getCameraList().first()
_selectedCamera.value = cameraDeviceManager.getDeviceList().first()

val text = TextScreenObject()
text.size = 60f
Expand Down Expand Up @@ -187,9 +187,8 @@ class CameraViewModel(
}

override fun onCleared() {
Log.d("TAG", "onCleared")
super.onCleared()
deviceManager.release()
cameraDeviceManager.release()
mixer.dispose()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,20 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow

/**
* Manages available camera devices on the system.
*
* This class observes both:
* - Camera availability changes reported by [CameraManager]
* - USB camera attach / detach events
*
* and keeps an up-to-date list of [CameraDevice] instances exposed via [deviceList].
*
* Call [release] when this manager is no longer needed to unregister callbacks
* and receivers.
*/
@Suppress("UNUSED")
class DeviceManager(
class CameraDeviceManager(
private val context: Context,
) {
private val cameraManager =
Expand All @@ -34,8 +46,12 @@ class DeviceManager(
refresh()
}

private val _cameraList = MutableStateFlow<List<CameraDevice>>(emptyList())
val cameraList: StateFlow<List<CameraDevice>> = _cameraList.asStateFlow()
private val _deviceList = MutableStateFlow<List<CameraDevice>>(emptyList())

/**
* A read-only [StateFlow] that emits the current list of available camera devices.
*/
val deviceList: StateFlow<List<CameraDevice>> = _deviceList.asStateFlow()

init {
val filter =
Expand All @@ -51,7 +67,13 @@ class DeviceManager(
refresh()
}

fun getCameraList(): List<CameraDevice> {
/**
* Returns the current list of camera devices detected by the system.
*
* Each camera is mapped to a [CameraDevice] using its ID and lens facing
* information obtained from [CameraCharacteristics].
*/
fun getDeviceList(): List<CameraDevice> {
return cameraManager.cameraIdList.map { cameraId ->
val chars = cameraManager.getCameraCharacteristics(cameraId)
val position =
Expand All @@ -75,12 +97,17 @@ class DeviceManager(
}
}

/**
* Releases all registered callbacks and receivers.
*
* This method should be called to avoid memory leaks when the manager is no longer in use.
*/
fun release() {
cameraManager.unregisterAvailabilityCallback(availabilityCallback)
context.unregisterReceiver(usbReceiver)
}

private fun refresh() {
_cameraList.value = getCameraList()
_deviceList.value = getDeviceList()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import android.content.Context
import android.content.Intent
import android.hardware.usb.UsbManager

@Suppress("UNUSED")
internal class UsbCameraReceiver(
private val onChanged: () -> Unit,
) : BroadcastReceiver() {
Expand Down
Loading