@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
|
@@ -23,8 +23,13 @@ import {
|
|
|
23
23
|
GroupPlaybackTrack,
|
|
24
24
|
GroupPlaybackTrackEvents,
|
|
25
25
|
IPlaybackTrack,
|
|
26
|
+
IPlaybackTrackChangeEvent,
|
|
26
27
|
} from "./GroupPlaybackTrack";
|
|
27
|
-
import {
|
|
28
|
+
import {
|
|
29
|
+
GroupTransportState,
|
|
30
|
+
ITransportState,
|
|
31
|
+
ITransportStateChangeEvent,
|
|
32
|
+
} from "./GroupTransportState";
|
|
28
33
|
import {
|
|
29
34
|
GroupPlaybackPosition,
|
|
30
35
|
ICurrentPlaybackPosition,
|
|
@@ -35,9 +40,16 @@ import {
|
|
|
35
40
|
GroupPlaybackTrackData,
|
|
36
41
|
PlaybackTrackDataEvents,
|
|
37
42
|
IPlaybackTrackData,
|
|
43
|
+
IPlaybackTrackDataChangeEvent,
|
|
38
44
|
} from "./GroupPlaybackTrackData";
|
|
39
45
|
import { TelemetryEvents } from "./consts";
|
|
40
46
|
import { waitUntilConnected } from "@microsoft/live-share/bin/internals";
|
|
47
|
+
import {
|
|
48
|
+
GroupPlaybackRate,
|
|
49
|
+
IPlaybackRate,
|
|
50
|
+
IPlaybackRateChangeEvent,
|
|
51
|
+
PlaybackRateEvents,
|
|
52
|
+
} from "./GroupPlaybackRate";
|
|
41
53
|
|
|
42
54
|
/**
|
|
43
55
|
* @hidden
|
|
@@ -46,6 +58,7 @@ export interface IPositionUpdateEvent {
|
|
|
46
58
|
track: IPlaybackTrack;
|
|
47
59
|
trackData: IPlaybackTrackData;
|
|
48
60
|
transport: ITransportState;
|
|
61
|
+
playbackRate: IPlaybackRate;
|
|
49
62
|
playbackState: ExtendedMediaSessionPlaybackState;
|
|
50
63
|
position: number;
|
|
51
64
|
waitPoint?: CoordinationWaitPoint;
|
|
@@ -59,6 +72,13 @@ export interface ITransportCommandEvent {
|
|
|
59
72
|
position: number;
|
|
60
73
|
}
|
|
61
74
|
|
|
75
|
+
/**
|
|
76
|
+
* @hidden
|
|
77
|
+
*/
|
|
78
|
+
export interface IRateChangeCommandEvent {
|
|
79
|
+
playbackRate: number;
|
|
80
|
+
}
|
|
81
|
+
|
|
62
82
|
/**
|
|
63
83
|
* @hidden
|
|
64
84
|
*/
|
|
@@ -87,6 +107,7 @@ export interface ITriggerActionEvent extends IEvent {
|
|
|
87
107
|
export enum GroupCoordinatorStateEvents {
|
|
88
108
|
newwaitpoint = "newwaitpoint",
|
|
89
109
|
triggeraction = "triggeraction",
|
|
110
|
+
triggeractionignored = "triggeractionignored",
|
|
90
111
|
}
|
|
91
112
|
|
|
92
113
|
/**
|
|
@@ -104,6 +125,7 @@ export class GroupCoordinatorState extends EventEmitter {
|
|
|
104
125
|
private _playbackTrackData: GroupPlaybackTrackData;
|
|
105
126
|
private _transportState: GroupTransportState;
|
|
106
127
|
private _playbackPosition: GroupPlaybackPosition;
|
|
128
|
+
private _playbackRate: GroupPlaybackRate;
|
|
107
129
|
|
|
108
130
|
// Suspension tracking
|
|
109
131
|
private _suspensionCnt: number = 0;
|
|
@@ -132,13 +154,16 @@ export class GroupCoordinatorState extends EventEmitter {
|
|
|
132
154
|
this._playbackTrackData = new GroupPlaybackTrackData(
|
|
133
155
|
this._playbackTrack
|
|
134
156
|
);
|
|
157
|
+
this._playbackRate = new GroupPlaybackRate();
|
|
135
158
|
this._transportState = new GroupTransportState(
|
|
136
159
|
this._playbackTrack,
|
|
160
|
+
this._playbackRate,
|
|
137
161
|
getMediaPlayerState,
|
|
138
162
|
this._liveRuntime
|
|
139
163
|
);
|
|
140
164
|
this._playbackPosition = new GroupPlaybackPosition(
|
|
141
165
|
this._transportState,
|
|
166
|
+
this._playbackRate,
|
|
142
167
|
this._runtime,
|
|
143
168
|
this._liveRuntime,
|
|
144
169
|
positionUpdateInterval
|
|
@@ -146,104 +171,122 @@ export class GroupCoordinatorState extends EventEmitter {
|
|
|
146
171
|
this._getMediaPlayerState = getMediaPlayerState;
|
|
147
172
|
|
|
148
173
|
// Listen track related events
|
|
149
|
-
this._playbackTrack.on(
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
174
|
+
this._playbackTrack.on(
|
|
175
|
+
GroupPlaybackTrackEvents.trackChange,
|
|
176
|
+
(evt: IPlaybackTrackChangeEvent) => {
|
|
177
|
+
if (!this.isSuspended) {
|
|
178
|
+
this._logger.sendTelemetryEvent(
|
|
179
|
+
TelemetryEvents.GroupCoordinator.TrackChanged
|
|
180
|
+
);
|
|
181
|
+
this.emitSetTrack(evt.clientId, evt.metadata!, evt.source);
|
|
182
|
+
} else {
|
|
183
|
+
this._logger.sendTelemetryEvent(
|
|
184
|
+
TelemetryEvents.GroupCoordinator.TrackChangeDelayed
|
|
185
|
+
);
|
|
186
|
+
}
|
|
159
187
|
}
|
|
160
|
-
|
|
188
|
+
);
|
|
161
189
|
|
|
162
190
|
// Listen for track data changes
|
|
163
191
|
this._playbackTrackData.on(
|
|
164
192
|
PlaybackTrackDataEvents.dataChange,
|
|
165
|
-
async (evt) => {
|
|
193
|
+
async (evt: IPlaybackTrackDataChangeEvent) => {
|
|
166
194
|
if (!this.isSuspended && !this.isWaiting) {
|
|
167
195
|
this._logger.sendTelemetryEvent(
|
|
168
196
|
TelemetryEvents.GroupCoordinator.TrackDataChanged
|
|
169
197
|
);
|
|
170
|
-
const clientId = await waitUntilConnected(this._runtime);
|
|
171
|
-
this.emitTriggerAction({
|
|
172
|
-
action: "datachange",
|
|
173
|
-
source: evt.source,
|
|
174
|
-
data: evt.data,
|
|
175
|
-
clientId: evt.clientId,
|
|
176
|
-
local: evt.clientId === clientId,
|
|
177
|
-
});
|
|
178
198
|
} else {
|
|
179
199
|
this._logger.sendTelemetryEvent(
|
|
180
200
|
TelemetryEvents.GroupCoordinator.TrackDataChangeDelayed
|
|
181
201
|
);
|
|
182
202
|
}
|
|
203
|
+
const clientId = await waitUntilConnected(this._runtime);
|
|
204
|
+
this.emitTriggerActionOrIgnored({
|
|
205
|
+
action: "datachange",
|
|
206
|
+
source: evt.source,
|
|
207
|
+
data: evt.data,
|
|
208
|
+
clientId: evt.clientId,
|
|
209
|
+
local: evt.clientId === clientId,
|
|
210
|
+
});
|
|
183
211
|
}
|
|
184
212
|
);
|
|
185
213
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
async (evt) => {
|
|
214
|
+
this._playbackRate.on(
|
|
215
|
+
PlaybackRateEvents.rateChange,
|
|
216
|
+
async (evt: IPlaybackRateChangeEvent) => {
|
|
190
217
|
if (!this.isSuspended && !this.isWaiting) {
|
|
191
218
|
this._logger.sendTelemetryEvent(
|
|
192
|
-
TelemetryEvents.GroupCoordinator.
|
|
193
|
-
null,
|
|
194
|
-
{
|
|
195
|
-
action: evt.action,
|
|
196
|
-
seekTime: evt.seekTime,
|
|
197
|
-
}
|
|
219
|
+
TelemetryEvents.GroupCoordinator.PlaybackRateChanged
|
|
198
220
|
);
|
|
199
|
-
|
|
200
|
-
const localClientId = await waitUntilConnected(
|
|
201
|
-
this._runtime
|
|
202
|
-
);
|
|
203
|
-
const local = evt.clientId === localClientId;
|
|
204
|
-
// Trigger action
|
|
205
|
-
switch (evt.action) {
|
|
206
|
-
case "play":
|
|
207
|
-
this.emitTriggerAction({
|
|
208
|
-
action: "play",
|
|
209
|
-
source: evt.source,
|
|
210
|
-
clientId: evt.clientId,
|
|
211
|
-
local,
|
|
212
|
-
seekTime: evt.seekTime,
|
|
213
|
-
});
|
|
214
|
-
break;
|
|
215
|
-
|
|
216
|
-
case "pause":
|
|
217
|
-
this.emitTriggerAction({
|
|
218
|
-
action: "pause",
|
|
219
|
-
source: evt.source,
|
|
220
|
-
clientId: evt.clientId,
|
|
221
|
-
local,
|
|
222
|
-
seekTime: evt.seekTime,
|
|
223
|
-
});
|
|
224
|
-
break;
|
|
225
|
-
|
|
226
|
-
case "seekto":
|
|
227
|
-
this.emitTriggerAction({
|
|
228
|
-
action: "seekto",
|
|
229
|
-
source: evt.source,
|
|
230
|
-
clientId: evt.clientId,
|
|
231
|
-
local,
|
|
232
|
-
seekTime: evt.seekTime,
|
|
233
|
-
});
|
|
234
|
-
break;
|
|
235
|
-
}
|
|
236
221
|
} else {
|
|
237
222
|
this._logger.sendTelemetryEvent(
|
|
238
223
|
TelemetryEvents.GroupCoordinator
|
|
239
|
-
.
|
|
240
|
-
null,
|
|
241
|
-
{
|
|
242
|
-
action: evt.action,
|
|
243
|
-
seekTime: evt.seekTime,
|
|
244
|
-
}
|
|
224
|
+
.PlaybackRateChangedDelayed
|
|
245
225
|
);
|
|
246
226
|
}
|
|
227
|
+
|
|
228
|
+
const localClientId = await waitUntilConnected(this._runtime);
|
|
229
|
+
const local = evt.clientId === localClientId;
|
|
230
|
+
this.emitTriggerActionOrIgnored({
|
|
231
|
+
action: "ratechange",
|
|
232
|
+
source: evt.source,
|
|
233
|
+
clientId: evt.clientId,
|
|
234
|
+
local,
|
|
235
|
+
playbackRate: evt.playbackRate,
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
);
|
|
239
|
+
|
|
240
|
+
// Listen to transport related events
|
|
241
|
+
this._transportState.on(
|
|
242
|
+
GroupTransportStateEvents.transportStateChange,
|
|
243
|
+
async (evt: ITransportStateChangeEvent) => {
|
|
244
|
+
const tag =
|
|
245
|
+
this.isSuspended || this.isWaiting
|
|
246
|
+
? TelemetryEvents.GroupCoordinator
|
|
247
|
+
.TransportStateChangeDelayed
|
|
248
|
+
: TelemetryEvents.GroupCoordinator
|
|
249
|
+
.TransportStateChanged;
|
|
250
|
+
|
|
251
|
+
this._logger.sendTelemetryEvent(tag, null, {
|
|
252
|
+
action: evt.action,
|
|
253
|
+
seekTime: evt.seekTime,
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
const localClientId = await waitUntilConnected(this._runtime);
|
|
257
|
+
const local = evt.clientId === localClientId;
|
|
258
|
+
// Trigger action
|
|
259
|
+
switch (evt.action) {
|
|
260
|
+
case "play":
|
|
261
|
+
this.emitTriggerActionOrIgnored({
|
|
262
|
+
action: "play",
|
|
263
|
+
source: evt.source,
|
|
264
|
+
clientId: evt.clientId,
|
|
265
|
+
local,
|
|
266
|
+
seekTime: evt.seekTime,
|
|
267
|
+
});
|
|
268
|
+
break;
|
|
269
|
+
|
|
270
|
+
case "pause":
|
|
271
|
+
this.emitTriggerActionOrIgnored({
|
|
272
|
+
action: "pause",
|
|
273
|
+
source: evt.source,
|
|
274
|
+
clientId: evt.clientId,
|
|
275
|
+
local,
|
|
276
|
+
seekTime: evt.seekTime,
|
|
277
|
+
});
|
|
278
|
+
break;
|
|
279
|
+
|
|
280
|
+
case "seekto":
|
|
281
|
+
this.emitTriggerActionOrIgnored({
|
|
282
|
+
action: "seekto",
|
|
283
|
+
source: evt.source,
|
|
284
|
+
clientId: evt.clientId,
|
|
285
|
+
local,
|
|
286
|
+
seekTime: evt.seekTime,
|
|
287
|
+
});
|
|
288
|
+
break;
|
|
289
|
+
}
|
|
247
290
|
}
|
|
248
291
|
);
|
|
249
292
|
}
|
|
@@ -264,6 +307,10 @@ export class GroupCoordinatorState extends EventEmitter {
|
|
|
264
307
|
return this._playbackPosition;
|
|
265
308
|
}
|
|
266
309
|
|
|
310
|
+
public get playbackRate(): GroupPlaybackRate {
|
|
311
|
+
return this._playbackRate;
|
|
312
|
+
}
|
|
313
|
+
|
|
267
314
|
public get waitingAt(): CoordinationWaitPoint | undefined {
|
|
268
315
|
return this._waitPoint;
|
|
269
316
|
}
|
|
@@ -312,6 +359,7 @@ export class GroupCoordinatorState extends EventEmitter {
|
|
|
312
359
|
track: this.playbackTrack.current,
|
|
313
360
|
trackData: this.playbackTrackData.current,
|
|
314
361
|
transport: this.transportState.current,
|
|
362
|
+
playbackRate: this.playbackRate.current,
|
|
315
363
|
playbackState: "suspended",
|
|
316
364
|
position: positionState?.position || 0.0,
|
|
317
365
|
waitPoint: this._waitPoint,
|
|
@@ -322,6 +370,7 @@ export class GroupCoordinatorState extends EventEmitter {
|
|
|
322
370
|
track: this.playbackTrack.current,
|
|
323
371
|
trackData: this.playbackTrackData.current,
|
|
324
372
|
transport: this.transportState.current,
|
|
373
|
+
playbackRate: this.playbackRate.current,
|
|
325
374
|
playbackState: this._waitPoint ? "waiting" : playbackState,
|
|
326
375
|
position: positionState?.position || 0.0,
|
|
327
376
|
waitPoint: this._waitPoint,
|
|
@@ -388,6 +437,32 @@ export class GroupCoordinatorState extends EventEmitter {
|
|
|
388
437
|
}
|
|
389
438
|
}
|
|
390
439
|
|
|
440
|
+
public handleRateChangeCommand(
|
|
441
|
+
event: ILiveEvent<IRateChangeCommandEvent>,
|
|
442
|
+
local: boolean
|
|
443
|
+
): void {
|
|
444
|
+
const newState: IPlaybackRate = {
|
|
445
|
+
playbackRate: event.data.playbackRate,
|
|
446
|
+
clientId: event.clientId,
|
|
447
|
+
timestamp: event.timestamp,
|
|
448
|
+
};
|
|
449
|
+
const updated = this.playbackRate.updatePlaybackRate(newState, "user");
|
|
450
|
+
|
|
451
|
+
if (updated) {
|
|
452
|
+
const correlationId = LiveTelemetryLogger.formatCorrelationId(
|
|
453
|
+
event.clientId,
|
|
454
|
+
event.timestamp
|
|
455
|
+
);
|
|
456
|
+
this._logger.sendTelemetryEvent(
|
|
457
|
+
TelemetryEvents.SessionCoordinator.RemoteRateChangeReceived,
|
|
458
|
+
null,
|
|
459
|
+
{
|
|
460
|
+
correlationId: correlationId,
|
|
461
|
+
}
|
|
462
|
+
);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
391
466
|
public handleTransportCommand(
|
|
392
467
|
event: ILiveEvent<ITransportCommandEvent>,
|
|
393
468
|
local: boolean
|
|
@@ -579,7 +654,11 @@ export class GroupCoordinatorState extends EventEmitter {
|
|
|
579
654
|
if (
|
|
580
655
|
target >= 0.0 &&
|
|
581
656
|
Math.abs(target - position) >=
|
|
582
|
-
|
|
657
|
+
Math.max(
|
|
658
|
+
this._maxPlaybackDrift.seconds,
|
|
659
|
+
this._maxPlaybackDrift.seconds *
|
|
660
|
+
this._playbackRate.rate
|
|
661
|
+
)
|
|
583
662
|
) {
|
|
584
663
|
this._logger.sendTelemetryEvent(
|
|
585
664
|
TelemetryEvents.GroupCoordinator.PositionOutOfSync,
|
|
@@ -589,7 +668,7 @@ export class GroupCoordinatorState extends EventEmitter {
|
|
|
589
668
|
target: target,
|
|
590
669
|
}
|
|
591
670
|
);
|
|
592
|
-
|
|
671
|
+
this.emitTriggerAction({
|
|
593
672
|
action: "catchup",
|
|
594
673
|
source: "system",
|
|
595
674
|
clientId: await waitUntilConnected(this._runtime),
|
|
@@ -613,7 +692,7 @@ export class GroupCoordinatorState extends EventEmitter {
|
|
|
613
692
|
localClientId === this.transportState.current.clientId;
|
|
614
693
|
switch (this.transportState.playbackState) {
|
|
615
694
|
case "playing":
|
|
616
|
-
|
|
695
|
+
this.emitTriggerAction({
|
|
617
696
|
action: "play",
|
|
618
697
|
source: "system",
|
|
619
698
|
clientId: this.transportState.current.clientId,
|
|
@@ -621,7 +700,7 @@ export class GroupCoordinatorState extends EventEmitter {
|
|
|
621
700
|
});
|
|
622
701
|
break;
|
|
623
702
|
case "paused":
|
|
624
|
-
|
|
703
|
+
this.emitTriggerAction({
|
|
625
704
|
action: "pause",
|
|
626
705
|
source: "system",
|
|
627
706
|
clientId: this.transportState.current.clientId,
|
|
@@ -642,7 +721,7 @@ export class GroupCoordinatorState extends EventEmitter {
|
|
|
642
721
|
const local =
|
|
643
722
|
localClientId ===
|
|
644
723
|
this.playbackTrackData.current.clientId;
|
|
645
|
-
|
|
724
|
+
this.emitTriggerAction({
|
|
646
725
|
action: "datachange",
|
|
647
726
|
source: "system",
|
|
648
727
|
clientId: this.playbackTrackData.current.clientId,
|
|
@@ -650,6 +729,23 @@ export class GroupCoordinatorState extends EventEmitter {
|
|
|
650
729
|
data: this.playbackTrackData.data,
|
|
651
730
|
});
|
|
652
731
|
}
|
|
732
|
+
|
|
733
|
+
// Sync playback rate
|
|
734
|
+
if (this.playbackRate.rate != positionState?.playbackRate) {
|
|
735
|
+
this._logger.sendTelemetryEvent(
|
|
736
|
+
TelemetryEvents.GroupCoordinator.PlaybackRateOutOfSync
|
|
737
|
+
);
|
|
738
|
+
const local =
|
|
739
|
+
localClientId ===
|
|
740
|
+
this.playbackTrackData.current.clientId;
|
|
741
|
+
this.emitTriggerAction({
|
|
742
|
+
action: "ratechange",
|
|
743
|
+
source: "system",
|
|
744
|
+
clientId: this.playbackTrackData.current.clientId,
|
|
745
|
+
local,
|
|
746
|
+
playbackRate: this.playbackRate.rate,
|
|
747
|
+
});
|
|
748
|
+
}
|
|
653
749
|
}
|
|
654
750
|
}
|
|
655
751
|
}
|
|
@@ -673,6 +769,16 @@ export class GroupCoordinatorState extends EventEmitter {
|
|
|
673
769
|
});
|
|
674
770
|
}
|
|
675
771
|
|
|
772
|
+
private emitTriggerActionOrIgnored(
|
|
773
|
+
details: ExtendedMediaSessionActionDetails
|
|
774
|
+
): void {
|
|
775
|
+
if (this.isSuspended || this.isWaiting) {
|
|
776
|
+
return this.emitTriggerActionIgnored(details);
|
|
777
|
+
} else {
|
|
778
|
+
return this.emitTriggerAction(details);
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
|
|
676
782
|
private emitTriggerAction(
|
|
677
783
|
details: ExtendedMediaSessionActionDetails
|
|
678
784
|
): void {
|
|
@@ -682,4 +788,15 @@ export class GroupCoordinatorState extends EventEmitter {
|
|
|
682
788
|
};
|
|
683
789
|
this.emit(GroupCoordinatorStateEvents.triggeraction, evt);
|
|
684
790
|
}
|
|
791
|
+
|
|
792
|
+
private emitTriggerActionIgnored(
|
|
793
|
+
details: ExtendedMediaSessionActionDetails
|
|
794
|
+
): void {
|
|
795
|
+
details.ignoreReason = "localusersuspended";
|
|
796
|
+
const evt: ITriggerActionEvent = {
|
|
797
|
+
name: GroupCoordinatorStateEvents.triggeractionignored,
|
|
798
|
+
details: details,
|
|
799
|
+
};
|
|
800
|
+
this.emit(GroupCoordinatorStateEvents.triggeractionignored, evt);
|
|
801
|
+
}
|
|
685
802
|
}
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
CoordinationWaitPoint,
|
|
15
15
|
ExtendedMediaSessionPlaybackState,
|
|
16
16
|
} from "../MediaSessionExtensions";
|
|
17
|
+
import { GroupPlaybackRate } from "./GroupPlaybackRate";
|
|
17
18
|
|
|
18
19
|
/**
|
|
19
20
|
* Per client position
|
|
@@ -33,6 +34,7 @@ export interface ICurrentPlaybackPosition {
|
|
|
33
34
|
*/
|
|
34
35
|
export class GroupPlaybackPosition {
|
|
35
36
|
private _transportState: GroupTransportState;
|
|
37
|
+
private _playbackRate: GroupPlaybackRate;
|
|
36
38
|
private _runtime: IRuntimeSignaler;
|
|
37
39
|
private _liveRuntime: LiveShareRuntime;
|
|
38
40
|
private _updateInterval: TimeInterval;
|
|
@@ -40,11 +42,13 @@ export class GroupPlaybackPosition {
|
|
|
40
42
|
|
|
41
43
|
constructor(
|
|
42
44
|
transportState: GroupTransportState,
|
|
45
|
+
playbackRate: GroupPlaybackRate,
|
|
43
46
|
runtime: IRuntimeSignaler,
|
|
44
47
|
liveRuntime: LiveShareRuntime,
|
|
45
48
|
updateInterval: TimeInterval
|
|
46
49
|
) {
|
|
47
50
|
this._transportState = transportState;
|
|
51
|
+
this._playbackRate = playbackRate;
|
|
48
52
|
this._runtime = runtime;
|
|
49
53
|
this._liveRuntime = liveRuntime;
|
|
50
54
|
this._updateInterval = updateInterval;
|
|
@@ -135,7 +139,8 @@ export class GroupPlaybackPosition {
|
|
|
135
139
|
const now = this._liveRuntime.getTimestamp();
|
|
136
140
|
const projected =
|
|
137
141
|
this._transportState.startPosition +
|
|
138
|
-
(now - this._transportState.timestamp) / 1000
|
|
142
|
+
((now - this._transportState.timestamp) / 1000) *
|
|
143
|
+
this._playbackRate.rate;
|
|
139
144
|
return this.limitProjectedPosition(projected);
|
|
140
145
|
} else {
|
|
141
146
|
return this._transportState.startPosition;
|
|
@@ -175,7 +180,9 @@ export class GroupPlaybackPosition {
|
|
|
175
180
|
// - This computation does not take into account future wait points.
|
|
176
181
|
const projected =
|
|
177
182
|
position.playbackState == "playing" && shouldProject
|
|
178
|
-
? position.position +
|
|
183
|
+
? position.position +
|
|
184
|
+
((now - position.timestamp) / 1000) *
|
|
185
|
+
this._playbackRate.rate
|
|
179
186
|
: position.position;
|
|
180
187
|
callbackFn(position, this.limitProjectedPosition(projected));
|
|
181
188
|
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
|
+
* Licensed under the Microsoft Live Share SDK License.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import EventEmitter from "events";
|
|
7
|
+
import { ExtendedMediaSessionActionSource } from "../MediaSessionExtensions";
|
|
8
|
+
import { IGroupStateEvent } from "./interfaces";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @hidden
|
|
12
|
+
*/
|
|
13
|
+
export interface IPlaybackRate {
|
|
14
|
+
playbackRate: number;
|
|
15
|
+
timestamp: number;
|
|
16
|
+
clientId: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @hidden
|
|
21
|
+
*/
|
|
22
|
+
export enum PlaybackRateEvents {
|
|
23
|
+
rateChange = "rateChange",
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @hidden
|
|
28
|
+
*/
|
|
29
|
+
export interface IPlaybackRateChangeEvent extends IGroupStateEvent {
|
|
30
|
+
playbackRate: number;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @hidden
|
|
35
|
+
*/
|
|
36
|
+
export class GroupPlaybackRate extends EventEmitter {
|
|
37
|
+
private _current: IPlaybackRate;
|
|
38
|
+
|
|
39
|
+
constructor() {
|
|
40
|
+
super();
|
|
41
|
+
this._current = { playbackRate: 1.0, timestamp: 0, clientId: "" };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
public get current(): Readonly<IPlaybackRate> {
|
|
45
|
+
return this._current;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
public get rate(): number {
|
|
49
|
+
return this.current.playbackRate;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
public updatePlaybackRate(
|
|
53
|
+
newRate: IPlaybackRate,
|
|
54
|
+
source: ExtendedMediaSessionActionSource
|
|
55
|
+
): boolean {
|
|
56
|
+
// Ignore state changes that are older
|
|
57
|
+
const current = this.current;
|
|
58
|
+
if (newRate.timestamp < current.timestamp) {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Ignore state changes that have the same timestamp and the clientId sorts higher.
|
|
63
|
+
if (
|
|
64
|
+
newRate.timestamp == current.timestamp &&
|
|
65
|
+
newRate.clientId.localeCompare(current.clientId) > 0
|
|
66
|
+
) {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Ignore state changes for same data object
|
|
71
|
+
if (current.playbackRate == newRate.playbackRate) {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Update current data
|
|
76
|
+
this._current = newRate;
|
|
77
|
+
|
|
78
|
+
const event: IPlaybackRateChangeEvent = {
|
|
79
|
+
name: PlaybackRateEvents.rateChange,
|
|
80
|
+
clientId: newRate.clientId,
|
|
81
|
+
playbackRate: newRate.playbackRate,
|
|
82
|
+
source,
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
// Notify listeners
|
|
86
|
+
this.emit(PlaybackRateEvents.rateChange, event);
|
|
87
|
+
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
* Licensed under the Microsoft Live Share SDK License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { ILiveEvent } from "@microsoft/live-share";
|
|
7
6
|
import EventEmitter from "events";
|
|
8
7
|
import { IMediaPlayerState } from "../LiveMediaSessionCoordinator";
|
|
9
8
|
import {
|
|
@@ -11,6 +10,7 @@ import {
|
|
|
11
10
|
ExtendedMediaMetadata,
|
|
12
11
|
ExtendedMediaSessionActionSource,
|
|
13
12
|
} from "../MediaSessionExtensions";
|
|
13
|
+
import { IGroupStateEvent } from "./interfaces";
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* @hidden
|
|
@@ -32,7 +32,7 @@ export enum GroupPlaybackTrackEvents {
|
|
|
32
32
|
/**
|
|
33
33
|
* @hidden
|
|
34
34
|
*/
|
|
35
|
-
export interface IPlaybackTrackChangeEvent extends
|
|
35
|
+
export interface IPlaybackTrackChangeEvent extends IGroupStateEvent {
|
|
36
36
|
metadata: ExtendedMediaMetadata | null;
|
|
37
37
|
}
|
|
38
38
|
|
|
@@ -149,12 +149,13 @@ export class GroupPlaybackTrack extends EventEmitter {
|
|
|
149
149
|
this._current = track;
|
|
150
150
|
|
|
151
151
|
// Notify listeners
|
|
152
|
-
|
|
152
|
+
const event: IPlaybackTrackChangeEvent = {
|
|
153
153
|
name: GroupPlaybackTrackEvents.trackChange,
|
|
154
154
|
clientId: track.clientId,
|
|
155
155
|
metadata: track.metadata,
|
|
156
156
|
source,
|
|
157
|
-
}
|
|
157
|
+
};
|
|
158
|
+
this.emit(GroupPlaybackTrackEvents.trackChange, event);
|
|
158
159
|
|
|
159
160
|
return true;
|
|
160
161
|
}
|
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
* Licensed under the Microsoft Live Share SDK License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { ILiveEvent } from "@microsoft/live-share";
|
|
7
6
|
import EventEmitter from "events";
|
|
8
7
|
import {
|
|
9
8
|
GroupPlaybackTrack,
|
|
10
9
|
GroupPlaybackTrackEvents,
|
|
11
10
|
} from "./GroupPlaybackTrack";
|
|
12
11
|
import { ExtendedMediaSessionActionSource } from "../MediaSessionExtensions";
|
|
12
|
+
import { IGroupStateEvent } from "./interfaces";
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* @hidden
|
|
@@ -30,7 +30,7 @@ export enum PlaybackTrackDataEvents {
|
|
|
30
30
|
/**
|
|
31
31
|
* @hidden
|
|
32
32
|
*/
|
|
33
|
-
export interface IPlaybackTrackDataChangeEvent extends
|
|
33
|
+
export interface IPlaybackTrackDataChangeEvent extends IGroupStateEvent {
|
|
34
34
|
data: object | null;
|
|
35
35
|
}
|
|
36
36
|
|
|
@@ -66,38 +66,39 @@ export class GroupPlaybackTrackData extends EventEmitter {
|
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
public updateData(
|
|
69
|
-
|
|
69
|
+
trackData: IPlaybackTrackData,
|
|
70
70
|
source: ExtendedMediaSessionActionSource
|
|
71
71
|
): boolean {
|
|
72
72
|
// Ignore state changes that are older
|
|
73
73
|
const current = this.current;
|
|
74
|
-
if (
|
|
74
|
+
if (trackData.timestamp < current.timestamp) {
|
|
75
75
|
return false;
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
// Ignore state changes that have the same timestamp and the clientId sorts higher.
|
|
79
79
|
if (
|
|
80
|
-
|
|
81
|
-
|
|
80
|
+
trackData.timestamp == current.timestamp &&
|
|
81
|
+
trackData.clientId.localeCompare(current.clientId) > 0
|
|
82
82
|
) {
|
|
83
83
|
return false;
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
// Ignore state changes for same data object
|
|
87
|
-
if (JSON.stringify(current.data) == JSON.stringify(
|
|
87
|
+
if (JSON.stringify(current.data) == JSON.stringify(trackData.data)) {
|
|
88
88
|
return false;
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
// Update current data
|
|
92
|
-
this._current =
|
|
92
|
+
this._current = trackData;
|
|
93
93
|
|
|
94
94
|
// Notify listeners
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
clientId:
|
|
98
|
-
data:
|
|
95
|
+
const event: IPlaybackTrackDataChangeEvent = {
|
|
96
|
+
name: PlaybackTrackDataEvents.dataChange,
|
|
97
|
+
clientId: trackData.clientId,
|
|
98
|
+
data: trackData.data,
|
|
99
99
|
source,
|
|
100
|
-
}
|
|
100
|
+
};
|
|
101
|
+
this.emit(PlaybackTrackDataEvents.dataChange, event);
|
|
101
102
|
|
|
102
103
|
return true;
|
|
103
104
|
}
|