@dcl-regenesislabs/bevy-explorer-web 0.1.0-21253487481.commit-7fbf319 → 0.1.0-21289091889.commit-a8880bb
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/.env +1 -1
- package/index.html +2 -2
- package/package.json +3 -3
- package/pkg/snippets/comms-53217a45365bb5fa/livekit_web_bindings.js +405 -411
- package/pkg/webgpu_build.d.ts +103 -11
- package/pkg/webgpu_build.js +525 -90
- package/pkg/webgpu_build_bg.wasm +0 -0
- package/pkg/webgpu_build_bg.wasm.d.ts +49 -11
|
@@ -8,408 +8,465 @@ function error(...args) {
|
|
|
8
8
|
console.error("[livekit]", ...args)
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
let currentMicTrack = false;
|
|
12
|
-
const activeRooms = new Set();
|
|
13
|
-
|
|
14
|
-
// Store audio elements and panner nodes for spatial audio
|
|
15
|
-
const trackRigs = new Map();
|
|
16
|
-
const participantAudioSids = new Map();
|
|
17
|
-
const participantVideoSids = new Map();
|
|
18
11
|
var audioContext = null;
|
|
19
12
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
13
|
+
/**
|
|
14
|
+
*
|
|
15
|
+
* @returns boolean
|
|
16
|
+
*/
|
|
17
|
+
export function is_microphone_available() {
|
|
18
|
+
// Check if getUserMedia is available
|
|
19
|
+
const res = !!(navigator.mediaDevices && navigator.mediaDevices.getUserMedia);
|
|
20
|
+
return res;
|
|
21
|
+
}
|
|
25
22
|
|
|
26
|
-
|
|
23
|
+
/**
|
|
24
|
+
*
|
|
25
|
+
* @param {string} url
|
|
26
|
+
* @param {string} token
|
|
27
|
+
* @param {livekit.RoomOptions} room_options
|
|
28
|
+
* @param {livekit.RoomConnectOptions} room_connect_options
|
|
29
|
+
* @param {function} handler
|
|
30
|
+
* @returns livekit.Room
|
|
31
|
+
*/
|
|
32
|
+
export async function room_connect(url, token, room_options, room_connect_options, handler) {
|
|
33
|
+
const room = new LivekitClient.Room(room_options);
|
|
27
34
|
|
|
28
|
-
|
|
29
|
-
autoSubscribe: false,
|
|
30
|
-
});
|
|
35
|
+
set_room_event_handler(room, handler);
|
|
31
36
|
|
|
32
|
-
|
|
33
|
-
activeRooms.add(room);
|
|
34
|
-
|
|
35
|
-
// set up microphone
|
|
36
|
-
if (currentMicTrack) {
|
|
37
|
-
log(`sub ${room.name}`);
|
|
38
|
-
const audioTrack = await LivekitClient.createLocalAudioTrack({
|
|
39
|
-
echoCancellation: true,
|
|
40
|
-
noiseSuppression: true,
|
|
41
|
-
autoGainControl: true,
|
|
42
|
-
});
|
|
43
|
-
const pub = await room.localParticipant.publishTrack(audioTrack, {
|
|
44
|
-
source: LivekitClient.Track.Source.Microphone,
|
|
45
|
-
}).catch(error_msg => {
|
|
46
|
-
error(`Failed to publish to room: ${error_msg}`);
|
|
47
|
-
})
|
|
37
|
+
await room.connect(url, token, room_connect_options);
|
|
48
38
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
39
|
+
return room;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
*
|
|
44
|
+
* @param {livekit.Room} room
|
|
45
|
+
*/
|
|
46
|
+
export async function room_close(room) {
|
|
47
|
+
await room.disconnect();
|
|
48
|
+
}
|
|
54
49
|
|
|
55
|
-
|
|
50
|
+
/**
|
|
51
|
+
*
|
|
52
|
+
* @param {livekit.Room} room
|
|
53
|
+
* @returns string
|
|
54
|
+
*/
|
|
55
|
+
export function room_name(room) {
|
|
56
|
+
return room.name
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
*
|
|
61
|
+
* @param {livekit.Room} room
|
|
62
|
+
* @returns livekit.LocalParticipant
|
|
63
|
+
*/
|
|
64
|
+
export function room_local_participant(room) {
|
|
65
|
+
return room.localParticipant;
|
|
66
|
+
}
|
|
56
67
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
68
|
+
/**
|
|
69
|
+
*
|
|
70
|
+
* @param {livekit.Room} room
|
|
71
|
+
* @param {function} handler
|
|
72
|
+
*/
|
|
73
|
+
function set_room_event_handler(room, handler) {
|
|
74
|
+
room.on(LivekitClient.RoomEvent.Connected, () => {
|
|
75
|
+
const participants_with_tracks = Array
|
|
76
|
+
.from(room.remoteParticipants.values())
|
|
77
|
+
.filter(remote_participant => room.localParticipant.sid != remote_participant.sid)
|
|
78
|
+
.map(remote_participant => {
|
|
79
|
+
return {
|
|
80
|
+
participant: remote_participant,
|
|
81
|
+
tracks: Array.from(remote_participant.trackPublications.values())
|
|
82
|
+
};
|
|
83
|
+
});
|
|
60
84
|
handler({
|
|
61
|
-
type: '
|
|
62
|
-
|
|
63
|
-
participant: {
|
|
64
|
-
identity: participant.identity,
|
|
65
|
-
metadata: participant.metadata || ''
|
|
66
|
-
}
|
|
85
|
+
type: 'connected',
|
|
86
|
+
participants_with_tracks
|
|
67
87
|
})
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
88
|
+
});
|
|
89
|
+
room.on(LivekitClient.RoomEvent.ConnectionStateChanged, (state) => {
|
|
90
|
+
handler({
|
|
91
|
+
type: 'connectionStateChanged',
|
|
92
|
+
state: state
|
|
93
|
+
})
|
|
94
|
+
});
|
|
95
|
+
room.on(LivekitClient.RoomEvent.ConnectionQualityChanged, (connection_quality, participant) => {
|
|
96
|
+
handler({
|
|
97
|
+
type: 'connectionQualityChanged',
|
|
98
|
+
connection_quality,
|
|
99
|
+
participant
|
|
100
|
+
})
|
|
101
|
+
});
|
|
102
|
+
room.on(
|
|
103
|
+
LivekitClient.RoomEvent.DataReceived,
|
|
104
|
+
(payload, participant, kind, topic) => {
|
|
105
|
+
handler({
|
|
106
|
+
type: 'dataReceived',
|
|
107
|
+
payload,
|
|
108
|
+
participant,
|
|
109
|
+
kind,
|
|
110
|
+
topic
|
|
111
|
+
})
|
|
112
|
+
}
|
|
113
|
+
);
|
|
114
|
+
room.on(
|
|
115
|
+
LivekitClient.RoomEvent.ParticipantConnected,
|
|
116
|
+
(remote_participant) => {
|
|
117
|
+
handler({
|
|
118
|
+
type: 'participantConnected',
|
|
119
|
+
participant: remote_participant,
|
|
120
|
+
})
|
|
121
|
+
}
|
|
122
|
+
);
|
|
123
|
+
room.on(
|
|
124
|
+
LivekitClient.RoomEvent.ParticipantDisconnected,
|
|
125
|
+
(remote_participant) => {
|
|
126
|
+
handler({
|
|
127
|
+
type: 'participantDisconnected',
|
|
128
|
+
participant: remote_participant,
|
|
129
|
+
})
|
|
130
|
+
}
|
|
131
|
+
);
|
|
132
|
+
room.on(
|
|
133
|
+
LivekitClient.RoomEvent.ParticipantMetadataChanged,
|
|
134
|
+
(prev_metadata, participant) => {
|
|
135
|
+
handler({
|
|
136
|
+
type: 'participantMetadataChanged',
|
|
137
|
+
participant,
|
|
138
|
+
old_metadata: prev_metadata,
|
|
139
|
+
metadata: participant.metadata
|
|
140
|
+
})
|
|
141
|
+
}
|
|
142
|
+
);
|
|
143
|
+
room.on(
|
|
144
|
+
LivekitClient.RoomEvent.TrackPublished,
|
|
145
|
+
(remote_track_publication, remote_participant) => {
|
|
73
146
|
handler({
|
|
74
147
|
type: 'trackPublished',
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
participant: {
|
|
78
|
-
identity: participant.identity,
|
|
79
|
-
metadata: participant.metadata || ''
|
|
80
|
-
}
|
|
148
|
+
publication: remote_track_publication,
|
|
149
|
+
participant: remote_participant
|
|
81
150
|
})
|
|
82
151
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
152
|
+
);
|
|
153
|
+
room.on(
|
|
154
|
+
LivekitClient.RoomEvent.TrackUnpublished,
|
|
155
|
+
(remote_track_publication, remote_participant) => {
|
|
156
|
+
handler({
|
|
157
|
+
type: 'trackUnpublished',
|
|
158
|
+
publication: remote_track_publication,
|
|
159
|
+
participant: remote_participant
|
|
160
|
+
})
|
|
161
|
+
}
|
|
162
|
+
);
|
|
163
|
+
room.on(
|
|
164
|
+
LivekitClient.RoomEvent.TrackSubscribed,
|
|
165
|
+
(remote_track, remote_track_publication, remote_participant) => {
|
|
166
|
+
log(`Subscribed to track ${remote_track.sid} of ${remote_participant.sid} (${remote_participant.identity}).`);
|
|
167
|
+
|
|
168
|
+
if (remote_track.kind === "audio") {
|
|
169
|
+
if (remote_track.trackRig) {
|
|
170
|
+
error(`Rebuilding track rig of ${remote_track.sid} for ${remote_participant.sid} (${remote_participant.identity}).`);
|
|
171
|
+
track_rig_drop(remote_track);
|
|
172
|
+
}
|
|
173
|
+
if (!audioContext) {
|
|
174
|
+
audioContext = new (window.AudioContext || window.webkitAudioContext)();
|
|
175
|
+
}
|
|
87
176
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
const publishPromises = Array.from(activeRooms).map(async (room) => {
|
|
96
|
-
log(`publish ${room.name}`);
|
|
97
|
-
const audioTrack = await LivekitClient.createLocalAudioTrack({
|
|
98
|
-
echoCancellation: true,
|
|
99
|
-
noiseSuppression: true,
|
|
100
|
-
autoGainControl: true,
|
|
101
|
-
});
|
|
102
|
-
let pub = await room.localParticipant.publishTrack(audioTrack, {
|
|
103
|
-
source: LivekitClient.Track.Source.Microphone,
|
|
104
|
-
}).catch(error_msg => {
|
|
105
|
-
error(`Failed to publish to room: ${error_msg}`);
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
// avoid race
|
|
109
|
-
if (!currentMicTrack) {
|
|
110
|
-
await room.localParticipant.unpublishTrack(pub.track);
|
|
177
|
+
track_rig_new(remote_track);
|
|
178
|
+
} else if (remote_track.kind == "video") {
|
|
179
|
+
if (remote_track.videoElement) {
|
|
180
|
+
error(`Rebuilding video element of ${remote_track.sid} for ${remote_participant.sid} (${remote_participant.identity}).`);
|
|
181
|
+
const videoElement = remote_track.videoElement;
|
|
182
|
+
delete remote_track.videoElement;
|
|
183
|
+
remote_track.detach(videoElement);
|
|
111
184
|
}
|
|
112
|
-
|
|
185
|
+
const streamPlayerContainer = window.document.querySelector("#stream-player-container");
|
|
186
|
+
if (streamPlayerContainer) {
|
|
187
|
+
const videoElement = remote_track.attach();
|
|
188
|
+
streamPlayerContainer.append(videoElement);
|
|
189
|
+
remote_track.videoElement = videoElement;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
113
192
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
193
|
+
handler({
|
|
194
|
+
type: 'trackSubscribed',
|
|
195
|
+
track: remote_track,
|
|
196
|
+
publication: remote_track_publication,
|
|
197
|
+
participant: remote_participant
|
|
198
|
+
})
|
|
119
199
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
} catch (error_msg) {
|
|
132
|
-
error(`Failed to unpublish ${pub} from room ${room.name}:`, error_msg);
|
|
133
|
-
}
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
try {
|
|
137
|
-
await Promise.all(roomSpecificPromises);
|
|
138
|
-
} catch (error_msg) {
|
|
139
|
-
error(`Failed to unpublish audio from room ${room.name}:`, error_msg);
|
|
140
|
-
}
|
|
141
|
-
});
|
|
200
|
+
);
|
|
201
|
+
room.on(
|
|
202
|
+
LivekitClient.RoomEvent.TrackUnsubscribed,
|
|
203
|
+
// Note: The browser livekit docs say that the first parameter is a Livekit.Track,
|
|
204
|
+
// not a Livekit.RemoteTrack, verify if there is ever an event with a local
|
|
205
|
+
// track
|
|
206
|
+
(remote_track, remote_track_publication, remote_participant) => {
|
|
207
|
+
log(`Unsubscribed to track ${remote_track.sid} of ${remote_participant.sid} (${remote_participant.identity}).`);
|
|
208
|
+
if (remote_track.kind === "audio") {
|
|
209
|
+
track_rig_drop(remote_track);
|
|
210
|
+
}
|
|
142
211
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
});
|
|
212
|
+
handler({
|
|
213
|
+
type: 'trackUnsubscribed',
|
|
214
|
+
track: remote_track,
|
|
215
|
+
publication: remote_track_publication,
|
|
216
|
+
participant: remote_participant
|
|
217
|
+
})
|
|
150
218
|
}
|
|
151
|
-
|
|
219
|
+
);
|
|
152
220
|
}
|
|
153
221
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
222
|
+
/**
|
|
223
|
+
*
|
|
224
|
+
* @param {livekit.Participant} participant
|
|
225
|
+
* @returns bool
|
|
226
|
+
*/
|
|
227
|
+
export async function particinpant_is_local(participant) {
|
|
228
|
+
return particinpant.isLocal;
|
|
158
229
|
}
|
|
159
230
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
231
|
+
/**
|
|
232
|
+
*
|
|
233
|
+
* @param {livekit.LocalParticipant} local_participant
|
|
234
|
+
* @param {Uint8Array} payload
|
|
235
|
+
* @param {livekit.DataPublishOptions} payload
|
|
236
|
+
* @returns string
|
|
237
|
+
*/
|
|
238
|
+
export async function local_participant_publish_data(local_participant, payload, data_publish_options) {
|
|
239
|
+
local_participant.publishData(payload, data_publish_options).await;
|
|
167
240
|
}
|
|
168
241
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
242
|
+
/**
|
|
243
|
+
*
|
|
244
|
+
* @param {livekit.LocalParticipant} local_participant
|
|
245
|
+
* @param {livekit.LocalTrack} local_track
|
|
246
|
+
* @param {livekit.TrackPublishingOptions} track_publishing_option
|
|
247
|
+
* @returns livekit.LocalTrackPublication
|
|
248
|
+
*/
|
|
249
|
+
export async function local_participant_publish_track(local_participant, local_track, track_publishing_option) {
|
|
250
|
+
return await local_participant.publishTrack(local_track, track_publishing_option);
|
|
174
251
|
}
|
|
175
252
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
253
|
+
/**
|
|
254
|
+
*
|
|
255
|
+
* @param {livekit.LocalParticipant} local_participant
|
|
256
|
+
* @param {livekit.LocalTrack} local_track
|
|
257
|
+
* @returns livekit.LocalTrackPublication
|
|
258
|
+
*/
|
|
259
|
+
export async function local_participant_unpublish_track(local_participant, local_track) {
|
|
260
|
+
return await local_participant.unpublishTrack(local_track, true);
|
|
181
261
|
}
|
|
182
262
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
.filter(pub => pub.kind === 'audio');
|
|
191
|
-
|
|
192
|
-
for (const pub of audioPubs) {
|
|
193
|
-
log(`stop ${room.name} on exit`);
|
|
194
|
-
pub.track.stop();
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
await room.disconnect();
|
|
263
|
+
/**
|
|
264
|
+
*
|
|
265
|
+
* @param {livekit.LocalParticipant} local_participant
|
|
266
|
+
* @returns bool
|
|
267
|
+
*/
|
|
268
|
+
export function local_participant_is_local(local_participant) {
|
|
269
|
+
return local_participant.isLocal;
|
|
199
270
|
}
|
|
200
271
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
participant: {
|
|
210
|
-
identity: participant.identity,
|
|
211
|
-
metadata: participant.metadata || ''
|
|
212
|
-
}
|
|
213
|
-
});
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
room.on(LivekitClient.RoomEvent.TrackPublished, (publication, participant) => {
|
|
217
|
-
log(`${room.name} ${participant.identity} rec pub ${publication.kind}`);
|
|
218
|
-
handler({
|
|
219
|
-
type: 'trackPublished',
|
|
220
|
-
room_name: room_name,
|
|
221
|
-
kind: publication.kind,
|
|
222
|
-
participant: {
|
|
223
|
-
identity: participant.identity,
|
|
224
|
-
metadata: participant.metadata || ''
|
|
225
|
-
}
|
|
226
|
-
})
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
room.on(LivekitClient.RoomEvent.TrackUnpublished, (publication, participant) => {
|
|
230
|
-
log(`${room.name} ${participant.identity} rec unpub ${publication.kind}`);
|
|
231
|
-
|
|
232
|
-
const key = publication.trackSid;
|
|
233
|
-
const rig = trackRigs.get(key);
|
|
272
|
+
/**
|
|
273
|
+
*
|
|
274
|
+
* @param {livekit.LocalParticipant} local_participant
|
|
275
|
+
* @returns string
|
|
276
|
+
*/
|
|
277
|
+
export function local_participant_sid(local_participant) {
|
|
278
|
+
return local_participant.sid;
|
|
279
|
+
}
|
|
234
280
|
|
|
235
|
-
|
|
236
|
-
|
|
281
|
+
/**
|
|
282
|
+
*
|
|
283
|
+
* @param {livekit.LocalParticipant} local_participant
|
|
284
|
+
* @returns string
|
|
285
|
+
*/
|
|
286
|
+
export function local_participant_identity(local_participant) {
|
|
287
|
+
return local_participant.identity;
|
|
288
|
+
}
|
|
237
289
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
290
|
+
/**
|
|
291
|
+
*
|
|
292
|
+
* @param {livekit.LocalParticipant} local_participant
|
|
293
|
+
* @returns string
|
|
294
|
+
*/
|
|
295
|
+
export function local_participant_metadata(local_participant) {
|
|
296
|
+
return local_participant.metadata;
|
|
297
|
+
}
|
|
241
298
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
299
|
+
/**
|
|
300
|
+
*
|
|
301
|
+
* @param {livekit.LocalParticipant} remote_participant
|
|
302
|
+
* @returns bool
|
|
303
|
+
*/
|
|
304
|
+
export function remote_participant_is_local(remote_participant) {
|
|
305
|
+
return remote_participant.isLocal;
|
|
306
|
+
}
|
|
246
307
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
})
|
|
256
|
-
});
|
|
308
|
+
/**
|
|
309
|
+
*
|
|
310
|
+
* @param {livekit.RemoteParticipant} remote_participant
|
|
311
|
+
* @returns string
|
|
312
|
+
*/
|
|
313
|
+
export function remote_participant_sid(remote_participant) {
|
|
314
|
+
return remote_participant.sid;
|
|
315
|
+
}
|
|
257
316
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
317
|
+
/**
|
|
318
|
+
*
|
|
319
|
+
* @param {livekit.RemoteParticipant} remote_participant
|
|
320
|
+
* @returns string
|
|
321
|
+
*/
|
|
322
|
+
export function remote_participant_identity(remote_participant) {
|
|
323
|
+
return remote_participant.identity;
|
|
324
|
+
}
|
|
265
325
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
// use the track internal stream in playback
|
|
276
|
-
const stream = new MediaStream([track.mediaStreamTrack]);
|
|
277
|
-
const source = audioContext.createMediaStreamSource(stream);
|
|
278
|
-
const pannerNode = audioContext.createStereoPanner();
|
|
279
|
-
const gainNode = audioContext.createGain();
|
|
280
|
-
|
|
281
|
-
// Connect the audio graph: source -> panner -> gain -> destination
|
|
282
|
-
source.connect(pannerNode);
|
|
283
|
-
pannerNode.connect(gainNode);
|
|
284
|
-
gainNode.connect(audioContext.destination);
|
|
285
|
-
|
|
286
|
-
// Store the nodes for later control
|
|
287
|
-
trackRigs.set(key, {
|
|
288
|
-
audioElement,
|
|
289
|
-
source,
|
|
290
|
-
pannerNode,
|
|
291
|
-
gainNode,
|
|
292
|
-
stream,
|
|
293
|
-
});
|
|
294
|
-
}
|
|
326
|
+
/**
|
|
327
|
+
*
|
|
328
|
+
* @param {livekit.RemoteParticipant} remote_participant
|
|
329
|
+
* @returns string
|
|
330
|
+
*/
|
|
331
|
+
export function remote_participant_metadata(remote_participant) {
|
|
332
|
+
return remote_participant.metadata;
|
|
333
|
+
}
|
|
295
334
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
if (!trackRigs.get(key)) {
|
|
305
|
-
log("create video nodes for", key);
|
|
306
|
-
const parentElement = window.document.querySelector("#stream-player-container");
|
|
307
|
-
if (parentElement) {
|
|
308
|
-
const element = track.attach();
|
|
309
|
-
parentElement.appendChild(element);
|
|
310
|
-
trackRigs.set(key, {
|
|
311
|
-
videoElement: element,
|
|
312
|
-
});
|
|
313
|
-
}
|
|
314
|
-
}
|
|
335
|
+
/**
|
|
336
|
+
*
|
|
337
|
+
* @param {livekit.RemoteTrackPublication} remote_track_publication
|
|
338
|
+
* @returns string
|
|
339
|
+
*/
|
|
340
|
+
export function remote_track_publication_sid(remote_track_publication) {
|
|
341
|
+
return remote_track_publication.trackSid;
|
|
342
|
+
}
|
|
315
343
|
|
|
316
|
-
|
|
317
|
-
|
|
344
|
+
/**
|
|
345
|
+
*
|
|
346
|
+
* @param {livekit.RemoteTrackPublication} remote_track_publication
|
|
347
|
+
* @returns string
|
|
348
|
+
*/
|
|
349
|
+
export function remote_track_publication_kind(remote_track_publication) {
|
|
350
|
+
return remote_track_publication.kind;
|
|
351
|
+
}
|
|
318
352
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
});
|
|
353
|
+
/**
|
|
354
|
+
*
|
|
355
|
+
* @param {livekit.RemoteTrackPublication} remote_track_publication
|
|
356
|
+
* @returns string
|
|
357
|
+
*/
|
|
358
|
+
export function remote_track_publication_source(remote_track_publication) {
|
|
359
|
+
return remote_track_publication.source;
|
|
360
|
+
}
|
|
328
361
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
}
|
|
362
|
+
/**
|
|
363
|
+
*
|
|
364
|
+
* @param {livekit.RemoteTrackPublication} remote_track_publication
|
|
365
|
+
* @param {boolean} subscribed
|
|
366
|
+
* @returns string
|
|
367
|
+
*/
|
|
368
|
+
export function remote_track_publication_set_subscribed(remote_track_publication, subscribed) {
|
|
369
|
+
remote_track_publication.setSubscribed(subscribed);
|
|
370
|
+
}
|
|
339
371
|
|
|
340
|
-
const key = track.sid;
|
|
341
372
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
log(`detach videoElement for ${key}`)
|
|
352
|
-
track.detach(videoElement);
|
|
353
|
-
videoElement.remove();
|
|
354
|
-
}
|
|
355
|
-
trackRigs.delete(key);
|
|
356
|
-
}
|
|
373
|
+
/**
|
|
374
|
+
*
|
|
375
|
+
* @param {livekit.RemoteTrackPublication} remote_track_publication
|
|
376
|
+
* @returns livekit.RemoteTrack | null
|
|
377
|
+
*/
|
|
378
|
+
export function remote_track_publication_track(remote_track_publication) {
|
|
379
|
+
log(remote_track_publication);
|
|
380
|
+
return remote_track_publication.track;
|
|
381
|
+
}
|
|
357
382
|
|
|
383
|
+
/**
|
|
384
|
+
*
|
|
385
|
+
* @param {livekit.AudioCaptureOptions} options
|
|
386
|
+
* @returns livekit.LocalAudioTrack
|
|
387
|
+
*/
|
|
388
|
+
export async function local_audio_track_new(options) {
|
|
389
|
+
try {
|
|
390
|
+
return await LivekitClient.createLocalAudioTrack(options);
|
|
391
|
+
} catch (err) {
|
|
392
|
+
error(err);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
358
395
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
});
|
|
396
|
+
/**
|
|
397
|
+
*
|
|
398
|
+
* @param {livekit.LocalAudioTrack} local_audio_track
|
|
399
|
+
* @returns livekit.TrackSid
|
|
400
|
+
*/
|
|
401
|
+
export function local_audio_track_sid(local_audio_track) {
|
|
402
|
+
return local_audio_track.sid;
|
|
403
|
+
}
|
|
368
404
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
405
|
+
/**
|
|
406
|
+
*
|
|
407
|
+
* @param {livekit.RemoteTrack} remote_track
|
|
408
|
+
*/
|
|
409
|
+
function track_rig_new(remote_track) {
|
|
410
|
+
log(`Creating new track rig for ${remote_track.sid}.`);
|
|
411
|
+
|
|
412
|
+
// dummy audioElement
|
|
413
|
+
const audioElement = remote_track.attach();
|
|
414
|
+
audioElement.volume = 0;
|
|
415
|
+
|
|
416
|
+
// use the track internal stream in playback
|
|
417
|
+
const stream = new MediaStream([remote_track.mediaStreamTrack]);
|
|
418
|
+
const source = audioContext.createMediaStreamSource(stream);
|
|
419
|
+
const pannerNode = audioContext.createStereoPanner();
|
|
420
|
+
const gainNode = audioContext.createGain();
|
|
421
|
+
|
|
422
|
+
// Connect the audio graph: source -> panner -> gain -> destination
|
|
423
|
+
source.connect(pannerNode);
|
|
424
|
+
pannerNode.connect(gainNode);
|
|
425
|
+
gainNode.connect(audioContext.destination);
|
|
426
|
+
|
|
427
|
+
// Store the nodes for later control
|
|
428
|
+
remote_track.trackRig = {
|
|
429
|
+
audioElement,
|
|
430
|
+
source,
|
|
431
|
+
pannerNode,
|
|
432
|
+
gainNode,
|
|
433
|
+
stream,
|
|
434
|
+
};
|
|
379
435
|
|
|
380
|
-
|
|
381
|
-
participantAudioSids.delete(participant.identity);
|
|
382
|
-
participantVideoSids.delete(participant.identity);
|
|
383
|
-
handler({
|
|
384
|
-
type: 'participantDisconnected',
|
|
385
|
-
room_name: room_name,
|
|
386
|
-
participant: {
|
|
387
|
-
identity: participant.identity,
|
|
388
|
-
metadata: participant.metadata || ''
|
|
389
|
-
}
|
|
390
|
-
});
|
|
391
|
-
});
|
|
436
|
+
audioElement.play();
|
|
392
437
|
}
|
|
393
438
|
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
439
|
+
/**
|
|
440
|
+
*
|
|
441
|
+
* @param {livekit.RemoteTrack} remote_track
|
|
442
|
+
*/
|
|
443
|
+
function track_rig_drop(remote_track) {
|
|
444
|
+
log(`Dropping track rig of ${remote_track.sid}.`);
|
|
445
|
+
const track_rig = remote_track.trackRig;
|
|
446
|
+
if (track_rig) {
|
|
447
|
+
delete remote_track.trackRig;
|
|
448
|
+
|
|
449
|
+
remote_track.detach(track_rig.audioElement);
|
|
450
|
+
track_rig.source.disconnect();
|
|
451
|
+
track_rig.pannerNode.disconnect();
|
|
452
|
+
track_rig.gainNode.disconnect();
|
|
453
|
+
track_rig.audioElement.pause();
|
|
407
454
|
}
|
|
455
|
+
}
|
|
408
456
|
|
|
457
|
+
/**
|
|
458
|
+
*
|
|
459
|
+
* @param {livekit.RemoteTrack} remote_track
|
|
460
|
+
* @param {float} pan
|
|
461
|
+
* @param {float} volume
|
|
462
|
+
*/
|
|
463
|
+
export function remote_track_pan_and_volume(remote_track, pan, volume) {
|
|
464
|
+
log(`Setting pan and volume for track ${remote_track.sid}.`);
|
|
465
|
+
const track_rig = remote_track.trackRig;
|
|
409
466
|
// Pan value should be between -1 (left) and 1 (right)
|
|
410
|
-
|
|
467
|
+
track_rig.pannerNode.pan.value = Math.max(-1, Math.min(1, pan));
|
|
411
468
|
// Volume should be between 0 and 1 (or higher for boost)
|
|
412
|
-
|
|
469
|
+
track_rig.gainNode.gain.value = Math.max(0, volume);
|
|
413
470
|
|
|
414
471
|
// nodes.analyser.getByteTimeDomainData(nodes.dataArray);
|
|
415
472
|
|
|
@@ -424,66 +481,3 @@ export function set_participant_spatial_audio(participantIdentity, pan, volume)
|
|
|
424
481
|
|
|
425
482
|
// log(`[${audioContext.state}] Set spatial audio for ${participantIdentity} : pan=${nodes.pannerNode.pan.value}, volume=${nodes.gainNode.gain.value}`);
|
|
426
483
|
}
|
|
427
|
-
|
|
428
|
-
// Get all active participant identities with audio
|
|
429
|
-
export function get_audio_participants() {
|
|
430
|
-
return Array.from(participantAudioSids.keys());
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
export function subscribe_channel(roomName, participantId, subscribe) {
|
|
434
|
-
const room = Array.from(activeRooms).find(room => room.name === roomName);
|
|
435
|
-
if (!room) {
|
|
436
|
-
warn(`couldn't find room ${roomName} for subscription`);
|
|
437
|
-
return;
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
const participant = room.remoteParticipants.get(participantId);
|
|
441
|
-
if (!participant) {
|
|
442
|
-
warn(`couldn't find participant ${participantId} in room ${roomName} for subscription`);
|
|
443
|
-
return;
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
const audioPubs = Array.from(participant.trackPublications.values())
|
|
447
|
-
.filter(pub => pub.kind === 'audio');
|
|
448
|
-
|
|
449
|
-
log(`subscribing to ${audioPubs.length} audio tracks`);
|
|
450
|
-
|
|
451
|
-
for (const pub of audioPubs) {
|
|
452
|
-
log(`sub ${roomName}-${participantId}`);
|
|
453
|
-
pub.setSubscribed(subscribe);
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
export function streamer_subscribe_channel(roomName, subscribe_audio, subscribe_video) {
|
|
458
|
-
const room = Array.from(activeRooms).find(room => room.name === roomName);
|
|
459
|
-
if (!room) {
|
|
460
|
-
warn(`couldn't find room ${roomName} for subscription`);
|
|
461
|
-
return;
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
const participant = room.remoteParticipants.values().find(participant => participant.identity.endsWith("-streamer"));
|
|
465
|
-
if (!participant) {
|
|
466
|
-
warn(`couldn't find streamer participant in room ${roomName} for subscription`);
|
|
467
|
-
return;
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
const audioPubs = Array.from(participant.trackPublications.values())
|
|
471
|
-
.filter(pub => pub.kind === 'audio');
|
|
472
|
-
const videoPubs = Array.from(participant.trackPublications.values())
|
|
473
|
-
.filter(pub => pub.kind === 'video');
|
|
474
|
-
|
|
475
|
-
log(`subscribing to ${audioPubs.length} audio tracks and to ${videoPubs.length} video tracks`);
|
|
476
|
-
|
|
477
|
-
for (const pub of audioPubs) {
|
|
478
|
-
log(`sub(${subscribe_video}) ${roomName}-${participant.identity}`);
|
|
479
|
-
pub.setSubscribed(subscribe_audio);
|
|
480
|
-
}
|
|
481
|
-
for (const pub of videoPubs) {
|
|
482
|
-
log(`video sub(${subscribe_video}) ${roomName}-${participant.identity}`);
|
|
483
|
-
pub.setSubscribed(subscribe_video);
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
export function room_name(room) {
|
|
488
|
-
return room.name
|
|
489
|
-
}
|