From 5bf67993ad4dca1808d8408fd4a2949e1c0e6b00 Mon Sep 17 00:00:00 2001 From: Hiroshi Horie <548776+hiroshihorie@users.noreply.github.com> Date: Wed, 6 Dec 2023 19:28:08 +0900 Subject: [PATCH] v2 API changes (#280) --- .../LiveKit/Core/Room+EngineDelegate.swift | 13 +++++--- .../Core/Room+SignalClientDelegate.swift | 20 ++++++------ Sources/LiveKit/Core/Room.swift | 31 ++++++++++--------- Sources/LiveKit/E2EE/E2EEManager.swift | 6 ++-- Sources/LiveKit/Extensions/String.swift | 28 ----------------- .../Participant/LocalParticipant.swift | 6 ++-- Sources/LiveKit/Participant/Participant.swift | 12 +++---- .../Participant/RemoteParticipant.swift | 12 ++++--- .../Types/Options/DataPublishOptions.swift | 10 +++--- Sources/LiveKit/Types/Other.swift | 1 + Sources/LiveKit/Views/VideoView.swift | 2 +- 11 files changed, 63 insertions(+), 78 deletions(-) diff --git a/Sources/LiveKit/Core/Room+EngineDelegate.swift b/Sources/LiveKit/Core/Room+EngineDelegate.swift index a7a579dfa..82677deb6 100644 --- a/Sources/LiveKit/Core/Room+EngineDelegate.swift +++ b/Sources/LiveKit/Core/Room+EngineDelegate.swift @@ -89,7 +89,7 @@ extension Room: EngineDelegate { } activeSpeakers.append(localParticipant) } else { - if let participant = state.remoteParticipants[speaker.sid] { + if let participant = state.remoteParticipant(sid: speaker.sid) { participant._state.mutate { $0.audioLevel = speaker.level $0.isSpeaking = true @@ -140,9 +140,14 @@ extension Room: EngineDelegate { trackSid = track.trackId } - let participant = _state.mutate { $0.getOrCreateRemoteParticipant(sid: participantSid, room: self) } + let participant = _state.read { + $0.remoteParticipants.values.first { $0.sid == participantSid } + } - log("added media track from: \(participantSid), sid: \(trackSid)") + guard let participant else { + log("RemoteParticipant not found for sid: \(participantSid), remoteParticipants: \(remoteParticipants)", .warning) + return + } let task = Task.retrying(retryDelay: 0.2) { _, _ in // TODO: Only retry for TrackError.state = error @@ -163,7 +168,7 @@ extension Room: EngineDelegate { func engine(_ engine: Engine, didReceive userPacket: Livekit_UserPacket) { // participant could be null if data broadcasted from server - let participant = _state.remoteParticipants[userPacket.participantSid] + let participant = _state.remoteParticipants[userPacket.participantIdentity] engine.executeIfConnected { [weak self] in guard let self else { return } diff --git a/Sources/LiveKit/Core/Room+SignalClientDelegate.swift b/Sources/LiveKit/Core/Room+SignalClientDelegate.swift index 433b92561..cf170b5dc 100644 --- a/Sources/LiveKit/Core/Room+SignalClientDelegate.swift +++ b/Sources/LiveKit/Core/Room+SignalClientDelegate.swift @@ -82,7 +82,7 @@ extension Room: SignalClientDelegate { if !joinResponse.otherParticipants.isEmpty { for otherParticipant in joinResponse.otherParticipants { - $0.getOrCreateRemoteParticipant(sid: otherParticipant.sid, info: otherParticipant, room: self) + $0.updateRemoteParticipant(info: otherParticipant, room: self) } } } @@ -105,7 +105,7 @@ extension Room: SignalClientDelegate { var lastSpeakers = state.activeSpeakers.reduce(into: [Sid: Participant]()) { $0[$1.sid] = $1 } for speaker in speakers { - guard let participant = speaker.sid == localParticipant.sid ? localParticipant : state.remoteParticipants[speaker.sid] else { + guard let participant = speaker.sid == localParticipant.sid ? localParticipant : state.remoteParticipant(sid: speaker.sid) else { continue } @@ -142,7 +142,7 @@ extension Room: SignalClientDelegate { if entry.participantSid == localParticipant.sid { // update for LocalParticipant localParticipant._state.mutate { $0.connectionQuality = entry.quality.toLKType() } - } else if let participant = _state.remoteParticipants[entry.participantSid] { + } else if let participant = _state.read({ $0.remoteParticipant(sid: entry.participantSid) }) { // udpate for RemoteParticipant participant._state.mutate { $0.connectionQuality = entry.quality.toLKType() } } @@ -169,7 +169,7 @@ extension Room: SignalClientDelegate { func signalClient(_: SignalClient, didUpdateSubscriptionPermission subscriptionPermission: Livekit_SubscriptionPermissionUpdate) { log("did update subscriptionPermission: \(subscriptionPermission)") - guard let participant = _state.remoteParticipants[subscriptionPermission.participantSid], + guard let participant = _state.read({ $0.remoteParticipant(sid: subscriptionPermission.participantSid) }), let publication = participant.getTrackPublication(sid: subscriptionPermission.trackSid) else { return @@ -194,7 +194,7 @@ extension Room: SignalClientDelegate { func signalClient(_: SignalClient, didUpdateParticipants participants: [Livekit_ParticipantInfo]) { log("participants: \(participants)") - var disconnectedParticipants = [Sid]() + var disconnectedParticipantIdentities = [Identity]() var newParticipants = [RemoteParticipant]() _state.mutate { @@ -206,10 +206,10 @@ extension Room: SignalClientDelegate { if info.state == .disconnected { // when it's disconnected, send updates - disconnectedParticipants.append(info.sid) + disconnectedParticipantIdentities.append(info.identity) } else { - let isNewParticipant = $0.remoteParticipants[info.sid] == nil - let participant = $0.getOrCreateRemoteParticipant(sid: info.sid, info: info, room: self) + let isNewParticipant = $0.remoteParticipant(sid: info.sid) == nil + let participant = $0.updateRemoteParticipant(info: info, room: self) if isNewParticipant { newParticipants.append(participant) @@ -220,9 +220,9 @@ extension Room: SignalClientDelegate { } } - for sid in disconnectedParticipants { + for identity in disconnectedParticipantIdentities { Task { - try await onParticipantDisconnect(sid: sid) + try await _onParticipantDidDisconnect(identity: identity) } } diff --git a/Sources/LiveKit/Core/Room.swift b/Sources/LiveKit/Core/Room.swift index c3d863e4c..d486455b4 100644 --- a/Sources/LiveKit/Core/Room.swift +++ b/Sources/LiveKit/Core/Room.swift @@ -50,7 +50,7 @@ public class Room: NSObject, ObservableObject, Loggable { public var serverNodeId: String? { _state.serverInfo?.nodeID.nilIfEmpty } @objc - public var remoteParticipants: [Sid: RemoteParticipant] { _state.remoteParticipants } + public var remoteParticipants: [Identity: RemoteParticipant] { _state.remoteParticipants } @objc public var activeSpeakers: [Participant] { _state.activeSpeakers } @@ -102,7 +102,7 @@ public class Room: NSObject, ObservableObject, Loggable { var name: String? var metadata: String? - var remoteParticipants = [Sid: RemoteParticipant]() + var remoteParticipants = [Identity: RemoteParticipant]() var activeSpeakers = [Participant]() var isRecording: Bool = false @@ -114,15 +114,19 @@ public class Room: NSObject, ObservableObject, Loggable { var serverInfo: Livekit_ServerInfo? @discardableResult - mutating func getOrCreateRemoteParticipant(sid: Sid, info: Livekit_ParticipantInfo? = nil, room: Room) -> RemoteParticipant { - if let participant = remoteParticipants[sid] { - return participant - } - - let participant = RemoteParticipant(sid: sid, info: info, room: room) - remoteParticipants[sid] = participant + mutating func updateRemoteParticipant(info: Livekit_ParticipantInfo, room: Room) -> RemoteParticipant { + // Check if RemoteParticipant with same identity exists... + if let participant = remoteParticipants[info.identity] { return participant } + // Create new RemoteParticipant... + let participant = RemoteParticipant(info: info, room: room) + remoteParticipants[info.identity] = participant return participant } + + // Find RemoteParticipant by Sid + func remoteParticipant(sid: Sid) -> RemoteParticipant? { + remoteParticipants.values.first(where: { $0.sid == sid }) + } } var _state: StateSync @@ -296,8 +300,7 @@ extension Room { log("notify: \(_notify)") // Stop all local & remote tracks - let allParticipants = ([[localParticipant], - _state.remoteParticipants.map(\.value)] as [[Participant?]]) + let allParticipants = ([[localParticipant], Array(_state.remoteParticipants.values)] as [[Participant?]]) .joined() .compactMap { $0 } @@ -310,9 +313,9 @@ extension Room { } } - func onParticipantDisconnect(sid: Sid) async throws { - guard let participant = _state.mutate({ $0.remoteParticipants.removeValue(forKey: sid) }) else { - throw EngineError.state(message: "Participant not found for \(sid)") + func _onParticipantDidDisconnect(identity: Identity) async throws { + guard let participant = _state.mutate({ $0.remoteParticipants.removeValue(forKey: identity) }) else { + throw EngineError.state(message: "Participant not found for \(identity)") } await participant.cleanUp(notify: true) diff --git a/Sources/LiveKit/E2EE/E2EEManager.swift b/Sources/LiveKit/E2EE/E2EEManager.swift index d173ab441..8a962ce0b 100644 --- a/Sources/LiveKit/E2EE/E2EEManager.swift +++ b/Sources/LiveKit/E2EE/E2EEManager.swift @@ -63,7 +63,7 @@ public class E2EEManager: NSObject, ObservableObject, Loggable { } self.room = room self.room?.delegates.add(delegate: self) - self.room?.localParticipant.tracks.forEach { (_: Sid, publication: TrackPublication) in + self.room?.localParticipant.tracksPublications.values.forEach { (publication: TrackPublication) in if publication.encryptionType == EncryptionType.none { self.log("E2EEManager::setup: local participant \(self.room!.localParticipant.sid) track \(publication.sid) encryptionType is none, skip") return @@ -76,8 +76,8 @@ public class E2EEManager: NSObject, ObservableObject, Loggable { trackPublications[fc] = publication } - self.room?.remoteParticipants.forEach { (_: Sid, participant: RemoteParticipant) in - participant.tracks.forEach { (_: Sid, publication: TrackPublication) in + self.room?.remoteParticipants.values.forEach { (participant: RemoteParticipant) in + participant.tracksPublications.values.forEach { (publication: TrackPublication) in if publication.encryptionType == EncryptionType.none { self.log("E2EEManager::setup: remote participant \(participant.sid) track \(publication.sid) encryptionType is none, skip") return diff --git a/Sources/LiveKit/Extensions/String.swift b/Sources/LiveKit/Extensions/String.swift index 89734ea9a..60bd3e593 100644 --- a/Sources/LiveKit/Extensions/String.swift +++ b/Sources/LiveKit/Extensions/String.swift @@ -16,34 +16,6 @@ import Foundation -class Identity { - let identity: String - let publish: String? - - init(identity: String, - publish: String?) - { - self.identity = identity - self.publish = publish - } -} - -extension Livekit_ParticipantInfo { - // parses identity string for the &publish= param of identity - func parseIdentity() -> Identity { - let segments = identity.split(separator: "#", maxSplits: 1) - var publishSegment: String? - if segments.count >= 2 { - publishSegment = String(segments[1]) - } - - return Identity( - identity: String(segments[0]), - publish: publishSegment - ) - } -} - extension String { /// Simply return nil if String is empty var nilIfEmpty: String? { diff --git a/Sources/LiveKit/Participant/LocalParticipant.swift b/Sources/LiveKit/Participant/LocalParticipant.swift index 0e73447c2..656779b80 100644 --- a/Sources/LiveKit/Participant/LocalParticipant.swift +++ b/Sources/LiveKit/Participant/LocalParticipant.swift @@ -34,7 +34,7 @@ public class LocalParticipant: Participant { private var trackPermissions: [ParticipantTrackPermission] = [] init(room: Room) { - super.init(sid: "", room: room) + super.init(sid: "", identity: "", room: room) } func getTrackPublication(sid: Sid) -> LocalTrackPublication? { @@ -307,7 +307,7 @@ public class LocalParticipant: Participant { @objc public func publish(data: Data, reliability: Reliability = .reliable, - destinations: [Sid]? = nil, + destinationIdentities: [Identity]? = nil, topic: String? = nil, options: DataPublishOptions? = nil) async throws { @@ -316,7 +316,7 @@ public class LocalParticipant: Participant { let userPacket = Livekit_UserPacket.with { $0.participantSid = self.sid $0.payload = data - $0.destinationSids = destinations ?? options.destinations + $0.destinationIdentities = destinationIdentities ?? options.destinationIdentities $0.topic = topic ?? options.topic ?? "" } diff --git a/Sources/LiveKit/Participant/Participant.swift b/Sources/LiveKit/Participant/Participant.swift index d056724e3..f84f866b7 100644 --- a/Sources/LiveKit/Participant/Participant.swift +++ b/Sources/LiveKit/Participant/Participant.swift @@ -29,7 +29,7 @@ public class Participant: NSObject, ObservableObject, Loggable { public var sid: Sid { _state.sid } @objc - public var identity: String? { _state.identity } + public var identity: String { _state.identity } @objc public var name: String? { _state.name } @@ -53,7 +53,7 @@ public class Participant: NSObject, ObservableObject, Loggable { public var joinedAt: Date? { _state.joinedAt } @objc - public var tracks: [String: TrackPublication] { _state.tracks } + public var tracksPublications: [Sid: TrackPublication] { _state.tracks } @objc public var audioTracks: [TrackPublication] { @@ -74,7 +74,7 @@ public class Participant: NSObject, ObservableObject, Loggable { struct State: Equatable, Hashable { var sid: Sid - var identity: String? + var identity: String var name: String? var audioLevel: Float = 0.0 var isSpeaking: Bool = false @@ -87,11 +87,11 @@ public class Participant: NSObject, ObservableObject, Loggable { var _state: StateSync - init(sid: String, room: Room) { + init(sid: Sid, identity: Identity, room: Room) { self.room = room // initial state - _state = StateSync(State(sid: sid)) + _state = StateSync(State(sid: sid, identity: identity)) super.init() @@ -153,7 +153,7 @@ public class Participant: NSObject, ObservableObject, Loggable { func cleanUp(notify _notify: Bool = true) async { await unpublishAll(notify: _notify) // Reset state - _state.mutate { $0 = State(sid: "") } + _state.mutate { $0 = State(sid: "", identity: "") } } func unpublishAll(notify _: Bool = true) async { diff --git a/Sources/LiveKit/Participant/RemoteParticipant.swift b/Sources/LiveKit/Participant/RemoteParticipant.swift index 245d0d625..2e396eb88 100644 --- a/Sources/LiveKit/Participant/RemoteParticipant.swift +++ b/Sources/LiveKit/Participant/RemoteParticipant.swift @@ -20,12 +20,16 @@ import Foundation @objc public class RemoteParticipant: Participant { - init(sid: Sid, info: Livekit_ParticipantInfo?, room: Room) { - super.init(sid: sid, room: room) + init(info: Livekit_ParticipantInfo, room: Room) { + super.init(sid: info.sid, + identity: info.identity, + room: room) - if let info { - updateFromInfo(info: info) + if identity.isEmpty { + log("RemoteParticipant.identity is empty", .error) } + + updateFromInfo(info: info) } func getTrackPublication(sid: Sid) -> RemoteTrackPublication? { diff --git a/Sources/LiveKit/Types/Options/DataPublishOptions.swift b/Sources/LiveKit/Types/Options/DataPublishOptions.swift index 43fb78537..251900f79 100644 --- a/Sources/LiveKit/Types/Options/DataPublishOptions.swift +++ b/Sources/LiveKit/Types/Options/DataPublishOptions.swift @@ -22,17 +22,17 @@ public class DataPublishOptions: NSObject, PublishOptions { public let name: String? @objc - public let destinations: [Sid] + public let destinationIdentities: [Identity] @objc public let topic: String? public init(name: String? = nil, - destinations: [String] = [], + destinationIdentities: [Identity] = [], topic: String? = nil) { self.name = name - self.destinations = destinations + self.destinationIdentities = destinationIdentities self.topic = topic } @@ -41,14 +41,14 @@ public class DataPublishOptions: NSObject, PublishOptions { override public func isEqual(_ object: Any?) -> Bool { guard let other = object as? Self else { return false } return name == other.name && - destinations == other.destinations && + destinationIdentities == other.destinationIdentities && topic == other.topic } override public var hash: Int { var hasher = Hasher() hasher.combine(name) - hasher.combine(destinations) + hasher.combine(destinationIdentities) hasher.combine(topic) return hasher.finalize() } diff --git a/Sources/LiveKit/Types/Other.swift b/Sources/LiveKit/Types/Other.swift index 96f77494c..e4aab99c2 100644 --- a/Sources/LiveKit/Types/Other.swift +++ b/Sources/LiveKit/Types/Other.swift @@ -19,6 +19,7 @@ import Foundation @_implementationOnly import WebRTC public typealias Sid = String +public typealias Identity = String @objc public enum Reliability: Int { diff --git a/Sources/LiveKit/Views/VideoView.swift b/Sources/LiveKit/Views/VideoView.swift index 7ac6eb08d..af40158e3 100644 --- a/Sources/LiveKit/Views/VideoView.swift +++ b/Sources/LiveKit/Views/VideoView.swift @@ -416,7 +416,7 @@ public class VideoView: NativeView, Loggable { // dimensions are required to continue computation guard let dimensions = track._state.dimensions else { - log("dimensions are nil, cannot layout without dimensions, track: \(track)", .warning) + // log("dimensions are nil, cannot layout without dimensions, track: \(track)", .debug) return }