im-rtc-call-engine 1.0.0

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.
Files changed (218) hide show
  1. package/LICENSE +21 -0
  2. package/dist/bytes.d.ts +11 -0
  3. package/dist/bytes.d.ts.map +1 -0
  4. package/dist/bytes.js +20 -0
  5. package/dist/bytes.js.map +1 -0
  6. package/dist/callOptions.d.ts +27 -0
  7. package/dist/callOptions.d.ts.map +1 -0
  8. package/dist/callOptions.js +19 -0
  9. package/dist/callOptions.js.map +1 -0
  10. package/dist/deviceId.d.ts +10 -0
  11. package/dist/deviceId.d.ts.map +1 -0
  12. package/dist/deviceId.js +55 -0
  13. package/dist/deviceId.js.map +1 -0
  14. package/dist/engine.d.ts +302 -0
  15. package/dist/engine.d.ts.map +1 -0
  16. package/dist/engine.js +489 -0
  17. package/dist/engine.js.map +1 -0
  18. package/dist/engineBus.d.ts +16 -0
  19. package/dist/engineBus.d.ts.map +1 -0
  20. package/dist/engineBus.js +89 -0
  21. package/dist/engineBus.js.map +1 -0
  22. package/dist/engineWiring.d.ts +40 -0
  23. package/dist/engineWiring.d.ts.map +1 -0
  24. package/dist/engineWiring.js +57 -0
  25. package/dist/engineWiring.js.map +1 -0
  26. package/dist/errors.d.ts +185 -0
  27. package/dist/errors.d.ts.map +1 -0
  28. package/dist/errors.js +253 -0
  29. package/dist/errors.js.map +1 -0
  30. package/dist/eventBus.d.ts +24 -0
  31. package/dist/eventBus.d.ts.map +1 -0
  32. package/dist/eventBus.js +50 -0
  33. package/dist/eventBus.js.map +1 -0
  34. package/dist/events.d.ts +220 -0
  35. package/dist/events.d.ts.map +1 -0
  36. package/dist/events.js +35 -0
  37. package/dist/events.js.map +1 -0
  38. package/dist/frameLoop.d.ts +93 -0
  39. package/dist/frameLoop.d.ts.map +1 -0
  40. package/dist/frameLoop.js +346 -0
  41. package/dist/frameLoop.js.map +1 -0
  42. package/dist/index.d.ts +60 -0
  43. package/dist/index.d.ts.map +1 -0
  44. package/dist/index.js +42 -0
  45. package/dist/index.js.map +1 -0
  46. package/dist/logger.d.ts +64 -0
  47. package/dist/logger.d.ts.map +1 -0
  48. package/dist/logger.js +101 -0
  49. package/dist/logger.js.map +1 -0
  50. package/dist/media/engineMediaApi.d.ts +40 -0
  51. package/dist/media/engineMediaApi.d.ts.map +1 -0
  52. package/dist/media/engineMediaApi.js +71 -0
  53. package/dist/media/engineMediaApi.js.map +1 -0
  54. package/dist/media/firstFrameGate.d.ts +47 -0
  55. package/dist/media/firstFrameGate.d.ts.map +1 -0
  56. package/dist/media/firstFrameGate.js +117 -0
  57. package/dist/media/firstFrameGate.js.map +1 -0
  58. package/dist/media/mediaAdapter.d.ts +135 -0
  59. package/dist/media/mediaAdapter.d.ts.map +1 -0
  60. package/dist/media/mediaAdapter.js +2 -0
  61. package/dist/media/mediaAdapter.js.map +1 -0
  62. package/dist/media/mediaBridge.d.ts +99 -0
  63. package/dist/media/mediaBridge.d.ts.map +1 -0
  64. package/dist/media/mediaBridge.js +186 -0
  65. package/dist/media/mediaBridge.js.map +1 -0
  66. package/dist/media/mediaPlane.d.ts +58 -0
  67. package/dist/media/mediaPlane.d.ts.map +1 -0
  68. package/dist/media/mediaPlane.js +119 -0
  69. package/dist/media/mediaPlane.js.map +1 -0
  70. package/dist/media/videoProfile.d.ts +79 -0
  71. package/dist/media/videoProfile.d.ts.map +1 -0
  72. package/dist/media/videoProfile.js +46 -0
  73. package/dist/media/videoProfile.js.map +1 -0
  74. package/dist/media/videoProfile.test.d.ts +2 -0
  75. package/dist/media/videoProfile.test.d.ts.map +1 -0
  76. package/dist/media/videoProfile.test.js +26 -0
  77. package/dist/media/videoProfile.test.js.map +1 -0
  78. package/dist/media/viewRegistry.d.ts +75 -0
  79. package/dist/media/viewRegistry.d.ts.map +1 -0
  80. package/dist/media/viewRegistry.js +130 -0
  81. package/dist/media/viewRegistry.js.map +1 -0
  82. package/dist/media/webrtcAdapter.d.ts +158 -0
  83. package/dist/media/webrtcAdapter.d.ts.map +1 -0
  84. package/dist/media/webrtcAdapter.js +502 -0
  85. package/dist/media/webrtcAdapter.js.map +1 -0
  86. package/dist/protocolId.d.ts +14 -0
  87. package/dist/protocolId.d.ts.map +1 -0
  88. package/dist/protocolId.js +109 -0
  89. package/dist/protocolId.js.map +1 -0
  90. package/dist/reasons.d.ts +63 -0
  91. package/dist/reasons.d.ts.map +1 -0
  92. package/dist/reasons.js +81 -0
  93. package/dist/reasons.js.map +1 -0
  94. package/dist/redact.d.ts +19 -0
  95. package/dist/redact.d.ts.map +1 -0
  96. package/dist/redact.js +50 -0
  97. package/dist/redact.js.map +1 -0
  98. package/dist/signaling/backoff.d.ts +17 -0
  99. package/dist/signaling/backoff.d.ts.map +1 -0
  100. package/dist/signaling/backoff.js +23 -0
  101. package/dist/signaling/backoff.js.map +1 -0
  102. package/dist/signaling/candidate.d.ts +19 -0
  103. package/dist/signaling/candidate.d.ts.map +1 -0
  104. package/dist/signaling/candidate.js +22 -0
  105. package/dist/signaling/candidate.js.map +1 -0
  106. package/dist/signaling/caseMapping.d.ts +19 -0
  107. package/dist/signaling/caseMapping.d.ts.map +1 -0
  108. package/dist/signaling/caseMapping.js +34 -0
  109. package/dist/signaling/caseMapping.js.map +1 -0
  110. package/dist/signaling/connection.d.ts +110 -0
  111. package/dist/signaling/connection.d.ts.map +1 -0
  112. package/dist/signaling/connection.js +365 -0
  113. package/dist/signaling/connection.js.map +1 -0
  114. package/dist/signaling/connectionFactory.d.ts +55 -0
  115. package/dist/signaling/connectionFactory.d.ts.map +1 -0
  116. package/dist/signaling/connectionFactory.js +29 -0
  117. package/dist/signaling/connectionFactory.js.map +1 -0
  118. package/dist/signaling/connectionTypes.d.ts +94 -0
  119. package/dist/signaling/connectionTypes.d.ts.map +1 -0
  120. package/dist/signaling/connectionTypes.js +2 -0
  121. package/dist/signaling/connectionTypes.js.map +1 -0
  122. package/dist/signaling/discipline.d.ts +6 -0
  123. package/dist/signaling/discipline.d.ts.map +1 -0
  124. package/dist/signaling/discipline.js +100 -0
  125. package/dist/signaling/discipline.js.map +1 -0
  126. package/dist/signaling/enums.d.ts +53 -0
  127. package/dist/signaling/enums.d.ts.map +1 -0
  128. package/dist/signaling/enums.js +52 -0
  129. package/dist/signaling/enums.js.map +1 -0
  130. package/dist/signaling/envelope.d.ts +31 -0
  131. package/dist/signaling/envelope.d.ts.map +1 -0
  132. package/dist/signaling/envelope.js +91 -0
  133. package/dist/signaling/envelope.js.map +1 -0
  134. package/dist/signaling/fieldSpec.d.ts +101 -0
  135. package/dist/signaling/fieldSpec.d.ts.map +1 -0
  136. package/dist/signaling/fieldSpec.js +130 -0
  137. package/dist/signaling/fieldSpec.js.map +1 -0
  138. package/dist/signaling/frameSender.d.ts +34 -0
  139. package/dist/signaling/frameSender.d.ts.map +1 -0
  140. package/dist/signaling/frameSender.js +57 -0
  141. package/dist/signaling/frameSender.js.map +1 -0
  142. package/dist/signaling/frames.call.d.ts +299 -0
  143. package/dist/signaling/frames.call.d.ts.map +1 -0
  144. package/dist/signaling/frames.call.js +149 -0
  145. package/dist/signaling/frames.call.js.map +1 -0
  146. package/dist/signaling/frames.room.d.ts +566 -0
  147. package/dist/signaling/frames.room.d.ts.map +1 -0
  148. package/dist/signaling/frames.room.js +165 -0
  149. package/dist/signaling/frames.room.js.map +1 -0
  150. package/dist/signaling/frames.sys.d.ts +145 -0
  151. package/dist/signaling/frames.sys.d.ts.map +1 -0
  152. package/dist/signaling/frames.sys.js +53 -0
  153. package/dist/signaling/frames.sys.js.map +1 -0
  154. package/dist/signaling/handshakeGiveUp.d.ts +36 -0
  155. package/dist/signaling/handshakeGiveUp.d.ts.map +1 -0
  156. package/dist/signaling/handshakeGiveUp.js +48 -0
  157. package/dist/signaling/handshakeGiveUp.js.map +1 -0
  158. package/dist/signaling/heartbeat.d.ts +34 -0
  159. package/dist/signaling/heartbeat.d.ts.map +1 -0
  160. package/dist/signaling/heartbeat.js +52 -0
  161. package/dist/signaling/heartbeat.js.map +1 -0
  162. package/dist/signaling/pendingRequests.d.ts +33 -0
  163. package/dist/signaling/pendingRequests.d.ts.map +1 -0
  164. package/dist/signaling/pendingRequests.js +57 -0
  165. package/dist/signaling/pendingRequests.js.map +1 -0
  166. package/dist/signaling/reconnector.d.ts +43 -0
  167. package/dist/signaling/reconnector.d.ts.map +1 -0
  168. package/dist/signaling/reconnector.js +76 -0
  169. package/dist/signaling/reconnector.js.map +1 -0
  170. package/dist/signaling/registry.d.ts +100 -0
  171. package/dist/signaling/registry.d.ts.map +1 -0
  172. package/dist/signaling/registry.js +217 -0
  173. package/dist/signaling/registry.js.map +1 -0
  174. package/dist/signaling/resumeDeadline.d.ts +48 -0
  175. package/dist/signaling/resumeDeadline.d.ts.map +1 -0
  176. package/dist/signaling/resumeDeadline.js +89 -0
  177. package/dist/signaling/resumeDeadline.js.map +1 -0
  178. package/dist/signaling/tokenExpiry.d.ts +89 -0
  179. package/dist/signaling/tokenExpiry.d.ts.map +1 -0
  180. package/dist/signaling/tokenExpiry.js +106 -0
  181. package/dist/signaling/tokenExpiry.js.map +1 -0
  182. package/dist/signaling/webSocket.d.ts +59 -0
  183. package/dist/signaling/webSocket.d.ts.map +1 -0
  184. package/dist/signaling/webSocket.js +50 -0
  185. package/dist/signaling/webSocket.js.map +1 -0
  186. package/dist/state/callMachine.d.ts +56 -0
  187. package/dist/state/callMachine.d.ts.map +1 -0
  188. package/dist/state/callMachine.js +176 -0
  189. package/dist/state/callMachine.js.map +1 -0
  190. package/dist/state/callRecv.d.ts +12 -0
  191. package/dist/state/callRecv.d.ts.map +1 -0
  192. package/dist/state/callRecv.js +255 -0
  193. package/dist/state/callRecv.js.map +1 -0
  194. package/dist/state/engineMachine.d.ts +41 -0
  195. package/dist/state/engineMachine.d.ts.map +1 -0
  196. package/dist/state/engineMachine.js +210 -0
  197. package/dist/state/engineMachine.js.map +1 -0
  198. package/dist/state/forceEnd.d.ts +44 -0
  199. package/dist/state/forceEnd.d.ts.map +1 -0
  200. package/dist/state/forceEnd.js +96 -0
  201. package/dist/state/forceEnd.js.map +1 -0
  202. package/dist/state/roomMachine.d.ts +92 -0
  203. package/dist/state/roomMachine.d.ts.map +1 -0
  204. package/dist/state/roomMachine.js +319 -0
  205. package/dist/state/roomMachine.js.map +1 -0
  206. package/dist/state/roomRecv.d.ts +7 -0
  207. package/dist/state/roomRecv.d.ts.map +1 -0
  208. package/dist/state/roomRecv.js +209 -0
  209. package/dist/state/roomRecv.js.map +1 -0
  210. package/dist/state/types.d.ts +61 -0
  211. package/dist/state/types.d.ts.map +1 -0
  212. package/dist/state/types.js +30 -0
  213. package/dist/state/types.js.map +1 -0
  214. package/dist/version.d.ts +11 -0
  215. package/dist/version.d.ts.map +1 -0
  216. package/dist/version.js +11 -0
  217. package/dist/version.js.map +1 -0
  218. package/package.json +35 -0
