@mentra/glasses-media 3.2.1 → 3.3.0-beta.942
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/README.md +14 -2
- package/android/src/main/java/com/mentra/glassesmedia/source/StillPhotoInbox.kt +50 -0
- package/android/src/main/java/com/mentra/glassesmedia/source/WhipIngestProtocol.kt +22 -0
- package/android/src/main/java/com/mentra/glassesmedia/source/WhipIngestServer.kt +110 -10
- package/index.d.ts +11 -0
- package/index.js +9 -0
- package/ios/CoreKit/Sources/GlassesMediaCore/HotspotAccessPurpose.swift +5 -0
- package/ios/CoreKit/Sources/GlassesMediaCore/HotspotInterfacePolicy.swift +155 -0
- package/ios/CoreKit/Sources/GlassesMediaCore/HotspotJoinFailure.swift +23 -0
- package/ios/CoreKit/Sources/GlassesMediaCore/HotspotNetworkOwnership.swift +22 -0
- package/ios/CoreKit/Sources/GlassesMediaCore/MacHotspotAssociation.swift +161 -0
- package/ios/CoreKit/Sources/GlassesMediaCore/MacHotspotAuthorization.swift +59 -0
- package/ios/GlassesHotspotNetwork.swift +254 -73
- package/ios/GlassesMedia.podspec +1 -1
- package/ios/GlassesMediaRelayModule.swift +61 -11
- package/package.json +6 -1
- package/ios/CoreKit/Sources/GlassesMediaCore/MacE2EHotspotLease.swift +0 -20
package/ios/GlassesMedia.podspec
CHANGED
|
@@ -14,7 +14,7 @@ Pod::Spec.new do |s|
|
|
|
14
14
|
s.dependency 'ExpoModulesCore'
|
|
15
15
|
# Keep in sync with the iOS-only @livekit/react-native-webrtc podspec patch.
|
|
16
16
|
s.dependency 'WebRTC-SDK', '144.7559.15'
|
|
17
|
-
s.frameworks = 'AVFoundation', 'CoreMedia', 'CoreVideo', 'Network', 'NetworkExtension'
|
|
17
|
+
s.frameworks = 'AVFoundation', 'CoreMedia', 'CoreVideo', 'CoreLocation', 'Network', 'NetworkExtension'
|
|
18
18
|
s.source_files = '*.{swift,h,m,mm}', 'CoreKit/Sources/GlassesMediaCore/*.swift'
|
|
19
19
|
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' }
|
|
20
20
|
end
|
|
@@ -4,11 +4,49 @@ import Foundation
|
|
|
4
4
|
public final class GlassesMediaRelayModule: Module {
|
|
5
5
|
private let queue = DispatchQueue(label: "com.mentra.glassesmedia.relay")
|
|
6
6
|
private var session: ManagedRelaySession?
|
|
7
|
+
private let localHotspot = GlassesHotspotNetwork()
|
|
8
|
+
private var localHotspotOwner: String?
|
|
7
9
|
|
|
8
10
|
public func definition() -> ModuleDefinition {
|
|
9
11
|
Name("MentraGlassesMediaRelay")
|
|
10
12
|
Events("onRelayState")
|
|
11
13
|
|
|
14
|
+
AsyncFunction("joinLocalHotspot") { (id: String, ssid: String, password: String, gateway: String?, purpose: String, promise: Promise) in
|
|
15
|
+
self.queue.async {
|
|
16
|
+
guard self.localHotspotOwner == nil else {
|
|
17
|
+
promise.reject(LocalMediaError("Previous hotspot session has not been released")); return
|
|
18
|
+
}
|
|
19
|
+
guard let accessPurpose = HotspotAccessPurpose(rawValue: purpose) else {
|
|
20
|
+
promise.reject(LocalMediaError("Invalid hotspot access purpose")); return
|
|
21
|
+
}
|
|
22
|
+
self.localHotspotOwner = id
|
|
23
|
+
self.localHotspot.join(ssid: ssid, passphrase: password, gateway: gateway, purpose: accessPurpose) { result in
|
|
24
|
+
switch result {
|
|
25
|
+
case let .success(address): promise.resolve(address)
|
|
26
|
+
case let .failure(error):
|
|
27
|
+
let failure = HotspotJoinFailure(error)
|
|
28
|
+
promise.reject(Exception(name: "HotspotJoinFailure", description: failure.message, code: failure.code))
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
AsyncFunction("leaveLocalHotspot") { (id: String, promise: Promise) in
|
|
35
|
+
self.queue.async {
|
|
36
|
+
guard self.localHotspotOwner == id else { promise.resolve(nil); return }
|
|
37
|
+
self.localHotspot.leave { result in
|
|
38
|
+
self.queue.async {
|
|
39
|
+
switch result {
|
|
40
|
+
case .success:
|
|
41
|
+
if self.localHotspotOwner == id { self.localHotspotOwner = nil }
|
|
42
|
+
promise.resolve(nil)
|
|
43
|
+
case let .failure(error): promise.reject(error)
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
12
50
|
AsyncFunction("prepare") { (options: [String: Any], promise: Promise) in
|
|
13
51
|
guard let id = options["attemptId"] as? String,
|
|
14
52
|
let ingest = options["ingestUrl"] as? String, let url = URL(string: ingest), url.scheme == "https",
|
|
@@ -38,16 +76,23 @@ public final class GlassesMediaRelayModule: Module {
|
|
|
38
76
|
AsyncFunction("stop") { (id: String, promise: Promise) in
|
|
39
77
|
self.queue.async {
|
|
40
78
|
guard let session = self.session, session.id == id else { promise.resolve(nil); return }
|
|
41
|
-
session.stop {
|
|
42
|
-
|
|
43
|
-
|
|
79
|
+
session.stop { result in
|
|
80
|
+
switch result {
|
|
81
|
+
case .success:
|
|
82
|
+
if self.session === session { self.session = nil }
|
|
83
|
+
promise.resolve(nil)
|
|
84
|
+
case let .failure(error): promise.reject(error)
|
|
85
|
+
}
|
|
44
86
|
}
|
|
45
87
|
}
|
|
46
88
|
}
|
|
47
89
|
|
|
48
90
|
OnDestroy {
|
|
49
91
|
self.queue.async {
|
|
50
|
-
self.session?.stop {
|
|
92
|
+
self.session?.stop { result in
|
|
93
|
+
if case .success = result { self.session = nil }
|
|
94
|
+
}
|
|
95
|
+
self.localHotspot.leave { _ in }
|
|
51
96
|
}
|
|
52
97
|
}
|
|
53
98
|
}
|
|
@@ -62,7 +107,7 @@ private final class ManagedRelaySession {
|
|
|
62
107
|
private var publisher: PhoneWhipPublisher?
|
|
63
108
|
private var stopped = false
|
|
64
109
|
private var stopFinished = false
|
|
65
|
-
private var stopReplies: [() -> Void] = []
|
|
110
|
+
private var stopReplies: [(Result<Void, Error>) -> Void] = []
|
|
66
111
|
private var ready: ((Result<String, Error>) -> Void)?
|
|
67
112
|
|
|
68
113
|
init(id: String, queue: DispatchQueue) {
|
|
@@ -116,10 +161,11 @@ private final class ManagedRelaySession {
|
|
|
116
161
|
let callback = ready; ready = nil; callback?(result)
|
|
117
162
|
}
|
|
118
163
|
|
|
119
|
-
func stop(completion: @escaping () -> Void) {
|
|
120
|
-
if stopFinished { completion(); return }
|
|
164
|
+
func stop(completion: @escaping (Result<Void, Error>) -> Void) {
|
|
165
|
+
if stopFinished { completion(.success(())); return }
|
|
166
|
+
let stopping = !stopReplies.isEmpty
|
|
121
167
|
stopReplies.append(completion)
|
|
122
|
-
guard !
|
|
168
|
+
guard !stopping else { return }
|
|
123
169
|
stopped = true
|
|
124
170
|
finish(.failure(LocalMediaError("Relay cancelled")))
|
|
125
171
|
hotspot.onLost = nil
|
|
@@ -131,10 +177,14 @@ private final class ManagedRelaySession {
|
|
|
131
177
|
if let publisher {
|
|
132
178
|
group.enter(); publisher.stop { group.leave() }
|
|
133
179
|
}
|
|
134
|
-
|
|
180
|
+
var cleanupResult: Result<Void, Error> = .success(())
|
|
181
|
+
group.enter(); hotspot.leave { result in
|
|
182
|
+
self.queue.async { cleanupResult = result; group.leave() }
|
|
183
|
+
}
|
|
135
184
|
group.notify(queue: queue) {
|
|
136
|
-
self.receiver = nil; self.publisher = nil
|
|
137
|
-
|
|
185
|
+
self.receiver = nil; self.publisher = nil
|
|
186
|
+
if case .success = cleanupResult { self.stopFinished = true }
|
|
187
|
+
let replies = self.stopReplies; self.stopReplies.removeAll(); replies.forEach { $0(cleanupResult) }
|
|
138
188
|
}
|
|
139
189
|
}
|
|
140
190
|
}
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mentra/glasses-media",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.3.0-beta.942",
|
|
4
4
|
"description": "Shared native glasses media receivers and local network transport",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Mentra Community",
|
|
7
7
|
"homepage": "https://github.com/Mentra-Community/MentraOS",
|
|
8
8
|
"main": "index.js",
|
|
9
|
+
"types": "index.d.ts",
|
|
9
10
|
"repository": {
|
|
10
11
|
"type": "git",
|
|
11
12
|
"url": "git+https://github.com/Mentra-Community/MentraOS.git",
|
|
@@ -14,8 +15,12 @@
|
|
|
14
15
|
"publishConfig": {
|
|
15
16
|
"access": "public"
|
|
16
17
|
},
|
|
18
|
+
"peerDependencies": {
|
|
19
|
+
"expo-modules-core": "~55.0.25"
|
|
20
|
+
},
|
|
17
21
|
"files": [
|
|
18
22
|
"index.js",
|
|
23
|
+
"index.d.ts",
|
|
19
24
|
"expo-module.config.json",
|
|
20
25
|
"android/build.gradle",
|
|
21
26
|
"android/src/main",
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
#if MENTRA_E2E
|
|
2
|
-
import Foundation
|
|
3
|
-
|
|
4
|
-
/// Test-build dependency input, not a production network discovery mechanism.
|
|
5
|
-
/// The Mac harness owns association and verifies the USB fixture, gateway MAC,
|
|
6
|
-
/// DHCP address, local health and Ethernet before launching this process.
|
|
7
|
-
public struct MacE2EHotspotLease: Decodable {
|
|
8
|
-
public let ssid: String
|
|
9
|
-
public let gateway: String
|
|
10
|
-
public let address: String
|
|
11
|
-
public let issuedAt: TimeInterval
|
|
12
|
-
|
|
13
|
-
public func matches(ssid: String, gateway: String?, address: String?, now: TimeInterval) -> Bool {
|
|
14
|
-
let age = now - issuedAt
|
|
15
|
-
return age >= 0 && age <= 300 && self.gateway == gateway && self.address == address &&
|
|
16
|
-
LocalMediaPolicy.canReuseHotspot(requestedSSID: ssid, currentSSID: self.ssid,
|
|
17
|
-
address: address, gateway: gateway)
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
#endif
|