oficina / meet Public

Clone
git clone https://amadan.net/oficina/meet

Plain git — no account needed to clone.

Download

Download this file

1import Foundation
2
3public struct RosterPeer: Decodable, Identifiable {
4 public let peer: String
5 public let name: String
6 public let kind: String
7 public let gen: Int64?
8 /// The peer's own last report of its microphone and camera (spec
9 /// §15.19). Nil is unknown — a client that predates the report, or one
10 /// that has not made it yet — and is shown as neither muted nor off.
11 public let media: PeerMedia?
12 public var id: String { peer }
13}
14/// What a peer says about its own outgoing media (spec §15.19), and about
15/// the sound reaching it (§15.20).
16public struct PeerMedia: Decodable, Equatable, Sendable {
17 public let muted: Bool
18 public let cameraOff: Bool
19 /// Its browser refused to play sound and is waiting for a click. Nil
20 /// (absent) is none; the key only travels with bad news.
21 public let audioBlocked: Bool?
22 /// The peers whose audio reaches it as silence. Nil (absent) is none.
23 public let noSoundFrom: [String]?
24 /// The peers whose shared screen has stopped moving for it (§15.21).
25 /// A sharer that finds itself here recovers its screen. Nil is none.
26 public let screenStalled: [String]?
27 /// The peers whose audio it is actually receiving and playing (§15.24).
28 /// Nil (absent) is a client that does not say; empty is one that hears
29 /// nobody yet.
30 public let hearing: [String]?
31
32 public init(
33 muted: Bool, cameraOff: Bool, audioBlocked: Bool? = nil, noSoundFrom: [String]? = nil,
34 screenStalled: [String]? = nil, hearing: [String]? = nil
35 ) {
36 self.muted = muted
37 self.cameraOff = cameraOff
38 self.audioBlocked = audioBlocked
39 self.noSoundFrom = noSoundFrom
40 self.screenStalled = screenStalled
41 self.hearing = hearing
42 }
43}
44public struct RoomState: Decodable {
45 public let phase: String
46 public let peer: String
47 public let boot: String
48 public let reason: String?
49 public let peers: [RosterPeer]
50 public let waiting: [RosterPeer]
51}
52struct WireEvent: Decodable {
53 let type: String
54 let state: RoomState?
55 let message: Message?
56 let ice: ICEConfig?
57 let info: MeetingInfo?
58 let signal: Message?
59}
60struct Message: Decodable {
61 let type: String
62 let from: String?
63 let peer: String?
64 let name: String?
65 let gen: Int64?
66 let seq: UInt64?
67 let payload: Payload?
68}
69struct Payload: Decodable {
70 let description: Description?
71 let candidate: Candidate?
72 let text: String?
73 /// `share`'s one field (§5.4): whether the sender is showing a screen.
74 let on: Bool?
75}
76struct Description: Codable {
77 let type: String
78 let sdp: String
79}
80struct Candidate: Codable {
81 let candidate: String
82 let sdpMid: String?
83 let sdpMLineIndex: Int32?
84}
85struct ICEConfig: Decodable {
86 struct Server: Decodable {
87 let urls: [String]
88 let username: String?
89 let credential: String?
90 }
91 let iceServers: [Server]
92 let renewAt: Int64
93 let expiresAt: Int64
94}
95/// §5.3 for the apps: when a fresh `/ice` answer should restart ICE on a
96/// connection, rather than be adopted for whatever it gathers next.
97///
98/// The core fetches the list on every roster (every stream resume, five
99/// minutes apart at most) as well as at `renewAt`, so a connection hears a
100/// fresh list at the latest five minutes after its own renewal falls due —
101/// well inside the half hour between `renewAt` and expiry.
102enum ICERenewal {
103 /// - Parameters:
104 /// - gathered: the list the connection was built with, or last restarted onto.
105 /// - next: the list just fetched.
106 /// - now: unix seconds.
107 static func restart(gathered: ICEConfig?, next: ICEConfig, now: Int64) -> Bool {
108 // The floor has nothing to renew onto.
109 guard !next.iceServers.isEmpty else { return false }
110 guard let gathered else { return true }
111 // Built on the floor while the first fetch was out, and a relay since:
112 // adopt it now, as the browser's adoptServers does.
113 if gathered.iceServers.isEmpty { return true }
114 return gathered.renewAt > 0 && now >= gathered.renewAt
115 }
116}
117
118func json(_ value: Any) -> String? {
119 guard JSONSerialization.isValidJSONObject(value),
120 let data = try? JSONSerialization.data(withJSONObject: value)
121 else { return nil }
122 return String(data: data, encoding: .utf8)
123}
124
125struct MeetingInfo: Decodable {
126 let title: String
127 /// A title someone chose rather than one Calls wrote. Absent from a
128 /// server before 0.4.7, where it reads as false and no title is shown.
129 let named: Bool?
130 let name: String
131 let standing: String
132 let floor: Bool
133}
134