@microsoft/live-share-media 1.0.1 → 1.1.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 (44) hide show
  1. package/bin/LiveMediaSession.d.ts +2 -2
  2. package/bin/LiveMediaSession.d.ts.map +1 -1
  3. package/bin/LiveMediaSession.js +10 -4
  4. package/bin/LiveMediaSession.js.map +1 -1
  5. package/bin/LiveMediaSessionCoordinator.d.ts +8 -3
  6. package/bin/LiveMediaSessionCoordinator.d.ts.map +1 -1
  7. package/bin/LiveMediaSessionCoordinator.js +35 -52
  8. package/bin/LiveMediaSessionCoordinator.js.map +1 -1
  9. package/bin/MediaPlayerSynchronizer.d.ts +3 -2
  10. package/bin/MediaPlayerSynchronizer.d.ts.map +1 -1
  11. package/bin/MediaPlayerSynchronizer.js +49 -37
  12. package/bin/MediaPlayerSynchronizer.js.map +1 -1
  13. package/bin/MediaSessionActionThrottler.d.ts +2 -2
  14. package/bin/MediaSessionActionThrottler.d.ts.map +1 -1
  15. package/bin/MediaSessionActionThrottler.js.map +1 -1
  16. package/bin/MediaSessionExtensions.d.ts +4 -0
  17. package/bin/MediaSessionExtensions.d.ts.map +1 -1
  18. package/bin/RepeatedActionThrottler.d.ts +1 -1
  19. package/bin/RepeatedActionThrottler.d.ts.map +1 -1
  20. package/bin/RepeatedActionThrottler.js.map +1 -1
  21. package/bin/internals/GroupCoordinatorState.d.ts +1 -1
  22. package/bin/internals/GroupCoordinatorState.d.ts.map +1 -1
  23. package/bin/internals/GroupCoordinatorState.js +23 -6
  24. package/bin/internals/GroupCoordinatorState.js.map +1 -1
  25. package/bin/internals/GroupPlaybackTrack.d.ts.map +1 -1
  26. package/bin/internals/GroupPlaybackTrack.js +1 -0
  27. package/bin/internals/GroupPlaybackTrack.js.map +1 -1
  28. package/bin/internals/GroupPlaybackTrackData.d.ts.map +1 -1
  29. package/bin/internals/GroupPlaybackTrackData.js +1 -0
  30. package/bin/internals/GroupPlaybackTrackData.js.map +1 -1
  31. package/bin/internals/GroupTransportState.d.ts.map +1 -1
  32. package/bin/internals/GroupTransportState.js +3 -0
  33. package/bin/internals/GroupTransportState.js.map +1 -1
  34. package/package.json +4 -4
  35. package/src/LiveMediaSession.ts +20 -7
  36. package/src/LiveMediaSessionCoordinator.ts +39 -61
  37. package/src/MediaPlayerSynchronizer.ts +54 -44
  38. package/src/MediaSessionActionThrottler.ts +6 -3
  39. package/src/MediaSessionExtensions.ts +5 -0
  40. package/src/RepeatedActionThrottler.ts +4 -2
  41. package/src/internals/GroupCoordinatorState.ts +30 -7
  42. package/src/internals/GroupPlaybackTrack.ts +1 -0
  43. package/src/internals/GroupPlaybackTrackData.ts +1 -0
  44. package/src/internals/GroupTransportState.ts +4 -5
@@ -2,7 +2,11 @@
2
2
  * Copyright (c) Microsoft Corporation. All rights reserved.
3
3
  * Licensed under the Microsoft Live Share SDK License.
4
4
  */
5
- import { LiveTelemetryLogger, ILiveEvent } from "@microsoft/live-share";
5
+ import {
6
+ LiveTelemetryLogger,
7
+ ILiveEvent,
8
+ IRuntimeSignaler,
9
+ } from "@microsoft/live-share";
6
10
  import EventEmitter from "events";
