@4players/odin-plugin-web 1.0.0-alpha.20 → 1.0.0-alpha.21
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/lib/plugin/index.d.ts +17 -66
- package/lib/plugin/index.js +1 -404
- package/lib/worker/shared.js +1 -0
- package/lib/worker/unshared.js +1 -0
- package/package.json +12 -9
- package/lib/common/msgpack.d.ts +0 -2
- package/lib/common/msgpack.js +0 -117
- package/lib/plugin/config.d.ts +0 -12
- package/lib/plugin/config.js +0 -22
- package/lib/plugin/connectionPool.d.ts +0 -17
- package/lib/plugin/connectionPool.js +0 -130
- package/lib/plugin/deviceManager.d.ts +0 -12
- package/lib/plugin/deviceManager.js +0 -120
- package/lib/plugin/h3/mainConnection.d.ts +0 -31
- package/lib/plugin/h3/mainConnection.js +0 -195
- package/lib/plugin/mainConnection.d.ts +0 -30
- package/lib/plugin/mainConnection.js +0 -21
- package/lib/plugin/mediaController.d.ts +0 -66
- package/lib/plugin/mediaController.js +0 -215
- package/lib/plugin/medias/audio-capture.d.ts +0 -30
- package/lib/plugin/medias/audio-capture.js +0 -224
- package/lib/plugin/medias/audio-playback.d.ts +0 -13
- package/lib/plugin/medias/audio-playback.js +0 -40
- package/lib/plugin/medias/video-capture.d.ts +0 -15
- package/lib/plugin/medias/video-capture.js +0 -69
- package/lib/plugin/medias/video-playback.d.ts +0 -13
- package/lib/plugin/medias/video-playback.js +0 -20
- package/lib/plugin/roomConnection.d.ts +0 -40
- package/lib/plugin/roomConnection.js +0 -320
- package/lib/plugin/rpcConnection.d.ts +0 -57
- package/lib/plugin/rpcConnection.js +0 -281
- package/lib/plugin/token.d.ts +0 -13
- package/lib/plugin/token.js +0 -66
- package/lib/plugin/webrtc/mainConnection.d.ts +0 -53
- package/lib/plugin/webrtc/mainConnection.js +0 -455
- package/lib/plugin/webrtc/websocket.d.ts +0 -3
- package/lib/plugin/webrtc/websocket.js +0 -56
- package/lib/wasm/index.d.ts +0 -97
- package/lib/worker/buffers.d.ts +0 -42
- package/lib/worker/buffers.js +0 -79
- package/lib/worker/common.d.ts +0 -45
- package/lib/worker/common.js +0 -7
- package/lib/worker/config.d.ts +0 -8
- package/lib/worker/config.js +0 -11
- package/lib/worker/index.d.ts +0 -3
- package/lib/worker/index.js +0 -1
- package/lib/worker/memory.d.ts +0 -9
- package/lib/worker/memory.js +0 -29
- package/lib/worker/messages.d.ts +0 -14
- package/lib/worker/messages.js +0 -15
- package/lib/worker/packet.d.ts +0 -11
- package/lib/worker/packet.js +0 -59
- package/lib/worker/shared/capture.d.ts +0 -27
- package/lib/worker/shared/capture.js +0 -63
- package/lib/worker/shared/index.d.ts +0 -3
- package/lib/worker/shared/index.js +0 -147
- package/lib/worker/shared/messages.d.ts +0 -67
- package/lib/worker/shared/messages.js +0 -64
- package/lib/worker/shared/playback.d.ts +0 -20
- package/lib/worker/shared/playback.js +0 -78
- package/lib/worker/unshared/capture.d.ts +0 -27
- package/lib/worker/unshared/capture.js +0 -66
- package/lib/worker/unshared/index.d.ts +0 -3
- package/lib/worker/unshared/index.js +0 -170
- package/lib/worker/unshared/messages.d.ts +0 -81
- package/lib/worker/unshared/messages.js +0 -80
- package/lib/worker/unshared/playback.d.ts +0 -22
- package/lib/worker/unshared/playback.js +0 -83
package/lib/plugin/index.d.ts
CHANGED
|
@@ -1,70 +1,21 @@
|
|
|
1
|
-
import { Backend } from '@4players/odin-common';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
import { VideoPlayback } from './medias/video-playback';
|
|
8
|
-
import { AudioPlayback } from './medias/audio-playback';
|
|
9
|
-
import JoinRoomParameters = Backend.JoinRoomParameters;
|
|
10
|
-
export type { LogFilters } from '@4players/odin-common';
|
|
11
|
-
export { logger } from './config';
|
|
12
|
-
type Plugin = Backend.Plugin;
|
|
13
|
-
export interface DeviceParameters {
|
|
14
|
-
readonly device?: Device;
|
|
15
|
-
onStatusChanged?: Backend.OnStatusChanged;
|
|
1
|
+
import { Backend, Logger } from '@4players/odin-common';
|
|
2
|
+
|
|
3
|
+
export declare const logger: Logger;
|
|
4
|
+
export type AudioContextFactory = (sampleRate: number | undefined) => Promise<AudioContext>;
|
|
5
|
+
export interface WebDevice extends Backend.Device {
|
|
6
|
+
readonly device: MediaDeviceInfo;
|
|
16
7
|
}
|
|
17
|
-
export
|
|
18
|
-
|
|
19
|
-
|
|
8
|
+
export interface WebDeviceParameters extends Backend.DeviceParameters {
|
|
9
|
+
readonly device?: WebDevice;
|
|
10
|
+
onStatusChanged?: Backend.OnStatusChanged;
|
|
20
11
|
}
|
|
21
|
-
export
|
|
22
|
-
|
|
12
|
+
export declare function createPlugin(audioContextFactory: AudioContextFactory): WebPlugin;
|
|
13
|
+
export interface WebPlugin extends Backend.Plugin {
|
|
14
|
+
joinRoom(parameters: Backend.JoinRoomParameters): WebRoom;
|
|
15
|
+
enumerateDevices(): Promise<WebDevice[]>;
|
|
16
|
+
setOutputDevice(device: WebDeviceParameters): Promise<void>;
|
|
23
17
|
}
|
|
24
|
-
export
|
|
25
|
-
readonly options: WebPluginOptions;
|
|
26
|
-
readonly version = "2";
|
|
27
|
-
private _NextRoomId;
|
|
28
|
-
private _PlaybackVolume;
|
|
29
|
-
private readonly _ConnectionPool;
|
|
30
|
-
private readonly _MediaController;
|
|
31
|
-
get playbackVolume(): Backend.Volume;
|
|
32
|
-
static create(audioContextFactory: AudioContextFactory, options?: WebPluginOptions): Promise<WebPlugin>;
|
|
33
|
-
private constructor();
|
|
34
|
-
joinRoom(parameters: JoinRoomParameters): Room;
|
|
35
|
-
createAudioPlayback(parameters: CreateAudioPlaybackParameters): Promise<AudioPlayback>;
|
|
36
|
-
createAudioCapture(parameters: CreateAudioCaptureParameters): Promise<AudioCapture>;
|
|
37
|
-
createVideoCapture(ms: MediaStream, customType?: string): Promise<VideoCapture>;
|
|
38
|
-
createVideoPlayback(parameters: Backend.CreateVideoPlaybackParameters): Promise<VideoPlayback>;
|
|
39
|
-
enumerateDevices(): Promise<Device[]>;
|
|
40
|
-
setOutputVolume(volume: Backend.Volume): void;
|
|
41
|
-
setOutputDevice(device: DeviceParameters): Promise<void>;
|
|
42
|
-
close(): void;
|
|
43
|
-
}
|
|
44
|
-
export declare class Room implements Backend.Room {
|
|
45
|
-
private readonly _Id;
|
|
46
|
-
private readonly _Connection;
|
|
47
|
-
private readonly _OnEvent;
|
|
48
|
-
private readonly _Captures;
|
|
49
|
-
private readonly _Playbacks;
|
|
50
|
-
private readonly _Peers;
|
|
51
|
-
private readonly _AvailableMediaIds;
|
|
52
|
-
get mainConnection(): MainConnection | undefined;
|
|
53
|
-
constructor(id: number, connectionPool: ConnectionPool, parameters: JoinRoomParameters, options: WebPluginOptions);
|
|
54
|
-
get token(): string;
|
|
55
|
-
get cipher(): Backend.Cipher | undefined;
|
|
56
|
-
request(method: string, properties: unknown): Promise<unknown>;
|
|
57
|
-
link(media: Backend.Media): Promise<void>;
|
|
58
|
-
unlink(media: Backend.Media): void;
|
|
59
|
-
close(): void;
|
|
60
|
-
private _HandleEvent;
|
|
61
|
-
private _HandleKnownEvent;
|
|
62
|
-
private _HandleConnectionLost;
|
|
63
|
-
private _HandleRoomUpdate;
|
|
64
|
-
private _HandleRoomJoined;
|
|
65
|
-
private _HandlePeerUpdate;
|
|
66
|
-
private _HandlePeerJoined;
|
|
67
|
-
private _HandlePeerLeft;
|
|
68
|
-
private _HandleMediaStart;
|
|
69
|
-
private _HandleMediaStopped;
|
|
18
|
+
export interface WebRoom extends Backend.Room {
|
|
70
19
|
}
|
|
20
|
+
|
|
21
|
+
export {};
|
package/lib/plugin/index.js
CHANGED
|
@@ -1,404 +1 @@
|
|
|
1
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
-
});
|
|
9
|
-
};
|
|
10
|
-
import { assert, generateUUID, OdinError, RoomNotificationsRpc, unwrap, } from '@4players/odin-common';
|
|
11
|
-
import { AudioContextCollection, MediaController, } from './mediaController';
|
|
12
|
-
import { ConnectionPool } from './connectionPool';
|
|
13
|
-
import { RoomConnection } from './roomConnection';
|
|
14
|
-
import { parseRpcMessage } from './rpcConnection';
|
|
15
|
-
import { Token } from './token';
|
|
16
|
-
import { AudioCapture } from './medias/audio-capture';
|
|
17
|
-
import { enumerateDevices } from './deviceManager';
|
|
18
|
-
import { VideoCapture } from './medias/video-capture';
|
|
19
|
-
import { VideoPlayback } from './medias/video-playback';
|
|
20
|
-
import { AudioPlayback } from './medias/audio-playback';
|
|
21
|
-
import { logger } from './config';
|
|
22
|
-
export { logger } from './config';
|
|
23
|
-
export var Transport;
|
|
24
|
-
(function (Transport) {
|
|
25
|
-
Transport["H3"] = "h3";
|
|
26
|
-
Transport["WebRTC"] = "webrtc";
|
|
27
|
-
})(Transport || (Transport = {}));
|
|
28
|
-
export class WebPlugin {
|
|
29
|
-
get playbackVolume() {
|
|
30
|
-
return this._PlaybackVolume;
|
|
31
|
-
}
|
|
32
|
-
static create(audioContextFactory, options) {
|
|
33
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
34
|
-
logger.info(['Other'], 'Using ODIN Plugin Web!');
|
|
35
|
-
// eslint-disable-next-line prefer-const
|
|
36
|
-
let audioController;
|
|
37
|
-
const pool = new ConnectionPool((connectionId, packet) => audioController.receivePacket(connectionId, packet));
|
|
38
|
-
const audioContexts = new AudioContextCollection(audioContextFactory);
|
|
39
|
-
audioController = new MediaController(audioContexts, pool.sendDatagram.bind(pool));
|
|
40
|
-
return new WebPlugin(pool, audioController, options);
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
constructor(connectionPool, audioController, options = { transport: Transport.H3 }) {
|
|
44
|
-
this.options = options;
|
|
45
|
-
this.version = '2';
|
|
46
|
-
this._NextRoomId = 0;
|
|
47
|
-
this._PlaybackVolume = 1;
|
|
48
|
-
this._ConnectionPool = connectionPool;
|
|
49
|
-
this._MediaController = audioController;
|
|
50
|
-
}
|
|
51
|
-
joinRoom(parameters) {
|
|
52
|
-
return new Room(this._NextRoomId++, this._ConnectionPool, parameters, this.options);
|
|
53
|
-
}
|
|
54
|
-
createAudioPlayback(parameters) {
|
|
55
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
56
|
-
return this._MediaController.createAudioPlayback(parameters);
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
createAudioCapture(parameters) {
|
|
60
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
61
|
-
return this._MediaController.createAudioCapture(parameters);
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
createVideoCapture(ms, customType) {
|
|
65
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
66
|
-
return this._MediaController.createVideoCapture(ms, customType);
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
createVideoPlayback(parameters) {
|
|
70
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
71
|
-
return new VideoPlayback(parameters.uid, parameters.customType);
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
enumerateDevices() {
|
|
75
|
-
return enumerateDevices();
|
|
76
|
-
}
|
|
77
|
-
setOutputVolume(volume) {
|
|
78
|
-
this._MediaController.playbackSetGlobalVolume(volume);
|
|
79
|
-
}
|
|
80
|
-
setOutputDevice(device) {
|
|
81
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
82
|
-
yield this._MediaController.playbackSetDevice(device.device);
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
close() {
|
|
86
|
-
logger.debug(['Other', 'Plugin.close()']);
|
|
87
|
-
this._MediaController.close();
|
|
88
|
-
this._ConnectionPool.close();
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
export class Room {
|
|
92
|
-
get mainConnection() {
|
|
93
|
-
return this._Connection.mainConnection;
|
|
94
|
-
}
|
|
95
|
-
constructor(id, connectionPool, parameters, options) {
|
|
96
|
-
var _a, _b, _c;
|
|
97
|
-
this._Captures = new Map();
|
|
98
|
-
this._Playbacks = new Map();
|
|
99
|
-
this._Peers = new Map();
|
|
100
|
-
this._AvailableMediaIds = [];
|
|
101
|
-
const { gateway, onEvent } = parameters;
|
|
102
|
-
const token = unwrap(Token.parse(parameters.token));
|
|
103
|
-
const roomId = (_a = parameters.roomId) !== null && _a !== void 0 ? _a : token.defaultRoomId;
|
|
104
|
-
const userData = (_b = parameters.userData) !== null && _b !== void 0 ? _b : new Uint8Array(0);
|
|
105
|
-
const position = (_c = parameters.position) !== null && _c !== void 0 ? _c : [0, 0, 0];
|
|
106
|
-
assert(roomId !== undefined, 'room id required for multi-room-tokens');
|
|
107
|
-
this._Id = id;
|
|
108
|
-
this._OnEvent = onEvent;
|
|
109
|
-
this._Connection = new RoomConnection(connectionPool, options.transport, this._HandleEvent.bind(this), gateway ? new URL(gateway) : undefined, token, roomId, userData, position);
|
|
110
|
-
}
|
|
111
|
-
get token() {
|
|
112
|
-
return this._Connection.token.text;
|
|
113
|
-
}
|
|
114
|
-
get cipher() {
|
|
115
|
-
return undefined; // not implemented
|
|
116
|
-
}
|
|
117
|
-
request(method, properties) {
|
|
118
|
-
return this._Connection.request(method, properties);
|
|
119
|
-
}
|
|
120
|
-
link(media) {
|
|
121
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
122
|
-
var _a, _b, _c, _d;
|
|
123
|
-
logger.debug(['Audio'], 'Room.link() media', media);
|
|
124
|
-
if (media instanceof AudioPlayback) {
|
|
125
|
-
const entry = (_a = this._Playbacks.get(media.uid)) !== null && _a !== void 0 ? _a : {
|
|
126
|
-
media: undefined,
|
|
127
|
-
playback: undefined,
|
|
128
|
-
};
|
|
129
|
-
if (entry.playback !== undefined) {
|
|
130
|
-
this.unlink(entry.playback);
|
|
131
|
-
}
|
|
132
|
-
if (entry.media !== undefined) {
|
|
133
|
-
media.start(entry.media);
|
|
134
|
-
}
|
|
135
|
-
entry.playback = media;
|
|
136
|
-
this._Playbacks.set(media.uid, entry);
|
|
137
|
-
}
|
|
138
|
-
else if (media instanceof AudioCapture) {
|
|
139
|
-
let entry = this._Captures.get(media);
|
|
140
|
-
if (entry) {
|
|
141
|
-
this.unlink(media);
|
|
142
|
-
}
|
|
143
|
-
const mediaId = this.mainConnection !== undefined && this._AvailableMediaIds.shift();
|
|
144
|
-
entry = {
|
|
145
|
-
media: mediaId
|
|
146
|
-
? {
|
|
147
|
-
connectionId: this.mainConnection.id,
|
|
148
|
-
mediaId,
|
|
149
|
-
}
|
|
150
|
-
: undefined,
|
|
151
|
-
uid: generateUUID(),
|
|
152
|
-
};
|
|
153
|
-
this._Captures.set(media, entry);
|
|
154
|
-
if (entry.media !== undefined) {
|
|
155
|
-
media.start(this._Connection, entry.media, entry.uid);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
else if (media instanceof VideoCapture) {
|
|
159
|
-
let mediaEntry = this._Captures.get(media);
|
|
160
|
-
if (mediaEntry) {
|
|
161
|
-
this.unlink(media);
|
|
162
|
-
}
|
|
163
|
-
if (media.mediaStream) {
|
|
164
|
-
const mediaId = this.mainConnection !== undefined && this._AvailableMediaIds.shift();
|
|
165
|
-
mediaEntry = {
|
|
166
|
-
media: mediaId
|
|
167
|
-
? {
|
|
168
|
-
connectionId: this.mainConnection.id,
|
|
169
|
-
mediaId,
|
|
170
|
-
}
|
|
171
|
-
: undefined,
|
|
172
|
-
uid: generateUUID(),
|
|
173
|
-
};
|
|
174
|
-
this._Captures.set(media, mediaEntry);
|
|
175
|
-
if (mediaEntry.media !== undefined) {
|
|
176
|
-
// @TODO Check if we also need to send the StartMedia rpc
|
|
177
|
-
(_b = this.mainConnection) === null || _b === void 0 ? void 0 : _b.startLocalVideo(mediaEntry.media.mediaId, media.mediaStream.getTracks()[0]);
|
|
178
|
-
media.start(this._Connection, mediaEntry.media, mediaEntry.uid);
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
else if (media instanceof VideoPlayback) {
|
|
183
|
-
const entry = (_c = this._Playbacks.get(media.uid)) !== null && _c !== void 0 ? _c : {
|
|
184
|
-
media: undefined,
|
|
185
|
-
playback: undefined,
|
|
186
|
-
};
|
|
187
|
-
if (entry.playback !== undefined) {
|
|
188
|
-
this.unlink(entry.playback);
|
|
189
|
-
}
|
|
190
|
-
if (entry.media !== undefined) {
|
|
191
|
-
const track = yield ((_d = this.mainConnection) === null || _d === void 0 ? void 0 : _d.startRemoteVideo(entry.media.mediaId));
|
|
192
|
-
if (track) {
|
|
193
|
-
media.setMediaStream(new MediaStream([track]));
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
entry.playback = media;
|
|
197
|
-
this._Playbacks.set(media.uid, entry);
|
|
198
|
-
}
|
|
199
|
-
else {
|
|
200
|
-
throw new OdinError('invalid media');
|
|
201
|
-
}
|
|
202
|
-
});
|
|
203
|
-
}
|
|
204
|
-
unlink(media) {
|
|
205
|
-
var _a, _b;
|
|
206
|
-
logger.debug(['Audio'], 'Room.unlink() media', media);
|
|
207
|
-
if (media instanceof AudioPlayback || media instanceof VideoPlayback) {
|
|
208
|
-
const entry = this._Playbacks.get(media.uid);
|
|
209
|
-
if ((entry === null || entry === void 0 ? void 0 : entry.playback) !== media)
|
|
210
|
-
return;
|
|
211
|
-
entry.playback = undefined;
|
|
212
|
-
if (entry.media !== undefined) {
|
|
213
|
-
if (media instanceof AudioPlayback) {
|
|
214
|
-
media.stop(entry.media);
|
|
215
|
-
}
|
|
216
|
-
else {
|
|
217
|
-
(_a = this.mainConnection) === null || _a === void 0 ? void 0 : _a.stopRemoteVideo(entry.media.mediaId);
|
|
218
|
-
// @TODO check if the MediaStream and its tracks have to get closed manually.
|
|
219
|
-
media.setMediaStream(undefined);
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
this._Playbacks.set(media.uid, entry);
|
|
223
|
-
}
|
|
224
|
-
else if (media instanceof AudioCapture || media instanceof VideoCapture) {
|
|
225
|
-
const entry = this._Captures.get(media);
|
|
226
|
-
if (entry === undefined)
|
|
227
|
-
return;
|
|
228
|
-
if (entry.media !== undefined) {
|
|
229
|
-
media.stop(this._Connection, entry.media);
|
|
230
|
-
this._AvailableMediaIds.push(entry.media.mediaId);
|
|
231
|
-
if (media instanceof VideoCapture) {
|
|
232
|
-
(_b = this.mainConnection) === null || _b === void 0 ? void 0 : _b.stopLocalVideo(entry.media.mediaId);
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
this._Captures.delete(media);
|
|
236
|
-
}
|
|
237
|
-
else {
|
|
238
|
-
throw new Error('invalid media');
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
close() {
|
|
242
|
-
logger.debug(['Other', 'Room.close()']);
|
|
243
|
-
this._Connection.close();
|
|
244
|
-
}
|
|
245
|
-
_HandleEvent(method, properties) {
|
|
246
|
-
const rpc = parseRpcMessage(RoomNotificationsRpc, {
|
|
247
|
-
name: method,
|
|
248
|
-
properties,
|
|
249
|
-
});
|
|
250
|
-
if (rpc !== undefined) {
|
|
251
|
-
this._HandleKnownEvent(rpc);
|
|
252
|
-
properties = rpc.properties;
|
|
253
|
-
}
|
|
254
|
-
this._OnEvent(method, properties);
|
|
255
|
-
}
|
|
256
|
-
_HandleKnownEvent(event) {
|
|
257
|
-
switch (event.name) {
|
|
258
|
-
case 'RoomStatusChanged':
|
|
259
|
-
if (event.properties.status !== 'Joined') {
|
|
260
|
-
this._HandleConnectionLost();
|
|
261
|
-
}
|
|
262
|
-
break;
|
|
263
|
-
case 'RoomUpdated':
|
|
264
|
-
for (const update of event.properties.updates) {
|
|
265
|
-
this._HandleRoomUpdate(update);
|
|
266
|
-
}
|
|
267
|
-
break;
|
|
268
|
-
case 'PeerUpdated':
|
|
269
|
-
this._HandlePeerUpdate(event.properties);
|
|
270
|
-
break;
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
_HandleConnectionLost() {
|
|
274
|
-
for (const peerId of this._Peers.keys()) {
|
|
275
|
-
this._HandlePeerLeft(peerId);
|
|
276
|
-
}
|
|
277
|
-
for (const [capture, entry] of this._Captures) {
|
|
278
|
-
if (entry.media === undefined)
|
|
279
|
-
continue;
|
|
280
|
-
if (capture instanceof VideoCapture) {
|
|
281
|
-
// @TODO Check if it needs implementation
|
|
282
|
-
logger.debug(['Connection', 'Video'], '_HandleConnectionLost() VideoCapture');
|
|
283
|
-
}
|
|
284
|
-
if (capture instanceof AudioCapture) {
|
|
285
|
-
capture.stop(this._Connection, entry.media);
|
|
286
|
-
entry.media = undefined;
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
this._AvailableMediaIds.length = 0;
|
|
290
|
-
}
|
|
291
|
-
_HandleRoomUpdate(update) {
|
|
292
|
-
if (update.kind === 'Joined') {
|
|
293
|
-
assert(this._AvailableMediaIds.length === 0, 'left over media ids');
|
|
294
|
-
this._AvailableMediaIds.push(...update.media_ids);
|
|
295
|
-
this._HandleRoomJoined();
|
|
296
|
-
for (const peer of update.room.peers) {
|
|
297
|
-
this._HandlePeerJoined(peer);
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
else if (update.kind === 'PeerJoined') {
|
|
301
|
-
this._HandlePeerJoined(update.peer);
|
|
302
|
-
}
|
|
303
|
-
else if (update.kind === 'PeerLeft') {
|
|
304
|
-
this._HandlePeerLeft(update.peer_id);
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
_HandleRoomJoined() {
|
|
308
|
-
for (const [capture, entry] of this._Captures) {
|
|
309
|
-
if (this._AvailableMediaIds.length === 0 ||
|
|
310
|
-
this.mainConnection === undefined) {
|
|
311
|
-
continue;
|
|
312
|
-
}
|
|
313
|
-
entry.media = {
|
|
314
|
-
connectionId: this.mainConnection.id,
|
|
315
|
-
mediaId: this._AvailableMediaIds.shift(),
|
|
316
|
-
};
|
|
317
|
-
if (capture instanceof AudioCapture) {
|
|
318
|
-
capture.start(this._Connection, entry.media, entry.uid);
|
|
319
|
-
}
|
|
320
|
-
if (capture instanceof VideoCapture) {
|
|
321
|
-
// @TODO Check if it needs implementation
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
_HandlePeerUpdate(update) {
|
|
326
|
-
if (update.kind === 'MediaStarted') {
|
|
327
|
-
this._HandleMediaStart(update.peer_id, update.media);
|
|
328
|
-
}
|
|
329
|
-
else if (update.kind === 'MediaStopped') {
|
|
330
|
-
this._HandleMediaStopped(update.peer_id, update.media_id);
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
_HandlePeerJoined(peer) {
|
|
334
|
-
if (!this._Peers.has(peer.id)) {
|
|
335
|
-
this._Peers.set(peer.id, { mediaIds: new Map() });
|
|
336
|
-
}
|
|
337
|
-
for (const media of peer.medias) {
|
|
338
|
-
this._HandleMediaStart(peer.id, media);
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
_HandlePeerLeft(peerId) {
|
|
342
|
-
const entry = this._Peers.get(peerId);
|
|
343
|
-
if (entry === undefined)
|
|
344
|
-
return;
|
|
345
|
-
for (const mediaId of entry.mediaIds.keys()) {
|
|
346
|
-
this._HandleMediaStopped(peerId, mediaId);
|
|
347
|
-
}
|
|
348
|
-
this._Peers.delete(peerId);
|
|
349
|
-
}
|
|
350
|
-
_HandleMediaStart(peerId, media) {
|
|
351
|
-
var _a, _b;
|
|
352
|
-
if (this.mainConnection === undefined)
|
|
353
|
-
return;
|
|
354
|
-
const peer = this._Peers.get(peerId);
|
|
355
|
-
if (peer === undefined ||
|
|
356
|
-
(media.properties.kind !== 'audio' && media.properties.kind !== 'video')) {
|
|
357
|
-
return;
|
|
358
|
-
}
|
|
359
|
-
const uid = String((_a = media.properties.uid) !== null && _a !== void 0 ? _a : generateUUID());
|
|
360
|
-
media.properties.uid = uid; // feed the uid back, so it can be seen be seen down the chain.
|
|
361
|
-
const entry = (_b = this._Playbacks.get(uid)) !== null && _b !== void 0 ? _b : {
|
|
362
|
-
media: undefined,
|
|
363
|
-
playback: undefined,
|
|
364
|
-
};
|
|
365
|
-
this._Playbacks.set(uid, entry);
|
|
366
|
-
peer.mediaIds.set(media.id, uid);
|
|
367
|
-
entry.media = {
|
|
368
|
-
connectionId: this.mainConnection.id,
|
|
369
|
-
mediaId: media.id,
|
|
370
|
-
};
|
|
371
|
-
if (entry.playback !== undefined) {
|
|
372
|
-
if (entry.playback instanceof AudioPlayback) {
|
|
373
|
-
entry.playback.start(entry.media);
|
|
374
|
-
}
|
|
375
|
-
if (entry.playback instanceof VideoPlayback) {
|
|
376
|
-
logger.debug(['Video'], '_HandleMediaStart() VideoPlayback');
|
|
377
|
-
this.mainConnection.startRemoteVideo(entry.media.mediaId).then();
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
_HandleMediaStopped(peerId, mediaId) {
|
|
382
|
-
var _a;
|
|
383
|
-
const peer = this._Peers.get(peerId);
|
|
384
|
-
if (peer === undefined)
|
|
385
|
-
return;
|
|
386
|
-
const uid = peer === null || peer === void 0 ? void 0 : peer.mediaIds.get(mediaId);
|
|
387
|
-
if (uid === undefined)
|
|
388
|
-
return;
|
|
389
|
-
const entry = this._Playbacks.get(uid);
|
|
390
|
-
if (entry === undefined)
|
|
391
|
-
return;
|
|
392
|
-
if (entry.media !== undefined && entry.playback !== undefined) {
|
|
393
|
-
if (entry.playback instanceof AudioPlayback) {
|
|
394
|
-
entry.playback.stop(entry.media);
|
|
395
|
-
}
|
|
396
|
-
if (entry.playback instanceof VideoPlayback) {
|
|
397
|
-
logger.debug(['Video', '_HandleMediaStopped() VideoPlayback']);
|
|
398
|
-
(_a = this.mainConnection) === null || _a === void 0 ? void 0 : _a.stopRemoteVideo(entry.media.mediaId);
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
entry.media = undefined;
|
|
402
|
-
peer.mediaIds.delete(mediaId);
|
|
403
|
-
}
|
|
404
|
-
}
|
|
1
|
+
"use strict";var pe=Object.create;var B=Object.defineProperty;var fe=Object.getOwnPropertyDescriptor;var he=Object.getOwnPropertyNames;var Ce=Object.getPrototypeOf,be=Object.prototype.hasOwnProperty;var Y=(i,e)=>{for(var t in e)B(i,t,{get:e[t],enumerable:!0})},Z=(i,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of he(e))!be.call(i,o)&&o!==t&&B(i,o,{get:()=>e[o],enumerable:!(n=fe(e,o))||n.enumerable});return i};var ee=(i,e,t)=>(t=i!=null?pe(Ce(i)):{},Z(e||!i||!i.__esModule?B(t,"default",{value:i,enumerable:!0}):t,i)),ye=i=>Z(B({},"__esModule",{value:!0}),i);var Je={};Y(Je,{createPlugin:()=>je,logger:()=>a});module.exports=ye(Je);var h=require("@4players/odin-common");var q=require("@4players/odin-common");var x=require("@4players/odin-common");var l={};Y(l,{AdditionalCaptureBlocks:()=>ge,AdditionalPlaybackBlocks:()=>ke,ClockJumpCheckInterval:()=>xe,ClockJumpThreshold:()=>Se,ConnectInterval:()=>Be,ConnectTimeout:()=>We,DefaultRefreshRate:()=>_e,HealthCheckTimeout:()=>we,MainConnectionPingInterval:()=>Te,MaxNumberOfDecoders:()=>Pe,MinDBFs:()=>$,MinTokenLifetime:()=>Ve,MinTokenRefreshInterval:()=>De,PlaybackPreload:()=>ve,RenderQuantumSize:()=>Me,VadPreload:()=>Re,WebRtcIceTimeout:()=>Ie,WebRtcTimeout:()=>Ae});var ke=3,ge=3,ve=3,_e=250,Re=3,Pe=512,Me=128,$=-758.596,we=1e3,xe=1e3,Se=1e4,Te=void 0,Ie=3e3,Ae=1e4,We=5e3,Ve=6e4,De=5e3,Be=[0,100,200,200,500,1e3,2e3];function te(i,e){let t=ne(i,e);return t!==void 0?t-e:void 0}function ne(i,e){if(e>=i.length)return;let t=i[e++];switch(t){case 192:case 194:case 195:return e;case 196:return m(i,e+1,G(i,e));case 197:return m(i,e+2,A(i,e));case 198:return m(i,e+4,W(i,e));case 199:return m(i,e+2,G(i,e));case 200:return m(i,e+3,A(i,e));case 201:return m(i,e+5,W(i,e));case 202:return m(i,e,4);case 203:return m(i,e,8);case 204:return m(i,e,1);case 205:return m(i,e,2);case 206:return m(i,e,4);case 207:return m(i,e,8);case 208:return m(i,e,1);case 209:return m(i,e,2);case 210:return m(i,e,4);case 211:return m(i,e,8);case 212:return m(i,e,2);case 213:return m(i,e,3);case 214:return m(i,e,5);case 215:return m(i,e,9);case 216:return m(i,e,17);case 217:return m(i,e+1,G(i,e));case 218:return m(i,e+2,A(i,e));case 219:return m(i,e+4,W(i,e));case 220:return I(i,e+2,A(i,e));case 221:return I(i,e+4,W(i,e));case 222:return I(i,e+2,A(i,e)*2);case 223:return I(i,e+4,W(i,e)*2);default:if(!(t&128))return e;if((t&240)===128)return I(i,e,(t&15)*2);if((t&240)===144)return I(i,e,t&15);if((t&224)===160)return m(i,e,t&31);if((t&224)===224)return e;throw new Error("invalid msgpack")}}function m(i,e,t){let n=e+t;return isFinite(n)?n:void 0}function I(i,e,t){if(isFinite(t)){for(let n=0;n<t;n++){let o=ne(i,e);if(o===void 0)return;e=o}return e}}function G(i,e){return e>i.length?NaN:i[e]}function A(i,e){return e+2>i.length?NaN:i[e]<<8|i[e+1]}function W(i,e){return e+4>i.length?NaN:i[e]<<24|i[e+1]<<16|i[e+2]<<8|i[e+3]}var oe="1.0.0-alpha.21";var ie=require("@4players/odin-common"),a=new ie.Logger("odin-plugin-web",{tags:[]});var V={isSilent:!0,rmsDBFS:-758.596};var z=!1;async function re(i,e,t){let n=e.device?{exact:e.device.device.deviceId}:void 0,o;try{let c=await Promise.race([navigator.mediaDevices.getUserMedia({audio:{echoCancellation:t.echoCanceller,noiseSuppression:t.noiseSuppression!=="None",autoGainControl:t.gainController,echoCancellationType:"system",deviceId:n}}),(0,x.sleep)(3e4,"timeout")]);if(c==="timeout")return(0,x.failure)("getUserMedia; timeout");z=!0,o=c}catch(c){return(0,x.failure)(`getUserMedia; ${c}`)}let r=o.getAudioTracks()[0].getSettings().sampleRate,s=await i.audioContexts.acquire(r);return(0,x.success)(s.createMediaStreamSource(o))}function D(i){i.getTracks().forEach(e=>{e.stop(),i.removeTrack(e)})}async function E(){z=await Ee();let i=await navigator.mediaDevices.enumerateDevices(),e=[],t=0,n=0;for(let o of i)o.kind==="audioinput"&&(n===0?e.push(new w(o,!0)):e.push(new w(o,!1)),n++),o.kind==="audiooutput"&&(t===0?e.push(new w(o,!0)):e.push(new w(o,!1)),t++);return e}async function Ee(){if(z)return!0;let i={};i.audio={sampleRate:void 0,deviceId:"",autoGainControl:!1,noiseSuppression:!1,advanced:void 0,aspectRatio:void 0,echoCancellation:!1,channelCount:0,facingMode:void 0,frameRate:0},a.debug(["Device"],"requestMediaPermissions(): constraints",i);try{let e=await navigator.mediaDevices.getUserMedia(i);e?.getTracks().forEach(t=>{t.stop(),e.removeTrack(t)})}catch(e){return a.error(["Device"],"requestMediaPermissions(): Requesting permissions for audio failed.",e),!1}return!0}var ae=require("@4players/odin-common");var se=(0,ae.isSharedArrayBufferCapable)()?import("../worker/shared"):import("../worker/unshared");function ce(i,e){return se.then(t=>t.createCaptureWorker(i,e,l.DefaultRefreshRate))}async function de(i){return se.then(e=>e.createPlaybackWorker(i,l.DefaultRefreshRate))}var Ue={voiceActivity:{attackThreshold:.9,releaseThreshold:.8},volumeGate:{attackThreshold:-30,releaseThreshold:-40}},Ne={echoCanceller:!1,highPassFilter:!1,preAmplifier:!1,captureLevelAdjustment:!1,noiseSuppression:"None",transientSuppressor:!1,gainController:!1},R=class{constructor(e,t){this.controller=e;this._Medias=new Map;this._Vad=Ue;this._Apm=Ne;this._Volume=1;this._ProcessingSetDevice=!1;this._DeviceCb=async()=>{a.debug(["Audio"],"AudioCapture._DeviceCb()"),(await E()).some(n=>n.device.deviceId===this._DeviceParameters?.device?.device.deviceId)||(this._DeviceParameters?.onStatusChanged?.("stopped"),navigator.mediaDevices.removeEventListener("devicechange",this._DeviceCb))};t.apm&&this.setApm(t.apm),t.vad&&this.setVad(t.vad),t.volume&&this.setVolume(t.volume)}get vad(){return this._Vad}get apm(){return this._Apm}get volume(){return this._Volume}get activity(){return this._Capture?.worker.activity??V}get speechProbability(){return this._Capture?.worker.speechProbability??0}get deviceId(){return this._DeviceParameters?.device?.device.deviceId}start(e,t,n){a.debug(["Audio"],"AudioCapture.start()",t);let o=this._Medias.get(t.connectionId);o===void 0&&this._Medias.set(t.connectionId,o=new Set),!o.has(t.mediaId)&&(o.add(t.mediaId),e.mainConnection?.id===t.connectionId&&e.typedRequest("StartMedia",{media_id:t.mediaId,properties:{kind:"audio",uid:n}}).catch(r=>console.error(`StartMedia got rejected; ${r}`)),this._UpdateMedias())}stop(e,t){a.debug(["Audio"],"AudioCapture.stop()",t),this._Medias.get(t.connectionId)?.delete(t.mediaId)&&(e.mainConnection?.id===t.connectionId&&e.typedRequest("StopMedia",{media_id:t.mediaId}).catch(o=>console.error(`StopMedia got rejected; ${o}`)),this._UpdateMedias())}setVad(e){a.debug(["Audio"],"AudioCapture.setVad() config",e),this._Vad=e,this._Capture&&this._Capture.worker.setVad(e)}async setApm(e){a.debug(["Audio"],"AudioCapture.setApm() config",e),this._Apm=e,this._DeviceParameters&&await this.setDevice(this._DeviceParameters)}async setDevice(e){a.debug(["Device"],"AudioCapture.setDevice() parameters: ",e);let t=this._DeviceParameters;if(this._DeviceParameters=e,this._Volume==="muted")return;let n=await re(this.controller,e,this._Apm);if(n.type!=="Success"){a.error(["Device",`AudioCapture.setDevice() Unable to open capture device; ${n.reason}`]),this._DeviceParameters=t;return}this._DeviceParameters.onStatusChanged&&navigator.mediaDevices.addEventListener("devicechange",this._DeviceCb),this._Capture!==void 0&&(D(this._Capture.source.mediaStream),this._Capture.source.disconnect(this._Capture.worker.worklet)),this._Capture===void 0||this._Capture.worker.worklet.context!==n.value.context?(this._Capture?.worker.close(),this._Capture={worker:await ce(n.value.context,this.controller.sink),source:n.value},await this._UpdateMedias(),await this.setVolume(this._Volume),this.setVad(this.vad)):this._Capture.source=n.value,this._Capture.source.connect(this._Capture.worker.worklet)}async setVolume(e){a.debug(["Audio"],"AudioCapture.setVolume()",e),this._Volume=e,!this._ProcessingSetDevice&&(this._ProcessingSetDevice=!0,e==="muted"?await this.close():(!this._Capture&&this._DeviceParameters&&await this.setDevice(this._DeviceParameters),this._Capture?.worker.setVolume(e)),this._ProcessingSetDevice=!1,e!==this._Volume&&await this.setVolume(this._Volume))}async close(){a.debug(["Audio"],"AudioCapture.close()"),this._Capture!==void 0&&(this._Capture.worker.close(),D(this._Capture.source.mediaStream),this._Capture.source.disconnect(this._Capture.worker.worklet),await this.controller.audioContexts.release(this._Capture.source.context)),navigator.mediaDevices.removeEventListener("devicechange",this._DeviceCb),this._Capture=void 0}async _UpdateMedias(){if(a.debug(["Audio"],"AudioCapture._UpdateMedias()"),this._Capture!==void 0){let e=Array.from(this._Medias).map(([t,n])=>({id:t,mediaIds:Array.from(n)}));this._Capture.worker.setMedias(e)}}};var k=class{constructor(e,t){this.controller=e;this.customType=t;this._Medias=new Map}get mediaStream(){return this._mediaStream}async setDevice(e){this._mediaStream=e}start(e,t,n){let o=this._Medias.get(t.connectionId);if(o===void 0&&this._Medias.set(t.connectionId,o=new Set),!o.has(t.mediaId)&&(o.add(t.mediaId),e.mainConnection?.id===t.connectionId)){let r={kind:"video",codec:"VP8",uid:n};this.customType&&(r.customType=this.customType),e.typedRequest("StartMedia",{media_id:t.mediaId,properties:r}).catch(s=>console.error(`StartMedia got rejected; ${s}`))}}stop(e,t){this._mediaStream&&D(this._mediaStream),this._Medias.get(t.connectionId)?.delete(t.mediaId)&&e.mainConnection?.id===t.connectionId&&e.typedRequest("StopMedia",{media_id:t.mediaId}).catch(o=>console.error(`StopMedia got rejected; ${o}`))}close(){throw new Error("Method not implemented.")}};var g=class{constructor(e,t){this.controller=e;this.uid=t}get volume(){return this.controller.playbackGetVolume(this.uid)}get activity(){return this.controller.playbackGetActivity(this.uid)}start(e){a.debug(["Audio"],"AudioPlayback.start()",e),this.controller.playbackStart(this.uid,e)}stop(e){a.debug(["Audio"],"AudioPlayback.stop()",e),this.controller.playbackStop(this.uid,e)}async setVolume(e){a.debug(["Audio"],"AudioPlayback.setVolume()",e),this.controller.playbackSetVolume(this.uid,e)}close(){a.debug(["Audio"],"AudioPlayback.close()"),this.controller.closePlayback(this.uid)}};var U=class{constructor(e){this.audioContextFactory=e;this.entries=[]}async acquire(e){for(let n of this.entries){if(n.sampleRate===e)return n.references+=1,n.audioContext;if(n.sampleRate===void 0&&n.audioContext.sampleRate===e)return n.references+=1,n.audioContext}let t={sampleRate:e,audioContext:await this.audioContextFactory(e),references:1};return this.entries.push(t),t.audioContext}async release(e){let t=this.entries.findIndex(n=>n.audioContext===e);if(!(t<0)&&(this.entries[t].references-=1,this.entries[t].references<=0)){let n=this.entries[t].audioContext;this.entries.splice(t,1),await n.close()}}close(){for(let e of this.entries)e.audioContext.close()}},N=class i{constructor(e,t){this.audioContexts=e;this.sink=t;this.globalVolume=1;this.playbacks=new Map;this.setPlaybackStrand=new q.Strand}async create(e,t){return new i(e,t)}receivePacket(e,t){this.playbackWorker?.receivePacket(e,t)}async createAudioPlayback(e){let t=this.playbacks.get(e.uid),n=e.volume;if(t===void 0){n=n??1;let o=new g(this,e.uid);t={id:void 0,volume:n,playback:o},this.playbacks.set(e.uid,t)}return n!==void 0&&t.playback.setVolume(n),t.playback}async createAudioCapture(e){let t=new R(this,e);return await t.setDevice(e),t}async createVideoCapture(e,t){let n=new k(this,t);return await n.setDevice(e),n}playbackSetDevice(e){return this.setPlaybackStrand.enqueue(async()=>{let t=e?.device.deviceId??"";t==="default"&&(t="");let n;this.playbackWorker===void 0?(n=await this.audioContexts.acquire(48e3),this.playbackWorker=await de(n),this.playbackWorker.setGlobalVolume(this.globalVolume)):n=this.playbackWorker.audioContext,n?.setSinkId!==void 0&&e&&n?.state==="running"&&await n.setSinkId(t)})}playbackSetGlobalVolume(e){this.globalVolume=e==="muted"?0:e,this.playbackWorker?.setGlobalVolume(this.globalVolume)}playbackStart(e,t){let n=this.playbacks.get(e);n!==void 0&&(n.id!==void 0&&this.playbackStop(e,n.id),n.id=t,this.playbackWorker?.addMedias(t.connectionId,[t.mediaId]),this.playbackSetVolume(e,n.volume))}playbackStop(e,t){let n=this.playbacks.get(e);n!==void 0&&(n.id!==t&&console.error("playback mediaId out of sync!"),n.id=void 0,this.playbackWorker?.removeMedias(t.connectionId,[t.mediaId]))}playbackGetActivity(e){let t=this.playbacks.get(e);return t?.id===void 0?V:this.playbackWorker?.getActivity(t.id)??V}playbackSetVolume(e,t){let n=this.playbacks.get(e);n!==void 0&&(n.volume=t==="muted"?0:t,n.id!==void 0&&this.playbackWorker?.setVolume(n.id,n.volume))}playbackGetVolume(e){return this.playbacks.get(e)?.volume??1}closePlayback(e){let t=this.playbacks.get(e);t!==void 0&&(t.id!==void 0&&this.playbackStop(e,t.id),this.playbacks.delete(e))}close(){this.playbackWorker?.close(),this.audioContexts.close()}},w=class{constructor(e,t=!1){this.device=e;this.isDefault=t}get type(){switch(this.device.kind){case"audioinput":return"AudioCapture";case"audiooutput":return"AudioPlayback";default:(0,q.assert)(!1,"not supported device")}}get name(){return this.device.label}};var T=require("@4players/odin-common");var Q=require("@4players/odin-common");var p=require("@4players/odin-common");var y=require("@4players/odin-common"),v=class i{constructor(e,t){this.text=e;this.claims=t;if(t.ups!==void 0){let n=i.parse(t.ups);n.type==="Success"?this._Upstream=n.value:console.error(`upstream in token can not be parsed; ${n.reason}`)}}static parse(e){let t=e.split(".");if(t.length!==3)return(0,y.failure)("expected three segments");let n=(0,y.fromBase64Url)(t[1]);if(n.type==="Failure")return(0,y.failure)(`invalid token; ${n.reason}`);let o;try{o=JSON.parse(n.value)}catch(s){return(0,y.failure)(`invalid token; ${s}`)}let r=y.TokenClaimsSchema.safeParse(o);return r.success===!1?(0,y.failure)(`invalid token; ${r.error}`):(0,y.success)(new i(e,r.data))}get timeLeft(){return this.claims.exp===void 0?1/0:this.claims.exp*1e3-Date.now()}get upstream(){return this._Upstream}get defaultRoomId(){return Array.isArray(this.claims.rid)?this.claims.rid.length===1?this.claims.rid[0]:void 0:this.claims.rid}hasAudience(e){return this.claims.aud===void 0?!0:Array.isArray(this.claims.aud)?this.claims.aud.includes(e):this.claims.aud===e}toString(){return this.text}};var b=require("@4players/odin-common");var P=class{constructor(e){this.stream=e;this._SendQueue=[];this._RecvQueue=[];this._EventQueue=[];this._Requests=new Map;this._Close=new AbortController;this._NextId=1;e instanceof WebSocket?(e.addEventListener("open",this._OnWebSocketOpen.bind(this)),e.addEventListener("close",this.close.bind(this)),e.addEventListener("message",t=>this._OnRpcMessage(t.data)),e.binaryType="arraybuffer"):(Le(e.readable.getReader(),this._OnRpcMessage.bind(this)).finally(this.close.bind(this)),this._Writer=e.writable.getWriter()),this._Close.signal.addEventListener("abort",this._Cleanup.bind(this),{once:!0})}get closed(){return this._Close.signal.aborted}get closeSignal(){return this._Close.signal}async request(e,t,n){if(this.closed)return Promise.resolve("closed");let o=this._NextId,r=n===void 0?void 0:setTimeout(()=>this._TimeoutRequest(o),n);return this._Send([0,o,e,t]),new Promise(s=>{this._Requests.set(o,{accept:s,timeoutId:r}),this._NextId+=1})}notify(e,t){this.closed||this._Send([2,e,t])}recv(){if(this.closed)return Promise.resolve("closed");let e=this._EventQueue.shift();return e!==void 0?Promise.resolve(e):new Promise(t=>this._RecvQueue.push(t))}close(){this._Close.abort()}_TimeoutRequest(e){let t=this._Requests.get(e);t!==void 0&&(this._Requests.delete(e),clearTimeout(t.timeoutId),t.accept("timeout"))}_Send(e){let t=e instanceof Uint8Array?e:new Uint8Array((0,b.msgpackEncode)(e));this.stream instanceof WebSocket?this.stream.send(t):this._Writer.write(t)}_OnWebSocketOpen(e){for(let t of this._SendQueue)this._Send(t);this._SendQueue.length=0}_OnRpcMessage(e){let t=qe(e);if(t.type!=="Success"){this.close();return}switch(t.value[0]){case 0:this._Send([1,t.value[1],"not supported",void 0]);break;case 1:this._OnResponse(t.value[1],t.value[2],t.value[3]);break;case 2:this._OnEvent(t.value[1],t.value[2]);break}}_OnResponse(e,t,n){let o=this._Requests.get(e);o!==void 0&&(clearTimeout(o.timeoutId),o.accept(t===null?(0,b.success)(n):(0,b.failure)(t)))}_OnEvent(e,t){let n=this._RecvQueue.shift();n!==void 0?n({name:e,properties:t}):this._EventQueue.push({name:e,properties:t})}_Cleanup(){for(let e of this._Requests.values())clearTimeout(e.timeoutId),e.accept("closed");this._Requests.clear();for(let e of this._RecvQueue)e("closed");this._RecvQueue.length=0,this._SendQueue.length=0,this.stream instanceof WebSocket?this.stream.close():this._Writer?.close().catch(()=>{})}},S=class{constructor(e,t,n){this.rpcConnection=e;this._Commands=t;this._Events=n}get closed(){return this.rpcConnection.closed}get closeSignal(){return this.rpcConnection.closeSignal}async request(e,t,n){t=this._Commands[e].request.parse(t);let o=await(n!==void 0?this.rpcConnection.request(e,t,n):this.rpcConnection.request(e,t));if(o==="closed"||o==="timeout"||o.type==="Failure")return o;let r=this._Commands[e].response.safeParse(o.value);return r.success?(o.value=r.data,o):(0,b.failure)(`response schema failed validation: ${r.error}`)}notify(e,t){t=this._Commands[e].request.parse(t),this.rpcConnection.notify(e,t)}async recv(){let e=await this.rpcConnection.recv();if(e==="closed")return e;let t=j(this._Events,e);return t!==void 0?t:(ue(this._Events,e.name)?(console.error(`schema validation for ${e.name} failed; closing connection`),this.rpcConnection.close()):console.warn(`dropping unknown event ${e.name}`),"closed")}close(){this.rpcConnection.close()}};function qe(i){try{return(0,b.success)(b.MessagePackRpcSchema.parse((0,b.msgpackDecode)(i)))}catch(e){return(0,b.failure)(String(e))}}function j(i,e){if(!ue(i,e.name))return;let t=i[e.name].safeParse(e.properties);if(t.success)return{name:e.name,properties:t.data}}function ue(i,e){return e in i}async function Le(i,e){let t=new Uint8Array;for(;;){let n;try{n=await i.read()}catch{break}if(n.done)break;t=He(t,n.value);let o=0;for(let r;r=te(t,o),!(!r||o+r>t.length);o+=r)e(t.subarray(o,o+r));t=t.subarray(o)}t.length>0&&console.warn("unprocessed bytes left in rpc stream",t)}function He(i,e){if(i.length===0)return e;if(e.length===0)return i;let t=new Uint8Array(i.length+e.length);return t.set(i,0),t.set(e,i.length),t}var _=require("@4players/odin-common");async function J(i,e){let t=performance.now(),n=(0,_.failure)("no connection was attempted");for(let o=0;o<l.ConnectInterval.length&&!e.aborted;o++){let r=t+Fe(o);if(await(0,_.abortableSleep)(r-performance.now(),e)==="aborted")return(0,_.failure)("aborted");if(n=await Oe(i,e),n.type==="Success")return n}return n}async function Oe(i,e){let t;try{t=new WebSocket(i)}catch(u){return(0,_.failure)(`unable to create websocket; ${u}`)}let n=new AbortController,o=new Promise(u=>t.addEventListener("open",()=>u((0,_.success)(t)),{signal:n.signal})),r=new Promise(u=>t.addEventListener("close",C=>u((0,_.failure)(C.reason)),{signal:n.signal})),s=new Promise(u=>e.addEventListener("abort",()=>u((0,_.failure)("aborted")),{signal:n.signal})),c=await Promise.race([o,r,s]);return n.abort(),c}function Fe(i){let e=l.ConnectInterval.slice(0,i).reduce((n,o)=>n+o,0),t=l.ConnectInterval[i]*Math.random();return e+t}var L=class i{constructor(e,t,n,o,r,s){this.id=e;this.address=t;this.url=n;this.onDatagram=o;this.connection=r;this.webRtc=s;this._PeerIdMap=new WeakMap;this._LocalVideos=new Map;this._RemoteVideos=new Map;this._Negotiation={requested:!1,running:!1};this._awaitingRemoteTracks=new Map;a.info(["Connection","WebRTC"],"WebRtcMainConnection.constructor()"),this.closeSignal.addEventListener("abort",()=>this._CleanUp(),{once:!0}),this._ClosePromise=new Promise(c=>{this.closeSignal.addEventListener("abort",()=>{a.debug(["Connection","WebRTC"],"WebRtcMainConnection.constructor(): _ClosePromise aborted"),c("closed")},{once:!0})}),this._AudioChannel=this.webRtc.createDataChannel("audio",{id:2,negotiated:!0,ordered:!1,maxRetransmits:0}),this._AudioChannel.binaryType="arraybuffer",this._AudioChannel.onmessage=c=>o(this.id,new Uint8Array(c.data)),this.webRtc.onicecandidate=this._OnIceCandidate.bind(this),this.webRtc.onconnectionstatechange=()=>this._OnWebRtcStatusChanged(),this.webRtc.ontrack=this.onTrack.bind(this),this._SendOffer(),l.MainConnectionPingInterval!==void 0&&(this._PeriodicHealthCheck=setInterval(()=>this.healthCheck(),l.MainConnectionPingInterval)),this._readWebRtcUpdate()}static async create(e,t,n,o){if(o.aborted)return(0,p.failure)("aborted");let r;try{r=new URL(`wss://${t}`)}catch(C){return(0,p.fail)(`address in token(${t}) can not be parsed; ${C}`)}let s=(0,p.extendUrl)(r,"main");if(s.type!=="Success")return s;let c=await J(s.value,o);if(c.type!=="Success")return c;let u=new S(new P(c.value),p.MainCommandsRpc,p.MainNotificationsRpc);try{return(0,p.success)(new i(e,t,r,n,u,new RTCPeerConnection))}catch(C){return u.close(),(0,p.failure)(`unable to create webrtc connection; ${C}`)}}get transport(){return p.Backend.Transport.H3}get closePromise(){return this._ClosePromise}get closeSignal(){return this.connection.closeSignal}get closed(){return this.connection.closed}get status(){switch(this.webRtc.connectionState){case"connected":return 1;case"failed":case"closed":return 2;default:return 0}}async waitForConnected(){return new Promise((e,t)=>{let n=()=>{let o=this.status;o!==0&&(this.webRtc.removeEventListener("connectionstatechange",n),o===1?e():t(new Error("connection closed")))};this.webRtc.addEventListener("connectionstatechange",n),n()})}close(){console.debug("MainConnection close(): ",this._PeerIdMap),a.log(["WebRTC","Connection"],"MainConnection close(): ",this._PeerIdMap),this.connection.close()}async joinRoom(e,t,n,o){a.info(["Connection","WebRTC"],"WebRtcMainConnection.joinRoom()");let r=await this.connection.request("JoinRoom",{token:String(e),room_id:t,user_data:n,position:o});if(r==="closed"||r.type==="Failure")return r;let s=r.value;if(!("token"in s))return(0,p.failure)("JoinRoom responded without token");let c=(0,p.extendUrl)(this.url,`room?token=${s.token}`);if(c.type==="Failure")return c;let u=await J(c.value,this.closeSignal);return u.type==="Success"?(this._PeerIdMap.set(u.value,s.peer_id),(0,p.success)(new P(u.value))):u}async getReconnectToken(e){let t=await this._Request("RequestReconnectToken",{peer_id:e});return t.type==="Failure"?t:v.parse(t.value)}sendDatagram(e){this._AudioChannel.readyState==="open"&&this._AudioChannel.send(e)}async healthCheck(){let e=await this._Request("Ping",{},l.HealthCheckTimeout),t=e!=="timeout"&&e.type==="Success";return t||this.close(),t}async _Request(e,t,n){let o=await(n===void 0?this.connection.request(e,t):this.connection.request(e,t,n));return o==="closed"?(0,p.failure)("connection lost"):o}async _readWebRtcUpdate(){for(;;){let e=await this.connection.recv();if(e==="closed")break;e.name==="WebRtcUpdate"&&this._OnWebRtcUpdate(e.properties)}this.close()}async _SendOffer(){this.ensureTransceivers();try{let e=await this.webRtc.createOffer();(0,p.assert)(e.type==="offer"&&e.sdp,"Generated broken webrtc offer"),await this.webRtc.setLocalDescription(e);let t=e.sdp??"",n=[];for(let[o,{transceiver:r}]of this._LocalVideos)n.push([o,r.mid]);a.info(["Connection","WebRTC"],"WebRtcMainConnection._sendOffer(): media_map",n),await this._Request("WebRtcUpdate",{kind:"Sdp",type:"Offer",sdp:t,media_map:n})}catch{a.error(["Connection","WebRTC"],"WebRtcMainConnection._sendOffer(): Failed creating an offer")}}_OnWebRtcStatusChanged(){switch(a.info(["WebRTC","Connection"],`WebRtcMainConnection._OnWebRtcStatusChanged(): ${this.webRtc.connectionState}`),this.webRtc.connectionState){case"closed":case"failed":this.close();break;case"connected":clearTimeout(this._WebRtcIceTimeout),this._WebRtcIceTimeout=void 0,clearTimeout(this._WebRtcTimeout),this._WebRtcTimeout=void 0;break;case"disconnected":typeof l.MainConnectionPingInterval<"u"&&this.healthCheck().then();default:this._WebRtcIceTimeout===void 0&&(this._WebRtcIceTimeout=setTimeout(()=>{a.info(["WebRTC","Connection"],"WebRtcMainConnection._OnWebRtcStatusChanged(): _WebRtcIceTimeout was reached"),this.webRtc.restartIce(),this._WebRtcIceTimeout=void 0},l.WebRtcIceTimeout)),this._WebRtcTimeout===void 0&&(this._WebRtcTimeout=setTimeout(()=>{a.info(["WebRTC","Connection"],"WebRtcMainConnection._OnWebRtcStatusChanged(): _WebRtcTimeout was reached"),this.close()},l.WebRtcTimeout));break}}_OnWebRtcUpdate(e){switch(e.kind){case"Sdp":this._UpdateRemoteSdp(e);break;case"Trickle":this._AddIceCandidate(e);break;case"TrickleFinished":this.webRtc.addIceCandidate(void 0);break}}async _UpdateRemoteSdp(e){a.info(["WebRTC","Connection","Video"],"WebRtcMainConnection._UpdateRemoteSdp(): update.media_map, this._RemoteVideos",e.media_map,this._RemoteVideos);let t=!1;for(let[o,r]of e.media_map){let s=this._RemoteVideos.get(o);s?.state==="Requested"&&(t=!0,this._RemoteVideos.set(o,{state:"Mapped",mid:r,promise:s.promise}))}let n=e.type==="Answer"?"answer":"offer";a.info(["WebRTC","Connection","Video"],"WebRtcMainConnection._UpdateRemoteSdp(): remoteChanged: ",t),this._ResolveOffer?.(!0),await this.webRtc.setRemoteDescription({type:n,sdp:e.sdp})}_AddIceCandidate(e){let t={candidate:e.candidate,sdpMid:e.spd_mid??void 0,sdpMLineIndex:e.spd_mline_index??void 0,usernameFragment:e.username_fragment??void 0};t.sdpMid===void 0&&t.sdpMLineIndex===void 0&&(t.sdpMid="0"),this.webRtc.addIceCandidate(t)}async _OnIceCandidate(e){if(e.candidate){let{candidate:t,sdpMid:n,sdpMLineIndex:o,usernameFragment:r}=e.candidate;this._Request("WebRtcUpdate",{kind:"Trickle",candidate:t,spd_mid:n??void 0,spd_mline_index:o??void 0,username_fragment:r??void 0})}else this._Request("WebRtcUpdate",{kind:"TrickleFinished"})}ensureTransceivers(){let e=this._LocalVideos.size+this._RemoteVideos.size,t=this.webRtc.getTransceivers().length;for(let n=t;n<e;n++)this.webRtc.addTransceiver("video")}async startLocalVideo(e,t){(0,p.assert)(!this._LocalVideos.has(e),`No LocalVideo with mediaID ${e}`),a.debug(["WebRTC","Connection","Video"],"WebRtcMainConnection.startLocalVideo(): ",e);let n=this.webRtc.getTransceivers().find(o=>o.sender.track===t);if(n)a.debug(["WebRTC","Connection","Video"],"WebRtcMainConnection.startLocalVideo(): Transceiver found -> replaceTrack."),await n.sender.replaceTrack(t);else{let o=this.webRtc.addTrack(t);n=this.webRtc.getTransceivers().find(r=>r.sender===o),(0,p.assert)(n,"")}this._LocalVideos.set(e,{transceiver:n}),this.requestNegotiation(!1)}stopLocalVideo(e){let t=this._LocalVideos.get(e);t&&(t.transceiver.sender.track.enabled=!1,this._LocalVideos.delete(e),this.requestNegotiation(!1))}startRemoteVideo(e){return a.debug(["WebRTC","Connection","Video"],"WebRtcMainConnection.startRemoteVideo()"),this.requestNegotiation(!0),new Promise((t,n)=>{this._RemoteVideos.set(e,{state:"Requested",promise:{resolve:t,reject:n}})})}stopRemoteVideo(e){let t=this._RemoteVideos.get(e);t&&(this._RemoteVideos.delete(e),t.state!=="Active"&&t.promise.reject(new Error("video stopped")),this.requestNegotiation(!1))}onTrack(e){a.debug(["WebRTC","Connection","Video"],"WebRtcMainConnection.onTrack(): RemoteVideos: ",this._RemoteVideos);let t=Array.from(this._RemoteVideos).find(r=>r[1].state==="Mapped"&&r[1].mid===e.transceiver.mid);if(a.debug(["WebRTC","Connection","Video"],"WebRtcMainConnection.onTrack(): Found the RemoteVideo",t),t===void 0){a.warn(["WebRTC","Connection","Video"],`WebRtcMainConnection.onTrack(): Unexpected track(mid: ${e.transceiver.mid})`);return}let[n,o]=t;(0,p.assert)(o.state==="Mapped",""),this._RemoteVideos.set(n,{state:"Active",transceiver:e.transceiver}),e.track.onended=()=>a.debug(["WebRTC","Connection","Video"],`WebRtcMainConnection.onTrack(): Track ended ${e.transceiver.mid}`,t),o.promise.resolve(e.track)}requestNegotiation(e){a.debug(["WebRTC","Connection","Video"],"WebRtcMainConnection.requestNegotiation():"),this._Negotiation.requested=!0,!this._Negotiation.running&&(this._Negotiation.running=!0,setTimeout(async()=>{this._Negotiation.requested=!1;try{await this._SendOffer(),e&&await new Promise(t=>{this._ResolveOffer=t})}finally{a.debug(["WebRTC","Connection","Video"],"WebRtcMainConnection.requestNegotiation(): Finally"),this._Negotiation.running=!1;let t=Array.from(this._RemoteVideos).find(n=>n[1].state==="Requested");this._Negotiation.requested&&this.requestNegotiation(e)}}))}_CleanUp(){console.debug(`${this.url} closed`),this.connection.close(),this.webRtc.close(),clearInterval(this._PeriodicHealthCheck),clearTimeout(this._WebRtcIceTimeout),clearTimeout(this._WebRtcTimeout)}};var f=require("@4players/odin-common");var H=class i{constructor(e,t,n,o,r,s){this.id=e;this.address=t;this.connection=o;this.rpc=r;this.closeController=s;this.status=0;a.info(["Connection","H3"],"H3MainConnection.constructor()"),this.closePromise=(async()=>{try{await this.connection.closed}catch{}return"closed"})(),this.datagramWriter=o.datagrams.writable.getWriter(),this.connection.closed.then(()=>{this.status=2}).catch(()=>{}),this.connection.ready.then(()=>{this.status=1}).catch(()=>{}),Ge(this.connection,this.id,n)}get closeSignal(){return this.closeController.signal}get transport(){return f.Backend.Transport.H3}get closed(){return this.closeSignal.aborted}static async create(e,t,n,o){try{let r=new URL(`https://${t}?version=${oe}`),s=new AbortController;o.addEventListener("abort",()=>s.abort(),{once:!0});let c=new WebTransport(r);s.signal.addEventListener("abort",async()=>{try{await c.close()}catch{}},{once:!0}),await c.ready;let u=await c.createBidirectionalStream(),C=new S(new P(u),f.MainCommandsRpc,f.MainNotificationsRpc);return s.signal.addEventListener("abort",()=>C.close(),{once:!0}),C.notify("Hello",{stream:"main"}),(0,f.success)(new i(e,t,n,c,C,s))}catch(r){return(0,f.failure)(`connecting to ${t} failed with ${r}`)}}waitForConnected(){return Promise.race([this.connection.ready,this.connection.closed.then(()=>{throw new Error("connection closed")})])}close(){this.closeController.abort("request")}async joinRoom(e,t,n,o){try{if(this.closed)return"closed";let r=await this.connection.createBidirectionalStream().catch(()=>"closed");if(r==="closed")return"closed";let s=new S(new P(r),f.RoomCommandsRpc,f.RoomNotificationsRpc),c=await s.request("Hello",{stream:"room",token:String(e),room_id:t,user_data:n,position:o});return c==="closed"||c.type==="Failure"?c:(0,f.success)(s.rpcConnection)}catch(r){return(0,f.failure)(`JoinRoom failed; ${r}`)}}async getReconnectToken(e){let t=await this._Request("RequestReconnectToken",{peer_id:e});return t.type==="Failure"?t:v.parse(t.value)}async healthCheck(){let e=await this._Request("Ping",{},l.HealthCheckTimeout),t=e!=="timeout"&&e.type==="Success";return t||this.close(),t}sendDatagram(e){try{this.datagramWriter.write(e)}catch{}}startLocalVideo(e,t){throw new Error("not supported.")}stopLocalVideo(e){throw new Error("not supported.")}startRemoteVideo(e){throw new Error("not supported.")}stopRemoteVideo(e){throw new Error("not supported.")}async _Request(e,t,n){let o=await(n===void 0?this.rpc.request(e,t):this.rpc.request(e,t,n));return o==="closed"?(0,f.failure)("connection lost"):o}};async function Ge(i,e,t){try{let n=i.datagrams.readable.getReader();for(;;){let o=await n.read();if(o.done)break;t(e,o.value)}}catch{}}function le(i,e,t,n,o){switch(a.log(["Connection"],"createMainConnection() ",e,t),e){case Q.Backend.Transport.H3:return H.create(i,t,n,o);case Q.Backend.Transport.WebRTC:return L.create(i,t,n,o);default:throw new Error(`transport ${e} is not supported`)}}var O=class{constructor(e){this.onDatagram=e;this._LastConnectionId=0;this._ConnectionsByUrl=new Map;this._ConnectionsById=new Map;this._Close=new AbortController;let t=Date.now(),n=setInterval(()=>{Math.abs(Date.now()-t)>l.ClockJumpThreshold&&this.checkHealth(),t=Date.now()},l.ClockJumpCheckInterval);addEventListener("offline",()=>this.checkHealth(),{signal:this._Close.signal}),this._Close.signal.addEventListener("abort",()=>{clearInterval(n);for(let o of this._ConnectionsById.values())o.status==="ready"&&o.connection.close();this._ConnectionsById.clear(),this._ConnectionsByUrl.clear()},{once:!0})}get closed(){return this._Close.signal.aborted}sendDatagram(e,t){let n=this._ConnectionsById.get(e);n!==void 0&&n.status==="ready"&&n.connection.sendDatagram(t)}async acquireConnection(e,t,n){if(a.log(["Connection"],"ConnectionPool.acquireConnection(): transport, address",e,t),await(0,T.nextTick)(),this.closed)return"closed";let o=me(e,t),r=this._ConnectionsByUrl.get(o);if(r===void 0||r.status==="ready"&&r.connection.closed){let s=this._LastConnectionId++,c=le(s,e,t,this.onDatagram,n);r={status:"init",id:s,promise:c,references:1},this._ConnectionsByUrl.set(o,r),this._ConnectionsById.set(s,r);let u=await c;return a.log(["Connection"],"ConnectionPool.acquireConnection(): MainConnection resolved.",u),u.type!=="Success"?(this._ConnectionsByUrl.delete(o),this._ConnectionsById.delete(s),u):(r={status:"ready",connection:u.value,references:r.references},this._ConnectionsByUrl.set(o,r),this._ConnectionsById.set(s,r),(0,T.success)(r.connection))}else if(r.status==="init"){a.log(["Connection"],"ConnectionPool.acquireConnection(): Found a MainConnection but it is still in init state."),r.references+=1;let s=new Promise(u=>n.addEventListener("abort",()=>u(),{once:!0})),c=await Promise.race([r.promise,s]);return a.log(["Connection"],"ConnectionPool.acquireConnection(): MainConnection was in init state but is now resolved",c),c===void 0?(0,T.failure)("aborted"):c}else return a.log(["Connection"],"ConnectionPool.acquireConnection(): Found a MainConnection and return it"),r.references+=1,(0,T.success)(r.connection)}releaseConnection(e){a.log(["Connection"],"ConnectionPool.releaseConnection()");let t=this._ConnectionsById.get(e.id);if(a.log(["Connection"],"ConnectionPool.releaseConnection() entry refs",t?.references),t!==void 0){if(t.status==="init"){a.log(["Connection"],"ConnectionPool.releaseConnection(): MainConnection state is init, return.");return}t.references-=1,t.references===0&&(a.log(["Connection"],"ConnectionPool.releaseConnection(): References is 0, close the MainConnection."),t.connection.close(),this._ConnectionsByUrl.delete(me(e.transport,e.address)),this._ConnectionsById.delete(e.id))}}async checkHealth(){await Promise.all(Array.from(this._ConnectionsById.values()).map(e=>e.status==="ready"?e.connection.healthCheck():void 0))}close(){this._Close.abort()}};function me(i,e){return`${i} ${e}`}var d=require("@4players/odin-common");var F=class{constructor(e,t,n,o,r,s,c,u){this.connectionPool=e;this.transport=t;this.onEvent=n;this.gateway=o;this.roomId=s;this._Close=new AbortController;this._ConnectionFailures=0;this._FailureLimit=5;this._LastTokenRenew=performance.now();this._Closed=new Promise(e=>this._Close.signal.addEventListener("abort",()=>e("closed"),{once:!0}));this._Token=r,this._UserData=c,this._Position=u,this._Main().then(({reason:C})=>a.info(["Connection"],`RoomConnection: Main() resolved; ${C}`))}get token(){return this._Token}get userData(){return this._UserData}get position(){return this._Position}get closed(){return this._Close.signal.aborted}get mainConnection(){return this._MainConnection}async request(e,t){if(this._RoomConnection===void 0)return(0,d.failure)("currently not connected");let n=await this._RoomConnection.request(e,t);return n!=="closed"&&n.type==="Success"&&this._HandleRequests(e,t,n.value),n}async typedRequest(e,t){let n=d.RoomCommandsRpc[e],o=await this.request(e,t);if(o==="closed"||o.type==="Failure")return o;let r=n.response.safeParse(o.value);return r.success?(0,d.success)(r.data):(0,d.failure)(`received invalid response from ${e}; ${r.error}`)}close(){this._Close.abort()}get renewTokenIn(){let e=this._LastTokenRenew-performance.now()+l.MinTokenRefreshInterval,t=this._Token.timeLeft-l.MinTokenLifetime;return Math.max(e,t)}async _Main(){let e="unable to establish connection";for(;this._Close.signal.aborted===!1;){a.debug(["Connection","RoomConnection._Main() loop"]);let t=await this._ConnectToServer();if(t.type==="Failure"){e=t.reason;break}if(this._RoomConnection?.close(),this._RoomConnection=void 0,this._MainConnection=void 0,this._OwnPeerId=void 0,this.gateway!==void 0&&this._Token.hasAudience("gateway")){this._ChangeStatus("Joining",`requesting new token from ${this.gateway}`);let n=await ze(this.gateway,this._Token,this._Close.signal);if(n.type==="Success")if(n.value!=="rejected"){this._Token=n.value;continue}else{e="invalid or expired token";break}}if(this._Token.timeLeft<=0){e="unable to use expired token";break}await(0,d.sleep)(1e3)}return this._ChangeStatus("Closed",this._Close.signal.aborted?"user requested":e),this.close(),(0,d.failure)(e)}async _ConnectToServer(){let e=this._Token;for(;e.upstream!==void 0;)e=e.upstream;if(!e.hasAudience("sfu")||e.claims.adr===void 0)return(0,d.success)("continue");if(this._ChangeStatus("Joining",`connecting to ${e.claims.adr}`),this._MainConnection===void 0||this._MainConnection.closed||this._MainConnection.address!==e.claims.adr){this._MainConnection&&this.connectionPool.releaseConnection(this._MainConnection);let n=await this.connectionPool.acquireConnection(this.transport,e.claims.adr,this._Close.signal);if(n==="closed")return(0,d.failure)("connection pool closed");if(n.type==="Failure")return this._ConnectionFailures<this._FailureLimit?(this._ConnectionFailures++,(0,d.failure)(`Connection failed; ${n.reason}`)):(this._ChangeStatus("Joining",`connection failed; ${n.reason}`),(0,d.success)("continue"));this._MainConnection=n.value,this._OwnPeerId=void 0}let t=await Promise.race([Promise.all([this._MainConnection.joinRoom(e,this.roomId,this.userData,this.position)]),(0,d.sleep)(l.ConnectTimeout,"timeout"),this._Closed]);if(t==="closed")return(0,d.failure)("room connection closed");if(t!=="timeout"){let[n]=t;if(n==="closed")return(0,d.success)("continue");if(n.type==="Failure")return(0,d.failure)(`unable to join room; ${n.reason}`);this._RoomConnection=n.value,this._ChangeStatus("Joined",`joined ${this.roomId}`),this._MainConnection.waitForConnected().catch(o=>console.error("main connection failed to connect",o)),await this._HandleConnection(this._MainConnection)}return(0,d.success)("continue")}_ChangeStatus(e,t){let n={status:e,message:t};this.onEvent("RoomStatusChanged",n)}async _HandleConnection(e){if(this._RoomConnection===void 0)return;let t=new d.Selector([()=>(0,d.sleep)(this.renewTokenIn,"renewToken"),()=>(0,d.sleep)(500,"tick"),async()=>await this._RoomConnection?.recv(),()=>this._Closed,()=>e.closePromise]);for(;;){let n=await t.next();switch(n){case void 0:case"closed":a.debug(["Connection"],"RoomConnection._HandleConnection: closed"),this._RoomConnection?.close(),this.connectionPool.releaseConnection(e);return;case"renewToken":this._RenewToken();break;case"tick":break;default:this._HandleEvent(n);break}}}_HandleEvent(e){if(e.name==="RoomUpdated"){let t=d.RoomNotificationsRpc.RoomUpdated.safeParse(e.properties);if(!t.success)return;for(let n of t.data.updates)switch(n.kind){case"Joined":this._OwnPeerId=n.own_peer_id;break}}this.onEvent(e.name,e.properties)}_HandleRequests(e,t,n){switch(e){case"SetPeerPosition":{let o=d.RoomCommandsRpc.SetPeerPosition.request.safeParse(t);if(o.success){let r=o.data.position;this._Position=r.length===2?[r[0],r[1],0]:r}else console.warn("unable to process SetPeerPosition command");break}case"UpdatePeer":{let o=d.RoomCommandsRpc.UpdatePeer.request.safeParse(t);o.success?this._UserData=o.data.user_data:console.warn("unable to process UpdatePeer command");break}}}async _RenewToken(){if(this._LastTokenRenew=performance.now(),this.token.timeLeft>l.MinTokenLifetime)return;if(this._MainConnection===void 0||this._OwnPeerId===void 0)return(0,d.failure)("connection is not established");let e=await this._MainConnection.getReconnectToken(this._OwnPeerId);return e.type==="Success"?(e.value.timeLeft>this._Token.timeLeft&&(this._Token=e.value),(0,d.success)()):(0,d.failure)(`${this._MainConnection.address} rejected reconnect token request; ${e.reason}`)}};async function ze(i,e,t){try{let o=await(await fetch(i,{body:'{"jsonrpc":"2.0","id":1,"method":"Connect","params":{}}',method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${e}`},signal:t})).json();return"result"in o?v.parse(o.result.token):(console.error(`gateway rejected token; ${o.error?.message??"malformed response"}`),(0,d.success)("rejected"))}catch(n){return(0,d.failure)(String(n))}}var M=class{constructor(e,t){this.uid=e;this.customType=t}setMediaStream(e){this._mediaStream||(this._mediaStream=e)}get mediaStream(){return this._mediaStream}close(){throw new Error("Method not implemented.")}};function je(i){a.info(["Other"],"Using ODIN Plugin Web!");let e,t=new O((o,r)=>e.receivePacket(o,r)),n=new U(i);return e=new N(n,t.sendDatagram.bind(t)),new K(t,e)}var K=class{constructor(e,t){this.version="3";this.supportedTransports=[h.Backend.Transport.WebRTC,h.Backend.Transport.H3];this._NextRoomId=0;this._PlaybackVolume=1;this._ConnectionPool=e,this._MediaController=t}get playbackVolume(){return this._PlaybackVolume}joinRoom(e){return new X(this._NextRoomId++,this._ConnectionPool,e)}async createAudioPlayback(e){return console.log("Eyvor createAudioCapture: ",e),this._MediaController.createAudioPlayback(e)}async createAudioCapture(e){return this._MediaController.createAudioCapture(e)}async createVideoCapture(e,t){return this._MediaController.createVideoCapture(e,t)}async createVideoPlayback(e){return new M(e.uid,e.customType)}enumerateDevices(){return E()}setOutputVolume(e){this._MediaController.playbackSetGlobalVolume(e)}async setOutputDevice(e){await this._MediaController.playbackSetDevice(e.device)}close(){a.debug(["Other","Plugin.close()"]),this._MediaController.close(),this._ConnectionPool.close()}},X=class{constructor(e,t,n){this._Captures=new Map;this._Playbacks=new Map;this._Peers=new Map;this._AvailableMediaIds=[];let{gateway:o,onEvent:r}=n,s=(0,h.unwrap)(v.parse(n.token)),c=n.roomId??s.defaultRoomId,u=n.userData??new Uint8Array(0),C=n.position??[0,0,0];(0,h.assert)(c!==void 0,"room id required for multi-room-tokens"),this._Id=e,this._OnEvent=r,this._Connection=new F(t,n.transport??h.Backend.Transport.H3,this._HandleEvent.bind(this),o?new URL(o):void 0,s,c,u,C)}get mainConnection(){return this._Connection.mainConnection}get token(){return this._Connection.token.text}get cipher(){}request(e,t){return this._Connection.request(e,t)}async link(e){if(a.debug(["Audio"],"Room.link() media",e),e instanceof g){let t=this._Playbacks.get(e.uid)??{media:void 0,playback:void 0};t.playback!==void 0&&this.unlink(t.playback),t.media!==void 0&&e.start(t.media),t.playback=e,this._Playbacks.set(e.uid,t)}else if(e instanceof R){let t=this._Captures.get(e);t&&this.unlink(e);let n=this.mainConnection!==void 0&&this._AvailableMediaIds.shift();t={media:n?{connectionId:this.mainConnection.id,mediaId:n}:void 0,uid:(0,h.generateUUID)()},this._Captures.set(e,t),t.media!==void 0&&e.start(this._Connection,t.media,t.uid)}else if(e instanceof k){let t=this._Captures.get(e);if(t&&this.unlink(e),e.mediaStream){let n=this.mainConnection!==void 0&&this._AvailableMediaIds.shift();t={media:n?{connectionId:this.mainConnection.id,mediaId:n}:void 0,uid:(0,h.generateUUID)()},this._Captures.set(e,t),t.media!==void 0&&(this.mainConnection?.startLocalVideo(t.media.mediaId,e.mediaStream.getTracks()[0]),e.start(this._Connection,t.media,t.uid))}}else if(e instanceof M){a.log(["Video"],"Room.link() VideoPlayback: ",e);let t=this._Playbacks.get(e.uid)??{media:void 0,playback:void 0};if(a.log(["Video"],"Room.link() VideoPlayback entry: ",t),t.playback!==void 0&&this.unlink(t.playback),t.media!==void 0){a.log(["Video"],"Room.link() VideoPlayback before startRemoteVideo: ",t);let n=await this.mainConnection?.startRemoteVideo(t.media.mediaId);a.log(["Video"],"Room.link() VideoPlayback track: ",n),n&&e.setMediaStream(new MediaStream([n]))}t.playback=e,this._Playbacks.set(e.uid,t)}else throw new h.OdinError("invalid media")}unlink(e){if(a.debug(["Audio"],"Room.unlink() media",e),e instanceof g||e instanceof M){let t=this._Playbacks.get(e.uid);if(t?.playback!==e)return;t.playback=void 0,t.media!==void 0&&(e instanceof g?e.stop(t.media):(this.mainConnection?.stopRemoteVideo(t.media.mediaId),e.setMediaStream(void 0))),this._Playbacks.set(e.uid,t)}else if(e instanceof R||e instanceof k){let t=this._Captures.get(e);if(t===void 0)return;t.media!==void 0&&(e.stop(this._Connection,t.media),this._AvailableMediaIds.push(t.media.mediaId),e instanceof k&&this.mainConnection?.stopLocalVideo(t.media.mediaId)),this._Captures.delete(e)}else throw new Error("invalid media")}close(){a.debug(["Other","Room.close()"]),this._Connection.close()}_HandleEvent(e,t){let n=j(h.RoomNotificationsRpc,{name:e,properties:t});n!==void 0&&(this._HandleKnownEvent(n),t=n.properties),this._OnEvent(e,t)}_HandleKnownEvent(e){switch(e.name){case"RoomStatusChanged":e.properties.status!=="Joined"&&this._HandleConnectionLost();break;case"RoomUpdated":for(let t of e.properties.updates)this._HandleRoomUpdate(t);break;case"PeerUpdated":this._HandlePeerUpdate(e.properties);break}}_HandleConnectionLost(){for(let e of this._Peers.keys())this._HandlePeerLeft(e);for(let[e,t]of this._Captures)t.media!==void 0&&(e instanceof k&&a.debug(["Connection","Video"],"_HandleConnectionLost() VideoCapture"),e instanceof R&&(e.stop(this._Connection,t.media),t.media=void 0));this._AvailableMediaIds.length=0}_HandleRoomUpdate(e){if(e.kind==="Joined"){(0,h.assert)(this._AvailableMediaIds.length===0,"left over media ids"),this._AvailableMediaIds.push(...e.media_ids),this._HandleRoomJoined();for(let t of e.room.peers)this._HandlePeerJoined(t)}else e.kind==="PeerJoined"?this._HandlePeerJoined(e.peer):e.kind==="PeerLeft"&&this._HandlePeerLeft(e.peer_id)}_HandleRoomJoined(){for(let[e,t]of this._Captures)this._AvailableMediaIds.length===0||this.mainConnection===void 0||(t.media={connectionId:this.mainConnection.id,mediaId:this._AvailableMediaIds.shift()},e instanceof R&&e.start(this._Connection,t.media,t.uid),e instanceof k)}_HandlePeerUpdate(e){e.kind==="MediaStarted"?this._HandleMediaStart(e.peer_id,e.media):e.kind==="MediaStopped"&&this._HandleMediaStopped(e.peer_id,e.media_id)}_HandlePeerJoined(e){this._Peers.has(e.id)||this._Peers.set(e.id,{mediaIds:new Map});for(let t of e.medias)this._HandleMediaStart(e.id,t)}_HandlePeerLeft(e){let t=this._Peers.get(e);if(t!==void 0){for(let n of t.mediaIds.keys())this._HandleMediaStopped(e,n);this._Peers.delete(e)}}_HandleMediaStart(e,t){if(this.mainConnection===void 0)return;let n=this._Peers.get(e);if(n===void 0||t.properties.kind!=="audio"&&t.properties.kind!=="video")return;let o=String(t.properties.uid??(0,h.generateUUID)());t.properties.uid=o;let r=this._Playbacks.get(o)??{media:void 0,playback:void 0};this._Playbacks.set(o,r),n.mediaIds.set(t.id,o),r.media={connectionId:this.mainConnection.id,mediaId:t.id},r.playback!==void 0&&(r.playback instanceof g&&r.playback.start(r.media),r.playback instanceof M&&(a.debug(["Video"],"_HandleMediaStart() VideoPlayback"),this.mainConnection.startRemoteVideo(r.media.mediaId).then()))}_HandleMediaStopped(e,t){let n=this._Peers.get(e);if(n===void 0)return;let o=n?.mediaIds.get(t);if(o===void 0)return;let r=this._Playbacks.get(o);r!==void 0&&(r.media!==void 0&&r.playback!==void 0&&(r.playback instanceof g&&r.playback.stop(r.media),r.playback instanceof M&&(a.debug(["Video","_HandleMediaStopped() VideoPlayback"]),this.mainConnection?.stopRemoteVideo(r.media.mediaId))),r.media=void 0,n.mediaIds.delete(t))}};0&&(module.exports={createPlugin,logger});
|