livekit-client 0.17.1 → 0.17.4
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/dist/api/SignalClient.d.ts +2 -2
- package/dist/api/SignalClient.js +27 -1
- package/dist/api/SignalClient.js.map +1 -1
- package/dist/proto/google/protobuf/timestamp.d.ts +122 -0
- package/dist/proto/google/protobuf/timestamp.js +93 -0
- package/dist/proto/google/protobuf/timestamp.js.map +1 -0
- package/dist/proto/livekit_models.d.ts +78 -2
- package/dist/proto/livekit_models.js +830 -34
- package/dist/proto/livekit_models.js.map +1 -1
- package/dist/proto/livekit_rtc.d.ts +12 -0
- package/dist/proto/livekit_rtc.js +72 -1
- package/dist/proto/livekit_rtc.js.map +1 -1
- package/dist/room/RTCEngine.d.ts +1 -1
- package/dist/room/RTCEngine.js +22 -5
- package/dist/room/RTCEngine.js.map +1 -1
- package/dist/room/Room.d.ts +2 -1
- package/dist/room/Room.js +16 -5
- package/dist/room/Room.js.map +1 -1
- package/dist/room/events.d.ts +12 -2
- package/dist/room/events.js +10 -0
- package/dist/room/events.js.map +1 -1
- package/dist/room/participant/LocalParticipant.d.ts +4 -1
- package/dist/room/participant/LocalParticipant.js +25 -0
- package/dist/room/participant/LocalParticipant.js.map +1 -1
- package/dist/room/participant/Participant.d.ts +5 -1
- package/dist/room/participant/Participant.js +15 -1
- package/dist/room/participant/Participant.js.map +1 -1
- package/dist/room/track/LocalTrack.js +1 -0
- package/dist/room/track/LocalTrack.js.map +1 -1
- package/dist/room/track/RemoteTrack.js +1 -0
- package/dist/room/track/RemoteTrack.js.map +1 -1
- package/dist/room/track/Track.d.ts +1 -0
- package/dist/room/track/Track.js +10 -3
- package/dist/room/track/Track.js.map +1 -1
- package/dist/room/track/create.js +1 -0
- package/dist/room/track/create.js.map +1 -1
- package/dist/room/utils.d.ts +1 -0
- package/dist/room/utils.js +5 -1
- package/dist/room/utils.js.map +1 -1
- package/dist/version.d.ts +2 -2
- package/dist/version.js +2 -2
- package/package.json +2 -2
- package/src/api/SignalClient.ts +12 -2
- package/src/proto/google/protobuf/timestamp.ts +222 -0
- package/src/proto/livekit_models.ts +937 -30
- package/src/proto/livekit_rtc.ts +107 -0
- package/src/room/RTCEngine.ts +23 -7
- package/src/room/Room.ts +32 -15
- package/src/room/events.ts +12 -0
- package/src/room/participant/LocalParticipant.ts +33 -2
- package/src/room/participant/Participant.ts +22 -2
- package/src/room/track/LocalTrack.ts +1 -0
- package/src/room/track/RemoteTrack.ts +1 -0
- package/src/room/track/Track.ts +12 -4
- package/src/room/track/create.ts +1 -0
- package/src/room/utils.ts +4 -0
- package/src/version.ts +2 -2
package/src/proto/livekit_rtc.ts
CHANGED
@@ -152,6 +152,8 @@ export interface SignalResponse {
|
|
152
152
|
subscriptionPermissionUpdate?: SubscriptionPermissionUpdate | undefined;
|
153
153
|
/** update the token the client was using, to prevent an active client from using an expired token */
|
154
154
|
refreshToken: string | undefined;
|
155
|
+
/** server initiated track unpublish */
|
156
|
+
trackUnpublished?: TrackUnpublishedResponse | undefined;
|
155
157
|
}
|
156
158
|
|
157
159
|
export interface AddTrackRequest {
|
@@ -202,6 +204,10 @@ export interface TrackPublishedResponse {
|
|
202
204
|
track?: TrackInfo;
|
203
205
|
}
|
204
206
|
|
207
|
+
export interface TrackUnpublishedResponse {
|
208
|
+
trackSid: string;
|
209
|
+
}
|
210
|
+
|
205
211
|
export interface SessionDescription {
|
206
212
|
/** "answer" | "offer" | "pranswer" | "rollback" */
|
207
213
|
type: string;
|
@@ -738,6 +744,12 @@ export const SignalResponse = {
|
|
738
744
|
if (message.refreshToken !== undefined) {
|
739
745
|
writer.uint32(130).string(message.refreshToken);
|
740
746
|
}
|
747
|
+
if (message.trackUnpublished !== undefined) {
|
748
|
+
TrackUnpublishedResponse.encode(
|
749
|
+
message.trackUnpublished,
|
750
|
+
writer.uint32(138).fork()
|
751
|
+
).ldelim();
|
752
|
+
}
|
741
753
|
return writer;
|
742
754
|
},
|
743
755
|
|
@@ -809,6 +821,12 @@ export const SignalResponse = {
|
|
809
821
|
case 16:
|
810
822
|
message.refreshToken = reader.string();
|
811
823
|
break;
|
824
|
+
case 17:
|
825
|
+
message.trackUnpublished = TrackUnpublishedResponse.decode(
|
826
|
+
reader,
|
827
|
+
reader.uint32()
|
828
|
+
);
|
829
|
+
break;
|
812
830
|
default:
|
813
831
|
reader.skipType(tag & 7);
|
814
832
|
break;
|
@@ -922,6 +940,16 @@ export const SignalResponse = {
|
|
922
940
|
} else {
|
923
941
|
message.refreshToken = undefined;
|
924
942
|
}
|
943
|
+
if (
|
944
|
+
object.trackUnpublished !== undefined &&
|
945
|
+
object.trackUnpublished !== null
|
946
|
+
) {
|
947
|
+
message.trackUnpublished = TrackUnpublishedResponse.fromJSON(
|
948
|
+
object.trackUnpublished
|
949
|
+
);
|
950
|
+
} else {
|
951
|
+
message.trackUnpublished = undefined;
|
952
|
+
}
|
925
953
|
return message;
|
926
954
|
},
|
927
955
|
|
@@ -985,6 +1013,10 @@ export const SignalResponse = {
|
|
985
1013
|
: undefined);
|
986
1014
|
message.refreshToken !== undefined &&
|
987
1015
|
(obj.refreshToken = message.refreshToken);
|
1016
|
+
message.trackUnpublished !== undefined &&
|
1017
|
+
(obj.trackUnpublished = message.trackUnpublished
|
1018
|
+
? TrackUnpublishedResponse.toJSON(message.trackUnpublished)
|
1019
|
+
: undefined);
|
988
1020
|
return obj;
|
989
1021
|
},
|
990
1022
|
|
@@ -1089,6 +1121,16 @@ export const SignalResponse = {
|
|
1089
1121
|
message.subscriptionPermissionUpdate = undefined;
|
1090
1122
|
}
|
1091
1123
|
message.refreshToken = object.refreshToken ?? undefined;
|
1124
|
+
if (
|
1125
|
+
object.trackUnpublished !== undefined &&
|
1126
|
+
object.trackUnpublished !== null
|
1127
|
+
) {
|
1128
|
+
message.trackUnpublished = TrackUnpublishedResponse.fromPartial(
|
1129
|
+
object.trackUnpublished
|
1130
|
+
);
|
1131
|
+
} else {
|
1132
|
+
message.trackUnpublished = undefined;
|
1133
|
+
}
|
1092
1134
|
return message;
|
1093
1135
|
},
|
1094
1136
|
};
|
@@ -1729,6 +1771,71 @@ export const TrackPublishedResponse = {
|
|
1729
1771
|
},
|
1730
1772
|
};
|
1731
1773
|
|
1774
|
+
const baseTrackUnpublishedResponse: object = { trackSid: "" };
|
1775
|
+
|
1776
|
+
export const TrackUnpublishedResponse = {
|
1777
|
+
encode(
|
1778
|
+
message: TrackUnpublishedResponse,
|
1779
|
+
writer: _m0.Writer = _m0.Writer.create()
|
1780
|
+
): _m0.Writer {
|
1781
|
+
if (message.trackSid !== "") {
|
1782
|
+
writer.uint32(10).string(message.trackSid);
|
1783
|
+
}
|
1784
|
+
return writer;
|
1785
|
+
},
|
1786
|
+
|
1787
|
+
decode(
|
1788
|
+
input: _m0.Reader | Uint8Array,
|
1789
|
+
length?: number
|
1790
|
+
): TrackUnpublishedResponse {
|
1791
|
+
const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input);
|
1792
|
+
let end = length === undefined ? reader.len : reader.pos + length;
|
1793
|
+
const message = {
|
1794
|
+
...baseTrackUnpublishedResponse,
|
1795
|
+
} as TrackUnpublishedResponse;
|
1796
|
+
while (reader.pos < end) {
|
1797
|
+
const tag = reader.uint32();
|
1798
|
+
switch (tag >>> 3) {
|
1799
|
+
case 1:
|
1800
|
+
message.trackSid = reader.string();
|
1801
|
+
break;
|
1802
|
+
default:
|
1803
|
+
reader.skipType(tag & 7);
|
1804
|
+
break;
|
1805
|
+
}
|
1806
|
+
}
|
1807
|
+
return message;
|
1808
|
+
},
|
1809
|
+
|
1810
|
+
fromJSON(object: any): TrackUnpublishedResponse {
|
1811
|
+
const message = {
|
1812
|
+
...baseTrackUnpublishedResponse,
|
1813
|
+
} as TrackUnpublishedResponse;
|
1814
|
+
if (object.trackSid !== undefined && object.trackSid !== null) {
|
1815
|
+
message.trackSid = String(object.trackSid);
|
1816
|
+
} else {
|
1817
|
+
message.trackSid = "";
|
1818
|
+
}
|
1819
|
+
return message;
|
1820
|
+
},
|
1821
|
+
|
1822
|
+
toJSON(message: TrackUnpublishedResponse): unknown {
|
1823
|
+
const obj: any = {};
|
1824
|
+
message.trackSid !== undefined && (obj.trackSid = message.trackSid);
|
1825
|
+
return obj;
|
1826
|
+
},
|
1827
|
+
|
1828
|
+
fromPartial(
|
1829
|
+
object: DeepPartial<TrackUnpublishedResponse>
|
1830
|
+
): TrackUnpublishedResponse {
|
1831
|
+
const message = {
|
1832
|
+
...baseTrackUnpublishedResponse,
|
1833
|
+
} as TrackUnpublishedResponse;
|
1834
|
+
message.trackSid = object.trackSid ?? "";
|
1835
|
+
return message;
|
1836
|
+
},
|
1837
|
+
};
|
1838
|
+
|
1732
1839
|
const baseSessionDescription: object = { type: "", sdp: "" };
|
1733
1840
|
|
1734
1841
|
export const SessionDescription = {
|
package/src/room/RTCEngine.ts
CHANGED
@@ -16,7 +16,7 @@ import {
|
|
16
16
|
import { ConnectionError, TrackInvalidError, UnexpectedConnectionState } from './errors';
|
17
17
|
import { EngineEvent } from './events';
|
18
18
|
import PCTransport from './PCTransport';
|
19
|
-
import { isFireFox, sleep } from './utils';
|
19
|
+
import { isFireFox, isWeb, sleep } from './utils';
|
20
20
|
|
21
21
|
const lossyDataChannel = '_lossy';
|
22
22
|
const reliableDataChannel = '_reliable';
|
@@ -112,7 +112,10 @@ export default class RTCEngine extends (
|
|
112
112
|
if (this.publisher && this.publisher.pc.signalingState !== 'closed') {
|
113
113
|
this.publisher.pc.getSenders().forEach((sender) => {
|
114
114
|
try {
|
115
|
-
|
115
|
+
// TODO: react-native-webrtc doesn't have removeTrack yet.
|
116
|
+
if (this.publisher?.pc.removeTrack) {
|
117
|
+
this.publisher?.pc.removeTrack(sender);
|
118
|
+
}
|
116
119
|
} catch (e) {
|
117
120
|
log.warn('could not removeTrack', e);
|
118
121
|
}
|
@@ -171,6 +174,11 @@ export default class RTCEngine extends (
|
|
171
174
|
this.rtcConfig.iceServers = rtcIceServers;
|
172
175
|
}
|
173
176
|
|
177
|
+
// @ts-ignore
|
178
|
+
this.rtcConfig.sdpSemantics = 'unified-plan';
|
179
|
+
// @ts-ignore
|
180
|
+
this.rtcConfig.continualGatheringPolicy = 'gather_continually';
|
181
|
+
|
174
182
|
this.publisher = new PCTransport(this.rtcConfig);
|
175
183
|
this.subscriber = new PCTransport(this.rtcConfig);
|
176
184
|
|
@@ -221,10 +229,18 @@ export default class RTCEngine extends (
|
|
221
229
|
}
|
222
230
|
};
|
223
231
|
|
224
|
-
|
225
|
-
this.
|
226
|
-
|
227
|
-
|
232
|
+
if (isWeb()) {
|
233
|
+
this.subscriber.pc.ontrack = (ev: RTCTrackEvent) => {
|
234
|
+
this.emit(EngineEvent.MediaTrackAdded, ev.track, ev.streams[0], ev.receiver);
|
235
|
+
};
|
236
|
+
} else {
|
237
|
+
// TODO: react-native-webrtc doesn't have ontrack yet, replace when ready.
|
238
|
+
// @ts-ignore
|
239
|
+
this.subscriber.pc.onaddstream = (ev: { stream: MediaStream }) => {
|
240
|
+
const track = ev.stream.getTracks()[0];
|
241
|
+
this.emit(EngineEvent.MediaTrackAdded, track, ev.stream);
|
242
|
+
};
|
243
|
+
}
|
228
244
|
// data channels
|
229
245
|
this.lossyDC = this.publisher.pc.createDataChannel(lossyDataChannel, {
|
230
246
|
// will drop older packets that arrive
|
@@ -628,7 +644,7 @@ export type EngineEventCallbacks = {
|
|
628
644
|
mediaTrackAdded: (
|
629
645
|
track: MediaStreamTrack,
|
630
646
|
streams: MediaStream,
|
631
|
-
receiver
|
647
|
+
receiver?: RTCRtpReceiver
|
632
648
|
) => void,
|
633
649
|
activeSpeakersUpdate: (speakers: Array<SpeakerInfo>) => void,
|
634
650
|
dataPacketReceived: (userPacket: UserPacket, kind: DataPacket_Kind) => void,
|
package/src/room/Room.ts
CHANGED
@@ -5,7 +5,7 @@ import log from '../logger';
|
|
5
5
|
import { RoomConnectOptions, RoomOptions } from '../options';
|
6
6
|
import {
|
7
7
|
DataPacket_Kind, ParticipantInfo,
|
8
|
-
ParticipantInfo_State, Room as RoomModel, SpeakerInfo, UserPacket,
|
8
|
+
ParticipantInfo_State, ParticipantPermission, Room as RoomModel, SpeakerInfo, UserPacket,
|
9
9
|
} from '../proto/livekit_models';
|
10
10
|
import {
|
11
11
|
ConnectionQualityUpdate,
|
@@ -30,7 +30,7 @@ import { Track } from './track/Track';
|
|
30
30
|
import { TrackPublication } from './track/TrackPublication';
|
31
31
|
import { AdaptiveStreamSettings, RemoteTrack } from './track/types';
|
32
32
|
import { getNewAudioContext } from './track/utils';
|
33
|
-
import { unpackStreamId } from './utils';
|
33
|
+
import { isWeb, unpackStreamId } from './utils';
|
34
34
|
|
35
35
|
export enum RoomState {
|
36
36
|
Disconnected = 'disconnected',
|
@@ -211,12 +211,9 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
211
211
|
this.state = RoomState.Connected;
|
212
212
|
this.emit(RoomEvent.StateChanged, this.state);
|
213
213
|
const pi = joinResponse.participant!;
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
this.engine,
|
218
|
-
this.options,
|
219
|
-
);
|
214
|
+
|
215
|
+
this.localParticipant.sid = pi.sid;
|
216
|
+
this.localParticipant.identity = pi.identity;
|
220
217
|
|
221
218
|
this.localParticipant.updateInfo(pi);
|
222
219
|
// forward metadata changed for the local participant
|
@@ -244,7 +241,15 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
244
241
|
})
|
245
242
|
.on(ParticipantEvent.MediaDevicesError, (e: Error) => {
|
246
243
|
this.emit(RoomEvent.MediaDevicesError, e);
|
247
|
-
})
|
244
|
+
})
|
245
|
+
.on(ParticipantEvent.ParticipantPermissionsChanged,
|
246
|
+
(prevPermissions: ParticipantPermission) => {
|
247
|
+
this.emit(
|
248
|
+
RoomEvent.ParticipantPermissionsChanged,
|
249
|
+
prevPermissions,
|
250
|
+
this.localParticipant,
|
251
|
+
);
|
252
|
+
});
|
248
253
|
|
249
254
|
// populate remote participants, these should not trigger new events
|
250
255
|
joinResponse.otherParticipants.forEach((info) => {
|
@@ -271,8 +276,10 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
271
276
|
clearTimeout(connectTimeout);
|
272
277
|
|
273
278
|
// also hook unload event
|
274
|
-
|
275
|
-
|
279
|
+
if (isWeb()) {
|
280
|
+
window.addEventListener('beforeunload', this.onBeforeUnload);
|
281
|
+
navigator.mediaDevices.addEventListener('devicechange', this.handleDeviceChange);
|
282
|
+
}
|
276
283
|
|
277
284
|
resolve(this);
|
278
285
|
});
|
@@ -521,8 +528,10 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
521
528
|
this.audioContext.close();
|
522
529
|
this.audioContext = undefined;
|
523
530
|
}
|
524
|
-
|
525
|
-
|
531
|
+
if (isWeb()) {
|
532
|
+
window.removeEventListener('beforeunload', this.onBeforeUnload);
|
533
|
+
navigator.mediaDevices.removeEventListener('devicechange', this.handleDeviceChange);
|
534
|
+
}
|
526
535
|
this.state = RoomState.Disconnected;
|
527
536
|
this.emit(RoomEvent.Disconnected);
|
528
537
|
this.emit(RoomEvent.StateChanged, this.state);
|
@@ -532,7 +541,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
532
541
|
// handle changes to participant state, and send events
|
533
542
|
participantInfos.forEach((info) => {
|
534
543
|
if (info.sid === this.localParticipant.sid
|
535
|
-
|
544
|
+
|| info.identity === this.localParticipant.identity) {
|
536
545
|
this.localParticipant.updateInfo(info);
|
537
546
|
return;
|
538
547
|
}
|
@@ -798,7 +807,11 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
798
807
|
})
|
799
808
|
.on(ParticipantEvent.ConnectionQualityChanged, (quality: ConnectionQuality) => {
|
800
809
|
this.emit(RoomEvent.ConnectionQualityChanged, quality, participant);
|
801
|
-
})
|
810
|
+
})
|
811
|
+
.on(ParticipantEvent.ParticipantPermissionsChanged,
|
812
|
+
(prevPermissions: ParticipantPermission) => {
|
813
|
+
this.emit(RoomEvent.ParticipantPermissionsChanged, prevPermissions, participant);
|
814
|
+
});
|
802
815
|
return participant;
|
803
816
|
}
|
804
817
|
|
@@ -903,6 +916,10 @@ export type RoomEventCallbacks = {
|
|
903
916
|
metadata: string | undefined,
|
904
917
|
participant: RemoteParticipant | LocalParticipant
|
905
918
|
) => void,
|
919
|
+
participantPermissionsChanged: (
|
920
|
+
prevPermissions: ParticipantPermission,
|
921
|
+
participant: RemoteParticipant | LocalParticipant
|
922
|
+
) => void,
|
906
923
|
activeSpeakersChanged: (speakers: Array<Participant>) => void,
|
907
924
|
roomMetadataChanged: (metadata: string) => void,
|
908
925
|
dataReceived: (
|
package/src/room/events.ts
CHANGED
@@ -225,6 +225,12 @@ export enum RoomEvent {
|
|
225
225
|
* args: (error: Error)
|
226
226
|
*/
|
227
227
|
MediaDevicesError = 'mediaDevicesError',
|
228
|
+
|
229
|
+
/**
|
230
|
+
* A participant's permission has changed. Currently only fired on LocalParticipant.
|
231
|
+
* args: (prevPermissions: [[ParticipantPermission]], participant: [[Participant]])
|
232
|
+
*/
|
233
|
+
ParticipantPermissionsChanged = 'participantPermissionsChanged',
|
228
234
|
}
|
229
235
|
|
230
236
|
export enum ParticipantEvent {
|
@@ -370,6 +376,12 @@ export enum ParticipantEvent {
|
|
370
376
|
// fired only on LocalParticipant
|
371
377
|
/** @internal */
|
372
378
|
MediaDevicesError = 'mediaDevicesError',
|
379
|
+
|
380
|
+
/**
|
381
|
+
* A participant's permission has changed. Currently only fired on LocalParticipant.
|
382
|
+
* args: (prevPermissions: [[ParticipantPermission]])
|
383
|
+
*/
|
384
|
+
ParticipantPermissionsChanged = 'participantPermissionsChanged',
|
373
385
|
}
|
374
386
|
|
375
387
|
/** @internal */
|
@@ -1,10 +1,11 @@
|
|
1
1
|
import log from '../../logger';
|
2
2
|
import { RoomOptions } from '../../options';
|
3
3
|
import {
|
4
|
-
DataPacket, DataPacket_Kind,
|
4
|
+
DataPacket, DataPacket_Kind, ParticipantPermission,
|
5
5
|
} from '../../proto/livekit_models';
|
6
6
|
import {
|
7
|
-
AddTrackRequest, DataChannelInfo,
|
7
|
+
AddTrackRequest, DataChannelInfo,
|
8
|
+
SubscribedQualityUpdate, TrackPublishedResponse, TrackUnpublishedResponse,
|
8
9
|
} from '../../proto/livekit_rtc';
|
9
10
|
import {
|
10
11
|
TrackInvalidError,
|
@@ -24,6 +25,7 @@ import {
|
|
24
25
|
} from '../track/options';
|
25
26
|
import { Track } from '../track/Track';
|
26
27
|
import { constraintsForOptions, mergeDefaultOptions } from '../track/utils';
|
28
|
+
import { isFireFox } from '../utils';
|
27
29
|
import Participant from './Participant';
|
28
30
|
import { ParticipantTrackPermission, trackPermissionToProto } from './ParticipantTrackPermission';
|
29
31
|
import { computeVideoEncodings, mediaTrackToLocalTrack } from './publishUtils';
|
@@ -70,6 +72,8 @@ export default class LocalParticipant extends Participant {
|
|
70
72
|
};
|
71
73
|
|
72
74
|
this.engine.client.onSubscribedQualityUpdate = this.handleSubscribedQualityUpdate;
|
75
|
+
|
76
|
+
this.engine.client.onLocalTrackUnpublished = this.handleLocalTrackUnpublished;
|
73
77
|
}
|
74
78
|
|
75
79
|
get lastCameraError(): Error | undefined {
|
@@ -119,6 +123,16 @@ export default class LocalParticipant extends Participant {
|
|
119
123
|
return this.setTrackEnabled(Track.Source.ScreenShare, enabled);
|
120
124
|
}
|
121
125
|
|
126
|
+
/** @internal */
|
127
|
+
setPermissions(permissions: ParticipantPermission): boolean {
|
128
|
+
const prevPermissions = this.permissions;
|
129
|
+
const changed = super.setPermissions(permissions);
|
130
|
+
if (changed && prevPermissions) {
|
131
|
+
this.emit(ParticipantEvent.ParticipantPermissionsChanged, prevPermissions);
|
132
|
+
}
|
133
|
+
return changed;
|
134
|
+
}
|
135
|
+
|
122
136
|
/**
|
123
137
|
* Enable or disable publishing for a track by source. This serves as a simple
|
124
138
|
* way to manage the common tracks (camera, mic, or screen share)
|
@@ -259,6 +273,7 @@ export default class LocalParticipant extends Participant {
|
|
259
273
|
} else if (track.kind === Track.Kind.Audio) {
|
260
274
|
track.source = Track.Source.Microphone;
|
261
275
|
}
|
276
|
+
track.mediaStream = stream;
|
262
277
|
return track;
|
263
278
|
});
|
264
279
|
}
|
@@ -358,6 +373,12 @@ export default class LocalParticipant extends Participant {
|
|
358
373
|
track.stopOnMute = true;
|
359
374
|
}
|
360
375
|
|
376
|
+
if (track.source === Track.Source.ScreenShare && isFireFox()) {
|
377
|
+
// Firefox does not work well with simulcasted screen share
|
378
|
+
// we frequently get no data on layer 0 when enabled
|
379
|
+
opts.simulcast = false;
|
380
|
+
}
|
381
|
+
|
361
382
|
// handle track actions
|
362
383
|
track.on(TrackEvent.Muted, this.onTrackMuted);
|
363
384
|
track.on(TrackEvent.Unmuted, this.onTrackUnmuted);
|
@@ -612,6 +633,16 @@ export default class LocalParticipant extends Participant {
|
|
612
633
|
pub.videoTrack?.setPublishingLayers(update.subscribedQualities);
|
613
634
|
};
|
614
635
|
|
636
|
+
private handleLocalTrackUnpublished = (unpublished: TrackUnpublishedResponse) => {
|
637
|
+
const track = this.tracks.get(unpublished.trackSid);
|
638
|
+
if (!track) {
|
639
|
+
log.warn('handleLocalTrackUnpublished',
|
640
|
+
'received unpublished event for unknown track', unpublished.trackSid);
|
641
|
+
return;
|
642
|
+
}
|
643
|
+
this.unpublishTrack(track.track!);
|
644
|
+
};
|
645
|
+
|
615
646
|
private onTrackUnpublish = (track: LocalTrack) => {
|
616
647
|
this.unpublishTrack(track);
|
617
648
|
};
|
@@ -1,6 +1,8 @@
|
|
1
1
|
import { EventEmitter } from 'events';
|
2
2
|
import type TypedEmitter from 'typed-emitter';
|
3
|
-
import {
|
3
|
+
import {
|
4
|
+
ConnectionQuality as ProtoQuality, DataPacket_Kind, ParticipantInfo, ParticipantPermission,
|
5
|
+
} from '../../proto/livekit_models';
|
4
6
|
import { ParticipantEvent, TrackEvent } from '../events';
|
5
7
|
import LocalTrackPublication from '../track/LocalTrackPublication';
|
6
8
|
import RemoteTrackPublication from '../track/RemoteTrackPublication';
|
@@ -60,6 +62,8 @@ export default class Participant extends (
|
|
60
62
|
|
61
63
|
lastSpokeAt?: Date | undefined;
|
62
64
|
|
65
|
+
permissions?: ParticipantPermission;
|
66
|
+
|
63
67
|
private _connectionQuality: ConnectionQuality = ConnectionQuality.Unknown;
|
64
68
|
|
65
69
|
/** @internal */
|
@@ -153,13 +157,16 @@ export default class Participant extends (
|
|
153
157
|
this.sid = info.sid;
|
154
158
|
this.name = info.name;
|
155
159
|
this.setMetadata(info.metadata);
|
160
|
+
if (info.permission) {
|
161
|
+
this.setPermissions(info.permission);
|
162
|
+
}
|
156
163
|
// set this last so setMetadata can detect changes
|
157
164
|
this.participantInfo = info;
|
158
165
|
}
|
159
166
|
|
160
167
|
/** @internal */
|
161
168
|
setMetadata(md: string) {
|
162
|
-
const changed =
|
169
|
+
const changed = this.metadata !== md;
|
163
170
|
const prevMetadata = this.metadata;
|
164
171
|
this.metadata = md;
|
165
172
|
|
@@ -169,6 +176,18 @@ export default class Participant extends (
|
|
169
176
|
}
|
170
177
|
}
|
171
178
|
|
179
|
+
/** @internal */
|
180
|
+
setPermissions(permissions: ParticipantPermission): boolean {
|
181
|
+
const changed = permissions.canPublish !== this.permissions?.canPublish
|
182
|
+
|| permissions.canSubscribe !== this.permissions?.canSubscribe
|
183
|
+
|| permissions.canPublishData !== this.permissions?.canPublishData
|
184
|
+
|| permissions.hidden !== this.permissions?.hidden
|
185
|
+
|| permissions.recorder !== this.permissions?.recorder;
|
186
|
+
this.permissions = permissions;
|
187
|
+
|
188
|
+
return changed;
|
189
|
+
}
|
190
|
+
|
172
191
|
/** @internal */
|
173
192
|
setIsSpeaking(speaking: boolean) {
|
174
193
|
if (speaking === this.isSpeaking) {
|
@@ -246,4 +265,5 @@ export type ParticipantEventCallbacks = {
|
|
246
265
|
status: TrackPublication.SubscriptionStatus
|
247
266
|
) => void,
|
248
267
|
mediaDevicesError: (error: Error) => void,
|
268
|
+
participantPermissionsChanged: (prevPermissions: ParticipantPermission) => void,
|
249
269
|
};
|
@@ -32,6 +32,7 @@ export default abstract class RemoteTrack extends Track {
|
|
32
32
|
// this is needed to determine when the track is finished
|
33
33
|
// we send each track down in its own MediaStream, so we can assume the
|
34
34
|
// current track is the only one that can be removed.
|
35
|
+
this.mediaStream = stream;
|
35
36
|
stream.onremovetrack = () => {
|
36
37
|
this.receiver = undefined;
|
37
38
|
this._currentBitrate = 0;
|
package/src/room/track/Track.ts
CHANGED
@@ -3,7 +3,7 @@ import type TypedEventEmitter from 'typed-emitter';
|
|
3
3
|
import { TrackSource, TrackType } from '../../proto/livekit_models';
|
4
4
|
import { StreamState as ProtoStreamState } from '../../proto/livekit_rtc';
|
5
5
|
import { TrackEvent } from '../events';
|
6
|
-
import { isFireFox, isSafari } from '../utils';
|
6
|
+
import { isFireFox, isSafari, isWeb } from '../utils';
|
7
7
|
|
8
8
|
// keep old audio elements when detached, we would re-use them since on iOS
|
9
9
|
// Safari tracks which audio elements have been "blessed" by the user.
|
@@ -12,6 +12,8 @@ const recycledElements: Array<HTMLAudioElement> = [];
|
|
12
12
|
export class Track extends (EventEmitter as new () => TypedEventEmitter<TrackEventCallbacks>) {
|
13
13
|
kind: Track.Kind;
|
14
14
|
|
15
|
+
mediaStream?: MediaStream;
|
16
|
+
|
15
17
|
mediaStreamTrack: MediaStreamTrack;
|
16
18
|
|
17
19
|
attachedElements: HTMLMediaElement[] = [];
|
@@ -34,8 +36,12 @@ export class Track extends (EventEmitter as new () => TypedEventEmitter<TrackEve
|
|
34
36
|
this.kind = kind;
|
35
37
|
this.mediaStreamTrack = mediaTrack;
|
36
38
|
this.source = Track.Source.Unknown;
|
37
|
-
|
38
|
-
|
39
|
+
if (isWeb()) {
|
40
|
+
this.isInBackground = document.visibilityState === 'hidden';
|
41
|
+
document.addEventListener('visibilitychange', this.appVisibilityChangedListener);
|
42
|
+
} else {
|
43
|
+
this.isInBackground = false;
|
44
|
+
}
|
39
45
|
}
|
40
46
|
|
41
47
|
/** current receive bits per second */
|
@@ -133,7 +139,9 @@ export class Track extends (EventEmitter as new () => TypedEventEmitter<TrackEve
|
|
133
139
|
|
134
140
|
stop() {
|
135
141
|
this.mediaStreamTrack.stop();
|
136
|
-
|
142
|
+
if (isWeb()) {
|
143
|
+
document.removeEventListener('visibilitychange', this.appVisibilityChangedListener);
|
144
|
+
}
|
137
145
|
}
|
138
146
|
|
139
147
|
protected enable() {
|
package/src/room/track/create.ts
CHANGED
package/src/room/utils.ts
CHANGED
@@ -27,6 +27,10 @@ export function isMobile(): boolean {
|
|
27
27
|
return /Tablet|iPad|Mobile|Android|BlackBerry/.test(navigator.userAgent);
|
28
28
|
}
|
29
29
|
|
30
|
+
export function isWeb(): boolean {
|
31
|
+
return typeof document !== 'undefined';
|
32
|
+
}
|
33
|
+
|
30
34
|
function roDispatchCallback(entries: ResizeObserverEntry[]) {
|
31
35
|
for (const entry of entries) {
|
32
36
|
(entry.target as ObservableMediaElement).handleResize(entry);
|
package/src/version.ts
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
export const version = '0.17.
|
2
|
-
export const protocolVersion =
|
1
|
+
export const version = '0.17.4';
|
2
|
+
export const protocolVersion = 7;
|