livekit-client 0.18.4-RC8 → 0.18.6

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 (127) hide show
  1. package/README.md +2 -5
  2. package/dist/api/RequestQueue.d.ts +13 -12
  3. package/dist/api/RequestQueue.d.ts.map +1 -0
  4. package/dist/api/SignalClient.d.ts +67 -66
  5. package/dist/api/SignalClient.d.ts.map +1 -0
  6. package/dist/connect.d.ts +24 -23
  7. package/dist/connect.d.ts.map +1 -0
  8. package/dist/index.d.ts +27 -26
  9. package/dist/index.d.ts.map +1 -0
  10. package/dist/livekit-client.esm.mjs +593 -507
  11. package/dist/livekit-client.esm.mjs.map +1 -1
  12. package/dist/livekit-client.umd.js +1 -1
  13. package/dist/livekit-client.umd.js.map +1 -1
  14. package/dist/logger.d.ts +26 -25
  15. package/dist/logger.d.ts.map +1 -0
  16. package/dist/options.d.ts +128 -127
  17. package/dist/options.d.ts.map +1 -0
  18. package/dist/proto/google/protobuf/timestamp.d.ts +133 -132
  19. package/dist/proto/google/protobuf/timestamp.d.ts.map +1 -0
  20. package/dist/proto/livekit_models.d.ts +876 -875
  21. package/dist/proto/livekit_models.d.ts.map +1 -0
  22. package/dist/proto/livekit_rtc.d.ts +3904 -3903
  23. package/dist/proto/livekit_rtc.d.ts.map +1 -0
  24. package/dist/room/DeviceManager.d.ts +8 -7
  25. package/dist/room/DeviceManager.d.ts.map +1 -0
  26. package/dist/room/PCTransport.d.ts +16 -15
  27. package/dist/room/PCTransport.d.ts.map +1 -0
  28. package/dist/room/RTCEngine.d.ts +67 -66
  29. package/dist/room/RTCEngine.d.ts.map +1 -0
  30. package/dist/room/Room.d.ts +166 -165
  31. package/dist/room/Room.d.ts.map +1 -0
  32. package/dist/room/errors.d.ts +29 -28
  33. package/dist/room/errors.d.ts.map +1 -0
  34. package/dist/room/events.d.ts +391 -390
  35. package/dist/room/events.d.ts.map +1 -0
  36. package/dist/room/participant/LocalParticipant.d.ts +126 -125
  37. package/dist/room/participant/LocalParticipant.d.ts.map +1 -0
  38. package/dist/room/participant/Participant.d.ts +94 -93
  39. package/dist/room/participant/Participant.d.ts.map +1 -0
  40. package/dist/room/participant/ParticipantTrackPermission.d.ts +26 -25
  41. package/dist/room/participant/ParticipantTrackPermission.d.ts.map +1 -0
  42. package/dist/room/participant/RemoteParticipant.d.ts +40 -39
  43. package/dist/room/participant/RemoteParticipant.d.ts.map +1 -0
  44. package/dist/room/participant/publishUtils.d.ts +18 -17
  45. package/dist/room/participant/publishUtils.d.ts.map +1 -0
  46. package/dist/room/stats.d.ts +66 -65
  47. package/dist/room/stats.d.ts.map +1 -0
  48. package/dist/room/track/LocalAudioTrack.d.ts +20 -19
  49. package/dist/room/track/LocalAudioTrack.d.ts.map +1 -0
  50. package/dist/room/track/LocalTrack.d.ts +28 -27
  51. package/dist/room/track/LocalTrack.d.ts.map +1 -0
  52. package/dist/room/track/LocalTrackPublication.d.ts +38 -37
  53. package/dist/room/track/LocalTrackPublication.d.ts.map +1 -0
  54. package/dist/room/track/LocalVideoTrack.d.ts +31 -30
  55. package/dist/room/track/LocalVideoTrack.d.ts.map +1 -0
  56. package/dist/room/track/RemoteAudioTrack.d.ts +20 -19
  57. package/dist/room/track/RemoteAudioTrack.d.ts.map +1 -0
  58. package/dist/room/track/RemoteTrack.d.ts +16 -15
  59. package/dist/room/track/RemoteTrack.d.ts.map +1 -0
  60. package/dist/room/track/RemoteTrackPublication.d.ts +51 -50
  61. package/dist/room/track/RemoteTrackPublication.d.ts.map +1 -0
  62. package/dist/room/track/RemoteVideoTrack.d.ts +30 -27
  63. package/dist/room/track/RemoteVideoTrack.d.ts.map +1 -0
  64. package/dist/room/track/Track.d.ts +105 -100
  65. package/dist/room/track/Track.d.ts.map +1 -0
  66. package/dist/room/track/TrackPublication.d.ts +50 -49
  67. package/dist/room/track/TrackPublication.d.ts.map +1 -0
  68. package/dist/room/track/create.d.ts +24 -23
  69. package/dist/room/track/create.d.ts.map +1 -0
  70. package/dist/room/track/defaults.d.ts +5 -4
  71. package/dist/room/track/defaults.d.ts.map +1 -0
  72. package/dist/room/track/options.d.ts +232 -222
  73. package/dist/room/track/options.d.ts.map +1 -0
  74. package/dist/room/track/types.d.ts +19 -18
  75. package/dist/room/track/types.d.ts.map +1 -0
  76. package/dist/room/track/utils.d.ts +14 -13
  77. package/dist/room/track/utils.d.ts.map +1 -0
  78. package/dist/room/utils.d.ts +17 -16
  79. package/dist/room/utils.d.ts.map +1 -0
  80. package/dist/test/mocks.d.ts +12 -11
  81. package/dist/test/mocks.d.ts.map +1 -0
  82. package/dist/version.d.ts +3 -2
  83. package/dist/version.d.ts.map +1 -0
  84. package/package.json +6 -6
  85. package/src/api/RequestQueue.ts +53 -0
  86. package/src/api/SignalClient.ts +497 -0
  87. package/src/connect.ts +98 -0
  88. package/src/index.ts +49 -0
  89. package/src/logger.ts +56 -0
  90. package/src/options.ts +156 -0
  91. package/src/proto/google/protobuf/timestamp.ts +216 -0
  92. package/src/proto/livekit_models.ts +2456 -0
  93. package/src/proto/livekit_rtc.ts +2859 -0
  94. package/src/room/DeviceManager.ts +80 -0
  95. package/src/room/PCTransport.ts +88 -0
  96. package/src/room/RTCEngine.ts +695 -0
  97. package/src/room/Room.ts +970 -0
  98. package/src/room/errors.ts +65 -0
  99. package/src/room/events.ts +438 -0
  100. package/src/room/participant/LocalParticipant.ts +779 -0
  101. package/src/room/participant/Participant.ts +287 -0
  102. package/src/room/participant/ParticipantTrackPermission.ts +42 -0
  103. package/src/room/participant/RemoteParticipant.ts +263 -0
  104. package/src/room/participant/publishUtils.test.ts +144 -0
  105. package/src/room/participant/publishUtils.ts +258 -0
  106. package/src/room/stats.ts +134 -0
  107. package/src/room/track/LocalAudioTrack.ts +134 -0
  108. package/src/room/track/LocalTrack.ts +229 -0
  109. package/src/room/track/LocalTrackPublication.ts +87 -0
  110. package/src/room/track/LocalVideoTrack.test.ts +72 -0
  111. package/src/room/track/LocalVideoTrack.ts +295 -0
  112. package/src/room/track/RemoteAudioTrack.ts +86 -0
  113. package/src/room/track/RemoteTrack.ts +62 -0
  114. package/src/room/track/RemoteTrackPublication.ts +207 -0
  115. package/src/room/track/RemoteVideoTrack.ts +253 -0
  116. package/src/room/track/Track.ts +365 -0
  117. package/src/room/track/TrackPublication.ts +120 -0
  118. package/src/room/track/create.ts +122 -0
  119. package/src/room/track/defaults.ts +26 -0
  120. package/src/room/track/options.ts +292 -0
  121. package/src/room/track/types.ts +20 -0
  122. package/src/room/track/utils.test.ts +110 -0
  123. package/src/room/track/utils.ts +113 -0
  124. package/src/room/utils.ts +115 -0
  125. package/src/test/mocks.ts +17 -0
  126. package/src/version.ts +2 -0
  127. package/CHANGELOG.md +0 -5
