@mentra/acs-meeting 3.2.0-dev.226 → 3.2.0-dev.232
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/build/AcsMeeting.types.d.ts +5 -0
- package/build/AcsMeeting.types.d.ts.map +1 -1
- package/build/AcsMeeting.types.js.map +1 -1
- package/build/AcsMeetingModule.d.ts +8 -0
- package/build/AcsMeetingModule.d.ts.map +1 -1
- package/build/AcsMeetingModule.js.map +1 -1
- package/ios/AcsMeetingModule.swift +67 -18
- package/package.json +2 -2
- package/src/AcsMeeting.types.ts +5 -0
- package/src/AcsMeetingModule.ts +4 -0
|
@@ -26,6 +26,11 @@ export type AcsMeetingState = {
|
|
|
26
26
|
activeStream?: AcsActiveStream;
|
|
27
27
|
audioSafety?: AcsAudioSafety;
|
|
28
28
|
mediaSource?: AcsMediaSourceState;
|
|
29
|
+
/** Native receiver diagnostic, including the reason a local WHIP offer was rejected. */
|
|
30
|
+
mediaSourceReason?: string;
|
|
31
|
+
/** ACS disconnect diagnostics, emitted when the final disconnected state arrives. */
|
|
32
|
+
endReason_code?: number;
|
|
33
|
+
endReason_subcode?: number;
|
|
29
34
|
/** Local WHIP endpoint, available after a SoftAP join. */
|
|
30
35
|
ingestUrl?: string;
|
|
31
36
|
/** Remote roster (Android emits this; iOS does not yet). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AcsMeeting.types.d.ts","sourceRoot":"","sources":["../src/AcsMeeting.types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,YAAY,GAAG,OAAO,GAAG,WAAW,GAAG,cAAc,GAAG,OAAO,CAAA;AAEnG,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,OAAO,CAAA;AAChD,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,CAAA;AAC1D,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,UAAU,GAAG,QAAQ,CAAA;AAC3D;;;;GAIG;AACH,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,YAAY,GAAG,MAAM,GAAG,QAAQ,CAAA;AAE3E,MAAM,MAAM,0BAA0B,GAAG,MAAM,GAAG,YAAY,GAAG,WAAW,GAAG,OAAO,GAAG,MAAM,GAAG,cAAc,CAAA;AAEhH,MAAM,MAAM,qBAAqB,GAAG;IAClC,EAAE,EAAE,MAAM,CAAA;IACV,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,KAAK,EAAE,0BAA0B,CAAA;IACjC,OAAO,EAAE,OAAO,CAAA;IAChB,UAAU,EAAE,OAAO,CAAA;CACpB,CAAA;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EAAE,YAAY,CAAA;IACnB,KAAK,EAAE,OAAO,CAAA;IACd,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,EAAE,WAAW,CAAA;IACtB,WAAW,CAAC,EAAE,cAAc,CAAA;IAC5B,YAAY,CAAC,EAAE,eAAe,CAAA;IAC9B,WAAW,CAAC,EAAE,cAAc,CAAA;IAC5B,WAAW,CAAC,EAAE,mBAAmB,CAAA;IACjC,0DAA0D;IAC1D,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,4DAA4D;IAC5D,YAAY,CAAC,EAAE,qBAAqB,EAAE,CAAA;CACvC,CAAA;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC7B,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,GAAG,EAAE,MAAM,CAAA;IACX,aAAa,EAAE,MAAM,CAAA;CACtB,CAAA;AAED,MAAM,MAAM,qBAAqB,GAAG;IAClC,UAAU,EAAE,MAAM,CAAA;IAClB,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,WAAW,CAAC,EAAE;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAC,GAAG;QAAC,IAAI,EAAE,QAAQ,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAC,CAAA;IACjF,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,gFAAgF;IAChF,WAAW,CAAC,EAAE,cAAc,CAAA;IAC5B;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,2DAA2D;IAC3D,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,yDAAyD;IACzD,KAAK,CAAC,EAAE,gBAAgB,CAAA;CACzB,CAAA;AAED,MAAM,MAAM,mBAAmB,GAAG;IAChC,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;CACjB,CAAA;AAED,MAAM,MAAM,sBAAsB,GAAG;IACnC,OAAO,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAA;IACzC,aAAa,EAAE,CAAC,GAAG,EAAE,mBAAmB,KAAK,IAAI,CAAA;CAClD,CAAA"}
|
|
1
|
+
{"version":3,"file":"AcsMeeting.types.d.ts","sourceRoot":"","sources":["../src/AcsMeeting.types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,YAAY,GAAG,OAAO,GAAG,WAAW,GAAG,cAAc,GAAG,OAAO,CAAA;AAEnG,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,OAAO,CAAA;AAChD,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,CAAA;AAC1D,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,UAAU,GAAG,QAAQ,CAAA;AAC3D;;;;GAIG;AACH,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,YAAY,GAAG,MAAM,GAAG,QAAQ,CAAA;AAE3E,MAAM,MAAM,0BAA0B,GAAG,MAAM,GAAG,YAAY,GAAG,WAAW,GAAG,OAAO,GAAG,MAAM,GAAG,cAAc,CAAA;AAEhH,MAAM,MAAM,qBAAqB,GAAG;IAClC,EAAE,EAAE,MAAM,CAAA;IACV,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,KAAK,EAAE,0BAA0B,CAAA;IACjC,OAAO,EAAE,OAAO,CAAA;IAChB,UAAU,EAAE,OAAO,CAAA;CACpB,CAAA;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EAAE,YAAY,CAAA;IACnB,KAAK,EAAE,OAAO,CAAA;IACd,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,EAAE,WAAW,CAAA;IACtB,WAAW,CAAC,EAAE,cAAc,CAAA;IAC5B,YAAY,CAAC,EAAE,eAAe,CAAA;IAC9B,WAAW,CAAC,EAAE,cAAc,CAAA;IAC5B,WAAW,CAAC,EAAE,mBAAmB,CAAA;IACjC,wFAAwF;IACxF,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,qFAAqF;IACrF,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,0DAA0D;IAC1D,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,4DAA4D;IAC5D,YAAY,CAAC,EAAE,qBAAqB,EAAE,CAAA;CACvC,CAAA;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC7B,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,GAAG,EAAE,MAAM,CAAA;IACX,aAAa,EAAE,MAAM,CAAA;CACtB,CAAA;AAED,MAAM,MAAM,qBAAqB,GAAG;IAClC,UAAU,EAAE,MAAM,CAAA;IAClB,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,WAAW,CAAC,EAAE;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAC,GAAG;QAAC,IAAI,EAAE,QAAQ,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAC,CAAA;IACjF,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,gFAAgF;IAChF,WAAW,CAAC,EAAE,cAAc,CAAA;IAC5B;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,2DAA2D;IAC3D,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,yDAAyD;IACzD,KAAK,CAAC,EAAE,gBAAgB,CAAA;CACzB,CAAA;AAED,MAAM,MAAM,mBAAmB,GAAG;IAChC,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;CACjB,CAAA;AAED,MAAM,MAAM,sBAAsB,GAAG;IACnC,OAAO,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAA;IACzC,aAAa,EAAE,CAAC,GAAG,EAAE,mBAAmB,KAAK,IAAI,CAAA;CAClD,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AcsMeeting.types.js","sourceRoot":"","sources":["../src/AcsMeeting.types.ts"],"names":[],"mappings":"","sourcesContent":["export type MeetingPhase = \"idle\" | \"connecting\" | \"lobby\" | \"connected\" | \"disconnected\" | \"error\"\n\nexport type AcsAudioSource = \"glasses\" | \"phone\"\nexport type AcsActiveStream = \"none\" | \"virtual\" | \"local\"\nexport type AcsAudioSafety = \"safe\" | \"degraded\" | \"unsafe\"\n/**\n * Health of the glasses WHEP subscription feeding the call. `failed` = ICE dropped or\n * the WHEP endpoint went away while the ACS call may still be `connected`. Native\n * rebuilds it on its own with backoff; the host can force one via `restartVideoSource`.\n */\nexport type AcsMediaSourceState = \"idle\" | \"connecting\" | \"live\" | \"failed\"\n\nexport type AcsMeetingParticipantState = \"idle\" | \"connecting\" | \"connected\" | \"lobby\" | \"hold\" | \"disconnected\"\n\nexport type AcsMeetingParticipant = {\n id: string\n displayName: string | null\n state: AcsMeetingParticipantState\n isMuted: boolean\n isSpeaking: boolean\n}\n\nexport type AcsMeetingState = {\n state: MeetingPhase\n muted: boolean\n error?: string\n meetingUrl?: string\n provider?: \"acs-teams\"\n audioSource?: AcsAudioSource\n activeStream?: AcsActiveStream\n audioSafety?: AcsAudioSafety\n mediaSource?: AcsMediaSourceState\n /** Local WHIP endpoint, available after a SoftAP join. */\n ingestUrl?: string\n /** Remote roster (Android emits this; iOS does not yet). */\n participants?: AcsMeetingParticipant[]\n}\n\nexport type AcsOutgoingVideo = {\n width: number\n height: number\n fps: number\n maxBitrateBps: number\n}\n\nexport type AcsMeetingJoinOptions = {\n meetingUrl: string\n token: string\n whepUrl?: string\n videoSource?: {type: \"whep\"; url: string} | {type: \"softap\"; bindAddress: string}\n displayName?: string\n /** \"glasses\" sends WHEP PCM. \"phone\" uses the ACS local mic (handset or BT). */\n audioSource?: AcsAudioSource\n /**\n * Hold outgoing PCM this many ms before ACS. SoftAP+LC3 sets this in the host\n * because BLE audio reaches the phone before SoftAP video.\n */\n audioDelayMs?: number\n /** Dump WHEP PCM to a WAV in cache for P4 verification. */\n dumpPcmWav?: boolean\n /** When omitted, native keeps 1280×720@15 / 2.5 Mbps. */\n video?: AcsOutgoingVideo\n}\n\nexport type AcsIncomingPcmEvent = {\n base64: string\n sampleRate: number\n channels: number\n}\n\nexport type AcsMeetingModuleEvents = {\n onState: (state: AcsMeetingState) => void\n onIncomingPcm: (pcm: AcsIncomingPcmEvent) => void\n}\n"]}
|
|
1
|
+
{"version":3,"file":"AcsMeeting.types.js","sourceRoot":"","sources":["../src/AcsMeeting.types.ts"],"names":[],"mappings":"","sourcesContent":["export type MeetingPhase = \"idle\" | \"connecting\" | \"lobby\" | \"connected\" | \"disconnected\" | \"error\"\n\nexport type AcsAudioSource = \"glasses\" | \"phone\"\nexport type AcsActiveStream = \"none\" | \"virtual\" | \"local\"\nexport type AcsAudioSafety = \"safe\" | \"degraded\" | \"unsafe\"\n/**\n * Health of the glasses WHEP subscription feeding the call. `failed` = ICE dropped or\n * the WHEP endpoint went away while the ACS call may still be `connected`. Native\n * rebuilds it on its own with backoff; the host can force one via `restartVideoSource`.\n */\nexport type AcsMediaSourceState = \"idle\" | \"connecting\" | \"live\" | \"failed\"\n\nexport type AcsMeetingParticipantState = \"idle\" | \"connecting\" | \"connected\" | \"lobby\" | \"hold\" | \"disconnected\"\n\nexport type AcsMeetingParticipant = {\n id: string\n displayName: string | null\n state: AcsMeetingParticipantState\n isMuted: boolean\n isSpeaking: boolean\n}\n\nexport type AcsMeetingState = {\n state: MeetingPhase\n muted: boolean\n error?: string\n meetingUrl?: string\n provider?: \"acs-teams\"\n audioSource?: AcsAudioSource\n activeStream?: AcsActiveStream\n audioSafety?: AcsAudioSafety\n mediaSource?: AcsMediaSourceState\n /** Native receiver diagnostic, including the reason a local WHIP offer was rejected. */\n mediaSourceReason?: string\n /** ACS disconnect diagnostics, emitted when the final disconnected state arrives. */\n endReason_code?: number\n endReason_subcode?: number\n /** Local WHIP endpoint, available after a SoftAP join. */\n ingestUrl?: string\n /** Remote roster (Android emits this; iOS does not yet). */\n participants?: AcsMeetingParticipant[]\n}\n\nexport type AcsOutgoingVideo = {\n width: number\n height: number\n fps: number\n maxBitrateBps: number\n}\n\nexport type AcsMeetingJoinOptions = {\n meetingUrl: string\n token: string\n whepUrl?: string\n videoSource?: {type: \"whep\"; url: string} | {type: \"softap\"; bindAddress: string}\n displayName?: string\n /** \"glasses\" sends WHEP PCM. \"phone\" uses the ACS local mic (handset or BT). */\n audioSource?: AcsAudioSource\n /**\n * Hold outgoing PCM this many ms before ACS. SoftAP+LC3 sets this in the host\n * because BLE audio reaches the phone before SoftAP video.\n */\n audioDelayMs?: number\n /** Dump WHEP PCM to a WAV in cache for P4 verification. */\n dumpPcmWav?: boolean\n /** When omitted, native keeps 1280×720@15 / 2.5 Mbps. */\n video?: AcsOutgoingVideo\n}\n\nexport type AcsIncomingPcmEvent = {\n base64: string\n sampleRate: number\n channels: number\n}\n\nexport type AcsMeetingModuleEvents = {\n onState: (state: AcsMeetingState) => void\n onIncomingPcm: (pcm: AcsIncomingPcmEvent) => void\n}\n"]}
|
|
@@ -24,8 +24,16 @@ declare class AcsMeetingNativeModule extends NativeModule<AcsMeetingModuleEvents
|
|
|
24
24
|
restartVideoSource(): Promise<void>;
|
|
25
25
|
/** SoftAP: join the glasses hotspot; resolves to the phone's IPv4 on it. */
|
|
26
26
|
joinScopedNetwork(ssid: string, passphrase: string): Promise<string>;
|
|
27
|
+
/** iOS: verify DHCP against the gateway advertised by the glasses before resolving. */
|
|
28
|
+
joinScopedNetworkWithGateway?(ssid: string, passphrase: string, gateway: string): Promise<string>;
|
|
27
29
|
beginTrace(traceId: string): Promise<void>;
|
|
28
30
|
leaveScopedNetwork(): Promise<void>;
|
|
31
|
+
cancelScopedNetworkJoin?(): Promise<void>;
|
|
32
|
+
awaitDefaultNetworkAfterHotspot?(): Promise<{
|
|
33
|
+
usable: boolean;
|
|
34
|
+
detail: string;
|
|
35
|
+
transport: string;
|
|
36
|
+
}>;
|
|
29
37
|
/** SoftAP: TCP-probe the hotspot gateway over the scoped network. */
|
|
30
38
|
probeScopedGateway(): Promise<{
|
|
31
39
|
reachable: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AcsMeetingModule.d.ts","sourceRoot":"","sources":["../src/AcsMeetingModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,YAAY,EAAsB,MAAM,MAAM,CAAA;AAEtD,OAAO,KAAK,EAAC,qBAAqB,EAAE,sBAAsB,EAAE,eAAe,EAAC,MAAM,oBAAoB,CAAA;AAEtG,OAAO,OAAO,sBAAuB,SAAQ,YAAY,CAAC,sBAAsB,CAAC;IAC/E,IAAI,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,eAAe,CAAC;IAC9D,iFAAiF;IACjF,YAAY,CAAC,OAAO,EAAE;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAC,GAAG,OAAO,CAAC,eAAe,CAAC;IACtF,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IACtB;;;OAGG;IACH,aAAa,CAAC,OAAO,EAAE;QAAC,SAAS,EAAE,MAAM,CAAA;KAAC,GAAG,OAAO,CAAC;QAAC,SAAS,EAAE,OAAO,CAAA;KAAC,CAAC;IAC1E,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,eAAe,CAAC;IAClD,cAAc,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,GAAG,OAAO,CAAC,eAAe,CAAC;IACrE,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IACjD,wEAAwE;IACxE,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC;IACnC,4EAA4E;IAC5E,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IACpE,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAC1C,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC;IACnC,qEAAqE;IACrE,kBAAkB,IAAI,OAAO,CAAC;QAAC,SAAS,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAC,CAAC;IACnE,QAAQ,IAAI,OAAO,CAAC,eAAe,CAAC;CACrC;;AAED,wBAA8E"}
|
|
1
|
+
{"version":3,"file":"AcsMeetingModule.d.ts","sourceRoot":"","sources":["../src/AcsMeetingModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,YAAY,EAAsB,MAAM,MAAM,CAAA;AAEtD,OAAO,KAAK,EAAC,qBAAqB,EAAE,sBAAsB,EAAE,eAAe,EAAC,MAAM,oBAAoB,CAAA;AAEtG,OAAO,OAAO,sBAAuB,SAAQ,YAAY,CAAC,sBAAsB,CAAC;IAC/E,IAAI,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,eAAe,CAAC;IAC9D,iFAAiF;IACjF,YAAY,CAAC,OAAO,EAAE;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAC,GAAG,OAAO,CAAC,eAAe,CAAC;IACtF,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IACtB;;;OAGG;IACH,aAAa,CAAC,OAAO,EAAE;QAAC,SAAS,EAAE,MAAM,CAAA;KAAC,GAAG,OAAO,CAAC;QAAC,SAAS,EAAE,OAAO,CAAA;KAAC,CAAC;IAC1E,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,eAAe,CAAC;IAClD,cAAc,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,GAAG,OAAO,CAAC,eAAe,CAAC;IACrE,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IACjD,wEAAwE;IACxE,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC;IACnC,4EAA4E;IAC5E,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IACpE,uFAAuF;IACvF,4BAA4B,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IACjG,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAC1C,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC;IACnC,uBAAuB,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC;IACzC,+BAA+B,CAAC,IAAI,OAAO,CAAC;QAAC,MAAM,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAC,CAAC;IACjG,qEAAqE;IACrE,kBAAkB,IAAI,OAAO,CAAC;QAAC,SAAS,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAC,CAAC;IACnE,QAAQ,IAAI,OAAO,CAAC,eAAe,CAAC;CACrC;;AAED,wBAA8E"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AcsMeetingModule.js","sourceRoot":"","sources":["../src/AcsMeetingModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,mBAAmB,EAAC,MAAM,MAAM,CAAA;
|
|
1
|
+
{"version":3,"file":"AcsMeetingModule.js","sourceRoot":"","sources":["../src/AcsMeetingModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,mBAAmB,EAAC,MAAM,MAAM,CAAA;AAgCtD,eAAe,mBAAmB,CAAyB,kBAAkB,CAAC,CAAA","sourcesContent":["import {NativeModule, requireNativeModule} from \"expo\"\n\nimport type {AcsMeetingJoinOptions, AcsMeetingModuleEvents, AcsMeetingState} from \"./AcsMeeting.types\"\n\ndeclare class AcsMeetingNativeModule extends NativeModule<AcsMeetingModuleEvents> {\n join(options: AcsMeetingJoinOptions): Promise<AcsMeetingState>\n /** Sign in to ACS before SoftAP so Teams is not resolved through glasses DNS. */\n prepareAgent(options: {token: string; displayName?: string}): Promise<AcsMeetingState>\n leave(): Promise<void>\n /**\n * Leave, and resolve only once the hang-up, the agent disposal, and the network releases have\n * finished. Use this explicit barrier across platforms; Android `leave()` only queues cleanup.\n */\n leaveAndAwait(options: {timeoutMs: number}): Promise<{completed: boolean}>\n setMuted(muted: boolean): Promise<AcsMeetingState>\n setAudioSource(source: \"glasses\" | \"phone\"): Promise<AcsMeetingState>\n updateVideoSource(whepUrl: string): Promise<void>\n /** Force a WHEP rebuild on the current URL (phone changed networks). */\n restartVideoSource(): Promise<void>\n /** SoftAP: join the glasses hotspot; resolves to the phone's IPv4 on it. */\n joinScopedNetwork(ssid: string, passphrase: string): Promise<string>\n /** iOS: verify DHCP against the gateway advertised by the glasses before resolving. */\n joinScopedNetworkWithGateway?(ssid: string, passphrase: string, gateway: string): Promise<string>\n beginTrace(traceId: string): Promise<void>\n leaveScopedNetwork(): Promise<void>\n cancelScopedNetworkJoin?(): Promise<void>\n awaitDefaultNetworkAfterHotspot?(): Promise<{usable: boolean; detail: string; transport: string}>\n /** SoftAP: TCP-probe the hotspot gateway over the scoped network. */\n probeScopedGateway(): Promise<{reachable: boolean; detail: string}>\n getState(): Promise<AcsMeetingState>\n}\n\nexport default requireNativeModule<AcsMeetingNativeModule>(\"MentraAcsMeeting\")\n"]}
|
|
@@ -21,6 +21,18 @@ public class AcsMeetingModule: Module {
|
|
|
21
21
|
return session
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
private func joinHotspot(ssid: String, passphrase: String, gateway: String?, promise: Promise) {
|
|
25
|
+
hotspot.onLost = { [weak self] reason in
|
|
26
|
+
self?.sendEvent("onScopedNetworkLost", ["code": "SOFTAP_LOST", "message": reason])
|
|
27
|
+
}
|
|
28
|
+
hotspot.join(ssid: ssid, passphrase: passphrase, gateway: gateway) { result in
|
|
29
|
+
switch result {
|
|
30
|
+
case let .success(address): promise.resolve(address)
|
|
31
|
+
case let .failure(error): promise.reject(error)
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
24
36
|
public func definition() -> ModuleDefinition {
|
|
25
37
|
Name("MentraAcsMeeting")
|
|
26
38
|
Events("onState", "onIncomingPcm", "onScopedNetworkLost")
|
|
@@ -57,25 +69,30 @@ public class AcsMeetingModule: Module {
|
|
|
57
69
|
}
|
|
58
70
|
|
|
59
71
|
AsyncFunction("joinScopedNetwork") { (ssid: String, passphrase: String, promise: Promise) in
|
|
60
|
-
self.
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
case let .success(address): promise.resolve(address)
|
|
66
|
-
case let .failure(error): promise.reject(error)
|
|
67
|
-
}
|
|
68
|
-
}
|
|
72
|
+
self.joinHotspot(ssid: ssid, passphrase: passphrase, gateway: nil, promise: promise)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
AsyncFunction("joinScopedNetworkWithGateway") { (ssid: String, passphrase: String, gateway: String, promise: Promise) in
|
|
76
|
+
self.joinHotspot(ssid: ssid, passphrase: passphrase, gateway: gateway, promise: promise)
|
|
69
77
|
}
|
|
70
78
|
|
|
71
79
|
AsyncFunction("leaveScopedNetwork") { (promise: Promise) in
|
|
72
80
|
self.hotspot.leave { promise.resolve(nil) }
|
|
73
81
|
}
|
|
74
82
|
|
|
83
|
+
AsyncFunction("cancelScopedNetworkJoin") { (promise: Promise) in
|
|
84
|
+
self.hotspot.leave { promise.resolve(nil) }
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
AsyncFunction("awaitDefaultNetworkAfterHotspot") { (promise: Promise) in
|
|
88
|
+
self.hotspot.awaitInternet(requireCellular: false) { usable, detail in
|
|
89
|
+
promise.resolve(["usable": usable, "detail": detail, "transport": usable ? detail : "unknown", "validated": usable, "present": usable])
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
75
93
|
AsyncFunction("beginTrace") { (traceId: String) in
|
|
76
94
|
NSLog("SOFTAP_TRACE trace=\(traceId) stage=ios_begin")
|
|
77
95
|
}
|
|
78
|
-
|
|
79
96
|
AsyncFunction("probeScopedGateway") { (promise: Promise) in
|
|
80
97
|
self.hotspot.probeGateway { reachable, detail in
|
|
81
98
|
promise.resolve(["reachable": reachable, "detail": detail])
|
|
@@ -188,6 +205,7 @@ final class AcsMeetingSession {
|
|
|
188
205
|
private var muted = false
|
|
189
206
|
private var meetingUrl: String?
|
|
190
207
|
private var lastError: String?
|
|
208
|
+
private var callEndReason: (code: Int, subcode: Int)?
|
|
191
209
|
private var callClient: CallClient?
|
|
192
210
|
private var callAgent: CallAgent?
|
|
193
211
|
private var call: Call?
|
|
@@ -213,6 +231,7 @@ final class AcsMeetingSession {
|
|
|
213
231
|
// Health of the glasses WHEP feed, reported alongside the ACS phase so the host
|
|
214
232
|
// can tell "call is up, glasses video is dead" from a healthy call.
|
|
215
233
|
private var mediaSource: SourceState = .idle
|
|
234
|
+
private var mediaSourceReason: String?
|
|
216
235
|
private var mediaRestartAttempts = 0
|
|
217
236
|
private var mediaRestartTask: DispatchWorkItem?
|
|
218
237
|
private static let mediaRestartBaseMs = 1000
|
|
@@ -254,8 +273,13 @@ final class AcsMeetingSession {
|
|
|
254
273
|
}
|
|
255
274
|
result["capabilities"] = ["hangUpForEveryone": hangUp]
|
|
256
275
|
if let ingestUrl = media?.ingestUrl { result["ingestUrl"] = ingestUrl }
|
|
276
|
+
if let mediaSourceReason { result["mediaSourceReason"] = mediaSourceReason }
|
|
257
277
|
if let meetingUrl { result["meetingUrl"] = meetingUrl }
|
|
258
278
|
if let lastError { result["error"] = lastError }
|
|
279
|
+
if let callEndReason {
|
|
280
|
+
result["endReason_code"] = callEndReason.code
|
|
281
|
+
result["endReason_subcode"] = callEndReason.subcode
|
|
282
|
+
}
|
|
259
283
|
return result
|
|
260
284
|
}
|
|
261
285
|
|
|
@@ -319,6 +343,7 @@ final class AcsMeetingSession {
|
|
|
319
343
|
self.audioSource = AcsAudioPolicy.parseSource(audioSource) == .phone ? "phone" : "glasses"
|
|
320
344
|
self.meetingUrl = meetingUrl
|
|
321
345
|
self.lastError = nil
|
|
346
|
+
self.callEndReason = nil
|
|
322
347
|
self.emit("connecting")
|
|
323
348
|
let useAgent: (CallAgent) -> Void = { agent in
|
|
324
349
|
do { try self.joinWithAgentLocked(agent, generation: generation, meetingUrl: meetingUrl,
|
|
@@ -508,6 +533,9 @@ final class AcsMeetingSession {
|
|
|
508
533
|
guard self.joinGeneration == generation, self.media === source else { return }
|
|
509
534
|
switch result {
|
|
510
535
|
case .success:
|
|
536
|
+
// The SDK may have changed state before its delegate was attached.
|
|
537
|
+
// Reading it here also makes the join result reflect admission already granted.
|
|
538
|
+
self.refreshCallStateLocked(call)
|
|
511
539
|
let reply = self.pendingJoin
|
|
512
540
|
self.pendingJoin = nil
|
|
513
541
|
reply?(.success(self.snapshot()))
|
|
@@ -559,6 +587,7 @@ final class AcsMeetingSession {
|
|
|
559
587
|
private func onMediaSourceState(_ state: SourceState, reason: String) {
|
|
560
588
|
let previous = mediaSource
|
|
561
589
|
mediaSource = state
|
|
590
|
+
mediaSourceReason = reason
|
|
562
591
|
if state == .live { mediaRestartAttempts = 0 }
|
|
563
592
|
if state == .failed { scheduleMediaRestart(reason: reason) }
|
|
564
593
|
// start() emits idle then connecting back to back; one snapshot per real change.
|
|
@@ -787,6 +816,7 @@ final class AcsMeetingSession {
|
|
|
787
816
|
media?.onFrame = nil
|
|
788
817
|
media?.onPcm = nil
|
|
789
818
|
mediaSource = .idle
|
|
819
|
+
mediaSourceReason = nil
|
|
790
820
|
if let local = media as? LocalWhipIngestSource {
|
|
791
821
|
cleanup.enter()
|
|
792
822
|
local.stop { self.cleanup.leave() }
|
|
@@ -832,6 +862,7 @@ final class AcsMeetingSession {
|
|
|
832
862
|
// carries it and the call delegate keeps ignoring late disconnected callbacks.
|
|
833
863
|
if emitIdle {
|
|
834
864
|
lastError = nil
|
|
865
|
+
callEndReason = nil
|
|
835
866
|
emit("idle")
|
|
836
867
|
}
|
|
837
868
|
}
|
|
@@ -851,15 +882,33 @@ final class AcsMeetingSession {
|
|
|
851
882
|
private func handleCallStateChange(_ changedCall: Call) {
|
|
852
883
|
queue.async {
|
|
853
884
|
guard self.call === changedCall, self.lastError == nil else { return }
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
885
|
+
self.refreshCallStateLocked(changedCall)
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
private func refreshCallStateLocked(_ changedCall: Call) {
|
|
890
|
+
switch changedCall.state {
|
|
891
|
+
case .connecting: emit("connecting")
|
|
892
|
+
case .inLobby: emit("lobby")
|
|
893
|
+
case .connected:
|
|
894
|
+
emit("connected")
|
|
895
|
+
applyAudioPolicyOnQueue("call-connected")
|
|
896
|
+
case .disconnected:
|
|
897
|
+
let reason = changedCall.callEndReason
|
|
898
|
+
callEndReason = (Int(reason.code), Int(reason.subcode))
|
|
899
|
+
if phase == "connecting" || phase == "lobby" {
|
|
900
|
+
let error = AcsMeetingError("The Teams call ended before admission (ACS \(reason.code)/\(reason.subcode))")
|
|
901
|
+
if pendingJoin != nil {
|
|
902
|
+
failJoinLocked(error, generation: joinGeneration)
|
|
903
|
+
return
|
|
904
|
+
}
|
|
905
|
+
lastError = error.localizedDescription
|
|
862
906
|
}
|
|
907
|
+
emit("disconnected")
|
|
908
|
+
// The final reason is only available at disconnected. Reporting disconnecting as
|
|
909
|
+
// terminal makes the host dispose the call before that diagnostic can arrive.
|
|
910
|
+
case .disconnecting: break
|
|
911
|
+
default: break
|
|
863
912
|
}
|
|
864
913
|
}
|
|
865
914
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mentra/acs-meeting",
|
|
3
|
-
"version": "3.2.0-dev.
|
|
3
|
+
"version": "3.2.0-dev.232",
|
|
4
4
|
"description": "MentraOS native ACS Teams meeting module (WHEP decode → ACS raw media)",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"react-native": "src/index.ts",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"@expo/config-plugins": "~55.0.10",
|
|
35
|
-
"@mentra/glasses-media": "3.2.0-dev.
|
|
35
|
+
"@mentra/glasses-media": "3.2.0-dev.232"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"expo-module-scripts": "^55.0.2",
|
package/src/AcsMeeting.types.ts
CHANGED
|
@@ -30,6 +30,11 @@ export type AcsMeetingState = {
|
|
|
30
30
|
activeStream?: AcsActiveStream
|
|
31
31
|
audioSafety?: AcsAudioSafety
|
|
32
32
|
mediaSource?: AcsMediaSourceState
|
|
33
|
+
/** Native receiver diagnostic, including the reason a local WHIP offer was rejected. */
|
|
34
|
+
mediaSourceReason?: string
|
|
35
|
+
/** ACS disconnect diagnostics, emitted when the final disconnected state arrives. */
|
|
36
|
+
endReason_code?: number
|
|
37
|
+
endReason_subcode?: number
|
|
33
38
|
/** Local WHIP endpoint, available after a SoftAP join. */
|
|
34
39
|
ingestUrl?: string
|
|
35
40
|
/** Remote roster (Android emits this; iOS does not yet). */
|
package/src/AcsMeetingModule.ts
CHANGED
|
@@ -19,8 +19,12 @@ declare class AcsMeetingNativeModule extends NativeModule<AcsMeetingModuleEvents
|
|
|
19
19
|
restartVideoSource(): Promise<void>
|
|
20
20
|
/** SoftAP: join the glasses hotspot; resolves to the phone's IPv4 on it. */
|
|
21
21
|
joinScopedNetwork(ssid: string, passphrase: string): Promise<string>
|
|
22
|
+
/** iOS: verify DHCP against the gateway advertised by the glasses before resolving. */
|
|
23
|
+
joinScopedNetworkWithGateway?(ssid: string, passphrase: string, gateway: string): Promise<string>
|
|
22
24
|
beginTrace(traceId: string): Promise<void>
|
|
23
25
|
leaveScopedNetwork(): Promise<void>
|
|
26
|
+
cancelScopedNetworkJoin?(): Promise<void>
|
|
27
|
+
awaitDefaultNetworkAfterHotspot?(): Promise<{usable: boolean; detail: string; transport: string}>
|
|
24
28
|
/** SoftAP: TCP-probe the hotspot gateway over the scoped network. */
|
|
25
29
|
probeScopedGateway(): Promise<{reachable: boolean; detail: string}>
|
|
26
30
|
getState(): Promise<AcsMeetingState>
|