@microsoft/live-share-media 1.0.0-preview.1 → 1.0.0-preview.4

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 (96) hide show
  1. package/README.md +2 -1
  2. package/bin/IMediaPlayer.d.ts.map +1 -1
  3. package/bin/LiveMediaSession.d.ts +7 -7
  4. package/bin/LiveMediaSession.d.ts.map +1 -1
  5. package/bin/LiveMediaSession.js +28 -17
  6. package/bin/LiveMediaSession.js.map +1 -1
  7. package/bin/LiveMediaSessionCoordinator.d.ts +2 -2
  8. package/bin/LiveMediaSessionCoordinator.d.ts.map +1 -1
  9. package/bin/LiveMediaSessionCoordinator.js +34 -22
  10. package/bin/LiveMediaSessionCoordinator.js.map +1 -1
  11. package/bin/LiveMediaSessionCoordinatorSuspension.d.ts +1 -1
  12. package/bin/LiveMediaSessionCoordinatorSuspension.d.ts.map +1 -1
  13. package/bin/LiveMediaSessionCoordinatorSuspension.js.map +1 -1
  14. package/bin/MediaPlayerSynchronizer.d.ts +7 -7
  15. package/bin/MediaPlayerSynchronizer.d.ts.map +1 -1
  16. package/bin/MediaPlayerSynchronizer.js +127 -72
  17. package/bin/MediaPlayerSynchronizer.js.map +1 -1
  18. package/bin/MediaSessionActionThrottler.d.ts +1 -1
  19. package/bin/MediaSessionActionThrottler.d.ts.map +1 -1
  20. package/bin/MediaSessionActionThrottler.js.map +1 -1
  21. package/bin/MediaSessionExtensions.d.ts.map +1 -1
  22. package/bin/RepeatedActionThrottler.d.ts +2 -2
  23. package/bin/RepeatedActionThrottler.d.ts.map +1 -1
  24. package/bin/RepeatedActionThrottler.js +9 -3
  25. package/bin/RepeatedActionThrottler.js.map +1 -1
  26. package/bin/VolumeManager.d.ts +1 -1
  27. package/bin/VolumeManager.d.ts.map +1 -1
  28. package/bin/VolumeManager.js +4 -2
  29. package/bin/VolumeManager.js.map +1 -1
  30. package/bin/index.d.ts +9 -9
  31. package/bin/index.d.ts.map +1 -1
  32. package/bin/index.js.map +1 -1
  33. package/bin/internals/GroupCoordinatorState.d.ts +8 -8
  34. package/bin/internals/GroupCoordinatorState.d.ts.map +1 -1
  35. package/bin/internals/GroupCoordinatorState.js +91 -53
  36. package/bin/internals/GroupCoordinatorState.js.map +1 -1
  37. package/bin/internals/GroupPlaybackPosition.d.ts +3 -3
  38. package/bin/internals/GroupPlaybackPosition.d.ts.map +1 -1
  39. package/bin/internals/GroupPlaybackPosition.js +22 -13
  40. package/bin/internals/GroupPlaybackPosition.js.map +1 -1
  41. package/bin/internals/GroupPlaybackTrack.d.ts +4 -4
  42. package/bin/internals/GroupPlaybackTrack.d.ts.map +1 -1
  43. package/bin/internals/GroupPlaybackTrack.js +14 -4
  44. package/bin/internals/GroupPlaybackTrack.js.map +1 -1
  45. package/bin/internals/GroupPlaybackTrackData.d.ts +3 -3
  46. package/bin/internals/GroupPlaybackTrackData.d.ts.map +1 -1
  47. package/bin/internals/GroupPlaybackTrackData.js +8 -4
  48. package/bin/internals/GroupPlaybackTrackData.js.map +1 -1
  49. package/bin/internals/GroupTransportState.d.ts +5 -5
  50. package/bin/internals/GroupTransportState.d.ts.map +1 -1
  51. package/bin/internals/GroupTransportState.js +27 -12
  52. package/bin/internals/GroupTransportState.js.map +1 -1
  53. package/bin/internals/consts.d.ts.map +1 -1
  54. package/bin/internals/consts.js +54 -54
  55. package/bin/internals/consts.js.map +1 -1
  56. package/bin/internals/index.d.ts +6 -6
  57. package/package.json +3 -3
  58. package/src/IMediaPlayer.ts +10 -2
  59. package/src/LiveMediaSession.ts +133 -71
  60. package/src/LiveMediaSessionCoordinator.ts +302 -142
  61. package/src/LiveMediaSessionCoordinatorSuspension.ts +13 -5
  62. package/src/MediaPlayerSynchronizer.ts +329 -149
  63. package/src/MediaSessionActionThrottler.ts +5 -3
  64. package/src/MediaSessionExtensions.ts +15 -5
  65. package/src/RepeatedActionThrottler.ts +27 -9
  66. package/src/VolumeManager.ts +24 -12
  67. package/src/index.ts +9 -10
  68. package/src/internals/GroupCoordinatorState.ts +274 -114
  69. package/src/internals/GroupPlaybackPosition.ts +61 -27
  70. package/src/internals/GroupPlaybackTrack.ts +51 -22
  71. package/src/internals/GroupPlaybackTrackData.ts +24 -16
  72. package/src/internals/GroupTransportState.ts +53 -23
  73. package/src/internals/consts.ts +73 -55
  74. package/src/internals/index.ts +6 -6
  75. package/src/test/GroupPlaybackPosition.spec.ts +371 -111
  76. package/src/test/GroupPlaybackTrack.spec.ts +91 -35
  77. package/src/test/SharedMediaSession.spec.ts +64 -28
  78. package/src/test/SharedMediaSessionCoordinator.spec.ts +86 -46
  79. package/src/test/VolumeManager.spec.ts +38 -31
  80. package/tsconfig.json +1 -0
  81. package/bin/EphemeralMediaSession.d.ts +0 -102
  82. package/bin/EphemeralMediaSession.d.ts.map +0 -1
  83. package/bin/EphemeralMediaSession.js +0 -226
  84. package/bin/EphemeralMediaSession.js.map +0 -1
  85. package/bin/EphemeralMediaSessionCoordinator.d.ts +0 -228
  86. package/bin/EphemeralMediaSessionCoordinator.d.ts.map +0 -1
  87. package/bin/EphemeralMediaSessionCoordinator.js +0 -396
  88. package/bin/EphemeralMediaSessionCoordinator.js.map +0 -1
  89. package/bin/EphemeralMediaSessionCoordinatorSuspension.d.ts +0 -20
  90. package/bin/EphemeralMediaSessionCoordinatorSuspension.d.ts.map +0 -1
  91. package/bin/EphemeralMediaSessionCoordinatorSuspension.js +0 -28
  92. package/bin/EphemeralMediaSessionCoordinatorSuspension.js.map +0 -1
  93. package/bin/VolumeLimiter.d.ts +0 -70
  94. package/bin/VolumeLimiter.d.ts.map +0 -1
  95. package/bin/VolumeLimiter.js +0 -170
  96. package/bin/VolumeLimiter.js.map +0 -1