@@ -0,0 +1,292 @@
1
+ import { Track } from './Track';
2
+
3
+ export interface TrackPublishDefaults {
4
+ /**
5
+ * encoding parameters for camera track
6
+ */
7
+ videoEncoding?: VideoEncoding;
8
+
9
+ /**
10
+ * encoding parameters for screen share track
11
+ */
12
+ screenShareEncoding?: VideoEncoding;
13
+
14
+ /**
15
+ * codec, defaults to vp8
16
+ */
17
+ videoCodec?: VideoCodec;
18
+
19
+ /**
20
+ * max audio bitrate, defaults to [[AudioPresets.speech]]
21
+ */
22
+ audioBitrate?: number;
23
+
24
+ /**
25
+ * dtx (Discontinuous Transmission of audio), defaults to true
26
+ */
27
+ dtx?: boolean;
28
+
29
+ /**
30
+ * use simulcast, defaults to true.
31
+ * When using simulcast, LiveKit will publish up to three versions of the stream
32
+ * at various resolutions.
33
+ */
34
+ simulcast?: boolean;
35
+
36
+ /**
37
+ * scalability mode for svc codecs, defaults to 'L3T3'.
38
+ * for svc codecs, simulcast is disabled.
39
+ */
40
+ scalabilityMode?: ScalabilityMode;
41
+
42
+ /**
43
+ * custom video simulcast layers for camera tracks, defaults to h180, h360, h540
44
+ * You can specify up to two custom layers that will be used instead of
45
+ * the LiveKit default layers.
46
+ * Note: the layers need to be ordered from lowest to highest quality
47
+ */
48
+ videoSimulcastLayers?: Array<VideoPreset>;
49
+
50
+ /**
51
+ * custom video simulcast layers for screen tracks
52
+ * Note: the layers need to be ordered from lowest to highest quality
53
+ */
54
+ screenShareSimulcastLayers?: Array<VideoPreset>;
55
+
56
+ /**
57
+ * For local tracks, stop the underlying MediaStreamTrack when the track is muted (or paused)
58
+ * on some platforms, this option is necessary to disable the microphone recording indicator.
59
+ * Note: when this is enabled, and BT devices are connected, they will transition between
60
+ * profiles (e.g. HFP to A2DP) and there will be an audible difference in playback.
61
+ *
62
+ * defaults to false
63
+ */
64
+ stopMicTrackOnMute?: boolean;
65
+ }
66
+
67
+ /**
68
+ * Options when publishing tracks
69
+ */
70
+ export interface TrackPublishOptions extends TrackPublishDefaults {
71
+ /**
72
+ * set a track name
73
+ */
74
+ name?: string;
75
+
76
+ /**
77
+ * Source of track, camera, microphone, or screen
78
+ */
79
+ source?: Track.Source;
80
+ }
81
+
82
+ export interface CreateLocalTracksOptions {
83
+ /**
84
+ * audio track options, true to create with defaults. false if audio shouldn't be created
85
+ * default true
86
+ */
87
+ audio?: boolean | AudioCaptureOptions;
88
+
89
+ /**
90
+ * video track options, true to create with defaults. false if video shouldn't be created
91
+ * default true
92
+ */
93
+ video?: boolean | VideoCaptureOptions;
94
+ }
95
+
96
+ export interface VideoCaptureOptions {
97
+ /**
98
+ * A ConstrainDOMString object specifying a device ID or an array of device
99
+ * IDs which are acceptable and/or required.
100
+ */
101
+ deviceId?: ConstrainDOMString;
102
+
103
+ /**
104
+ * a facing or an array of facings which are acceptable and/or required.
105
+ */
106
+ facingMode?: 'user' | 'environment' | 'left' | 'right';
107
+
108
+ resolution?: VideoResolution;
109
+ }
110
+
111
+ export interface ScreenShareCaptureOptions {
112
+ /**
113
+ * true to capture audio shared. browser support for audio capturing in
114
+ * screenshare is limited: https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getDisplayMedia#browser_compatibility
115
+ */
116
+ audio?: boolean;
117
+
118
+ /** capture resolution, defaults to full HD */
119
+ resolution?: VideoResolution;
120
+ }
121
+
122
+ export interface AudioCaptureOptions {
123
+ /**
124
+ * specifies whether automatic gain control is preferred and/or required
125
+ */
126
+ autoGainControl?: ConstrainBoolean;
127
+
128
+ /**
129
+ * the channel count or range of channel counts which are acceptable and/or required
130
+ */
131
+ channelCount?: ConstrainULong;
132
+
133
+ /**
134
+ * A ConstrainDOMString object specifying a device ID or an array of device
135
+ * IDs which are acceptable and/or required.
136
+ */
137
+ deviceId?: ConstrainDOMString;
138
+
139
+ /**
140
+ * whether or not echo cancellation is preferred and/or required
141
+ */
142
+ echoCancellation?: ConstrainBoolean;
143
+
144
+ /**
145
+ * the latency or range of latencies which are acceptable and/or required.
146
+ */
147
+ latency?: ConstrainDouble;
148
+
149
+ /**
150
+ * whether noise suppression is preferred and/or required.
151
+ */
152
+ noiseSuppression?: ConstrainBoolean;
153
+
154
+ /**
155
+ * the sample rate or range of sample rates which are acceptable and/or required.
156
+ */
157
+ sampleRate?: ConstrainULong;
158
+
159
+ /**
160
+ * sample size or range of sample sizes which are acceptable and/or required.
161
+ */
162
+ sampleSize?: ConstrainULong;
163
+ }
164
+
165
+ export interface VideoResolution {
166
+ width: number;
167
+ height: number;
168
+ frameRate?: number;
169
+ aspectRatio?: number;
170
+ }
171
+
172
+ export interface VideoEncoding {
173
+ maxBitrate: number;
174
+ maxFramerate?: number;
175
+ }
176
+
177
+ export class VideoPreset {
178
+ encoding: VideoEncoding;
179
+
180
+ width: number;
181
+
182
+ height: number;
183
+
184
+ constructor(width: number, height: number, maxBitrate: number, maxFramerate?: number) {
185
+ this.width = width;
186
+ this.height = height;
187
+ this.encoding = {
188
+ maxBitrate,
189
+ maxFramerate,
190
+ };
191
+ }
192
+
193
+ get resolution(): VideoResolution {
194
+ return {
195
+ width: this.width,
196
+ height: this.height,
197
+ frameRate: this.encoding.maxFramerate,
198
+ aspectRatio: this.width / this.height,
199
+ };
200
+ }
201
+ }
202
+
203
+ export interface AudioPreset {
204
+ maxBitrate: number;
205
+ }
206
+
207
+ export type VideoCodec = 'vp8' | 'h264' | 'av1' | 'vp9';
208
+
209
+ /**
210
+ * scalability modes for svc, only supprot l3t3 now.
211
+ */
212
+ export type ScalabilityMode = 'L3T3';
213
+
214
+ export namespace AudioPresets {
215
+ export const telephone: AudioPreset = {
216
+ maxBitrate: 12_000,
217
+ };
218
+ export const speech: AudioPreset = {
219
+ maxBitrate: 20_000,
220
+ };
221
+ export const music: AudioPreset = {
222
+ maxBitrate: 32_000,
223
+ };
224
+ }
225
+
226
+ /**
227
+ * Sane presets for video resolution/encoding
228
+ */
229
+ export const VideoPresets = {
230
+ h90: new VideoPreset(160, 90, 60_000, 15),
231
+ h180: new VideoPreset(320, 180, 120_000, 15),
232
+ h216: new VideoPreset(384, 216, 180_000, 15),
233
+ h360: new VideoPreset(640, 360, 300_000, 20),
234
+ h540: new VideoPreset(960, 540, 600_000, 25),
235
+ h720: new VideoPreset(1280, 720, 1_700_000, 30),
236
+ h1080: new VideoPreset(1920, 1080, 3_000_000, 30),
237
+ h1440: new VideoPreset(2560, 1440, 5_000_000, 30),
238
+ h2160: new VideoPreset(3840, 2160, 8_000_000, 30),
239
+ /** @deprecated */
240
+ qvga: new VideoPreset(320, 180, 120_000, 10),
241
+ /** @deprecated */
242
+ vga: new VideoPreset(640, 360, 300_000, 20),
243
+ /** @deprecated */
244
+ qhd: new VideoPreset(960, 540, 600_000, 25),
245
+ /** @deprecated */
246
+ hd: new VideoPreset(1280, 720, 1_700_000, 30),
247
+ /** @deprecated */
248
+ fhd: new VideoPreset(1920, 1080, 3_000_000, 30),
249
+ } as const;
250
+
251
+ /**
252
+ * Four by three presets
253
+ */
254
+ export const VideoPresets43 = {
255
+ h120: new VideoPreset(160, 120, 80_000, 15),
256
+ h180: new VideoPreset(240, 180, 100_000, 15),
257
+ h240: new VideoPreset(320, 240, 150_000, 15),
258
+ h360: new VideoPreset(480, 360, 225_000, 20),
259
+ h480: new VideoPreset(640, 480, 300_000, 20),
260
+ h540: new VideoPreset(720, 540, 450_000, 25),
261
+ h720: new VideoPreset(960, 720, 1_500_000, 30),
262
+ h1080: new VideoPreset(1440, 1080, 2_500_000, 30),
263
+ h1440: new VideoPreset(1920, 1440, 3_500_000, 30),
264
+ /** @deprecated */
265
+ qvga: new VideoPreset(240, 180, 90_000, 10),
266
+ /** @deprecated */
267
+ vga: new VideoPreset(480, 360, 225_000, 20),
268
+ /** @deprecated */
269
+ qhd: new VideoPreset(720, 540, 450_000, 25),
270
+ /** @deprecated */
271
+ hd: new VideoPreset(960, 720, 1_500_000, 30),
272
+ /** @deprecated */
273
+ fhd: new VideoPreset(1440, 1080, 2_800_000, 30),
274
+ } as const;
275
+
276
+ export const ScreenSharePresets = {
277
+ h360fps3: new VideoPreset(640, 360, 200_000, 3),
278
+ h720fps5: new VideoPreset(1280, 720, 400_000, 5),
279
+ h720fps15: new VideoPreset(1280, 720, 1_000_000, 15),
280
+ h1080fps15: new VideoPreset(1920, 1080, 1_500_000, 15),
281
+ h1080fps30: new VideoPreset(1920, 1080, 3_000_000, 30),
282
+ /** @deprecated */
283
+ vga: new VideoPreset(640, 360, 200_000, 3),
284
+ /** @deprecated */
285
+ hd_8: new VideoPreset(1280, 720, 400_000, 5),
286
+ /** @deprecated */
287
+ hd_15: new VideoPreset(1280, 720, 1_000_000, 15),
288
+ /** @deprecated */
289
+ fhd_15: new VideoPreset(1920, 1080, 1_500_000, 15),
290
+ /** @deprecated */
291
+ fhd_30: new VideoPreset(1920, 1080, 3_000_000, 30),
292
+ } as const;
@@ -0,0 +1,20 @@
1
+ import type LocalAudioTrack from './LocalAudioTrack';
2
+ import type LocalVideoTrack from './LocalVideoTrack';
3
+ import type RemoteAudioTrack from './RemoteAudioTrack';
4
+ import type RemoteVideoTrack from './RemoteVideoTrack';
5
+
6
+ export type RemoteTrack = RemoteAudioTrack | RemoteVideoTrack;
7
+ export type AudioTrack = RemoteAudioTrack | LocalAudioTrack;
8
+ export type VideoTrack = RemoteVideoTrack | LocalVideoTrack;
9
+
10
+ export type AdaptiveStreamSettings = {
11
+ /**
12
+ * Set a custom pixel density, defaults to 1
13
+ * When streaming videos on a ultra high definition screen this setting
14
+ * let's you account for the devicePixelRatio of those screens.
15
+ * Set it to `screen` to use the actual pixel density of the screen
16
+ * Note: this might significantly increase the bandwidth consumed by people
17
+ * streaming on high definition screens.
18
+ */
19
+ pixelDensity?: number | 'screen';
20
+ };
@@ -0,0 +1,110 @@
1
+ import { AudioCaptureOptions, VideoCaptureOptions, VideoPresets } from './options';
2
+ import { constraintsForOptions, mergeDefaultOptions } from './utils';
3
+
4
+ describe('mergeDefaultOptions', () => {
5
+ const audioDefaults: AudioCaptureOptions = {
6
+ autoGainControl: true,
7
+ channelCount: 2,
8
+ };
9
+ const videoDefaults: VideoCaptureOptions = {
10
+ deviceId: 'video123',
11
+ resolution: VideoPresets.fhd.resolution,
12
+ };
13
+
14
+ it('does not enable undefined options', () => {
15
+ const opts = mergeDefaultOptions(undefined, audioDefaults, videoDefaults);
16
+ expect(opts.audio).toEqual(undefined);
17
+ expect(opts.video).toEqual(undefined);
18
+ });
19
+
20
+ it('does not enable explicitly disabled', () => {
21
+ const opts = mergeDefaultOptions({
22
+ video: false,
23
+ });
24
+ expect(opts.audio).toEqual(undefined);
25
+ expect(opts.video).toEqual(false);
26
+ });
27
+
28
+ it('accepts true for options', () => {
29
+ const opts = mergeDefaultOptions(
30
+ {
31
+ audio: true,
32
+ },
33
+ audioDefaults,
34
+ videoDefaults,
35
+ );
36
+ expect(opts.audio).toEqual(audioDefaults);
37
+ expect(opts.video).toEqual(undefined);
38
+ });
39
+
40
+ it('enables overriding specific fields', () => {
41
+ const opts = mergeDefaultOptions(
42
+ {
43
+ audio: { channelCount: 1 },
44
+ },
45
+ audioDefaults,
46
+ videoDefaults,
47
+ );
48
+ const audioOpts = opts.audio as AudioCaptureOptions;
49
+ expect(audioOpts.channelCount).toEqual(1);
50
+ expect(audioOpts.autoGainControl).toEqual(true);
51
+ });
52
+
53
+ it('does not override explicit false', () => {
54
+ const opts = mergeDefaultOptions(
55
+ {
56
+ audio: { autoGainControl: false },
57
+ },
58
+ audioDefaults,
59
+ videoDefaults,
60
+ );
61
+ const audioOpts = opts.audio as AudioCaptureOptions;
62
+ expect(audioOpts.autoGainControl).toEqual(false);
63
+ });
64
+ });
65
+
66
+ describe('constraintsForOptions', () => {
67
+ it('correctly enables audio bool', () => {
68
+ const constraints = constraintsForOptions({
69
+ audio: true,
70
+ });
71
+ expect(constraints.audio).toEqual(true);
72
+ expect(constraints.video).toEqual(false);
73
+ });
74
+
75
+ it('converts audio options correctly', () => {
76
+ const constraints = constraintsForOptions({
77
+ audio: {
78
+ noiseSuppression: true,
79
+ echoCancellation: false,
80
+ },
81
+ });
82
+ const audioOpts = constraints.audio as MediaTrackConstraints;
83
+ expect(Object.keys(audioOpts)).toEqual(['noiseSuppression', 'echoCancellation']);
84
+ expect(audioOpts.noiseSuppression).toEqual(true);
85
+ expect(audioOpts.echoCancellation).toEqual(false);
86
+ });
87
+
88
+ it('converts video options correctly', () => {
89
+ const constraints = constraintsForOptions({
90
+ video: {
91
+ resolution: VideoPresets.hd.resolution,
92
+ facingMode: 'user',
93
+ deviceId: 'video123',
94
+ },
95
+ });
96
+ const videoOpts = constraints.video as MediaTrackConstraints;
97
+ expect(Object.keys(videoOpts)).toEqual([
98
+ 'width',
99
+ 'height',
100
+ 'frameRate',
101
+ 'aspectRatio',
102
+ 'facingMode',
103
+ 'deviceId',
104
+ ]);
105
+ expect(videoOpts.width).toEqual(VideoPresets.hd.resolution.width);
106
+ expect(videoOpts.height).toEqual(VideoPresets.hd.resolution.height);
107
+ expect(videoOpts.frameRate).toEqual(VideoPresets.hd.resolution.frameRate);
108
+ expect(videoOpts.aspectRatio).toEqual(VideoPresets.hd.resolution.aspectRatio);
109
+ });
110
+ });
@@ -0,0 +1,113 @@
1
+ import { sleep } from '../utils';
2
+ import { AudioCaptureOptions, CreateLocalTracksOptions, VideoCaptureOptions } from './options';
3
+ import { AudioTrack } from './types';
4
+
5
+ export function mergeDefaultOptions(
6
+ options?: CreateLocalTracksOptions,
7
+ audioDefaults?: AudioCaptureOptions,
8
+ videoDefaults?: VideoCaptureOptions,
9
+ ): CreateLocalTracksOptions {
10
+ const opts: CreateLocalTracksOptions = {
11
+ ...options,
12
+ };
13
+ if (opts.audio === true) opts.audio = {};
14
+ if (opts.video === true) opts.video = {};
15
+
16
+ // use defaults
17
+ if (opts.audio) {
18
+ mergeObjectWithoutOverwriting(
19
+ opts.audio as Record<string, unknown>,
20
+ audioDefaults as Record<string, unknown>,
21
+ );
22
+ }
23
+ if (opts.video) {
24
+ mergeObjectWithoutOverwriting(
25
+ opts.video as Record<string, unknown>,
26
+ videoDefaults as Record<string, unknown>,
27
+ );
28
+ }
29
+ return opts;
30
+ }
31
+
32
+ function mergeObjectWithoutOverwriting(
33
+ mainObject: Record<string, unknown>,
34
+ objectToMerge: Record<string, unknown>,
35
+ ): Record<string, unknown> {
36
+ Object.keys(objectToMerge).forEach((key) => {
37
+ if (mainObject[key] === undefined) mainObject[key] = objectToMerge[key];
38
+ });
39
+ return mainObject;
40
+ }
41
+
42
+ export function constraintsForOptions(options: CreateLocalTracksOptions): MediaStreamConstraints {
43
+ const constraints: MediaStreamConstraints = {};
44
+
45
+ if (options.video) {
46
+ // default video options
47
+ if (typeof options.video === 'object') {
48
+ const videoOptions: MediaTrackConstraints = {};
49
+ const target = videoOptions as Record<string, unknown>;
50
+ const source = options.video as Record<string, unknown>;
51
+ Object.keys(source).forEach((key) => {
52
+ switch (key) {
53
+ case 'resolution':
54
+ // flatten VideoResolution fields
55
+ mergeObjectWithoutOverwriting(target, source.resolution as Record<string, unknown>);
56
+ break;
57
+ default:
58
+ target[key] = source[key];
59
+ }
60
+ });
61
+ constraints.video = videoOptions;
62
+ } else {
63
+ constraints.video = options.video;
64
+ }
65
+ } else {
66
+ constraints.video = false;
67
+ }
68
+
69
+ if (options.audio) {
70
+ if (typeof options.audio === 'object') {
71
+ constraints.audio = options.audio;
72
+ } else {
73
+ constraints.audio = true;
74
+ }
75
+ } else {
76
+ constraints.audio = false;
77
+ }
78
+ return constraints;
79
+ }
80
+ /**
81
+ * This function detects silence on a given [[Track]] instance.
82
+ * Returns true if the track seems to be entirely silent.
83
+ */
84
+ export async function detectSilence(track: AudioTrack, timeOffset = 200): Promise<boolean> {
85
+ const ctx = getNewAudioContext();
86
+ if (ctx) {
87
+ const analyser = ctx.createAnalyser();
88
+ analyser.fftSize = 2048;
89
+
90
+ const bufferLength = analyser.frequencyBinCount;
91
+ const dataArray = new Uint8Array(bufferLength);
92
+ const source = ctx.createMediaStreamSource(new MediaStream([track.mediaStreamTrack]));
93
+
94
+ source.connect(analyser);
95
+ await sleep(timeOffset);
96
+ analyser.getByteTimeDomainData(dataArray);
97
+ const someNoise = dataArray.some((sample) => sample !== 128 && sample !== 0);
98
+ ctx.close();
99
+ return !someNoise;
100
+ }
101
+ return false;
102
+ }
103
+
104
+ /**
105
+ * @internal
106
+ */
107
+ export function getNewAudioContext(): AudioContext | void {
108
+ // @ts-ignore
109
+ const AudioContext = window.AudioContext || window.webkitAudioContext;
110
+ if (AudioContext) {
111
+ return new AudioContext();
112
+ }
113
+ }
@@ -0,0 +1,115 @@
1
+ import { ClientInfo, ClientInfo_SDK } from '../proto/livekit_models';
2
+ import { protocolVersion, version } from '../version';
3
+
4
+ const separator = '|';
5
+
6
+ export function unpackStreamId(packed: string): string[] {
7
+ const parts = packed.split(separator);
8
+ if (parts.length > 1) {
9
+ return [parts[0], packed.substr(parts[0].length + 1)];
10
+ }
11
+ return [packed, ''];
12
+ }
13
+
14
+ export async function sleep(duration: number): Promise<void> {
15
+ return new Promise((resolve) => setTimeout(resolve, duration));
16
+ }
17
+
18
+ export function isFireFox(): boolean {
19
+ if (!isWeb()) return false;
20
+ return navigator.userAgent.indexOf('Firefox') !== -1;
21
+ }
22
+
23
+ export function isSafari(): boolean {
24
+ if (!isWeb()) return false;
25
+ return /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
26
+ }
27
+
28
+ export function isMobile(): boolean {
29
+ if (!isWeb()) return false;
30
+ return /Tablet|iPad|Mobile|Android|BlackBerry/.test(navigator.userAgent);
31
+ }
32
+
33
+ export function isWeb(): boolean {
34
+ return typeof document !== 'undefined';
35
+ }
36
+
37
+ function roDispatchCallback(entries: ResizeObserverEntry[]) {
38
+ for (const entry of entries) {
39
+ (entry.target as ObservableMediaElement).handleResize(entry);
40
+ }
41
+ }
42
+
43
+ function ioDispatchCallback(entries: IntersectionObserverEntry[]) {
44
+ for (const entry of entries) {
45
+ (entry.target as ObservableMediaElement).handleVisibilityChanged(entry);
46
+ }
47
+ }
48
+
49
+ let resizeObserver: ResizeObserver | null = null;
50
+ export const getResizeObserver = () => {
51
+ if (!resizeObserver) resizeObserver = new ResizeObserver(roDispatchCallback);
52
+ return resizeObserver;
53
+ };
54
+
55
+ let intersectionObserver: IntersectionObserver | null = null;
56
+ export const getIntersectionObserver = () => {
57
+ if (!intersectionObserver)
58
+ intersectionObserver = new IntersectionObserver(ioDispatchCallback, {
59
+ root: document,
60
+ rootMargin: '0px',
61
+ });
62
+ return intersectionObserver;
63
+ };
64
+
65
+ export interface ObservableMediaElement extends HTMLMediaElement {
66
+ handleResize: (entry: ResizeObserverEntry) => void;
67
+ handleVisibilityChanged: (entry: IntersectionObserverEntry) => void;
68
+ }
69
+
70
+ export function getClientInfo(): ClientInfo {
71
+ const info = ClientInfo.fromPartial({
72
+ sdk: ClientInfo_SDK.JS,
73
+ protocol: protocolVersion,
74
+ version,
75
+ });
76
+ return info;
77
+ }
78
+
79
+ let emptyVideoStreamTrack: MediaStreamTrack | undefined;
80
+
81
+ export function getEmptyVideoStreamTrack() {
82
+ if (!emptyVideoStreamTrack) {
83
+ const canvas = document.createElement('canvas');
84
+ canvas.width = 2;
85
+ canvas.height = 2;
86
+ canvas.getContext('2d')?.fillRect(0, 0, canvas.width, canvas.height);
87
+ // @ts-ignore
88
+ const emptyStream = canvas.captureStream();
89
+ [emptyVideoStreamTrack] = emptyStream.getTracks();
90
+ if (!emptyVideoStreamTrack) {
91
+ throw Error('Could not get empty media stream video track');
92
+ }
93
+ emptyVideoStreamTrack.enabled = false;
94
+ }
95
+ return emptyVideoStreamTrack;
96
+ }
97
+
98
+ let emptyAudioStreamTrack: MediaStreamTrack | undefined;
99
+
100
+ export function getEmptyAudioStreamTrack() {
101
+ if (!emptyAudioStreamTrack) {
102
+ // implementation adapted from https://blog.mozilla.org/webrtc/warm-up-with-replacetrack/
103
+ const ctx = new AudioContext();
104
+ const oscillator = ctx.createOscillator();
105
+ const dst = ctx.createMediaStreamDestination();
106
+ oscillator.connect(dst);
107
+ oscillator.start();
108
+ [emptyAudioStreamTrack] = dst.stream.getAudioTracks();
109
+ if (!emptyAudioStreamTrack) {
110
+ throw Error('Could not get empty media stream audio track');
111
+ }
112
+ emptyAudioStreamTrack.enabled = false;
113
+ }
114
+ return emptyAudioStreamTrack;
115
+ }
@@ -0,0 +1,17 @@
1
+ import { SignalClient } from '../api/SignalClient';
2
+ import RTCEngine from '../room/RTCEngine';
3
+
4
+ jest.mock('../api/SignalClient');
5
+ jest.mock('../room/RTCEngine');
6
+
7
+ // mock helpers for testing
8
+
9
+ const mocks = {
10
+ SignalClient: SignalClient as jest.MockedClass<typeof SignalClient>,
11
+ RTCEngine: RTCEngine as jest.MockedClass<typeof RTCEngine>,
12
+ MockLocalVideoTrack: {
13
+ stop: jest.fn(),
14
+ },
15
+ };
16
+
17
+ export default mocks;
package/src/version.ts ADDED
@@ -0,0 +1,2 @@
1
+ export const version = '0.18.6';
2
+ export const protocolVersion = 7;
package/CHANGELOG.md DELETED
@@ -1,5 +0,0 @@
1
- # Change Log
2
-
3
- ## 0.12.0
4
-
5
- - Updated API to create screen share tracks.