@@ -35,28 +35,25 @@ class CameraSource : InputSource, KoinComponent {
3535 @JvmStatic var currentWebcamIndex = - 1
3636 }
3737
38- override val hasSlowInitialization: Boolean get() = true
38+ override val hasSlowInitialization = true
3939
40- @JsonProperty @JvmField var cameraPortIndex: Int = - 1
41- @JsonProperty @JvmField var exactPortMatch: Boolean = false
40+ @JsonProperty @JvmField var cameraPortIndex = - 1
41+ @JsonProperty @JvmField var exactPortMatch = false
4242 @JsonProperty @JvmField var vendorId: Int? = null
4343 @JsonProperty @JvmField var productId: Int? = null
44- @Transient var webcamName: String = " "
44+ @Transient var webcamName = " "
4545
4646 @JsonProperty @JvmField var videoMode: VideoMode ? = null
4747
4848 @Transient var camera: UsbCamera ? = null
4949 private set
5050
5151 @Transient private var cvSink: CvSink ? = null
52-
5352 @Transient private var lastFrame = Mat ()
54-
5553 @Transient private var initialized = false
56-
5754 @Transient var isLegacyByIndex = false
55+ @Transient private var capTimeNanos = 0L
5856
59- @Transient private var capTimeNanos: Long = 0
6057 private val configManager: ConfigManager by inject()
6158 private val logger by loggerForThis()
6259
@@ -96,94 +93,65 @@ class CameraSource : InputSource, KoinComponent {
9693 this .isLegacyByIndex = true
9794 }
9895
99- override fun setSize (size : Size ) {
100- // deprecated concept now; derived from VideoMode
101- }
102-
103- override fun getSize (): Size =
104- videoMode?.let { Size (it.width.toDouble(), it.height.toDouble()) } ? : Size ()
96+ override val sourceSize: Size
97+ get() = videoMode?.let { Size (it.width.toDouble(), it.height.toDouble()) } ? : Size ()
10598
10699 override fun init (): Boolean {
107100 if (initialized) return false
108101 initialized = true
109102
103+ val cameras by lazy { UsbCamera .enumerateUsbCameras() }
104+
110105 val matchedInfo = when {
111- exactPortMatch -> {
112- val infos = UsbCamera .enumerateUsbCameras()
113- infos.firstOrNull {
114- cameraPortIndex >= 0 &&
106+ exactPortMatch -> cameras.firstOrNull {
107+ cameraPortIndex >= 0 &&
115108 it.dev == cameraPortIndex &&
116109 vendorId != null && productId != null &&
117110 it.vendorId == vendorId &&
118111 it.productId == productId
119- } ? : run {
120- logger.error(" Camera not found on the same connection: $cameraPortIndex " )
112+ } ? : run {
113+ logger.error(" Camera not found on the same connection: $cameraPortIndex " )
114+ return false
115+ }
116+
117+ cameraPortIndex >= 0 -> cameras.firstOrNull { it.dev == cameraPortIndex }
118+ ? : run {
119+ logger.error(" Camera not found on port: $cameraPortIndex " )
121120 return false
122121 }
123- }
124122
125- cameraPortIndex >= 0 -> {
126- val infos = UsbCamera .enumerateUsbCameras()
127- infos.firstOrNull { it.dev == cameraPortIndex }
128- ? : run {
129- logger.error(" Camera not found on port: $cameraPortIndex " )
130- return false
131- }
123+ vendorId != null && productId != null -> cameras.firstOrNull {
124+ it.vendorId == vendorId && it.productId == productId
125+ } ? : run {
126+ logger.error(" Camera not found by VID/PID: $vendorId :$productId " )
127+ return false
132128 }
133129
134- vendorId != null && productId != null -> {
135- val infos = UsbCamera .enumerateUsbCameras()
136- infos.firstOrNull {
137- it.vendorId == vendorId && it.productId == productId
138- } ? : run {
139- logger.error(" Camera not found by VID/PID: ${vendorId} :${productId} " )
130+ webcamName.isNotEmpty() -> cameras.firstOrNull { it.name == webcamName }
131+ ? : run {
132+ logger.error(" Camera not found: $webcamName " )
140133 return false
141134 }
142- }
143-
144- webcamName.isNotEmpty() -> {
145- val infos = UsbCamera .enumerateUsbCameras()
146- infos.firstOrNull { it.name == webcamName }
147- ? : run {
148- logger.error(" Camera not found: $webcamName " )
149- return false
150- }
151- }
152135
153136 else -> null
154137 }
155138
156- val cam = if (matchedInfo != null ) {
139+ camera = if (matchedInfo != null ) {
157140 webcamName = matchedInfo.name
158141 UsbCamera (matchedInfo.name, matchedInfo.dev)
159142 } else {
160143 UsbCamera (" $cameraPortIndex " , cameraPortIndex)
161144 }
162145
163- camera = cam
164-
165- val desiredMode = videoMode
166-
167- if (desiredMode != null ) {
168- cam.videoMode = desiredMode
169- } else {
170- val mode = cam.videoMode
171- cam.videoMode = mode
172- }
173-
174- val mode = cam.videoMode
146+ camera!! .videoMode = videoMode ? : camera!! .videoMode
175147
176- logger.info(
177- " Camera started: ${matchedInfo?.name ? : webcamName.ifEmpty { " Camera $cameraPortIndex " }} ${mode?.stringify()} "
178- )
148+ logger.info(" Camera started: ${matchedInfo?.name ? : webcamName.ifEmpty { " Camera $cameraPortIndex " }} ${camera!! .videoMode?.stringify()} " )
179149
180150 cvSink = CvSink (" eocvsim_sink_$cameraPortIndex " , PixelFormat .BGR ).also {
181- it.source = cam
151+ it.source = camera
182152 }
183153
184- val ok = cvSink!! .grabFrame(lastFrame, configManager.config.webcamOpenTimeoutSec)
185-
186- if (ok == 0L || lastFrame.empty()) {
154+ if (cvSink!! .grabFrame(lastFrame, configManager.config.webcamOpenTimeoutSec) == 0L || lastFrame.empty()) {
187155 logger.error(" Failed to open camera: ${cvSink!! .error} " )
188156 return false
189157 }
@@ -194,61 +162,51 @@ class CameraSource : InputSource, KoinComponent {
194162
195163 override fun reset () {
196164 if (! initialized) return
197-
198- cvSink?.close()
199- cvSink = null
200-
201- camera?.close()
202- camera = null
203-
165+ teardown()
204166 lastFrame.release()
205-
206167 initialized = false
207168 }
208169
209- override fun close () {
210- cvSink?.close()
211- camera?.close()
212- currentWebcamIndex = - 1
213- }
170+ override fun close () = teardown()
214171
215172 override fun update (): Mat {
216173 if (isPaused) return lastFrame
217174
218- val grabTime = cvSink?.grabFrame(lastFrame, configManager.config.webcamNewFrameTimeoutSec) ? : 0L
175+ capTimeNanos = cvSink?.grabFrame(lastFrame, configManager.config.webcamNewFrameTimeoutSec) ? : 0L
219176
220- if ( lastFrame.empty()) {
221- return lastFrame
177+ if ( ! lastFrame.empty()) {
178+ Imgproc .cvtColor(lastFrame, lastFrame, Imgproc . COLOR_BGR2RGBA )
222179 }
223180
224- capTimeNanos = grabTime
225- Imgproc .cvtColor(lastFrame, lastFrame, Imgproc .COLOR_BGR2RGBA )
226181 return lastFrame
227182 }
228183
229184 override fun onPause () {
230185 cvSink?.grabFrame(lastFrame, configManager.config.webcamNewFrameTimeoutSec)
231- cvSink?.close()
232- camera?.close()
233- currentWebcamIndex = - 1
186+ teardown()
234187 }
235188
236189 override fun onResume () {
237190 InputSourceInitializer .runWithTimeout(this ) { init () }
238191 }
239192
193+ private fun teardown () {
194+ cvSink?.close()
195+ cvSink = null
196+ camera?.close()
197+ camera = null
198+ currentWebcamIndex = - 1
199+ }
200+
240201 override fun internalCloneSource (): InputSource =
241- if (isLegacyByIndex) {
242- CameraSource (cameraPortIndex, videoMode)
243- } else {
244- CameraSource (webcamName, cameraPortIndex, exactPortMatch, vendorId, productId, videoMode)
245- }
202+ if (isLegacyByIndex) CameraSource (cameraPortIndex, videoMode)
203+ else CameraSource (webcamName, cameraPortIndex, exactPortMatch, vendorId, productId, videoMode)
246204
247- override val fileFilters: FileFilter ? get() = null
248- override val captureTimeNanos: Long get() = capTimeNanos
205+ override val fileFilters: FileFilter ? = null
206+ override val captureTimeNanos get() = capTimeNanos
249207
250208 override fun toString () =
251209 " CameraSource($webcamName , port=$cameraPortIndex , exactPortMatch=$exactPortMatch , vid=$vendorId , pid=$productId , ${videoMode?.stringify()} )"
252210
253211 private fun VideoMode.stringify () = " ${width} x${height} @${fps} $pixelFormat "
254- }
212+ }
0 commit comments