@@ -3,40 +3,49 @@
3
3
  * Licensed under the Microsoft Live Share SDK License.
4
4
  */
5
5
 
6
- import { DataObject, DataObjectFactory } from '@fluidframework/aqueduct';
7
- import { LiveTelemetryLogger, UserMeetingRole } from '@microsoft/live-share';
8
- import { MediaPlayerSynchronizer } from './MediaPlayerSynchronizer';
9
- import { ITriggerActionEvent, TelemetryEvents } from './internals';
10
- import { MediaSessionCoordinatorEvents, ExtendedMediaSessionAction, ExtendedMediaSessionActionDetails } from './MediaSessionExtensions';
11
- import { LiveMediaSessionCoordinator, IMediaPlayerState } from './LiveMediaSessionCoordinator';
12
- import { MediaSessionActionThrottler } from './MediaSessionActionThrottler';
13
- import { RepeatedActionThrottler } from './RepeatedActionThrottler';
14
- import { IMediaPlayer } from './IMediaPlayer';
6
+ import { DataObject, DataObjectFactory } from "@fluidframework/aqueduct";
7
+ import { LiveTelemetryLogger, UserMeetingRole } from "@microsoft/live-share";
8
+ import { MediaPlayerSynchronizer } from "./MediaPlayerSynchronizer";
9
+ import { ITriggerActionEvent, TelemetryEvents } from "./internals";
10
+ import {
11
+ MediaSessionCoordinatorEvents,
12
+ ExtendedMediaSessionAction,
13
+ ExtendedMediaSessionActionDetails,
14
+ } from "./MediaSessionExtensions";
15
+ import {
16
+ LiveMediaSessionCoordinator,
17
+ IMediaPlayerState,
18
+ } from "./LiveMediaSessionCoordinator";
19
+ import { MediaSessionActionThrottler } from "./MediaSessionActionThrottler";
20
+ import { RepeatedActionThrottler } from "./RepeatedActionThrottler";
21
+ import { IMediaPlayer } from "./IMediaPlayer";
15
22
 
