@microsoft/live-share-media 1.2.2 → 1.3.1

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 (54) hide show
  1. package/.nycrc.json +10 -10
  2. package/LICENSE +90 -90
  3. package/README.md +135 -135
  4. package/bin/LiveMediaSession.d.ts.map +1 -1
  5. package/bin/LiveMediaSession.js +4 -6
  6. package/bin/LiveMediaSession.js.map +1 -1
  7. package/bin/LiveMediaSessionCoordinator.d.ts +4 -3
  8. package/bin/LiveMediaSessionCoordinator.d.ts.map +1 -1
  9. package/bin/LiveMediaSessionCoordinator.js +56 -69
  10. package/bin/LiveMediaSessionCoordinator.js.map +1 -1
  11. package/bin/MediaPlayerSynchronizer.d.ts +12 -0
  12. package/bin/MediaPlayerSynchronizer.d.ts.map +1 -1
  13. package/bin/MediaPlayerSynchronizer.js +26 -6
  14. package/bin/MediaPlayerSynchronizer.js.map +1 -1
  15. package/bin/MediaSessionExtensions.d.ts +0 -4
  16. package/bin/MediaSessionExtensions.d.ts.map +1 -1
  17. package/bin/MediaSessionExtensions.js.map +1 -1
  18. package/bin/internals/GroupCoordinatorState.d.ts.map +1 -1
  19. package/bin/internals/GroupCoordinatorState.js +1 -1
  20. package/bin/internals/GroupCoordinatorState.js.map +1 -1
  21. package/bin/internals/consts.d.ts +2 -2
  22. package/bin/internals/consts.d.ts.map +1 -1
  23. package/bin/internals/consts.js +2 -2
  24. package/bin/internals/consts.js.map +1 -1
  25. package/bin/internals/errors.d.ts +17 -0
  26. package/bin/internals/errors.d.ts.map +1 -0
  27. package/bin/internals/errors.js +34 -0
  28. package/bin/internals/errors.js.map +1 -0
  29. package/bin/internals/index.d.ts +1 -0
  30. package/bin/internals/index.d.ts.map +1 -1
  31. package/bin/internals/index.js +1 -0
  32. package/bin/internals/index.js.map +1 -1
  33. package/package.json +45 -45
  34. package/src/IMediaPlayer.ts +29 -29
  35. package/src/LiveMediaSession.ts +337 -334
  36. package/src/LiveMediaSessionCoordinator.ts +711 -671
  37. package/src/LiveMediaSessionCoordinatorSuspension.ts +39 -39
  38. package/src/MediaPlayerSynchronizer.ts +714 -690
  39. package/src/MediaSessionActionThrottler.ts +19 -19
  40. package/src/MediaSessionExtensions.ts +182 -187
  41. package/src/RepeatedActionThrottler.ts +76 -76
  42. package/src/VolumeManager.ts +200 -200
  43. package/src/index.ts +14 -14
  44. package/src/internals/GroupCoordinatorState.ts +685 -683
  45. package/src/internals/GroupPlaybackPosition.ts +228 -228
  46. package/src/internals/GroupPlaybackTrack.ts +182 -182
  47. package/src/internals/GroupPlaybackTrackData.ts +104 -104
  48. package/src/internals/GroupTransportState.ts +170 -170
  49. package/src/internals/consts.ts +103 -102
  50. package/src/internals/errors.ts +42 -0
  51. package/src/internals/index.ts +12 -11
  52. package/tsconfig.build.json +8 -8
  53. package/tsconfig.json +76 -76
  54. package/typedoc.json +10 -10
