@wetspace/wetrtc 3.0.2 → 4.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 (139) hide show
  1. package/README.md +156 -29
  2. package/dist/es/__test__/codec-preference.test.js +36 -0
  3. package/dist/es/__test__/codec-preference.test.js.map +1 -0
  4. package/dist/es/__test__/data-manager.test.js +60 -0
  5. package/dist/es/__test__/data-manager.test.js.map +1 -0
  6. package/dist/es/__test__/fsm.test.js +33 -0
  7. package/dist/es/__test__/fsm.test.js.map +1 -0
  8. package/dist/es/__test__/media-manager.test.js +41 -0
  9. package/dist/es/__test__/media-manager.test.js.map +1 -0
  10. package/dist/es/__test__/signal-manager.test.js +100 -0
  11. package/dist/es/__test__/signal-manager.test.js.map +1 -0
  12. package/dist/es/__test__/wetrtc-lifecycle.test.js +152 -0
  13. package/dist/es/__test__/wetrtc-lifecycle.test.js.map +1 -0
  14. package/dist/es/data/data-manager.d.ts +37 -0
  15. package/dist/es/data/data-manager.d.ts.map +1 -0
  16. package/dist/es/data/data-manager.js +282 -0
  17. package/dist/es/data/data-manager.js.map +1 -0
  18. package/dist/es/data/types.d.ts +34 -0
  19. package/dist/es/data/types.d.ts.map +1 -0
  20. package/dist/es/data/types.js +0 -0
  21. package/dist/es/disposable.d.ts +12 -0
  22. package/dist/es/disposable.d.ts.map +1 -0
  23. package/dist/es/disposable.js +36 -0
  24. package/dist/es/disposable.js.map +1 -0
  25. package/dist/es/fsm.d.ts +26 -0
  26. package/dist/es/fsm.d.ts.map +1 -0
  27. package/dist/es/fsm.js +63 -0
  28. package/dist/es/fsm.js.map +1 -0
  29. package/dist/es/index.d.ts +22 -0
  30. package/dist/es/index.d.ts.map +1 -0
  31. package/dist/es/index.js +48 -0
  32. package/dist/es/index.js.map +1 -0
  33. package/dist/es/media/audio-encoding.d.ts +10 -0
  34. package/dist/es/media/audio-encoding.d.ts.map +1 -0
  35. package/dist/es/media/audio-encoding.js +41 -0
  36. package/dist/es/media/audio-encoding.js.map +1 -0
  37. package/dist/es/media/codec-preference.d.ts +11 -0
  38. package/dist/es/media/codec-preference.d.ts.map +1 -0
  39. package/dist/es/media/codec-preference.js +77 -0
  40. package/dist/es/media/codec-preference.js.map +1 -0
  41. package/dist/es/media/encoding-utils.d.ts +2 -0
  42. package/dist/es/media/encoding-utils.d.ts.map +1 -0
  43. package/dist/es/media/encoding-utils.js +8 -0
  44. package/dist/es/media/encoding-utils.js.map +1 -0
  45. package/dist/es/media/media-manager.d.ts +39 -0
  46. package/dist/es/media/media-manager.d.ts.map +1 -0
  47. package/dist/es/media/media-manager.js +121 -0
  48. package/dist/es/media/media-manager.js.map +1 -0
  49. package/dist/es/media/types.d.ts +25 -0
  50. package/dist/es/media/types.d.ts.map +1 -0
  51. package/dist/es/media/types.js +0 -0
  52. package/dist/es/media/video-encoding.d.ts +12 -0
  53. package/dist/es/media/video-encoding.d.ts.map +1 -0
  54. package/dist/es/media/video-encoding.js +60 -0
  55. package/dist/es/media/video-encoding.js.map +1 -0
  56. package/dist/es/signal/signal-manager.d.ts +45 -0
  57. package/dist/es/signal/signal-manager.d.ts.map +1 -0
  58. package/dist/es/signal/signal-manager.js +250 -0
  59. package/dist/es/signal/signal-manager.js.map +1 -0
  60. package/dist/es/signal/types.d.ts +26 -0
  61. package/dist/es/signal/types.d.ts.map +1 -0
  62. package/dist/es/signal/types.js +8 -0
  63. package/dist/es/signal/types.js.map +1 -0
  64. package/dist/es/stats/stats-monitor.d.ts +32 -0
  65. package/dist/es/stats/stats-monitor.d.ts.map +1 -0
  66. package/dist/es/stats/stats-monitor.js +191 -0
  67. package/dist/es/stats/stats-monitor.js.map +1 -0
  68. package/dist/es/stats/types.d.ts +33 -0
  69. package/dist/es/stats/types.d.ts.map +1 -0
  70. package/dist/es/stats/types.js +0 -0
  71. package/dist/es/utils/types.d.ts +46 -0
  72. package/dist/es/utils/types.d.ts.map +1 -0
  73. package/dist/es/utils/types.js +80 -0
  74. package/dist/es/utils/types.js.map +1 -0
  75. package/dist/es/wetrtc.d.ts +92 -0
  76. package/dist/es/wetrtc.d.ts.map +1 -0
  77. package/dist/es/wetrtc.js +403 -0
  78. package/dist/es/wetrtc.js.map +1 -0
  79. package/dist/lib/__test__/codec-preference.test.js +34 -0
  80. package/dist/lib/__test__/codec-preference.test.js.map +1 -0
  81. package/dist/lib/__test__/data-manager.test.js +61 -0
  82. package/dist/lib/__test__/data-manager.test.js.map +1 -0
  83. package/dist/lib/__test__/fsm.test.js +34 -0
  84. package/dist/lib/__test__/fsm.test.js.map +1 -0
  85. package/dist/lib/__test__/media-manager.test.js +42 -0
  86. package/dist/lib/__test__/media-manager.test.js.map +1 -0
  87. package/dist/lib/__test__/signal-manager.test.js +101 -0
  88. package/dist/lib/__test__/signal-manager.test.js.map +1 -0
  89. package/dist/lib/__test__/wetrtc-lifecycle.test.js +153 -0
  90. package/dist/lib/__test__/wetrtc-lifecycle.test.js.map +1 -0
  91. package/dist/lib/data/data-manager.js +306 -0
  92. package/dist/lib/data/data-manager.js.map +1 -0
  93. package/dist/lib/data/types.js +18 -0
  94. package/dist/lib/data/types.js.map +1 -0
  95. package/dist/lib/disposable.js +60 -0
  96. package/dist/lib/disposable.js.map +1 -0
  97. package/dist/lib/fsm.js +87 -0
  98. package/dist/lib/fsm.js.map +1 -0
  99. package/dist/lib/index.js +75 -0
  100. package/dist/lib/index.js.map +1 -0
  101. package/dist/lib/media/audio-encoding.js +66 -0
  102. package/dist/lib/media/audio-encoding.js.map +1 -0
  103. package/dist/lib/media/codec-preference.js +106 -0
  104. package/dist/lib/media/codec-preference.js.map +1 -0
  105. package/dist/lib/media/encoding-utils.js +32 -0
  106. package/dist/lib/media/encoding-utils.js.map +1 -0
  107. package/dist/lib/media/media-manager.js +145 -0
  108. package/dist/lib/media/media-manager.js.map +1 -0
  109. package/dist/lib/media/types.js +18 -0
  110. package/dist/lib/media/types.js.map +1 -0
  111. package/dist/lib/media/video-encoding.js +87 -0
  112. package/dist/lib/media/video-encoding.js.map +1 -0
  113. package/dist/lib/signal/signal-manager.js +274 -0
  114. package/dist/lib/signal/signal-manager.js.map +1 -0
  115. package/dist/lib/signal/types.js +32 -0
  116. package/dist/lib/signal/types.js.map +1 -0
  117. package/dist/lib/stats/stats-monitor.js +215 -0
  118. package/dist/lib/stats/stats-monitor.js.map +1 -0
  119. package/dist/lib/stats/types.js +18 -0
  120. package/dist/lib/stats/types.js.map +1 -0
  121. package/dist/lib/utils/types.js +108 -0
  122. package/dist/lib/utils/types.js.map +1 -0
  123. package/dist/lib/wetrtc.js +415 -0
  124. package/dist/lib/wetrtc.js.map +1 -0
  125. package/package.json +38 -42
  126. package/es/core/constant.d.ts +0 -6
  127. package/es/core/hook.d.ts +0 -31
  128. package/es/core/index.d.ts +0 -39
  129. package/es/index.d.ts +0 -6
  130. package/es/index.js +0 -1
  131. package/es/libs/index.d.ts +0 -41
  132. package/es/libs/record.d.ts +0 -8
  133. package/lib/core/constant.d.ts +0 -6
  134. package/lib/core/hook.d.ts +0 -31
  135. package/lib/core/index.d.ts +0 -39
  136. package/lib/index.d.ts +0 -6
  137. package/lib/index.js +0 -1
  138. package/lib/libs/index.d.ts +0 -41
  139. package/lib/libs/record.d.ts +0 -8
