livekit-client 0.17.4 → 0.17.6-rc2

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.
Files changed (155) hide show
  1. package/CHANGELOG.md +1 -1
  2. package/README.md +26 -20
  3. package/dist/api/SignalClient.d.ts +1 -0
  4. package/dist/connect.d.ts +2 -0
  5. package/dist/index.d.ts +2 -2
  6. package/dist/livekit-client.esm.js +17344 -0
  7. package/dist/livekit-client.esm.js.map +1 -0
  8. package/dist/livekit-client.umd.js +2 -0
  9. package/dist/livekit-client.umd.js.map +1 -0
  10. package/dist/logger.d.ts +22 -11
  11. package/dist/options.d.ts +4 -2
  12. package/dist/proto/google/protobuf/timestamp.d.ts +12 -2
  13. package/dist/proto/livekit_models.d.ts +524 -17
  14. package/dist/proto/livekit_rtc.d.ts +3449 -31
  15. package/dist/room/DeviceManager.d.ts +1 -1
  16. package/dist/room/RTCEngine.d.ts +1 -1
  17. package/dist/room/Room.d.ts +2 -2
  18. package/dist/room/events.d.ts +1 -1
  19. package/dist/room/participant/LocalParticipant.d.ts +9 -5
  20. package/dist/room/participant/RemoteParticipant.d.ts +9 -0
  21. package/dist/room/track/RemoteAudioTrack.d.ts +11 -0
  22. package/dist/room/track/options.d.ts +1 -1
  23. package/dist/test/mocks.d.ts +11 -0
  24. package/dist/version.d.ts +1 -1
  25. package/package.json +41 -16
  26. package/.eslintrc.js +0 -17
  27. package/.gitmodules +0 -3
  28. package/dist/api/RequestQueue.js +0 -61
  29. package/dist/api/RequestQueue.js.map +0 -1
  30. package/dist/api/SignalClient.js +0 -428
  31. package/dist/api/SignalClient.js.map +0 -1
  32. package/dist/connect.js +0 -130
  33. package/dist/connect.js.map +0 -1
  34. package/dist/index.js +0 -71
  35. package/dist/index.js.map +0 -1
  36. package/dist/logger.js +0 -24
  37. package/dist/logger.js.map +0 -1
  38. package/dist/options.js +0 -3
  39. package/dist/options.js.map +0 -1
  40. package/dist/proto/google/protobuf/timestamp.js +0 -93
  41. package/dist/proto/google/protobuf/timestamp.js.map +0 -1
  42. package/dist/proto/livekit_models.js +0 -2688
  43. package/dist/proto/livekit_models.js.map +0 -1
  44. package/dist/proto/livekit_rtc.js +0 -2995
  45. package/dist/proto/livekit_rtc.js.map +0 -1
  46. package/dist/room/DeviceManager.js +0 -62
  47. package/dist/room/DeviceManager.js.map +0 -1
  48. package/dist/room/PCTransport.js +0 -91
  49. package/dist/room/PCTransport.js.map +0 -1
  50. package/dist/room/RTCEngine.js +0 -562
  51. package/dist/room/RTCEngine.js.map +0 -1
  52. package/dist/room/Room.js +0 -759
  53. package/dist/room/Room.js.map +0 -1
  54. package/dist/room/errors.js +0 -68
  55. package/dist/room/errors.js.map +0 -1
  56. package/dist/room/events.js +0 -385
  57. package/dist/room/events.js.map +0 -1
  58. package/dist/room/participant/LocalParticipant.js +0 -647
  59. package/dist/room/participant/LocalParticipant.js.map +0 -1
  60. package/dist/room/participant/Participant.js +0 -189
  61. package/dist/room/participant/Participant.js.map +0 -1
  62. package/dist/room/participant/ParticipantTrackPermission.js +0 -16
  63. package/dist/room/participant/ParticipantTrackPermission.js.map +0 -1
  64. package/dist/room/participant/RemoteParticipant.js +0 -194
  65. package/dist/room/participant/RemoteParticipant.js.map +0 -1
  66. package/dist/room/participant/publishUtils.js +0 -189
  67. package/dist/room/participant/publishUtils.js.map +0 -1
  68. package/dist/room/participant/publishUtils.test.d.ts +0 -1
  69. package/dist/room/participant/publishUtils.test.js +0 -118
  70. package/dist/room/participant/publishUtils.test.js.map +0 -1
  71. package/dist/room/stats.js +0 -26
  72. package/dist/room/stats.js.map +0 -1
  73. package/dist/room/track/LocalAudioTrack.js +0 -153
  74. package/dist/room/track/LocalAudioTrack.js.map +0 -1
  75. package/dist/room/track/LocalTrack.js +0 -158
  76. package/dist/room/track/LocalTrack.js.map +0 -1
  77. package/dist/room/track/LocalTrackPublication.js +0 -64
  78. package/dist/room/track/LocalTrackPublication.js.map +0 -1
  79. package/dist/room/track/LocalVideoTrack.js +0 -297
  80. package/dist/room/track/LocalVideoTrack.js.map +0 -1
  81. package/dist/room/track/LocalVideoTrack.test.d.ts +0 -1
  82. package/dist/room/track/LocalVideoTrack.test.js +0 -68
  83. package/dist/room/track/LocalVideoTrack.test.js.map +0 -1
  84. package/dist/room/track/RemoteAudioTrack.js +0 -64
  85. package/dist/room/track/RemoteAudioTrack.js.map +0 -1
  86. package/dist/room/track/RemoteTrack.js +0 -49
  87. package/dist/room/track/RemoteTrack.js.map +0 -1
  88. package/dist/room/track/RemoteTrackPublication.js +0 -178
  89. package/dist/room/track/RemoteTrackPublication.js.map +0 -1
  90. package/dist/room/track/RemoteVideoTrack.js +0 -201
  91. package/dist/room/track/RemoteVideoTrack.js.map +0 -1
  92. package/dist/room/track/Track.js +0 -283
  93. package/dist/room/track/Track.js.map +0 -1
  94. package/dist/room/track/TrackPublication.js +0 -92
  95. package/dist/room/track/TrackPublication.js.map +0 -1
  96. package/dist/room/track/create.js +0 -131
  97. package/dist/room/track/create.js.map +0 -1
  98. package/dist/room/track/defaults.js +0 -21
  99. package/dist/room/track/defaults.js.map +0 -1
  100. package/dist/room/track/options.js +0 -100
  101. package/dist/room/track/options.js.map +0 -1
  102. package/dist/room/track/types.js +0 -3
  103. package/dist/room/track/types.js.map +0 -1
  104. package/dist/room/track/utils.js +0 -113
  105. package/dist/room/track/utils.js.map +0 -1
  106. package/dist/room/track/utils.test.d.ts +0 -1
  107. package/dist/room/track/utils.test.js +0 -85
  108. package/dist/room/track/utils.test.js.map +0 -1
  109. package/dist/room/utils.js +0 -79
  110. package/dist/room/utils.js.map +0 -1
  111. package/dist/version.js +0 -6
  112. package/dist/version.js.map +0 -1
  113. package/jest.config.js +0 -6
  114. package/src/api/RequestQueue.ts +0 -53
  115. package/src/api/SignalClient.ts +0 -499
  116. package/src/connect.ts +0 -100
  117. package/src/index.ts +0 -47
  118. package/src/logger.ts +0 -22
  119. package/src/options.ts +0 -149
  120. package/src/proto/google/protobuf/timestamp.ts +0 -222
  121. package/src/proto/livekit_models.ts +0 -3019
  122. package/src/proto/livekit_rtc.ts +0 -3677
  123. package/src/room/DeviceManager.ts +0 -57
  124. package/src/room/PCTransport.ts +0 -86
  125. package/src/room/RTCEngine.ts +0 -652
  126. package/src/room/Room.ts +0 -943
  127. package/src/room/errors.ts +0 -65
  128. package/src/room/events.ts +0 -424
  129. package/src/room/participant/LocalParticipant.ts +0 -734
  130. package/src/room/participant/Participant.ts +0 -269
  131. package/src/room/participant/ParticipantTrackPermission.ts +0 -32
  132. package/src/room/participant/RemoteParticipant.ts +0 -243
  133. package/src/room/participant/publishUtils.test.ts +0 -145
  134. package/src/room/participant/publishUtils.ts +0 -225
  135. package/src/room/stats.ts +0 -130
  136. package/src/room/track/LocalAudioTrack.ts +0 -137
  137. package/src/room/track/LocalTrack.ts +0 -161
  138. package/src/room/track/LocalTrackPublication.ts +0 -66
  139. package/src/room/track/LocalVideoTrack.test.ts +0 -70
  140. package/src/room/track/LocalVideoTrack.ts +0 -293
  141. package/src/room/track/RemoteAudioTrack.ts +0 -58
  142. package/src/room/track/RemoteTrack.ts +0 -62
  143. package/src/room/track/RemoteTrackPublication.ts +0 -198
  144. package/src/room/track/RemoteVideoTrack.ts +0 -235
  145. package/src/room/track/Track.ts +0 -343
  146. package/src/room/track/TrackPublication.ts +0 -120
  147. package/src/room/track/create.ts +0 -121
  148. package/src/room/track/defaults.ts +0 -23
  149. package/src/room/track/options.ts +0 -281
  150. package/src/room/track/types.ts +0 -20
  151. package/src/room/track/utils.test.ts +0 -93
  152. package/src/room/track/utils.ts +0 -115
  153. package/src/room/utils.ts +0 -70
  154. package/src/version.ts +0 -2
  155. package/tsconfig.eslint.json +0 -11
