@microsoft/live-share-media 0.1.2 → 0.2.2

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 (77) hide show
  1. package/.vscode/settings.json +2 -0
  2. package/LICENSE +87 -0
  3. package/README.md +118 -92
  4. package/bin/EphemeralMediaSession.d.ts +51 -4
  5. package/bin/EphemeralMediaSession.d.ts.map +1 -1
  6. package/bin/EphemeralMediaSession.js +91 -44
  7. package/bin/EphemeralMediaSession.js.map +1 -1
  8. package/bin/EphemeralMediaSessionCoordinator.d.ts +173 -6
  9. package/bin/EphemeralMediaSessionCoordinator.d.ts.map +1 -1
  10. package/bin/EphemeralMediaSessionCoordinator.js +170 -23
  11. package/bin/EphemeralMediaSessionCoordinator.js.map +1 -1
  12. package/bin/EphemeralMediaSessionCoordinatorSuspension.d.ts +1 -1
  13. package/bin/EphemeralMediaSessionCoordinatorSuspension.js +1 -1
  14. package/bin/IMediaPlayer.d.ts +1 -1
  15. package/bin/IMediaPlayer.js +1 -1
  16. package/bin/MediaPlayerSynchronizer.d.ts +95 -6
  17. package/bin/MediaPlayerSynchronizer.d.ts.map +1 -1
  18. package/bin/MediaPlayerSynchronizer.js +93 -31
  19. package/bin/MediaPlayerSynchronizer.js.map +1 -1
  20. package/bin/MediaSessionActionThrottler.d.ts +1 -1
  21. package/bin/MediaSessionActionThrottler.js +1 -1
  22. package/bin/MediaSessionExtensions.d.ts +1 -1
  23. package/bin/MediaSessionExtensions.js +1 -1
  24. package/bin/RepeatedActionThrottler.d.ts +1 -1
  25. package/bin/RepeatedActionThrottler.js +1 -1
  26. package/bin/VolumeLimiter.d.ts +2 -2
  27. package/bin/VolumeLimiter.js +2 -2
  28. package/bin/index.d.ts +1 -1
  29. package/bin/index.js +1 -1
  30. package/bin/internals/GroupCoordinatorState.d.ts +22 -1
  31. package/bin/internals/GroupCoordinatorState.d.ts.map +1 -1
  32. package/bin/internals/GroupCoordinatorState.js +10 -4
  33. package/bin/internals/GroupCoordinatorState.js.map +1 -1
  34. package/bin/internals/GroupPlaybackPosition.d.ts +8 -1
  35. package/bin/internals/GroupPlaybackPosition.d.ts.map +1 -1
  36. package/bin/internals/GroupPlaybackPosition.js +5 -2
  37. package/bin/internals/GroupPlaybackPosition.js.map +1 -1
  38. package/bin/internals/GroupPlaybackTrack.d.ts +13 -1
  39. package/bin/internals/GroupPlaybackTrack.d.ts.map +1 -1
  40. package/bin/internals/GroupPlaybackTrack.js +8 -2
  41. package/bin/internals/GroupPlaybackTrack.js.map +1 -1
  42. package/bin/internals/GroupPlaybackTrackData.d.ts +13 -1
  43. package/bin/internals/GroupPlaybackTrackData.d.ts.map +1 -1
  44. package/bin/internals/GroupPlaybackTrackData.js +8 -2
  45. package/bin/internals/GroupPlaybackTrackData.js.map +1 -1
  46. package/bin/internals/GroupTransportState.d.ts +13 -1
  47. package/bin/internals/GroupTransportState.d.ts.map +1 -1
  48. package/bin/internals/GroupTransportState.js +8 -2
  49. package/bin/internals/GroupTransportState.js.map +1 -1
  50. package/bin/internals/consts.d.ts +2 -1
  51. package/bin/internals/consts.d.ts.map +1 -1
  52. package/bin/internals/consts.js +5 -1
  53. package/bin/internals/consts.js.map +1 -1
  54. package/bin/internals/index.d.ts +1 -1
  55. package/bin/internals/index.js +1 -1
  56. package/package.json +14 -5
  57. package/src/EphemeralMediaSession.ts +95 -48
  58. package/src/EphemeralMediaSessionCoordinator.ts +190 -26
  59. package/src/EphemeralMediaSessionCoordinatorSuspension.ts +1 -1
  60. package/src/IMediaPlayer.ts +1 -1
  61. package/src/MediaPlayerSynchronizer.ts +101 -33
  62. package/src/MediaSessionActionThrottler.ts +1 -1
  63. package/src/MediaSessionExtensions.ts +1 -1
  64. package/src/RepeatedActionThrottler.ts +1 -1
  65. package/src/VolumeLimiter.ts +2 -2
  66. package/src/index.ts +1 -1
  67. package/src/internals/GroupCoordinatorState.ts +39 -18
  68. package/src/internals/GroupPlaybackPosition.ts +16 -10
  69. package/src/internals/GroupPlaybackTrack.ts +22 -9
  70. package/src/internals/GroupPlaybackTrackData.ts +20 -7
  71. package/src/internals/GroupTransportState.ts +20 -7
  72. package/src/internals/consts.ts +7 -3
  73. package/src/internals/index.ts +1 -1
  74. package/src/test/GroupPlaybackPosition.spec.ts +1 -1
  75. package/src/test/GroupPlaybackTrack.spec.ts +1 -1
  76. package/src/test/SharedMediaSession.spec.ts +1 -1
  77. package/src/test/SharedMediaSessionCoordinator.spec.ts +1 -1
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Copyright (c) Microsoft Corporation. All rights reserved.
3
- * Licensed under the MIT License.
3
+ * Licensed under the Microsoft Live Share SDK License.
4
4
  */
