@microsoft/live-share-media 1.0.0-preview.9 → 1.0.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.
- package/LICENSE +64 -61
- package/README.md +7 -13
- package/bin/IMediaPlayer.d.ts +20 -20
- package/bin/IMediaPlayer.js +6 -6
- package/bin/LiveMediaSession.d.ts +90 -94
- package/bin/LiveMediaSession.d.ts.map +1 -1
- package/bin/LiveMediaSession.js +228 -231
- package/bin/LiveMediaSession.js.map +1 -1
- package/bin/LiveMediaSessionCoordinator.d.ts +238 -227
- package/bin/LiveMediaSessionCoordinator.d.ts.map +1 -1
- package/bin/LiveMediaSessionCoordinator.js +476 -407
- package/bin/LiveMediaSessionCoordinator.js.map +1 -1
- package/bin/LiveMediaSessionCoordinatorSuspension.d.ts +19 -19
- package/bin/LiveMediaSessionCoordinatorSuspension.js +27 -27
- package/bin/MediaPlayerSynchronizer.d.ts +173 -163
- package/bin/MediaPlayerSynchronizer.d.ts.map +1 -1
- package/bin/MediaPlayerSynchronizer.js +491 -468
- package/bin/MediaPlayerSynchronizer.js.map +1 -1
- package/bin/MediaSessionActionThrottler.d.ts +11 -11
- package/bin/MediaSessionActionThrottler.js +13 -13
- package/bin/MediaSessionExtensions.d.ts +34 -34
- package/bin/MediaSessionExtensions.d.ts.map +1 -1
- package/bin/MediaSessionExtensions.js +12 -12
- package/bin/RepeatedActionThrottler.d.ts +19 -19
- package/bin/RepeatedActionThrottler.js +62 -62
- package/bin/RepeatedActionThrottler.js.map +1 -1
- package/bin/VolumeManager.d.ts +83 -83
- package/bin/VolumeManager.js +174 -174
- package/bin/index.d.ts +13 -13
- package/bin/index.js +29 -29
- package/bin/internals/GroupCoordinatorState.d.ts +93 -92
- package/bin/internals/GroupCoordinatorState.d.ts.map +1 -1
- package/bin/internals/GroupCoordinatorState.js +407 -404
- package/bin/internals/GroupCoordinatorState.js.map +1 -1
- package/bin/internals/GroupPlaybackPosition.d.ts +67 -66
- package/bin/internals/GroupPlaybackPosition.d.ts.map +1 -1
- package/bin/internals/GroupPlaybackPosition.js +166 -166
- package/bin/internals/GroupPlaybackPosition.js.map +1 -1
- package/bin/internals/GroupPlaybackTrack.d.ts +45 -45
- package/bin/internals/GroupPlaybackTrack.d.ts.map +1 -1
- package/bin/internals/GroupPlaybackTrack.js +121 -121
- package/bin/internals/GroupPlaybackTrackData.d.ts +39 -39
- package/bin/internals/GroupPlaybackTrackData.d.ts.map +1 -1
- package/bin/internals/GroupPlaybackTrackData.js +69 -69
- package/bin/internals/GroupTransportState.d.ts +49 -48
- package/bin/internals/GroupTransportState.d.ts.map +1 -1
- package/bin/internals/GroupTransportState.js +111 -111
- package/bin/internals/GroupTransportState.js.map +1 -1
- package/bin/internals/consts.d.ts +75 -72
- package/bin/internals/consts.d.ts.map +1 -1
- package/bin/internals/consts.js +84 -81
- package/bin/internals/consts.js.map +1 -1
- package/bin/internals/index.d.ts +10 -10
- package/bin/internals/index.js +26 -26
- package/package.json +9 -7
- package/src/LiveMediaSession.ts +24 -20
- package/src/LiveMediaSessionCoordinator.ts +101 -29
- package/src/MediaPlayerSynchronizer.ts +30 -14
- package/src/internals/GroupCoordinatorState.ts +49 -34
- package/src/internals/GroupPlaybackPosition.ts +7 -3
- package/src/internals/GroupPlaybackTrack.ts +2 -2
- package/src/internals/GroupPlaybackTrackData.ts +2 -2
- package/src/internals/GroupTransportState.ts +11 -4
- package/src/internals/consts.ts +4 -0
- package/typedoc.json +10 -0
- package/bin/EphemeralMediaSession.d.ts +0 -102
- package/bin/EphemeralMediaSession.d.ts.map +0 -1
- package/bin/EphemeralMediaSession.js +0 -226
- package/bin/EphemeralMediaSession.js.map +0 -1
- package/bin/EphemeralMediaSessionCoordinator.d.ts +0 -228
- package/bin/EphemeralMediaSessionCoordinator.d.ts.map +0 -1
- package/bin/EphemeralMediaSessionCoordinator.js +0 -396
- package/bin/EphemeralMediaSessionCoordinator.js.map +0 -1
- package/bin/EphemeralMediaSessionCoordinatorSuspension.d.ts +0 -20
- package/bin/EphemeralMediaSessionCoordinatorSuspension.d.ts.map +0 -1
- package/bin/EphemeralMediaSessionCoordinatorSuspension.js +0 -28
- package/bin/EphemeralMediaSessionCoordinatorSuspension.js.map +0 -1
- package/bin/VolumeLimiter.d.ts +0 -70
- package/bin/VolumeLimiter.d.ts.map +0 -1
- package/bin/VolumeLimiter.js +0 -170
- package/bin/VolumeLimiter.js.map +0 -1
- package/src/test/GroupPlaybackPosition.spec.ts +0 -600
- package/src/test/GroupPlaybackTrack.spec.ts +0 -122
- package/src/test/SharedMediaSession.spec.ts +0 -149
- package/src/test/SharedMediaSessionCoordinator.spec.ts +0 -271
- package/src/test/VolumeManager.spec.ts +0 -219
package/bin/internals/index.js
CHANGED
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
4
|
-
* Licensed under the Microsoft Live Share SDK License.
|
|
5
|
-
*/
|
|
6
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
|
-
if (k2 === undefined) k2 = k;
|
|
8
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
9
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
10
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
11
|
-
}
|
|
12
|
-
Object.defineProperty(o, k2, desc);
|
|
13
|
-
}) : (function(o, m, k, k2) {
|
|
14
|
-
if (k2 === undefined) k2 = k;
|
|
15
|
-
o[k2] = m[k];
|
|
16
|
-
}));
|
|
17
|
-
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
18
|
-
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
19
|
-
};
|
|
20
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
-
__exportStar(require("./consts"), exports);
|
|
22
|
-
__exportStar(require("./GroupCoordinatorState"), exports);
|
|
23
|
-
__exportStar(require("./GroupPlaybackPosition"), exports);
|
|
24
|
-
__exportStar(require("./GroupPlaybackTrack"), exports);
|
|
25
|
-
__exportStar(require("./GroupPlaybackTrackData"), exports);
|
|
26
|
-
__exportStar(require("./GroupTransportState"), exports);
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
4
|
+
* Licensed under the Microsoft Live Share SDK License.
|
|
5
|
+
*/
|
|
6
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
|
+
if (k2 === undefined) k2 = k;
|
|
8
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
9
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
10
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
11
|
+
}
|
|
12
|
+
Object.defineProperty(o, k2, desc);
|
|
13
|
+
}) : (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
o[k2] = m[k];
|
|
16
|
+
}));
|
|
17
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
18
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
19
|
+
};
|
|
20
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
+
__exportStar(require("./consts"), exports);
|
|
22
|
+
__exportStar(require("./GroupCoordinatorState"), exports);
|
|
23
|
+
__exportStar(require("./GroupPlaybackPosition"), exports);
|
|
24
|
+
__exportStar(require("./GroupPlaybackTrack"), exports);
|
|
25
|
+
__exportStar(require("./GroupPlaybackTrackData"), exports);
|
|
26
|
+
__exportStar(require("./GroupTransportState"), exports);
|
|
27
27
|
//# sourceMappingURL=index.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@microsoft/live-share-media",
|
|
3
|
-
"version": "1.0.0
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "Live Share classes that synchronize media playback across clients.",
|
|
5
5
|
"author": "Microsoft",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE",
|
|
@@ -13,18 +13,20 @@
|
|
|
13
13
|
"test:debug": "ts-mocha src/**/*.spec.ts --inspect-brk",
|
|
14
14
|
"test:coverage": "nyc --reporter=html --reporter=text --reporter=text-summary npm test"
|
|
15
15
|
},
|
|
16
|
-
"dependencies": {
|
|
17
|
-
"@microsoft/live-share": "1.0.0-preview.9",
|
|
18
|
-
"fluid-framework": "~1.2.3"
|
|
19
|
-
},
|
|
20
16
|
"devDependencies": {
|
|
17
|
+
"@microsoft/live-share": "1.0.0",
|
|
21
18
|
"@types/assert": "^1.5.6",
|
|
22
19
|
"@types/mocha": "^10.0.1",
|
|
23
|
-
"@types/node": "^
|
|
20
|
+
"@types/node": "^20.2.3",
|
|
21
|
+
"fluid-framework": "^1.3.6",
|
|
24
22
|
"mocha": "^10.2.0",
|
|
25
23
|
"nyc": "^15.1.0",
|
|
26
24
|
"ts-mocha": "^10.0.0",
|
|
27
|
-
"typescript": "^
|
|
25
|
+
"typescript": "^5.0.4"
|
|
26
|
+
},
|
|
27
|
+
"peerDependencies": {
|
|
28
|
+
"@microsoft/live-share": "1.0.0",
|
|
29
|
+
"fluid-framework": "^1.2.3"
|
|
28
30
|
},
|
|
29
31
|
"repository": {
|
|
30
32
|
"directory": "packages/live-share-media",
|
package/src/LiveMediaSession.ts
CHANGED
|
@@ -3,8 +3,13 @@
|
|
|
3
3
|
* Licensed under the Microsoft Live Share SDK License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
6
|
+
import { DataObjectFactory } from "@fluidframework/aqueduct";
|
|
7
|
+
import {
|
|
8
|
+
DynamicObjectRegistry,
|
|
9
|
+
LiveDataObject,
|
|
10
|
+
LiveTelemetryLogger,
|
|
11
|
+
UserMeetingRole,
|
|
12
|
+
} from "@microsoft/live-share";
|
|
8
13
|
import { MediaPlayerSynchronizer } from "./MediaPlayerSynchronizer";
|
|
9
14
|
import { ITriggerActionEvent, TelemetryEvents } from "./internals";
|
|
10
15
|
import {
|
|
@@ -23,7 +28,7 @@ import { IMediaPlayer } from "./IMediaPlayer";
|
|
|
23
28
|
/**
|
|
24
29
|
* Live fluid object that synchronizes media playback across multiple clients.
|
|
25
30
|
*/
|
|
26
|
-
export class LiveMediaSession extends
|
|
31
|
+
export class LiveMediaSession extends LiveDataObject {
|
|
27
32
|
private _actionThrottler: MediaSessionActionThrottler =
|
|
28
33
|
new RepeatedActionThrottler();
|
|
29
34
|
private _logger?: LiveTelemetryLogger;
|
|
@@ -80,14 +85,6 @@ export class LiveMediaSession extends DataObject {
|
|
|
80
85
|
return this.coordinator.isInitialized;
|
|
81
86
|
}
|
|
82
87
|
|
|
83
|
-
/**
|
|
84
|
-
* @deprecated isInitialized should be used instead
|
|
85
|
-
* Returns true if the object has been initialized.
|
|
86
|
-
*/
|
|
87
|
-
public get isStarted(): boolean {
|
|
88
|
-
return this.isInitialized;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
88
|
/**
|
|
92
89
|
* The group coordinator for the session.
|
|
93
90
|
*/
|
|
@@ -107,13 +104,15 @@ export class LiveMediaSession extends DataObject {
|
|
|
107
104
|
|
|
108
105
|
/**
|
|
109
106
|
* initialize the object.
|
|
110
|
-
* @param
|
|
107
|
+
* @param allowedRoles Optional. List of roles allowed to group transport
|
|
111
108
|
* operations like play/pause/seek/setTrack.
|
|
109
|
+
* @returns a void promise that resolves once complete
|
|
112
110
|
*/
|
|
113
|
-
public initialize(
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
111
|
+
public initialize(allowedRoles?: UserMeetingRole[]): Promise<void> {
|
|
112
|
+
if (allowedRoles) {
|
|
113
|
+
this._allowedRoles = allowedRoles;
|
|
114
|
+
}
|
|
115
|
+
return this.coordinator.initialize(allowedRoles);
|
|
117
116
|
}
|
|
118
117
|
|
|
119
118
|
/**
|
|
@@ -184,11 +183,13 @@ export class LiveMediaSession extends DataObject {
|
|
|
184
183
|
}
|
|
185
184
|
|
|
186
185
|
protected async hasInitialized(): Promise<void> {
|
|
187
|
-
this._logger = new LiveTelemetryLogger(this.runtime);
|
|
186
|
+
this._logger = new LiveTelemetryLogger(this.runtime, this.liveRuntime);
|
|
188
187
|
|
|
189
188
|
// Create coordinator and listen for triggered actions
|
|
190
|
-
this._coordinator = new LiveMediaSessionCoordinator(
|
|
191
|
-
this.
|
|
189
|
+
this._coordinator = new LiveMediaSessionCoordinator(
|
|
190
|
+
this.runtime,
|
|
191
|
+
this.liveRuntime,
|
|
192
|
+
() => this.getCurrentPlayerState()
|
|
192
193
|
);
|
|
193
194
|
this._coordinator.on(
|
|
194
195
|
MediaSessionCoordinatorEvents.triggeraction,
|
|
@@ -302,4 +303,7 @@ export class LiveMediaSession extends DataObject {
|
|
|
302
303
|
/**
|
|
303
304
|
* Register `LiveMediaSession` as an available `LoadableObjectClass` for use in packages that support dynamic object loading, such as `@microsoft/live-share-turbo`.
|
|
304
305
|
*/
|
|
305
|
-
DynamicObjectRegistry.registerObjectClass(
|
|
306
|
+
DynamicObjectRegistry.registerObjectClass(
|
|
307
|
+
LiveMediaSession,
|
|
308
|
+
LiveMediaSession.TypeName
|
|
309
|
+
);
|
|
@@ -7,18 +7,15 @@ import {
|
|
|
7
7
|
LiveEventScope,
|
|
8
8
|
LiveTelemetryLogger,
|
|
9
9
|
LiveEventTarget,
|
|
10
|
-
ILiveEvent,
|
|
11
10
|
IRuntimeSignaler,
|
|
12
11
|
TimeInterval,
|
|
13
12
|
UserMeetingRole,
|
|
13
|
+
LiveShareRuntime,
|
|
14
14
|
} from "@microsoft/live-share";
|
|
15
15
|
import {
|
|
16
16
|
CoordinationWaitPoint,
|
|
17
|
-
ExtendedMediaSessionActionDetails,
|
|
18
17
|
ExtendedMediaMetadata,
|
|
19
18
|
ExtendedMediaSessionPlaybackState,
|
|
20
|
-
MediaSessionCoordinatorEvents,
|
|
21
|
-
MediaSessionCoordinatorState,
|
|
22
19
|
MediaSessionCoordinatorSuspension,
|
|
23
20
|
} from "./MediaSessionExtensions";
|
|
24
21
|
import {
|
|
@@ -28,7 +25,6 @@ import {
|
|
|
28
25
|
IPositionUpdateEvent,
|
|
29
26
|
GroupCoordinatorState,
|
|
30
27
|
GroupCoordinatorStateEvents,
|
|
31
|
-
ITriggerActionEvent,
|
|
32
28
|
ISetTrackDataEvent,
|
|
33
29
|
} from "./internals";
|
|
34
30
|
import { LiveMediaSessionCoordinatorSuspension } from "./LiveMediaSessionCoordinatorSuspension";
|
|
@@ -69,6 +65,7 @@ export interface IMediaPlayerState {
|
|
|
69
65
|
*/
|
|
70
66
|
export class LiveMediaSessionCoordinator extends EventEmitter {
|
|
71
67
|
private readonly _runtime: IRuntimeSignaler;
|
|
68
|
+
private readonly _liveRuntime: LiveShareRuntime;
|
|
72
69
|
private readonly _logger: LiveTelemetryLogger;
|
|
73
70
|
private readonly _getPlayerState: () => IMediaPlayerState;
|
|
74
71
|
private _positionUpdateInterval = new TimeInterval(2000);
|
|
@@ -83,22 +80,26 @@ export class LiveMediaSessionCoordinator extends EventEmitter {
|
|
|
83
80
|
private _setTrackEvent?: LiveEventTarget<ISetTrackEvent>;
|
|
84
81
|
private _setTrackDataEvent?: LiveEventTarget<ISetTrackDataEvent>;
|
|
85
82
|
private _positionUpdateEvent?: LiveEventTarget<IPositionUpdateEvent>;
|
|
86
|
-
private _joinedEvent?: LiveEventTarget<
|
|
83
|
+
private _joinedEvent?: LiveEventTarget<undefined>;
|
|
87
84
|
|
|
88
85
|
// Distributed state
|
|
89
86
|
private _groupState?: GroupCoordinatorState;
|
|
90
87
|
|
|
88
|
+
private _allowedRoles?: UserMeetingRole[];
|
|
89
|
+
|
|
91
90
|
/**
|
|
92
91
|
* @hidden
|
|
93
92
|
* Applications shouldn't directly create new coordinator instances.
|
|
94
93
|
*/
|
|
95
94
|
constructor(
|
|
96
95
|
runtime: IRuntimeSignaler,
|
|
96
|
+
liveRuntime: LiveShareRuntime,
|
|
97
97
|
getPlayerState: () => IMediaPlayerState
|
|
98
98
|
) {
|
|
99
99
|
super();
|
|
100
100
|
this._runtime = runtime;
|
|
101
|
-
this.
|
|
101
|
+
this._liveRuntime = liveRuntime;
|
|
102
|
+
this._logger = new LiveTelemetryLogger(runtime, liveRuntime);
|
|
102
103
|
this._getPlayerState = getPlayerState;
|
|
103
104
|
}
|
|
104
105
|
|
|
@@ -201,8 +202,10 @@ export class LiveMediaSessionCoordinator extends EventEmitter {
|
|
|
201
202
|
* @remarks
|
|
202
203
|
* Throws an exception if the session/coordinator hasn't been initialized, no track has been
|
|
203
204
|
* loaded, or `canPlayPause` is false.
|
|
205
|
+
*
|
|
206
|
+
* @returns a void promise that resolves once complete, throws if user does not have proper roles
|
|
204
207
|
*/
|
|
205
|
-
public play(): void {
|
|
208
|
+
public async play(): Promise<void> {
|
|
206
209
|
if (!this._hasInitialized) {
|
|
207
210
|
throw new Error(
|
|
208
211
|
`LiveMediaSessionCoordinator.play() called before initialize() called.`
|
|
@@ -230,7 +233,7 @@ export class LiveMediaSessionCoordinator extends EventEmitter {
|
|
|
230
233
|
null,
|
|
231
234
|
{ position: position }
|
|
232
235
|
);
|
|
233
|
-
this._playEvent!.sendEvent({
|
|
236
|
+
await this._playEvent!.sendEvent({
|
|
234
237
|
track: this._groupState.playbackTrack.current,
|
|
235
238
|
position: position,
|
|
236
239
|
});
|
|
@@ -242,8 +245,10 @@ export class LiveMediaSessionCoordinator extends EventEmitter {
|
|
|
242
245
|
* @remarks
|
|
243
246
|
* Throws an exception if the session/coordinator hasn't been initialized, no track has been
|
|
244
247
|
* loaded, or `canPlayPause` is false.
|
|
248
|
+
*
|
|
249
|
+
* @returns a void promise that resolves once complete, throws if user does not have proper roles
|
|
245
250
|
*/
|
|
246
|
-
public pause(): void {
|
|
251
|
+
public async pause(): Promise<void> {
|
|
247
252
|
if (!this._hasInitialized) {
|
|
248
253
|
throw new Error(
|
|
249
254
|
`LiveMediaSessionCoordinator.pause() called before initialize() called.`
|
|
@@ -271,7 +276,7 @@ export class LiveMediaSessionCoordinator extends EventEmitter {
|
|
|
271
276
|
null,
|
|
272
277
|
{ position: position }
|
|
273
278
|
);
|
|
274
|
-
this._pauseEvent!.sendEvent({
|
|
279
|
+
await this._pauseEvent!.sendEvent({
|
|
275
280
|
track: this._groupState.playbackTrack.current,
|
|
276
281
|
position: position,
|
|
277
282
|
});
|
|
@@ -284,8 +289,9 @@ export class LiveMediaSessionCoordinator extends EventEmitter {
|
|
|
284
289
|
* Throws an exception if the session/coordinator hasn't been initialized, no track has been
|
|
285
290
|
* loaded, or `canSeek` is false.
|
|
286
291
|
* @param time Playback position in seconds to seek to.
|
|
292
|
+
* @returns a void promise that resolves once complete, throws if user does not have proper roles
|
|
287
293
|
*/
|
|
288
|
-
public seekTo(time: number): void {
|
|
294
|
+
public async seekTo(time: number): Promise<void> {
|
|
289
295
|
if (!this._hasInitialized) {
|
|
290
296
|
throw new Error(
|
|
291
297
|
`LiveMediaSessionCoordinator.seekTo() called before initialize() called.`
|
|
@@ -310,10 +316,15 @@ export class LiveMediaSessionCoordinator extends EventEmitter {
|
|
|
310
316
|
null,
|
|
311
317
|
{ position: time }
|
|
312
318
|
);
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
319
|
+
try {
|
|
320
|
+
await this._seekToEvent!.sendEvent({
|
|
321
|
+
track: this._groupState.playbackTrack.current,
|
|
322
|
+
position: time,
|
|
323
|
+
});
|
|
324
|
+
} catch (err) {
|
|
325
|
+
await this._groupState!.syncLocalMediaSession();
|
|
326
|
+
throw err;
|
|
327
|
+
}
|
|
317
328
|
}
|
|
318
329
|
|
|
319
330
|
/**
|
|
@@ -324,11 +335,12 @@ export class LiveMediaSessionCoordinator extends EventEmitter {
|
|
|
324
335
|
* false.
|
|
325
336
|
* @param metadata The track to load or `null` to indicate that the end of the track is reached.
|
|
326
337
|
* @param waitPoints Optional. List of static wait points to configure for the track. Dynamic wait points can be added via the `beginSuspension()` call.
|
|
338
|
+
* @returns a void promise that resolves once complete, throws if user does not have proper roles
|
|
327
339
|
*/
|
|
328
|
-
public setTrack(
|
|
340
|
+
public async setTrack(
|
|
329
341
|
metadata: ExtendedMediaMetadata | null,
|
|
330
342
|
waitPoints?: CoordinationWaitPoint[]
|
|
331
|
-
): void {
|
|
343
|
+
): Promise<void> {
|
|
332
344
|
if (!this._hasInitialized) {
|
|
333
345
|
throw new Error(
|
|
334
346
|
`LiveMediaSessionCoordinator.setTrack() called before initialize() called.`
|
|
@@ -345,7 +357,7 @@ export class LiveMediaSessionCoordinator extends EventEmitter {
|
|
|
345
357
|
this._logger.sendTelemetryEvent(
|
|
346
358
|
TelemetryEvents.SessionCoordinator.SetTrackCalled
|
|
347
359
|
);
|
|
348
|
-
this._setTrackEvent!.sendEvent({
|
|
360
|
+
await this._setTrackEvent!.sendEvent({
|
|
349
361
|
metadata: metadata,
|
|
350
362
|
waitPoints: waitPoints || [],
|
|
351
363
|
});
|
|
@@ -361,8 +373,9 @@ export class LiveMediaSessionCoordinator extends EventEmitter {
|
|
|
361
373
|
* Throws an exception if the session/coordinator hasn't been initialized or `canSetTrackData` is
|
|
362
374
|
* false.
|
|
363
375
|
* @param data New data object to sync with the group. This value will be synchronized using a last writer wins strategy.
|
|
376
|
+
* @returns a void promise that resolves once complete, throws if user does not have proper roles
|
|
364
377
|
*/
|
|
365
|
-
public setTrackData(data: object | null): void {
|
|
378
|
+
public async setTrackData(data: object | null): Promise<void> {
|
|
366
379
|
if (!this._hasInitialized) {
|
|
367
380
|
throw new Error(
|
|
368
381
|
`LiveMediaSessionCoordinator.setTrackData() called before initialize() called.`
|
|
@@ -379,7 +392,7 @@ export class LiveMediaSessionCoordinator extends EventEmitter {
|
|
|
379
392
|
this._logger.sendTelemetryEvent(
|
|
380
393
|
TelemetryEvents.SessionCoordinator.SetTrackDataCalled
|
|
381
394
|
);
|
|
382
|
-
this._setTrackDataEvent!.sendEvent({
|
|
395
|
+
await this._setTrackDataEvent!.sendEvent({
|
|
383
396
|
data: data,
|
|
384
397
|
});
|
|
385
398
|
}
|
|
@@ -520,23 +533,51 @@ export class LiveMediaSessionCoordinator extends EventEmitter {
|
|
|
520
533
|
}
|
|
521
534
|
|
|
522
535
|
// Send position update event
|
|
523
|
-
|
|
524
|
-
|
|
536
|
+
this.verifyLocalUserRoles()
|
|
537
|
+
.then((valid) => {
|
|
538
|
+
const evt = this._groupState!.createPositionUpdateEvent(state);
|
|
539
|
+
if (!valid) {
|
|
540
|
+
// We still need to update _groupState with the local user's position.
|
|
541
|
+
// So we do this here rather than on the receiving end.
|
|
542
|
+
this._groupState!.handlePositionUpdate(
|
|
543
|
+
{
|
|
544
|
+
clientId: this._runtime.clientId ?? "",
|
|
545
|
+
timestamp: this._liveRuntime.getTimestamp(),
|
|
546
|
+
data: evt,
|
|
547
|
+
},
|
|
548
|
+
true
|
|
549
|
+
);
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
552
|
+
return this._positionUpdateEvent?.sendEvent(evt);
|
|
553
|
+
})
|
|
554
|
+
.catch((err) => {
|
|
555
|
+
this._logger.sendErrorEvent(
|
|
556
|
+
TelemetryEvents.SessionCoordinator.PositionUpdateEventError,
|
|
557
|
+
err
|
|
558
|
+
);
|
|
559
|
+
});
|
|
525
560
|
}
|
|
526
561
|
|
|
527
562
|
protected async createChildren(
|
|
528
563
|
acceptTransportChangesFrom?: UserMeetingRole[]
|
|
529
564
|
): Promise<void> {
|
|
565
|
+
this._allowedRoles = acceptTransportChangesFrom;
|
|
530
566
|
// Create event scopes
|
|
531
567
|
const scope = new LiveEventScope(
|
|
532
568
|
this._runtime,
|
|
569
|
+
this._liveRuntime,
|
|
533
570
|
acceptTransportChangesFrom
|
|
534
571
|
);
|
|
535
|
-
const unrestrictedScope = new LiveEventScope(
|
|
572
|
+
const unrestrictedScope = new LiveEventScope(
|
|
573
|
+
this._runtime,
|
|
574
|
+
this._liveRuntime
|
|
575
|
+
);
|
|
536
576
|
|
|
537
577
|
// Initialize internal coordinator state
|
|
538
578
|
this._groupState = new GroupCoordinatorState(
|
|
539
579
|
this._runtime,
|
|
580
|
+
this._liveRuntime,
|
|
540
581
|
this._maxPlaybackDrift,
|
|
541
582
|
this._positionUpdateInterval,
|
|
542
583
|
this._getPlayerState
|
|
@@ -596,14 +637,22 @@ export class LiveMediaSessionCoordinator extends EventEmitter {
|
|
|
596
637
|
}
|
|
597
638
|
);
|
|
598
639
|
const state = this._getPlayerState();
|
|
599
|
-
|
|
600
|
-
this._groupState!.createPositionUpdateEvent(state);
|
|
601
|
-
this._positionUpdateEvent?.sendEvent(update);
|
|
640
|
+
this.sendPositionUpdate(state);
|
|
602
641
|
}
|
|
603
642
|
);
|
|
604
643
|
|
|
605
|
-
|
|
606
|
-
|
|
644
|
+
this.verifyLocalUserRoles()
|
|
645
|
+
.then((verified) => {
|
|
646
|
+
if (!verified) return;
|
|
647
|
+
// Send initial joined event
|
|
648
|
+
return this._joinedEvent?.sendEvent(undefined);
|
|
649
|
+
})
|
|
650
|
+
.catch((err) => {
|
|
651
|
+
this._logger.sendErrorEvent(
|
|
652
|
+
TelemetryEvents.SessionCoordinator.SendJoinedEventError,
|
|
653
|
+
err
|
|
654
|
+
);
|
|
655
|
+
});
|
|
607
656
|
}
|
|
608
657
|
|
|
609
658
|
private getPlayerPosition(): number {
|
|
@@ -618,4 +667,27 @@ export class LiveMediaSessionCoordinator extends EventEmitter {
|
|
|
618
667
|
: 0.0;
|
|
619
668
|
}
|
|
620
669
|
}
|
|
670
|
+
|
|
671
|
+
private async verifyLocalUserRoles(): Promise<boolean> {
|
|
672
|
+
const clientId = await this.waitUntilConnected();
|
|
673
|
+
return this._liveRuntime.verifyRolesAllowed(
|
|
674
|
+
clientId,
|
|
675
|
+
this._allowedRoles ?? []
|
|
676
|
+
);
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
private waitUntilConnected(): Promise<string> {
|
|
680
|
+
return new Promise((resolve) => {
|
|
681
|
+
const onConnected = (clientId: string) => {
|
|
682
|
+
this._runtime.off("connected", onConnected);
|
|
683
|
+
resolve(clientId);
|
|
684
|
+
};
|
|
685
|
+
|
|
686
|
+
if (this._runtime.clientId && this._runtime.connected) {
|
|
687
|
+
resolve(this._runtime.clientId);
|
|
688
|
+
} else {
|
|
689
|
+
this._runtime.on("connected", onConnected);
|
|
690
|
+
}
|
|
691
|
+
});
|
|
692
|
+
}
|
|
621
693
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
3
|
* Licensed under the Microsoft Live Share SDK License.
|
|
4
4
|
*/
|
|
5
|
-
import { LiveTelemetryLogger,
|
|
5
|
+
import { LiveTelemetryLogger, ILiveEvent } from "@microsoft/live-share";
|
|
6
6
|
import EventEmitter from "events";
|
|
7
7
|
import {
|
|
8
8
|
ExtendedMediaSessionAction,
|
|
@@ -21,7 +21,7 @@ import { TelemetryEvents } from "./internals";
|
|
|
21
21
|
/**
|
|
22
22
|
* Event data returned by `MediaPlayerSynchronizer` object.
|
|
23
23
|
*/
|
|
24
|
-
export interface IMediaPlayerSynchronizerEvent extends
|
|
24
|
+
export interface IMediaPlayerSynchronizerEvent extends ILiveEvent {
|
|
25
25
|
/**
|
|
26
26
|
* Event details.
|
|
27
27
|
*/
|
|
@@ -164,7 +164,13 @@ export class MediaPlayerSynchronizer extends EventEmitter {
|
|
|
164
164
|
TelemetryEvents.MediaPlayerSynchronizer
|
|
165
165
|
.UserTappedVideoToPlay
|
|
166
166
|
);
|
|
167
|
-
this.play()
|
|
167
|
+
this.play().catch((err) => {
|
|
168
|
+
this._logger.sendErrorEvent(
|
|
169
|
+
TelemetryEvents.MediaPlayerSynchronizer
|
|
170
|
+
.UserTappedVideoToPlayError,
|
|
171
|
+
err
|
|
172
|
+
);
|
|
173
|
+
});
|
|
168
174
|
}
|
|
169
175
|
}
|
|
170
176
|
// block play if player state is playing when expected synced state is paused and coordinator is not suspended.
|
|
@@ -498,13 +504,15 @@ export class MediaPlayerSynchronizer extends EventEmitter {
|
|
|
498
504
|
* @remarks
|
|
499
505
|
* For proper operation apps should avoid calling `mediaSession.coordinator.play()` directly
|
|
500
506
|
* and instead use the synchronizers `play()` method.
|
|
507
|
+
*
|
|
508
|
+
* @returns a void promise that resolves once complete, throws if user does not have proper roles
|
|
501
509
|
*/
|
|
502
|
-
public play(): void {
|
|
510
|
+
public async play(): Promise<void> {
|
|
503
511
|
this._logger.sendTelemetryEvent(
|
|
504
512
|
TelemetryEvents.MediaPlayerSynchronizer.PlayCalled
|
|
505
513
|
);
|
|
506
514
|
this._expectedPlaybackState = "playing";
|
|
507
|
-
this._mediaSession.coordinator.play();
|
|
515
|
+
await this._mediaSession.coordinator.play();
|
|
508
516
|
|
|
509
517
|
this.dispatchUserAction({ action: "play" });
|
|
510
518
|
}
|
|
@@ -515,13 +523,15 @@ export class MediaPlayerSynchronizer extends EventEmitter {
|
|
|
515
523
|
* @remarks
|
|
516
524
|
* For proper operation apps should avoid calling `mediaSession.coordinator.pause()` directly
|
|
517
525
|
* and instead use the synchronizers `pause()` method.
|
|
526
|
+
*
|
|
527
|
+
* @returns a void promise that resolves once complete, throws if user does not have proper roles
|
|
518
528
|
*/
|
|
519
|
-
public pause(): void {
|
|
529
|
+
public async pause(): Promise<void> {
|
|
520
530
|
this._logger.sendTelemetryEvent(
|
|
521
531
|
TelemetryEvents.MediaPlayerSynchronizer.PauseCalled
|
|
522
532
|
);
|
|
523
533
|
this._expectedPlaybackState = "paused";
|
|
524
|
-
this._mediaSession.coordinator.pause();
|
|
534
|
+
await this._mediaSession.coordinator.pause();
|
|
525
535
|
|
|
526
536
|
this.dispatchUserAction({ action: "pause" });
|
|
527
537
|
}
|
|
@@ -532,8 +542,10 @@ export class MediaPlayerSynchronizer extends EventEmitter {
|
|
|
532
542
|
* @remarks
|
|
533
543
|
* For proper operation apps should avoid calling `mediaSession.coordinator.seekTo()` directly
|
|
534
544
|
* and instead use the synchronizers `seekTo()` method.
|
|
545
|
+
*
|
|
546
|
+
* @returns a void promise that resolves once complete, throws if user does not have proper roles
|
|
535
547
|
*/
|
|
536
|
-
public seekTo(time: number): void {
|
|
548
|
+
public async seekTo(time: number): Promise<void> {
|
|
537
549
|
// Always seek player to new time.
|
|
538
550
|
// - This resolves an issue where the timeline scrubber can temporarily snap back to the original
|
|
539
551
|
// location.
|
|
@@ -544,7 +556,7 @@ export class MediaPlayerSynchronizer extends EventEmitter {
|
|
|
544
556
|
null,
|
|
545
557
|
{ position: time }
|
|
546
558
|
);
|
|
547
|
-
this._mediaSession.coordinator.seekTo(time);
|
|
559
|
+
await this._mediaSession.coordinator.seekTo(time);
|
|
548
560
|
|
|
549
561
|
this.dispatchUserAction({ action: "seekto", seekTime: time });
|
|
550
562
|
}
|
|
@@ -555,15 +567,17 @@ export class MediaPlayerSynchronizer extends EventEmitter {
|
|
|
555
567
|
* @remarks
|
|
556
568
|
* For proper operation apps should avoid calling `mediaSession.coordinator.setTrack()` directly
|
|
557
569
|
* and instead use the synchronizers `setTrack()` method.
|
|
570
|
+
*
|
|
571
|
+
* @returns a void promise that resolves once complete, throws if user does not have proper roles
|
|
558
572
|
*/
|
|
559
|
-
public setTrack(
|
|
573
|
+
public async setTrack(
|
|
560
574
|
track: ExtendedMediaMetadata,
|
|
561
575
|
waitPoints?: CoordinationWaitPoint[]
|
|
562
|
-
): void {
|
|
576
|
+
): Promise<void> {
|
|
563
577
|
this._logger.sendTelemetryEvent(
|
|
564
578
|
TelemetryEvents.MediaPlayerSynchronizer.SetTrackCalled
|
|
565
579
|
);
|
|
566
|
-
this._mediaSession.coordinator.setTrack(track, waitPoints);
|
|
580
|
+
await this._mediaSession.coordinator.setTrack(track, waitPoints);
|
|
567
581
|
|
|
568
582
|
this.dispatchUserAction({ action: "settrack", metadata: track });
|
|
569
583
|
}
|
|
@@ -574,13 +588,15 @@ export class MediaPlayerSynchronizer extends EventEmitter {
|
|
|
574
588
|
* @remarks
|
|
575
589
|
* For proper operation apps should avoid calling `mediaSession.coordinator.setTrackData()` directly
|
|
576
590
|
* and instead use the synchronizers `setTrackData()` method.
|
|
591
|
+
*
|
|
592
|
+
* @returns a void promise that resolves once complete, throws if user does not have proper roles
|
|
577
593
|
*/
|
|
578
|
-
public setTrackData(data: object | null): void {
|
|
594
|
+
public async setTrackData(data: object | null): Promise<void> {
|
|
579
595
|
this._logger.sendTelemetryEvent(
|
|
580
596
|
TelemetryEvents.MediaPlayerSynchronizer.SetTrackDataCalled
|
|
581
597
|
);
|
|
582
598
|
this._trackData = data;
|
|
583
|
-
this._mediaSession.coordinator.setTrackData(data);
|
|
599
|
+
await this._mediaSession.coordinator.setTrackData(data);
|
|
584
600
|
|
|
585
601
|
this.dispatchUserAction({ action: "datachange", data: data });
|
|
586
602
|
}
|