You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Musubi/Packages/MusubiKit/Sources/NDIKit/NDIFinder.swift

60 lines
2.0 KiB

import CNDI
import Foundation
import MusubiCore
/// Discovers NDI sources on a dedicated thread and publishes the current list.
public final class NDIFinder: @unchecked Sendable {
// Only touched on `thread`, except for creation and `stop()`.
private let instance: NDIlib_find_instance_t
private let lock = NSLock()
private var running = true
private var thread: Thread?
public let sources: AsyncStream<[Source]>
private let continuation: AsyncStream<[Source]>.Continuation
public init?(showLocalSources: Bool = true) {
var settings = NDIlib_find_create_t(show_local_sources: showLocalSources, p_groups: nil, p_extra_ips: nil)
guard let instance = NDIlib_find_create_v2(&settings) else { return nil }
self.instance = instance
(sources, continuation) = AsyncStream.makeStream(bufferingPolicy: .bufferingNewest(1))
let thread = Thread { [weak self] in self?.run() }
thread.name = "studio.zielke.musubi.ndi-finder"
thread.qualityOfService = .utility
self.thread = thread
thread.start()
}
deinit {
stop()
NDIlib_find_destroy(instance)
}
public func stop() {
lock.withLock { running = false }
continuation.finish()
}
private var isRunning: Bool { lock.withLock { running } }
private func run() {
continuation.yield(currentSources())
while isRunning {
// Returns early when the list changed, otherwise after the timeout.
if NDIlib_find_wait_for_sources(instance, 1000) {
continuation.yield(currentSources())
}
}
}
private func currentSources() -> [Source] {
var count: UInt32 = 0
guard let list = NDIlib_find_get_current_sources(instance, &count) else { return [] }
return (0..<Int(count)).compactMap { index in
guard let name = list[index].p_ndi_name else { return nil }
return Source(ndiName: String(cString: name))
}
}
}

Powered by TurnKey Linux.