package/dist/engine.js ADDED
@@ -0,0 +1,489 @@
1
+ import { byteLength } from './bytes.js';
2
+ import { violatesCallOptionLimits } from './callOptions.js';
3
+ import { EngineBus } from './engineBus.js';
4
+ import { engineConnectionHandlers, engineMediaDeps } from './engineWiring.js';
5
+ import { ErrorCode, RtcError } from './errors.js';
6
+ import { FrameLoop } from './frameLoop.js';
7
+ import { logger } from './logger.js';
8
+ import { checkDeviceId, checkRoomId } from './protocolId.js';
9
+ import { CallEndReason } from './reasons.js';
10
+ import { probeCamera, probeMicrophone, publishCamera, publishMicrophone, setMuted, setRemoteLayer, } from './media/engineMediaApi.js';
11
+ import { MediaBridge } from './media/mediaBridge.js';
12
+ import { mediaEvents } from './media/mediaPlane.js';
13
+ import { WebRTCAdapter } from './media/webrtcAdapter.js';
14
+ import { createConnection } from './signaling/connectionFactory.js';
15
+ import { FrameSender } from './signaling/frameSender.js';
16
+ /** CallEngine 是宿主唯一需要接触的类型。 */
17
+ export class CallEngine {
18
+ bus = new EngineBus();
19
+ bridge;
20
+ media;
21
+ options;
22
+ sender;
23
+ connection = null;
24
+ /** 握手拿到的自己的 uid。用来挡「呼叫自己」,也供宿主读。 */
25
+ myUid = '';
26
+ loop;
27
+ /** 最近一次 hello.ok 喂进状态机的那个 promise,`login()` 要等它。 */
28
+ helloApplied = Promise.resolve();
29
+ /** 终态销毁标记。见 {@link destroy}。 */
30
+ destroyed = false;
31
+ constructor(options) {
32
+ // 与 Android 的 `Config.init` 对齐:构造时就拦,不等宿主取完票走到 login()。
33
+ checkDeviceId(options.deviceId);
34
+ this.options = options;
35
+ this.media = options.media ?? new WebRTCAdapter(undefined, options.videoProfile);
36
+ this.bridge = new MediaBridge(this.media);
37
+ this.sender = new FrameSender(this.media);
38
+ this.loop = new FrameLoop({
39
+ bus: this.bus,
40
+ bridge: this.bridge,
41
+ media: this.media,
42
+ sender: this.sender,
43
+ connection: () => this.connection,
44
+ mediaDeps: () => engineMediaDeps(this.wiring()),
45
+ });
46
+ }
47
+ /** on 订阅事件,返回退订函数。事件表见 events.ts(= 设计文档 §7.5)。 */
48
+ on(name, handler) {
49
+ return this.bus.on(name, handler);
50
+ }
51
+ /** uid 是当前登录的用户。未登录时是空串。 */
52
+ get uid() {
53
+ return this.myUid;
54
+ }
55
+ /** state 返回当前的通话与房间状态,供 UI 渲染。 */
56
+ get state() {
57
+ return this.loop.state;
58
+ }
59
+ /**
60
+ * login 建立信令连接并完成握手。
61
+ *
62
+ * # 重复调用会被拒掉
63
+ *
64
+ * **两条 WS 带着同一个 uid + device_id,服务端按顶号把先来的那条踢下线**,于是宿主收到
65
+ * 一个**假的 `kickedOut{takenOver}`**——「账号在别处登录」,可根本没有别处,就是这台
66
+ * 机器自己把自己踢了。更隐蔽的是旧那条**从不 close**:它的 `ResumeDeadline` 在第一次
67
+ * 断开时就已经武装好(只有 `close()` 撤得掉),约 75 秒后照样触发 `onSessionUnrecoverable`,
68
+ * 把**新会话**的房间清成 idle 并合成一条 `onCallEnd(network)`——用户刚换票重登、
69
+ * 正通着话,画面无故收场。而 `events.ts` 恰恰建议宿主在 `kickedOut{authExpired}` 之后
70
+ * 「取一枚新票再 login」,正好走的就是这条路。
71
+ *
72
+ * 要换账号或换一枚票,先 `logout()`。(连着的时候换票用 {@link updateToken}。)
73
+ * iOS 的 `login(_:)` 早就是这条规矩,本端是没跟上的那个。
74
+ *
75
+ * # 失败会把摊子收干净
76
+ *
77
+ * 握手失败时把连接关掉、`connection` 置回 null。不收的话上面那道门会把**重试**
78
+ * 也一起挡掉,用户从此再也登不上——比原来的毛病还糟。
79
+ */
80
+ async login(token) {
81
+ this.assertNotDestroyed();
82
+ if (this.connection !== null) {
83
+ throw new RtcError(ErrorCode.invalidState, {
84
+ cause: new Error('已经登录了:换账号或换票请先 logout()'),
85
+ });
86
+ }
87
+ const connection = createConnection({
88
+ url: this.options.url,
89
+ token,
90
+ deviceId: this.options.deviceId,
91
+ ...(this.options.webSocketFactory === undefined
92
+ ? {}
93
+ : { webSocketFactory: this.options.webSocketFactory }),
94
+ }, engineConnectionHandlers(this.wiring(), (applied) => {
95
+ this.helloApplied = applied;
96
+ }));
97
+ this.connection = connection;
98
+ this.bridge.open(mediaEvents(engineMediaDeps(this.wiring())));
99
+ let hello;
100
+ try {
101
+ hello = await connection.connect();
102
+ }
103
+ catch (err) {
104
+ // 收摊:不收的话上面那道「已经登录了」的门会把重试也挡掉。
105
+ connection.close();
106
+ this.bridge.close();
107
+ if (this.connection === connection)
108
+ this.connection = null;
109
+ throw err;
110
+ }
111
+ this.myUid = hello.uid;
112
+ // 首次登录要等状态机吃完 hello.ok 再返回:宿主拿到 login 的返回值时,
113
+ // engine 的状态应该已经是最终的了。(重连那些不需要等——没人在 await 它们。)
114
+ await this.helloApplied;
115
+ return hello;
116
+ }
117
+ /**
118
+ * updateToken 换一枚新的接入票。**下一次重连时生效,不打断当前连接。**
119
+ *
120
+ * # 为什么是宿主推给我们,而不是我们去要
121
+ *
122
+ * 协议 §1.5 说 `4401` 的处置是「换新 token 后重连」。**换票是宿主的事**——
123
+ * 票从宿主的账号体系来,engine 不认识那套东西,也不该替它决定什么时候去要。
124
+ * 所以这里是 push 不是 pull:没有「token provider 回调」这种设计。
125
+ *
126
+ * # 宿主该怎么用
127
+ *
128
+ * 在 `disconnected` 里看到 `code === 4401` 就去取一枚新票、调这个方法。
129
+ * 重连是已经排好的(第一档 1 秒起),所以**只要在下一次尝试之前调到就行**;
130
+ * 连续 3 次鉴权失败之后 engine 会抛 `kickedOut` 收手,那时就只能重新 `login` 了。
131
+ *
132
+ * 连上着的时候调它也是安全的(比如票快过期了提前换)——当前连接不受影响。
133
+ */
134
+ updateToken(token, expiresAtMs) {
135
+ this.connection?.updateToken(token, expiresAtMs);
136
+ }
137
+ /** logout 关掉连接与媒体。 */
138
+ logout() {
139
+ this.connection?.close();
140
+ this.connection = null;
141
+ this.bridge.close();
142
+ this.loop.reset();
143
+ }
144
+ /**
145
+ * destroy 终态销毁:`logout()` + 清空全部事件订阅。**不可逆**,给宿主整个放手音视频能力时用
146
+ * (账号注销、SDK 卸载)。**可重复调用**——已经销毁过再调什么都不做,不会重复 logout。
147
+ *
148
+ * # 之后再调别的方法
149
+ *
150
+ * 会发起动作的方法(`login` / `call` / `accept` / … / `publishMicrophone` / `openCamera` 等,
151
+ * 经 {@link act} 或 {@link mediaApi} 转发的那一批)一律**抛 `2005 invalid_state`**,
152
+ * 不是静默空操作:事件订阅已经清空,静默的话宿主的 `hangup()` 之类调用会石沉大海——
153
+ * 不抛错也不会有任何事件把原因告诉它,界面只会永远停在转圈。与 `login()` 拦
154
+ * 「已经登录了」同一个理由:让宿主一调就知道错在哪,不用猜。
155
+ *
156
+ * `logout()` / `forceEnd()` / `on()` / `uid` / `state`,以及读或清理类的方法
157
+ * (`attachView` 传 `null`、`attachLocalView` 传 `null`、`localTrack`、`stopLocalPreview`、
158
+ * `updateToken`)**不受影响**,销毁后调用仍然安全——宿主卸载时经常无脑清理这几个,
159
+ * 不该因为清理顺序先后而报错。
160
+ */
161
+ destroy() {
162
+ if (this.destroyed)
163
+ return;
164
+ this.logout();
165
+ this.bus.clear();
166
+ this.destroyed = true;
167
+ }
168
+ /**
169
+ * assertNotDestroyed 挡住 {@link destroy} 之后的调用。见 destroy 的文档注释。
170
+ */
171
+ assertNotDestroyed() {
172
+ if (this.destroyed) {
173
+ throw new RtcError(ErrorCode.invalidState, { cause: new Error('engine 已销毁(destroy 之后不可再用)') });
174
+ }
175
+ }
176
+ /**
177
+ * act 是「发一个业务动作、等状态机处理完」的公共外壳:`call` / `accept` / `reject` / `cancel` /
178
+ * `hangup` / `inviteMore` / `joinCall` / `joinRoom` / `leaveRoom` 共用,唯一的区别只是
179
+ * op 名与参数。抽出来顺带把 {@link assertNotDestroyed} 的检查收在一处。
180
+ */
181
+ async act(op, args) {
182
+ this.assertNotDestroyed();
183
+ await this.loop.dispatch(args === undefined ? { kind: 'act', op } : { kind: 'act', op, args });
184
+ }
185
+ /**
186
+ * call 发起通话。**名单里不能有自己**,见 {@link rejectsSelf}。
187
+ *
188
+ * `options` 传布尔值等同旧的 `isGroup` 参数(三参数签名保持兼容);传 {@link CallOptions}
189
+ * 可以带上群号 / user_data / 振铃超时(HOST_INTEGRATION_DESIGN §3.3)。
190
+ */
191
+ async call(calleeIds, mediaType, options) {
192
+ this.assertNotDestroyed();
193
+ const opts = typeof options === 'boolean' ? { isGroup: options } : (options ?? {});
194
+ const isGroup = opts.isGroup ?? false;
195
+ const chatGroupId = opts.chatGroupId ?? '';
196
+ const userData = opts.userData ?? '';
197
+ if (this.rejectsSelf(calleeIds, '呼叫') || this.rejectsBadCallOptions(chatGroupId, userData)) {
198
+ /*
199
+ **本地拒掉也要给界面一个出口。**
200
+
201
+ 调用方(uikit / 宿主)在调 `call()` 之前就已经切到「正在呼叫…」了——
202
+ 这是对的,不然按下去几百毫秒没反应。但只抛一个 error 事件,
203
+ 界面不知道该退回哪儿:实测三人测试里 carol 卡在「正在呼叫…」,
204
+ 连点五次挂断收到五个 2005(状态机是 idle,没有 call 可挂),
205
+ 除了刷新页面没有别的出路。
206
+
207
+ `callEnd` 是所有结束分支的唯一出口(设计 §7.5),
208
+ 这一条与「服务端拒了 invite」(call_failed)走同一个出口,界面只认它。
209
+ chatGroupId / userData 超限走同一个出口——不上线路,理由同上。
210
+ */
211
+ this.bus.emit('callEnd', {
212
+ callId: '',
213
+ reason: CallEndReason.error,
214
+ durationSec: 0,
215
+ endedBy: '',
216
+ });
217
+ return;
218
+ }
219
+ const args = { callee_ids: calleeIds, media_type: mediaType, is_group: isGroup };
220
+ // 省略表达「没传」,见 callMachine.ts startCall 的同一条注释。
221
+ if (chatGroupId !== '')
222
+ args['chat_group_id'] = chatGroupId;
223
+ if (userData !== '')
224
+ args['user_data'] = userData;
225
+ if (opts.timeoutSec !== undefined)
226
+ args['timeout_sec'] = opts.timeoutSec;
227
+ await this.act('call', args);
228
+ }
229
+ /**
230
+ * joinCall 是「群成员看到『进行中』主动加入」(协议 §4.1 `call.join`)。
231
+ *
232
+ * **「怎么知道有通话在进行中」不是 engine 的事**——宿主拿 webhook `call.started`
233
+ * 或后台 `GET /v1/calls?chat_group_id=...&active=1` 自己判断、自己摆横幅。
234
+ * 服务端拒绝(不存在 / 已结束 / 满员 / 本人已在通话中 / 宿主邀请鉴权回调拒绝 1409)
235
+ * 时与 `call()` 被拒同一个出口:`onError` + `onCallEnd(error)`。
236
+ */
237
+ async joinCall(callId) {
238
+ await this.act('join_call', { call_id: callId });
239
+ }
240
+ /** accept 接听。 */
241
+ async accept() {
242
+ await this.act('accept');
243
+ }
244
+ /** reject 拒接。 */
245
+ async reject() {
246
+ await this.act('reject');
247
+ }
248
+ /** cancel 取消呼出(**仅接通前**;接通后用 hangup)。 */
249
+ async cancel() {
250
+ await this.act('cancel');
251
+ }
252
+ /** hangup 挂断(接通后,主被叫都用它)。 */
253
+ async hangup() {
254
+ await this.act('hangup');
255
+ }
256
+ /**
257
+ * forceEnd 强制结束当前这一场:**结束帧立刻上线路,本地立刻收场,不等服务端**(设计文档 §7.5,五端同名)。
258
+ *
259
+ * 给「红键按下去、等不到结束事件」用——uikit 的红键看门狗 3 秒到点就调它。宿主自画 UI 时同理:
260
+ * `hangup()` 发出去几秒没收到 `callEnd`,就调这个。同步、不抛。
261
+ *
262
+ * 与 `hangup()` 的区别:`hangup()` 只发帧、等服务端的 `call.ended` 推进状态(§5.1),
263
+ * 帧没发出去或被拒了,这一场就收不掉(2026-09-13 iOS frank:界面收了,人还挂在房里四分钟)。
264
+ * `forceEnd()` 不等:按此刻状态挑结束帧(通话中 hangup、响铃中 reject、拨出中 cancel、
265
+ * 会议里 room.leave)**直接交给信令连接**,再本地收场并抛 `callEnd` / `roomLeft`;
266
+ * 服务端随后的 `call.ended` 会因为本地已是 idle 被静默丢弃,不会抛第二次。
267
+ *
268
+ * 收场之后才到的东西也兜住了:迟到的 `room.join.ok` 补发 `room.leave`(服务端只验房票、
269
+ * 不查通话成员),迟到的 `call.invite.ok` 补发 `call.cancel`、`call.connected` 补发 `call.hangup`,
270
+ * 迟到的候选与 SDP 不再交给媒体层。
271
+ *
272
+ * **已知限制**:没登录 / 连接断着时帧发不出去,只做本地收场;服务端那边由恢复窗口到期兜底。
273
+ */
274
+ forceEnd() {
275
+ this.loop.forceEnd();
276
+ }
277
+ /**
278
+ * inviteMore 往进行中的群通话里再拉人(协议 §4.1 `call.invite_more`)。
279
+ * 名单里同样不能有自己,见 {@link rejectsSelf}。
280
+ *
281
+ * **通话里的任何人都能发**(2026-09-15 起,原先仅主叫);还在响铃 / 已离场的人发会被服务端拒成
282
+ * `1407 not_call_owner`(交互稿 §05)。房间满了回 `1202 room_full`;离场的发起人也能被重新邀请。
283
+ */
284
+ async inviteMore(calleeIds) {
285
+ this.assertNotDestroyed();
286
+ if (this.rejectsSelf(calleeIds, '加人'))
287
+ return;
288
+ await this.act('invite_more', { callee_ids: calleeIds });
289
+ }
290
+ /**
291
+ * probeMicrophone 在拨出 / 接听**之前**探一下麦克风权限(交互稿 §01)。
292
+ *
293
+ * 拿到就放掉,不占设备;被拒抛 `2001`、没设备抛 `2002`。
294
+ */
295
+ async probeMicrophone() {
296
+ await probeMicrophone(this.mediaApi());
297
+ }
298
+ /**
299
+ * probeCamera 只探摄像头权限,**不起预览**。契约同 `probeMicrophone`。
300
+ *
301
+ * 摄像头默认关着的场合(群通话、来电页上关掉了摄像头)也可能要先问权限,
302
+ * 拿预览去探会把摄像头真的打开。要看见自己另调 `startLocalPreview`。
303
+ */
304
+ async probeCamera() {
305
+ await probeCamera(this.mediaApi());
306
+ }
307
+ /** joinRoom 直接进一个会议房(不走振铃)。 */
308
+ async joinRoom(roomId, roomToken, autoSubscribe = true) {
309
+ this.assertNotDestroyed();
310
+ // 宿主指定的房间号同属 §2.5,不拦的话又是一条「1004 但不说为什么」。
311
+ checkRoomId(roomId);
312
+ await this.act('join', { room_id: roomId, room_token: roomToken, auto_subscribe: autoSubscribe });
313
+ }
314
+ /** leaveRoom 离房。 */
315
+ async leaveRoom() {
316
+ await this.act('leave');
317
+ }
318
+ /** publishMicrophone 发布麦克风,返回轨道的 cid。 */
319
+ async publishMicrophone() {
320
+ return publishMicrophone(this.mediaApi());
321
+ }
322
+ /**
323
+ * openMicrophone 是麦克风开关的**按类型**便捷接口(与腾讯 TUICallEngine 同名)。
324
+ *
325
+ * 这条轨道还没发布过就发布(等价 `publishMicrophone()`);已经发布了就取消静音,
326
+ * **不会重新发布**——重复发布同一路麦克风会在 pub PC 上多挂一条 sender。
327
+ *
328
+ * 「发没发布过」问的是**媒体适配器自己的账**(`media.publishedMicrophoneCid()`),
329
+ * 不在门面另开一份:宿主先直接调 `publishMicrophone()` 发布过、再调这个方法的话,
330
+ * 门面自己那份账不知道已经发布过,会误判成「没发布」再发一次(2026-09-15 iOS 踩过)。
331
+ * `publishMicrophone()` / `setMuted(cid)` 仍然保留,给需要自己管 cid 的宿主用。
332
+ */
333
+ async openMicrophone() {
334
+ const cid = this.media.publishedMicrophoneCid();
335
+ if (cid !== null) {
336
+ await this.setMuted(cid, false);
337
+ return;
338
+ }
339
+ await this.publishMicrophone();
340
+ }
341
+ /**
342
+ * closeMicrophone 关麦克风:对已发布的那条轨道 `setMuted(cid, true)`——**不 unpublish**,
343
+ * 协商保留。没发布过是空操作。
344
+ */
345
+ async closeMicrophone() {
346
+ const cid = this.media.publishedMicrophoneCid();
347
+ if (cid !== null)
348
+ await this.setMuted(cid, true);
349
+ }
350
+ /**
351
+ * startLocalPreview 只起本端采集,**不发布**(设计文档 §7.5,四端同名)。
352
+ *
353
+ * 拨出中还没有房间,推流无从谈起,但界面这时就该让人看见自己(草图 §03-E)。
354
+ * 随后的 `publishCamera` 复用这条轨道,不会把摄像头开第二次。
355
+ *
356
+ * 返回轨道的 cid,宿主拿它调 `attachLocalView`。
357
+ */
358
+ async startLocalPreview() {
359
+ this.assertNotDestroyed();
360
+ const info = await this.media.startLocalPreview();
361
+ return info.cid;
362
+ }
363
+ /**
364
+ * stopLocalPreview 进房前关摄像头:**真的停采集**,指示灯灭(设计文档 §7.5)。
365
+ *
366
+ * 只停还没发布的预览;已发布的摄像头走 `setMuted`。再打开时重新 `startLocalPreview`,cid 会变。
367
+ */
368
+ async stopLocalPreview() {
369
+ await this.media.stopLocalPreview();
370
+ this.bridge.refreshLocalViews();
371
+ }
372
+ /** publishCamera 发布摄像头。已经在预览的话复用那条轨道。 */
373
+ async publishCamera(simulcast = true) {
374
+ return publishCamera(this.mediaApi(), simulcast);
375
+ }
376
+ /**
377
+ * openCamera 是摄像头开关的**按类型**便捷接口。还没发布就发布(有本端预览时复用它,
378
+ * 同 `publishCamera()` 现有逻辑);已发布就取消静音——通话中开关摄像头走的是
379
+ * `setMuted` 既有的「关停采集、开重新采集换 sender」语义,不重新协商。
380
+ *
381
+ * 「发没发布过」同样问媒体适配器自己的账(`media.publishedCameraCid()`),
382
+ * 理由见 {@link openMicrophone}。
383
+ */
384
+ async openCamera() {
385
+ const cid = this.media.publishedCameraCid();
386
+ if (cid !== null) {
387
+ await this.setMuted(cid, false);
388
+ return;
389
+ }
390
+ await this.publishCamera();
391
+ }
392
+ /** closeCamera 关摄像头:`setMuted(cid, true)`(停采集,指示灯灭;不 unpublish)。没发布过是空操作。 */
393
+ async closeCamera() {
394
+ const cid = this.media.publishedCameraCid();
395
+ if (cid !== null)
396
+ await this.setMuted(cid, true);
397
+ }
398
+ /**
399
+ * setMuted 开关本端某条轨道。**不是 unpublish**,协商保留。
400
+ *
401
+ * 摄像头关 = 停采集(指示灯灭),开 = 重新采集换上去,cid 不变;重新采集失败时抛 RtcError。
402
+ */
403
+ async setMuted(cid, muted) {
404
+ await setMuted(this.mediaApi(), cid, muted);
405
+ this.bridge.refreshLocalViews();
406
+ }
407
+ /** localTrack 取本端轨道做预览。 */
408
+ localTrack(cid) {
409
+ return this.media.localTrack(cid);
410
+ }
411
+ /**
412
+ * attachView 把某个 uid 的远端画面挂到一个 `<video>` 上;传 `null` 卸载。
413
+ *
414
+ * **这是 UI 拿到画面的唯一途径**(CONVENTIONS §1):uikit 不许自己碰
415
+ * `RTCPeerConnection`,也不该自己拼 `MediaStream`。
416
+ */
417
+ attachView(uid, el) {
418
+ this.bridge.attachView(uid, el);
419
+ }
420
+ /** attachLocalView 把本端某条轨道挂到元素上做预览;传 `null` 卸载。 */
421
+ attachLocalView(cid, el) {
422
+ this.bridge.attachLocalView(cid, el);
423
+ }
424
+ /**
425
+ * setRemoteLayer 报某人画面的**层上界**(协议 §3.5:上界不是命令)。
426
+ *
427
+ * 九宫格缩略图报 `l`、双击放大报 `h`。**不触发重协商**,也不保证立刻切——
428
+ * 服务端要等目标层的关键帧,还会再按带宽估计压一次。
429
+ */
430
+ async setRemoteLayer(uid, layer) {
431
+ await setRemoteLayer(this.mediaApi(), uid, layer);
432
+ }
433
+ // ── 内部 ──────────────────────────────────────────────
434
+ /**
435
+ * rejectsSelf 挡住「名单里有自己」,就地报错并返回 true。
436
+ *
437
+ * 服务端会以 `1004 bad_params` 拒掉("callee_ids 不能含主叫自己"),但那条链路上的
438
+ * 失败很难看懂:界面已经乐观地进了「正在呼叫…」,而错误只是一条没头没尾的 1004。
439
+ * (实测撞过:Demo 的群呼默认名单里正好有登录的那个人。)
440
+ *
441
+ * **一份实现供 call 与 inviteMore 共用**——两处各写一遍的话,改了一处忘了另一处,
442
+ * 就又是一个「同一条规则在一处成立、另一处不成立」。
443
+ */
444
+ rejectsSelf(calleeIds, what) {
445
+ if (this.myUid === '' || !calleeIds.includes(this.myUid))
446
+ return false;
447
+ logger.warn(`${what}名单里含自己,已就地拒掉`, { uid: this.myUid });
448
+ this.bus.emitError(new RtcError(ErrorCode.badParams));
449
+ return true;
450
+ }
451
+ /**
452
+ * rejectsBadCallOptions 挡住超限的 `chatGroupId` / `userData`,就地报错并返回 true。
453
+ *
454
+ * 服务端会以 `1004 bad_params` 拒掉,但那条链路上主叫已经乐观地进了「正在呼叫…」,
455
+ * 错误只是一条没头没尾的 1004——与 {@link rejectsSelf} 同一个理由,本地先拦,
456
+ * 走同一个出口(HOST_INTEGRATION_DESIGN §3.3)。判断本身在 `callOptions.ts`
457
+ * (纯函数,直接单测);这里只管日志与出口这两件带副作用的事。
458
+ */
459
+ rejectsBadCallOptions(chatGroupId, userData) {
460
+ if (!violatesCallOptionLimits(chatGroupId, userData))
461
+ return false;
462
+ logger.warn('chatGroupId / userData 超限,已就地拒掉', {
463
+ chatGroupId, userDataBytes: byteLength(userData),
464
+ });
465
+ this.bus.emitError(new RtcError(ErrorCode.badParams));
466
+ return true;
467
+ }
468
+ /** mediaApi 是交给 media/engineMediaApi 那几个编排函数的一把依赖。 */
469
+ mediaApi() {
470
+ this.assertNotDestroyed();
471
+ return { media: this.media, loop: this.loop, bus: this.bus };
472
+ }
473
+ /**
474
+ * wiring 是交给 engineWiring 那两个装配函数的一把依赖。
475
+ *
476
+ * 每次现造一个:里面的 `connection` 是闭包,读的永远是**当前**那条连接
477
+ * ——它会随重连换对象。
478
+ */
479
+ wiring() {
480
+ return {
481
+ bus: this.bus,
482
+ bridge: this.bridge,
483
+ sender: this.sender,
484
+ loop: this.loop,
485
+ connection: () => this.connection,
486
+ };
487
+ }
488
+ }
489
+ //# sourceMappingURL=engine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.js","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import type { EngineEventHandler, EngineEventName, EngineEvents } from './events.js';
2
+ import type { EmittedEvent } from './state/types.js';
3
+ export declare class EngineBus {
4
+ private readonly bus;
5
+ /** on 订阅事件,返回退订函数。 */
6
+ on<K extends EngineEventName>(name: K, handler: EngineEventHandler<K>): () => void;
7
+ /** emit 抛一个事件,并按级别记一条日志。 */
8
+ emit<K extends EngineEventName>(name: K, payload: EngineEvents[K]): void;
9
+ /** emitMachine 把状态机的 `onXxx` 翻成公开事件名,并把参数转成 camelCase。 */
10
+ emitMachine(event: EmittedEvent): void;
11
+ /** emitError 把任意异常收敛成 error 事件。 */
12
+ emitError(err: unknown): void;
13
+ /** clear 清空全部订阅。给 `CallEngine.destroy()` 用——终态销毁,之后不会再抛任何事件。 */
14
+ clear(): void;
15
+ }
16
+ //# sourceMappingURL=engineBus.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engineBus.d.ts","sourceRoot":"","sources":["../src/engineBus.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAIrF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAuBrD,qBAAa,SAAS;IACpB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAkB;IAEtC,sBAAsB;IACtB,EAAE,CAAC,CAAC,SAAS,eAAe,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,kBAAkB,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI;IAIlF,4BAA4B;IAC5B,IAAI,CAAC,CAAC,SAAS,eAAe,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG,IAAI;IAMxE,0DAA0D;IAC1D,WAAW,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI;IAmBtC,mCAAmC;IACnC,SAAS,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI;IAS7B,gEAAgE;IAChE,KAAK,IAAI,IAAI;CAGd"}
@@ -0,0 +1,89 @@
1
+ import { ErrorCode, RtcError, errorName, isRtcError } from './errors.js';
2
+ import { EventBus } from './eventBus.js';
3
+ import { MACHINE_EVENT_NAMES } from './events.js';
4
+ import { logger } from './logger.js';
5
+ import { camelizeArgs } from './signaling/caseMapping.js';
6
+ /**
7
+ * engine 的事件出口:抛给宿主的同时**写一条日志**。
8
+ *
9
+ * # 为什么事件要进日志
10
+ *
11
+ * 出问题时最需要的一样东西是「这一通电话到底发生了什么」,而那正是公开事件表。
12
+ * 只有宿主自己订阅才看得到的话,一次排障就得让人手动复制事件流——
13
+ * 复制不全、顺序错乱、跟服务端日志对不上时间轴。
14
+ * 走 `logger` 之后,宿主装一个 sink 就能把两端与服务端放在同一条时间轴上读。
15
+ *
16
+ * # 级别怎么定
17
+ *
18
+ * 按 `docs/mechanism/LOGGING.md`:info 是**状态跃迁**,一次 1v1 通话个位数条。
19
+ * `activeSpeakers` / `networkQuality` 是服务端节流后的**周期性**事件(300ms / 2s),
20
+ * 放 info 会把真正要看的那几条冲掉,所以降到 debug。
21
+ */
22
+ const PERIODIC_EVENTS = new Set([
23
+ 'activeSpeakers',
24
+ 'networkQuality',
25
+ ]);
26
+ export class EngineBus {
27
+ bus = new EventBus();
28
+ /** on 订阅事件,返回退订函数。 */
29
+ on(name, handler) {
30
+ return this.bus.on(name, handler);
31
+ }
32
+ /** emit 抛一个事件,并按级别记一条日志。 */
33
+ emit(name, payload) {
34
+ if (PERIODIC_EVENTS.has(name))
35
+ logger.debug(`event ${name}`, toFields(payload));
36
+ else
37
+ logger.info(`event ${name}`, toFields(payload));
38
+ this.bus.emit(name, payload);
39
+ }
40
+ /** emitMachine 把状态机的 `onXxx` 翻成公开事件名,并把参数转成 camelCase。 */
41
+ emitMachine(event) {
42
+ const name = MACHINE_EVENT_NAMES[event.cb];
43
+ if (name === undefined) {
44
+ logger.warn('状态机抛了一个没登记的回调', { cb: event.cb });
45
+ return;
46
+ }
47
+ const args = camelizeArgs(event.args);
48
+ /*
49
+ callCancelled 的公开事件字段改成了 uid(四端命名核对,2026-09-15),但一致性向量
50
+ (四端共用,见 call_fsm.json)钉的是线路字段名 by,callRecv.ts 不能跟着改——
51
+ 只在进公开事件表这最后一步做翻译,别处都还是 by。
52
+ */
53
+ if (name === 'callCancelled' && Object.hasOwn(args, 'by')) {
54
+ args['uid'] = args['by'];
55
+ delete args['by'];
56
+ }
57
+ this.emit(name, args);
58
+ }
59
+ /** emitError 把任意异常收敛成 error 事件。 */
60
+ emitError(err) {
61
+ const error = isRtcError(err) ? err : new RtcError(ErrorCode.internal, { cause: err });
62
+ this.emit('error', {
63
+ code: error.code,
64
+ name: errorName(error.code),
65
+ message: error.message,
66
+ });
67
+ }
68
+ /** clear 清空全部订阅。给 `CallEngine.destroy()` 用——终态销毁,之后不会再抛任何事件。 */
69
+ clear() {
70
+ this.bus.clear();
71
+ }
72
+ }
73
+ /**
74
+ * toFields 把事件参数摊平成日志字段。
75
+ *
76
+ * 对象与数组转成 JSON 字符串:日志字段是给人 grep 的,嵌套结构在一行里读不出来,
77
+ * 而且各家 sink 对嵌套值的处理还不一致。
78
+ */
79
+ function toFields(payload) {
80
+ if (payload === null || typeof payload !== 'object')
81
+ return {};
82
+ const out = {};
83
+ for (const [key, value] of Object.entries(payload)) {
84
+ out[key] =
85
+ value !== null && typeof value === 'object' ? JSON.stringify(value) : value;
86
+ }
87
+ return out;
88
+ }
89
+ //# sourceMappingURL=engineBus.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engineBus.js","sourceRoot":"","sources":["../src/engineBus.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ import type { EngineBus } from './engineBus.js';
2
+ import type { FrameLoop } from './frameLoop.js';
3
+ import type { MediaBridge } from './media/mediaBridge.js';
4
+ import type { MediaPlaneDeps } from './media/mediaPlane.js';
5
+ import type { Connection } from './signaling/connection.js';
6
+ import type { EngineConnectionHandlers } from './signaling/connectionFactory.js';
7
+ import type { FrameSender } from './signaling/frameSender.js';
8
+ /**
9
+ * 门面的**装配**那一半:把连接回调与媒体依赖拼出来。
10
+ *
11
+ * 单拎出来不是为了凑行数——这两块是 `CallEngine` 里**唯一不属于公开 API 的东西**。
12
+ * 它旁边那些方法(`call` / `accept` / `publishCamera` …)是宿主直接调的签名,
13
+ * 拆出去只会让每个方法多一跳;而这两块是纯接线,宿主永远看不见,
14
+ * 放在门面里只是让那个文件一直贴着 400 行的红线。
15
+ */
16
+ /** EngineWiring 是接线要用到的那一把东西。 */
17
+ export interface EngineWiring {
18
+ bus: EngineBus;
19
+ bridge: MediaBridge;
20
+ sender: FrameSender;
21
+ loop: FrameLoop;
22
+ /**
23
+ * 当前连接。**取成函数**:它会随重连换对象,传值的话拿到的是构造那一刻的旧账。
24
+ */
25
+ connection: () => Connection | null;
26
+ }
27
+ /**
28
+ * engineMediaDeps 是交给媒体接线的那一小把依赖(见 media/mediaPlane.ts)。
29
+ *
30
+ * `uidOf` 同样读的是状态机的**当前**快照,理由和 `connection` 那条一样。
31
+ */
32
+ export declare function engineMediaDeps(w: EngineWiring): MediaPlaneDeps;
33
+ /**
34
+ * engineConnectionHandlers 把连接层的回调接到状态机与事件总线上。
35
+ *
36
+ * `noteHelloApplied` 是回给门面的那条线:`login()` 要 await「hello.ok 已经喂进状态机」,
37
+ * 而握手结果**每一次**都从这里进来(包括自动重连那些),所以 promise 只能由这里产出。
38
+ */
39
+ export declare function engineConnectionHandlers(w: EngineWiring, noteHelloApplied: (applied: Promise<void>) => void): EngineConnectionHandlers;
40
+ //# sourceMappingURL=engineWiring.d.ts.map