@microsoft/live-share-media 0.1.2 → 0.2.0

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 (65) hide show
  1. package/.vscode/settings.json +2 -0
  2. package/LICENSE +87 -0
  3. package/README.md +108 -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 +1 -1
  31. package/bin/internals/GroupCoordinatorState.js +1 -1
  32. package/bin/internals/GroupPlaybackPosition.d.ts +1 -1
  33. package/bin/internals/GroupPlaybackPosition.js +1 -1
  34. package/bin/internals/GroupPlaybackTrack.d.ts +1 -1
  35. package/bin/internals/GroupPlaybackTrack.js +1 -1
  36. package/bin/internals/GroupPlaybackTrackData.d.ts +1 -1
  37. package/bin/internals/GroupPlaybackTrackData.js +1 -1
  38. package/bin/internals/GroupTransportState.d.ts +1 -1
  39. package/bin/internals/GroupTransportState.js +1 -1
  40. package/bin/internals/consts.d.ts +1 -1
  41. package/bin/internals/consts.js +1 -1
  42. package/bin/internals/index.d.ts +1 -1
  43. package/bin/internals/index.js +1 -1
  44. package/package.json +13 -4
  45. package/src/EphemeralMediaSession.ts +95 -48
  46. package/src/EphemeralMediaSessionCoordinator.ts +190 -26
  47. package/src/EphemeralMediaSessionCoordinatorSuspension.ts +1 -1
  48. package/src/IMediaPlayer.ts +1 -1
  49. package/src/MediaPlayerSynchronizer.ts +101 -33
  50. package/src/MediaSessionActionThrottler.ts +1 -1
  51. package/src/MediaSessionExtensions.ts +1 -1
  52. package/src/RepeatedActionThrottler.ts +1 -1
  53. package/src/VolumeLimiter.ts +2 -2
  54. package/src/index.ts +1 -1
  55. package/src/internals/GroupCoordinatorState.ts +1 -1
  56. package/src/internals/GroupPlaybackPosition.ts +1 -1
  57. package/src/internals/GroupPlaybackTrack.ts +1 -1
  58. package/src/internals/GroupPlaybackTrackData.ts +1 -1
  59. package/src/internals/GroupTransportState.ts +1 -1
  60. package/src/internals/consts.ts +1 -1
  61. package/src/internals/index.ts +1 -1
  62. package/src/test/GroupPlaybackPosition.spec.ts +1 -1
  63. package/src/test/GroupPlaybackTrack.spec.ts +1 -1
  64. package/src/test/SharedMediaSession.spec.ts +1 -1
  65. 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';
@@ -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';
@@ -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';
@@ -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';
@@ -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, EphemeralEvent } 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
  /**
@@ -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 * from './consts';
@@ -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 'mocha';
@@ -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 'mocha';
@@ -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 'mocha';
@@ -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 'mocha';