@microsoft/live-share-media 1.3.1 → 2.0.0-internal.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.
- package/bin/LiveMediaSession.d.ts.map +1 -1
- package/bin/LiveMediaSession.js +9 -1
- package/bin/LiveMediaSession.js.map +1 -1
- package/bin/LiveMediaSessionCoordinator.d.ts +15 -0
- package/bin/LiveMediaSessionCoordinator.d.ts.map +1 -1
- package/bin/LiveMediaSessionCoordinator.js +25 -0
- package/bin/LiveMediaSessionCoordinator.js.map +1 -1
- package/bin/MediaPlayerSynchronizer.d.ts +6 -4
- package/bin/MediaPlayerSynchronizer.d.ts.map +1 -1
- package/bin/MediaPlayerSynchronizer.js +43 -14
- package/bin/MediaPlayerSynchronizer.js.map +1 -1
- package/bin/MediaSessionExtensions.d.ts +17 -4
- package/bin/MediaSessionExtensions.d.ts.map +1 -1
- package/bin/MediaSessionExtensions.js +5 -1
- package/bin/MediaSessionExtensions.js.map +1 -1
- package/bin/RepeatedActionThrottler.js +1 -1
- package/bin/RepeatedActionThrottler.js.map +1 -1
- package/bin/VolumeManager.js +1 -1
- package/bin/VolumeManager.js.map +1 -1
- package/bin/internals/GroupCoordinatorState.d.ts +15 -1
- package/bin/internals/GroupCoordinatorState.d.ts.map +1 -1
- package/bin/internals/GroupCoordinatorState.js +124 -58
- package/bin/internals/GroupCoordinatorState.js.map +1 -1
- package/bin/internals/GroupPlaybackPosition.d.ts +3 -1
- package/bin/internals/GroupPlaybackPosition.d.ts.map +1 -1
- package/bin/internals/GroupPlaybackPosition.js +7 -3
- package/bin/internals/GroupPlaybackPosition.js.map +1 -1
- package/bin/internals/GroupPlaybackRate.d.ts +39 -0
- package/bin/internals/GroupPlaybackRate.d.ts.map +1 -0
- package/bin/internals/GroupPlaybackRate.js +62 -0
- package/bin/internals/GroupPlaybackRate.js.map +1 -0
- package/bin/internals/GroupPlaybackTrack.d.ts +2 -2
- package/bin/internals/GroupPlaybackTrack.d.ts.map +1 -1
- package/bin/internals/GroupPlaybackTrack.js +4 -3
- package/bin/internals/GroupPlaybackTrack.js.map +1 -1
- package/bin/internals/GroupPlaybackTrackData.d.ts +3 -3
- package/bin/internals/GroupPlaybackTrackData.d.ts.map +1 -1
- package/bin/internals/GroupPlaybackTrackData.js +13 -12
- package/bin/internals/GroupPlaybackTrackData.js.map +1 -1
- package/bin/internals/GroupTransportState.d.ts +6 -3
- package/bin/internals/GroupTransportState.d.ts.map +1 -1
- package/bin/internals/GroupTransportState.js +17 -12
- package/bin/internals/GroupTransportState.js.map +1 -1
- package/bin/internals/consts.d.ts +8 -1
- package/bin/internals/consts.d.ts.map +1 -1
- package/bin/internals/consts.js +8 -1
- package/bin/internals/consts.js.map +1 -1
- package/bin/internals/index.d.ts +1 -0
- package/bin/internals/index.d.ts.map +1 -1
- package/bin/internals/index.js +1 -0
- package/bin/internals/index.js.map +1 -1
- package/bin/internals/interfaces.d.ts +10 -0
- package/bin/internals/interfaces.d.ts.map +1 -0
- package/bin/internals/interfaces.js +3 -0
- package/bin/internals/interfaces.js.map +1 -0
- package/package.json +6 -9
- package/src/LiveMediaSession.ts +12 -0
- package/src/LiveMediaSessionCoordinator.ts +49 -0
- package/src/MediaPlayerSynchronizer.ts +55 -15
- package/src/MediaSessionExtensions.ts +18 -2
- package/src/internals/GroupCoordinatorState.ts +196 -79
- package/src/internals/GroupPlaybackPosition.ts +9 -2
- package/src/internals/GroupPlaybackRate.ts +90 -0
- package/src/internals/GroupPlaybackTrack.ts +5 -4
- package/src/internals/GroupPlaybackTrackData.ts +14 -13
- package/src/internals/GroupTransportState.ts +21 -12
- package/src/internals/consts.ts +14 -2
- package/src/internals/index.ts +1 -0
- package/src/internals/interfaces.ts +10 -0
|
@@ -33,6 +33,7 @@ import {
|
|
|
33
33
|
ISetTrackDataEvent,
|
|
34
34
|
TrackMetadataNotSetError,
|
|
35
35
|
ActionBlockedError,
|
|
36
|
+
IRateChangeCommandEvent,
|
|
36
37
|
} from "./internals";
|
|
37
38
|
import { LiveMediaSessionCoordinatorSuspension } from "./LiveMediaSessionCoordinatorSuspension";
|
|
38
39
|
import EventEmitter from "events";
|
|
@@ -87,6 +88,7 @@ export class LiveMediaSessionCoordinator extends EventEmitter {
|
|
|
87
88
|
private _playEvent?: LiveEventTarget<ITransportCommandEvent>;
|
|
88
89
|
private _pauseEvent?: LiveEventTarget<ITransportCommandEvent>;
|
|
89
90
|
private _seekToEvent?: LiveEventTarget<ITransportCommandEvent>;
|
|
91
|
+
private _rateChangeEvent?: LiveEventTarget<IRateChangeCommandEvent>;
|
|
90
92
|
private _setTrackEvent?: LiveEventTarget<ISetTrackEvent>;
|
|
91
93
|
private _setTrackDataEvent?: LiveEventTarget<ISetTrackDataEvent>;
|
|
92
94
|
private _positionUpdateEvent?: LiveEventTarget<IPositionUpdateEvent>;
|
|
@@ -170,6 +172,20 @@ export class LiveMediaSessionCoordinator extends EventEmitter {
|
|
|
170
172
|
*/
|
|
171
173
|
public canSetTrackData: boolean = true;
|
|
172
174
|
|
|
175
|
+
/**
|
|
176
|
+
* Controls whether or not the local client is allowed to change the playback rate.
|
|
177
|
+
*
|
|
178
|
+
* @remarks
|
|
179
|
+
* This flag largely meant to influence decisions made by the coordinator and can be used by
|
|
180
|
+
* the UI to determine what controls should be shown to the user. It does not provide any
|
|
181
|
+
* security in itself.
|
|
182
|
+
*
|
|
183
|
+
* If your app is running in a semi-trusted environment where only some clients are allowed
|
|
184
|
+
* to change the tracks data object, you should use "role based verification" to enforce those
|
|
185
|
+
* policies.
|
|
186
|
+
*/
|
|
187
|
+
public canSetPlaybackRate: boolean = true;
|
|
188
|
+
|
|
173
189
|
/**
|
|
174
190
|
* Controls whether or not the local client is allowed to send position updates to the group.
|
|
175
191
|
*
|
|
@@ -346,6 +362,27 @@ export class LiveMediaSessionCoordinator extends EventEmitter {
|
|
|
346
362
|
}
|
|
347
363
|
}
|
|
348
364
|
|
|
365
|
+
public async setPlaybackRate(playbackRate: number): Promise<void> {
|
|
366
|
+
LiveDataObjectNotInitializedError.assert(
|
|
367
|
+
"LiveMediaSessionCoordinator:setPlaybackRate",
|
|
368
|
+
"setPlaybackRate",
|
|
369
|
+
this.initializeState
|
|
370
|
+
);
|
|
371
|
+
ActionBlockedError.assert(
|
|
372
|
+
this.canSetPlaybackRate,
|
|
373
|
+
"LiveMediaSessionCoordinator:setPlaybackRate",
|
|
374
|
+
"setPlaybackRate",
|
|
375
|
+
"canSetPlaybackRate"
|
|
376
|
+
);
|
|
377
|
+
|
|
378
|
+
this._logger.sendTelemetryEvent(
|
|
379
|
+
TelemetryEvents.SessionCoordinator.SetPlaybackRateCalled,
|
|
380
|
+
null,
|
|
381
|
+
{ playbackRate }
|
|
382
|
+
);
|
|
383
|
+
await this._rateChangeEvent!.sendEvent({ playbackRate });
|
|
384
|
+
}
|
|
385
|
+
|
|
349
386
|
/**
|
|
350
387
|
* Instructs the group to load a new track.
|
|
351
388
|
*
|
|
@@ -624,6 +661,11 @@ export class LiveMediaSessionCoordinator extends EventEmitter {
|
|
|
624
661
|
this.emit(evt.name, evt)
|
|
625
662
|
);
|
|
626
663
|
|
|
664
|
+
this._groupState.on(
|
|
665
|
+
GroupCoordinatorStateEvents.triggeractionignored,
|
|
666
|
+
(evt) => this.emit(evt.name, evt)
|
|
667
|
+
);
|
|
668
|
+
|
|
627
669
|
// Listen for track changes
|
|
628
670
|
this._setTrackEvent = new LiveEventTarget<ISetTrackEvent>(
|
|
629
671
|
scope,
|
|
@@ -649,6 +691,13 @@ export class LiveMediaSessionCoordinator extends EventEmitter {
|
|
|
649
691
|
(event, local) =>
|
|
650
692
|
this._groupState!.handleTransportCommand(event, local)
|
|
651
693
|
);
|
|
694
|
+
this._rateChangeEvent = new LiveEventTarget(
|
|
695
|
+
scope,
|
|
696
|
+
"rateChange",
|
|
697
|
+
(event: ILiveEvent<IRateChangeCommandEvent>, local) => {
|
|
698
|
+
this._groupState!.handleRateChangeCommand(event, local);
|
|
699
|
+
}
|
|
700
|
+
);
|
|
652
701
|
|
|
653
702
|
// Listen for position updates
|
|
654
703
|
this._positionUpdateEvent = new LiveEventTarget(
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
import { VolumeManager } from "./VolumeManager";
|
|
21
21
|
import { LiveMediaSession } from "./LiveMediaSession";
|
|
22
22
|
import { IMediaPlayer } from "./IMediaPlayer";
|
|
23
|
-
import { TelemetryEvents } from "./internals";
|
|
23
|
+
import { ITriggerActionEvent, TelemetryEvents } from "./internals";
|
|
24
24
|
import { waitUntilConnected } from "@microsoft/live-share/bin/internals";
|
|
25
25
|
|
|
26
26
|
/**
|
|
@@ -64,6 +64,7 @@ export class MediaPlayerSynchronizer extends EventEmitter {
|
|
|
64
64
|
"pause",
|
|
65
65
|
"seekto",
|
|
66
66
|
"settrack",
|
|
67
|
+
"ratechange",
|
|
67
68
|
"datachange",
|
|
68
69
|
"catchup",
|
|
69
70
|
"wait",
|
|
@@ -89,6 +90,7 @@ export class MediaPlayerSynchronizer extends EventEmitter {
|
|
|
89
90
|
private _viewOnly = false;
|
|
90
91
|
private _blockUnexpectedPlayerEvents = true;
|
|
91
92
|
private _expectedPlaybackState: ExtendedMediaSessionPlaybackState = "none";
|
|
93
|
+
private _expectedPlaybackRate: number = 1.0;
|
|
92
94
|
private _metadata: ExtendedMediaMetadata | null = null;
|
|
93
95
|
private _trackData: object | null = null;
|
|
94
96
|
|
|
@@ -142,7 +144,7 @@ export class MediaPlayerSynchronizer extends EventEmitter {
|
|
|
142
144
|
playbackState: playbackState,
|
|
143
145
|
positionState: {
|
|
144
146
|
position: this._player.currentTime,
|
|
145
|
-
playbackRate: this._player.playbackRate,
|
|
147
|
+
playbackRate: this._player.playbackRate ?? 1,
|
|
146
148
|
duration: this._player.duration,
|
|
147
149
|
},
|
|
148
150
|
trackData: this._trackData,
|
|
@@ -227,22 +229,19 @@ export class MediaPlayerSynchronizer extends EventEmitter {
|
|
|
227
229
|
}
|
|
228
230
|
break;
|
|
229
231
|
case "ratechange":
|
|
230
|
-
// Block rate changes unless suspended.
|
|
231
232
|
if (
|
|
232
|
-
this.
|
|
233
|
+
this._blockUnexpectedPlayerEvents &&
|
|
234
|
+
this._expectedPlaybackRate !==
|
|
235
|
+
this._player.playbackRate &&
|
|
233
236
|
!this._mediaSession.coordinator.isSuspended
|
|
234
237
|
) {
|
|
235
|
-
this.
|
|
236
|
-
TelemetryEvents.MediaPlayerSynchronizer
|
|
237
|
-
.PlaybackRateChangeBlocked
|
|
238
|
-
);
|
|
239
|
-
this._player.playbackRate = 1.0;
|
|
238
|
+
this._player.playbackRate = this._expectedPlaybackRate;
|
|
240
239
|
}
|
|
241
240
|
break;
|
|
242
241
|
case "blocked":
|
|
243
242
|
this._logger.sendTelemetryEvent(
|
|
244
243
|
TelemetryEvents.MediaPlayerSynchronizer
|
|
245
|
-
.
|
|
244
|
+
.PlayerBlockedOperation
|
|
246
245
|
);
|
|
247
246
|
break;
|
|
248
247
|
}
|
|
@@ -254,6 +253,13 @@ export class MediaPlayerSynchronizer extends EventEmitter {
|
|
|
254
253
|
(evt) => this.emit(evt.type, evt)
|
|
255
254
|
);
|
|
256
255
|
|
|
256
|
+
this._mediaSession.coordinator.on(
|
|
257
|
+
MediaSessionCoordinatorEvents.triggeractionignored,
|
|
258
|
+
(evt: ITriggerActionEvent) => {
|
|
259
|
+
this.dispatchGroupAction(evt.details, undefined);
|
|
260
|
+
}
|
|
261
|
+
);
|
|
262
|
+
|
|
257
263
|
// Register media session actions
|
|
258
264
|
for (const action of MediaPlayerSynchronizer.SESSION_ACTIONS) {
|
|
259
265
|
this._mediaSession.setActionHandler(
|
|
@@ -344,6 +350,22 @@ export class MediaPlayerSynchronizer extends EventEmitter {
|
|
|
344
350
|
.DataChangeAction
|
|
345
351
|
);
|
|
346
352
|
break;
|
|
353
|
+
case "ratechange":
|
|
354
|
+
if (!details.playbackRate) {
|
|
355
|
+
break;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
this._logger.sendTelemetryEvent(
|
|
359
|
+
TelemetryEvents.MediaPlayerSynchronizer
|
|
360
|
+
.RateChangeAction,
|
|
361
|
+
null,
|
|
362
|
+
{ playbackRate: details.playbackRate }
|
|
363
|
+
);
|
|
364
|
+
this._expectedPlaybackRate =
|
|
365
|
+
details.playbackRate;
|
|
366
|
+
this._player.playbackRate =
|
|
367
|
+
details.playbackRate;
|
|
368
|
+
break;
|
|
347
369
|
case "catchup":
|
|
348
370
|
if (typeof details.seekTime == "number") {
|
|
349
371
|
this._logger.sendTelemetryEvent(
|
|
@@ -401,10 +423,10 @@ export class MediaPlayerSynchronizer extends EventEmitter {
|
|
|
401
423
|
*
|
|
402
424
|
* @remarks
|
|
403
425
|
* Toggling this value to true results in `mediaSession.coordinator.canPlayPause`,
|
|
404
|
-
* `mediaSession.coordinator.canSeek`, `mediaSession.coordinator.canSetTrack`,
|
|
405
|
-
* `mediaSession.coordinator.canSetTrackData
|
|
406
|
-
* grained control over the local clients policies,
|
|
407
|
-
* directly.
|
|
426
|
+
* `mediaSession.coordinator.canSeek`, `mediaSession.coordinator.canSetTrack`,
|
|
427
|
+
* `mediaSession.coordinator.canSetTrackData`, and `mediaSession.coordinator.canSetPlaybackRate`
|
|
428
|
+
* all being set to false. For more fine grained control over the local clients policies,
|
|
429
|
+
* call the `mediaSession.coordinator` directly.
|
|
408
430
|
*/
|
|
409
431
|
public get viewOnly(): boolean {
|
|
410
432
|
return this._viewOnly;
|
|
@@ -416,6 +438,7 @@ export class MediaPlayerSynchronizer extends EventEmitter {
|
|
|
416
438
|
this.mediaSession.coordinator.canSeek = !value;
|
|
417
439
|
this.mediaSession.coordinator.canSetTrack = !value;
|
|
418
440
|
this.mediaSession.coordinator.canSetTrackData = !value;
|
|
441
|
+
this.mediaSession.coordinator.canSetPlaybackRate = !value;
|
|
419
442
|
}
|
|
420
443
|
|
|
421
444
|
/**
|
|
@@ -635,6 +658,24 @@ export class MediaPlayerSynchronizer extends EventEmitter {
|
|
|
635
658
|
});
|
|
636
659
|
}
|
|
637
660
|
|
|
661
|
+
public async setPlaybackRate(playbackRate: number): Promise<void> {
|
|
662
|
+
this._logger.sendTelemetryEvent(
|
|
663
|
+
TelemetryEvents.MediaPlayerSynchronizer.RateChangeCalled,
|
|
664
|
+
null,
|
|
665
|
+
{ playbackRate }
|
|
666
|
+
);
|
|
667
|
+
|
|
668
|
+
await this._mediaSession.coordinator.setPlaybackRate(playbackRate);
|
|
669
|
+
|
|
670
|
+
this.dispatchUserAction({
|
|
671
|
+
action: "ratechange",
|
|
672
|
+
source: "user",
|
|
673
|
+
clientId: await waitUntilConnected(this._runtime),
|
|
674
|
+
local: true,
|
|
675
|
+
playbackRate,
|
|
676
|
+
});
|
|
677
|
+
}
|
|
678
|
+
|
|
638
679
|
/**
|
|
639
680
|
* Tells the group to change to a new track.
|
|
640
681
|
*
|
|
@@ -687,7 +728,6 @@ export class MediaPlayerSynchronizer extends EventEmitter {
|
|
|
687
728
|
});
|
|
688
729
|
}
|
|
689
730
|
|
|
690
|
-
// TODO: what is delay useful for? we do not use, delete?
|
|
691
731
|
private dispatchGroupAction(
|
|
692
732
|
details: ExtendedMediaSessionActionDetails,
|
|
693
733
|
error: Error | undefined
|
|
@@ -59,6 +59,10 @@ export enum MediaSessionCoordinatorEvents {
|
|
|
59
59
|
* Event emitted when an action was triggered.
|
|
60
60
|
*/
|
|
61
61
|
triggeraction = "triggeraction",
|
|
62
|
+
/**
|
|
63
|
+
* Event emitted when an action would have been triggered, but a suspension is active for the local client.
|
|
64
|
+
*/
|
|
65
|
+
triggeractionignored = "triggeractionignored",
|
|
62
66
|
}
|
|
63
67
|
|
|
64
68
|
/**
|
|
@@ -71,6 +75,7 @@ export enum MediaSessionCoordinatorEvents {
|
|
|
71
75
|
export type ExtendedMediaSessionAction =
|
|
72
76
|
| MediaSessionAction
|
|
73
77
|
| "settrack"
|
|
78
|
+
| "ratechange"
|
|
74
79
|
| "wait"
|
|
75
80
|
| "catchup"
|
|
76
81
|
| "datachange"
|
|
@@ -116,6 +121,15 @@ export interface ExtendedMediaMetadata extends MediaMetadata {
|
|
|
116
121
|
liveStream?: boolean;
|
|
117
122
|
}
|
|
118
123
|
|
|
124
|
+
/**
|
|
125
|
+
* The reasons why an action is ignored.
|
|
126
|
+
*
|
|
127
|
+
* @remarks
|
|
128
|
+
* Is `localusersuspended` when the action updated the group state, but ignored locally due to the local client being suspended.
|
|
129
|
+
|
|
130
|
+
*/
|
|
131
|
+
export type ExtendedMediaSessionActionIgnore = "localusersuspended";
|
|
132
|
+
|
|
119
133
|
/**
|
|
120
134
|
* Details for emitted actions from `LiveMediaSession`.
|
|
121
135
|
*
|
|
@@ -169,9 +183,11 @@ export interface ExtendedMediaSessionActionDetails {
|
|
|
169
183
|
*/
|
|
170
184
|
data?: object | null;
|
|
171
185
|
/**
|
|
172
|
-
*
|
|
186
|
+
* Reason an action is ignored by the local client, undefined if not ignored.
|
|
173
187
|
*/
|
|
174
|
-
|
|
188
|
+
ignoreReason?: ExtendedMediaSessionActionIgnore | null;
|
|
189
|
+
|
|
190
|
+
playbackRate?: number | null;
|
|
175
191
|
}
|
|
176
192
|
|
|
177
193
|
/**
|