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,235 +0,0 @@
1
- import { debounce } from 'ts-debounce';
2
- import { TrackEvent } from '../events';
3
- import { computeBitrate, monitorFrequency, VideoReceiverStats } from '../stats';
4
- import {
5
- getIntersectionObserver, getResizeObserver, isMobile, ObservableMediaElement,
6
- } from '../utils';
7
- import RemoteTrack from './RemoteTrack';
8
- import { attachToElement, detachTrack, Track } from './Track';
9
- import { AdaptiveStreamSettings } from './types';
10
-
11
- const REACTION_DELAY = 100;
12
-
13
- export default class RemoteVideoTrack extends RemoteTrack {
14
- /** @internal */
15
- receiver?: RTCRtpReceiver;
16
-
17
- private prevStats?: VideoReceiverStats;
18
-
19
- private elementInfos: ElementInfo[] = [];
20
-
21
- private adaptiveStreamSettings?: AdaptiveStreamSettings;
22
-
23
- private lastVisible?: boolean;
24
-
25
- private lastDimensions?: Track.Dimensions;
26
-
27
- constructor(
28
- mediaTrack: MediaStreamTrack,
29
- sid: string,
30
- receiver?: RTCRtpReceiver,
31
- adaptiveStreamSettings?: AdaptiveStreamSettings,
32
- ) {
33
- super(mediaTrack, sid, Track.Kind.Video, receiver);
34
- this.adaptiveStreamSettings = adaptiveStreamSettings;
35
- }
36
-
37
- get isAdaptiveStream(): boolean {
38
- return this.adaptiveStreamSettings !== undefined;
39
- }
40
-
41
- /** @internal */
42
- setMuted(muted: boolean) {
43
- super.setMuted(muted);
44
-
45
- this.attachedElements.forEach((element) => {
46
- // detach or attach
47
- if (muted) {
48
- detachTrack(this.mediaStreamTrack, element);
49
- } else {
50
- attachToElement(this.mediaStreamTrack, element);
51
- }
52
- });
53
- }
54
-
55
- attach(): HTMLMediaElement;
56
- attach(element: HTMLMediaElement): HTMLMediaElement;
57
- attach(element?: HTMLMediaElement): HTMLMediaElement {
58
- if (!element) {
59
- element = super.attach();
60
- } else {
61
- super.attach(element);
62
- }
63
-
64
- // It's possible attach is called multiple times on an element. When that's
65
- // the case, we'd want to avoid adding duplicate elementInfos
66
- if (this.adaptiveStreamSettings
67
- && this.elementInfos.find((info) => info.element === element) === undefined
68
- ) {
69
- this.elementInfos.push({
70
- element,
71
- visible: true, // default visible
72
- });
73
-
74
- (element as ObservableMediaElement)
75
- .handleResize = this.debouncedHandleResize;
76
- (element as ObservableMediaElement)
77
- .handleVisibilityChanged = this.handleVisibilityChanged;
78
-
79
- getIntersectionObserver().observe(element);
80
- getResizeObserver().observe(element);
81
-
82
- // trigger the first resize update cycle
83
- // if the tab is backgrounded, the initial resize event does not fire until
84
- // the tab comes into focus for the first time.
85
- this.debouncedHandleResize();
86
- }
87
- return element;
88
- }
89
-
90
- detach(): HTMLMediaElement[];
91
- detach(element: HTMLMediaElement): HTMLMediaElement;
92
- detach(element?: HTMLMediaElement): HTMLMediaElement | HTMLMediaElement[] {
93
- let detachedElements: HTMLMediaElement[] = [];
94
- if (element) {
95
- this.stopObservingElement(element);
96
- return super.detach(element);
97
- }
98
- detachedElements = super.detach();
99
-
100
- for (const e of detachedElements) {
101
- this.stopObservingElement(e);
102
- }
103
-
104
- return detachedElements;
105
- }
106
-
107
- protected monitorReceiver = async () => {
108
- if (!this.receiver) {
109
- this._currentBitrate = 0;
110
- return;
111
- }
112
- const stats = await this.getReceiverStats();
113
-
114
- if (stats && this.prevStats && this.receiver) {
115
- this._currentBitrate = computeBitrate(stats, this.prevStats);
116
- }
117
-
118
- this.prevStats = stats;
119
- setTimeout(() => {
120
- this.monitorReceiver();
121
- }, monitorFrequency);
122
- };
123
-
124
- private async getReceiverStats(): Promise<VideoReceiverStats | undefined> {
125
- if (!this.receiver) {
126
- return;
127
- }
128
-
129
- const stats = await this.receiver.getStats();
130
- let receiverStats: VideoReceiverStats | undefined;
131
- stats.forEach((v) => {
132
- if (v.type === 'inbound-rtp') {
133
- receiverStats = {
134
- type: 'video',
135
- framesDecoded: v.framesDecoded,
136
- framesDropped: v.framesDropped,
137
- framesReceived: v.framesReceived,
138
- packetsReceived: v.packetsReceived,
139
- packetsLost: v.packetsLost,
140
- frameWidth: v.frameWidth,
141
- frameHeight: v.frameHeight,
142
- pliCount: v.pliCount,
143
- firCount: v.firCount,
144
- nackCount: v.nackCount,
145
- jitter: v.jitter,
146
- timestamp: v.timestamp,
147
- bytesReceived: v.bytesReceived,
148
- };
149
- }
150
- });
151
- return receiverStats;
152
- }
153
-
154
- private stopObservingElement(element: HTMLMediaElement) {
155
- getIntersectionObserver()?.unobserve(element);
156
- getResizeObserver()?.unobserve(element);
157
- this.elementInfos = this.elementInfos.filter((info) => info.element !== element);
158
- }
159
-
160
- private handleVisibilityChanged = (entry: IntersectionObserverEntry) => {
161
- const { target, isIntersecting } = entry;
162
- const elementInfo = this.elementInfos.find((info) => info.element === target);
163
- if (elementInfo) {
164
- elementInfo.visible = isIntersecting;
165
- elementInfo.visibilityChangedAt = Date.now();
166
- }
167
- this.updateVisibility();
168
- };
169
-
170
- protected async handleAppVisibilityChanged() {
171
- await super.handleAppVisibilityChanged();
172
- if (!this.isAdaptiveStream) return;
173
- // on desktop don't pause when tab is backgrounded
174
- if (!isMobile()) return;
175
- this.updateVisibility();
176
- }
177
-
178
- private readonly debouncedHandleResize = debounce(() => {
179
- this.updateDimensions();
180
- }, REACTION_DELAY);
181
-
182
- private updateVisibility() {
183
- const lastVisibilityChange = this.elementInfos.reduce(
184
- (prev, info) => Math.max(prev, info.visibilityChangedAt || 0),
185
- 0,
186
- );
187
- const isVisible = this.elementInfos.some((info) => info.visible) && !this.isInBackground;
188
-
189
- if (this.lastVisible === isVisible) {
190
- return;
191
- }
192
-
193
- if (!isVisible && Date.now() - lastVisibilityChange < REACTION_DELAY) {
194
- // delay hidden events
195
- setTimeout(() => {
196
- this.updateVisibility();
197
- }, REACTION_DELAY);
198
- return;
199
- }
200
-
201
- this.lastVisible = isVisible;
202
- this.emit(TrackEvent.VisibilityChanged, isVisible, this);
203
- }
204
-
205
- private updateDimensions() {
206
- let maxWidth = 0;
207
- let maxHeight = 0;
208
- for (const info of this.elementInfos) {
209
- const pixelDensity = this.adaptiveStreamSettings?.pixelDensity ?? 1;
210
- const pixelDensityValue = pixelDensity === 'screen' ? window.devicePixelRatio : pixelDensity;
211
- const currentElementWidth = info.element.clientWidth * pixelDensityValue;
212
- const currentElementHeight = info.element.clientHeight * pixelDensityValue;
213
- if (currentElementWidth + currentElementHeight > maxWidth + maxHeight) {
214
- maxWidth = currentElementWidth;
215
- maxHeight = currentElementHeight;
216
- }
217
- }
218
-
219
- if (this.lastDimensions?.width === maxWidth && this.lastDimensions?.height === maxHeight) {
220
- return;
221
- }
222
-
223
- this.lastDimensions = {
224
- width: maxWidth,
225
- height: maxHeight,
226
- };
227
- this.emit(TrackEvent.VideoDimensionsChanged, this.lastDimensions, this);
228
- }
229
- }
230
-
231
- interface ElementInfo {
232
- element: HTMLMediaElement;
233
- visible: boolean;
234
- visibilityChangedAt?: number;
235
- }
@@ -1,343 +0,0 @@
1
- import { EventEmitter } from 'events';
2
- import type TypedEventEmitter from 'typed-emitter';
3
- import { TrackSource, TrackType } from '../../proto/livekit_models';
4
- import { StreamState as ProtoStreamState } from '../../proto/livekit_rtc';
5
- import { TrackEvent } from '../events';
6
- import { isFireFox, isSafari, isWeb } from '../utils';
7
-
8
- // keep old audio elements when detached, we would re-use them since on iOS
9
- // Safari tracks which audio elements have been "blessed" by the user.
10
- const recycledElements: Array<HTMLAudioElement> = [];
11
-
12
- export class Track extends (EventEmitter as new () => TypedEventEmitter<TrackEventCallbacks>) {
13
- kind: Track.Kind;
14
-
15
- mediaStream?: MediaStream;
16
-
17
- mediaStreamTrack: MediaStreamTrack;
18
-
19
- attachedElements: HTMLMediaElement[] = [];
20
-
21
- isMuted: boolean = false;
22
-
23
- source: Track.Source;
24
-
25
- protected isInBackground: boolean;
26
-
27
- /**
28
- * sid is set after track is published to server, or if it's a remote track
29
- */
30
- sid?: Track.SID;
31
-
32
- protected _currentBitrate: number = 0;
33
-
34
- protected constructor(mediaTrack: MediaStreamTrack, kind: Track.Kind) {
35
- super();
36
- this.kind = kind;
37
- this.mediaStreamTrack = mediaTrack;
38
- this.source = Track.Source.Unknown;
39
- if (isWeb()) {
40
- this.isInBackground = document.visibilityState === 'hidden';
41
- document.addEventListener('visibilitychange', this.appVisibilityChangedListener);
42
- } else {
43
- this.isInBackground = false;
44
- }
45
- }
46
-
47
- /** current receive bits per second */
48
- get currentBitrate(): number {
49
- return this._currentBitrate;
50
- }
51
-
52
- /**
53
- * creates a new HTMLAudioElement or HTMLVideoElement, attaches to it, and returns it
54
- */
55
- attach(): HTMLMediaElement;
56
-
57
- /**
58
- * attaches track to an existing HTMLAudioElement or HTMLVideoElement
59
- */
60
- attach(element: HTMLMediaElement): HTMLMediaElement;
61
- attach(element?: HTMLMediaElement): HTMLMediaElement {
62
- let elementType = 'audio';
63
- if (this.kind === Track.Kind.Video) {
64
- elementType = 'video';
65
- }
66
- if (!element) {
67
- if (elementType === 'audio') {
68
- recycledElements.forEach((e) => {
69
- if (e.parentElement === null && !element) {
70
- element = e;
71
- }
72
- });
73
- if (element) {
74
- // remove it from pool
75
- recycledElements.splice(recycledElements.indexOf(element), 1);
76
- }
77
- }
78
- if (!element) {
79
- element = <HTMLMediaElement>document.createElement(elementType);
80
- }
81
- }
82
-
83
- if (!this.attachedElements.includes(element)) {
84
- this.attachedElements.push(element);
85
- }
86
-
87
- // even if we believe it's already attached to the element, it's possible
88
- // the element's srcObject was set to something else out of band.
89
- // we'll want to re-attach it in that case
90
- attachToElement(this.mediaStreamTrack, element);
91
-
92
- if (element instanceof HTMLAudioElement) {
93
- // manually play audio to detect audio playback status
94
- element.play()
95
- .then(() => {
96
- this.emit(TrackEvent.AudioPlaybackStarted);
97
- })
98
- .catch((e) => {
99
- this.emit(TrackEvent.AudioPlaybackFailed, e);
100
- });
101
- }
102
-
103
- return element;
104
- }
105
-
106
- /**
107
- * Detaches from all attached elements
108
- */
109
- detach(): HTMLMediaElement[];
110
-
111
- /**
112
- * Detach from a single element
113
- * @param element
114
- */
115
- detach(element: HTMLMediaElement): HTMLMediaElement;
116
- detach(element?: HTMLMediaElement): HTMLMediaElement | HTMLMediaElement[] {
117
- // detach from a single element
118
- if (element) {
119
- detachTrack(this.mediaStreamTrack, element);
120
- const idx = this.attachedElements.indexOf(element);
121
- if (idx >= 0) {
122
- this.attachedElements.splice(idx, 1);
123
- this.recycleElement(element);
124
- }
125
- return element;
126
- }
127
-
128
- const detached: HTMLMediaElement[] = [];
129
- this.attachedElements.forEach((elm) => {
130
- detachTrack(this.mediaStreamTrack, elm);
131
- detached.push(elm);
132
- this.recycleElement(elm);
133
- });
134
-
135
- // remove all tracks
136
- this.attachedElements = [];
137
- return detached;
138
- }
139
-
140
- stop() {
141
- this.mediaStreamTrack.stop();
142
- if (isWeb()) {
143
- document.removeEventListener('visibilitychange', this.appVisibilityChangedListener);
144
- }
145
- }
146
-
147
- protected enable() {
148
- this.mediaStreamTrack.enabled = true;
149
- }
150
-
151
- protected disable() {
152
- this.mediaStreamTrack.enabled = false;
153
- }
154
-
155
- private recycleElement(element: HTMLMediaElement) {
156
- if (element instanceof HTMLAudioElement) {
157
- // we only need to re-use a single element
158
- let shouldCache = true;
159
- element.pause();
160
- recycledElements.forEach((e) => {
161
- if (!e.parentElement) {
162
- shouldCache = false;
163
- }
164
- });
165
- if (shouldCache) {
166
- recycledElements.push(element);
167
- }
168
- }
169
- }
170
-
171
- appVisibilityChangedListener = () => {
172
- this.handleAppVisibilityChanged();
173
- };
174
-
175
- protected async handleAppVisibilityChanged() {
176
- this.isInBackground = document.visibilityState === 'hidden';
177
- }
178
- }
179
-
180
- /** @internal */
181
- export function attachToElement(track: MediaStreamTrack, element: HTMLMediaElement) {
182
- let mediaStream: MediaStream;
183
- if (element.srcObject instanceof MediaStream) {
184
- mediaStream = element.srcObject;
185
- } else {
186
- mediaStream = new MediaStream();
187
- }
188
-
189
- // check if track matches existing track
190
- let existingTracks: MediaStreamTrack[];
191
- if (track.kind === 'audio') {
192
- existingTracks = mediaStream.getAudioTracks();
193
- } else {
194
- existingTracks = mediaStream.getVideoTracks();
195
- }
196
- if (!existingTracks.includes(track)) {
197
- existingTracks.forEach((et) => {
198
- mediaStream.removeTrack(et);
199
- });
200
- mediaStream.addTrack(track);
201
- }
202
-
203
- // avoid flicker
204
- if (element.srcObject !== mediaStream) {
205
- element.srcObject = mediaStream;
206
- if ((isSafari() || isFireFox()) && element instanceof HTMLVideoElement) {
207
- // Firefox also has a timing issue where video doesn't actually get attached unless
208
- // performed out-of-band
209
- // Safari 15 has a bug where in certain layouts, video element renders
210
- // black until the page is resized or other changes take place.
211
- // Resetting the src triggers it to render.
212
- // https://developer.apple.com/forums/thread/690523
213
- setTimeout(() => {
214
- element.srcObject = mediaStream;
215
- }, 0);
216
- }
217
- }
218
- element.autoplay = true;
219
- if (element instanceof HTMLVideoElement) {
220
- element.playsInline = true;
221
- }
222
- }
223
-
224
- /** @internal */
225
- export function detachTrack(
226
- track: MediaStreamTrack,
227
- element: HTMLMediaElement,
228
- ) {
229
- if (element.srcObject instanceof MediaStream) {
230
- const mediaStream = element.srcObject;
231
- mediaStream.removeTrack(track);
232
- element.srcObject = null;
233
- }
234
- }
235
-
236
- export namespace Track {
237
- export enum Kind {
238
- Audio = 'audio',
239
- Video = 'video',
240
- Unknown = 'unknown',
241
- }
242
- export type SID = string;
243
- export enum Source {
244
- Camera = 'camera',
245
- Microphone = 'microphone',
246
- ScreenShare = 'screen_share',
247
- ScreenShareAudio = 'screen_share_audio',
248
- Unknown = 'unknown',
249
- }
250
-
251
- export enum StreamState {
252
- Active = 'active',
253
- Paused = 'paused',
254
- Unknown = 'unknown',
255
- }
256
-
257
- export interface Dimensions {
258
- width: number;
259
- height: number;
260
- }
261
-
262
- /** @internal */
263
- export function kindToProto(k: Kind): TrackType {
264
- switch (k) {
265
- case Kind.Audio:
266
- return TrackType.AUDIO;
267
- case Kind.Video:
268
- return TrackType.VIDEO;
269
- default:
270
- return TrackType.UNRECOGNIZED;
271
- }
272
- }
273
-
274
- /** @internal */
275
- export function kindFromProto(t: TrackType): Kind | undefined {
276
- switch (t) {
277
- case TrackType.AUDIO:
278
- return Kind.Audio;
279
- case TrackType.VIDEO:
280
- return Kind.Video;
281
- default:
282
- return Kind.Unknown;
283
- }
284
- }
285
-
286
- /** @internal */
287
- export function sourceToProto(s: Source): TrackSource {
288
- switch (s) {
289
- case Source.Camera:
290
- return TrackSource.CAMERA;
291
- case Source.Microphone:
292
- return TrackSource.MICROPHONE;
293
- case Source.ScreenShare:
294
- return TrackSource.SCREEN_SHARE;
295
- case Source.ScreenShareAudio:
296
- return TrackSource.SCREEN_SHARE_AUDIO;
297
- default:
298
- return TrackSource.UNRECOGNIZED;
299
- }
300
- }
301
-
302
- /** @internal */
303
- export function sourceFromProto(s: TrackSource): Source {
304
- switch (s) {
305
- case TrackSource.CAMERA:
306
- return Source.Camera;
307
- case TrackSource.MICROPHONE:
308
- return Source.Microphone;
309
- case TrackSource.SCREEN_SHARE:
310
- return Source.ScreenShare;
311
- case TrackSource.SCREEN_SHARE_AUDIO:
312
- return Source.ScreenShareAudio;
313
- default:
314
- return Source.Unknown;
315
- }
316
- }
317
-
318
- /** @internal */
319
- export function streamStateFromProto(s: ProtoStreamState): StreamState {
320
- switch (s) {
321
- case ProtoStreamState.ACTIVE:
322
- return StreamState.Active;
323
- case ProtoStreamState.PAUSED:
324
- return StreamState.Paused;
325
- default:
326
- return StreamState.Unknown;
327
- }
328
- }
329
- }
330
-
331
- export type TrackEventCallbacks = {
332
- message: () => void,
333
- muted: (track?: any) => void,
334
- unmuted: (track?: any) => void,
335
- ended: (track?: any) => void,
336
- updateSettings: () => void,
337
- updateSubscription: () => void,
338
- audioPlaybackStarted: () => void,
339
- audioPlaybackFailed: (error: Error) => void,
340
- audioSilenceDetected: () => void,
341
- visibilityChanged: (visible: boolean, track?: any) => void,
342
- videoDimensionsChanged: (dimensions: Track.Dimensions, track?: any) => void,
343
- };
@@ -1,120 +0,0 @@
1
- import { EventEmitter } from 'events';
2
- import { TrackInfo } from '../../proto/livekit_models';
3
- import { TrackEvent } from '../events';
4
- import LocalAudioTrack from './LocalAudioTrack';
5
- import LocalVideoTrack from './LocalVideoTrack';
6
- import RemoteAudioTrack from './RemoteAudioTrack';
7
- import RemoteVideoTrack from './RemoteVideoTrack';
8
- import { Track } from './Track';
9
-
10
- export class TrackPublication extends EventEmitter {
11
- kind: Track.Kind;
12
-
13
- trackName: string;
14
-
15
- trackSid: Track.SID;
16
-
17
- track?: Track;
18
-
19
- source: Track.Source;
20
-
21
- /** MimeType of the published track */
22
- mimeType?: string;
23
-
24
- /** dimension of the original published stream, video-only */
25
- dimensions?: Track.Dimensions;
26
-
27
- /** true if track was simulcasted to server, video-only */
28
- simulcasted?: boolean;
29
-
30
- /** @internal */
31
- trackInfo?: TrackInfo;
32
-
33
- protected metadataMuted: boolean = false;
34
-
35
- constructor(kind: Track.Kind, id: string, name: string) {
36
- super();
37
- this.kind = kind;
38
- this.trackSid = id;
39
- this.trackName = name;
40
- this.source = Track.Source.Unknown;
41
- }
42
-
43
- /** @internal */
44
- setTrack(track?: Track) {
45
- if (this.track) {
46
- this.track.off(TrackEvent.Muted, this.handleMuted);
47
- this.track.off(TrackEvent.Unmuted, this.handleUnmuted);
48
- }
49
-
50
- this.track = track;
51
-
52
- if (track) {
53
- // forward events
54
- track.on(TrackEvent.Muted, this.handleMuted);
55
- track.on(TrackEvent.Unmuted, this.handleUnmuted);
56
- }
57
- }
58
-
59
- get isMuted(): boolean {
60
- return this.metadataMuted;
61
- }
62
-
63
- get isEnabled(): boolean {
64
- return true;
65
- }
66
-
67
- get isSubscribed(): boolean {
68
- return this.track !== undefined;
69
- }
70
-
71
- /**
72
- * an [AudioTrack] if this publication holds an audio track
73
- */
74
- get audioTrack(): LocalAudioTrack | RemoteAudioTrack | undefined {
75
- if (this.track instanceof LocalAudioTrack || this.track instanceof RemoteAudioTrack) {
76
- return this.track;
77
- }
78
- }
79
-
80
- /**
81
- * an [VideoTrack] if this publication holds a video track
82
- */
83
- get videoTrack(): LocalVideoTrack | RemoteVideoTrack | undefined {
84
- if (this.track instanceof LocalVideoTrack || this.track instanceof RemoteVideoTrack) {
85
- return this.track;
86
- }
87
- }
88
-
89
- handleMuted = () => {
90
- this.emit(TrackEvent.Muted);
91
- };
92
-
93
- handleUnmuted = () => {
94
- this.emit(TrackEvent.Unmuted);
95
- };
96
-
97
- /** @internal */
98
- updateInfo(info: TrackInfo) {
99
- this.trackSid = info.sid;
100
- this.trackName = info.name;
101
- this.source = Track.sourceFromProto(info.source);
102
- this.mimeType = info.mimeType;
103
- if (this.kind === Track.Kind.Video && info.width > 0) {
104
- this.dimensions = {
105
- width: info.width,
106
- height: info.height,
107
- };
108
- this.simulcasted = info.simulcast;
109
- }
110
- this.trackInfo = info;
111
- }
112
- }
113
-
114
- export namespace TrackPublication {
115
- export enum SubscriptionStatus {
116
- Subscribed = 'subscribed',
117
- NotAllowed = 'not_allowed',
118
- Unsubscribed = 'unsubscribed',
119
- }
120
- }