@@ -1,228 +1,228 @@
1
- /**
2
- * Copyright (c) Microsoft Corporation. All rights reserved.
3
- * Licensed under the Microsoft Live Share SDK License.
4
- */
5
-
6
- import {
7
- IRuntimeSignaler,
8
- TimeInterval,
9
- LiveShareRuntime,
10
- } from "@microsoft/live-share";
11
- import { GroupTransportState } from "./GroupTransportState";
12
- import { GroupPlaybackTrackEvents } from "./GroupPlaybackTrack";
13
- import {
14
- CoordinationWaitPoint,
15
- ExtendedMediaSessionPlaybackState,
16
- } from "../MediaSessionExtensions";
17
-
18
- /**
19
- * Per client position
20
- * @hidden
21
- */
22
- export interface ICurrentPlaybackPosition {
23
- playbackState: ExtendedMediaSessionPlaybackState;
24
- waitPoint?: CoordinationWaitPoint;
25
- position: number;
26
- duration?: number;
27
- timestamp: number;
28
- clientId: string;
29
- }
30
-
31
- /**
32
- * @hidden
33
- */
34
- export class GroupPlaybackPosition {
35
- private _transportState: GroupTransportState;
36
- private _runtime: IRuntimeSignaler;
37
- private _liveRuntime: LiveShareRuntime;
38
- private _updateInterval: TimeInterval;
39
- private _positions: Map<string, ICurrentPlaybackPosition>;
40
-
41
- constructor(
42
- transportState: GroupTransportState,
43
- runtime: IRuntimeSignaler,
44
- liveRuntime: LiveShareRuntime,
45
- updateInterval: TimeInterval
46
- ) {
47
- this._transportState = transportState;
48
- this._runtime = runtime;
49
- this._liveRuntime = liveRuntime;
50
- this._updateInterval = updateInterval;
51
- this._positions = new Map();
52
-
53
- // Listen for track change
54
- this._transportState.track.on(
55
- GroupPlaybackTrackEvents.trackChange,
56
- (_) => {
57
- // Reset position tracking and duration
58
- this._positions = new Map();
59
- this.mediaDuration = undefined;
60
- }
61
- );
62
- }
63
-
64
- /**
65
- * Returns the latest position info for the local client.
66
- */
67
- public get localPosition(): ICurrentPlaybackPosition | undefined {
68
- return this._positions.get(this._runtime.clientId!);
69
- }
70
-
71
- /**
72
- * Returns the total number of clients actively being tracked.
73
- */
74
- public get totalClients(): number {
75
- let cnt = 0;
76
- this.forEach((_) => cnt++);
77
- return cnt;
78
- }
79
-
80
- /**
81
- * Returns true if all clients being tracked are reporting that they have
82
- * either ended or haven't started.
83
- */
84
- public get trackEnded(): boolean {
85
- let playing = 0;
86
- this.forEach((position, projectedPosition) => {
87
- switch (position.playbackState) {
88
- case "none":
89
- case "ended":
90
- // not playing.
91
- break;
92
- default:
93
- playing++;
94
- break;
95
- }
96
- });
97
-
98
- return playing == 0;
99
- }
100
-
101
- /**
102
- * Returns the number of clients we're waiting for before we can stop waiting.
103
- */
104
- public get clientsWaiting(): number {
105
- let cnt = 0;
106
- const waitPoint = this.localPosition?.waitPoint;
107
- if (
108
- waitPoint &&
109
- (waitPoint.maxClients == undefined ||
110
- this.totalClients <= waitPoint.maxClients)
111
- ) {
112
- this.forEach((position, projectedPosition) => {
113
- if (
114
- position.playbackState == "suspended" &&
115
- position.waitPoint
116
- ) {
117
- cnt++;
118
- } else if (position.position < waitPoint.position) {
119
- cnt++;
120
- }
121
- });
122
- }
123
-
124
- return cnt;
125
- }
126
-
127
- /**
128
- * Returns the max playback position relative to the start position.
129
- *
130
- * @remarks
131
- * This is called when calculating the current seekTo position.
132
- */
133
- public get maxPosition(): number {
134
- if (this._transportState.playbackState == "playing") {
135
- const now = this._liveRuntime.getTimestamp();
136
- const projected =
137
- this._transportState.startPosition +
138
- (now - this._transportState.timestamp) / 1000;
139
- return this.limitProjectedPosition(projected);
140
- } else {
141
- return this._transportState.startPosition;
142
- }
143
- }
144
-
145
- /**
146
- * Optional media duration if known.
147
- */
148
- public mediaDuration?: number;
149
-
150
- public get targetPosition(): number {
151
- if (this._transportState.playbackState == "playing") {
152
- return this.getMostProgressedPosition();
153
- } else {
154
- return this._transportState.startPosition;
155
- }
156
- }
157
-
158
- /**
159
- * Enumerates every reported playback position.
160
- * @param callbackFn Function applied to each position,
161
- */
162
- public forEach(
163
- callbackFn: (
164
- position: ICurrentPlaybackPosition,
165
- projectedPosition: number
166
- ) => void
167
- ): void {
168
- const now = this._liveRuntime.getTimestamp();
169
- const ignoreBefore = now - this._updateInterval.milliseconds * 2;
170
- const shouldProject = !this._transportState.track.metadata?.liveStream;
171
- this._positions.forEach((position, _) => {
172
- // Ignore any old updates
173
- if (position.timestamp > ignoreBefore) {
174
- // Compute projected playback position
175
- // - This computation does not take into account future wait points.
176
- const projected =
177
- position.playbackState == "playing" && shouldProject
178
- ? position.position + (now - position.timestamp) / 1000
179
- : position.position;
180
- callbackFn(position, this.limitProjectedPosition(projected));
181
- }
182
- });
183
- }
184
-
185
- public UpdatePlaybackPosition(position: ICurrentPlaybackPosition): void {
186
- // Update duration
187
- if (position.duration != undefined) {
188
- this.mediaDuration = position.duration;
189
- }
190
-
191
- // Save last position
192
- if (this._positions.has(position.clientId)) {
193
- // Only update if newer position
194
- const current = this._positions.get(position.clientId)!;
195
- if (position.timestamp >= current.timestamp) {
196
- this._positions.set(position.clientId, position);
197
- }
198
- } else {
199
- this._positions.set(position.clientId, position);
200
- }
201
- }
202
-
203
- private getMostProgressedPosition(): number {
204
- // Compute the max possible position for the current transport state.
205
- // - This is needed to properly handle seeking backwards in time. Some playback heads may
206
- // not have performed their seek yet and will therefore be ahead of the local player.
207
- const maxPosition = this.maxPosition;
208
-
209
- // Compute max progress
210
- let progress = -1;
211
- this.forEach((position, projectedPosition) => {
212
- if (
213
- projectedPosition <= maxPosition &&
214
- projectedPosition > progress
215
- ) {
216
- progress = projectedPosition;
217
- }
218
- });
219
-
220
- return progress;
221
- }
222
-
223
- private limitProjectedPosition(position: number): number {
224
- return this.mediaDuration != undefined
225
- ? Math.min(position, this.mediaDuration)
226
- : position;
227
- }
228
- }
1
+ /**
2
+ * Copyright (c) Microsoft Corporation. All rights reserved.
3
+ * Licensed under the Microsoft Live Share SDK License.
4
+ */
5
+
6
+ import {
7
+ IRuntimeSignaler,
8
+ TimeInterval,
9
+ LiveShareRuntime,
10
+ } from "@microsoft/live-share";
11
+ import { GroupTransportState } from "./GroupTransportState";
12
+ import { GroupPlaybackTrackEvents } from "./GroupPlaybackTrack";
13
+ import {
14
+ CoordinationWaitPoint,
15
+ ExtendedMediaSessionPlaybackState,
16
+ } from "../MediaSessionExtensions";
17
+
18
+ /**
19
+ * Per client position
20
+ * @hidden
21
+ */
22
+ export interface ICurrentPlaybackPosition {
23
+ playbackState: ExtendedMediaSessionPlaybackState;
24
+ waitPoint?: CoordinationWaitPoint;
25
+ position: number;
26
+ duration?: number;
27
+ timestamp: number;
28
+ clientId: string;
29
+ }
30
+
31
+ /**
32
+ * @hidden
33
+ */
34
+ export class GroupPlaybackPosition {
35
+ private _transportState: GroupTransportState;
36
+ private _runtime: IRuntimeSignaler;
37
+ private _liveRuntime: LiveShareRuntime;
38
+ private _updateInterval: TimeInterval;
39
+ private _positions: Map<string, ICurrentPlaybackPosition>;
40
+
41
+ constructor(
42
+ transportState: GroupTransportState,
43
+ runtime: IRuntimeSignaler,
44
+ liveRuntime: LiveShareRuntime,
45
+ updateInterval: TimeInterval
46
+ ) {
47
+ this._transportState = transportState;
48
+ this._runtime = runtime;
49
+ this._liveRuntime = liveRuntime;
50
+ this._updateInterval = updateInterval;
51
+ this._positions = new Map();
52
+
53
+ // Listen for track change
54
+ this._transportState.track.on(
55
+ GroupPlaybackTrackEvents.trackChange,
56
+ (_) => {
57
+ // Reset position tracking and duration
58
+ this._positions = new Map();
59
+ this.mediaDuration = undefined;
60
+ }
61
+ );
62
+ }
63
+
64
+ /**
65
+ * Returns the latest position info for the local client.
66
+ */
67
+ public get localPosition(): ICurrentPlaybackPosition | undefined {
68
+ return this._positions.get(this._runtime.clientId!);
69
+ }
70
+
71
+ /**
72
+ * Returns the total number of clients actively being tracked.
73
+ */
74
+ public get totalClients(): number {
75
+ let cnt = 0;
76
+ this.forEach((_) => cnt++);
77
+ return cnt;
78
+ }
79
+
80
+ /**
81
+ * Returns true if all clients being tracked are reporting that they have
82
+ * either ended or haven't started.
83
+ */
84
+ public get trackEnded(): boolean {
85
+ let playing = 0;
86
+ this.forEach((position, projectedPosition) => {
87
+ switch (position.playbackState) {
88
+ case "none":
89
+ case "ended":
90
+ // not playing.
91
+ break;
92
+ default:
93
+ playing++;
94
+ break;
95
+ }
96
+ });
97
+
98
+ return playing == 0;
99
+ }
100
+
101
+ /**
102
+ * Returns the number of clients we're waiting for before we can stop waiting.
103
+ */
104
+ public get clientsWaiting(): number {
105
+ let cnt = 0;
106
+ const waitPoint = this.localPosition?.waitPoint;
107
+ if (
108
+ waitPoint &&
109
+ (waitPoint.maxClients == undefined ||
110
+ this.totalClients <= waitPoint.maxClients)
111
+ ) {
112
+ this.forEach((position, projectedPosition) => {
113
+ if (
114
+ position.playbackState == "suspended" &&
115
+ position.waitPoint
116
+ ) {
117
+ cnt++;
118
+ } else if (position.position < waitPoint.position) {
119
+ cnt++;
120
+ }
121
+ });
122
+ }
123
+
124
+ return cnt;
125
+ }
126
+
127
+ /**
128
+ * Returns the max playback position relative to the start position.
129
+ *
130
+ * @remarks
131
+ * This is called when calculating the current seekTo position.
132
+ */
133
+ public get maxPosition(): number {
134
+ if (this._transportState.playbackState == "playing") {
135
+ const now = this._liveRuntime.getTimestamp();
136
+ const projected =
137
+ this._transportState.startPosition +
138
+ (now - this._transportState.timestamp) / 1000;
139
+ return this.limitProjectedPosition(projected);
140
+ } else {
141
+ return this._transportState.startPosition;
142
+ }
143
+ }
144
+
145
+ /**
146
+ * Optional media duration if known.
147
+ */
148
+ public mediaDuration?: number;
149
+
150
+ public get targetPosition(): number {
151
+ if (this._transportState.playbackState == "playing") {
152
+ return this.getMostProgressedPosition();
153
+ } else {
154
+ return this._transportState.startPosition;
155
+ }
156
+ }
157
+
158
+ /**
159
+ * Enumerates every reported playback position.
160
+ * @param callbackFn Function applied to each position,
161
+ */
162
+ public forEach(
163
+ callbackFn: (
164
+ position: ICurrentPlaybackPosition,
165
+ projectedPosition: number
166
+ ) => void
167
+ ): void {
168
+ const now = this._liveRuntime.getTimestamp();
169
+ const ignoreBefore = now - this._updateInterval.milliseconds * 2;
170
+ const shouldProject = !this._transportState.track.metadata?.liveStream;
171
+ this._positions.forEach((position, _) => {
172
+ // Ignore any old updates
173
+ if (position.timestamp > ignoreBefore) {
174
+ // Compute projected playback position
175
+ // - This computation does not take into account future wait points.
176
+ const projected =
177
+ position.playbackState == "playing" && shouldProject
178
+ ? position.position + (now - position.timestamp) / 1000
179
+ : position.position;
180
+ callbackFn(position, this.limitProjectedPosition(projected));
181
+ }
182
+ });
183
+ }
184
+
185
+ public UpdatePlaybackPosition(position: ICurrentPlaybackPosition): void {
186
+ // Update duration
187
+ if (position.duration != undefined) {
188
+ this.mediaDuration = position.duration;
189
+ }
190
+
191
+ // Save last position
192
+ if (this._positions.has(position.clientId)) {
193
+ // Only update if newer position
194
+ const current = this._positions.get(position.clientId)!;
195
+ if (position.timestamp >= current.timestamp) {
196
+ this._positions.set(position.clientId, position);
197
+ }
198
+ } else {
199
+ this._positions.set(position.clientId, position);
200
+ }
201
+ }
202
+
203
+ private getMostProgressedPosition(): number {
204
+ // Compute the max possible position for the current transport state.
205
+ // - This is needed to properly handle seeking backwards in time. Some playback heads may
206
+ // not have performed their seek yet and will therefore be ahead of the local player.
207
+ const maxPosition = this.maxPosition;
208
+
209
+ // Compute max progress
210
+ let progress = -1;
211
+ this.forEach((position, projectedPosition) => {
212
+ if (
213
+ projectedPosition <= maxPosition &&
214
+ projectedPosition > progress
215
+ ) {
216
+ progress = projectedPosition;
217
+ }
218
+ });
219
+
220
+ return progress;
221
+ }
222
+
223
+ private limitProjectedPosition(position: number): number {
224
+ return this.mediaDuration != undefined
225
+ ? Math.min(position, this.mediaDuration)
226
+ : position;
227
+ }
228
+ }