@@ -0,0 +1,46 @@
1
+ /** 客户端事件 */
2
+ export type WetRTCEvent = 'statechange' | 'track' | 'datachannel' | 'message' | 'error' | 'reconnecting' | 'stats';
3
+ export type WetRTCEventMap = {
4
+ statechange: (state: string, prev: string) => void;
5
+ track: (ev: RTCTrackEvent) => void;
6
+ datachannel: (channel: RTCDataChannel) => void;
7
+ message: (data: unknown, channel: RTCDataChannel) => void;
8
+ error: (err: WetRTCError) => void;
9
+ reconnecting: (attempt: number, max: number) => void;
10
+ stats: (snapshot: import('../stats/types').StatsSnapshot) => void;
11
+ };
12
+ export type WetRTCErrorCode = 'SIGNAL_FAILED' | 'NEGOTIATION_FAILED' | 'ICE_FAILED' | 'MEDIA_FAILED' | 'DATA_FAILED' | 'RECONNECT_FAILED' | 'INTERNAL' | 'TIMEOUT';
13
+ export interface WetRTCError {
14
+ code: WetRTCErrorCode;
15
+ message: string;
16
+ recoverable: boolean;
17
+ }
18
+ export declare enum LogLevel {
19
+ DEBUG = 0,
20
+ INFO = 1,
21
+ WARN = 2,
22
+ ERROR = 3,
23
+ NONE = 4
24
+ }
25
+ export interface Logger {
26
+ debug(...args: unknown[]): void;
27
+ info(...args: unknown[]): void;
28
+ warn(...args: unknown[]): void;
29
+ error(...args: unknown[]): void;
30
+ }
31
+ export declare function createLogger(level?: LogLevel, prefix?: string): Logger;
32
+ /** 定时器工具 */
33
+ export declare function createTimer(callback: () => void, ms: number): IDisposable;
34
+ import { IDisposable } from '../disposable';
35
+ /** 退避计算 */
36
+ export declare function backoffDelay(attempt: number, baseMs: number, maxMs: number, multiplier: number): number;
37
+ /** 事件发射器(轻量,无依赖) */
38
+ export declare class TypedEmitter<E extends Record<string, (...args: any[]) => void>> {
39
+ private handlers;
40
+ on<K extends keyof E>(event: K, handler: E[K]): () => void;
41
+ once<K extends keyof E>(event: K, handler: E[K]): void;
42
+ off<K extends keyof E>(event: K, handler: E[K]): void;
43
+ emit<K extends keyof E>(event: K, ...args: Parameters<E[K]>): void;
44
+ removeAll(): void;
45
+ }
46
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/utils/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,80 @@
1
+ var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
2
+ LogLevel2[LogLevel2["DEBUG"] = 0] = "DEBUG";
3
+ LogLevel2[LogLevel2["INFO"] = 1] = "INFO";
4
+ LogLevel2[LogLevel2["WARN"] = 2] = "WARN";
5
+ LogLevel2[LogLevel2["ERROR"] = 3] = "ERROR";
6
+ LogLevel2[LogLevel2["NONE"] = 4] = "NONE";
7
+ return LogLevel2;
8
+ })(LogLevel || {});
9
+ function createLogger(level = 2 /* WARN */, prefix = "[WetRTC]") {
10
+ return {
11
+ debug(...args) {
12
+ if (level <= 0 /* DEBUG */)
13
+ console.debug(prefix, ...args);
14
+ },
15
+ info(...args) {
16
+ if (level <= 1 /* INFO */)
17
+ console.info(prefix, ...args);
18
+ },
19
+ warn(...args) {
20
+ if (level <= 2 /* WARN */)
21
+ console.warn(prefix, ...args);
22
+ },
23
+ error(...args) {
24
+ if (level <= 3 /* ERROR */)
25
+ console.error(prefix, ...args);
26
+ }
27
+ };
28
+ }
29
+ function createTimer(callback, ms) {
30
+ const id = setInterval(callback, ms);
31
+ return { dispose: () => clearInterval(id) };
32
+ }
33
+ function backoffDelay(attempt, baseMs, maxMs, multiplier) {
34
+ return Math.min(baseMs * Math.pow(multiplier, attempt - 1), maxMs);
35
+ }
36
+ class TypedEmitter {
37
+ constructor() {
38
+ this.handlers = /* @__PURE__ */ new Map();
39
+ }
40
+ on(event, handler) {
41
+ if (!this.handlers.has(event)) {
42
+ this.handlers.set(event, /* @__PURE__ */ new Set());
43
+ }
44
+ this.handlers.get(event).add(handler);
45
+ return () => this.handlers.get(event)?.delete(handler);
46
+ }
47
+ once(event, handler) {
48
+ const wrapper = (...args) => {
49
+ this.off(event, wrapper);
50
+ handler(...args);
51
+ };
52
+ this.on(event, wrapper);
53
+ }
54
+ off(event, handler) {
55
+ this.handlers.get(event)?.delete(handler);
56
+ }
57
+ emit(event, ...args) {
58
+ const set = this.handlers.get(event);
59
+ if (!set)
60
+ return;
61
+ for (const fn of set) {
62
+ try {
63
+ fn(...args);
64
+ } catch {
65
+ }
66
+ }
67
+ }
68
+ removeAll() {
69
+ this.handlers.clear();
70
+ }
71
+ }
72
+ export {
73
+ LogLevel,
74
+ TypedEmitter,
75
+ backoffDelay,
76
+ createLogger,
77
+ createTimer
78
+ };
79
+
80
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"mappings":"AAuCO,IAAK,WAAL,kBAAKA,cAAL;AACL,EAAAA,oBAAA,WAAQ,KAAR;AACA,EAAAA,oBAAA,UAAO,KAAP;AACA,EAAAA,oBAAA,UAAO,KAAP;AACA,EAAAA,oBAAA,WAAQ,KAAR;AACA,EAAAA,oBAAA,UAAO,KAAP;AALU,SAAAA;AAAA;AAeL,SAAS,aAAa,QAAkB,cAAe,SAAS,YAAoB;AACzF,SAAO;AAAA,IACL,SAAS,MAAM;AAAE,UAAI,SAAS;AAAgB,gBAAQ,MAAM,QAAQ,GAAG,IAAI;AAAA,IAAG;AAAA,IAC9E,QAAQ,MAAO;AAAE,UAAI,SAAS;AAAgB,gBAAQ,KAAK,QAAQ,GAAG,IAAI;AAAA,IAAG;AAAA,IAC7E,QAAQ,MAAO;AAAE,UAAI,SAAS;AAAgB,gBAAQ,KAAK,QAAQ,GAAG,IAAI;AAAA,IAAG;AAAA,IAC7E,SAAS,MAAM;AAAE,UAAI,SAAS;AAAgB,gBAAQ,MAAM,QAAQ,GAAG,IAAI;AAAA,IAAG;AAAA,EAChF;AACF;AAGO,SAAS,YAAY,UAAsB,IAAyB;AACzE,QAAM,KAAK,YAAY,UAAU,EAAE;AACnC,SAAO,EAAE,SAAS,MAAM,cAAc,EAAE,EAAE;AAC5C;AAKO,SAAS,aACd,SACA,QACA,OACA,YACQ;AACR,SAAO,KAAK,IAAI,SAAS,KAAK,IAAI,YAAY,UAAU,CAAC,GAAG,KAAK;AACnE;AAGO,MAAM,aAAiE;AAAA,EAAvE;AACL,SAAQ,WAAW,oBAAI,IAA4B;AAAA;AAAA,EAEnD,GAAsB,OAAU,SAA2B;AACzD,QAAI,CAAC,KAAK,SAAS,IAAI,KAAK,GAAG;AAC7B,WAAK,SAAS,IAAI,OAAO,oBAAI,IAAI,CAAC;AAAA,IACpC;AACA,SAAK,SAAS,IAAI,KAAK,EAAG,IAAI,OAAO;AACrC,WAAO,MAAM,KAAK,SAAS,IAAI,KAAK,GAAG,OAAO,OAAO;AAAA,EACvD;AAAA,EAEA,KAAwB,OAAU,SAAqB;AACrD,UAAM,UAAW,IAAI,SAAgB;AACnC,WAAK,IAAI,OAAO,OAAc;AAC9B,cAAQ,GAAG,IAAI;AAAA,IACjB;AACA,SAAK,GAAG,OAAO,OAAO;AAAA,EACxB;AAAA,EAEA,IAAuB,OAAU,SAAqB;AACpD,SAAK,SAAS,IAAI,KAAK,GAAG,OAAO,OAAO;AAAA,EAC1C;AAAA,EAEA,KAAwB,UAAa,MAA8B;AACjE,UAAM,MAAM,KAAK,SAAS,IAAI,KAAK;AACnC,QAAI,CAAC;AAAK;AACV,eAAW,MAAM,KAAK;AACpB,UAAI;AAAE,QAAC,GAAgB,GAAG,IAAI;AAAA,MAAG,QAAQ;AAAA,MAAgB;AAAA,IAC3D;AAAA,EACF;AAAA,EAEA,YAAkB;AAChB,SAAK,SAAS,MAAM;AAAA,EACtB;AACF","names":["LogLevel"],"ignoreList":[],"sources":["../../../src/utils/types.ts"],"sourcesContent":["// ── 事件与错误类型 ──\r\n\r\n/** 客户端事件 */\r\nexport type WetRTCEvent =\r\n | 'statechange'\r\n | 'track'\r\n | 'datachannel'\r\n | 'message'\r\n | 'error'\r\n | 'reconnecting'\r\n | 'stats';\r\n\r\nexport type WetRTCEventMap = {\r\n statechange: (state: string, prev: string) => void;\r\n track: (ev: RTCTrackEvent) => void;\r\n datachannel: (channel: RTCDataChannel) => void;\r\n message: (data: unknown, channel: RTCDataChannel) => void;\r\n error: (err: WetRTCError) => void;\r\n reconnecting: (attempt: number, max: number) => void;\r\n stats: (snapshot: import('../stats/types').StatsSnapshot) => void;\r\n};\r\n\r\nexport type WetRTCErrorCode =\r\n | 'SIGNAL_FAILED'\r\n | 'NEGOTIATION_FAILED'\r\n | 'ICE_FAILED'\r\n | 'MEDIA_FAILED'\r\n | 'DATA_FAILED'\r\n | 'RECONNECT_FAILED'\r\n | 'INTERNAL'\r\n | 'TIMEOUT';\r\n\r\nexport interface WetRTCError {\r\n code: WetRTCErrorCode;\r\n message: string;\r\n recoverable: boolean;\r\n}\r\n\r\n// ── 日志 ──\r\nexport enum LogLevel {\r\n DEBUG = 0,\r\n INFO = 1,\r\n WARN = 2,\r\n ERROR = 3,\r\n NONE = 4,\r\n}\r\n\r\nexport interface Logger {\r\n debug(...args: unknown[]): void;\r\n info(...args: unknown[]): void;\r\n warn(...args: unknown[]): void;\r\n error(...args: unknown[]): void;\r\n}\r\n\r\nexport function createLogger(level: LogLevel = LogLevel.WARN, prefix = '[WetRTC]'): Logger {\r\n return {\r\n debug(...args) { if (level <= LogLevel.DEBUG) console.debug(prefix, ...args); },\r\n info(...args) { if (level <= LogLevel.INFO) console.info(prefix, ...args); },\r\n warn(...args) { if (level <= LogLevel.WARN) console.warn(prefix, ...args); },\r\n error(...args) { if (level <= LogLevel.ERROR) console.error(prefix, ...args); },\r\n };\r\n}\r\n\r\n/** 定时器工具 */\r\nexport function createTimer(callback: () => void, ms: number): IDisposable {\r\n const id = setInterval(callback, ms);\r\n return { dispose: () => clearInterval(id) };\r\n}\r\n\r\nimport { IDisposable } from '../disposable';\r\n\r\n/** 退避计算 */\r\nexport function backoffDelay(\r\n attempt: number,\r\n baseMs: number,\r\n maxMs: number,\r\n multiplier: number,\r\n): number {\r\n return Math.min(baseMs * Math.pow(multiplier, attempt - 1), maxMs);\r\n}\r\n\r\n/** 事件发射器(轻量,无依赖) */\r\nexport class TypedEmitter<E extends Record<string, (...args: any[]) => void>> {\r\n private handlers = new Map<keyof E, Set<Function>>();\r\n\r\n on<K extends keyof E>(event: K, handler: E[K]): () => void {\r\n if (!this.handlers.has(event)) {\r\n this.handlers.set(event, new Set());\r\n }\r\n this.handlers.get(event)!.add(handler);\r\n return () => this.handlers.get(event)?.delete(handler);\r\n }\r\n\r\n once<K extends keyof E>(event: K, handler: E[K]): void {\r\n const wrapper = ((...args: any[]) => {\r\n this.off(event, wrapper as any);\r\n handler(...args);\r\n }) as E[K];\r\n this.on(event, wrapper);\r\n }\r\n\r\n off<K extends keyof E>(event: K, handler: E[K]): void {\r\n this.handlers.get(event)?.delete(handler);\r\n }\r\n\r\n emit<K extends keyof E>(event: K, ...args: Parameters<E[K]>): void {\r\n const set = this.handlers.get(event);\r\n if (!set) return;\r\n for (const fn of set) {\r\n try { (fn as Function)(...args); } catch { /* 吞掉监听器异常 */ }\r\n }\r\n }\r\n\r\n removeAll(): void {\r\n this.handlers.clear();\r\n }\r\n}//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJtYXBwaW5ncyI6IiIsIm5hbWVzIjpbXSwiaWdub3JlTGlzdCI6W10sInNvdXJjZXMiOltdLCJzb3VyY2VzQ29udGVudCI6W119"]}
@@ -0,0 +1,92 @@
1
+ import { ConnectionStateMachine } from './fsm';
2
+ import type { ConnectionState } from './fsm';
3
+ import { type IDisposable } from './disposable';
4
+ import type { SignalConfig } from './signal/signal-manager';
5
+ import { MediaManager } from './media/media-manager';
6
+ import type { DisplayMediaOptions } from './media/types';
7
+ import { type VideoEncodingOptions } from './media/video-encoding';
8
+ import { type AudioEncodingOptions } from './media/audio-encoding';
9
+ import { type PreferredVideoCodec, type PreferredAudioCodec } from './media/codec-preference';
10
+ import { DataManager } from './data/data-manager';
11
+ import type { DataChannelConfig } from './data/types';
12
+ import { StatsMonitor } from './stats/stats-monitor';
13
+ import type { SignalChannel } from './signal/types';
14
+ import type { WetRTCEvent, WetRTCEventMap, WetRTCError, WetRTCErrorCode } from './utils/types';
15
+ export type { ConnectionState };
16
+ export type { WetRTCEvent, WetRTCEventMap, WetRTCError, WetRTCErrorCode };
17
+ export type { SignalChannel };
18
+ export type { VideoEncodingOptions } from './media/video-encoding';
19
+ export type { AudioEncodingOptions } from './media/audio-encoding';
20
+ export type { PreferredVideoCodec, PreferredAudioCodec } from './media/codec-preference';
21
+ export interface WetRTCConfig {
22
+ signal: SignalChannel;
23
+ direction?: 'sendonly' | 'recvonly' | 'sendrecv';
24
+ iceServers?: RTCIceServer[];
25
+ iceTransportPolicy?: RTCIceTransportPolicy;
26
+ polite?: boolean;
27
+ /** 是否主动发起 SDP 协商;默认 sendonly/sendrecv 为 true,recvonly 为 false */
28
+ initiator?: boolean;
29
+ dataChannels?: DataChannelConfig[];
30
+ signalConfig?: SignalConfig;
31
+ statsInterval?: number;
32
+ logLevel?: 'debug' | 'info' | 'warn' | 'error' | 'none';
33
+ reconnect?: false | {
34
+ maxAttempts?: number;
35
+ baseDelay?: number;
36
+ maxDelay?: number;
37
+ backoffMultiplier?: number;
38
+ };
39
+ /** 发送端视频编码低延迟参数(屏幕共享场景) */
40
+ videoEncoding?: VideoEncodingOptions;
41
+ /** 视频编解码器偏好;h264 优先使用 H.264(Electron/Chrome 下通常走硬件编码) */
42
+ preferredVideoCodec?: PreferredVideoCodec;
43
+ /** 发送端音频编码参数(码率、优先级等) */
44
+ audioEncoding?: AudioEncodingOptions;
45
+ /** 音频编解码器偏好;opus 优先使用 Opus(WebRTC 默认语音编解码器) */
46
+ preferredAudioCodec?: PreferredAudioCodec;
47
+ }
48
+ export declare class WetRTC implements IDisposable {
49
+ readonly fsm: ConnectionStateMachine;
50
+ readonly media: MediaManager;
51
+ readonly data: DataManager;
52
+ readonly stats: StatsMonitor;
53
+ private events;
54
+ private disposables;
55
+ private logger;
56
+ private pc;
57
+ private signalManager;
58
+ private config;
59
+ private reconnectAttempt;
60
+ private pcListenerCleanup;
61
+ private reconnecting;
62
+ private reconnectTimer;
63
+ private screenStream;
64
+ constructor(config: WetRTCConfig);
65
+ connect(): Promise<void>;
66
+ disconnect(): Promise<void>;
67
+ dispose(): void;
68
+ on<E extends WetRTCEvent>(event: E, handler: WetRTCEventMap[E]): () => void;
69
+ once<E extends WetRTCEvent>(event: E, handler: WetRTCEventMap[E]): void;
70
+ off<E extends WetRTCEvent>(event: E, handler: WetRTCEventMap[E]): void;
71
+ get state(): ConnectionState;
72
+ get peerConnection(): RTCPeerConnection | null;
73
+ addTrack(track: MediaStreamTrack, stream: MediaStream): void;
74
+ removeTrack(track: MediaStreamTrack): void;
75
+ replaceTrack(oldTrack: MediaStreamTrack, newTrack: MediaStreamTrack): Promise<void>;
76
+ shareScreen(options?: DisplayMediaOptions): Promise<MediaStream>;
77
+ stopScreenShare(): void;
78
+ switchCamera(deviceId?: string): Promise<MediaStream>;
79
+ replaceVideoTrack(track: MediaStreamTrack): Promise<void>;
80
+ private rebindPeerConnection;
81
+ private attachPcListeners;
82
+ private restoreLocalTracks;
83
+ private addTrackToPeerConnection;
84
+ private scheduleReconnect;
85
+ private cancelReconnect;
86
+ private ensureTransceivers;
87
+ private doConnect;
88
+ private createPeerConnection;
89
+ private normalizeConfig;
90
+ private emitError;
91
+ }
92
+ //# sourceMappingURL=wetrtc.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wetrtc.d.ts","sourceRoot":"","sources":["../../src/wetrtc.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,403 @@
1
+ import { ConnectionStateMachine } from "./fsm";
2
+ import { DisposableStack } from "./disposable";
3
+ import { SignalManager } from "./signal/signal-manager";
4
+ import { MediaManager } from "./media/media-manager";
5
+ import {
6
+ applyVideoEncodingToConnection,
7
+ applyVideoSenderEncoding,
8
+ applyVideoTrackContentHint
9
+ } from "./media/video-encoding";
10
+ import {
11
+ applyAudioEncodingToConnection,
12
+ applyAudioSenderEncoding
13
+ } from "./media/audio-encoding";
14
+ import {
15
+ applyH264CodecPreference,
16
+ applyH264CodecPreferences,
17
+ applyOpusCodecPreference,
18
+ applyOpusCodecPreferences
19
+ } from "./media/codec-preference";
20
+ import { DataManager } from "./data/data-manager";
21
+ import { StatsMonitor } from "./stats/stats-monitor";
22
+ import { TypedEmitter, createLogger } from "./utils/types";
23
+ const DEFAULT_ICE_SERVERS = [
24
+ { urls: "stun:stun.l.google.com:19302" }
25
+ ];
26
+ const DEFAULT_RECONNECT = {
27
+ maxAttempts: 5,
28
+ baseDelay: 1e3,
29
+ maxDelay: 3e4,
30
+ backoffMultiplier: 2
31
+ };
32
+ class WetRTC {
33
+ constructor(config) {
34
+ this.events = new TypedEmitter();
35
+ this.disposables = new DisposableStack();
36
+ this.pc = null;
37
+ this.reconnectAttempt = 0;
38
+ this.pcListenerCleanup = null;
39
+ this.reconnecting = false;
40
+ this.reconnectTimer = null;
41
+ this.screenStream = null;
42
+ this.config = this.normalizeConfig(config);
43
+ this.logger = createLogger(
44
+ config.logLevel === "debug" ? 0 : config.logLevel === "info" ? 1 : config.logLevel === "none" ? 4 : 2
45
+ );
46
+ this.fsm = new ConnectionStateMachine();
47
+ this.media = new MediaManager();
48
+ this.pc = this.createPeerConnection();
49
+ this.data = new DataManager(this.pc);
50
+ this.stats = new StatsMonitor(this.pc, {
51
+ interval: this.config.statsInterval || void 0
52
+ });
53
+ this.fsm.onChange((newState, prev) => {
54
+ this.events.emit("statechange", newState, prev);
55
+ });
56
+ this.signalManager = new SignalManager(
57
+ this.config.signal,
58
+ this.pc,
59
+ this.fsm,
60
+ this.config.polite,
61
+ this.config.signalConfig
62
+ );
63
+ this.attachPcListeners(this.pc);
64
+ if (this.config.dataChannels.length > 0) {
65
+ this.data.configureChannels(this.config.dataChannels);
66
+ }
67
+ this.data.onMessage((data, label) => {
68
+ const channel = label ? this.data.getChannel(label) : void 0;
69
+ this.events.emit("message", data, channel);
70
+ });
71
+ this.stats.onStats((snapshot) => {
72
+ this.events.emit("stats", snapshot);
73
+ });
74
+ this.disposables.push(this.fsm);
75
+ this.disposables.push(this.media);
76
+ this.disposables.push(this.data);
77
+ this.disposables.push(this.stats);
78
+ this.logger.info("WetRTC initialized", { direction: this.config.direction });
79
+ }
80
+ async connect() {
81
+ if (!this.fsm.is("idle", "failed")) {
82
+ throw new Error(`Cannot connect in state "${this.fsm.state}"`);
83
+ }
84
+ this.reconnectAttempt = 0;
85
+ return this.doConnect();
86
+ }
87
+ async disconnect() {
88
+ this.logger.info("Disconnecting");
89
+ this.stats.stop();
90
+ this.cancelReconnect();
91
+ try {
92
+ await this.config.signal.send({ type: "bye" });
93
+ } catch (err) {
94
+ this.logger.warn("Failed to send bye:", err);
95
+ }
96
+ this.rebindPeerConnection();
97
+ this.fsm.force("idle");
98
+ }
99
+ dispose() {
100
+ this.logger.info("Disposing WetRTC");
101
+ this.stats.stop();
102
+ this.cancelReconnect();
103
+ this.screenStream?.getTracks().forEach((track) => track.stop());
104
+ this.screenStream = null;
105
+ this.pcListenerCleanup?.();
106
+ this.pcListenerCleanup = null;
107
+ this.signalManager.dispose();
108
+ this.pc?.close();
109
+ this.pc = null;
110
+ this.disposables.dispose();
111
+ this.events.removeAll();
112
+ this.fsm.force("disposed");
113
+ }
114
+ on(event, handler) {
115
+ return this.events.on(event, handler);
116
+ }
117
+ once(event, handler) {
118
+ this.events.once(event, handler);
119
+ }
120
+ off(event, handler) {
121
+ this.events.off(event, handler);
122
+ }
123
+ get state() {
124
+ return this.fsm.state;
125
+ }
126
+ get peerConnection() {
127
+ return this.pc;
128
+ }
129
+ addTrack(track, stream) {
130
+ if (track.kind === "video" && this.config.videoEncoding) {
131
+ applyVideoTrackContentHint(
132
+ track,
133
+ this.config.videoEncoding.contentHint ?? "motion"
134
+ );
135
+ }
136
+ this.media.addTrack(track, stream);
137
+ this.addTrackToPeerConnection(track, stream);
138
+ }
139
+ removeTrack(track) {
140
+ const sender = this.pc?.getSenders().find((s) => s.track === track);
141
+ if (sender) {
142
+ this.pc?.removeTrack(sender);
143
+ }
144
+ this.media.removeTrack(track);
145
+ }
146
+ async replaceTrack(oldTrack, newTrack) {
147
+ const sender = this.pc?.getSenders().find((s) => s.track === oldTrack);
148
+ if (sender) {
149
+ await sender.replaceTrack(newTrack);
150
+ }
151
+ this.media.replaceTrack(oldTrack, newTrack);
152
+ }
153
+ async shareScreen(options) {
154
+ try {
155
+ const stream = await this.media.getDisplayMedia(options);
156
+ this.screenStream = stream;
157
+ for (const track of stream.getTracks()) {
158
+ this.addTrack(track, stream);
159
+ }
160
+ stream.getVideoTracks()[0]?.addEventListener("ended", () => {
161
+ this.stopScreenShare();
162
+ }, { once: true });
163
+ return stream;
164
+ } catch (err) {
165
+ this.emitError("MEDIA_FAILED", err.message, true);
166
+ throw err;
167
+ }
168
+ }
169
+ stopScreenShare() {
170
+ if (!this.screenStream)
171
+ return;
172
+ for (const track of this.screenStream.getTracks()) {
173
+ this.removeTrack(track);
174
+ }
175
+ this.screenStream = null;
176
+ }
177
+ async switchCamera(deviceId) {
178
+ const stream = await this.media.getUserMedia({
179
+ video: deviceId ? { deviceId: { exact: deviceId } } : true,
180
+ audio: false
181
+ });
182
+ const nextVideo = stream.getVideoTracks()[0];
183
+ const currentVideo = this.media.getLocalTracks("video")[0];
184
+ if (currentVideo && nextVideo) {
185
+ await this.replaceVideoTrack(nextVideo);
186
+ } else if (nextVideo) {
187
+ this.addTrack(nextVideo, stream);
188
+ }
189
+ return stream;
190
+ }
191
+ async replaceVideoTrack(track) {
192
+ const current = this.media.getLocalTracks("video")[0];
193
+ if (!current) {
194
+ const stream = new MediaStream([track]);
195
+ this.addTrack(track, stream);
196
+ return;
197
+ }
198
+ await this.replaceTrack(current, track);
199
+ }
200
+ // ── private ──
201
+ rebindPeerConnection() {
202
+ this.pcListenerCleanup?.();
203
+ this.pc?.close();
204
+ const pc = this.createPeerConnection();
205
+ this.pc = pc;
206
+ this.signalManager.setPeerConnection(pc);
207
+ this.data.setPeerConnection(pc);
208
+ this.stats.setPeerConnection(pc);
209
+ this.attachPcListeners(pc);
210
+ this.restoreLocalTracks(pc);
211
+ if (this.config.dataChannels.length > 0) {
212
+ this.data.configureChannels(this.config.dataChannels);
213
+ }
214
+ return pc;
215
+ }
216
+ attachPcListeners(pc) {
217
+ const onTrack = (ev) => {
218
+ if (ev.streams[0]) {
219
+ this.media.addRemoteStream(ev.streams[0]);
220
+ }
221
+ this.events.emit("track", ev);
222
+ };
223
+ const onDataChannel = (ev) => {
224
+ this.data.registerRemoteChannel(ev);
225
+ this.events.emit("datachannel", ev.channel);
226
+ };
227
+ const onIceDisconnected = () => {
228
+ if (this.pc !== pc)
229
+ return;
230
+ if (pc.iceConnectionState === "disconnected" && this.config.reconnect !== false) {
231
+ this.scheduleReconnect();
232
+ }
233
+ };
234
+ const onConnectionStateChange = () => {
235
+ if (this.pc !== pc || pc.connectionState !== "connected")
236
+ return;
237
+ if (this.config.videoEncoding) {
238
+ void applyVideoEncodingToConnection(pc, this.config.videoEncoding);
239
+ }
240
+ if (this.config.audioEncoding) {
241
+ void applyAudioEncodingToConnection(pc, this.config.audioEncoding);
242
+ }
243
+ };
244
+ pc.addEventListener("track", onTrack);
245
+ pc.addEventListener("datachannel", onDataChannel);
246
+ pc.addEventListener("iceconnectionstatechange", onIceDisconnected);
247
+ pc.addEventListener("connectionstatechange", onConnectionStateChange);
248
+ this.pcListenerCleanup = () => {
249
+ pc.removeEventListener("track", onTrack);
250
+ pc.removeEventListener("datachannel", onDataChannel);
251
+ pc.removeEventListener("iceconnectionstatechange", onIceDisconnected);
252
+ pc.removeEventListener("connectionstatechange", onConnectionStateChange);
253
+ };
254
+ }
255
+ restoreLocalTracks(pc) {
256
+ const stream = this.media.getLocalStream();
257
+ if (!stream)
258
+ return;
259
+ for (const track of this.media.getLocalTracks()) {
260
+ this.addTrackToPeerConnection(track, stream, pc);
261
+ }
262
+ }
263
+ addTrackToPeerConnection(track, stream, pc = this.pc) {
264
+ if (!pc)
265
+ return;
266
+ const sender = pc.addTrack(track, stream);
267
+ const transceiver = pc.getTransceivers().find((t) => t.sender === sender);
268
+ if (!transceiver)
269
+ return;
270
+ if (this.config.direction === "sendonly") {
271
+ transceiver.direction = "sendonly";
272
+ } else if (this.config.direction === "sendrecv") {
273
+ transceiver.direction = "sendrecv";
274
+ }
275
+ if (track.kind === "video" && this.config.videoEncoding) {
276
+ void applyVideoSenderEncoding(sender, this.config.videoEncoding);
277
+ }
278
+ if (track.kind === "audio" && this.config.audioEncoding) {
279
+ void applyAudioSenderEncoding(sender, this.config.audioEncoding);
280
+ }
281
+ if (track.kind === "video" && this.config.preferredVideoCodec === "h264") {
282
+ applyH264CodecPreference(transceiver);
283
+ }
284
+ if (track.kind === "audio" && this.config.preferredAudioCodec === "opus") {
285
+ applyOpusCodecPreference(transceiver);
286
+ }
287
+ }
288
+ scheduleReconnect() {
289
+ if (this.reconnecting)
290
+ return;
291
+ const { maxAttempts = 5, baseDelay = 1e3, maxDelay = 3e4, backoffMultiplier = 2 } = this.config.reconnect === false ? DEFAULT_RECONNECT : this.config.reconnect;
292
+ if (this.reconnectAttempt >= maxAttempts) {
293
+ this.logger.error("Max reconnect attempts reached");
294
+ this.fsm.transition("failed");
295
+ this.emitError("RECONNECT_FAILED", "Max reconnect attempts reached", false);
296
+ return;
297
+ }
298
+ this.reconnecting = true;
299
+ this.reconnectAttempt++;
300
+ const delay = Math.min(
301
+ baseDelay * Math.pow(backoffMultiplier, this.reconnectAttempt - 1),
302
+ maxDelay
303
+ );
304
+ this.logger.info(`Reconnecting in ${delay}ms (attempt ${this.reconnectAttempt}/${maxAttempts})`);
305
+ this.events.emit("reconnecting", this.reconnectAttempt, maxAttempts);
306
+ this.reconnectTimer = setTimeout(async () => {
307
+ try {
308
+ this.stats.stop();
309
+ this.rebindPeerConnection();
310
+ this.fsm.force("idle");
311
+ await this.doConnect();
312
+ this.reconnecting = false;
313
+ this.reconnectTimer = null;
314
+ } catch {
315
+ this.reconnecting = false;
316
+ this.reconnectTimer = null;
317
+ this.scheduleReconnect();
318
+ }
319
+ }, delay);
320
+ }
321
+ cancelReconnect() {
322
+ this.reconnecting = false;
323
+ if (this.reconnectTimer) {
324
+ clearTimeout(this.reconnectTimer);
325
+ this.reconnectTimer = null;
326
+ }
327
+ }
328
+ ensureTransceivers() {
329
+ if (!this.pc || this.config.direction !== "recvonly")
330
+ return;
331
+ if (this.pc.getTransceivers().length > 0)
332
+ return;
333
+ const videoTransceiver = this.pc.addTransceiver("video", { direction: "recvonly" });
334
+ const audioTransceiver = this.pc.addTransceiver("audio", { direction: "recvonly" });
335
+ if (this.config.preferredVideoCodec === "h264") {
336
+ applyH264CodecPreference(videoTransceiver);
337
+ }
338
+ if (this.config.preferredAudioCodec === "opus") {
339
+ applyOpusCodecPreference(audioTransceiver);
340
+ }
341
+ }
342
+ async doConnect() {
343
+ try {
344
+ this.fsm.transition("signaling");
345
+ this.stats.start();
346
+ this.ensureTransceivers();
347
+ if (this.config.preferredVideoCodec === "h264" && this.pc) {
348
+ applyH264CodecPreferences(this.pc);
349
+ }
350
+ if (this.config.preferredAudioCodec === "opus" && this.pc) {
351
+ applyOpusCodecPreferences(this.pc);
352
+ }
353
+ if (this.config.initiator) {
354
+ this.data.initConfiguredChannels();
355
+ await this.signalManager.createOffer();
356
+ } else {
357
+ this.logger.info("Waiting for remote offer (passive)");
358
+ }
359
+ } catch (err) {
360
+ this.emitError("NEGOTIATION_FAILED", err.message, true);
361
+ throw err;
362
+ }
363
+ }
364
+ createPeerConnection() {
365
+ const config = {
366
+ iceServers: this.config.iceServers
367
+ };
368
+ if (this.config.iceTransportPolicy) {
369
+ config.iceTransportPolicy = this.config.iceTransportPolicy;
370
+ }
371
+ return new RTCPeerConnection(config);
372
+ }
373
+ normalizeConfig(config) {
374
+ return {
375
+ signal: config.signal,
376
+ direction: config.direction ?? "sendrecv",
377
+ iceServers: config.iceServers ?? DEFAULT_ICE_SERVERS,
378
+ iceTransportPolicy: config.iceTransportPolicy,
379
+ polite: config.polite ?? config.direction === "sendonly",
380
+ initiator: config.initiator ?? config.direction !== "recvonly",
381
+ dataChannels: config.dataChannels ?? [],
382
+ signalConfig: config.signalConfig ?? {},
383
+ reconnect: config.reconnect === false ? false : { ...DEFAULT_RECONNECT, ...config.reconnect },
384
+ statsInterval: config.statsInterval ?? 2e3,
385
+ videoEncoding: config.videoEncoding,
386
+ preferredVideoCodec: config.preferredVideoCodec ?? "auto",
387
+ audioEncoding: config.audioEncoding,
388
+ preferredAudioCodec: config.preferredAudioCodec ?? "auto"
389
+ };
390
+ }
391
+ emitError(code, message, recoverable) {
392
+ this.events.emit("error", {
393
+ code,
394
+ message: `[WetRTC] ${code}: ${message}`,
395
+ recoverable
396
+ });
397
+ }
398
+ }
399
+ export {
400
+ WetRTC
401
+ };
402
+
403
+ //# sourceMappingURL=wetrtc.js.map