@@ -1,269 +0,0 @@
1
- import { EventEmitter } from 'events';
2
- import type TypedEmitter from 'typed-emitter';
3
- import {
4
- ConnectionQuality as ProtoQuality, DataPacket_Kind, ParticipantInfo, ParticipantPermission,
5
- } from '../../proto/livekit_models';
6
- import { ParticipantEvent, TrackEvent } from '../events';
7
- import LocalTrackPublication from '../track/LocalTrackPublication';
8
- import RemoteTrackPublication from '../track/RemoteTrackPublication';
9
- import { Track } from '../track/Track';
10
- import { TrackPublication } from '../track/TrackPublication';
11
- import { RemoteTrack } from '../track/types';
12
-
13
- export enum ConnectionQuality {
14
- Excellent = 'excellent',
15
- Good = 'good',
16
- Poor = 'poor',
17
- Unknown = 'unknown',
18
- }
19
-
20
- function qualityFromProto(q: ProtoQuality): ConnectionQuality {
21
- switch (q) {
22
- case ProtoQuality.EXCELLENT:
23
- return ConnectionQuality.Excellent;
24
- case ProtoQuality.GOOD:
25
- return ConnectionQuality.Good;
26
- case ProtoQuality.POOR:
27
- return ConnectionQuality.Poor;
28
- default:
29
- return ConnectionQuality.Unknown;
30
- }
31
- }
32
-
33
- export default class Participant extends (
34
- EventEmitter as new () => TypedEmitter<ParticipantEventCallbacks>
35
- ) {
36
- protected participantInfo?: ParticipantInfo;
37
-
38
- audioTracks: Map<string, TrackPublication>;
39
-
40
- videoTracks: Map<string, TrackPublication>;
41
-
42
- /** map of track sid => all published tracks */
43
- tracks: Map<string, TrackPublication>;
44
-
45
- /** audio level between 0-1.0, 1 being loudest, 0 being softest */
46
- audioLevel: number = 0;
47
-
48
- /** if participant is currently speaking */
49
- isSpeaking: boolean = false;
50
-
51
- /** server assigned unique id */
52
- sid: string;
53
-
54
- /** client assigned identity, encoded in JWT token */
55
- identity: string;
56
-
57
- /** client assigned display name, encoded in JWT token */
58
- name?: string;
59
-
60
- /** client metadata, opaque to livekit */
61
- metadata?: string;
62
-
63
- lastSpokeAt?: Date | undefined;
64
-
65
- permissions?: ParticipantPermission;
66
-
67
- private _connectionQuality: ConnectionQuality = ConnectionQuality.Unknown;
68
-
69
- /** @internal */
70
- constructor(sid: string, identity: string) {
71
- super();
72
- this.sid = sid;
73
- this.identity = identity;
74
- this.audioTracks = new Map();
75
- this.videoTracks = new Map();
76
- this.tracks = new Map();
77
- }
78
-
79
- getTracks(): TrackPublication[] {
80
- return Array.from(this.tracks.values());
81
- }
82
-
83
- /**
84
- * Finds the first track that matches the source filter, for example, getting
85
- * the user's camera track with getTrackBySource(Track.Source.Camera).
86
- * @param source
87
- * @returns
88
- */
89
- getTrack(source: Track.Source): TrackPublication | undefined {
90
- if (source === Track.Source.Unknown) {
91
- return;
92
- }
93
- for (const [, pub] of this.tracks) {
94
- if (pub.source === source) {
95
- return pub;
96
- }
97
- if (pub.source === Track.Source.Unknown) {
98
- if (source === Track.Source.Microphone && pub.kind === Track.Kind.Audio && pub.trackName !== 'screen') {
99
- return pub;
100
- }
101
- if (source === Track.Source.Camera && pub.kind === Track.Kind.Video && pub.trackName !== 'screen') {
102
- return pub;
103
- }
104
- if (source === Track.Source.ScreenShare && pub.kind === Track.Kind.Video && pub.trackName === 'screen') {
105
- return pub;
106
- }
107
- if (source === Track.Source.ScreenShareAudio && pub.kind === Track.Kind.Audio && pub.trackName === 'screen') {
108
- return pub;
109
- }
110
- }
111
- }
112
- }
113
-
114
- /**
115
- * Finds the first track that matches the track's name.
116
- * @param name
117
- * @returns
118
- */
119
- getTrackByName(name: string): TrackPublication | undefined {
120
- for (const [, pub] of this.tracks) {
121
- if (pub.trackName === name) {
122
- return pub;
123
- }
124
- }
125
- }
126
-
127
- get connectionQuality(): ConnectionQuality {
128
- return this._connectionQuality;
129
- }
130
-
131
- get isCameraEnabled(): boolean {
132
- const track = this.getTrack(Track.Source.Camera);
133
- return !(track?.isMuted ?? true);
134
- }
135
-
136
- get isMicrophoneEnabled(): boolean {
137
- const track = this.getTrack(Track.Source.Microphone);
138
- return !(track?.isMuted ?? true);
139
- }
140
-
141
- get isScreenShareEnabled(): boolean {
142
- const track = this.getTrack(Track.Source.ScreenShare);
143
- return !!track;
144
- }
145
-
146
- /** when participant joined the room */
147
- get joinedAt(): Date | undefined {
148
- if (this.participantInfo) {
149
- return new Date(this.participantInfo.joinedAt * 1000);
150
- }
151
- return new Date();
152
- }
153
-
154
- /** @internal */
155
- updateInfo(info: ParticipantInfo) {
156
- this.identity = info.identity;
157
- this.sid = info.sid;
158
- this.name = info.name;
159
- this.setMetadata(info.metadata);
160
- if (info.permission) {
161
- this.setPermissions(info.permission);
162
- }
163
- // set this last so setMetadata can detect changes
164
- this.participantInfo = info;
165
- }
166
-
167
- /** @internal */
168
- setMetadata(md: string) {
169
- const changed = this.metadata !== md;
170
- const prevMetadata = this.metadata;
171
- this.metadata = md;
172
-
173
- if (changed) {
174
- this.emit(ParticipantEvent.MetadataChanged, prevMetadata);
175
- this.emit(ParticipantEvent.ParticipantMetadataChanged, prevMetadata);
176
- }
177
- }
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
-
191
- /** @internal */
192
- setIsSpeaking(speaking: boolean) {
193
- if (speaking === this.isSpeaking) {
194
- return;
195
- }
196
- this.isSpeaking = speaking;
197
- if (speaking) {
198
- this.lastSpokeAt = new Date();
199
- }
200
- this.emit(ParticipantEvent.IsSpeakingChanged, speaking);
201
- }
202
-
203
- /** @internal */
204
- setConnectionQuality(q: ProtoQuality) {
205
- const prevQuality = this._connectionQuality;
206
- this._connectionQuality = qualityFromProto(q);
207
- if (prevQuality !== this._connectionQuality) {
208
- this.emit(ParticipantEvent.ConnectionQualityChanged, this._connectionQuality);
209
- }
210
- }
211
-
212
- protected addTrackPublication(publication: TrackPublication) {
213
- // forward publication driven events
214
- publication.on(TrackEvent.Muted, () => {
215
- this.emit(ParticipantEvent.TrackMuted, publication);
216
- });
217
-
218
- publication.on(TrackEvent.Unmuted, () => {
219
- this.emit(ParticipantEvent.TrackUnmuted, publication);
220
- });
221
-
222
- const pub = publication;
223
- if (pub.track) {
224
- pub.track.sid = publication.trackSid;
225
- }
226
-
227
- this.tracks.set(publication.trackSid, publication);
228
- switch (publication.kind) {
229
- case Track.Kind.Audio:
230
- this.audioTracks.set(publication.trackSid, publication);
231
- break;
232
- case Track.Kind.Video:
233
- this.videoTracks.set(publication.trackSid, publication);
234
- break;
235
- default:
236
- break;
237
- }
238
- }
239
- }
240
-
241
- export type ParticipantEventCallbacks = {
242
- trackPublished: (publication: RemoteTrackPublication) => void,
243
- trackSubscribed: (track: RemoteTrack, publication: RemoteTrackPublication) => void,
244
- trackSubscriptionFailed: (trackSid: string) => void,
245
- trackUnpublished: (publication: RemoteTrackPublication) => void,
246
- trackUnsubscribed: (track: RemoteTrack, publication: RemoteTrackPublication) => void,
247
- trackMuted: (publication: TrackPublication) => void,
248
- trackUnmuted: (publication: TrackPublication) => void,
249
- localTrackPublished: (publication: LocalTrackPublication) => void,
250
- localTrackUnpublished: (publication: LocalTrackPublication) => void,
251
- /**
252
- * @deprecated use [[participantMetadataChanged]] instead
253
- */
254
- metadataChanged: (prevMetadata: string | undefined, participant?: any) => void,
255
- participantMetadataChanged: (prevMetadata: string | undefined, participant?: any) => void,
256
- dataReceived: (payload: Uint8Array, kind: DataPacket_Kind) => void,
257
- isSpeakingChanged: (speaking: boolean) => void,
258
- connectionQualityChanged: (connectionQuality: ConnectionQuality) => void,
259
- trackStreamStateChanged: (
260
- publication: RemoteTrackPublication,
261
- streamState: Track.StreamState
262
- ) => void,
263
- trackSubscriptionPermissionChanged: (
264
- publication: RemoteTrackPublication,
265
- status: TrackPublication.SubscriptionStatus
266
- ) => void,
267
- mediaDevicesError: (error: Error) => void,
268
- participantPermissionsChanged: (prevPermissions: ParticipantPermission) => void,
269
- };
@@ -1,32 +0,0 @@
1
- import { TrackPermission } from '../../proto/livekit_rtc';
2
-
3
- export interface ParticipantTrackPermission {
4
- /**
5
- * The participant id this permission applies to.
6
- */
7
- participantSid: string;
8
-
9
- /**
10
- * Grant permission to all all tracks. Takes precedence over allowedTrackSids.
11
- * false if unset.
12
- */
13
- allowAll?: boolean;
14
-
15
- /**
16
- * The list of track ids that the target participant can subscribe to.
17
- * When unset, it'll allow all tracks to be subscribed by the participant.
18
- * When empty, this participant is disallowed from subscribing to any tracks.
19
- */
20
- allowedTrackSids?: string[];
21
- }
22
-
23
- export function trackPermissionToProto(perms: ParticipantTrackPermission): TrackPermission {
24
- if (!perms.participantSid) {
25
- throw new Error('Invalid track permission, missing participantSid');
26
- }
27
- return {
28
- participantSid: perms.participantSid,
29
- allTracks: perms.allowAll ?? false,
30
- trackSids: perms.allowedTrackSids || [],
31
- };
32
- }
@@ -1,243 +0,0 @@
1
- import { SignalClient } from '../../api/SignalClient';
2
- import log from '../../logger';
3
- import { ParticipantInfo } from '../../proto/livekit_models';
4
- import {
5
- UpdateSubscription,
6
- UpdateTrackSettings,
7
- } from '../../proto/livekit_rtc';
8
- import { ParticipantEvent, TrackEvent } from '../events';
9
- import RemoteAudioTrack from '../track/RemoteAudioTrack';
10
- import RemoteTrackPublication from '../track/RemoteTrackPublication';
11
- import RemoteVideoTrack from '../track/RemoteVideoTrack';
12
- import { Track } from '../track/Track';
13
- import { AdaptiveStreamSettings, RemoteTrack } from '../track/types';
14
- import Participant, { ParticipantEventCallbacks } from './Participant';
15
-
16
- export default class RemoteParticipant extends Participant {
17
- audioTracks: Map<string, RemoteTrackPublication>;
18
-
19
- videoTracks: Map<string, RemoteTrackPublication>;
20
-
21
- tracks: Map<string, RemoteTrackPublication>;
22
-
23
- signalClient: SignalClient;
24
-
25
- /** @internal */
26
- static fromParticipantInfo(
27
- signalClient: SignalClient,
28
- pi: ParticipantInfo,
29
- ): RemoteParticipant {
30
- const rp = new RemoteParticipant(signalClient, pi.sid, pi.identity);
31
- rp.updateInfo(pi);
32
- return rp;
33
- }
34
-
35
- /** @internal */
36
- constructor(signalClient: SignalClient, id: string, name?: string) {
37
- super(id, name || '');
38
- this.signalClient = signalClient;
39
- this.tracks = new Map();
40
- this.audioTracks = new Map();
41
- this.videoTracks = new Map();
42
- }
43
-
44
- protected addTrackPublication(publication: RemoteTrackPublication) {
45
- super.addTrackPublication(publication);
46
-
47
- // register action events
48
- publication.on(
49
- TrackEvent.UpdateSettings,
50
- (settings: UpdateTrackSettings) => {
51
- this.signalClient.sendUpdateTrackSettings(settings);
52
- },
53
- );
54
- publication.on(TrackEvent.UpdateSubscription, (sub: UpdateSubscription) => {
55
- sub.participantTracks.forEach((pt) => {
56
- pt.participantSid = this.sid;
57
- });
58
- this.signalClient.sendUpdateSubscription(sub);
59
- });
60
- publication.on(TrackEvent.Ended, (track: RemoteTrack) => {
61
- this.emit(ParticipantEvent.TrackUnsubscribed, track, publication);
62
- });
63
- }
64
-
65
- getTrack(source: Track.Source): RemoteTrackPublication | undefined {
66
- const track = super.getTrack(source);
67
- if (track) {
68
- return track as RemoteTrackPublication;
69
- }
70
- }
71
-
72
- getTrackByName(name: string): RemoteTrackPublication | undefined {
73
- const track = super.getTrackByName(name);
74
- if (track) {
75
- return track as RemoteTrackPublication;
76
- }
77
- }
78
-
79
- /** @internal */
80
- addSubscribedMediaTrack(
81
- mediaTrack: MediaStreamTrack,
82
- sid: Track.SID,
83
- mediaStream: MediaStream,
84
- receiver?: RTCRtpReceiver,
85
- adaptiveStreamSettings?: AdaptiveStreamSettings,
86
- triesLeft?: number,
87
- ) {
88
- // find the track publication
89
- // it's possible for the media track to arrive before participant info
90
- let publication = this.getTrackPublication(sid);
91
-
92
- // it's also possible that the browser didn't honor our original track id
93
- // FireFox would use its own local uuid instead of server track id
94
- if (!publication) {
95
- if (!sid.startsWith('TR')) {
96
- // find the first track that matches type
97
- this.tracks.forEach((p) => {
98
- if (!publication && mediaTrack.kind === p.kind.toString()) {
99
- publication = p;
100
- }
101
- });
102
- }
103
- }
104
-
105
- // when we couldn't locate the track, it's possible that the metadata hasn't
106
- // yet arrived. Wait a bit longer for it to arrive, or fire an error
107
- if (!publication) {
108
- if (triesLeft === 0) {
109
- log.error('could not find published track', this.sid, sid);
110
- this.emit(ParticipantEvent.TrackSubscriptionFailed, sid);
111
- return;
112
- }
113
-
114
- if (triesLeft === undefined) triesLeft = 20;
115
- setTimeout(() => {
116
- this.addSubscribedMediaTrack(mediaTrack, sid, mediaStream,
117
- receiver, adaptiveStreamSettings, triesLeft! - 1);
118
- }, 150);
119
- return;
120
- }
121
-
122
- const isVideo = mediaTrack.kind === 'video';
123
- let track: RemoteTrack;
124
- if (isVideo) {
125
- track = new RemoteVideoTrack(mediaTrack, sid, receiver, adaptiveStreamSettings);
126
- } else {
127
- track = new RemoteAudioTrack(mediaTrack, sid, receiver);
128
- }
129
-
130
- // set track info
131
- track.source = publication.source;
132
- // keep publication's muted status
133
- track.isMuted = publication.isMuted;
134
- track.setMediaStream(mediaStream);
135
- track.start();
136
-
137
- publication.setTrack(track);
138
-
139
- this.emit(ParticipantEvent.TrackSubscribed, track, publication);
140
-
141
- return publication;
142
- }
143
-
144
- /** @internal */
145
- get hasMetadata(): boolean {
146
- return !!this.participantInfo;
147
- }
148
-
149
- getTrackPublication(sid: Track.SID): RemoteTrackPublication | undefined {
150
- return this.tracks.get(sid);
151
- }
152
-
153
- /** @internal */
154
- updateInfo(info: ParticipantInfo) {
155
- const alreadyHasMetadata = this.hasMetadata;
156
-
157
- super.updateInfo(info);
158
-
159
- // we are getting a list of all available tracks, reconcile in here
160
- // and send out events for changes
161
-
162
- // reconcile track publications, publish events only if metadata is already there
163
- // i.e. changes since the local participant has joined
164
- const validTracks = new Map<string, RemoteTrackPublication>();
165
- const newTracks = new Map<string, RemoteTrackPublication>();
166
-
167
- info.tracks.forEach((ti) => {
168
- let publication = this.getTrackPublication(ti.sid);
169
- if (!publication) {
170
- // new publication
171
- const kind = Track.kindFromProto(ti.type);
172
- if (!kind) {
173
- return;
174
- }
175
- publication = new RemoteTrackPublication(kind, ti.sid, ti.name);
176
- publication.updateInfo(ti);
177
- newTracks.set(ti.sid, publication);
178
- this.addTrackPublication(publication);
179
- } else {
180
- publication.updateInfo(ti);
181
- }
182
- validTracks.set(ti.sid, publication);
183
- });
184
-
185
- // send new tracks
186
- if (alreadyHasMetadata) {
187
- newTracks.forEach((publication) => {
188
- this.emit(ParticipantEvent.TrackPublished, publication);
189
- });
190
- }
191
-
192
- // detect removed tracks
193
- this.tracks.forEach((publication) => {
194
- if (!validTracks.has(publication.trackSid)) {
195
- this.unpublishTrack(publication.trackSid, true);
196
- }
197
- });
198
- }
199
-
200
- /** @internal */
201
- unpublishTrack(sid: Track.SID, sendUnpublish?: boolean) {
202
- const publication = <RemoteTrackPublication> this.tracks.get(sid);
203
- if (!publication) {
204
- return;
205
- }
206
-
207
- this.tracks.delete(sid);
208
-
209
- // remove from the right type map
210
- switch (publication.kind) {
211
- case Track.Kind.Audio:
212
- this.audioTracks.delete(sid);
213
- break;
214
- case Track.Kind.Video:
215
- this.videoTracks.delete(sid);
216
- break;
217
- default:
218
- break;
219
- }
220
-
221
- // also send unsubscribe, if track is actively subscribed
222
- const { track } = publication;
223
- if (track) {
224
- const { isSubscribed } = publication;
225
- track.stop();
226
- publication.setTrack(undefined);
227
- // always send unsubscribed, since apps may rely on this
228
- if (isSubscribed) {
229
- this.emit(ParticipantEvent.TrackUnsubscribed, track, publication);
230
- }
231
- }
232
- if (sendUnpublish) { this.emit(ParticipantEvent.TrackUnpublished, publication); }
233
- }
234
-
235
- /** @internal */
236
- emit<E extends keyof ParticipantEventCallbacks>(
237
- event: E,
238
- ...args: Parameters<ParticipantEventCallbacks[E]>
239
- ): boolean {
240
- log.trace('participant event', this.sid, event, ...args);
241
- return super.emit(event, ...args);
242
- }
243
- }
@@ -1,145 +0,0 @@
1
- import {
2
- ScreenSharePresets, VideoPreset, VideoPresets, VideoPresets43,
3
- } from '../track/options';
4
- import {
5
- computeDefaultScreenShareSimulcastPresets,
6
- computeVideoEncodings,
7
- determineAppropriateEncoding,
8
- presets169,
9
- presets43,
10
- presetsForResolution,
11
- presetsScreenShare,
12
- sortPresets,
13
- } from './publishUtils';
14
-
15
- describe('presetsForResolution', () => {
16
- it('handles screenshare', () => {
17
- expect(presetsForResolution(true, 600, 300)).toEqual(presetsScreenShare);
18
- });
19
-
20
- it('handles landscape', () => {
21
- expect(presetsForResolution(false, 600, 300)).toEqual(presets169);
22
- expect(presetsForResolution(false, 500, 500)).toEqual(presets43);
23
- });
24
-
25
- it('handles portrait', () => {
26
- expect(presetsForResolution(false, 300, 600)).toEqual(presets169);
27
- expect(presetsForResolution(false, 500, 500)).toEqual(presets43);
28
- });
29
- });
30
-
31
- describe('determineAppropriateEncoding', () => {
32
- it('uses higher encoding', () => {
33
- expect(determineAppropriateEncoding(false, 600, 300))
34
- .toEqual(VideoPresets.vga.encoding);
35
- });
36
-
37
- it('handles portrait', () => {
38
- expect(determineAppropriateEncoding(false, 300, 600))
39
- .toEqual(VideoPresets.vga.encoding);
40
- });
41
- });
42
-
43
- describe('computeVideoEncodings', () => {
44
- it('handles non-simulcast', () => {
45
- const encodings = computeVideoEncodings(false, 640, 480, {
46
- simulcast: false,
47
- });
48
- expect(encodings).toEqual([{}]);
49
- });
50
-
51
- it('respects client defined bitrate', () => {
52
- const encodings = computeVideoEncodings(false, 640, 480, {
53
- simulcast: false,
54
- videoEncoding: {
55
- maxBitrate: 1024,
56
- },
57
- });
58
- expect(encodings).toHaveLength(1);
59
- expect(encodings![0].maxBitrate).toBe(1024);
60
- });
61
-
62
- it('returns three encodings for high-res simulcast', () => {
63
- const encodings = computeVideoEncodings(false, 960, 540, {
64
- simulcast: true,
65
- });
66
- expect(encodings).toHaveLength(3);
67
-
68
- // ensure they are what we expect
69
- expect(encodings![0].rid).toBe('q');
70
- expect(encodings![0].maxBitrate).toBe(VideoPresets.h180.encoding.maxBitrate);
71
- expect(encodings![0].scaleResolutionDownBy).toBe(3);
72
- expect(encodings![1].rid).toBe('h');
73
- expect(encodings![1].scaleResolutionDownBy).toBe(1.5);
74
- expect(encodings![2].rid).toBe('f');
75
- });
76
-
77
- it('handles portrait simulcast', () => {
78
- const encodings = computeVideoEncodings(false, 540, 960, {
79
- simulcast: true,
80
- });
81
- expect(encodings).toHaveLength(3);
82
- expect(encodings![0].scaleResolutionDownBy).toBe(3);
83
- expect(encodings![1].scaleResolutionDownBy).toBe(1.5);
84
- expect(encodings![2].maxBitrate).toBe(VideoPresets.h540.encoding.maxBitrate);
85
- });
86
-
87
- it('returns two encodings for lower-res simulcast', () => {
88
- const encodings = computeVideoEncodings(false, 640, 360, {
89
- simulcast: true,
90
- });
91
- expect(encodings).toHaveLength(2);
92
-
93
- // ensure they are what we expect
94
- expect(encodings![0].rid).toBe('q');
95
- expect(encodings![0].maxBitrate).toBe(VideoPresets.h180.encoding.maxBitrate);
96
- expect(encodings![1].rid).toBe('h');
97
- expect(encodings![1].maxBitrate).toBe(VideoPresets.h360.encoding.maxBitrate);
98
- });
99
-
100
- it('respects provided min resolution', () => {
101
- const encodings = computeVideoEncodings(false, 100, 120, {
102
- simulcast: true,
103
- });
104
- expect(encodings).toHaveLength(1);
105
- expect(encodings![0].rid).toBe('q');
106
- expect(encodings![0].maxBitrate).toBe(VideoPresets43.h120.encoding.maxBitrate);
107
- expect(encodings![0].scaleResolutionDownBy).toBe(1);
108
- });
109
- });
110
-
111
- describe('customSimulcastLayers', () => {
112
- it('sorts presets from lowest to highest', () => {
113
- const sortedPresets = sortPresets(
114
- [VideoPresets.h1440, VideoPresets.h360, VideoPresets.h1080, VideoPresets.h90],
115
- ) as Array<VideoPreset>;
116
- expect(sortPresets).not.toBeUndefined();
117
- expect(sortedPresets[0]).toBe(VideoPresets.h90);
118
- expect(sortedPresets[1]).toBe(VideoPresets.h360);
119
- expect(sortedPresets[2]).toBe(VideoPresets.h1080);
120
- expect(sortedPresets[3]).toBe(VideoPresets.h1440);
121
- });
122
- it('sorts presets from lowest to highest, even when dimensions are the same', () => {
123
- const sortedPresets = sortPresets([
124
- new VideoPreset(1920, 1080, 3_000_000, 20),
125
- new VideoPreset(1920, 1080, 2_000_000, 15),
126
- new VideoPreset(1920, 1080, 3_000_000, 15),
127
- ]) as Array<VideoPreset>;
128
- expect(sortPresets).not.toBeUndefined();
129
- expect(sortedPresets[0].encoding.maxBitrate).toBe(2_000_000);
130
- expect(sortedPresets[1].encoding.maxFramerate).toBe(15);
131
- expect(sortedPresets[2].encoding.maxFramerate).toBe(20);
132
- });
133
- });
134
-
135
- describe('screenShareSimulcastDefaults', () => {
136
- it('computes appropriate bitrate from original preset', () => {
137
- const defaultSimulcastLayers = computeDefaultScreenShareSimulcastPresets(
138
- ScreenSharePresets.h720fps15,
139
- );
140
- expect(defaultSimulcastLayers[0].width).toBe(640);
141
- expect(defaultSimulcastLayers[0].height).toBe(360);
142
- expect(defaultSimulcastLayers[0].encoding.maxFramerate).toBe(3);
143
- expect(defaultSimulcastLayers[0].encoding.maxBitrate).toBe(150_000);
144
- });
145
- });