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,70 +0,0 @@
1
- import { VideoQuality } from '../../proto/livekit_models';
2
- import { videoLayersFromEncodings } from './LocalVideoTrack';
3
-
4
- describe('videoLayersFromEncodings', () => {
5
- it('returns single layer for no encoding', () => {
6
- const layers = videoLayersFromEncodings(640, 360);
7
- expect(layers).toHaveLength(1);
8
- expect(layers[0].quality).toBe(VideoQuality.HIGH);
9
- expect(layers[0].width).toBe(640);
10
- expect(layers[0].height).toBe(360);
11
- });
12
-
13
- it('returns single layer for explicit encoding', () => {
14
- const layers = videoLayersFromEncodings(640, 360, [{
15
- maxBitrate: 200_000,
16
- }]);
17
- expect(layers).toHaveLength(1);
18
- expect(layers[0].quality).toBe(VideoQuality.HIGH);
19
- expect(layers[0].bitrate).toBe(200_000);
20
- });
21
-
22
- it('returns three layers for simulcast', () => {
23
- const layers = videoLayersFromEncodings(1280, 720, [
24
- {
25
- scaleResolutionDownBy: 4,
26
- rid: 'q',
27
- maxBitrate: 125_000,
28
- },
29
- {
30
- scaleResolutionDownBy: 2,
31
- rid: 'h',
32
- maxBitrate: 500_000,
33
- },
34
- {
35
- rid: 'f',
36
- maxBitrate: 1_200_000,
37
- },
38
- ]);
39
-
40
- expect(layers).toHaveLength(3);
41
- expect(layers[0].quality).toBe(VideoQuality.LOW);
42
- expect(layers[0].width).toBe(320);
43
- expect(layers[2].quality).toBe(VideoQuality.HIGH);
44
- expect(layers[2].height).toBe(720);
45
- });
46
-
47
- it('handles portrait', () => {
48
- const layers = videoLayersFromEncodings(720, 1280, [
49
- {
50
- scaleResolutionDownBy: 4,
51
- rid: 'q',
52
- maxBitrate: 125_000,
53
- },
54
- {
55
- scaleResolutionDownBy: 2,
56
- rid: 'h',
57
- maxBitrate: 500_000,
58
- },
59
- {
60
- rid: 'f',
61
- maxBitrate: 1_200_000,
62
- },
63
- ]);
64
- expect(layers).toHaveLength(3);
65
- expect(layers[0].quality).toBe(VideoQuality.LOW);
66
- expect(layers[0].height).toBe(320);
67
- expect(layers[2].quality).toBe(VideoQuality.HIGH);
68
- expect(layers[2].width).toBe(720);
69
- });
70
- });
@@ -1,293 +0,0 @@
1
- import { SignalClient } from '../../api/SignalClient';
2
- import log from '../../logger';
3
- import { VideoLayer, VideoQuality } from '../../proto/livekit_models';
4
- import { SubscribedQuality } from '../../proto/livekit_rtc';
5
- import { computeBitrate, monitorFrequency, VideoSenderStats } from '../stats';
6
- import { isFireFox, isMobile } from '../utils';
7
- import LocalTrack from './LocalTrack';
8
- import { VideoCaptureOptions } from './options';
9
- import { Track } from './Track';
10
- import { constraintsForOptions } from './utils';
11
-
12
- export default class LocalVideoTrack extends LocalTrack {
13
- /* internal */
14
- signalClient?: SignalClient;
15
-
16
- private prevStats?: Map<string, VideoSenderStats>;
17
-
18
- private encodings?: RTCRtpEncodingParameters[];
19
-
20
- constructor(
21
- mediaTrack: MediaStreamTrack,
22
- constraints?: MediaTrackConstraints,
23
- ) {
24
- super(mediaTrack, Track.Kind.Video, constraints);
25
- }
26
-
27
- get isSimulcast(): boolean {
28
- if (this.sender && this.sender.getParameters().encodings.length > 1) {
29
- return true;
30
- }
31
- return false;
32
- }
33
-
34
- /* @internal */
35
- startMonitor(signalClient: SignalClient) {
36
- this.signalClient = signalClient;
37
- // save original encodings
38
- const params = this.sender?.getParameters();
39
- if (params) {
40
- this.encodings = params.encodings;
41
- }
42
-
43
- setTimeout(() => {
44
- this.monitorSender();
45
- }, monitorFrequency);
46
- }
47
-
48
- stop() {
49
- this.sender = undefined;
50
- this.mediaStreamTrack.getConstraints();
51
- super.stop();
52
- }
53
-
54
- async mute(): Promise<LocalVideoTrack> {
55
- if (this.source === Track.Source.Camera) {
56
- log.debug('stopping camera track');
57
- // also stop the track, so that camera indicator is turned off
58
- this.mediaStreamTrack.stop();
59
- }
60
- await super.mute();
61
- return this;
62
- }
63
-
64
- async unmute(): Promise<LocalVideoTrack> {
65
- if (this.source === Track.Source.Camera) {
66
- log.debug('reacquiring camera track');
67
- await this.restartTrack();
68
- }
69
- await super.unmute();
70
- return this;
71
- }
72
-
73
- async getSenderStats(): Promise<VideoSenderStats[]> {
74
- if (!this.sender) {
75
- return [];
76
- }
77
-
78
- const items: VideoSenderStats[] = [];
79
-
80
- const stats = await this.sender.getStats();
81
- stats.forEach((v) => {
82
- if (v.type === 'outbound-rtp') {
83
- const vs: VideoSenderStats = {
84
- type: 'video',
85
- streamId: v.id,
86
- frameHeight: v.frameHeight,
87
- frameWidth: v.frameWidth,
88
- firCount: v.firCount,
89
- pliCount: v.pliCount,
90
- nackCount: v.nackCount,
91
- packetsSent: v.packetsSent,
92
- bytesSent: v.bytesSent,
93
- framesSent: v.framesSent,
94
- timestamp: v.timestamp,
95
- rid: v.rid ?? '',
96
- retransmittedPacketsSent: v.retransmittedPacketsSent,
97
- qualityLimitationReason: v.qualityLimitationReason,
98
- qualityLimitationResolutionChanges:
99
- v.qualityLimitationResolutionChanges,
100
- };
101
-
102
- // locate the appropriate remote-inbound-rtp item
103
- const r = stats.get(v.remoteId);
104
- if (r) {
105
- vs.jitter = r.jitter;
106
- vs.packetsLost = r.packetsLost;
107
- vs.roundTripTime = r.roundTripTime;
108
- }
109
-
110
- items.push(vs);
111
- }
112
- });
113
-
114
- return items;
115
- }
116
-
117
- setPublishingQuality(maxQuality: VideoQuality) {
118
- const qualities: SubscribedQuality[] = [];
119
- for (let q = VideoQuality.LOW; q <= VideoQuality.HIGH; q += 1) {
120
- qualities.push({
121
- quality: q,
122
- enabled: q <= maxQuality,
123
- });
124
- }
125
- log.debug('setting publishing quality. max quality', maxQuality);
126
- this.setPublishingLayers(qualities);
127
- }
128
-
129
- async setDeviceId(deviceId: string) {
130
- if (this.constraints.deviceId === deviceId) {
131
- return;
132
- }
133
- this.constraints.deviceId = deviceId;
134
- // when video is muted, underlying media stream track is stopped and
135
- // will be restarted later
136
- if (!this.isMuted) {
137
- await this.restartTrack();
138
- }
139
- }
140
-
141
- async restartTrack(options?: VideoCaptureOptions) {
142
- let constraints: MediaTrackConstraints | undefined;
143
- if (options) {
144
- const streamConstraints = constraintsForOptions({ video: options });
145
- if (typeof streamConstraints.video !== 'boolean') {
146
- constraints = streamConstraints.video;
147
- }
148
- }
149
- await this.restart(constraints);
150
- }
151
-
152
- /**
153
- * @internal
154
- * Sets layers that should be publishing
155
- */
156
- async setPublishingLayers(qualities: SubscribedQuality[]) {
157
- log.debug('setting publishing layers', qualities);
158
- if (!this.sender || !this.encodings) {
159
- return;
160
- }
161
- const params = this.sender.getParameters();
162
- const { encodings } = params;
163
- if (!encodings) {
164
- return;
165
- }
166
-
167
- if (encodings.length !== this.encodings.length) {
168
- log.warn('cannot set publishing layers, encodings mismatch');
169
- return;
170
- }
171
-
172
- let hasChanged = false;
173
- encodings.forEach((encoding, idx) => {
174
- let rid = encoding.rid ?? '';
175
- if (rid === '') {
176
- rid = 'q';
177
- }
178
- const quality = videoQualityForRid(rid);
179
- const subscribedQuality = qualities.find((q) => q.quality === quality);
180
- if (!subscribedQuality) {
181
- return;
182
- }
183
- if (encoding.active !== subscribedQuality.enabled) {
184
- hasChanged = true;
185
- encoding.active = subscribedQuality.enabled;
186
- log.debug(`setting layer ${subscribedQuality.quality} to ${encoding.active ? 'enabled' : 'disabled'}`);
187
-
188
- // FireFox does not support setting encoding.active to false, so we
189
- // have a workaround of lowering its bitrate and resolution to the min.
190
- if (isFireFox()) {
191
- if (subscribedQuality.enabled) {
192
- encoding.scaleResolutionDownBy = this.encodings![idx].scaleResolutionDownBy;
193
- encoding.maxBitrate = this.encodings![idx].maxBitrate;
194
- /* @ts-ignore */
195
- encoding.maxFrameRate = this.encodings![idx].maxFrameRate;
196
- } else {
197
- encoding.scaleResolutionDownBy = 4;
198
- encoding.maxBitrate = 10;
199
- /* @ts-ignore */
200
- encoding.maxFrameRate = 2;
201
- }
202
- }
203
- }
204
- });
205
-
206
- if (hasChanged) {
207
- params.encodings = encodings;
208
- await this.sender.setParameters(params);
209
- }
210
- }
211
-
212
- private monitorSender = async () => {
213
- if (!this.sender) {
214
- this._currentBitrate = 0;
215
- return;
216
- }
217
-
218
- let stats: VideoSenderStats[] | undefined;
219
- try {
220
- stats = await this.getSenderStats();
221
- } catch (e) {
222
- log.error('could not get audio sender stats', e);
223
- return;
224
- }
225
- const statsMap = new Map<string, VideoSenderStats>(stats.map((s) => [s.rid, s]));
226
-
227
- if (this.prevStats) {
228
- let totalBitrate = 0;
229
- statsMap.forEach((s, key) => {
230
- const prev = this.prevStats?.get(key);
231
- totalBitrate += computeBitrate(s, prev);
232
- });
233
- this._currentBitrate = totalBitrate;
234
- }
235
-
236
- this.prevStats = statsMap;
237
- setTimeout(() => {
238
- this.monitorSender();
239
- }, monitorFrequency);
240
- };
241
-
242
- protected async handleAppVisibilityChanged() {
243
- await super.handleAppVisibilityChanged();
244
- if (!isMobile()) return;
245
- if (this.isInBackground && this.source === Track.Source.Camera) {
246
- this.mediaStreamTrack.enabled = false;
247
- }
248
- }
249
- }
250
-
251
- export function videoQualityForRid(rid: string): VideoQuality {
252
- switch (rid) {
253
- case 'f':
254
- return VideoQuality.HIGH;
255
- case 'h':
256
- return VideoQuality.MEDIUM;
257
- case 'q':
258
- return VideoQuality.LOW;
259
- default:
260
- return VideoQuality.UNRECOGNIZED;
261
- }
262
- }
263
-
264
- export function videoLayersFromEncodings(
265
- width: number,
266
- height: number,
267
- encodings?: RTCRtpEncodingParameters[],
268
- ): VideoLayer[] {
269
- // default to a single layer, HQ
270
- if (!encodings) {
271
- return [{
272
- quality: VideoQuality.HIGH,
273
- width,
274
- height,
275
- bitrate: 0,
276
- ssrc: 0,
277
- }];
278
- }
279
- return encodings.map((encoding) => {
280
- const scale = encoding.scaleResolutionDownBy ?? 1;
281
- let quality = videoQualityForRid(encoding.rid ?? '');
282
- if (quality === VideoQuality.UNRECOGNIZED && encodings.length === 1) {
283
- quality = VideoQuality.HIGH;
284
- }
285
- return {
286
- quality,
287
- width: width / scale,
288
- height: height / scale,
289
- bitrate: encoding.maxBitrate ?? 0,
290
- ssrc: 0,
291
- };
292
- });
293
- }
@@ -1,58 +0,0 @@
1
- import { AudioReceiverStats, computeBitrate, monitorFrequency } from '../stats';
2
- import RemoteTrack from './RemoteTrack';
3
- import { Track } from './Track';
4
-
5
- export default class RemoteAudioTrack extends RemoteTrack {
6
- private prevStats?: AudioReceiverStats;
7
-
8
- constructor(
9
- mediaTrack: MediaStreamTrack,
10
- sid: string,
11
- receiver?: RTCRtpReceiver,
12
- ) {
13
- super(mediaTrack, sid, Track.Kind.Audio, receiver);
14
- }
15
-
16
- protected monitorReceiver = async () => {
17
- if (!this.receiver) {
18
- this._currentBitrate = 0;
19
- return;
20
- }
21
- const stats = await this.getReceiverStats();
22
-
23
- if (stats && this.prevStats && this.receiver) {
24
- this._currentBitrate = computeBitrate(stats, this.prevStats);
25
- }
26
-
27
- this.prevStats = stats;
28
- setTimeout(() => {
29
- this.monitorReceiver();
30
- }, monitorFrequency);
31
- };
32
-
33
- protected async getReceiverStats(): Promise<AudioReceiverStats | undefined> {
34
- if (!this.receiver) {
35
- return;
36
- }
37
-
38
- const stats = await this.receiver.getStats();
39
- let receiverStats: AudioReceiverStats | undefined;
40
- stats.forEach((v) => {
41
- if (v.type === 'inbound-rtp') {
42
- receiverStats = {
43
- type: 'audio',
44
- timestamp: v.timestamp,
45
- jitter: v.jitter,
46
- bytesReceived: v.bytesReceived,
47
- concealedSamples: v.concealedSamples,
48
- concealmentEvents: v.concealmentEvents,
49
- silentConcealedSamples: v.silentConcealedSamples,
50
- silentConcealmentEvents: v.silentConcealmentEvents,
51
- totalAudioEnergy: v.totalAudioEnergy,
52
- totalSamplesDuration: v.totalSamplesDuration,
53
- };
54
- }
55
- });
56
- return receiverStats;
57
- }
58
- }
@@ -1,62 +0,0 @@
1
- import { TrackEvent } from '../events';
2
- import { monitorFrequency } from '../stats';
3
- import { Track } from './Track';
4
-
5
- export default abstract class RemoteTrack extends Track {
6
- /** @internal */
7
- receiver?: RTCRtpReceiver;
8
-
9
- streamState: Track.StreamState = Track.StreamState.Active;
10
-
11
- constructor(
12
- mediaTrack: MediaStreamTrack,
13
- sid: string,
14
- kind: Track.Kind,
15
- receiver?: RTCRtpReceiver,
16
- ) {
17
- super(mediaTrack, kind);
18
- this.sid = sid;
19
- this.receiver = receiver;
20
- }
21
-
22
- /** @internal */
23
- setMuted(muted: boolean) {
24
- if (this.isMuted !== muted) {
25
- this.isMuted = muted;
26
- this.emit(muted ? TrackEvent.Muted : TrackEvent.Unmuted, this);
27
- }
28
- }
29
-
30
- /** @internal */
31
- setMediaStream(stream: MediaStream) {
32
- // this is needed to determine when the track is finished
33
- // we send each track down in its own MediaStream, so we can assume the
34
- // current track is the only one that can be removed.
35
- this.mediaStream = stream;
36
- stream.onremovetrack = () => {
37
- this.receiver = undefined;
38
- this._currentBitrate = 0;
39
- this.emit(TrackEvent.Ended, this);
40
- };
41
- }
42
-
43
- start() {
44
- this.startMonitor();
45
- // use `enabled` of track to enable re-use of transceiver
46
- super.enable();
47
- }
48
-
49
- stop() {
50
- // use `enabled` of track to enable re-use of transceiver
51
- super.disable();
52
- }
53
-
54
- /* @internal */
55
- startMonitor() {
56
- setTimeout(() => {
57
- this.monitorReceiver();
58
- }, monitorFrequency);
59
- }
60
-
61
- protected abstract monitorReceiver(): void;
62
- }
@@ -1,198 +0,0 @@
1
- import log from '../../logger';
2
- import { TrackInfo, VideoQuality } from '../../proto/livekit_models';
3
- import {
4
- UpdateSubscription,
5
- UpdateTrackSettings,
6
- } from '../../proto/livekit_rtc';
7
- import { TrackEvent } from '../events';
8
- import RemoteVideoTrack from './RemoteVideoTrack';
9
- import { Track } from './Track';
10
- import { TrackPublication } from './TrackPublication';
11
- import { RemoteTrack } from './types';
12
-
13
- export default class RemoteTrackPublication extends TrackPublication {
14
- track?: RemoteTrack;
15
-
16
- /** @internal */
17
- _allowed = true;
18
-
19
- // keeps track of client's desire to subscribe to a track
20
- protected subscribed?: boolean;
21
-
22
- protected disabled: boolean = false;
23
-
24
- protected currentVideoQuality?: VideoQuality = VideoQuality.HIGH;
25
-
26
- protected videoDimensions?: Track.Dimensions;
27
-
28
- /**
29
- * Subscribe or unsubscribe to this remote track
30
- * @param subscribed true to subscribe to a track, false to unsubscribe
31
- */
32
- setSubscribed(subscribed: boolean) {
33
- this.subscribed = subscribed;
34
-
35
- const sub: UpdateSubscription = {
36
- trackSids: [this.trackSid],
37
- subscribe: this.subscribed,
38
- participantTracks: [{
39
- // sending an empty participant id since TrackPublication doesn't keep it
40
- // this is filled in by the participant that receives this message
41
- participantSid: '',
42
- trackSids: [this.trackSid],
43
- }],
44
- };
45
- this.emit(TrackEvent.UpdateSubscription, sub);
46
- }
47
-
48
- get subscriptionStatus(): TrackPublication.SubscriptionStatus {
49
- if (this.subscribed === false || !super.isSubscribed) {
50
- return TrackPublication.SubscriptionStatus.Unsubscribed;
51
- }
52
- if (!this._allowed) {
53
- return TrackPublication.SubscriptionStatus.NotAllowed;
54
- }
55
- return TrackPublication.SubscriptionStatus.Subscribed;
56
- }
57
-
58
- /**
59
- * Returns true if track is subscribed, and ready for playback
60
- */
61
- get isSubscribed(): boolean {
62
- if (this.subscribed === false) {
63
- return false;
64
- }
65
- if (!this._allowed) {
66
- return false;
67
- }
68
- return super.isSubscribed;
69
- }
70
-
71
- get isEnabled(): boolean {
72
- return !this.disabled;
73
- }
74
-
75
- /**
76
- * disable server from sending down data for this track. this is useful when
77
- * the participant is off screen, you may disable streaming down their video
78
- * to reduce bandwidth requirements
79
- * @param enabled
80
- */
81
- setEnabled(enabled: boolean) {
82
- if (!this.isManualOperationAllowed() || this.disabled === !enabled) {
83
- return;
84
- }
85
- this.disabled = !enabled;
86
-
87
- this.emitTrackUpdate();
88
- }
89
-
90
- /**
91
- * for tracks that support simulcasting, adjust subscribed quality
92
- *
93
- * This indicates the highest quality the client can accept. if network
94
- * bandwidth does not allow, server will automatically reduce quality to
95
- * optimize for uninterrupted video
96
- */
97
- setVideoQuality(quality: VideoQuality) {
98
- if (!this.isManualOperationAllowed() || this.currentVideoQuality === quality) {
99
- return;
100
- }
101
- this.currentVideoQuality = quality;
102
- this.videoDimensions = undefined;
103
-
104
- this.emitTrackUpdate();
105
- }
106
-
107
- setVideoDimensions(dimensions: Track.Dimensions) {
108
- if (!this.isManualOperationAllowed()) {
109
- return;
110
- }
111
- if (this.videoDimensions?.width === dimensions.width
112
- && this.videoDimensions?.height === dimensions.height) {
113
- return;
114
- }
115
- if (this.track instanceof RemoteVideoTrack) { this.videoDimensions = dimensions; }
116
- this.currentVideoQuality = undefined;
117
-
118
- this.emitTrackUpdate();
119
- }
120
-
121
- get videoQuality(): VideoQuality | undefined {
122
- return this.currentVideoQuality;
123
- }
124
-
125
- setTrack(track?: Track) {
126
- if (this.track) {
127
- // unregister listener
128
- this.track.off(TrackEvent.VideoDimensionsChanged, this.handleVideoDimensionsChange);
129
- this.track.off(TrackEvent.VisibilityChanged, this.handleVisibilityChange);
130
- this.track.off(TrackEvent.Ended, this.handleEnded);
131
- }
132
- super.setTrack(track);
133
- if (track) {
134
- track.sid = this.trackSid;
135
- track.on(TrackEvent.VideoDimensionsChanged, this.handleVideoDimensionsChange);
136
- track.on(TrackEvent.VisibilityChanged, this.handleVisibilityChange);
137
- track.on(TrackEvent.Ended, this.handleEnded);
138
- }
139
- }
140
-
141
- /** @internal */
142
- updateInfo(info: TrackInfo) {
143
- super.updateInfo(info);
144
- this.metadataMuted = info.muted;
145
- this.track?.setMuted(info.muted);
146
- }
147
-
148
- private isManualOperationAllowed(): boolean {
149
- if (this.isAdaptiveStream) {
150
- log.warn('adaptive stream is enabled, cannot change track settings', this.trackSid);
151
- return false;
152
- }
153
- if (!this.isSubscribed) {
154
- log.warn('cannot update track settings when not subscribed', this.trackSid);
155
- return false;
156
- }
157
- return true;
158
- }
159
-
160
- protected handleEnded = (track: RemoteTrack) => {
161
- this.emit(TrackEvent.Ended, track);
162
- };
163
-
164
- protected get isAdaptiveStream(): boolean {
165
- return this.track instanceof RemoteVideoTrack && this.track.isAdaptiveStream;
166
- }
167
-
168
- protected handleVisibilityChange = (visible: boolean) => {
169
- log.debug('adaptivestream video visibility', this.trackSid, `visible=${visible}`);
170
- this.disabled = !visible;
171
- this.emitTrackUpdate();
172
- };
173
-
174
- protected handleVideoDimensionsChange = (dimensions: Track.Dimensions) => {
175
- log.debug('adaptivestream video dimensions', this.trackSid, `${dimensions.width}x${dimensions.height}`);
176
- this.videoDimensions = dimensions;
177
- this.emitTrackUpdate();
178
- };
179
-
180
- /* @internal */
181
- emitTrackUpdate() {
182
- const settings: UpdateTrackSettings = UpdateTrackSettings.fromPartial({
183
- trackSids: [this.trackSid],
184
- disabled: this.disabled,
185
- });
186
- if (this.videoDimensions) {
187
- settings.width = this.videoDimensions.width;
188
- settings.height = this.videoDimensions.height;
189
- } else if (this.currentVideoQuality !== undefined) {
190
- settings.quality = this.currentVideoQuality;
191
- } else {
192
- // defaults to high quality
193
- settings.quality = VideoQuality.HIGH;
194
- }
195
-
196
- this.emit(TrackEvent.UpdateSettings, settings);
197
- }
198
- }