5
5
 
6
6
  import { EphemeralTelemetryLogger, IEvent, TimeInterval } from '@microsoft/live-share';
@@ -11,11 +11,23 @@ import { VolumeLimiter } from './VolumeLimiter';
11
11
  import { IMediaPlayer } from './IMediaPlayer';
12
12
  import { TelemetryEvents } from './internals';
13
13
 
14
+ /**
15
+ * Event data returned by `MediaPlayerSynchronizer` object.
16
+ */
14
17
  export interface IMediaPlayerSynchronizerEvent extends IEvent {
18
+ /**
19
+ * Event details.
20
+ */
15
21
  details: ExtendedMediaSessionActionDetails;
16
22
  }
17
23
 
24
+ /**
25
+ * Events supported by `MediaPlayerSynchronizer` object.
26
+ */
18
27
  export enum MediaPlayerSynchronizerEvents {
28
+ /**
29
+ *
30
+ */
19
31
  coordinatorstatechange = 'coordinatorstatechange',
20
32
  groupaction = 'groupaction',
21
33
  useraction = 'useraction'
@@ -40,7 +52,6 @@ export class MediaPlayerSynchronizer extends EventEmitter {
40
52
  private _volumeLimiter: VolumeLimiter;
41
53
  private _onEnd?: () => void;
42
54
  private _onPlayerEvent: EventListener;
43
- private _catchupFastSeekPeriod: TimeInterval = new TimeInterval(0);
44
55
  private _seekSuspension?: MediaSessionCoordinatorSuspension;
45
56
  private _viewOnly = false;
46
57
  private _expectedPlaybackState: ExtendedMediaSessionPlaybackState = 'none';
@@ -61,7 +72,7 @@ export class MediaPlayerSynchronizer extends EventEmitter {
61
72
  this._onEnd = onEnd;
62
73
 
63
74
  // Listen for player state requests
64
- mediaSession.onRequestPlayerState = () => {
75
+ mediaSession.setRequestPlayerStateHandler(() => {
65
76
  // Find playback state
66
77
  let playbackState: ExtendedMediaSessionPlaybackState;
67
78
  const src = this._player.currentSrc || this._player.src;
@@ -87,7 +98,7 @@ export class MediaPlayerSynchronizer extends EventEmitter {
87
98
  };
88
99
 
89
100
  return state;
90
- }
101
+ });
91
102
 
92
103
  // Listen for player events
93
104
  this._onPlayerEvent = (evt: Event) => {
@@ -104,7 +115,7 @@ export class MediaPlayerSynchronizer extends EventEmitter {
104
115
  // Handle case for YouTube player where user can pause/play video by clicking on it.
105
116
  // - Videos don't always start at 0.0 seconds.
106
117
  if (this._expectedPlaybackState != 'playing') {
107
- if (!this._viewOnly && this._player.currentTime < 1.0) {
118
+ if (this._mediaSession.coordinator.canPlayPause && this._player.currentTime < 1.0) {
108
119
  this._logger.sendTelemetryEvent(TelemetryEvents.MediaPlayerSynchronizer.UserTappedVideoToPlay);
109
120
  this.play();
110
121
  }
@@ -185,22 +196,30 @@ export class MediaPlayerSynchronizer extends EventEmitter {
185
196
  this._logger.sendTelemetryEvent(TelemetryEvents.MediaPlayerSynchronizer.SynchronizationStarted);
186
197
  }
187
198
 
188
- public get catchupFastSeekPeriod(): number {
189
- return this._catchupFastSeekPeriod.seconds;
190
- }
191
-
192
- public set catchupFastSeekPeriod(value: number) {
193
- this._catchupFastSeekPeriod.seconds = value;
194
- }
195
-
199
+ /**
200
+ * Media player being synchronized.
201
+ */
196
202
  public get player(): IMediaPlayer {
197
203
  return this._player;
198
204
  }
199
205
 
206
+ /**
207
+ * Synchronizers media session.
208
+ */
200
209
  public get mediaSession(): EphemeralMediaSession {
201
210
  return this._mediaSession;
202
211
  }
203
212
 
213
+ /**
214
+ * If true the client is in a view only mode.
215
+ *
216
+ * @remarks
217
+ * Toggling this value to true results in `mediaSession.coordinator.canPlayPause`,
218
+ * `mediaSession.coordinator.canSeek`, `mediaSession.coordinator.canSetTrack`, and
219
+ * `mediaSession.coordinator.canSetTrackData` all being set to false. For more fine
220
+ * grained control over the local clients policies, call the `mediaSession.coordinator`
221
+ * directly.
222
+ */
204
223
  public get viewOnly(): boolean {
205
224
  return this._viewOnly;
206
225
  }
@@ -209,22 +228,40 @@ export class MediaPlayerSynchronizer extends EventEmitter {
209
228
  this._viewOnly = value;
210
229
  this.mediaSession.coordinator.canPlayPause = !value;
211
230
  this.mediaSession.coordinator.canSeek = !value;
231
+ this.mediaSession.coordinator.canSetTrack = !value;
232
+ this.mediaSession.coordinator.canSetTrackData = !value;
212
233
  }
213
234
 
235
+ /**
236
+ * Volume limiter used to temporarily reduce the videos volume when someone speaks in a meeting.
237
+ */
214
238
  public get volumeLimiter(): VolumeLimiter {
215
239
  return this._volumeLimiter;
216
240
  }
217
241
 
218
- public addEventListener(event: MediaPlayerSynchronizerEvents, listener: () => void): this {
242
+ /**
243
+ * Registers a new event listener.
244
+ * @param event Name of the event to add.
245
+ * @param listener Function to call when the event is triggered.
246
+ */
247
+ public addEventListener(event: MediaPlayerSynchronizerEvents, listener: (evt: IMediaPlayerSynchronizerEvent) => void): this {
219
248
  this.on(event, listener);
220
249
  return this;
221
250
  }
222
251
 
223
- public removeEventListener(event: MediaPlayerSynchronizerEvents, listener: () => void): this {
252
+ /**
253
+ * Un-registers an existing event listener.
254
+ * @param event Name of the event to remove.
255
+ * @param listener Function that was registered in call to `addEventListener()`.
256
+ */
257
+ public removeEventListener(event: MediaPlayerSynchronizerEvents, listener: (evt: IMediaPlayerSynchronizerEvent) => void): this {
224
258
  this.off(event, listener);
225
259
  return this;
226
260
  }
227
261
 
262
+ /**
263
+ * Ends synchronization of the current media player.
264
+ */
228
265
  public end(): void {
229
266
  if (this._onEnd) {
230
267
  try {
@@ -246,6 +283,15 @@ export class MediaPlayerSynchronizer extends EventEmitter {
246
283
  }
247
284
  }
248
285
 
286
+ /**
287
+ * Begin a local seek operation.
288
+ *
289
+ * @remarks
290
+ * UI can call this when a user grabs a timeline scrubber and starts scrubbing the video to a
291
+ * new playback position. The synchronizer will being a new suspension which temporarily
292
+ * disconnects the client for the rest of the group for synchronization purposes. Calling
293
+ * `endSeek()` will end the suspension and seek the group to the users final seek position.
294
+ */
249
295
  public beginSeek(): void {
250
296
  if (this._seekSuspension) {
251
297
  throw new Error(`MediaPlayerSynchronizer: cannot begin seek. A seek is already in progress.`);
@@ -255,6 +301,10 @@ export class MediaPlayerSynchronizer extends EventEmitter {
255
301
  this._seekSuspension = this._mediaSession.coordinator.beginSuspension();
256
302
  }
257
303
 
304
+ /**
305
+ * Ends a seek operation that was started by calling `beginSeek()`.
306
+ * @param seekTo Playback position in seconds to seek the group to.
307
+ */
258
308
  public endSeek(seekTo: number): void {
259
309
  if (!this._seekSuspension) {
260
310
  throw new Error(`MediaPlayerSynchronizer: cannot end seek. No seek is in progress.`);
@@ -274,6 +324,13 @@ export class MediaPlayerSynchronizer extends EventEmitter {
274
324
  this.dispatchUserAction({action: 'seekto', seekTime: seekTo});
275
325
  }
276
326
 
327
+ /**
328
+ * Tells the group to begin playing the current video.
329
+ *
330
+ * @remarks
331
+ * For proper operation apps should avoid calling `mediaSession.coordinator.play()` directly
332
+ * and instead use the synchronizers `play()` method.
333
+ */
277
334
  public play(): void {
278
335
  this._logger.sendTelemetryEvent(TelemetryEvents.MediaPlayerSynchronizer.PlayCalled);
279
336
  this._expectedPlaybackState = 'playing';
@@ -282,6 +339,13 @@ export class MediaPlayerSynchronizer extends EventEmitter {
282
339
  this.dispatchUserAction({action: 'play'});
283
340
  }
284
341
 
342
+ /**
343
+ * Tells the group to pause the current video.
344
+ *
345
+ * @remarks
346
+ * For proper operation apps should avoid calling `mediaSession.coordinator.pause()` directly
347
+ * and instead use the synchronizers `pause()` method.
348
+ */
285
349
  public pause(): void {
286
350
  this._logger.sendTelemetryEvent(TelemetryEvents.MediaPlayerSynchronizer.PauseCalled);
287
351
  this._expectedPlaybackState = 'paused';
@@ -290,6 +354,13 @@ export class MediaPlayerSynchronizer extends EventEmitter {
290
354
  this.dispatchUserAction({action: 'pause'});
291
355
  }
292
356
 
357
+ /**
358
+ * Tells the group to seek the current video to a new playback position.
359
+ *
360
+ * @remarks
361
+ * For proper operation apps should avoid calling `mediaSession.coordinator.seekTo()` directly
362
+ * and instead use the synchronizers `seekTo()` method.
363
+ */
293
364
  public seekTo(time: number): void {
294
365
  // Always seek player to new time.
295
366
  // - This resolves an issue where the timeline scrubber can temporarily snap back to the original
@@ -302,6 +373,13 @@ export class MediaPlayerSynchronizer extends EventEmitter {
302
373
  this.dispatchUserAction({action: 'seekto', seekTime: time});
303
374
  }
304
375
 
376
+ /**
377
+ * Tells the group to change to a new track.
378
+ *
379
+ * @remarks
380
+ * For proper operation apps should avoid calling `mediaSession.coordinator.setTrack()` directly
381
+ * and instead use the synchronizers `setTrack()` method.
382
+ */
305
383
  public setTrack(track: ExtendedMediaMetadata, waitPoints?: CoordinationWaitPoint[]): void {
306
384
  this._logger.sendTelemetryEvent(TelemetryEvents.MediaPlayerSynchronizer.SetTrackCalled);
307
385
  this._mediaSession.coordinator.setTrack(track, waitPoints);
@@ -309,6 +387,13 @@ export class MediaPlayerSynchronizer extends EventEmitter {
309
387
  this.dispatchUserAction({action: 'settrack', metadata: track});
310
388
  }
311
389
 
390
+ /**
391
+ * Updates the current tracks data object.
392
+ *
393
+ * @remarks
394
+ * For proper operation apps should avoid calling `mediaSession.coordinator.setTrackData()` directly
395
+ * and instead use the synchronizers `setTrackData()` method.
396
+ */
312
397
  public setTrackData(data: object|null): void {
313
398
  this._logger.sendTelemetryEvent(TelemetryEvents.MediaPlayerSynchronizer.SetTrackDataCalled);
314
399
  this._trackData = data;
@@ -334,23 +419,6 @@ export class MediaPlayerSynchronizer extends EventEmitter {
334
419
  }
335
420
 
336
421
  private async catchupPlayer(time: number): Promise<void> {
337
- // Only fast seek if:
338
- // - more then 2 seconds behind.
339
- // - fast seek hasn't been disabled.
340
- // - the current playback rate is 1.0.
341
- if (time >= (this._player.currentTime + 2.0) && this.catchupFastSeekPeriod > 0 && this._player.playbackRate == 1.0) {
342
- // Fast seek to new position
343
- const suspension = await this._mediaSession.coordinator.beginSuspension();
344
- const seekPeriod = Math.min(time - this._player.currentTime, this.catchupFastSeekPeriod);
345
- const startTime = time - seekPeriod;
346
- this._player.currentTime = startTime;
347
- this._player.playbackRate = 4.0;
348
- setTimeout(async () => {
349
- this._player.playbackRate = 1.0;
350
- await suspension.end();
351
- }, (seekPeriod * 0.25) * 1000)
352
- } else {
353
- this._player.currentTime = time;
354
- }
422
+ this._player.currentTime = time;
355
423
  }
356
424
  }
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Copyright (c) Microsoft Corporation. All rights reserved.
3
- * Licensed under the MIT License.
3
+ * Licensed under the Microsoft Live Share SDK License.
4
4
  */
5
5
 
6
6
  import { ExtendedMediaSessionActionDetails } from './MediaSessionExtensions';
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Copyright (c) Microsoft Corporation. All rights reserved.
3
- * Licensed under the MIT License.
3
+ * Licensed under the Microsoft Live Share SDK License.
4
4
  */
5
5
 
6
6
  export interface MediaSessionCoordinatorSuspension {
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Copyright (c) Microsoft Corporation. All rights reserved.
3
- * Licensed under the MIT License.
3
+ * Licensed under the Microsoft Live Share SDK License.
4
4
  */
5
5
 
6
6
  import { TimeInterval } from '@microsoft/live-share';
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Copyright (c) Microsoft Corporation. All rights reserved.
3
- * Licensed under the MIT License.
3
+ * Licensed under the Microsoft Live Share SDK License.
4
4
  */
5
5
 
6
6
  import { TimeInterval } from '@microsoft/live-share';
@@ -33,7 +33,7 @@ export class VolumeLimiter {
33
33
  * Time, in seconds, that the volume should be lowered.
34
34
  *
35
35
  * @remarks
36
- * Applications should periodically call `lowerVolume()` to keep teh volume lowered.
36
+ * Applications should periodically call `lowerVolume()` to keep the volume lowered.
37
37
  */
38
38
  public get duration(): number {
39
39
  return this._duration.seconds;
package/src/index.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Copyright (c) Microsoft Corporation. All rights reserved.
3
- * Licensed under the MIT License.
3
+ * Licensed under the Microsoft Live Share SDK License.
4
4
  */
5
5
 
6
6
 
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Copyright (c) Microsoft Corporation. All rights reserved.
3
- * Licensed under the MIT License.
3
+ * Licensed under the Microsoft Live Share SDK License.
4
4
  */
5
5
 
6
6
  import { IEvent, IEphemeralEvent, EphemeralEvent, TimeInterval, IRuntimeSignaler, EphemeralTelemetryLogger } from '@microsoft/live-share';
@@ -14,6 +14,9 @@ import { GroupTransportStateEvents } from './GroupTransportState';
14
14
  import { GroupPlaybackTrackData, PlaybackTrackDataEvents, IPlaybackTrackData } from './GroupPlaybackTrackData';
15
15
  import { TelemetryEvents } from './consts';
16
16
 
17
+ /**
18
+ * @hidden
19
+ */
17
20
  export interface IPositionUpdateEvent extends IEphemeralEvent {
18
21
  track: IPlaybackTrack;
19
22
  trackData: IPlaybackTrackData;
@@ -23,35 +26,53 @@ export interface IPositionUpdateEvent extends IEphemeralEvent {
23
26
  waitPoint?: CoordinationWaitPoint;
24
27
  }
25
28
 
29
+ /**
30
+ * @hidden
31
+ */
26
32
  export interface ITransportCommandEvent extends IEphemeralEvent {
27
33
  track: IPlaybackTrack;
28
34
  position: number;
29
35
  }
30
36
 
37
+ /**
38
+ * @hidden
39
+ */
31
40
  export interface ISetTrackEvent extends IEphemeralEvent {
32
41
  metadata: ExtendedMediaMetadata|null;
33
42
  waitPoints: CoordinationWaitPoint[];
34
43
  }
35
44
 
45
+ /**
46
+ * @hidden
47
+ */
36
48
  export interface ISetTrackDataEvent extends IEphemeralEvent {
37
49
  data: object|null;
38
50
  }
39
51
 
40
- export interface ITriggerActionEvent extends IEvent {
52
+ /**
53
+ * @hidden
54
+ */
55
+ export interface ITriggerActionEvent extends IEvent {
41
56
  details: ExtendedMediaSessionActionDetails;
42
57
  }
43
58
 
59
+ /**
60
+ * @hidden
61
+ */
44
62
  export enum GroupCoordinatorStateEvents {
45
63
  newwaitpoint = 'newwaitpoint',
46
64
  triggeraction = 'triggeraction'
47
65
  }
48
66
 
49
- export class GroupCoordinatorState extends EventEmitter {
50
- private readonly _runtime: IRuntimeSignaler;
67
+ /**
68
+ * @hidden
69
+ */
70
+ export class GroupCoordinatorState extends EventEmitter {
71
+ private readonly _runtime: IRuntimeSignaler;
51
72
  private readonly _logger: EphemeralTelemetryLogger;
52
73
  private readonly _maxPlaybackDrift: TimeInterval;
53
74
  private _getMediaPlayerState: () => IMediaPlayerState;
54
-
75
+
55
76
  // Shared group state
56
77
  private _playbackTrack: GroupPlaybackTrack;
57
78
  private _playbackTrackData: GroupPlaybackTrackData;
@@ -65,7 +86,7 @@ export class GroupCoordinatorState extends EventEmitter {
65
86
  // "Soft Suspension" tracking
66
87
  // - Soft suspensions temporarily disconnect the local media session when transitioning between
67
88
  // paused, playing, and none states. This is to give the local client time to finish any inprogress
68
- // operations like sending a transport command or ending a suspension.
89
+ // operations like sending a transport command or ending a suspension.
69
90
  private _lastStateChange: ExtendedMediaSessionPlaybackState = 'none';
70
91
  private _lastStateChangeTime: number = 0;
71
92
 
@@ -161,13 +182,13 @@ export class GroupCoordinatorState extends EventEmitter {
161
182
 
162
183
  public beginSuspension(waitPoint?: CoordinationWaitPoint): void {
163
184
  this._suspensionCnt++;
164
-
185
+
165
186
  if (waitPoint) {
166
187
  // Save most recent wait point
167
188
  this._waitPoint = waitPoint;
168
189
 
169
190
  // Add to track state if dynamic wait point
170
- this.playbackTrack.addWaitPoint(waitPoint);
191
+ this.playbackTrack.addWaitPoint(waitPoint);
171
192
  }
172
193
  }
173
194
 
@@ -204,7 +225,7 @@ export class GroupCoordinatorState extends EventEmitter {
204
225
  playbackState: this._waitPoint ? 'waiting' : playbackState,
205
226
  position: positionState?.position || 0.0,
206
227
  waitPoint: this._waitPoint
207
- };
228
+ };
208
229
  }
209
230
  }
210
231
 
@@ -251,7 +272,7 @@ export class GroupCoordinatorState extends EventEmitter {
251
272
  case 'pause':
252
273
  playbackState = 'paused';
253
274
  break;
254
- }
275
+ }
255
276
 
256
277
  // Try to update playback state
257
278
  const newState: ITransportState = {
@@ -312,7 +333,7 @@ export class GroupCoordinatorState extends EventEmitter {
312
333
  // Begin a "soft suspension" on select state changes
313
334
  // - This is needed because the catchup logic can try to sink the client with
314
335
  // the rest of the group while the local player is trying to to seek to a
315
- // new position.
336
+ // new position.
316
337
  switch (event.playbackState) {
317
338
  case 'none':
318
339
  case 'paused':
@@ -349,7 +370,7 @@ export class GroupCoordinatorState extends EventEmitter {
349
370
  if (!this.isWaiting && softSuspensionDelta >= 1000) {
350
371
  let { metadata, trackData, positionState, playbackState } = this._getMediaPlayerState();
351
372
  this._logger.sendTelemetryEvent(TelemetryEvents.GroupCoordinator.CheckingForSyncIssues);
352
-
373
+
353
374
  // Check for track change
354
375
  if (!this.playbackTrack.compare(metadata)) {
355
376
  this._logger.sendTelemetryEvent(TelemetryEvents.GroupCoordinator.TrackOutOfSync);
@@ -359,7 +380,7 @@ export class GroupCoordinatorState extends EventEmitter {
359
380
 
360
381
  // Once local playback has ended there's no need to further sync the client. In fact
361
382
  // doing so could cause the player to loop.
362
- // - The playbackPosition.trackEnded check is to catch late joiners and prevents syncing if everyone
383
+ // - The playbackPosition.trackEnded check is to catch late joiners and prevents syncing if everyone
363
384
  // else has finished the video.
364
385
  // - Should another user seek or press play after the video has ended that will cause
365
386
  // a transport action to trigger which will take the player out of the ended state.
@@ -374,18 +395,18 @@ export class GroupCoordinatorState extends EventEmitter {
374
395
  // - Target can return -1 in cases where there is no position tracking data
375
396
  const target = this.playbackPosition.targetPosition;
376
397
  if (target >= 0.0 && Math.abs(target - position) >= this._maxPlaybackDrift.seconds) {
377
- this._logger.sendTelemetryEvent(TelemetryEvents.GroupCoordinator.PositionOutOfSync, null, {
378
- current: position,
379
- target: target
398
+ this._logger.sendTelemetryEvent(TelemetryEvents.GroupCoordinator.PositionOutOfSync, null, {
399
+ current: position,
400
+ target: target
380
401
  });
381
402
  await this.emitTriggerAction({action: 'catchup', seekTime: target});
382
403
  }
383
404
 
384
405
  // Sync transport state
385
406
  if (this.transportState.playbackState != playbackState) {
386
- this._logger.sendTelemetryEvent(TelemetryEvents.GroupCoordinator.TransportOutOfSync, null, {
407
+ this._logger.sendTelemetryEvent(TelemetryEvents.GroupCoordinator.TransportOutOfSync, null, {
387
408
  current: playbackState,
388
- target: this.transportState.playbackState
409
+ target: this.transportState.playbackState
389
410
  });
390
411
  switch (this.transportState.playbackState) {
391
412
  case 'playing':
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Copyright (c) Microsoft Corporation. All rights reserved.
3
- * Licensed under the MIT License.
3
+ * Licensed under the Microsoft Live Share SDK License.
4
4
  */
5
5
 
6
6
  import { EphemeralEvent, IRuntimeSignaler, TimeInterval } from '@microsoft/live-share';
@@ -8,7 +8,10 @@ import { GroupTransportState } from './GroupTransportState';
8
8
  import { GroupPlaybackTrackEvents } from './GroupPlaybackTrack';
9
9
  import { CoordinationWaitPoint, ExtendedMediaSessionPlaybackState } from '../MediaSessionExtensions';
10
10
 
11
- // Per client position
11
+ /**
12
+ *Per client position
13
+ * @hidden
14
+ */
12
15
  export interface ICurrentPlaybackPosition {
13
16
  playbackState: ExtendedMediaSessionPlaybackState;
14
17
  waitPoint?: CoordinationWaitPoint
@@ -18,13 +21,16 @@ export interface ICurrentPlaybackPosition {
18
21
  clientId: string;
19
22
  }
20
23
 
24
+ /**
25
+ * @hidden
26
+ */
21
27
  export class GroupPlaybackPosition {
22
28
  private _transportState: GroupTransportState;
23
29
  private _runtime: IRuntimeSignaler;
24
30
  private _updateInterval: TimeInterval;
25
31
  private _positions: Map<string, ICurrentPlaybackPosition>;
26
32
 
27
-
33
+
28
34
  constructor(transportState: GroupTransportState, runtime: IRuntimeSignaler, updateInterval: TimeInterval) {
29
35
  this._transportState = transportState;
30
36
  this._runtime = runtime;
@@ -67,14 +73,14 @@ export class GroupPlaybackPosition {
67
73
  case 'ended':
68
74
  // not playing.
69
75
  break;
70
- default:
76
+ default:
71
77
  playing++;
72
78
  break;
73
79
  }
74
80
  });
75
81
 
76
82
  return playing == 0;
77
- }
83
+ }
78
84
 
79
85
  /**
80
86
  * Returns the number of clients we're waiting for before we can stop waiting.
@@ -93,13 +99,13 @@ export class GroupPlaybackPosition {
93
99
  }
94
100
 
95
101
  return cnt;
96
- }
102
+ }
97
103
 
98
104
  /**
99
105
  * Returns the max playback position relative to the start position.
100
- *
106
+ *
101
107
  * @remarks
102
- * This is called when calculating the current seekTo position.
108
+ * This is called when calculating the current seekTo position.
103
109
  */
104
110
  public get maxPosition(): number {
105
111
  if (this._transportState.playbackState == 'playing') {
@@ -151,7 +157,7 @@ export class GroupPlaybackPosition {
151
157
  if (position.duration != undefined) {
152
158
  this.mediaDuration = position.duration;
153
159
  }
154
-
160
+
155
161
  // Save last position
156
162
  if (this._positions.has(position.clientId)) {
157
163
  // Only update if newer position
@@ -166,7 +172,7 @@ export class GroupPlaybackPosition {
166
172
 
167
173
  private getMostProgressedPosition(): number {
168
174
  // Compute the max possible position for the current transport state.
169
- // - This is needed to properly handle seeking backwards in time. Some playback heads may
175
+ // - This is needed to properly handle seeking backwards in time. Some playback heads may
170
176
  // not have performed their seek yet and will therefore be ahead of the local player.
171
177
  const maxPosition = this.maxPosition;
172
178
 
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Copyright (c) Microsoft Corporation. All rights reserved.
3
- * Licensed under the MIT License.
3
+ * Licensed under the Microsoft Live Share SDK License.
4
4
  */
5
5
 
6
6
  import { IEvent } from '@microsoft/live-share';
@@ -8,22 +8,35 @@ import EventEmitter from 'events';
8
8
  import { IMediaPlayerState } from '../EphemeralMediaSessionCoordinator';
9
9
  import { CoordinationWaitPoint, ExtendedMediaMetadata } from '../MediaSessionExtensions';
10
10
 
11
- export interface IPlaybackTrack {
11
+ /**
12
+ * @hidden
13
+ */
14
+ export interface IPlaybackTrack {
12
15
  metadata: ExtendedMediaMetadata|null;
13
16
  waitPoints: CoordinationWaitPoint[];
14
17
  timestamp: number;
15
18
  clientId: string;
16
19
  }
17
20
 
18
- export enum GroupPlaybackTrackEvents {
21
+
22
+ /**
23
+ * @hidden
24
+ */
25
+ export enum GroupPlaybackTrackEvents {
19
26
  trackChange = 'trackChange'
20
27
  }
21
28
 
22
- export interface IPlaybackTrackChangeEvent extends IEvent {
29
+ /**
30
+ * @hidden
31
+ */
32
+ export interface IPlaybackTrackChangeEvent extends IEvent {
23
33
  metadata: ExtendedMediaMetadata|null;
24
34
  }
25
35
 
26
- export class GroupPlaybackTrack extends EventEmitter {
36
+ /**
37
+ * @hidden
38
+ */
39
+ export class GroupPlaybackTrack extends EventEmitter {
27
40
  private readonly _getMediaPlayerState: () => IMediaPlayerState;
28
41
  private _current: IPlaybackTrack;
29
42
 
@@ -95,9 +108,9 @@ export class GroupPlaybackTrack extends EventEmitter {
95
108
  if (track.timestamp == current.timestamp && track.clientId == current.clientId) {
96
109
  return false;
97
110
  }
98
- }
111
+ }
99
112
 
100
- // Ignore state changes that are older
113
+ // Ignore state changes that are older
101
114
  if (track.timestamp < current.timestamp) {
102
115
  return false;
103
116
  }
@@ -112,8 +125,8 @@ export class GroupPlaybackTrack extends EventEmitter {
112
125
  this._current = track;
113
126
 
114
127
  // Notify listeners
115
- this.emit(GroupPlaybackTrackEvents.trackChange, { name: GroupPlaybackTrackEvents.trackChange, metadata: track.metadata });
116
-
128
+ this.emit(GroupPlaybackTrackEvents.trackChange, { name: GroupPlaybackTrackEvents.trackChange, metadata: track.metadata });
129
+
117
130
  return true;
118
131
  }
119
132