@mentra/glasses-media 3.2.0-dev.227 → 3.2.0-dev.233
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ios/CoreKit/Sources/GlassesMediaCore/LocalMediaPolicy.swift +5 -1
- package/ios/GlassesHotspotNetwork.swift +19 -3
- package/ios/GlassesMedia.podspec +2 -1
- package/ios/GlassesPeerFactory.swift +23 -0
- package/ios/LocalWhipIngestSource.swift +1 -1
- package/ios/PhoneWhipPublisher.swift +1 -1
- package/package.json +1 -1
|
@@ -53,7 +53,11 @@ public enum LocalMediaPolicy {
|
|
|
53
53
|
candidates += 1
|
|
54
54
|
return true
|
|
55
55
|
}
|
|
56
|
-
guard candidates > 0 else {
|
|
56
|
+
guard candidates > 0 else {
|
|
57
|
+
throw LocalMediaError(answer
|
|
58
|
+
? "Phone answer has no host ICE candidate on the glasses hotspot"
|
|
59
|
+
: "Glasses offer has no host ICE candidate on the glasses hotspot")
|
|
60
|
+
}
|
|
57
61
|
return lines.joined(separator: "\r\n") + "\r\n"
|
|
58
62
|
}
|
|
59
63
|
}
|
|
@@ -7,6 +7,7 @@ import NetworkExtension
|
|
|
7
7
|
public final class GlassesHotspotNetwork {
|
|
8
8
|
private let queue = DispatchQueue(label: "com.mentra.glassesmedia.hotspot")
|
|
9
9
|
private var ssid: String?
|
|
10
|
+
private var lastHotspotSSID: String?
|
|
10
11
|
private var localAddress: String?
|
|
11
12
|
private var gatewayAddress: String?
|
|
12
13
|
private var generation = 0
|
|
@@ -27,6 +28,7 @@ public final class GlassesHotspotNetwork {
|
|
|
27
28
|
self.generation += 1
|
|
28
29
|
let gen = self.generation
|
|
29
30
|
self.ssid = ssid
|
|
31
|
+
self.lastHotspotSSID = ssid
|
|
30
32
|
self.gatewayAddress = gateway
|
|
31
33
|
self.cancelled = false
|
|
32
34
|
self.applying = true
|
|
@@ -104,7 +106,7 @@ public final class GlassesHotspotNetwork {
|
|
|
104
106
|
}
|
|
105
107
|
}
|
|
106
108
|
|
|
107
|
-
public func awaitInternet(completion: @escaping (Bool, String) -> Void) {
|
|
109
|
+
public func awaitInternet(requireCellular: Bool = true, completion: @escaping (Bool, String) -> Void) {
|
|
108
110
|
queue.async {
|
|
109
111
|
let monitor = NWPathMonitor()
|
|
110
112
|
var finished = false
|
|
@@ -116,9 +118,21 @@ public final class GlassesHotspotNetwork {
|
|
|
116
118
|
}
|
|
117
119
|
monitor.pathUpdateHandler = { path in
|
|
118
120
|
if path.status == .satisfied, path.usesInterfaceType(.cellular) { finish(true, "cellular") }
|
|
121
|
+
// Once the hotspot is released, a return to the user's Wi-Fi is also valid.
|
|
122
|
+
// Do not mistake the departing glasses AP's local-only path for restored internet.
|
|
123
|
+
if !requireCellular, path.status == .satisfied, path.usesInterfaceType(.wifi) {
|
|
124
|
+
NEHotspotNetwork.fetchCurrent { network in
|
|
125
|
+
self.queue.async {
|
|
126
|
+
guard let network, !network.ssid.isEmpty, network.ssid != self.lastHotspotSSID else { return }
|
|
127
|
+
finish(true, "wifi")
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
119
131
|
}
|
|
120
132
|
monitor.start(queue: self.queue)
|
|
121
|
-
self.queue.asyncAfter(deadline: .now() + 15) {
|
|
133
|
+
self.queue.asyncAfter(deadline: .now() + 15) {
|
|
134
|
+
finish(false, requireCellular ? "Cellular internet did not become the default route" : "Internet did not return after leaving the glasses hotspot")
|
|
135
|
+
}
|
|
122
136
|
}
|
|
123
137
|
}
|
|
124
138
|
|
|
@@ -136,7 +150,9 @@ public final class GlassesHotspotNetwork {
|
|
|
136
150
|
} else if remaining > 0 {
|
|
137
151
|
self.queue.asyncAfter(deadline: .now() + 0.5) { self.waitForAddress(ssid: ssid, generation: gen, remaining: remaining - 1) }
|
|
138
152
|
} else {
|
|
139
|
-
|
|
153
|
+
let association = network == nil ? "unavailable" : (network?.ssid == ssid ? "matched" : "different")
|
|
154
|
+
let address = Self.wifiAddress() ?? "none"
|
|
155
|
+
self.finishJoin(.failure(LocalMediaError("Glasses hotspot has no verified Wi-Fi address (SSID=\(association), Wi-Fi IPv4=\(address))")))
|
|
140
156
|
self.finishLeave()
|
|
141
157
|
}
|
|
142
158
|
}
|
package/ios/GlassesMedia.podspec
CHANGED
|
@@ -12,7 +12,8 @@ Pod::Spec.new do |s|
|
|
|
12
12
|
s.swift_version = '5.9'
|
|
13
13
|
s.static_framework = true
|
|
14
14
|
s.dependency 'ExpoModulesCore'
|
|
15
|
-
|
|
15
|
+
# Keep in sync with the iOS-only @livekit/react-native-webrtc podspec patch.
|
|
16
|
+
s.dependency 'WebRTC-SDK', '144.7559.15'
|
|
16
17
|
s.frameworks = 'AVFoundation', 'CoreMedia', 'CoreVideo', 'Network', 'NetworkExtension'
|
|
17
18
|
s.source_files = '*.{swift,h,m,mm}', 'CoreKit/Sources/GlassesMediaCore/*.swift'
|
|
18
19
|
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' }
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import WebRTC
|
|
2
|
+
|
|
3
|
+
/// Both legs need real interface enumeration: local Wi-Fi can be absent from the default
|
|
4
|
+
/// internet NWPath while cellular carries the uplink. Each factory's adapter mask and the
|
|
5
|
+
/// receiver's SDP policy still restrict candidates to the appropriate leg.
|
|
6
|
+
enum GlassesPeerFactory {
|
|
7
|
+
/// M144 honors this field trial for factories with custom audio devices; M137 installed
|
|
8
|
+
/// NWPathMonitor unconditionally (including when disableNetworkMonitor was set).
|
|
9
|
+
/// The SDK setting is process-wide. Set it once and never toggle it around factory creation:
|
|
10
|
+
/// receiver, relay, and React Native WebRTC factories may initialize concurrently.
|
|
11
|
+
private static let configure: Void = {
|
|
12
|
+
RTCPeerConnectionFactory.configureFieldTrials("WebRTC-Network-UseNWPathMonitor/Disabled/")
|
|
13
|
+
}()
|
|
14
|
+
|
|
15
|
+
static func make(audioDevice: RTCAudioDevice) -> RTCPeerConnectionFactory {
|
|
16
|
+
_ = configure
|
|
17
|
+
return RTCPeerConnectionFactory(
|
|
18
|
+
encoderFactory: RTCDefaultVideoEncoderFactory(),
|
|
19
|
+
decoderFactory: RTCDefaultVideoDecoderFactory(),
|
|
20
|
+
audioDevice: audioDevice
|
|
21
|
+
)
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -67,7 +67,7 @@ public final class LocalWhipIngestSource: NSObject, DecodedGlassesMediaSource {
|
|
|
67
67
|
let gen = self.generation
|
|
68
68
|
self.transition(.connecting, "listening")
|
|
69
69
|
RTCInitializeSSL()
|
|
70
|
-
let factory =
|
|
70
|
+
let factory = GlassesPeerFactory.make(audioDevice: ReceiveOnlyAudioDevice())
|
|
71
71
|
let options = RTCPeerConnectionFactoryOptions()
|
|
72
72
|
options.ignoreCellularNetworkAdapter = true
|
|
73
73
|
options.ignoreVPNNetworkAdapter = true
|
|
@@ -39,7 +39,7 @@ final class PhoneWhipPublisher: NSObject {
|
|
|
39
39
|
queue.async {
|
|
40
40
|
guard !self.stopped else { return }
|
|
41
41
|
RTCInitializeSSL()
|
|
42
|
-
let factory =
|
|
42
|
+
let factory = GlassesPeerFactory.make(audioDevice: self.audioDevice)
|
|
43
43
|
let options = RTCPeerConnectionFactoryOptions()
|
|
44
44
|
options.ignoreWiFiNetworkAdapter = true
|
|
45
45
|
options.ignoreVPNNetworkAdapter = true
|