16
23
  /**
17
24
  * Live fluid object that synchronizes media playback across multiple clients.
18
25
  */
19
- export class LiveMediaSession extends DataObject {
20
- private _actionThrottler: MediaSessionActionThrottler = new RepeatedActionThrottler();
26
+ export class LiveMediaSession extends DataObject {
27
+ private _actionThrottler: MediaSessionActionThrottler =
28
+ new RepeatedActionThrottler();
21
29
  private _logger?: LiveTelemetryLogger;
22
30
  private _requestPlayerStateHandler?: () => IMediaPlayerState;
23
31
  private _coordinator?: LiveMediaSessionCoordinator;
24
- private readonly _actionHandlers: Map<string, MediaSessionActionHandler> = new Map();
32
+ private readonly _actionHandlers: Map<string, MediaSessionActionHandler> =
33
+ new Map();
25
34
  private _synchronizing?: MediaPlayerSynchronizer;
26
35
 
27
36
  // Position update timer
28
37
  private _updateTimer: any;
29
38
  private _lastUpdateTime: number = 0;
30
-
39
+
31
40
  /**
32
41
  * The objects fluid type name.
33
42
  */
34
- public static readonly TypeName = `@microsoft/fluid-media:SharedMediaSession`;
43
+ public static readonly TypeName = `@microsoft/fluid-media:SharedMediaSession`;
35
44
 
36
45
  /**
37
46
  * The objects fluid type factory.
38
47
  */
39
- public static readonly factory = new DataObjectFactory(
48
+ public static readonly factory = new DataObjectFactory(
40
49
  LiveMediaSession.TypeName,
41
50
  LiveMediaSession,
42
51
  [],
@@ -46,15 +55,15 @@ import { IMediaPlayer } from './IMediaPlayer';
46
55
  /**
47
56
  * Extension point that lets applications replace the default logic for throttling the sessions
48
57
  * local sync behavior.
49
- *
58
+ *
50
59
  * @remarks
51
- * The `LiveMediaCoordinator` is fairly aggressive at wanting to keep the local media player
52
- * in sync with the rest of the group. This aggressiveness can result in the coordinator sending
53
- * new sync actions before the local player has finished responding to the previous sync action.
60
+ * The `LiveMediaCoordinator` is fairly aggressive at wanting to keep the local media player
61
+ * in sync with the rest of the group. This aggressiveness can result in the coordinator sending
62
+ * new sync actions before the local player has finished responding to the previous sync action.
54
63
  * The `ActionThrottler` gives apps fine grain control over how aggressive they want sync to be.
55
- *
56
- * By default, a `RepeatedAction` throttler is used which prevents the same sync action from
57
- * being sent within an adjustable time period.
64
+ *
65
+ * By default, a `RepeatedAction` throttler is used which prevents the same sync action from
66
+ * being sent within an adjustable time period.
58
67
  */
59
68
  public get actionThrottler(): MediaSessionActionThrottler {
60
69
  return this._actionThrottler;
@@ -76,7 +85,7 @@ import { IMediaPlayer } from './IMediaPlayer';
76
85
  * Returns true if the object has been initialized.
77
86
  */
78
87
  public get isStarted(): boolean {
79
- return this.isInitialized
88
+ return this.isInitialized;
80
89
  }
81
90
 
82
91
  /**
@@ -88,7 +97,7 @@ import { IMediaPlayer } from './IMediaPlayer';
88
97
 
89
98
  /**
90
99
  * Returns the logger used by the session and coordinator.
91
- *
100
+ *
92
101
  * @remarks
93
102
  * This is used by the `MediaPlayerSynchronizer` to log events.
94
103
  */
@@ -98,10 +107,12 @@ import { IMediaPlayer } from './IMediaPlayer';
98
107
 
99
108
  /**
100
109
  * initialize the object.
101
- * @param acceptTransportChangesFrom Optional. List of roles allowed to group transport
110
+ * @param acceptTransportChangesFrom Optional. List of roles allowed to group transport
102
111
  * operations like play/pause/seek/setTrack.
103
112
  */
104
- public initialize(acceptTransportChangesFrom?: UserMeetingRole[]): Promise<void> {
113
+ public initialize(
114
+ acceptTransportChangesFrom?: UserMeetingRole[]
115
+ ): Promise<void> {
105
116
  return this.coordinator.initialize(acceptTransportChangesFrom);
106
117
  }
107
118
 
@@ -110,11 +121,13 @@ import { IMediaPlayer } from './IMediaPlayer';
110
121
  * @param action Name of the action to register a handler for.
111
122
  * @param handler Function called when the action is triggered.
112
123
  */
113
- public setActionHandler(action: ExtendedMediaSessionAction, handler: MediaSessionActionHandler | null): void {
124
+ public setActionHandler(
125
+ action: ExtendedMediaSessionAction,
126
+ handler: MediaSessionActionHandler | null
127
+ ): void {
114
128
  if (handler) {
115
129
  // add handler
116
130
  this._actionHandlers.set(action, handler);
117
-
118
131
  } else if (this._actionHandlers.has(action)) {
119
132
  // remove handler
120
133
  this._actionHandlers.delete(action);
@@ -122,8 +135,8 @@ import { IMediaPlayer } from './IMediaPlayer';
122
135
  }
123
136
 
124
137
  /**
125
- * Registers a handler that will be queried anytime the group coordinate needs to know the
126
- * local players transport state and position.
138
+ * Registers a handler that will be queried anytime the group coordinate needs to know the
139
+ * local players transport state and position.
127
140
  */
128
141
  public setRequestPlayerStateHandler(handler: () => IMediaPlayerState) {
129
142
  this._requestPlayerStateHandler = handler;
@@ -142,7 +155,9 @@ import { IMediaPlayer } from './IMediaPlayer';
142
155
 
143
156
  // Start position update timer
144
157
  if (!this._updateTimer) {
145
- this.logger.sendTelemetryEvent(TelemetryEvents.MediaSession.BeginPositionUpdateTimer);
158
+ this.logger.sendTelemetryEvent(
159
+ TelemetryEvents.MediaSession.BeginPositionUpdateTimer
160
+ );
146
161
  this._updateTimer = setInterval(() => {
147
162
  // Get current state
148
163
  const state = this.getCurrentPlayerState();
@@ -153,7 +168,10 @@ import { IMediaPlayer } from './IMediaPlayer';
153
168
  // Send position update if interval hit
154
169
  const now = new Date().getTime();
155
170
  const delta = (now - this._lastUpdateTime) / 1000;
156
- if (this.coordinator.isInitialized && delta >= this.coordinator.positionUpdateInterval) {
171
+ if (
172
+ this.coordinator.isInitialized &&
173
+ delta >= this.coordinator.positionUpdateInterval
174
+ ) {
157
175
  this._lastUpdateTime = now;
158
176
  this.coordinator.sendPositionUpdate(state);
159
177
  }
@@ -166,70 +184,114 @@ import { IMediaPlayer } from './IMediaPlayer';
166
184
  }
167
185
 
168
186
  protected async hasInitialized(): Promise<void> {
169
- this. _logger = new LiveTelemetryLogger(this.runtime);
187
+ this._logger = new LiveTelemetryLogger(this.runtime);
170
188
 
171
189
  // Create coordinator and listen for triggered actions
172
- this._coordinator = new LiveMediaSessionCoordinator(this.runtime, () => this.getCurrentPlayerState());
173
- this._coordinator.on(MediaSessionCoordinatorEvents.triggeraction, (event: ITriggerActionEvent) => {
174
- // Pre-process actions
175
- const details = event.details;
176
- const seekTime = typeof details.seekTime == 'number' ? details.seekTime : -1;
177
- switch (details.action) {
178
- case 'play':
179
- this.logger.sendTelemetryEvent(TelemetryEvents.MediaSession.PlayAction, null, {position: seekTime});
180
- break;
181
- case 'pause':
182
- this.logger.sendTelemetryEvent(TelemetryEvents.MediaSession.PauseAction, null, {position: seekTime});
183
- break;
184
- case 'seekto':
185
- this.logger.sendTelemetryEvent(TelemetryEvents.MediaSession.SeekToAction, null, {position: seekTime});
186
- break;
187
- case 'settrack':
188
- this.logger.sendTelemetryEvent(TelemetryEvents.MediaSession.SetTrackAction);
189
- break;
190
- case 'datachange':
191
- this.logger.sendTelemetryEvent(TelemetryEvents.MediaSession.DataChangeAction);
192
- break;
193
- case 'catchup':
194
- // Default to seekto if catchup not implemented
195
- this.logger.sendTelemetryEvent(TelemetryEvents.MediaSession.CatchUpAction, null, {position: seekTime});
196
- if (!this._actionHandlers.has('catchup')) {
197
- details.action = 'seekto';
198
- }
199
- break;
200
- }
190
+ this._coordinator = new LiveMediaSessionCoordinator(this.runtime, () =>
191
+ this.getCurrentPlayerState()
192
+ );
193
+ this._coordinator.on(
194
+ MediaSessionCoordinatorEvents.triggeraction,
195
+ (event: ITriggerActionEvent) => {
196
+ // Pre-process actions
197
+ const details = event.details;
198
+ const seekTime =
199
+ typeof details.seekTime == "number" ? details.seekTime : -1;
200
+ switch (details.action) {
201
+ case "play":
202
+ this.logger.sendTelemetryEvent(
203
+ TelemetryEvents.MediaSession.PlayAction,
204
+ null,
205
+ {
206
+ position: seekTime,
207
+ }
208
+ );
209
+ break;
210
+ case "pause":
211
+ this.logger.sendTelemetryEvent(
212
+ TelemetryEvents.MediaSession.PauseAction,
213
+ null,
214
+ {
215
+ position: seekTime,
216
+ }
217
+ );
218
+ break;
219
+ case "seekto":
220
+ this.logger.sendTelemetryEvent(
221
+ TelemetryEvents.MediaSession.SeekToAction,
222
+ null,
223
+ {
224
+ position: seekTime,
225
+ }
226
+ );
227
+ break;
228
+ case "settrack":
229
+ this.logger.sendTelemetryEvent(
230
+ TelemetryEvents.MediaSession.SetTrackAction
231
+ );
232
+ break;
233
+ case "datachange":
234
+ this.logger.sendTelemetryEvent(
235
+ TelemetryEvents.MediaSession.DataChangeAction
236
+ );
237
+ break;
238
+ case "catchup":
239
+ // Default to seekto if catchup not implemented
240
+ this.logger.sendTelemetryEvent(
241
+ TelemetryEvents.MediaSession.CatchUpAction,
242
+ null,
243
+ {
244
+ position: seekTime,
245
+ }
246
+ );
247
+ if (!this._actionHandlers.has("catchup")) {
248
+ details.action = "seekto";
249
+ }
250
+ break;
251
+ }
201
252
 
202
- // Dispatch action
203
- this.dispatchAction(details);
204
- });
253
+ // Dispatch action
254
+ this.dispatchAction(details);
255
+ }
256
+ );
205
257
  }
206
258
 
207
259
  private checkWaitPointHit(state: IMediaPlayerState): void {
208
260
  // Was a wait point hit?
209
261
  if (state.positionState && this.coordinator.isInitialized) {
210
262
  const waitPoint = this.coordinator.findNextWaitPoint();
211
- if (waitPoint && state.positionState.position != undefined && state.positionState.position >= waitPoint.position) {
263
+ if (
264
+ waitPoint &&
265
+ state.positionState.position != undefined &&
266
+ state.positionState.position >= waitPoint.position
267
+ ) {
212
268
  // Ensure handler registered
213
- if (!this._actionHandlers.has('wait')) {
214
- throw new Error(`SharedMediaSession: wait point hit but no 'wait' action registered.`);
269
+ if (!this._actionHandlers.has("wait")) {
270
+ throw new Error(
271
+ `SharedMediaSession: wait point hit but no 'wait' action registered.`
272
+ );
215
273
  }
216
274
 
217
275
  // Begin suspension for wait point
218
276
  const suspension = this.coordinator.beginSuspension(waitPoint);
219
- this.dispatchAction({action: 'wait', suspension: suspension});
277
+ this.dispatchAction({ action: "wait", suspension: suspension });
220
278
  }
221
279
  }
222
280
  }
223
281
 
224
282
  private getCurrentPlayerState(): IMediaPlayerState {
225
283
  if (!this._requestPlayerStateHandler) {
226
- throw new Error(`SharedMediaSession: no getPlayerState callback configured.`);
284
+ throw new Error(
285
+ `SharedMediaSession: no getPlayerState callback configured.`
286
+ );
227
287
  }
228
288
 
229
289
  return this._requestPlayerStateHandler();
230
290
  }
231
291
 
232
- private dispatchAction(details: MediaSessionActionDetails|ExtendedMediaSessionActionDetails): void {
292
+ private dispatchAction(
293
+ details: MediaSessionActionDetails | ExtendedMediaSessionActionDetails
294
+ ): void {
233
295
  if (this._actionHandlers.has(details.action)) {
234
296
  const handler = this._actionHandlers.get(details.action);
235
297
  this._actionThrottler.throttled(details, handler);