7
11
  import {
8
12
  ExtendedMediaSessionAction,
@@ -17,6 +21,7 @@ import { VolumeManager } from "./VolumeManager";
17
21
  import { LiveMediaSession } from "./LiveMediaSession";
18
22
  import { IMediaPlayer } from "./IMediaPlayer";
19
23
  import { TelemetryEvents } from "./internals";
24
+ import { waitUntilConnected } from "@microsoft/live-share/bin/internals";
20
25
 
21
26
  /**
22
27
  * Event data returned by `MediaPlayerSynchronizer` object.
@@ -76,6 +81,7 @@ export class MediaPlayerSynchronizer extends EventEmitter {
76
81
  private _logger: LiveTelemetryLogger;
77
82
  private _player: IMediaPlayer;
78
83
  private _mediaSession: LiveMediaSession;
84
+ private _runtime: IRuntimeSignaler;
79
85
  private _volumeManager: VolumeManager;
80
86
  private _onEnd?: () => void;
81
87
  private _onPlayerEvent: EventListener;
@@ -94,11 +100,13 @@ export class MediaPlayerSynchronizer extends EventEmitter {
94
100
  constructor(
95
101
  player: IMediaPlayer,
96
102
  mediaSession: LiveMediaSession,
103
+ runtime: IRuntimeSignaler,
97
104
  onEnd: () => void
98
105
  ) {
99
106
  super();
100
107
  this._player = player;
101
108
  this._mediaSession = mediaSession;
109
+ this._runtime = runtime;
102
110
  this._logger = mediaSession.logger;
103
111
  this._volumeManager = new VolumeManager(player);
104
112
  this._onEnd = onEnd;
@@ -204,7 +212,9 @@ export class MediaPlayerSynchronizer extends EventEmitter {
204
212
  // needed because cannot tell if its a user initiated event, so disallow pause
205
213
  if (
206
214
  this._expectedPlaybackState === "playing" &&
207
- !this._mediaSession.coordinator.isSuspended
215
+ !this._mediaSession.coordinator.isSuspended &&
216
+ // some players have ended, but emit a pause event immediately before ended event. Do not play if ended
217
+ !this._player.ended
208
218
  ) {
209
219
  this._player.play();
210
220
  }
@@ -350,7 +360,7 @@ export class MediaPlayerSynchronizer extends EventEmitter {
350
360
  }
351
361
  }
352
362
 
353
- this.dispatchGroupAction(details, false, error);
363
+ this.dispatchGroupAction(details, error);
354
364
  }
355
365
  );
356
366
  }
@@ -511,7 +521,13 @@ export class MediaPlayerSynchronizer extends EventEmitter {
511
521
  );
512
522
  suspension.end(seekTo);
513
523
 
514
- this.dispatchUserAction({ action: "seekto", seekTime: seekTo });
524
+ waitUntilConnected(this._runtime).then((clientId: string) => {
525
+ this.dispatchUserAction({
526
+ action: "seekto",
527
+ clientId,
528
+ seekTime: seekTo,
529
+ });
530
+ });
515
531
  }
516
532
 
517
533
  /**
@@ -530,7 +546,10 @@ export class MediaPlayerSynchronizer extends EventEmitter {
530
546
  this._expectedPlaybackState = "playing";
531
547
  await this._mediaSession.coordinator.play();
532
548
 
533
- this.dispatchUserAction({ action: "play" });
549
+ this.dispatchUserAction({
550
+ action: "play",
551
+ clientId: await waitUntilConnected(this._runtime),
552
+ });
534
553
  }
535
554
 
536
555
  /**
@@ -549,7 +568,10 @@ export class MediaPlayerSynchronizer extends EventEmitter {
549
568
  this._expectedPlaybackState = "paused";
550
569
  await this._mediaSession.coordinator.pause();
551
570
 
552
- this.dispatchUserAction({ action: "pause" });
571
+ this.dispatchUserAction({
572
+ action: "pause",
573
+ clientId: await waitUntilConnected(this._runtime),
574
+ });
553
575
  }
554
576
 
555
577
  /**
@@ -574,7 +596,11 @@ export class MediaPlayerSynchronizer extends EventEmitter {
574
596
  );
575
597
  await this._mediaSession.coordinator.seekTo(time);
576
598
 
577
- this.dispatchUserAction({ action: "seekto", seekTime: time });
599
+ this.dispatchUserAction({
600
+ action: "seekto",
601
+ clientId: await waitUntilConnected(this._runtime),
602
+ seekTime: time,
603
+ });
578
604
  }
579
605
 
580
606
  /**
@@ -595,7 +621,11 @@ export class MediaPlayerSynchronizer extends EventEmitter {
595
621
  );
596
622
  await this._mediaSession.coordinator.setTrack(track, waitPoints);
597
623
 
598
- this.dispatchUserAction({ action: "settrack", metadata: track });
624
+ this.dispatchUserAction({
625
+ action: "settrack",
626
+ clientId: await waitUntilConnected(this._runtime),
627
+ metadata: track,
628
+ });
599
629
  }
600
630
 
601
631
  /**
@@ -614,52 +644,32 @@ export class MediaPlayerSynchronizer extends EventEmitter {
614
644
  this._trackData = data;
615
645
  await this._mediaSession.coordinator.setTrackData(data);
616
646
 
617
- this.dispatchUserAction({ action: "datachange", data: data });
647
+ this.dispatchUserAction({
648
+ action: "datachange",
649
+ clientId: await waitUntilConnected(this._runtime),
650
+ data: data,
651
+ });
618
652
  }
619
653
 
654
+ // TODO: what is delay useful for? we do not use, delete?
620
655
  private dispatchGroupAction(
621
656
  details: ExtendedMediaSessionActionDetails,
622
- delay = false,
623
657
  error: Error | undefined
624
658
  ): void {
625
- if (delay) {
626
- setTimeout(
627
- () =>
628
- this.emit(MediaPlayerSynchronizerEvents.groupaction, {
629
- type: MediaPlayerSynchronizerEvents.groupaction,
630
- details: details,
631
- playerError: error,
632
- }),
633
- 50
634
- );
635
- } else {
636
- this.emit(MediaPlayerSynchronizerEvents.groupaction, {
637
- type: MediaPlayerSynchronizerEvents.groupaction,
638
- details: details,
639
- error,
640
- });
641
- }
659
+ this.emit(MediaPlayerSynchronizerEvents.groupaction, {
660
+ type: MediaPlayerSynchronizerEvents.groupaction,
661
+ details: details,
662
+ error,
663
+ });
642
664
  }
643
665
 
644
666
  private dispatchUserAction(
645
- details: ExtendedMediaSessionActionDetails,
646
- delay = false
667
+ details: ExtendedMediaSessionActionDetails
647
668
  ): void {
648
- if (delay) {
649
- setTimeout(
650
- () =>
651
- this.emit(MediaPlayerSynchronizerEvents.useraction, {
652
- type: MediaPlayerSynchronizerEvents.useraction,
653
- details: details,
654
- }),
655
- 50
656
- );
657
- } else {
658
- this.emit(MediaPlayerSynchronizerEvents.useraction, {
659
- type: MediaPlayerSynchronizerEvents.useraction,
660
- details: details,
661
- });
662
- }
669
+ this.emit(MediaPlayerSynchronizerEvents.useraction, {
670
+ type: MediaPlayerSynchronizerEvents.useraction,
671
+ details: details,
672
+ });
663
673
  }
664
674
 
665
675
  private async catchupPlayer(time: number): Promise<void> {
@@ -3,14 +3,17 @@
3
3
  * Licensed under the Microsoft Live Share SDK License.
4
4
  */
5
5
 
6
- import { ExtendedMediaSessionActionDetails } from "./MediaSessionExtensions";
6
+ import {
7
+ ExtendedMediaSessionActionDetails,
8
+ ExtendedMediaSessionActionHandler,
9
+ } from "./MediaSessionExtensions";
7
10
 
8
11
  /**
9
12
  * Base class for action throttlers.
10
13
  */
11
14
  export abstract class MediaSessionActionThrottler {
12
15
  public abstract throttled(
13
- details: ExtendedMediaSessionActionDetails,
14
- handler?: MediaSessionActionHandler
16
+ details: MediaSessionActionDetails | ExtendedMediaSessionActionDetails,
17
+ handler?: MediaSessionActionHandler | ExtendedMediaSessionActionHandler
15
18
  ): void;
16
19
  }
@@ -42,6 +42,7 @@ export interface CoordinationWaitPoint {
42
42
 
43
43
  export interface ExtendedMediaSessionActionDetails {
44
44
  action: ExtendedMediaSessionAction;
45
+ clientId: string;
45
46
  fastSeek?: boolean | null;
46
47
  seekOffset?: number | null;
47
48
  seekTime?: number | null;
@@ -50,3 +51,7 @@ export interface ExtendedMediaSessionActionDetails {
50
51
  data?: object | null;
51
52
  blocked?: ExtendedMediaSessionAction | null;
52
53
  }
54
+
55
+ export interface ExtendedMediaSessionActionHandler {
56
+ (details: ExtendedMediaSessionActionDetails): void;
57
+ }
@@ -33,7 +33,7 @@ export class RepeatedActionThrottler extends MediaSessionActionThrottler {
33
33
  }
34
34
 
35
35
  public throttled(
36
- details: ExtendedMediaSessionActionDetails,
36
+ details: MediaSessionActionDetails | ExtendedMediaSessionActionDetails,
37
37
  handler?: MediaSessionActionHandler
38
38
  ): void {
39
39
  if (handler) {
@@ -63,7 +63,9 @@ export class RepeatedActionThrottler extends MediaSessionActionThrottler {
63
63
  }
64
64
  }
65
65
 
66
- private getChangeKey(details: ExtendedMediaSessionActionDetails): string {
66
+ private getChangeKey(
67
+ details: MediaSessionActionDetails | ExtendedMediaSessionActionDetails
68
+ ): string {
67
69
  switch (details.action) {
68
70
  case "seekto":
69
71
  return `seekto:${details.seekTime}`;
@@ -36,6 +36,7 @@ import {
36
36
  IPlaybackTrackData,
37
37
  } from "./GroupPlaybackTrackData";
38
38
  import { TelemetryEvents } from "./consts";
39
+ import { waitUntilConnected } from "@microsoft/live-share/bin/internals";
39
40
 
40
41
  /**
41
42
  * @hidden
@@ -149,7 +150,7 @@ export class GroupCoordinatorState extends EventEmitter {
149
150
  this._logger.sendTelemetryEvent(
150
151
  TelemetryEvents.GroupCoordinator.TrackChanged
151
152
  );
152
- this.emitSetTrack(evt.metadata!);
153
+ this.emitSetTrack(evt.clientId, evt.metadata!);
153
154
  } else {
154
155
  this._logger.sendTelemetryEvent(
155
156
  TelemetryEvents.GroupCoordinator.TrackChangeDelayed
@@ -168,6 +169,7 @@ export class GroupCoordinatorState extends EventEmitter {
168
169
  this.emitTriggerAction({
169
170
  action: "datachange",
170
171
  data: evt.data,
172
+ clientId: evt.clientId,
171
173
  });
172
174
  } else {
173
175
  this._logger.sendTelemetryEvent(
@@ -196,6 +198,7 @@ export class GroupCoordinatorState extends EventEmitter {
196
198
  case "play":
197
199
  this.emitTriggerAction({
198
200
  action: "play",
201
+ clientId: evt.clientId,
199
202
  seekTime: evt.seekTime,
200
203
  });
201
204
  break;
@@ -203,6 +206,7 @@ export class GroupCoordinatorState extends EventEmitter {
203
206
  case "pause":
204
207
  this.emitTriggerAction({
205
208
  action: "pause",
209
+ clientId: evt.clientId,
206
210
  seekTime: evt.seekTime,
207
211
  });
208
212
  break;
@@ -210,6 +214,7 @@ export class GroupCoordinatorState extends EventEmitter {
210
214
  case "seekto":
211
215
  this.emitTriggerAction({
212
216
  action: "seekto",
217
+ clientId: evt.clientId,
213
218
  seekTime: evt.seekTime,
214
219
  });
215
220
  break;
@@ -431,7 +436,7 @@ export class GroupCoordinatorState extends EventEmitter {
431
436
  }
432
437
 
433
438
  public handlePositionUpdate(
434
- event: Omit<ILiveEvent<IPositionUpdateEvent>, "name">,
439
+ event: ILiveEvent<IPositionUpdateEvent>,
435
440
  local: boolean
436
441
  ): void {
437
442
  // Ensure change is for current track
@@ -522,7 +527,10 @@ export class GroupCoordinatorState extends EventEmitter {
522
527
  this._logger.sendTelemetryEvent(
523
528
  TelemetryEvents.GroupCoordinator.TrackOutOfSync
524
529
  );
525
- this.emitSetTrack(this.playbackTrack.metadata!);
530
+ this.emitSetTrack(
531
+ this.playbackTrack.current.clientId,
532
+ this.playbackTrack.metadata!
533
+ );
526
534
  return;
527
535
  }
528
536
 
@@ -560,6 +568,7 @@ export class GroupCoordinatorState extends EventEmitter {
560
568
  );
561
569
  await this.emitTriggerAction({
562
570
  action: "catchup",
571
+ clientId: await waitUntilConnected(this._runtime),
563
572
  seekTime: target,
564
573
  });
565
574
  }
@@ -576,10 +585,16 @@ export class GroupCoordinatorState extends EventEmitter {
576
585
  );
577
586
  switch (this.transportState.playbackState) {
578
587
  case "playing":
579
- await this.emitTriggerAction({ action: "play" });
588
+ await this.emitTriggerAction({
589
+ action: "play",
590
+ clientId: this.transportState.current.clientId,
591
+ });
580
592
  break;
581
593
  case "paused":
582
- await this.emitTriggerAction({ action: "pause" });
594
+ await this.emitTriggerAction({
595
+ action: "pause",
596
+ clientId: this.transportState.current.clientId,
597
+ });
583
598
  break;
584
599
  }
585
600
  }
@@ -594,6 +609,7 @@ export class GroupCoordinatorState extends EventEmitter {
594
609
  );
595
610
  await this.emitTriggerAction({
596
611
  action: "datachange",
612
+ clientId: this.playbackTrackData.current.clientId,
597
613
  data: this.playbackTrackData.data,
598
614
  });
599
615
  }
@@ -601,12 +617,19 @@ export class GroupCoordinatorState extends EventEmitter {
601
617
  }
602
618
  }
603
619
 
604
- private emitSetTrack(metadata: ExtendedMediaMetadata): void {
620
+ private emitSetTrack(
621
+ clientId: string,
622
+ metadata: ExtendedMediaMetadata
623
+ ): void {
605
624
  // Reset tracking states
606
625
  this._waitPoint = undefined;
607
626
 
608
627
  // Trigger settrack action
609
- this.emitTriggerAction({ action: "settrack", metadata: metadata });
628
+ this.emitTriggerAction({
629
+ action: "settrack",
630
+ clientId,
631
+ metadata,
632
+ });
610
633
  }
611
634
 
612
635
  private emitTriggerAction(
@@ -147,6 +147,7 @@ export class GroupPlaybackTrack extends EventEmitter {
147
147
  // Notify listeners
148
148
  this.emit(GroupPlaybackTrackEvents.trackChange, {
149
149
  name: GroupPlaybackTrackEvents.trackChange,
150
+ clientId: track.clientId,
150
151
  metadata: track.metadata,
151
152
  });
152
153
 
@@ -90,6 +90,7 @@ export class GroupPlaybackTrackData extends EventEmitter {
90
90
  // Notify listeners
91
91
  this.emit(PlaybackTrackDataEvents.dataChange, {
92
92
  type: PlaybackTrackDataEvents.dataChange,
93
+ clientId: event.clientId,
93
94
  data: event.data,
94
95
  });
95
96
 
@@ -3,11 +3,7 @@
3
3
  * Licensed under the Microsoft Live Share SDK License.
4
4
  */
5
5
 
6
- import {
7
- ILiveEvent,
8
- LiveShareRuntime,
9
- LiveShareClient,
10
- } from "@microsoft/live-share";
6
+ import { ILiveEvent, LiveShareRuntime } from "@microsoft/live-share";
11
7
  import EventEmitter from "events";
12
8
  import { IMediaPlayerState } from "../LiveMediaSessionCoordinator";
13
9
  import {
@@ -140,6 +136,7 @@ export class GroupTransportState extends EventEmitter {
140
136
  this.emit(GroupTransportStateEvents.transportStateChange, {
141
137
  type: GroupTransportStateEvents.transportStateChange,
142
138
  action: "seekto",
139
+ clientId: state.clientId,
143
140
  seekTime: state.startPosition,
144
141
  });
145
142
  } else if (state.playbackState == "playing") {
@@ -149,12 +146,14 @@ export class GroupTransportState extends EventEmitter {
149
146
  this.emit(GroupTransportStateEvents.transportStateChange, {
150
147
  type: GroupTransportStateEvents.transportStateChange,
151
148
  action: "play",
149
+ clientId: state.clientId,
152
150
  seekTime: projectedPosition,
153
151
  });
154
152
  } else {
155
153
  this.emit(GroupTransportStateEvents.transportStateChange, {
156
154
  type: GroupTransportStateEvents.transportStateChange,
157
155
  action: "pause",
156
+ clientId: state.clientId,
158
157
  seekTime: state.startPosition,
159
158
  });
160
159
  }