@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.
- package/README.md +156 -29
- package/dist/es/__test__/codec-preference.test.js +36 -0
- package/dist/es/__test__/codec-preference.test.js.map +1 -0
- package/dist/es/__test__/data-manager.test.js +60 -0
- package/dist/es/__test__/data-manager.test.js.map +1 -0
- package/dist/es/__test__/fsm.test.js +33 -0
- package/dist/es/__test__/fsm.test.js.map +1 -0
- package/dist/es/__test__/media-manager.test.js +41 -0
- package/dist/es/__test__/media-manager.test.js.map +1 -0
- package/dist/es/__test__/signal-manager.test.js +100 -0
- package/dist/es/__test__/signal-manager.test.js.map +1 -0
- package/dist/es/__test__/wetrtc-lifecycle.test.js +152 -0
- package/dist/es/__test__/wetrtc-lifecycle.test.js.map +1 -0
- package/dist/es/data/data-manager.d.ts +37 -0
- package/dist/es/data/data-manager.d.ts.map +1 -0
- package/dist/es/data/data-manager.js +282 -0
- package/dist/es/data/data-manager.js.map +1 -0
- package/dist/es/data/types.d.ts +34 -0
- package/dist/es/data/types.d.ts.map +1 -0
- package/dist/es/data/types.js +0 -0
- package/dist/es/disposable.d.ts +12 -0
- package/dist/es/disposable.d.ts.map +1 -0
- package/dist/es/disposable.js +36 -0
- package/dist/es/disposable.js.map +1 -0
- package/dist/es/fsm.d.ts +26 -0
- package/dist/es/fsm.d.ts.map +1 -0
- package/dist/es/fsm.js +63 -0
- package/dist/es/fsm.js.map +1 -0
- package/dist/es/index.d.ts +22 -0
- package/dist/es/index.d.ts.map +1 -0
- package/dist/es/index.js +48 -0
- package/dist/es/index.js.map +1 -0
- package/dist/es/media/audio-encoding.d.ts +10 -0
- package/dist/es/media/audio-encoding.d.ts.map +1 -0
- package/dist/es/media/audio-encoding.js +41 -0
- package/dist/es/media/audio-encoding.js.map +1 -0
- package/dist/es/media/codec-preference.d.ts +11 -0
- package/dist/es/media/codec-preference.d.ts.map +1 -0
- package/dist/es/media/codec-preference.js +77 -0
- package/dist/es/media/codec-preference.js.map +1 -0
- package/dist/es/media/encoding-utils.d.ts +2 -0
- package/dist/es/media/encoding-utils.d.ts.map +1 -0
- package/dist/es/media/encoding-utils.js +8 -0
- package/dist/es/media/encoding-utils.js.map +1 -0
- package/dist/es/media/media-manager.d.ts +39 -0
- package/dist/es/media/media-manager.d.ts.map +1 -0
- package/dist/es/media/media-manager.js +121 -0
- package/dist/es/media/media-manager.js.map +1 -0
- package/dist/es/media/types.d.ts +25 -0
- package/dist/es/media/types.d.ts.map +1 -0
- package/dist/es/media/types.js +0 -0
- package/dist/es/media/video-encoding.d.ts +12 -0
- package/dist/es/media/video-encoding.d.ts.map +1 -0
- package/dist/es/media/video-encoding.js +60 -0
- package/dist/es/media/video-encoding.js.map +1 -0
- package/dist/es/signal/signal-manager.d.ts +45 -0
- package/dist/es/signal/signal-manager.d.ts.map +1 -0
- package/dist/es/signal/signal-manager.js +250 -0
- package/dist/es/signal/signal-manager.js.map +1 -0
- package/dist/es/signal/types.d.ts +26 -0
- package/dist/es/signal/types.d.ts.map +1 -0
- package/dist/es/signal/types.js +8 -0
- package/dist/es/signal/types.js.map +1 -0
- package/dist/es/stats/stats-monitor.d.ts +32 -0
- package/dist/es/stats/stats-monitor.d.ts.map +1 -0
- package/dist/es/stats/stats-monitor.js +191 -0
- package/dist/es/stats/stats-monitor.js.map +1 -0
- package/dist/es/stats/types.d.ts +33 -0
- package/dist/es/stats/types.d.ts.map +1 -0
- package/dist/es/stats/types.js +0 -0
- package/dist/es/utils/types.d.ts +46 -0
- package/dist/es/utils/types.d.ts.map +1 -0
- package/dist/es/utils/types.js +80 -0
- package/dist/es/utils/types.js.map +1 -0
- package/dist/es/wetrtc.d.ts +92 -0
- package/dist/es/wetrtc.d.ts.map +1 -0
- package/dist/es/wetrtc.js +403 -0
- package/dist/es/wetrtc.js.map +1 -0
- package/dist/lib/__test__/codec-preference.test.js +34 -0
- package/dist/lib/__test__/codec-preference.test.js.map +1 -0
- package/dist/lib/__test__/data-manager.test.js +61 -0
- package/dist/lib/__test__/data-manager.test.js.map +1 -0
- package/dist/lib/__test__/fsm.test.js +34 -0
- package/dist/lib/__test__/fsm.test.js.map +1 -0
- package/dist/lib/__test__/media-manager.test.js +42 -0
- package/dist/lib/__test__/media-manager.test.js.map +1 -0
- package/dist/lib/__test__/signal-manager.test.js +101 -0
- package/dist/lib/__test__/signal-manager.test.js.map +1 -0
- package/dist/lib/__test__/wetrtc-lifecycle.test.js +153 -0
- package/dist/lib/__test__/wetrtc-lifecycle.test.js.map +1 -0
- package/dist/lib/data/data-manager.js +306 -0
- package/dist/lib/data/data-manager.js.map +1 -0
- package/dist/lib/data/types.js +18 -0
- package/dist/lib/data/types.js.map +1 -0
- package/dist/lib/disposable.js +60 -0
- package/dist/lib/disposable.js.map +1 -0
- package/dist/lib/fsm.js +87 -0
- package/dist/lib/fsm.js.map +1 -0
- package/dist/lib/index.js +75 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/media/audio-encoding.js +66 -0
- package/dist/lib/media/audio-encoding.js.map +1 -0
- package/dist/lib/media/codec-preference.js +106 -0
- package/dist/lib/media/codec-preference.js.map +1 -0
- package/dist/lib/media/encoding-utils.js +32 -0
- package/dist/lib/media/encoding-utils.js.map +1 -0
- package/dist/lib/media/media-manager.js +145 -0
- package/dist/lib/media/media-manager.js.map +1 -0
- package/dist/lib/media/types.js +18 -0
- package/dist/lib/media/types.js.map +1 -0
- package/dist/lib/media/video-encoding.js +87 -0
- package/dist/lib/media/video-encoding.js.map +1 -0
- package/dist/lib/signal/signal-manager.js +274 -0
- package/dist/lib/signal/signal-manager.js.map +1 -0
- package/dist/lib/signal/types.js +32 -0
- package/dist/lib/signal/types.js.map +1 -0
- package/dist/lib/stats/stats-monitor.js +215 -0
- package/dist/lib/stats/stats-monitor.js.map +1 -0
- package/dist/lib/stats/types.js +18 -0
- package/dist/lib/stats/types.js.map +1 -0
- package/dist/lib/utils/types.js +108 -0
- package/dist/lib/utils/types.js.map +1 -0
- package/dist/lib/wetrtc.js +415 -0
- package/dist/lib/wetrtc.js.map +1 -0
- package/package.json +38 -42
- package/es/core/constant.d.ts +0 -6
- package/es/core/hook.d.ts +0 -31
- package/es/core/index.d.ts +0 -39
- package/es/index.d.ts +0 -6
- package/es/index.js +0 -1
- package/es/libs/index.d.ts +0 -41
- package/es/libs/record.d.ts +0 -8
- package/lib/core/constant.d.ts +0 -6
- package/lib/core/hook.d.ts +0 -31
- package/lib/core/index.d.ts +0 -39
- package/lib/index.d.ts +0 -6
- package/lib/index.js +0 -1
- package/lib/libs/index.d.ts +0 -41
- package/lib/libs/record.d.ts +0 -8
package/dist/es/index.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { WetRTC } from "./wetrtc";
|
|
2
|
+
import { SignalManager } from "./signal/signal-manager";
|
|
3
|
+
import { MediaManager } from "./media/media-manager";
|
|
4
|
+
import { DataManager } from "./data/data-manager";
|
|
5
|
+
import { StatsMonitor } from "./stats/stats-monitor";
|
|
6
|
+
import { ConnectionStateMachine } from "./fsm";
|
|
7
|
+
import { DisposableStack } from "./disposable";
|
|
8
|
+
import {
|
|
9
|
+
applyVideoTrackContentHint,
|
|
10
|
+
applyVideoSenderEncoding,
|
|
11
|
+
applyReceiverPlayoutDelay,
|
|
12
|
+
applyVideoEncodingToConnection
|
|
13
|
+
} from "./media/video-encoding";
|
|
14
|
+
import {
|
|
15
|
+
applyAudioSenderEncoding,
|
|
16
|
+
applyAudioEncodingToConnection
|
|
17
|
+
} from "./media/audio-encoding";
|
|
18
|
+
import {
|
|
19
|
+
sortVideoCodecsH264First,
|
|
20
|
+
applyH264CodecPreference,
|
|
21
|
+
applyH264CodecPreferences,
|
|
22
|
+
sortAudioCodecsOpusFirst,
|
|
23
|
+
applyOpusCodecPreference,
|
|
24
|
+
applyOpusCodecPreferences
|
|
25
|
+
} from "./media/codec-preference";
|
|
26
|
+
export {
|
|
27
|
+
ConnectionStateMachine,
|
|
28
|
+
DataManager,
|
|
29
|
+
DisposableStack,
|
|
30
|
+
MediaManager,
|
|
31
|
+
SignalManager,
|
|
32
|
+
StatsMonitor,
|
|
33
|
+
WetRTC,
|
|
34
|
+
applyAudioEncodingToConnection,
|
|
35
|
+
applyAudioSenderEncoding,
|
|
36
|
+
applyH264CodecPreference,
|
|
37
|
+
applyH264CodecPreferences,
|
|
38
|
+
applyOpusCodecPreference,
|
|
39
|
+
applyOpusCodecPreferences,
|
|
40
|
+
applyReceiverPlayoutDelay,
|
|
41
|
+
applyVideoEncodingToConnection,
|
|
42
|
+
applyVideoSenderEncoding,
|
|
43
|
+
applyVideoTrackContentHint,
|
|
44
|
+
sortAudioCodecsOpusFirst,
|
|
45
|
+
sortVideoCodecsH264First
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"mappings":"AAAA,SAAS,cAAc;AACvB,SAAS,qBAAqB;AAC9B,SAAS,oBAAoB;AAC7B,SAAS,mBAAmB;AAC5B,SAAS,oBAAoB;AAC7B,SAAS,8BAA8B;AAEvC,SAAS,uBAAyC;AAmBlD;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP;AAAA,EACE;AAAA,EACA;AAAA,OACK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK","names":[],"ignoreList":[],"sources":["../../src/index.ts"],"sourcesContent":["export { WetRTC } from './wetrtc';\r\nexport { SignalManager } from './signal/signal-manager';\r\nexport { MediaManager } from './media/media-manager';\r\nexport { DataManager } from './data/data-manager';\r\nexport { StatsMonitor } from './stats/stats-monitor';\r\nexport { ConnectionStateMachine } from './fsm';\r\nexport type { ConnectionState } from './fsm';\r\nexport { DisposableStack, type IDisposable } from './disposable';\r\n\r\nexport type { WetRTCConfig } from './wetrtc';\r\nexport type { SignalChannel, SignalMessage } from './signal/types';\r\nexport type { SignalConfig } from './signal/signal-manager';\r\nexport type {\r\n DeviceInfo,\r\n DisplayMediaOptions,\r\n UserMediaOptions,\r\n MediaConstraints,\r\n} from './media/types';\r\nexport type { VideoEncodingOptions } from './media/video-encoding';\r\nexport type { AudioEncodingOptions } from './media/audio-encoding';\r\nexport type {\r\n PreferredVideoCodec,\r\n PreferredAudioCodec,\r\n VideoRtpCodec,\r\n AudioRtpCodec,\r\n} from './media/codec-preference';\r\nexport {\r\n applyVideoTrackContentHint,\r\n applyVideoSenderEncoding,\r\n applyReceiverPlayoutDelay,\r\n applyVideoEncodingToConnection,\r\n} from './media/video-encoding';\r\nexport {\r\n applyAudioSenderEncoding,\r\n applyAudioEncodingToConnection,\r\n} from './media/audio-encoding';\r\nexport {\r\n sortVideoCodecsH264First,\r\n applyH264CodecPreference,\r\n applyH264CodecPreferences,\r\n sortAudioCodecsOpusFirst,\r\n applyOpusCodecPreference,\r\n applyOpusCodecPreferences,\r\n} from './media/codec-preference';\r\nexport type {\r\n DataChannelOptions,\r\n DataChannelConfig,\r\n FileTransferOptions,\r\n FileMeta,\r\n} from './data/types';\r\nexport type {\r\n StatsSnapshot,\r\n DiagnosticReport,\r\n StatsMonitorOptions,\r\n} from './stats/types';\r\nexport type {\r\n WetRTCEvent,\r\n WetRTCEventMap,\r\n WetRTCError,\r\n WetRTCErrorCode,\r\n} from './utils/types';//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJtYXBwaW5ncyI6IiIsIm5hbWVzIjpbXSwiaWdub3JlTGlzdCI6W10sInNvdXJjZXMiOltdLCJzb3VyY2VzQ29udGVudCI6W119"]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface AudioEncodingOptions {
|
|
2
|
+
/** 最大码率 (bps),语音常用 32_000–64_000,音乐/系统声可更高 */
|
|
3
|
+
maxBitrate?: number;
|
|
4
|
+
priority?: RTCPriorityType;
|
|
5
|
+
networkPriority?: RTCPriorityType;
|
|
6
|
+
}
|
|
7
|
+
/** @returns 是否成功调用 setParameters(协商完成前失败属正常,可在 connected 后重试) */
|
|
8
|
+
export declare function applyAudioSenderEncoding(sender: RTCRtpSender, options: AudioEncodingOptions): Promise<boolean>;
|
|
9
|
+
export declare function applyAudioEncodingToConnection(pc: RTCPeerConnection, options: AudioEncodingOptions): Promise<boolean>;
|
|
10
|
+
//# sourceMappingURL=audio-encoding.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audio-encoding.d.ts","sourceRoot":"","sources":["../../../src/media/audio-encoding.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,oBAAoB;IACnC,8CAA8C;IAC9C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B,eAAe,CAAC,EAAE,eAAe,CAAC;CACnC;AAED,iEAAiE;AACjE,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,YAAY,EACpB,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,OAAO,CAAC,CA6BlB;AAED,wBAAsB,8BAA8B,CAClD,EAAE,EAAE,iBAAiB,EACrB,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,OAAO,CAAC,CAQlB"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { isExpectedSetParametersError } from "./encoding-utils";
|
|
2
|
+
async function applyAudioSenderEncoding(sender, options) {
|
|
3
|
+
const track = sender.track;
|
|
4
|
+
if (!track || track.kind !== "audio")
|
|
5
|
+
return false;
|
|
6
|
+
const params = sender.getParameters();
|
|
7
|
+
if (!params.encodings?.length) {
|
|
8
|
+
params.encodings = [{}];
|
|
9
|
+
}
|
|
10
|
+
const encoding = params.encodings[0];
|
|
11
|
+
if (options.maxBitrate != null) {
|
|
12
|
+
encoding.maxBitrate = options.maxBitrate;
|
|
13
|
+
}
|
|
14
|
+
if (options.priority != null) {
|
|
15
|
+
encoding.priority = options.priority;
|
|
16
|
+
}
|
|
17
|
+
if (options.networkPriority != null) {
|
|
18
|
+
encoding.networkPriority = options.networkPriority;
|
|
19
|
+
}
|
|
20
|
+
try {
|
|
21
|
+
await sender.setParameters(params);
|
|
22
|
+
return true;
|
|
23
|
+
} catch (err) {
|
|
24
|
+
if (isExpectedSetParametersError(err)) {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
throw err;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
async function applyAudioEncodingToConnection(pc, options) {
|
|
31
|
+
const results = await Promise.all(
|
|
32
|
+
pc.getSenders().filter((sender) => sender.track?.kind === "audio").map((sender) => applyAudioSenderEncoding(sender, options))
|
|
33
|
+
);
|
|
34
|
+
return results.some(Boolean);
|
|
35
|
+
}
|
|
36
|
+
export {
|
|
37
|
+
applyAudioEncodingToConnection,
|
|
38
|
+
applyAudioSenderEncoding
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
//# sourceMappingURL=audio-encoding.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"mappings":"AAAA,SAAS,oCAAoC;AAU7C,eAAsB,yBACpB,QACA,SACkB;AAClB,QAAM,QAAQ,OAAO;AACrB,MAAI,CAAC,SAAS,MAAM,SAAS;AAAS,WAAO;AAE7C,QAAM,SAAS,OAAO,cAAc;AACpC,MAAI,CAAC,OAAO,WAAW,QAAQ;AAC7B,WAAO,YAAY,CAAC,CAAC,CAAC;AAAA,EACxB;AAEA,QAAM,WAAW,OAAO,UAAU,CAAC;AACnC,MAAI,QAAQ,cAAc,MAAM;AAC9B,aAAS,aAAa,QAAQ;AAAA,EAChC;AACA,MAAI,QAAQ,YAAY,MAAM;AAC5B,aAAS,WAAW,QAAQ;AAAA,EAC9B;AACA,MAAI,QAAQ,mBAAmB,MAAM;AACnC,aAAS,kBAAkB,QAAQ;AAAA,EACrC;AAEA,MAAI;AACF,UAAM,OAAO,cAAc,MAAM;AACjC,WAAO;AAAA,EACT,SAAS,KAAK;AACZ,QAAI,6BAA6B,GAAG,GAAG;AACrC,aAAO;AAAA,IACT;AACA,UAAM;AAAA,EACR;AACF;AAEA,eAAsB,+BACpB,IACA,SACkB;AAClB,QAAM,UAAU,MAAM,QAAQ;AAAA,IAC5B,GACG,WAAW,EACX,OAAO,CAAC,WAAW,OAAO,OAAO,SAAS,OAAO,EACjD,IAAI,CAAC,WAAW,yBAAyB,QAAQ,OAAO,CAAC;AAAA,EAC9D;AACA,SAAO,QAAQ,KAAK,OAAO;AAC7B","names":[],"ignoreList":[],"sources":["../../../src/media/audio-encoding.ts"],"sourcesContent":["import { isExpectedSetParametersError } from './encoding-utils';\r\n\r\nexport interface AudioEncodingOptions {\r\n /** 最大码率 (bps),语音常用 32_000–64_000,音乐/系统声可更高 */\r\n maxBitrate?: number;\r\n priority?: RTCPriorityType;\r\n networkPriority?: RTCPriorityType;\r\n}\r\n\r\n/** @returns 是否成功调用 setParameters(协商完成前失败属正常,可在 connected 后重试) */\r\nexport async function applyAudioSenderEncoding(\r\n sender: RTCRtpSender,\r\n options: AudioEncodingOptions,\r\n): Promise<boolean> {\r\n const track = sender.track;\r\n if (!track || track.kind !== 'audio') return false;\r\n\r\n const params = sender.getParameters();\r\n if (!params.encodings?.length) {\r\n params.encodings = [{}];\r\n }\r\n\r\n const encoding = params.encodings[0];\r\n if (options.maxBitrate != null) {\r\n encoding.maxBitrate = options.maxBitrate;\r\n }\r\n if (options.priority != null) {\r\n encoding.priority = options.priority;\r\n }\r\n if (options.networkPriority != null) {\r\n encoding.networkPriority = options.networkPriority;\r\n }\r\n\r\n try {\r\n await sender.setParameters(params);\r\n return true;\r\n } catch (err) {\r\n if (isExpectedSetParametersError(err)) {\r\n return false;\r\n }\r\n throw err;\r\n }\r\n}\r\n\r\nexport async function applyAudioEncodingToConnection(\r\n pc: RTCPeerConnection,\r\n options: AudioEncodingOptions,\r\n): Promise<boolean> {\r\n const results = await Promise.all(\r\n pc\r\n .getSenders()\r\n .filter((sender) => sender.track?.kind === 'audio')\r\n .map((sender) => applyAudioSenderEncoding(sender, options)),\r\n );\r\n return results.some(Boolean);\r\n}\r\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJtYXBwaW5ncyI6IiIsIm5hbWVzIjpbXSwiaWdub3JlTGlzdCI6W10sInNvdXJjZXMiOltdLCJzb3VyY2VzQ29udGVudCI6W119"]}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type PreferredVideoCodec = 'auto' | 'h264';
|
|
2
|
+
export type PreferredAudioCodec = 'auto' | 'opus';
|
|
3
|
+
export type VideoRtpCodec = NonNullable<ReturnType<typeof RTCRtpReceiver.getCapabilities>>['codecs'][number];
|
|
4
|
+
export type AudioRtpCodec = NonNullable<ReturnType<typeof RTCRtpReceiver.getCapabilities>>['codecs'][number];
|
|
5
|
+
export declare function sortVideoCodecsH264First(codecs: VideoRtpCodec[]): VideoRtpCodec[];
|
|
6
|
+
export declare function applyH264CodecPreference(transceiver: RTCRtpTransceiver): boolean;
|
|
7
|
+
export declare function applyH264CodecPreferences(pc: RTCPeerConnection): void;
|
|
8
|
+
export declare function sortAudioCodecsOpusFirst(codecs: AudioRtpCodec[]): AudioRtpCodec[];
|
|
9
|
+
export declare function applyOpusCodecPreference(transceiver: RTCRtpTransceiver): boolean;
|
|
10
|
+
export declare function applyOpusCodecPreferences(pc: RTCPeerConnection): void;
|
|
11
|
+
//# sourceMappingURL=codec-preference.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codec-preference.d.ts","sourceRoot":"","sources":["../../../src/media/codec-preference.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,MAAM,CAAC;AAClD,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,MAAM,CAAC;AAElD,MAAM,MAAM,aAAa,GAAG,WAAW,CACrC,UAAU,CAAC,OAAO,cAAc,CAAC,eAAe,CAAC,CAClD,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC;AAEpB,MAAM,MAAM,aAAa,GAAG,WAAW,CACrC,UAAU,CAAC,OAAO,cAAc,CAAC,eAAe,CAAC,CAClD,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC;AAEpB,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,aAAa,EAAE,GAAG,aAAa,EAAE,CAmBjF;AAED,wBAAgB,wBAAwB,CAAC,WAAW,EAAE,iBAAiB,GAAG,OAAO,CAUhF;AAED,wBAAgB,yBAAyB,CAAC,EAAE,EAAE,iBAAiB,GAAG,IAAI,CAQrE;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,aAAa,EAAE,GAAG,aAAa,EAAE,CAajF;AAED,wBAAgB,wBAAwB,CAAC,WAAW,EAAE,iBAAiB,GAAG,OAAO,CAUhF;AAED,wBAAgB,yBAAyB,CAAC,EAAE,EAAE,iBAAiB,GAAG,IAAI,CAQrE"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
function sortVideoCodecsH264First(codecs) {
|
|
2
|
+
const h264 = [];
|
|
3
|
+
const rest = [];
|
|
4
|
+
for (const codec of codecs) {
|
|
5
|
+
if (codec.mimeType.toLowerCase() === "video/h264") {
|
|
6
|
+
h264.push(codec);
|
|
7
|
+
} else {
|
|
8
|
+
rest.push(codec);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
h264.sort((a, b) => {
|
|
12
|
+
const aPkt = a.sdpFmtpLine?.includes("packetization-mode=1") ? 0 : 1;
|
|
13
|
+
const bPkt = b.sdpFmtpLine?.includes("packetization-mode=1") ? 0 : 1;
|
|
14
|
+
return aPkt - bPkt;
|
|
15
|
+
});
|
|
16
|
+
return [...h264, ...rest];
|
|
17
|
+
}
|
|
18
|
+
function applyH264CodecPreference(transceiver) {
|
|
19
|
+
const caps = RTCRtpReceiver.getCapabilities("video");
|
|
20
|
+
if (!caps?.codecs?.length)
|
|
21
|
+
return false;
|
|
22
|
+
try {
|
|
23
|
+
transceiver.setCodecPreferences(sortVideoCodecsH264First(caps.codecs));
|
|
24
|
+
return true;
|
|
25
|
+
} catch {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function applyH264CodecPreferences(pc) {
|
|
30
|
+
for (const transceiver of pc.getTransceivers()) {
|
|
31
|
+
const kind = transceiver.sender.track?.kind ?? transceiver.receiver.track?.kind;
|
|
32
|
+
if (kind === "video") {
|
|
33
|
+
applyH264CodecPreference(transceiver);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function sortAudioCodecsOpusFirst(codecs) {
|
|
38
|
+
const opus = [];
|
|
39
|
+
const rest = [];
|
|
40
|
+
for (const codec of codecs) {
|
|
41
|
+
if (codec.mimeType.toLowerCase() === "audio/opus") {
|
|
42
|
+
opus.push(codec);
|
|
43
|
+
} else {
|
|
44
|
+
rest.push(codec);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return [...opus, ...rest];
|
|
48
|
+
}
|
|
49
|
+
function applyOpusCodecPreference(transceiver) {
|
|
50
|
+
const caps = RTCRtpReceiver.getCapabilities("audio");
|
|
51
|
+
if (!caps?.codecs?.length)
|
|
52
|
+
return false;
|
|
53
|
+
try {
|
|
54
|
+
transceiver.setCodecPreferences(sortAudioCodecsOpusFirst(caps.codecs));
|
|
55
|
+
return true;
|
|
56
|
+
} catch {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function applyOpusCodecPreferences(pc) {
|
|
61
|
+
for (const transceiver of pc.getTransceivers()) {
|
|
62
|
+
const kind = transceiver.sender.track?.kind ?? transceiver.receiver.track?.kind;
|
|
63
|
+
if (kind === "audio") {
|
|
64
|
+
applyOpusCodecPreference(transceiver);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
export {
|
|
69
|
+
applyH264CodecPreference,
|
|
70
|
+
applyH264CodecPreferences,
|
|
71
|
+
applyOpusCodecPreference,
|
|
72
|
+
applyOpusCodecPreferences,
|
|
73
|
+
sortAudioCodecsOpusFirst,
|
|
74
|
+
sortVideoCodecsH264First
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
//# sourceMappingURL=codec-preference.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"mappings":"AAWO,SAAS,yBAAyB,QAA0C;AACjF,QAAM,OAAwB,CAAC;AAC/B,QAAM,OAAwB,CAAC;AAE/B,aAAW,SAAS,QAAQ;AAC1B,QAAI,MAAM,SAAS,YAAY,MAAM,cAAc;AACjD,WAAK,KAAK,KAAK;AAAA,IACjB,OAAO;AACL,WAAK,KAAK,KAAK;AAAA,IACjB;AAAA,EACF;AAEA,OAAK,KAAK,CAAC,GAAG,MAAM;AAClB,UAAM,OAAO,EAAE,aAAa,SAAS,sBAAsB,IAAI,IAAI;AACnE,UAAM,OAAO,EAAE,aAAa,SAAS,sBAAsB,IAAI,IAAI;AACnE,WAAO,OAAO;AAAA,EAChB,CAAC;AAED,SAAO,CAAC,GAAG,MAAM,GAAG,IAAI;AAC1B;AAEO,SAAS,yBAAyB,aAAyC;AAChF,QAAM,OAAO,eAAe,gBAAgB,OAAO;AACnD,MAAI,CAAC,MAAM,QAAQ;AAAQ,WAAO;AAElC,MAAI;AACF,gBAAY,oBAAoB,yBAAyB,KAAK,MAAM,CAAC;AACrE,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,0BAA0B,IAA6B;AACrE,aAAW,eAAe,GAAG,gBAAgB,GAAG;AAC9C,UAAM,OACJ,YAAY,OAAO,OAAO,QAAQ,YAAY,SAAS,OAAO;AAChE,QAAI,SAAS,SAAS;AACpB,+BAAyB,WAAW;AAAA,IACtC;AAAA,EACF;AACF;AAEO,SAAS,yBAAyB,QAA0C;AACjF,QAAM,OAAwB,CAAC;AAC/B,QAAM,OAAwB,CAAC;AAE/B,aAAW,SAAS,QAAQ;AAC1B,QAAI,MAAM,SAAS,YAAY,MAAM,cAAc;AACjD,WAAK,KAAK,KAAK;AAAA,IACjB,OAAO;AACL,WAAK,KAAK,KAAK;AAAA,IACjB;AAAA,EACF;AAEA,SAAO,CAAC,GAAG,MAAM,GAAG,IAAI;AAC1B;AAEO,SAAS,yBAAyB,aAAyC;AAChF,QAAM,OAAO,eAAe,gBAAgB,OAAO;AACnD,MAAI,CAAC,MAAM,QAAQ;AAAQ,WAAO;AAElC,MAAI;AACF,gBAAY,oBAAoB,yBAAyB,KAAK,MAAM,CAAC;AACrE,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,0BAA0B,IAA6B;AACrE,aAAW,eAAe,GAAG,gBAAgB,GAAG;AAC9C,UAAM,OACJ,YAAY,OAAO,OAAO,QAAQ,YAAY,SAAS,OAAO;AAChE,QAAI,SAAS,SAAS;AACpB,+BAAyB,WAAW;AAAA,IACtC;AAAA,EACF;AACF","names":[],"ignoreList":[],"sources":["../../../src/media/codec-preference.ts"],"sourcesContent":["export type PreferredVideoCodec = 'auto' | 'h264';\r\nexport type PreferredAudioCodec = 'auto' | 'opus';\r\n\r\nexport type VideoRtpCodec = NonNullable<\r\n ReturnType<typeof RTCRtpReceiver.getCapabilities>\r\n>['codecs'][number];\r\n\r\nexport type AudioRtpCodec = NonNullable<\r\n ReturnType<typeof RTCRtpReceiver.getCapabilities>\r\n>['codecs'][number];\r\n\r\nexport function sortVideoCodecsH264First(codecs: VideoRtpCodec[]): VideoRtpCodec[] {\r\n const h264: VideoRtpCodec[] = [];\r\n const rest: VideoRtpCodec[] = [];\r\n\r\n for (const codec of codecs) {\r\n if (codec.mimeType.toLowerCase() === 'video/h264') {\r\n h264.push(codec);\r\n } else {\r\n rest.push(codec);\r\n }\r\n }\r\n\r\n h264.sort((a, b) => {\r\n const aPkt = a.sdpFmtpLine?.includes('packetization-mode=1') ? 0 : 1;\r\n const bPkt = b.sdpFmtpLine?.includes('packetization-mode=1') ? 0 : 1;\r\n return aPkt - bPkt;\r\n });\r\n\r\n return [...h264, ...rest];\r\n}\r\n\r\nexport function applyH264CodecPreference(transceiver: RTCRtpTransceiver): boolean {\r\n const caps = RTCRtpReceiver.getCapabilities('video');\r\n if (!caps?.codecs?.length) return false;\r\n\r\n try {\r\n transceiver.setCodecPreferences(sortVideoCodecsH264First(caps.codecs));\r\n return true;\r\n } catch {\r\n return false;\r\n }\r\n}\r\n\r\nexport function applyH264CodecPreferences(pc: RTCPeerConnection): void {\r\n for (const transceiver of pc.getTransceivers()) {\r\n const kind =\r\n transceiver.sender.track?.kind ?? transceiver.receiver.track?.kind;\r\n if (kind === 'video') {\r\n applyH264CodecPreference(transceiver);\r\n }\r\n }\r\n}\r\n\r\nexport function sortAudioCodecsOpusFirst(codecs: AudioRtpCodec[]): AudioRtpCodec[] {\r\n const opus: AudioRtpCodec[] = [];\r\n const rest: AudioRtpCodec[] = [];\r\n\r\n for (const codec of codecs) {\r\n if (codec.mimeType.toLowerCase() === 'audio/opus') {\r\n opus.push(codec);\r\n } else {\r\n rest.push(codec);\r\n }\r\n }\r\n\r\n return [...opus, ...rest];\r\n}\r\n\r\nexport function applyOpusCodecPreference(transceiver: RTCRtpTransceiver): boolean {\r\n const caps = RTCRtpReceiver.getCapabilities('audio');\r\n if (!caps?.codecs?.length) return false;\r\n\r\n try {\r\n transceiver.setCodecPreferences(sortAudioCodecsOpusFirst(caps.codecs));\r\n return true;\r\n } catch {\r\n return false;\r\n }\r\n}\r\n\r\nexport function applyOpusCodecPreferences(pc: RTCPeerConnection): void {\r\n for (const transceiver of pc.getTransceivers()) {\r\n const kind =\r\n transceiver.sender.track?.kind ?? transceiver.receiver.track?.kind;\r\n if (kind === 'audio') {\r\n applyOpusCodecPreference(transceiver);\r\n }\r\n }\r\n}\r\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"encoding-utils.d.ts","sourceRoot":"","sources":["../../../src/media/encoding-utils.ts"],"names":[],"mappings":"AAAA,wBAAgB,4BAA4B,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAKlE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"mappings":"AAAO,SAAS,6BAA6B,KAAuB;AAClE,SACE,eAAe,iBACd,IAAI,SAAS,8BAA8B,IAAI,SAAS;AAE7D","names":[],"ignoreList":[],"sources":["../../../src/media/encoding-utils.ts"],"sourcesContent":["export function isExpectedSetParametersError(err: unknown): boolean {\r\n return (\r\n err instanceof DOMException &&\r\n (err.name === 'InvalidModificationError' || err.name === 'InvalidStateError')\r\n );\r\n}\r\n"]}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { IDisposable } from '../disposable';
|
|
2
|
+
import type { DisplayMediaOptions, UserMediaOptions } from './types';
|
|
3
|
+
export type { DisplayMediaOptions, UserMediaOptions } from './types';
|
|
4
|
+
interface DeviceInfo {
|
|
5
|
+
deviceId: string;
|
|
6
|
+
groupId: string;
|
|
7
|
+
kind: MediaDeviceKind;
|
|
8
|
+
label: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* 媒体管理器 — 设备枚举、媒体流获取、track 操作
|
|
12
|
+
*/
|
|
13
|
+
export declare class MediaManager implements IDisposable {
|
|
14
|
+
private logger;
|
|
15
|
+
private localStream;
|
|
16
|
+
private remoteStreams;
|
|
17
|
+
private tracks;
|
|
18
|
+
constructor();
|
|
19
|
+
enumerateDevices(kind?: MediaDeviceKind): Promise<DeviceInfo[]>;
|
|
20
|
+
getUserMedia(constraints: UserMediaOptions): Promise<MediaStream>;
|
|
21
|
+
getDisplayMedia(options?: DisplayMediaOptions): Promise<MediaStream>;
|
|
22
|
+
/** 枚举设备 */
|
|
23
|
+
static enumerateDevices(kind?: MediaDeviceKind): Promise<DeviceInfo[]>;
|
|
24
|
+
/** 获取用户媒体(摄像头/麦克风) */
|
|
25
|
+
static getUserMedia(constraints: UserMediaOptions): Promise<MediaStream>;
|
|
26
|
+
/** 获取屏幕流(支持 Electron desktopCapturer sourceId) */
|
|
27
|
+
static getDisplayMedia(options?: DisplayMediaOptions): Promise<MediaStream>;
|
|
28
|
+
setLocalStream(stream: MediaStream): void;
|
|
29
|
+
getLocalStream(): MediaStream | null;
|
|
30
|
+
addTrack(track: MediaStreamTrack, stream: MediaStream): void;
|
|
31
|
+
removeTrack(track: MediaStreamTrack): void;
|
|
32
|
+
replaceTrack(oldTrack: MediaStreamTrack, newTrack: MediaStreamTrack): void;
|
|
33
|
+
getLocalTracks(kind?: 'audio' | 'video'): MediaStreamTrack[];
|
|
34
|
+
addRemoteStream(stream: MediaStream): void;
|
|
35
|
+
getRemoteStreams(): ReadonlyMap<string, MediaStream>;
|
|
36
|
+
dispose(): void;
|
|
37
|
+
}
|
|
38
|
+
export type { DeviceInfo };
|
|
39
|
+
//# sourceMappingURL=media-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media-manager.d.ts","sourceRoot":"","sources":["../../../src/media/media-manager.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,KAAK,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAErE,YAAY,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAErE,UAAU,UAAU;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,eAAe,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,qBAAa,YAAa,YAAW,WAAW;IAC9C,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,WAAW,CAA4B;IAC/C,OAAO,CAAC,aAAa,CAAkC;IACvD,OAAO,CAAC,MAAM,CAAuC;;IAQrD,gBAAgB,CAAC,IAAI,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;IAI/D,YAAY,CAAC,WAAW,EAAE,gBAAgB,GAAG,OAAO,CAAC,WAAW,CAAC;IAIjE,eAAe,CAAC,OAAO,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,WAAW,CAAC;IAMpE,WAAW;WACE,gBAAgB,CAAC,IAAI,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;IAc5E,sBAAsB;WACT,YAAY,CACvB,WAAW,EAAE,gBAAgB,GAC5B,OAAO,CAAC,WAAW,CAAC;IAIvB,kDAAkD;WACrC,eAAe,CAC1B,OAAO,CAAC,EAAE,mBAAmB,GAC5B,OAAO,CAAC,WAAW,CAAC;IA8BvB,cAAc,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI;IAOzC,cAAc,IAAI,WAAW,GAAG,IAAI;IAIpC,QAAQ,CAAC,KAAK,EAAE,gBAAgB,EAAE,MAAM,EAAE,WAAW,GAAG,IAAI;IAM5D,WAAW,CAAC,KAAK,EAAE,gBAAgB,GAAG,IAAI;IAM1C,YAAY,CACV,QAAQ,EAAE,gBAAgB,EAC1B,QAAQ,EAAE,gBAAgB,GACzB,IAAI;IAQP,cAAc,CAAC,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,GAAG,gBAAgB,EAAE;IAQ5D,eAAe,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI;IAI1C,gBAAgB,IAAI,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC;IAIpD,OAAO,IAAI,IAAI;CAShB;AAED,YAAY,EAAE,UAAU,EAAE,CAAC"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { createLogger } from "../utils/types";
|
|
2
|
+
class MediaManager {
|
|
3
|
+
constructor() {
|
|
4
|
+
this.localStream = null;
|
|
5
|
+
this.remoteStreams = /* @__PURE__ */ new Map();
|
|
6
|
+
this.tracks = /* @__PURE__ */ new Map();
|
|
7
|
+
this.logger = createLogger();
|
|
8
|
+
}
|
|
9
|
+
// ── 实例方法(委托 static,与文档 rtc.media.* 一致) ──
|
|
10
|
+
enumerateDevices(kind) {
|
|
11
|
+
return MediaManager.enumerateDevices(kind);
|
|
12
|
+
}
|
|
13
|
+
getUserMedia(constraints) {
|
|
14
|
+
return MediaManager.getUserMedia(constraints);
|
|
15
|
+
}
|
|
16
|
+
getDisplayMedia(options) {
|
|
17
|
+
return MediaManager.getDisplayMedia(options);
|
|
18
|
+
}
|
|
19
|
+
// ── 静态工具 ──
|
|
20
|
+
/** 枚举设备 */
|
|
21
|
+
static async enumerateDevices(kind) {
|
|
22
|
+
const devices = await navigator.mediaDevices.enumerateDevices();
|
|
23
|
+
let filtered = devices;
|
|
24
|
+
if (kind) {
|
|
25
|
+
filtered = devices.filter((d) => d.kind === kind);
|
|
26
|
+
}
|
|
27
|
+
return filtered.map((d) => ({
|
|
28
|
+
deviceId: d.deviceId,
|
|
29
|
+
groupId: d.groupId,
|
|
30
|
+
kind: d.kind,
|
|
31
|
+
label: d.label || `Unknown ${d.kind}`
|
|
32
|
+
}));
|
|
33
|
+
}
|
|
34
|
+
/** 获取用户媒体(摄像头/麦克风) */
|
|
35
|
+
static async getUserMedia(constraints) {
|
|
36
|
+
return navigator.mediaDevices.getUserMedia(constraints);
|
|
37
|
+
}
|
|
38
|
+
/** 获取屏幕流(支持 Electron desktopCapturer sourceId) */
|
|
39
|
+
static async getDisplayMedia(options) {
|
|
40
|
+
const baseConstraints = {
|
|
41
|
+
video: true,
|
|
42
|
+
audio: options?.audio ?? false
|
|
43
|
+
};
|
|
44
|
+
if (options?.sourceId) {
|
|
45
|
+
baseConstraints.video = {
|
|
46
|
+
// @ts-expect-error Electron desktopCapturer constraints
|
|
47
|
+
mandatory: {
|
|
48
|
+
chromeMediaSource: "desktop",
|
|
49
|
+
chromeMediaSourceId: options.sourceId
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
} else if (options?.width || options?.height || options?.frameRate) {
|
|
53
|
+
const video = {};
|
|
54
|
+
if (options.width)
|
|
55
|
+
video.width = options.width;
|
|
56
|
+
if (options.height)
|
|
57
|
+
video.height = options.height;
|
|
58
|
+
if (options.frameRate)
|
|
59
|
+
video.frameRate = options.frameRate;
|
|
60
|
+
baseConstraints.video = video;
|
|
61
|
+
}
|
|
62
|
+
if (navigator.mediaDevices.getDisplayMedia) {
|
|
63
|
+
return navigator.mediaDevices.getDisplayMedia(baseConstraints);
|
|
64
|
+
}
|
|
65
|
+
return navigator.mediaDevices.getUserMedia(baseConstraints);
|
|
66
|
+
}
|
|
67
|
+
// ── 本地流管理 ──
|
|
68
|
+
setLocalStream(stream) {
|
|
69
|
+
this.localStream = stream;
|
|
70
|
+
for (const track of stream.getTracks()) {
|
|
71
|
+
this.tracks.set(track.id, track);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
getLocalStream() {
|
|
75
|
+
return this.localStream;
|
|
76
|
+
}
|
|
77
|
+
addTrack(track, stream) {
|
|
78
|
+
stream.addTrack(track);
|
|
79
|
+
this.localStream = stream;
|
|
80
|
+
this.tracks.set(track.id, track);
|
|
81
|
+
}
|
|
82
|
+
removeTrack(track) {
|
|
83
|
+
track.stop();
|
|
84
|
+
this.tracks.delete(track.id);
|
|
85
|
+
this.localStream?.removeTrack(track);
|
|
86
|
+
}
|
|
87
|
+
replaceTrack(oldTrack, newTrack) {
|
|
88
|
+
oldTrack.stop();
|
|
89
|
+
this.tracks.delete(oldTrack.id);
|
|
90
|
+
this.tracks.set(newTrack.id, newTrack);
|
|
91
|
+
this.localStream?.removeTrack(oldTrack);
|
|
92
|
+
this.localStream?.addTrack(newTrack);
|
|
93
|
+
}
|
|
94
|
+
getLocalTracks(kind) {
|
|
95
|
+
const tracks = Array.from(this.tracks.values());
|
|
96
|
+
if (!kind)
|
|
97
|
+
return tracks;
|
|
98
|
+
return tracks.filter((t) => t.kind === kind);
|
|
99
|
+
}
|
|
100
|
+
// ── 远程流管理 ──
|
|
101
|
+
addRemoteStream(stream) {
|
|
102
|
+
this.remoteStreams.set(stream.id, stream);
|
|
103
|
+
}
|
|
104
|
+
getRemoteStreams() {
|
|
105
|
+
return this.remoteStreams;
|
|
106
|
+
}
|
|
107
|
+
dispose() {
|
|
108
|
+
for (const track of this.tracks.values()) {
|
|
109
|
+
track.stop();
|
|
110
|
+
}
|
|
111
|
+
this.tracks.clear();
|
|
112
|
+
this.localStream = null;
|
|
113
|
+
this.remoteStreams.clear();
|
|
114
|
+
this.logger.info("MediaManager disposed");
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
export {
|
|
118
|
+
MediaManager
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
//# sourceMappingURL=media-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"mappings":"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","names":[],"ignoreList":[],"sources":["../../../src/media/media-manager.ts"],"sourcesContent":["import { createLogger, type Logger } from '../utils/types';\r\nimport type { IDisposable } from '../disposable';\r\nimport type { DisplayMediaOptions, UserMediaOptions } from './types';\r\n\r\nexport type { DisplayMediaOptions, UserMediaOptions } from './types';\r\n\r\ninterface DeviceInfo {\r\n deviceId: string;\r\n groupId: string;\r\n kind: MediaDeviceKind;\r\n label: string;\r\n}\r\n\r\n/**\r\n * 媒体管理器 — 设备枚举、媒体流获取、track 操作\r\n */\r\nexport class MediaManager implements IDisposable {\r\n private logger: Logger;\r\n private localStream: MediaStream | null = null;\r\n private remoteStreams = new Map<string, MediaStream>();\r\n private tracks = new Map<string, MediaStreamTrack>();\r\n\r\n constructor() {\r\n this.logger = createLogger();\r\n }\r\n\r\n // ── 实例方法(委托 static,与文档 rtc.media.* 一致) ──\r\n\r\n enumerateDevices(kind?: MediaDeviceKind): Promise<DeviceInfo[]> {\r\n return MediaManager.enumerateDevices(kind);\r\n }\r\n\r\n getUserMedia(constraints: UserMediaOptions): Promise<MediaStream> {\r\n return MediaManager.getUserMedia(constraints);\r\n }\r\n\r\n getDisplayMedia(options?: DisplayMediaOptions): Promise<MediaStream> {\r\n return MediaManager.getDisplayMedia(options);\r\n }\r\n\r\n // ── 静态工具 ──\r\n\r\n /** 枚举设备 */\r\n static async enumerateDevices(kind?: MediaDeviceKind): Promise<DeviceInfo[]> {\r\n const devices = await navigator.mediaDevices.enumerateDevices();\r\n let filtered = devices;\r\n if (kind) {\r\n filtered = devices.filter(d => d.kind === kind);\r\n }\r\n return filtered.map(d => ({\r\n deviceId: d.deviceId,\r\n groupId: d.groupId,\r\n kind: d.kind,\r\n label: d.label || `Unknown ${d.kind}`,\r\n }));\r\n }\r\n\r\n /** 获取用户媒体(摄像头/麦克风) */\r\n static async getUserMedia(\r\n constraints: UserMediaOptions,\r\n ): Promise<MediaStream> {\r\n return navigator.mediaDevices.getUserMedia(constraints);\r\n }\r\n\r\n /** 获取屏幕流(支持 Electron desktopCapturer sourceId) */\r\n static async getDisplayMedia(\r\n options?: DisplayMediaOptions,\r\n ): Promise<MediaStream> {\r\n const baseConstraints: DisplayMediaStreamOptions = {\r\n video: true,\r\n audio: options?.audio ?? false,\r\n };\r\n\r\n if (options?.sourceId) {\r\n baseConstraints.video = {\r\n // @ts-expect-error Electron desktopCapturer constraints\r\n mandatory: {\r\n chromeMediaSource: 'desktop',\r\n chromeMediaSourceId: options.sourceId,\r\n },\r\n };\r\n } else if (options?.width || options?.height || options?.frameRate) {\r\n const video: MediaTrackConstraints = {};\r\n if (options.width) video.width = options.width;\r\n if (options.height) video.height = options.height;\r\n if (options.frameRate) video.frameRate = options.frameRate;\r\n baseConstraints.video = video;\r\n }\r\n\r\n if (navigator.mediaDevices.getDisplayMedia) {\r\n return navigator.mediaDevices.getDisplayMedia(baseConstraints);\r\n }\r\n return navigator.mediaDevices.getUserMedia(baseConstraints as MediaStreamConstraints);\r\n }\r\n\r\n // ── 本地流管理 ──\r\n\r\n setLocalStream(stream: MediaStream): void {\r\n this.localStream = stream;\r\n for (const track of stream.getTracks()) {\r\n this.tracks.set(track.id, track);\r\n }\r\n }\r\n\r\n getLocalStream(): MediaStream | null {\r\n return this.localStream;\r\n }\r\n\r\n addTrack(track: MediaStreamTrack, stream: MediaStream): void {\r\n stream.addTrack(track);\r\n this.localStream = stream;\r\n this.tracks.set(track.id, track);\r\n }\r\n\r\n removeTrack(track: MediaStreamTrack): void {\r\n track.stop();\r\n this.tracks.delete(track.id);\r\n this.localStream?.removeTrack(track);\r\n }\r\n\r\n replaceTrack(\r\n oldTrack: MediaStreamTrack,\r\n newTrack: MediaStreamTrack,\r\n ): void {\r\n oldTrack.stop();\r\n this.tracks.delete(oldTrack.id);\r\n this.tracks.set(newTrack.id, newTrack);\r\n this.localStream?.removeTrack(oldTrack);\r\n this.localStream?.addTrack(newTrack);\r\n }\r\n\r\n getLocalTracks(kind?: 'audio' | 'video'): MediaStreamTrack[] {\r\n const tracks = Array.from(this.tracks.values());\r\n if (!kind) return tracks;\r\n return tracks.filter(t => t.kind === kind);\r\n }\r\n\r\n // ── 远程流管理 ──\r\n\r\n addRemoteStream(stream: MediaStream): void {\r\n this.remoteStreams.set(stream.id, stream);\r\n }\r\n\r\n getRemoteStreams(): ReadonlyMap<string, MediaStream> {\r\n return this.remoteStreams;\r\n }\r\n\r\n dispose(): void {\r\n for (const track of this.tracks.values()) {\r\n track.stop();\r\n }\r\n this.tracks.clear();\r\n this.localStream = null;\r\n this.remoteStreams.clear();\r\n this.logger.info('MediaManager disposed');\r\n }\r\n}\r\n\r\nexport type { DeviceInfo };\r\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJtYXBwaW5ncyI6IiIsIm5hbWVzIjpbXSwiaWdub3JlTGlzdCI6W10sInNvdXJjZXMiOltdLCJzb3VyY2VzQ29udGVudCI6W119"]}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export interface DeviceInfo {
|
|
2
|
+
deviceId: string;
|
|
3
|
+
groupId: string;
|
|
4
|
+
kind: MediaDeviceKind;
|
|
5
|
+
label: string;
|
|
6
|
+
}
|
|
7
|
+
export interface DisplayMediaOptions {
|
|
8
|
+
/** Electron desktopCapturer sourceId */
|
|
9
|
+
sourceId?: string;
|
|
10
|
+
/** 是否捕获系统音频 */
|
|
11
|
+
audio?: boolean;
|
|
12
|
+
/** 分辨率约束 */
|
|
13
|
+
width?: number;
|
|
14
|
+
height?: number;
|
|
15
|
+
frameRate?: number;
|
|
16
|
+
}
|
|
17
|
+
export interface UserMediaOptions {
|
|
18
|
+
audio?: boolean | MediaTrackConstraints;
|
|
19
|
+
video?: boolean | MediaTrackConstraints;
|
|
20
|
+
}
|
|
21
|
+
export interface MediaConstraints {
|
|
22
|
+
audio?: boolean | MediaTrackConstraints;
|
|
23
|
+
video?: boolean | MediaTrackConstraints;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/media/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,eAAe,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,mBAAmB;IAClC,wCAAwC;IACxC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe;IACf,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,YAAY;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,EAAE,OAAO,GAAG,qBAAqB,CAAC;IACxC,KAAK,CAAC,EAAE,OAAO,GAAG,qBAAqB,CAAC;CACzC;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,EAAE,OAAO,GAAG,qBAAqB,CAAC;IACxC,KAAK,CAAC,EAAE,OAAO,GAAG,qBAAqB,CAAC;CACzC"}
|
|
File without changes
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface VideoEncodingOptions {
|
|
2
|
+
maxFrameRate?: number;
|
|
3
|
+
maxBitrate?: number;
|
|
4
|
+
degradationPreference?: RTCDegradationPreference;
|
|
5
|
+
contentHint?: 'motion' | 'detail' | 'text';
|
|
6
|
+
}
|
|
7
|
+
export declare function applyVideoTrackContentHint(track: MediaStreamTrack, contentHint?: VideoEncodingOptions['contentHint']): void;
|
|
8
|
+
/** @returns 是否成功调用 setParameters(协商完成前失败属正常,可在 connected 后重试) */
|
|
9
|
+
export declare function applyVideoSenderEncoding(sender: RTCRtpSender, options: VideoEncodingOptions): Promise<boolean>;
|
|
10
|
+
export declare function applyReceiverPlayoutDelay(pc: RTCPeerConnection, delaySeconds?: number): void;
|
|
11
|
+
export declare function applyVideoEncodingToConnection(pc: RTCPeerConnection, options: VideoEncodingOptions): Promise<boolean>;
|
|
12
|
+
//# sourceMappingURL=video-encoding.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"video-encoding.d.ts","sourceRoot":"","sources":["../../../src/media/video-encoding.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,oBAAoB;IACnC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qBAAqB,CAAC,EAAE,wBAAwB,CAAC;IACjD,WAAW,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAAC;CAC5C;AAED,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,gBAAgB,EACvB,WAAW,GAAE,oBAAoB,CAAC,aAAa,CAAY,GAC1D,IAAI,CAIN;AAED,iEAAiE;AACjE,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,YAAY,EACpB,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,OAAO,CAAC,CAiClB;AAED,wBAAgB,yBAAyB,CACvC,EAAE,EAAE,iBAAiB,EACrB,YAAY,SAAI,GACf,IAAI,CAON;AAED,wBAAsB,8BAA8B,CAClD,EAAE,EAAE,iBAAiB,EACrB,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,OAAO,CAAC,CAQlB"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { isExpectedSetParametersError } from "./encoding-utils";
|
|
2
|
+
function applyVideoTrackContentHint(track, contentHint = "motion") {
|
|
3
|
+
if (track.kind !== "video")
|
|
4
|
+
return;
|
|
5
|
+
if (!("contentHint" in track) || contentHint == null)
|
|
6
|
+
return;
|
|
7
|
+
track.contentHint = contentHint;
|
|
8
|
+
}
|
|
9
|
+
async function applyVideoSenderEncoding(sender, options) {
|
|
10
|
+
const track = sender.track;
|
|
11
|
+
if (!track || track.kind !== "video")
|
|
12
|
+
return false;
|
|
13
|
+
if (options.contentHint) {
|
|
14
|
+
applyVideoTrackContentHint(track, options.contentHint);
|
|
15
|
+
}
|
|
16
|
+
const params = sender.getParameters();
|
|
17
|
+
if (!params.encodings?.length) {
|
|
18
|
+
params.encodings = [{}];
|
|
19
|
+
}
|
|
20
|
+
const encoding = params.encodings[0];
|
|
21
|
+
if (options.maxFrameRate != null) {
|
|
22
|
+
encoding.maxFramerate = options.maxFrameRate;
|
|
23
|
+
}
|
|
24
|
+
if (options.maxBitrate != null) {
|
|
25
|
+
encoding.maxBitrate = options.maxBitrate;
|
|
26
|
+
}
|
|
27
|
+
if (options.degradationPreference) {
|
|
28
|
+
params.degradationPreference = options.degradationPreference;
|
|
29
|
+
}
|
|
30
|
+
try {
|
|
31
|
+
await sender.setParameters(params);
|
|
32
|
+
return true;
|
|
33
|
+
} catch (err) {
|
|
34
|
+
if (isExpectedSetParametersError(err)) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
throw err;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function applyReceiverPlayoutDelay(pc, delaySeconds = 0) {
|
|
41
|
+
for (const receiver of pc.getReceivers()) {
|
|
42
|
+
if (!("playoutDelayHint" in receiver))
|
|
43
|
+
continue;
|
|
44
|
+
receiver.playoutDelayHint = delaySeconds;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
async function applyVideoEncodingToConnection(pc, options) {
|
|
48
|
+
const results = await Promise.all(
|
|
49
|
+
pc.getSenders().filter((sender) => sender.track?.kind === "video").map((sender) => applyVideoSenderEncoding(sender, options))
|
|
50
|
+
);
|
|
51
|
+
return results.some(Boolean);
|
|
52
|
+
}
|
|
53
|
+
export {
|
|
54
|
+
applyReceiverPlayoutDelay,
|
|
55
|
+
applyVideoEncodingToConnection,
|
|
56
|
+
applyVideoSenderEncoding,
|
|
57
|
+
applyVideoTrackContentHint
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
//# sourceMappingURL=video-encoding.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"mappings":"AAAA,SAAS,oCAAoC;AAStC,SAAS,2BACd,OACA,cAAmD,UAC7C;AACN,MAAI,MAAM,SAAS;AAAS;AAC5B,MAAI,EAAE,iBAAiB,UAAU,eAAe;AAAM;AACtD,QAAM,cAAc;AACtB;AAGA,eAAsB,yBACpB,QACA,SACkB;AAClB,QAAM,QAAQ,OAAO;AACrB,MAAI,CAAC,SAAS,MAAM,SAAS;AAAS,WAAO;AAE7C,MAAI,QAAQ,aAAa;AACvB,+BAA2B,OAAO,QAAQ,WAAW;AAAA,EACvD;AAEA,QAAM,SAAS,OAAO,cAAc;AACpC,MAAI,CAAC,OAAO,WAAW,QAAQ;AAC7B,WAAO,YAAY,CAAC,CAAC,CAAC;AAAA,EACxB;AAEA,QAAM,WAAW,OAAO,UAAU,CAAC;AACnC,MAAI,QAAQ,gBAAgB,MAAM;AAChC,aAAS,eAAe,QAAQ;AAAA,EAClC;AACA,MAAI,QAAQ,cAAc,MAAM;AAC9B,aAAS,aAAa,QAAQ;AAAA,EAChC;AACA,MAAI,QAAQ,uBAAuB;AACjC,WAAO,wBAAwB,QAAQ;AAAA,EACzC;AAEA,MAAI;AACF,UAAM,OAAO,cAAc,MAAM;AACjC,WAAO;AAAA,EACT,SAAS,KAAK;AACZ,QAAI,6BAA6B,GAAG,GAAG;AACrC,aAAO;AAAA,IACT;AACA,UAAM;AAAA,EACR;AACF;AAEO,SAAS,0BACd,IACA,eAAe,GACT;AACN,aAAW,YAAY,GAAG,aAAa,GAAG;AACxC,QAAI,EAAE,sBAAsB;AAAW;AAEvC,IAAC,SAA2D,mBAC1D;AAAA,EACJ;AACF;AAEA,eAAsB,+BACpB,IACA,SACkB;AAClB,QAAM,UAAU,MAAM,QAAQ;AAAA,IAC5B,GACG,WAAW,EACX,OAAO,CAAC,WAAW,OAAO,OAAO,SAAS,OAAO,EACjD,IAAI,CAAC,WAAW,yBAAyB,QAAQ,OAAO,CAAC;AAAA,EAC9D;AACA,SAAO,QAAQ,KAAK,OAAO;AAC7B","names":[],"ignoreList":[],"sources":["../../../src/media/video-encoding.ts"],"sourcesContent":["import { isExpectedSetParametersError } from './encoding-utils';\r\n\r\nexport interface VideoEncodingOptions {\r\n maxFrameRate?: number;\r\n maxBitrate?: number;\r\n degradationPreference?: RTCDegradationPreference;\r\n contentHint?: 'motion' | 'detail' | 'text';\r\n}\r\n\r\nexport function applyVideoTrackContentHint(\r\n track: MediaStreamTrack,\r\n contentHint: VideoEncodingOptions['contentHint'] = 'motion',\r\n): void {\r\n if (track.kind !== 'video') return;\r\n if (!('contentHint' in track) || contentHint == null) return;\r\n track.contentHint = contentHint;\r\n}\r\n\r\n/** @returns 是否成功调用 setParameters(协商完成前失败属正常,可在 connected 后重试) */\r\nexport async function applyVideoSenderEncoding(\r\n sender: RTCRtpSender,\r\n options: VideoEncodingOptions,\r\n): Promise<boolean> {\r\n const track = sender.track;\r\n if (!track || track.kind !== 'video') return false;\r\n\r\n if (options.contentHint) {\r\n applyVideoTrackContentHint(track, options.contentHint);\r\n }\r\n\r\n const params = sender.getParameters();\r\n if (!params.encodings?.length) {\r\n params.encodings = [{}];\r\n }\r\n\r\n const encoding = params.encodings[0];\r\n if (options.maxFrameRate != null) {\r\n encoding.maxFramerate = options.maxFrameRate;\r\n }\r\n if (options.maxBitrate != null) {\r\n encoding.maxBitrate = options.maxBitrate;\r\n }\r\n if (options.degradationPreference) {\r\n params.degradationPreference = options.degradationPreference;\r\n }\r\n\r\n try {\r\n await sender.setParameters(params);\r\n return true;\r\n } catch (err) {\r\n if (isExpectedSetParametersError(err)) {\r\n return false;\r\n }\r\n throw err;\r\n }\r\n}\r\n\r\nexport function applyReceiverPlayoutDelay(\r\n pc: RTCPeerConnection,\r\n delaySeconds = 0,\r\n): void {\r\n for (const receiver of pc.getReceivers()) {\r\n if (!('playoutDelayHint' in receiver)) continue;\r\n\r\n (receiver as RTCRtpReceiver & { playoutDelayHint: number }).playoutDelayHint =\r\n delaySeconds;\r\n }\r\n}\r\n\r\nexport async function applyVideoEncodingToConnection(\r\n pc: RTCPeerConnection,\r\n options: VideoEncodingOptions,\r\n): Promise<boolean> {\r\n const results = await Promise.all(\r\n pc\r\n .getSenders()\r\n .filter((sender) => sender.track?.kind === 'video')\r\n .map((sender) => applyVideoSenderEncoding(sender, options)),\r\n );\r\n return results.some(Boolean);\r\n}\r\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJtYXBwaW5ncyI6IiIsIm5hbWVzIjpbXSwiaWdub3JlTGlzdCI6W10sInNvdXJjZXMiOltdLCJzb3VyY2VzQ29udGVudCI6W119"]}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { SignalChannel } from './types';
|
|
2
|
+
import { ConnectionStateMachine } from '../fsm';
|
|
3
|
+
import type { IDisposable } from '../disposable';
|
|
4
|
+
export interface SignalConfig {
|
|
5
|
+
/** SDP 交换超时 (ms),默认 30000 */
|
|
6
|
+
negotiationTimeout?: number;
|
|
7
|
+
/** ICE 候选收集超时 (ms),默认 10000 */
|
|
8
|
+
iceGatheringTimeout?: number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* 信令管理器 — 负责 SDP / ICE 信令的序列化与协商流程
|
|
12
|
+
*/
|
|
13
|
+
export declare class SignalManager implements IDisposable {
|
|
14
|
+
private channel;
|
|
15
|
+
private pc;
|
|
16
|
+
private fsm;
|
|
17
|
+
private polite;
|
|
18
|
+
private config;
|
|
19
|
+
private logger;
|
|
20
|
+
private makingOffer;
|
|
21
|
+
private ignoreOffer;
|
|
22
|
+
private pendingCandidates;
|
|
23
|
+
private cleanups;
|
|
24
|
+
constructor(channel: SignalChannel, pc: RTCPeerConnection, fsm: ConnectionStateMachine, polite: boolean, config?: SignalConfig);
|
|
25
|
+
/** 切换 PeerConnection(disconnect / reconnect 后 rebind) */
|
|
26
|
+
setPeerConnection(pc: RTCPeerConnection): void;
|
|
27
|
+
/** 发起协商(创建 offer) */
|
|
28
|
+
createOffer(): Promise<void>;
|
|
29
|
+
/** 接收远端 offer 并创建 answer */
|
|
30
|
+
handleOffer(sdp: string): Promise<void>;
|
|
31
|
+
/** 接收远端 answer */
|
|
32
|
+
handleAnswer(sdp: string): Promise<void>;
|
|
33
|
+
/** ICE 候选管理 */
|
|
34
|
+
addIceCandidate(candidate: RTCIceCandidateInit): Promise<void>;
|
|
35
|
+
dispose(): void;
|
|
36
|
+
private renegotiateAsOfferer;
|
|
37
|
+
private renegotiateAsAnswerer;
|
|
38
|
+
private enterSignaling;
|
|
39
|
+
private failConnection;
|
|
40
|
+
private setupListeners;
|
|
41
|
+
private teardownListeners;
|
|
42
|
+
private flushPendingCandidates;
|
|
43
|
+
private withTimeout;
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=signal-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signal-manager.d.ts","sourceRoot":"","sources":["../../../src/signal/signal-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7C,OAAO,EAAE,sBAAsB,EAAE,MAAM,QAAQ,CAAC;AAEhD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEjD,MAAM,WAAW,YAAY;IAC3B,6BAA6B;IAC7B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,+BAA+B;IAC/B,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAOD;;GAEG;AACH,qBAAa,aAAc,YAAW,WAAW;IAS7C,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,EAAE;IACV,OAAO,CAAC,GAAG;IACX,OAAO,CAAC,MAAM;IAXhB,OAAO,CAAC,MAAM,CAAyB;IACvC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,iBAAiB,CAA6B;IACtD,OAAO,CAAC,QAAQ,CAAsB;gBAG5B,OAAO,EAAE,aAAa,EACtB,EAAE,EAAE,iBAAiB,EACrB,GAAG,EAAE,sBAAsB,EAC3B,MAAM,EAAE,OAAO,EACvB,MAAM,CAAC,EAAE,YAAY;IAOvB,yDAAyD;IACzD,iBAAiB,CAAC,EAAE,EAAE,iBAAiB,GAAG,IAAI;IAS9C,qBAAqB;IACf,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IA8BlC,4BAA4B;IACtB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA6C7C,kBAAkB;IACZ,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAa9C,eAAe;IACT,eAAe,CAAC,SAAS,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IAYpE,OAAO,IAAI,IAAI;YAOD,oBAAoB;YAepB,qBAAqB;IAYnC,OAAO,CAAC,cAAc;IAMtB,OAAO,CAAC,cAAc;IAMtB,OAAO,CAAC,cAAc;IAuEtB,OAAO,CAAC,iBAAiB;YAKX,sBAAsB;IASpC,OAAO,CAAC,WAAW;CAQpB"}
|