@simplito/privmx-webendpoint 2.6.5 → 2.7.2
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/CONTRIBUTING.md +86 -0
- package/README.md +97 -7
- package/assets/driver-web-context.js +1 -1
- package/assets/e2ee-worker.js +1 -1
- package/assets/endpoint-wasm-module.js +2 -2
- package/assets/endpoint-wasm-module.wasm +0 -0
- package/assets/rms-processor.js +1 -0
- package/build-manifest.sh +6 -0
- package/{FinalizationHelper.js → dist/FinalizationHelper.js} +1 -1
- package/dist/ServerTypes.d.ts +7 -0
- package/{Types.d.ts → dist/Types.d.ts} +97 -10
- package/{Types.js → dist/Types.js} +17 -32
- package/{api → dist/api}/Api.d.ts +2 -0
- package/{api → dist/api}/Api.js +18 -1
- package/{api → dist/api}/ConnectionNative.js +1 -1
- package/{api → dist/api}/CryptoApiNative.js +1 -2
- package/{api → dist/api}/InboxApiNative.d.ts +21 -2
- package/{api → dist/api}/KvdbApiNative.d.ts +19 -2
- package/dist/api/NativeError.d.ts +13 -0
- package/dist/api/NativeError.js +20 -0
- package/{api → dist/api}/StoreApiNative.d.ts +19 -2
- package/dist/api/StreamApiNative.d.ts +72 -0
- package/dist/api/StreamApiNative.js +142 -0
- package/{api → dist/api}/ThreadApiNative.d.ts +19 -2
- package/dist/assets/e2ee-worker.js +1 -0
- package/dist/bundle/privmx-endpoint-web.js +2 -0
- package/dist/bundle/privmx-endpoint-web.js.LICENSE.txt +30 -0
- package/dist/bundle/rms-processor.js +1 -0
- package/{extra → dist/extra}/PrivmxClient.d.ts +9 -9
- package/{extra → dist/extra}/PrivmxClient.js +12 -12
- package/{extra → dist/extra}/PublicConnection.d.ts +2 -2
- package/{extra → dist/extra}/PublicConnection.js +2 -2
- package/{extra → dist/extra}/__tests__/connectionEventManager.test.js +1 -3
- package/{extra → dist/extra}/__tests__/customEventManager.test.js +1 -3
- package/{extra → dist/extra}/__tests__/userEventManager.test.js +2 -6
- package/{extra → dist/extra}/__tests__/utils.test.js +18 -18
- package/{extra → dist/extra}/events.js +32 -12
- package/{extra → dist/extra}/files.d.ts +5 -5
- package/{extra → dist/extra}/files.js +11 -11
- package/{extra → dist/extra}/generics.d.ts +1 -1
- package/{extra → dist/extra}/inbox.d.ts +1 -1
- package/{extra → dist/extra}/inbox.js +2 -2
- package/{extra → dist/extra}/index.d.ts +7 -7
- package/{extra → dist/extra}/managers.js +1 -1
- package/{extra → dist/extra}/utils.js +9 -9
- package/{index.d.ts → dist/index.d.ts} +3 -2
- package/{index.js → dist/index.js} +4 -1
- package/{service → dist/service}/Connection.js +13 -6
- package/{service → dist/service}/CryptoApi.js +2 -8
- package/{service → dist/service}/EndpointFactory.d.ts +11 -0
- package/{service → dist/service}/EndpointFactory.js +45 -10
- package/{service → dist/service}/EventApi.js +9 -5
- package/dist/service/EventDispatcher.d.ts +15 -0
- package/dist/service/EventDispatcher.js +25 -0
- package/{service → dist/service}/EventQueue.d.ts +4 -4
- package/{service → dist/service}/EventQueue.js +5 -9
- package/{service → dist/service}/ExtKey.d.ts +10 -10
- package/{service → dist/service}/ExtKey.js +10 -10
- package/{service → dist/service}/InboxApi.js +7 -11
- package/{service → dist/service}/KvdbApi.js +12 -7
- package/{service → dist/service}/StoreApi.js +10 -15
- package/dist/service/StreamApi.d.ts +237 -0
- package/dist/service/StreamApi.js +442 -0
- package/{service → dist/service}/ThreadApi.js +6 -7
- package/{service → dist/service}/UserVerifierInterface.d.ts +2 -2
- package/dist/service/UserVerifierInterface.js +2 -0
- package/dist/service/WebRtcInterface.d.ts +58 -0
- package/dist/service/WebRtcInterface.js +8 -0
- package/{service → dist/service}/index.d.ts +3 -2
- package/{service → dist/service}/index.js +5 -3
- package/dist/webStreams/CryptoUtils.d.ts +24 -0
- package/dist/webStreams/CryptoUtils.js +58 -0
- package/dist/webStreams/KeyStore.d.ts +9 -0
- package/dist/webStreams/KeyStore.js +29 -0
- package/dist/webStreams/Logger.d.ts +7 -0
- package/dist/webStreams/Logger.js +25 -0
- package/dist/webStreams/PeerConnectionsManager.d.ts +20 -0
- package/dist/webStreams/PeerConnectionsManager.js +92 -0
- package/dist/webStreams/Queue.d.ts +19 -0
- package/dist/webStreams/Queue.js +70 -0
- package/dist/webStreams/Utils.d.ts +20 -0
- package/dist/webStreams/Utils.js +211 -0
- package/dist/webStreams/WebRtcClient.d.ts +85 -0
- package/dist/webStreams/WebRtcClient.js +437 -0
- package/dist/webStreams/WebRtcClientTypes.d.ts +64 -0
- package/dist/webStreams/WebRtcClientTypes.js +2 -0
- package/dist/webStreams/WebRtcConfig.d.ts +20 -0
- package/dist/webStreams/WebRtcConfig.js +53 -0
- package/dist/webStreams/WebRtcInterfaceImpl.d.ts +28 -0
- package/dist/webStreams/WebRtcInterfaceImpl.js +85 -0
- package/dist/webStreams/WebWorkerHelper copy.d.ts +0 -0
- package/dist/webStreams/WebWorkerHelper copy.js +17 -0
- package/dist/webStreams/WebWorkerHelper.d.ts +18 -0
- package/dist/webStreams/WebWorkerHelper.js +69 -0
- package/dist/webStreams/audio/ActiveSpeakerDetector.d.ts +38 -0
- package/dist/webStreams/audio/ActiveSpeakerDetector.js +64 -0
- package/dist/webStreams/audio/LocalAudioLevelMeter.d.ts +12 -0
- package/dist/webStreams/audio/LocalAudioLevelMeter.js +101 -0
- package/dist/webStreams/types/ApiTypes.d.ts +74 -0
- package/dist/webStreams/types/ApiTypes.js +2 -0
- package/dist/webStreams/types/BaseServerTypes.d.ts +10 -0
- package/dist/webStreams/types/BaseServerTypes.js +2 -0
- package/dist/webStreams/types/MediaServerWebSocketApiTypes.d.ts +255 -0
- package/dist/webStreams/types/MediaServerWebSocketApiTypes.js +2 -0
- package/dist/webStreams/types/SignalingReceiverTypes.d.ts +13 -0
- package/dist/webStreams/types/SignalingReceiverTypes.js +2 -0
- package/dist/webStreams/types/SignalingSenderTypes.d.ts +24 -0
- package/dist/webStreams/types/SignalingSenderTypes.js +2 -0
- package/dist/webStreams/types/StreamsApiTypes.d.ts +144 -0
- package/dist/webStreams/types/StreamsApiTypes.js +2 -0
- package/dist/webStreams/worker/WorkerEvents.d.ts +11 -0
- package/dist/webStreams/worker/WorkerEvents.js +2 -0
- package/dist/webStreams/worker/worker.d.ts +13 -0
- package/dist/webStreams/worker/worker.js +202 -0
- package/package.json +46 -15
- package/assets/endpoint-wasm-module.worker.js +0 -1
- package/assets/privmx-endpoint-web.js +0 -2
- package/bundle/privmx-endpoint-web.js +0 -2
- package/bundle/privmx-endpoint-web.js.LICENSE.txt +0 -10
- /package/{FinalizationHelper.d.ts → dist/FinalizationHelper.d.ts} +0 -0
- /package/{extra/generics.js → dist/ServerTypes.js} +0 -0
- /package/{api → dist/api}/ApiStatic.d.ts +0 -0
- /package/{api → dist/api}/ApiStatic.js +0 -0
- /package/{api → dist/api}/BaseNative.d.ts +0 -0
- /package/{api → dist/api}/BaseNative.js +0 -0
- /package/{api → dist/api}/ConnectionNative.d.ts +0 -0
- /package/{api → dist/api}/CryptoApiNative.d.ts +0 -0
- /package/{api → dist/api}/EventApiNative.d.ts +0 -0
- /package/{api → dist/api}/EventApiNative.js +0 -0
- /package/{api → dist/api}/EventQueueNative.d.ts +0 -0
- /package/{api → dist/api}/EventQueueNative.js +0 -0
- /package/{api → dist/api}/ExtKeyNative.d.ts +0 -0
- /package/{api → dist/api}/ExtKeyNative.js +0 -0
- /package/{api → dist/api}/IdGenerator.d.ts +0 -0
- /package/{api → dist/api}/IdGenerator.js +0 -0
- /package/{api → dist/api}/InboxApiNative.js +0 -0
- /package/{api → dist/api}/KvdbApiNative.js +0 -0
- /package/{api → dist/api}/StoreApiNative.js +0 -0
- /package/{api → dist/api}/ThreadApiNative.js +0 -0
- /package/{bundle.d.ts → dist/bundle.d.ts} +0 -0
- /package/{bundle.js → dist/bundle.js} +0 -0
- /package/{extra → dist/extra}/__mocks__/constants.d.ts +0 -0
- /package/{extra → dist/extra}/__mocks__/constants.js +0 -0
- /package/{extra → dist/extra}/__mocks__/mockContainerSubscriber.d.ts +0 -0
- /package/{extra → dist/extra}/__mocks__/mockContainerSubscriber.js +0 -0
- /package/{extra → dist/extra}/__mocks__/mockEventQueue.d.ts +0 -0
- /package/{extra → dist/extra}/__mocks__/mockEventQueue.js +0 -0
- /package/{extra → dist/extra}/__mocks__/utils.d.ts +0 -0
- /package/{extra → dist/extra}/__mocks__/utils.js +0 -0
- /package/{extra → dist/extra}/__tests__/connectionEventManager.test.d.ts +0 -0
- /package/{extra → dist/extra}/__tests__/customEventManager.test.d.ts +0 -0
- /package/{extra → dist/extra}/__tests__/eventsManager.test.d.ts +0 -0
- /package/{extra → dist/extra}/__tests__/eventsManager.test.js +0 -0
- /package/{extra → dist/extra}/__tests__/inboxEventManager.d.ts +0 -0
- /package/{extra → dist/extra}/__tests__/inboxEventManager.js +0 -0
- /package/{extra → dist/extra}/__tests__/storeEventManager.test.d.ts +0 -0
- /package/{extra → dist/extra}/__tests__/storeEventManager.test.js +0 -0
- /package/{extra → dist/extra}/__tests__/threadEventManager.test.d.ts +0 -0
- /package/{extra → dist/extra}/__tests__/threadEventManager.test.js +0 -0
- /package/{extra → dist/extra}/__tests__/userEventManager.test.d.ts +0 -0
- /package/{extra → dist/extra}/__tests__/utils.test.d.ts +0 -0
- /package/{extra → dist/extra}/events.d.ts +0 -0
- /package/{service/UserVerifierInterface.js → dist/extra/generics.js} +0 -0
- /package/{extra → dist/extra}/index.js +0 -0
- /package/{extra → dist/extra}/managers.d.ts +0 -0
- /package/{extra → dist/extra}/subscriptions.d.ts +0 -0
- /package/{extra → dist/extra}/subscriptions.js +0 -0
- /package/{extra → dist/extra}/utils.d.ts +0 -0
- /package/{service → dist/service}/BaseApi.d.ts +0 -0
- /package/{service → dist/service}/BaseApi.js +0 -0
- /package/{service → dist/service}/Connection.d.ts +0 -0
- /package/{service → dist/service}/CryptoApi.d.ts +0 -0
- /package/{service → dist/service}/EventApi.d.ts +0 -0
- /package/{service → dist/service}/InboxApi.d.ts +0 -0
- /package/{service → dist/service}/KvdbApi.d.ts +0 -0
- /package/{service → dist/service}/StoreApi.d.ts +0 -0
- /package/{service → dist/service}/ThreadApi.d.ts +0 -0
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.WebRtcClient = void 0;
|
|
4
|
+
const WebWorkerHelper_1 = require("./WebWorkerHelper");
|
|
5
|
+
const WebRtcConfig_1 = require("./WebRtcConfig");
|
|
6
|
+
const KeyStore_1 = require("./KeyStore");
|
|
7
|
+
const PeerConnectionsManager_1 = require("./PeerConnectionsManager");
|
|
8
|
+
const Logger_1 = require("./Logger");
|
|
9
|
+
const Queue_1 = require("./Queue");
|
|
10
|
+
const LocalAudioLevelMeter_1 = require("./audio/LocalAudioLevelMeter");
|
|
11
|
+
const ActiveSpeakerDetector_1 = require("./audio/ActiveSpeakerDetector");
|
|
12
|
+
const EventDispatcher_1 = require("../service/EventDispatcher");
|
|
13
|
+
class WebRtcClient {
|
|
14
|
+
assetsDir;
|
|
15
|
+
uniqId;
|
|
16
|
+
e2eeWorker;
|
|
17
|
+
webWorkerApi;
|
|
18
|
+
dataChannels = [];
|
|
19
|
+
configuration;
|
|
20
|
+
keyStore = new KeyStore_1.KeyStore();
|
|
21
|
+
publishStreamHandle;
|
|
22
|
+
// to moze byc uzyte kiedy wymagany jest update credentials (jak straca waznosc)
|
|
23
|
+
peerCredentials;
|
|
24
|
+
remoteStreamsListeners = new Map();
|
|
25
|
+
peerConnectionsManager;
|
|
26
|
+
streamsApiInterface;
|
|
27
|
+
activeSpeakerDetector;
|
|
28
|
+
audioLevelCallback;
|
|
29
|
+
// private mediaServerAvailPublishers: {[publisherId: number]: Publisher} = {};
|
|
30
|
+
encByReceiver = new WeakMap();
|
|
31
|
+
logger = Logger_1.Logger.get();
|
|
32
|
+
peerConnectionReconfigureQueue;
|
|
33
|
+
lastProcessedAnswer = {};
|
|
34
|
+
lastMeasuredLocalRMS = -99;
|
|
35
|
+
eventsDispatcher = new EventDispatcher_1.StateChangeDispatcher();
|
|
36
|
+
localAudioLevelMeters = new Map();
|
|
37
|
+
constructor(assetsDir) {
|
|
38
|
+
this.assetsDir = assetsDir;
|
|
39
|
+
this.uniqId = "" + Math.random() + "-" + Math.random();
|
|
40
|
+
this.peerConnectionsManager = new PeerConnectionsManager_1.PeerConnectionManager((roomId) => {
|
|
41
|
+
return this.createPeerConnectionMultiForRoom(roomId, this.getPeerConnectionConfiguration());
|
|
42
|
+
}, (sessionId, candidate) => {
|
|
43
|
+
return this.streamsApiInterface.trickle(sessionId, candidate);
|
|
44
|
+
});
|
|
45
|
+
this.peerConnectionReconfigureQueue = new Queue_1.Queue();
|
|
46
|
+
this.peerConnectionReconfigureQueue.assignProcessorFunc(async (_item) => {
|
|
47
|
+
// await this.reconfigure(_item);
|
|
48
|
+
await this.reconfigureSingle(_item._room, _item.offer);
|
|
49
|
+
});
|
|
50
|
+
this.activeSpeakerDetector = new ActiveSpeakerDetector_1.ActiveSpeakerDetector(ActiveSpeakerDetector_1.DEFAULTS);
|
|
51
|
+
}
|
|
52
|
+
async ensureLocalAudioLevelMeter(track) {
|
|
53
|
+
if (this.localAudioLevelMeters.has(track.id)) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const worker = await this.getWorker();
|
|
57
|
+
const meter = new LocalAudioLevelMeter_1.LocalAudioLevelMeter(track, (onRms) => {
|
|
58
|
+
worker.postMessage({ operation: "rms", rms: onRms });
|
|
59
|
+
this.lastMeasuredLocalRMS = onRms;
|
|
60
|
+
});
|
|
61
|
+
this.localAudioLevelMeters.set(track.id, meter);
|
|
62
|
+
try {
|
|
63
|
+
await meter.init(this.assetsDir + "/rms-processor.js");
|
|
64
|
+
}
|
|
65
|
+
catch (e) {
|
|
66
|
+
this.localAudioLevelMeters.delete(track.id);
|
|
67
|
+
meter.stop();
|
|
68
|
+
throw e;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
stopLocalAudioLevelMeter(track) {
|
|
72
|
+
const meter = this.localAudioLevelMeters.get(track.id);
|
|
73
|
+
if (!meter) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
this.localAudioLevelMeters.delete(track.id);
|
|
77
|
+
meter.stop();
|
|
78
|
+
}
|
|
79
|
+
setAudioLevelCallback(func) {
|
|
80
|
+
this.audioLevelCallback = func;
|
|
81
|
+
}
|
|
82
|
+
bindApiInterface(streamsApiInterface) {
|
|
83
|
+
this.streamsApiInterface = streamsApiInterface;
|
|
84
|
+
}
|
|
85
|
+
addRemoteStreamListener(listener) {
|
|
86
|
+
let listeners = this.remoteStreamsListeners.get(listener.streamRoomId) || [];
|
|
87
|
+
const exists = listeners.find((x) => x.streamId === listener.streamId);
|
|
88
|
+
if (exists) {
|
|
89
|
+
throw new Error("RemoteStreamListener with given params already exists.");
|
|
90
|
+
}
|
|
91
|
+
listeners.push(listener);
|
|
92
|
+
this.remoteStreamsListeners.set(listener.streamRoomId, listeners);
|
|
93
|
+
}
|
|
94
|
+
getStreamStateChangeDispatcher() {
|
|
95
|
+
return this.eventsDispatcher;
|
|
96
|
+
}
|
|
97
|
+
getConnectionManager() {
|
|
98
|
+
if (!this.peerConnectionsManager) {
|
|
99
|
+
throw new Error("No peerConnectionManager initialized.");
|
|
100
|
+
}
|
|
101
|
+
return this.peerConnectionsManager;
|
|
102
|
+
}
|
|
103
|
+
getWebRtcEventDispatcher() {
|
|
104
|
+
return this.eventsDispatcher;
|
|
105
|
+
}
|
|
106
|
+
getEncKey() {
|
|
107
|
+
return this.keyStore.getEncriptionKey();
|
|
108
|
+
}
|
|
109
|
+
async getWorker() {
|
|
110
|
+
if (!this.e2eeWorker) {
|
|
111
|
+
const workerApi = await this.getWorkerApi();
|
|
112
|
+
this.e2eeWorker = workerApi.getWorker();
|
|
113
|
+
}
|
|
114
|
+
if (!this.e2eeWorker) {
|
|
115
|
+
throw new Error("Worker not initialized.");
|
|
116
|
+
}
|
|
117
|
+
return this.e2eeWorker;
|
|
118
|
+
}
|
|
119
|
+
async initPipeline(receiverTrackId, publisherId) {
|
|
120
|
+
const worker = await this.getWorker();
|
|
121
|
+
const waitPromise = new Promise((resolve) => {
|
|
122
|
+
const listener = (ev) => {
|
|
123
|
+
if (ev.data.operation === "init-pipeline" && ev.data.id === receiverTrackId) {
|
|
124
|
+
worker.removeEventListener("message", listener);
|
|
125
|
+
resolve();
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
worker.addEventListener("message", listener);
|
|
129
|
+
worker.postMessage({
|
|
130
|
+
operation: "init-pipeline",
|
|
131
|
+
id: receiverTrackId,
|
|
132
|
+
publisherId: publisherId,
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
return waitPromise;
|
|
136
|
+
}
|
|
137
|
+
async getWorkerApi() {
|
|
138
|
+
if (!this.webWorkerApi) {
|
|
139
|
+
this.webWorkerApi = new WebWorkerHelper_1.WebWorker(this.assetsDir, (frameInfo) => {
|
|
140
|
+
if (this.audioLevelCallback && typeof this.audioLevelCallback === "function") {
|
|
141
|
+
// report local rms to activeSpeakerDetector to have notifications for local streams
|
|
142
|
+
this.activeSpeakerDetector.onFrame({
|
|
143
|
+
id: 0,
|
|
144
|
+
rms: this.lastMeasuredLocalRMS,
|
|
145
|
+
timestamp: Date.now(),
|
|
146
|
+
});
|
|
147
|
+
const speakers = this.activeSpeakerDetector.onFrame({
|
|
148
|
+
id: frameInfo.publisherId,
|
|
149
|
+
rms: frameInfo.rms,
|
|
150
|
+
timestamp: Date.now(),
|
|
151
|
+
});
|
|
152
|
+
// if (laudestParticipant === frameInfo.publisherId) {
|
|
153
|
+
this.audioLevelCallback({ levels: speakers });
|
|
154
|
+
// }
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
await this.webWorkerApi.init_e2ee();
|
|
158
|
+
}
|
|
159
|
+
return this.webWorkerApi;
|
|
160
|
+
}
|
|
161
|
+
getPeerConnectionConfiguration() {
|
|
162
|
+
if (!this.configuration) {
|
|
163
|
+
// throw new Error("No peerConnectionConfiguration created");
|
|
164
|
+
this.configuration = WebRtcConfig_1.WebRtcConfig.generateTurnConfiguration(this.peerCredentials);
|
|
165
|
+
}
|
|
166
|
+
return this.configuration;
|
|
167
|
+
}
|
|
168
|
+
async setTurnCredentials(turnCredentials) {
|
|
169
|
+
this.peerCredentials = turnCredentials;
|
|
170
|
+
}
|
|
171
|
+
async createPeerConnectionWithLocalStream(streamHandle, streamRoomId, stream) {
|
|
172
|
+
this.publishStreamHandle = streamHandle;
|
|
173
|
+
this.configuration = WebRtcConfig_1.WebRtcConfig.generateTurnConfiguration(this.peerCredentials);
|
|
174
|
+
const peerConnManager = this.getConnectionManager();
|
|
175
|
+
peerConnManager.initialize(streamRoomId, "publisher");
|
|
176
|
+
const pc = this.getConnectionManager().getConnectionWithSession(streamRoomId, "publisher").pc;
|
|
177
|
+
if (stream.getTracks().length > 0) {
|
|
178
|
+
const tracks = stream.getTracks();
|
|
179
|
+
this.e2eeWorker = await this.getWorker();
|
|
180
|
+
for (const track of tracks) {
|
|
181
|
+
if (track.kind === "audio") {
|
|
182
|
+
// add RMSProcessor
|
|
183
|
+
await this.ensureLocalAudioLevelMeter(track);
|
|
184
|
+
}
|
|
185
|
+
const streamSender = pc.addTrack(track, stream);
|
|
186
|
+
this.setupSenderTransform(streamSender);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return pc;
|
|
190
|
+
}
|
|
191
|
+
removeSenderPeerConnectionOnUnpublish(streamRoomId, stream) {
|
|
192
|
+
const peerConnManager = this.getConnectionManager();
|
|
193
|
+
const session = peerConnManager.getConnectionWithSession(streamRoomId, "publisher");
|
|
194
|
+
for (const track of stream.getAudioTracks()) {
|
|
195
|
+
this.stopLocalAudioLevelMeter(track);
|
|
196
|
+
}
|
|
197
|
+
session.pc.close();
|
|
198
|
+
session.pc = undefined;
|
|
199
|
+
}
|
|
200
|
+
async updatePeerConnectionWithLocalStream(streamRoomId, localStream, tracksToAdd, tracksToRemove) {
|
|
201
|
+
this.configuration = WebRtcConfig_1.WebRtcConfig.generateTurnConfiguration(this.peerCredentials);
|
|
202
|
+
const peerConnManager = this.getConnectionManager();
|
|
203
|
+
peerConnManager.initialize(streamRoomId, "publisher");
|
|
204
|
+
const pc = this.getConnectionManager().getConnectionWithSession(streamRoomId, "publisher").pc;
|
|
205
|
+
if (tracksToAdd.length > 0) {
|
|
206
|
+
this.e2eeWorker = await this.getWorker();
|
|
207
|
+
for (const track of tracksToAdd) {
|
|
208
|
+
if (track.kind === "audio") {
|
|
209
|
+
await this.ensureLocalAudioLevelMeter(track);
|
|
210
|
+
}
|
|
211
|
+
const videoSender = pc.addTrack(track, localStream);
|
|
212
|
+
if (window.RTCRtpScriptTransform) {
|
|
213
|
+
const options = {
|
|
214
|
+
operation: "encode",
|
|
215
|
+
};
|
|
216
|
+
videoSender.transform = new RTCRtpScriptTransform(this.e2eeWorker, options);
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
const senderStreams = videoSender.createEncodedStreams();
|
|
220
|
+
this.e2eeWorker.postMessage({
|
|
221
|
+
operation: "encode",
|
|
222
|
+
readableStream: senderStreams.readable,
|
|
223
|
+
writableStream: senderStreams.writable,
|
|
224
|
+
}, [senderStreams.readable, senderStreams.writable]);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
if (tracksToRemove.length > 0) {
|
|
229
|
+
const senders = pc.getSenders();
|
|
230
|
+
for (const oldTrack of tracksToRemove) {
|
|
231
|
+
if (oldTrack.kind === "audio") {
|
|
232
|
+
this.stopLocalAudioLevelMeter(oldTrack);
|
|
233
|
+
}
|
|
234
|
+
const sender = senders.find((s) => s.track === oldTrack);
|
|
235
|
+
if (sender) {
|
|
236
|
+
pc.removeTrack(sender);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
return pc;
|
|
241
|
+
}
|
|
242
|
+
createPeerConnectionMultiForRoom(roomId, configuration, _handle, _session) {
|
|
243
|
+
const extConf = configuration;
|
|
244
|
+
extConf.encodedInsertableStreams = true;
|
|
245
|
+
const connection = new RTCPeerConnection(extConf);
|
|
246
|
+
// gethering state change
|
|
247
|
+
connection.addEventListener("icegatheringstatechange", (event) => {
|
|
248
|
+
this.logger.log("info", "on ice state change: ", event);
|
|
249
|
+
});
|
|
250
|
+
// ice candidate error
|
|
251
|
+
connection.addEventListener("icecandidateerror", (event) => {
|
|
252
|
+
this.logger.log("info", "on ice error: ", event);
|
|
253
|
+
});
|
|
254
|
+
connection.addEventListener("connectionstatechange", (event) => {
|
|
255
|
+
this.logger.log("info", "connectionstatechange: ", event);
|
|
256
|
+
if (connection.connectionState === "connected") {
|
|
257
|
+
this.logger.log("important-only", "Peers connected!");
|
|
258
|
+
}
|
|
259
|
+
else {
|
|
260
|
+
this.logger.log("info", "connection state: ", connection.connectionState);
|
|
261
|
+
}
|
|
262
|
+
this.eventsDispatcher.emit({
|
|
263
|
+
streamHandle: this.publishStreamHandle,
|
|
264
|
+
state: connection.connectionState,
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
connection.addEventListener("iceconnectionstatechange", (event) => {
|
|
268
|
+
this.logger.log("info", "iceconnectionstatechange: ", event);
|
|
269
|
+
});
|
|
270
|
+
connection.addEventListener("negotiationneeded", async (event) => {
|
|
271
|
+
this.logger.log("info", "negotiationneeded: ", event);
|
|
272
|
+
// await this.startNegotiationMulti(connection);
|
|
273
|
+
});
|
|
274
|
+
connection.addEventListener("signalingstatechange", (event) => {
|
|
275
|
+
this.logger.log("info", "signalingstatechange: ", event);
|
|
276
|
+
});
|
|
277
|
+
connection.addEventListener("track", async (event) => {
|
|
278
|
+
await this.addRemoteTrack(roomId, event /*, mappedPublisher*/);
|
|
279
|
+
});
|
|
280
|
+
return connection;
|
|
281
|
+
}
|
|
282
|
+
async updateKeys(_streamRoomId, keys) {
|
|
283
|
+
this.keyStore.setKeys(keys);
|
|
284
|
+
(await this.getWorkerApi()).setKeys(keys);
|
|
285
|
+
}
|
|
286
|
+
setupSenderTransform(videoSender) {
|
|
287
|
+
if (window.RTCRtpScriptTransform) {
|
|
288
|
+
const options = {
|
|
289
|
+
operation: "encode",
|
|
290
|
+
};
|
|
291
|
+
videoSender.transform = new RTCRtpScriptTransform(this.e2eeWorker, options);
|
|
292
|
+
}
|
|
293
|
+
else {
|
|
294
|
+
this.logger.log("important-only", "Worker - encoding frames using EncodedStreams");
|
|
295
|
+
const senderStreams = videoSender.createEncodedStreams();
|
|
296
|
+
this.e2eeWorker.postMessage({
|
|
297
|
+
operation: "encode",
|
|
298
|
+
readableStream: senderStreams.readable,
|
|
299
|
+
writableStream: senderStreams.writable,
|
|
300
|
+
}, [senderStreams.readable, senderStreams.writable]);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
async setupReceiverTransform(receiver, publisherId, worker) {
|
|
304
|
+
if ("RTCRtpScriptTransform" in window && !receiver.transform) {
|
|
305
|
+
this.logger.log("important-only", "-> using RtpScriptTransform");
|
|
306
|
+
const id = receiver.track.id;
|
|
307
|
+
receiver.transform = new window.RTCRtpScriptTransform(worker, {
|
|
308
|
+
operation: "decode",
|
|
309
|
+
id,
|
|
310
|
+
publisherId,
|
|
311
|
+
});
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
this.logger.log("important-only", "-> using EncodedStreams");
|
|
315
|
+
// Fallback: Encoded Streams
|
|
316
|
+
if (!this.encByReceiver.has(receiver) &&
|
|
317
|
+
"createEncodedStreams" in receiver &&
|
|
318
|
+
typeof receiver.createEncodedStreams === "function") {
|
|
319
|
+
this.logger.log("important-only", "-> call for createEncodedStreams()");
|
|
320
|
+
const { readable, writable } = await receiver.createEncodedStreams();
|
|
321
|
+
const enc = {
|
|
322
|
+
readable,
|
|
323
|
+
writable,
|
|
324
|
+
id: receiver.track.id,
|
|
325
|
+
publisherId: publisherId,
|
|
326
|
+
posted: false,
|
|
327
|
+
};
|
|
328
|
+
this.encByReceiver.set(receiver, enc);
|
|
329
|
+
this.logger.log("important-only", "-> posting EncodedStreams to worker (should happen only once)");
|
|
330
|
+
await this.initPipeline(enc.id, enc.publisherId);
|
|
331
|
+
worker.postMessage({
|
|
332
|
+
operation: "decode",
|
|
333
|
+
id: enc.id,
|
|
334
|
+
publisherId: enc.publisherId,
|
|
335
|
+
readableStream: enc.readable,
|
|
336
|
+
writableStream: enc.writable,
|
|
337
|
+
}, [enc.readable, enc.writable]);
|
|
338
|
+
}
|
|
339
|
+
else {
|
|
340
|
+
this.logger.log("important-only", "-> EncodedStreams posted to worker already.");
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
async waitUntilConnected(pc) {
|
|
344
|
+
if (pc.iceConnectionState === "connected" || pc.iceConnectionState === "completed")
|
|
345
|
+
return Promise.resolve();
|
|
346
|
+
return new Promise((resolve, reject) => {
|
|
347
|
+
const onChange = () => {
|
|
348
|
+
if (pc.iceConnectionState === "connected" ||
|
|
349
|
+
pc.iceConnectionState === "completed") {
|
|
350
|
+
// pc.removeEventListener('iceconnectionstatechange', onChange);
|
|
351
|
+
resolve();
|
|
352
|
+
}
|
|
353
|
+
else if (pc.iceConnectionState === "failed" ||
|
|
354
|
+
pc.connectionState === "failed" ||
|
|
355
|
+
pc.connectionState === "closed") {
|
|
356
|
+
pc.removeEventListener("iceconnectionstatechange", onChange);
|
|
357
|
+
reject(new Error("ICE/DTLS not connected"));
|
|
358
|
+
}
|
|
359
|
+
};
|
|
360
|
+
pc.addEventListener("iceconnectionstatechange", onChange);
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
async teardownReceiver(receiver, worker) {
|
|
364
|
+
const enc = this.encByReceiver.get(receiver);
|
|
365
|
+
if (enc) {
|
|
366
|
+
worker.postMessage({ operation: "stop", id: enc.id });
|
|
367
|
+
this.encByReceiver.delete(receiver);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
async addRemoteTrack(roomId, event /*, mappedPublisher: Publisher*/) {
|
|
371
|
+
const worker = await this.getWorker();
|
|
372
|
+
const track = event.track;
|
|
373
|
+
const receiver = event.receiver;
|
|
374
|
+
const publisherId = Number(event.streams[0].id);
|
|
375
|
+
const key = this.getEncKey();
|
|
376
|
+
const peerConnection = this.getConnectionManager().getConnectionWithSession(roomId, "subscriber").pc;
|
|
377
|
+
this.logger.log("important-only", "waitUntilConnected...");
|
|
378
|
+
await this.waitUntilConnected(peerConnection);
|
|
379
|
+
this.logger.log("important-only", "setupReceiverTransform...");
|
|
380
|
+
await this.setupReceiverTransform(receiver, publisherId, worker);
|
|
381
|
+
track.addEventListener("ended", async () => await this.teardownReceiver(receiver, worker));
|
|
382
|
+
this.callRegisteredListeners(roomId, event);
|
|
383
|
+
}
|
|
384
|
+
callRegisteredListeners(roomId, event) {
|
|
385
|
+
const remoteStreamId = Number(event.streams[0].id);
|
|
386
|
+
const listeners = this.remoteStreamsListeners.get(roomId);
|
|
387
|
+
if (!listeners) {
|
|
388
|
+
this.logger.log("info", "No remoteTrack listener registered for room: " + roomId);
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
const filteredListeners = listeners.filter((x) => x.streamId === remoteStreamId || x.streamId === undefined);
|
|
392
|
+
for (const listener of filteredListeners) {
|
|
393
|
+
if (listener.onRemoteStreamTrack &&
|
|
394
|
+
typeof listener.onRemoteStreamTrack === "function") {
|
|
395
|
+
listener.onRemoteStreamTrack(event);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
// test
|
|
400
|
+
async onSubscriptionUpdated(_room, offer) {
|
|
401
|
+
if (!this.peerConnectionReconfigureQueue) {
|
|
402
|
+
throw new Error("ReconfigureQueue does not exist.");
|
|
403
|
+
}
|
|
404
|
+
this.peerConnectionReconfigureQueue.enqueue({
|
|
405
|
+
taskId: Math.floor(1 + Math.random() * 10000),
|
|
406
|
+
_room,
|
|
407
|
+
offer,
|
|
408
|
+
});
|
|
409
|
+
try {
|
|
410
|
+
await this.peerConnectionReconfigureQueue.processAll();
|
|
411
|
+
}
|
|
412
|
+
catch (e) {
|
|
413
|
+
console.error("Error on onSubscriberAttached", e);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
async onSubscriptionUpdatedSingle(_room, offer) {
|
|
417
|
+
return this.reconfigureSingle(_room, offer);
|
|
418
|
+
}
|
|
419
|
+
async reconfigureSingle(room, offer) {
|
|
420
|
+
if (!this.configuration) {
|
|
421
|
+
throw new Error("Configuration missing.");
|
|
422
|
+
}
|
|
423
|
+
const janusConnection = this.getConnectionManager().getConnectionWithSession(room, "subscriber");
|
|
424
|
+
const peerConnection = janusConnection.pc;
|
|
425
|
+
this.logger.log("important-only", "1. Setting up remoteDescription...");
|
|
426
|
+
await peerConnection.setRemoteDescription(new RTCSessionDescription({ type: offer.type, sdp: offer.sdp }));
|
|
427
|
+
this.logger.log("important-only", "offer from Janus: ", JSON.stringify(offer, null, 2));
|
|
428
|
+
this.logger.log("important-only", "2. Creating an answer...", "peerConnection state", peerConnection.connectionState);
|
|
429
|
+
const answer = await peerConnection.createAnswer();
|
|
430
|
+
this.logger.log("important-only", "3. Setting up localDescription...");
|
|
431
|
+
await peerConnection.setLocalDescription(new RTCSessionDescription(answer));
|
|
432
|
+
// this.subscriberAttachedProcessing = false
|
|
433
|
+
this.lastProcessedAnswer[room] = answer;
|
|
434
|
+
return answer;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
exports.WebRtcClient = WebRtcClient;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { NewPublisherEvent } from "../service/WebRtcInterface";
|
|
2
|
+
import { StreamRoomId } from "./types/ApiTypes";
|
|
3
|
+
export declare class RTCRtpScriptTransform {
|
|
4
|
+
constructor(worker: any, options: any);
|
|
5
|
+
transform: (frame: any, controller: any) => void;
|
|
6
|
+
}
|
|
7
|
+
export interface PeerCredentials {
|
|
8
|
+
username: string;
|
|
9
|
+
password: string;
|
|
10
|
+
expirationTime: number;
|
|
11
|
+
}
|
|
12
|
+
export interface EncKey {
|
|
13
|
+
key: Buffer;
|
|
14
|
+
iv: Buffer;
|
|
15
|
+
}
|
|
16
|
+
export interface InitOptions {
|
|
17
|
+
signalingServer: string;
|
|
18
|
+
appServer: string;
|
|
19
|
+
mediaServer: string;
|
|
20
|
+
turnUrls?: string[];
|
|
21
|
+
iceTransportPolicy?: RTCIceTransportPolicy;
|
|
22
|
+
encKey?: string;
|
|
23
|
+
}
|
|
24
|
+
export interface VideoStream {
|
|
25
|
+
stream: MediaStream;
|
|
26
|
+
isLocal: boolean;
|
|
27
|
+
id: string;
|
|
28
|
+
}
|
|
29
|
+
export type PluginHandleId = number & {
|
|
30
|
+
_pluginHandleId: never;
|
|
31
|
+
};
|
|
32
|
+
export type PluginId = string & {
|
|
33
|
+
_pluginId: never;
|
|
34
|
+
};
|
|
35
|
+
export type SessionId = number & {
|
|
36
|
+
_sessionId: never;
|
|
37
|
+
};
|
|
38
|
+
export interface JanusPluginHandle {
|
|
39
|
+
id: PluginHandleId;
|
|
40
|
+
pluginId: PluginId;
|
|
41
|
+
}
|
|
42
|
+
export interface JanusSession {
|
|
43
|
+
id: SessionId;
|
|
44
|
+
}
|
|
45
|
+
export interface Publisher extends NewPublisherEvent {
|
|
46
|
+
attached: boolean;
|
|
47
|
+
room: StreamRoomId;
|
|
48
|
+
}
|
|
49
|
+
export type EncPair = {
|
|
50
|
+
readable: ReadableStream<any>;
|
|
51
|
+
writable: WritableStream<any>;
|
|
52
|
+
id: string;
|
|
53
|
+
publisherId: number;
|
|
54
|
+
posted: boolean;
|
|
55
|
+
};
|
|
56
|
+
export interface RTCEncodedStream {
|
|
57
|
+
readable: ReadableStream<EncodedAudioChunk | EncodedVideoChunk>;
|
|
58
|
+
writable: WritableStream<EncodedAudioChunk | EncodedVideoChunk>;
|
|
59
|
+
}
|
|
60
|
+
export interface QueueItem {
|
|
61
|
+
taskId: number;
|
|
62
|
+
_room: StreamRoomId;
|
|
63
|
+
offer: any;
|
|
64
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { TurnCredentials } from "../Types";
|
|
2
|
+
export declare class WebRtcConfig {
|
|
3
|
+
private static iv;
|
|
4
|
+
private static key;
|
|
5
|
+
private static iceTransportPolicy;
|
|
6
|
+
private static appServer;
|
|
7
|
+
static getIV(): string;
|
|
8
|
+
static getKey(): string;
|
|
9
|
+
static getTurnServers(): string[];
|
|
10
|
+
static getRTCIceTransportPolicy(): RTCIceTransportPolicy;
|
|
11
|
+
static generateTurnConfiguration(credentials: TurnCredentials[]): {
|
|
12
|
+
iceServers: {
|
|
13
|
+
urls: string;
|
|
14
|
+
username: string;
|
|
15
|
+
credential: string;
|
|
16
|
+
}[];
|
|
17
|
+
iceTransportPolicy: RTCIceTransportPolicy;
|
|
18
|
+
};
|
|
19
|
+
static getAppServerAddress(): string;
|
|
20
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.WebRtcConfig = void 0;
|
|
4
|
+
class WebRtcConfig {
|
|
5
|
+
// TMP IV
|
|
6
|
+
static iv = "uMuF0aaCZ6+kLrwp";
|
|
7
|
+
// TMP key
|
|
8
|
+
static key = "7c4ba0c3e256369f7c4ba0c3e256369f";
|
|
9
|
+
static iceTransportPolicy = "all";
|
|
10
|
+
static appServer = "localhost:8810";
|
|
11
|
+
static getIV() {
|
|
12
|
+
return this.iv;
|
|
13
|
+
}
|
|
14
|
+
static getKey() {
|
|
15
|
+
return this.key;
|
|
16
|
+
}
|
|
17
|
+
// Default turn servers
|
|
18
|
+
static getTurnServers() {
|
|
19
|
+
return ["turn:172.16.238.1:3478"];
|
|
20
|
+
}
|
|
21
|
+
static getRTCIceTransportPolicy() {
|
|
22
|
+
return this.iceTransportPolicy;
|
|
23
|
+
}
|
|
24
|
+
// public static generateTurnConfiguration(credentials: PeerCredentials|undefined) {
|
|
25
|
+
// const servers: RTCIceServer[] = this.getTurnServers().map(x => (
|
|
26
|
+
// {
|
|
27
|
+
// urls: x,
|
|
28
|
+
// username: credentials && credentials.username ? credentials.username : undefined,
|
|
29
|
+
// credential: credentials && credentials.password ? credentials.password : undefined
|
|
30
|
+
// }
|
|
31
|
+
// ));
|
|
32
|
+
// return {
|
|
33
|
+
// iceServers: servers,
|
|
34
|
+
// iceTransportPolicy: this.iceTransportPolicy
|
|
35
|
+
// }
|
|
36
|
+
// }
|
|
37
|
+
static generateTurnConfiguration(credentials) {
|
|
38
|
+
return {
|
|
39
|
+
iceServers: credentials.map((x) => {
|
|
40
|
+
return {
|
|
41
|
+
urls: x.url,
|
|
42
|
+
username: x.username,
|
|
43
|
+
credential: x.password,
|
|
44
|
+
};
|
|
45
|
+
}),
|
|
46
|
+
iceTransportPolicy: this.iceTransportPolicy,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
static getAppServerAddress() {
|
|
50
|
+
return this.appServer;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
exports.WebRtcConfig = WebRtcConfig;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
PrivMX Endpoint.
|
|
3
|
+
Copyright © 2024 Simplito sp. z o.o.
|
|
4
|
+
|
|
5
|
+
This file is part of the PrivMX Platform (https://privmx.dev).
|
|
6
|
+
This software is Licensed under the PrivMX Free License.
|
|
7
|
+
|
|
8
|
+
See the License for the specific language governing permissions and
|
|
9
|
+
limitations under the License.
|
|
10
|
+
*/
|
|
11
|
+
import { RoomModel, SdpWithRoomModel, SetAnswerAndSetRemoteDescriptionModel, UpdateKeysModel, WebRtcInterface } from "../service/WebRtcInterface";
|
|
12
|
+
import { ConnectionType } from "./PeerConnectionsManager";
|
|
13
|
+
import { StreamRoomId } from "./types/ApiTypes";
|
|
14
|
+
import { WebRtcClient } from "./WebRtcClient";
|
|
15
|
+
export declare class WebRtcInterfaceImpl implements WebRtcInterface {
|
|
16
|
+
private webRtcClient;
|
|
17
|
+
constructor(webRtcClient: WebRtcClient);
|
|
18
|
+
private methodsMap;
|
|
19
|
+
isMainThread(): boolean;
|
|
20
|
+
getClient(): WebRtcClient;
|
|
21
|
+
methodCall(name: string, params: any): Promise<any>;
|
|
22
|
+
createOfferAndSetLocalDescription(model: RoomModel): Promise<string>;
|
|
23
|
+
createAnswerAndSetDescriptions(model: SdpWithRoomModel): Promise<string>;
|
|
24
|
+
setAnswerAndSetRemoteDescription(model: SetAnswerAndSetRemoteDescriptionModel): Promise<void>;
|
|
25
|
+
close(roomId: StreamRoomId): Promise<void>;
|
|
26
|
+
updateKeys(model: UpdateKeysModel): Promise<void>;
|
|
27
|
+
updateSessionId(streamRoomId: StreamRoomId, sessionId: number, connectionType: ConnectionType): Promise<void>;
|
|
28
|
+
}
|