@microsoft/live-share-media 1.0.0-preview.1 → 1.0.0-preview.3
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/README.md +1 -1
- package/bin/IMediaPlayer.d.ts.map +1 -1
- package/bin/LiveMediaSession.d.ts +7 -7
- package/bin/LiveMediaSession.d.ts.map +1 -1
- package/bin/LiveMediaSession.js +28 -17
- package/bin/LiveMediaSession.js.map +1 -1
- package/bin/LiveMediaSessionCoordinator.d.ts +2 -2
- package/bin/LiveMediaSessionCoordinator.d.ts.map +1 -1
- package/bin/LiveMediaSessionCoordinator.js +34 -22
- package/bin/LiveMediaSessionCoordinator.js.map +1 -1
- package/bin/LiveMediaSessionCoordinatorSuspension.d.ts +1 -1
- package/bin/LiveMediaSessionCoordinatorSuspension.d.ts.map +1 -1
- package/bin/LiveMediaSessionCoordinatorSuspension.js.map +1 -1
- package/bin/MediaPlayerSynchronizer.d.ts +7 -7
- package/bin/MediaPlayerSynchronizer.d.ts.map +1 -1
- package/bin/MediaPlayerSynchronizer.js +121 -69
- package/bin/MediaPlayerSynchronizer.js.map +1 -1
- package/bin/MediaSessionActionThrottler.d.ts +1 -1
- package/bin/MediaSessionActionThrottler.d.ts.map +1 -1
- package/bin/MediaSessionActionThrottler.js.map +1 -1
- package/bin/MediaSessionExtensions.d.ts.map +1 -1
- package/bin/RepeatedActionThrottler.d.ts +2 -2
- package/bin/RepeatedActionThrottler.d.ts.map +1 -1
- package/bin/RepeatedActionThrottler.js +9 -3
- package/bin/RepeatedActionThrottler.js.map +1 -1
- package/bin/VolumeManager.d.ts +1 -1
- package/bin/VolumeManager.d.ts.map +1 -1
- package/bin/VolumeManager.js +4 -2
- package/bin/VolumeManager.js.map +1 -1
- package/bin/index.d.ts +9 -9
- package/bin/index.d.ts.map +1 -1
- package/bin/index.js.map +1 -1
- package/bin/internals/GroupCoordinatorState.d.ts +8 -8
- package/bin/internals/GroupCoordinatorState.d.ts.map +1 -1
- package/bin/internals/GroupCoordinatorState.js +91 -53
- package/bin/internals/GroupCoordinatorState.js.map +1 -1
- package/bin/internals/GroupPlaybackPosition.d.ts +3 -3
- package/bin/internals/GroupPlaybackPosition.d.ts.map +1 -1
- package/bin/internals/GroupPlaybackPosition.js +22 -13
- package/bin/internals/GroupPlaybackPosition.js.map +1 -1
- package/bin/internals/GroupPlaybackTrack.d.ts +4 -4
- package/bin/internals/GroupPlaybackTrack.d.ts.map +1 -1
- package/bin/internals/GroupPlaybackTrack.js +14 -4
- 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 +8 -4
- package/bin/internals/GroupPlaybackTrackData.js.map +1 -1
- package/bin/internals/GroupTransportState.d.ts +5 -5
- package/bin/internals/GroupTransportState.d.ts.map +1 -1
- package/bin/internals/GroupTransportState.js +27 -12
- package/bin/internals/GroupTransportState.js.map +1 -1
- package/bin/internals/consts.d.ts.map +1 -1
- package/bin/internals/consts.js +54 -54
- package/bin/internals/consts.js.map +1 -1
- package/bin/internals/index.d.ts +6 -6
- package/package.json +3 -3
- package/src/IMediaPlayer.ts +10 -2
- package/src/LiveMediaSession.ts +133 -71
- package/src/LiveMediaSessionCoordinator.ts +302 -142
- package/src/LiveMediaSessionCoordinatorSuspension.ts +13 -5
- package/src/MediaPlayerSynchronizer.ts +317 -146
- package/src/MediaSessionActionThrottler.ts +5 -3
- package/src/MediaSessionExtensions.ts +15 -5
- package/src/RepeatedActionThrottler.ts +27 -9
- package/src/VolumeManager.ts +24 -12
- package/src/index.ts +9 -10
- package/src/internals/GroupCoordinatorState.ts +274 -114
- package/src/internals/GroupPlaybackPosition.ts +61 -27
- package/src/internals/GroupPlaybackTrack.ts +51 -22
- package/src/internals/GroupPlaybackTrackData.ts +24 -16
- package/src/internals/GroupTransportState.ts +53 -23
- package/src/internals/consts.ts +73 -55
- package/src/internals/index.ts +6 -6
- package/src/test/GroupPlaybackPosition.spec.ts +371 -111
- package/src/test/GroupPlaybackTrack.spec.ts +91 -35
- package/src/test/SharedMediaSession.spec.ts +64 -28
- package/src/test/SharedMediaSessionCoordinator.spec.ts +86 -46
- package/src/test/VolumeManager.spec.ts +38 -31
- package/tsconfig.json +1 -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
|
@@ -3,10 +3,17 @@
|
|
|
3
3
|
* Licensed under the Microsoft Live Share SDK License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
import {
|
|
7
|
+
LiveEvent,
|
|
8
|
+
IRuntimeSignaler,
|
|
9
|
+
TimeInterval,
|
|
10
|
+
} from "@microsoft/live-share";
|
|
11
|
+
import { GroupTransportState } from "./GroupTransportState";
|
|
12
|
+
import { GroupPlaybackTrackEvents } from "./GroupPlaybackTrack";
|
|
13
|
+
import {
|
|
14
|
+
CoordinationWaitPoint,
|
|
15
|
+
ExtendedMediaSessionPlaybackState,
|
|
16
|
+
} from "../MediaSessionExtensions";
|
|
10
17
|
|
|
11
18
|
/**
|
|
12
19
|
*Per client position
|
|
@@ -14,7 +21,7 @@ import { CoordinationWaitPoint, ExtendedMediaSessionPlaybackState } from '../Med
|
|
|
14
21
|
*/
|
|
15
22
|
export interface ICurrentPlaybackPosition {
|
|
16
23
|
playbackState: ExtendedMediaSessionPlaybackState;
|
|
17
|
-
waitPoint?: CoordinationWaitPoint
|
|
24
|
+
waitPoint?: CoordinationWaitPoint;
|
|
18
25
|
position: number;
|
|
19
26
|
duration?: number;
|
|
20
27
|
timestamp: number;
|
|
@@ -30,25 +37,31 @@ export class GroupPlaybackPosition {
|
|
|
30
37
|
private _updateInterval: TimeInterval;
|
|
31
38
|
private _positions: Map<string, ICurrentPlaybackPosition>;
|
|
32
39
|
|
|
33
|
-
|
|
34
|
-
|
|
40
|
+
constructor(
|
|
41
|
+
transportState: GroupTransportState,
|
|
42
|
+
runtime: IRuntimeSignaler,
|
|
43
|
+
updateInterval: TimeInterval
|
|
44
|
+
) {
|
|
35
45
|
this._transportState = transportState;
|
|
36
46
|
this._runtime = runtime;
|
|
37
47
|
this._updateInterval = updateInterval;
|
|
38
48
|
this._positions = new Map();
|
|
39
49
|
|
|
40
50
|
// Listen for track change
|
|
41
|
-
this._transportState.track.on(
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
51
|
+
this._transportState.track.on(
|
|
52
|
+
GroupPlaybackTrackEvents.trackChange,
|
|
53
|
+
(_) => {
|
|
54
|
+
// Reset position tracking and duration
|
|
55
|
+
this._positions = new Map();
|
|
56
|
+
this.mediaDuration = undefined;
|
|
57
|
+
}
|
|
58
|
+
);
|
|
46
59
|
}
|
|
47
60
|
|
|
48
61
|
/**
|
|
49
62
|
* Returns the latest position info for the local client.
|
|
50
63
|
*/
|
|
51
|
-
public get localPosition(): ICurrentPlaybackPosition|undefined {
|
|
64
|
+
public get localPosition(): ICurrentPlaybackPosition | undefined {
|
|
52
65
|
return this._positions.get(this._runtime.clientId!);
|
|
53
66
|
}
|
|
54
67
|
|
|
@@ -57,7 +70,7 @@ export class GroupPlaybackPosition {
|
|
|
57
70
|
*/
|
|
58
71
|
public get totalClients(): number {
|
|
59
72
|
let cnt = 0;
|
|
60
|
-
this.forEach(_ => cnt++);
|
|
73
|
+
this.forEach((_) => cnt++);
|
|
61
74
|
return cnt;
|
|
62
75
|
}
|
|
63
76
|
|
|
@@ -69,8 +82,8 @@ export class GroupPlaybackPosition {
|
|
|
69
82
|
let playing = 0;
|
|
70
83
|
this.forEach((position, projectedPosition) => {
|
|
71
84
|
switch (position.playbackState) {
|
|
72
|
-
case
|
|
73
|
-
case
|
|
85
|
+
case "none":
|
|
86
|
+
case "ended":
|
|
74
87
|
// not playing.
|
|
75
88
|
break;
|
|
76
89
|
default:
|
|
@@ -88,9 +101,16 @@ export class GroupPlaybackPosition {
|
|
|
88
101
|
public get clientsWaiting(): number {
|
|
89
102
|
let cnt = 0;
|
|
90
103
|
const waitPoint = this.localPosition?.waitPoint;
|
|
91
|
-
if (
|
|
104
|
+
if (
|
|
105
|
+
waitPoint &&
|
|
106
|
+
(waitPoint.maxClients == undefined ||
|
|
107
|
+
this.totalClients <= waitPoint.maxClients)
|
|
108
|
+
) {
|
|
92
109
|
this.forEach((position, projectedPosition) => {
|
|
93
|
-
if (
|
|
110
|
+
if (
|
|
111
|
+
position.playbackState == "suspended" &&
|
|
112
|
+
position.waitPoint
|
|
113
|
+
) {
|
|
94
114
|
cnt++;
|
|
95
115
|
} else if (position.position < waitPoint.position) {
|
|
96
116
|
cnt++;
|
|
@@ -108,9 +128,11 @@ export class GroupPlaybackPosition {
|
|
|
108
128
|
* This is called when calculating the current seekTo position.
|
|
109
129
|
*/
|
|
110
130
|
public get maxPosition(): number {
|
|
111
|
-
if (this._transportState.playbackState ==
|
|
131
|
+
if (this._transportState.playbackState == "playing") {
|
|
112
132
|
const now = LiveEvent.getTimestamp();
|
|
113
|
-
const projected =
|
|
133
|
+
const projected =
|
|
134
|
+
this._transportState.startPosition +
|
|
135
|
+
(now - this._transportState.timestamp) / 1000;
|
|
114
136
|
return this.limitProjectedPosition(projected);
|
|
115
137
|
} else {
|
|
116
138
|
return this._transportState.startPosition;
|
|
@@ -123,21 +145,25 @@ export class GroupPlaybackPosition {
|
|
|
123
145
|
public mediaDuration?: number;
|
|
124
146
|
|
|
125
147
|
public get targetPosition(): number {
|
|
126
|
-
if (this._transportState.playbackState ==
|
|
148
|
+
if (this._transportState.playbackState == "playing") {
|
|
127
149
|
return this.getMostProgressedPosition();
|
|
128
150
|
} else {
|
|
129
151
|
return this._transportState.startPosition;
|
|
130
152
|
}
|
|
131
153
|
}
|
|
132
154
|
|
|
133
|
-
|
|
134
155
|
/**
|
|
135
156
|
* Enumerates every reported playback position.
|
|
136
157
|
* @param callbackFn Function applied to each position,
|
|
137
158
|
*/
|
|
138
|
-
|
|
159
|
+
public forEach(
|
|
160
|
+
callbackFn: (
|
|
161
|
+
position: ICurrentPlaybackPosition,
|
|
162
|
+
projectedPosition: number
|
|
163
|
+
) => void
|
|
164
|
+
): void {
|
|
139
165
|
const now = LiveEvent.getTimestamp();
|
|
140
|
-
const ignoreBefore = now -
|
|
166
|
+
const ignoreBefore = now - this._updateInterval.milliseconds * 2;
|
|
141
167
|
const shouldProject = !this._transportState.track.metadata?.liveStream;
|
|
142
168
|
this._positions.forEach((value, key) => {
|
|
143
169
|
const position = this._positions.get(key)!;
|
|
@@ -146,7 +172,10 @@ export class GroupPlaybackPosition {
|
|
|
146
172
|
if (position.timestamp > ignoreBefore) {
|
|
147
173
|
// Compute projected playback position
|
|
148
174
|
// - This computation does not take into account future wait points.
|
|
149
|
-
const projected =
|
|
175
|
+
const projected =
|
|
176
|
+
position.playbackState == "playing" && shouldProject
|
|
177
|
+
? position.position + (now - position.timestamp) / 1000
|
|
178
|
+
: position.position;
|
|
150
179
|
callbackFn(position, this.limitProjectedPosition(projected));
|
|
151
180
|
}
|
|
152
181
|
});
|
|
@@ -179,7 +208,10 @@ export class GroupPlaybackPosition {
|
|
|
179
208
|
// Compute max progress
|
|
180
209
|
let progress = -1;
|
|
181
210
|
this.forEach((position, projectedPosition) => {
|
|
182
|
-
if (
|
|
211
|
+
if (
|
|
212
|
+
projectedPosition <= maxPosition &&
|
|
213
|
+
projectedPosition > progress
|
|
214
|
+
) {
|
|
183
215
|
progress = projectedPosition;
|
|
184
216
|
}
|
|
185
217
|
});
|
|
@@ -188,6 +220,8 @@ export class GroupPlaybackPosition {
|
|
|
188
220
|
}
|
|
189
221
|
|
|
190
222
|
private limitProjectedPosition(position: number): number {
|
|
191
|
-
return this.mediaDuration != undefined
|
|
223
|
+
return this.mediaDuration != undefined
|
|
224
|
+
? Math.min(position, this.mediaDuration)
|
|
225
|
+
: position;
|
|
192
226
|
}
|
|
193
227
|
}
|
|
@@ -3,46 +3,53 @@
|
|
|
3
3
|
* Licensed under the Microsoft Live Share SDK License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { IEvent } from
|
|
7
|
-
import EventEmitter from
|
|
8
|
-
import { IMediaPlayerState } from
|
|
9
|
-
import {
|
|
6
|
+
import { IEvent } from "@microsoft/live-share";
|
|
7
|
+
import EventEmitter from "events";
|
|
8
|
+
import { IMediaPlayerState } from "../LiveMediaSessionCoordinator";
|
|
9
|
+
import {
|
|
10
|
+
CoordinationWaitPoint,
|
|
11
|
+
ExtendedMediaMetadata,
|
|
12
|
+
} from "../MediaSessionExtensions";
|
|
10
13
|
|
|
11
14
|
/**
|
|
12
15
|
* @hidden
|
|
13
16
|
*/
|
|
14
|
-
|
|
15
|
-
metadata: ExtendedMediaMetadata|null;
|
|
17
|
+
export interface IPlaybackTrack {
|
|
18
|
+
metadata: ExtendedMediaMetadata | null;
|
|
16
19
|
waitPoints: CoordinationWaitPoint[];
|
|
17
20
|
timestamp: number;
|
|
18
21
|
clientId: string;
|
|
19
22
|
}
|
|
20
23
|
|
|
21
|
-
|
|
22
24
|
/**
|
|
23
25
|
* @hidden
|
|
24
26
|
*/
|
|
25
|
-
|
|
26
|
-
trackChange =
|
|
27
|
+
export enum GroupPlaybackTrackEvents {
|
|
28
|
+
trackChange = "trackChange",
|
|
27
29
|
}
|
|
28
30
|
|
|
29
31
|
/**
|
|
30
32
|
* @hidden
|
|
31
33
|
*/
|
|
32
|
-
|
|
33
|
-
metadata: ExtendedMediaMetadata|null;
|
|
34
|
+
export interface IPlaybackTrackChangeEvent extends IEvent {
|
|
35
|
+
metadata: ExtendedMediaMetadata | null;
|
|
34
36
|
}
|
|
35
37
|
|
|
36
38
|
/**
|
|
37
39
|
* @hidden
|
|
38
40
|
*/
|
|
39
|
-
|
|
41
|
+
export class GroupPlaybackTrack extends EventEmitter {
|
|
40
42
|
private readonly _getMediaPlayerState: () => IMediaPlayerState;
|
|
41
43
|
private _current: IPlaybackTrack;
|
|
42
44
|
|
|
43
45
|
constructor(getMediaPlayerState: () => IMediaPlayerState) {
|
|
44
46
|
super();
|
|
45
|
-
this._current = {
|
|
47
|
+
this._current = {
|
|
48
|
+
metadata: null,
|
|
49
|
+
waitPoints: [],
|
|
50
|
+
timestamp: 0,
|
|
51
|
+
clientId: "",
|
|
52
|
+
};
|
|
46
53
|
this._getMediaPlayerState = getMediaPlayerState;
|
|
47
54
|
}
|
|
48
55
|
|
|
@@ -55,7 +62,7 @@ import { CoordinationWaitPoint, ExtendedMediaMetadata } from '../MediaSessionExt
|
|
|
55
62
|
return this._current;
|
|
56
63
|
}
|
|
57
64
|
|
|
58
|
-
public get metadata(): ExtendedMediaMetadata|null {
|
|
65
|
+
public get metadata(): ExtendedMediaMetadata | null {
|
|
59
66
|
return this.current.metadata;
|
|
60
67
|
}
|
|
61
68
|
|
|
@@ -77,7 +84,9 @@ import { CoordinationWaitPoint, ExtendedMediaMetadata } from '../MediaSessionExt
|
|
|
77
84
|
return true;
|
|
78
85
|
}
|
|
79
86
|
|
|
80
|
-
public findNextWaitPoint(
|
|
87
|
+
public findNextWaitPoint(
|
|
88
|
+
lastWaitPoint: CoordinationWaitPoint | undefined
|
|
89
|
+
): CoordinationWaitPoint | undefined {
|
|
81
90
|
const waitPoints = this._current.waitPoints || [];
|
|
82
91
|
for (const waitPoint of waitPoints) {
|
|
83
92
|
if (lastWaitPoint && waitPoint.position <= lastWaitPoint.position) {
|
|
@@ -98,14 +107,22 @@ import { CoordinationWaitPoint, ExtendedMediaMetadata } from '../MediaSessionExt
|
|
|
98
107
|
|
|
99
108
|
// Is this the same track?
|
|
100
109
|
const current = this.current;
|
|
101
|
-
if (
|
|
110
|
+
if (
|
|
111
|
+
GroupPlaybackTrack.compareMetadata(
|
|
112
|
+
this.current.metadata,
|
|
113
|
+
track.metadata
|
|
114
|
+
)
|
|
115
|
+
) {
|
|
102
116
|
// Add any dynamic wait points.
|
|
103
117
|
track.waitPoints.forEach((waitPoint) => {
|
|
104
118
|
this.addWaitPoint(waitPoint);
|
|
105
119
|
});
|
|
106
120
|
|
|
107
121
|
// Ignore if same playback state
|
|
108
|
-
if (
|
|
122
|
+
if (
|
|
123
|
+
track.timestamp == current.timestamp &&
|
|
124
|
+
track.clientId == current.clientId
|
|
125
|
+
) {
|
|
109
126
|
return false;
|
|
110
127
|
}
|
|
111
128
|
}
|
|
@@ -117,7 +134,10 @@ import { CoordinationWaitPoint, ExtendedMediaMetadata } from '../MediaSessionExt
|
|
|
117
134
|
|
|
118
135
|
// Ignore state changes that are from a clientId that sorts higher then the current one.
|
|
119
136
|
// - current.clientId should not be empty if current.timestamp != 0
|
|
120
|
-
if (
|
|
137
|
+
if (
|
|
138
|
+
track.timestamp == current.timestamp &&
|
|
139
|
+
track.clientId.localeCompare(current.clientId) > 0
|
|
140
|
+
) {
|
|
121
141
|
return false;
|
|
122
142
|
}
|
|
123
143
|
|
|
@@ -125,16 +145,25 @@ import { CoordinationWaitPoint, ExtendedMediaMetadata } from '../MediaSessionExt
|
|
|
125
145
|
this._current = track;
|
|
126
146
|
|
|
127
147
|
// Notify listeners
|
|
128
|
-
this.emit(GroupPlaybackTrackEvents.trackChange, {
|
|
148
|
+
this.emit(GroupPlaybackTrackEvents.trackChange, {
|
|
149
|
+
name: GroupPlaybackTrackEvents.trackChange,
|
|
150
|
+
metadata: track.metadata,
|
|
151
|
+
});
|
|
129
152
|
|
|
130
153
|
return true;
|
|
131
154
|
}
|
|
132
155
|
|
|
133
|
-
public compare(metadata: ExtendedMediaMetadata|null): boolean {
|
|
134
|
-
return GroupPlaybackTrack.compareMetadata(
|
|
156
|
+
public compare(metadata: ExtendedMediaMetadata | null): boolean {
|
|
157
|
+
return GroupPlaybackTrack.compareMetadata(
|
|
158
|
+
this.current.metadata,
|
|
159
|
+
metadata
|
|
160
|
+
);
|
|
135
161
|
}
|
|
136
162
|
|
|
137
|
-
public static compareMetadata(
|
|
163
|
+
public static compareMetadata(
|
|
164
|
+
current: ExtendedMediaMetadata | null,
|
|
165
|
+
metadata: ExtendedMediaMetadata | null
|
|
166
|
+
): boolean {
|
|
138
167
|
// Only compare when we have two metadata instances
|
|
139
168
|
if (current && metadata) {
|
|
140
169
|
return JSON.stringify(current) == JSON.stringify(metadata);
|
|
@@ -3,16 +3,18 @@
|
|
|
3
3
|
* Licensed under the Microsoft Live Share SDK License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { IEvent } from
|
|
7
|
-
import EventEmitter from
|
|
8
|
-
import {
|
|
9
|
-
|
|
6
|
+
import { IEvent } from "@microsoft/live-share";
|
|
7
|
+
import EventEmitter from "events";
|
|
8
|
+
import {
|
|
9
|
+
GroupPlaybackTrack,
|
|
10
|
+
GroupPlaybackTrackEvents,
|
|
11
|
+
} from "./GroupPlaybackTrack";
|
|
10
12
|
|
|
11
13
|
/**
|
|
12
14
|
* @hidden
|
|
13
15
|
*/
|
|
14
|
-
|
|
15
|
-
data: object|null;
|
|
16
|
+
export interface IPlaybackTrackData {
|
|
17
|
+
data: object | null;
|
|
16
18
|
timestamp: number;
|
|
17
19
|
clientId: string;
|
|
18
20
|
}
|
|
@@ -20,28 +22,28 @@ import { GroupPlaybackTrack, GroupPlaybackTrackEvents } from './GroupPlaybackTra
|
|
|
20
22
|
/**
|
|
21
23
|
* @hidden
|
|
22
24
|
*/
|
|
23
|
-
|
|
24
|
-
dataChange =
|
|
25
|
+
export enum PlaybackTrackDataEvents {
|
|
26
|
+
dataChange = "dataChange",
|
|
25
27
|
}
|
|
26
28
|
|
|
27
29
|
/**
|
|
28
30
|
* @hidden
|
|
29
31
|
*/
|
|
30
32
|
export interface IPlaybackTrackDataChangeEvent extends IEvent {
|
|
31
|
-
data: object|null;
|
|
33
|
+
data: object | null;
|
|
32
34
|
}
|
|
33
35
|
|
|
34
36
|
/**
|
|
35
37
|
* @hidden
|
|
36
38
|
*/
|
|
37
|
-
|
|
39
|
+
export class GroupPlaybackTrackData extends EventEmitter {
|
|
38
40
|
private _track: GroupPlaybackTrack;
|
|
39
41
|
private _current: IPlaybackTrackData;
|
|
40
42
|
|
|
41
43
|
constructor(track: GroupPlaybackTrack) {
|
|
42
44
|
super();
|
|
43
45
|
this._track = track;
|
|
44
|
-
this._current = { data: null, timestamp: 0, clientId:
|
|
46
|
+
this._current = { data: null, timestamp: 0, clientId: "" };
|
|
45
47
|
|
|
46
48
|
// Listen for track changes
|
|
47
49
|
this._track.on(GroupPlaybackTrackEvents.trackChange, () => {
|
|
@@ -49,8 +51,8 @@ export interface IPlaybackTrackDataChangeEvent extends IEvent {
|
|
|
49
51
|
this._current = {
|
|
50
52
|
data: null,
|
|
51
53
|
timestamp: this._track.current.timestamp,
|
|
52
|
-
clientId: this._track.current.clientId
|
|
53
|
-
}
|
|
54
|
+
clientId: this._track.current.clientId,
|
|
55
|
+
};
|
|
54
56
|
});
|
|
55
57
|
}
|
|
56
58
|
|
|
@@ -58,7 +60,7 @@ export interface IPlaybackTrackDataChangeEvent extends IEvent {
|
|
|
58
60
|
return this._current;
|
|
59
61
|
}
|
|
60
62
|
|
|
61
|
-
public get data(): object|null {
|
|
63
|
+
public get data(): object | null {
|
|
62
64
|
return this.current.data;
|
|
63
65
|
}
|
|
64
66
|
|
|
@@ -70,7 +72,10 @@ export interface IPlaybackTrackDataChangeEvent extends IEvent {
|
|
|
70
72
|
}
|
|
71
73
|
|
|
72
74
|
// Ignore state changes that have the same timestamp and the clientId sorts higher.
|
|
73
|
-
if (
|
|
75
|
+
if (
|
|
76
|
+
event.timestamp == current.timestamp &&
|
|
77
|
+
event.clientId.localeCompare(current.clientId) > 0
|
|
78
|
+
) {
|
|
74
79
|
return false;
|
|
75
80
|
}
|
|
76
81
|
|
|
@@ -83,7 +88,10 @@ export interface IPlaybackTrackDataChangeEvent extends IEvent {
|
|
|
83
88
|
this._current = event;
|
|
84
89
|
|
|
85
90
|
// Notify listeners
|
|
86
|
-
this.emit(PlaybackTrackDataEvents.dataChange, {
|
|
91
|
+
this.emit(PlaybackTrackDataEvents.dataChange, {
|
|
92
|
+
type: PlaybackTrackDataEvents.dataChange,
|
|
93
|
+
data: event.data,
|
|
94
|
+
});
|
|
87
95
|
|
|
88
96
|
return true;
|
|
89
97
|
}
|
|
@@ -3,17 +3,22 @@
|
|
|
3
3
|
* Licensed under the Microsoft Live Share SDK License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { IEvent, LiveEvent } from
|
|
7
|
-
import EventEmitter from
|
|
8
|
-
import { IMediaPlayerState } from
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
import { IEvent, LiveEvent } from "@microsoft/live-share";
|
|
7
|
+
import EventEmitter from "events";
|
|
8
|
+
import { IMediaPlayerState } from "../LiveMediaSessionCoordinator";
|
|
9
|
+
import {
|
|
10
|
+
ExtendedMediaSessionPlaybackState,
|
|
11
|
+
ExtendedMediaSessionAction,
|
|
12
|
+
} from "../MediaSessionExtensions";
|
|
13
|
+
import {
|
|
14
|
+
GroupPlaybackTrack,
|
|
15
|
+
GroupPlaybackTrackEvents,
|
|
16
|
+
} from "./GroupPlaybackTrack";
|
|
12
17
|
|
|
13
18
|
/**
|
|
14
19
|
* @hidden
|
|
15
20
|
*/
|
|
16
|
-
|
|
21
|
+
export interface ITransportState {
|
|
17
22
|
playbackState: ExtendedMediaSessionPlaybackState;
|
|
18
23
|
startPosition: number;
|
|
19
24
|
timestamp: number;
|
|
@@ -24,7 +29,7 @@ import { GroupPlaybackTrack, GroupPlaybackTrackEvents } from './GroupPlaybackTra
|
|
|
24
29
|
* @hidden
|
|
25
30
|
*/
|
|
26
31
|
export enum GroupTransportStateEvents {
|
|
27
|
-
transportStateChange =
|
|
32
|
+
transportStateChange = "transportStateChange",
|
|
28
33
|
}
|
|
29
34
|
|
|
30
35
|
/**
|
|
@@ -38,31 +43,34 @@ export interface ITransportStateChangeEvent extends IEvent {
|
|
|
38
43
|
/**
|
|
39
44
|
* @hidden
|
|
40
45
|
*/
|
|
41
|
-
|
|
46
|
+
export class GroupTransportState extends EventEmitter {
|
|
42
47
|
private readonly _getMediaPlayerState: () => IMediaPlayerState;
|
|
43
48
|
private _track: GroupPlaybackTrack;
|
|
44
49
|
private _current: ITransportState;
|
|
45
50
|
|
|
46
|
-
constructor(
|
|
51
|
+
constructor(
|
|
52
|
+
track: GroupPlaybackTrack,
|
|
53
|
+
getMediaPlayerState: () => IMediaPlayerState
|
|
54
|
+
) {
|
|
47
55
|
super();
|
|
48
56
|
this._getMediaPlayerState = getMediaPlayerState;
|
|
49
57
|
this._track = track;
|
|
50
58
|
this._current = {
|
|
51
|
-
playbackState:
|
|
59
|
+
playbackState: "none",
|
|
52
60
|
startPosition: 0.0,
|
|
53
61
|
timestamp: 0,
|
|
54
|
-
clientId:
|
|
62
|
+
clientId: "",
|
|
55
63
|
};
|
|
56
64
|
|
|
57
65
|
// Listen for track changes
|
|
58
66
|
this._track.on(GroupPlaybackTrackEvents.trackChange, () => {
|
|
59
67
|
// Track changed so reset state to stopped ad position 0.0
|
|
60
68
|
this._current = {
|
|
61
|
-
playbackState:
|
|
69
|
+
playbackState: "none",
|
|
62
70
|
startPosition: 0.0,
|
|
63
71
|
timestamp: this._track.current.timestamp,
|
|
64
|
-
clientId: this._track.current.clientId
|
|
65
|
-
}
|
|
72
|
+
clientId: this._track.current.clientId,
|
|
73
|
+
};
|
|
66
74
|
});
|
|
67
75
|
}
|
|
68
76
|
|
|
@@ -87,7 +95,10 @@ export interface ITransportStateChangeEvent extends IEvent {
|
|
|
87
95
|
}
|
|
88
96
|
|
|
89
97
|
public compare(state: ITransportState): boolean {
|
|
90
|
-
return
|
|
98
|
+
return (
|
|
99
|
+
this.current.playbackState == state.playbackState &&
|
|
100
|
+
this.current.startPosition == state.startPosition
|
|
101
|
+
);
|
|
91
102
|
}
|
|
92
103
|
|
|
93
104
|
public updateState(state: ITransportState): boolean {
|
|
@@ -103,7 +114,10 @@ export interface ITransportStateChangeEvent extends IEvent {
|
|
|
103
114
|
}
|
|
104
115
|
|
|
105
116
|
// Ignore state changes that have the same timestamp and the clientId sorts higher.
|
|
106
|
-
if (
|
|
117
|
+
if (
|
|
118
|
+
state.timestamp == originalState.timestamp &&
|
|
119
|
+
state.clientId.localeCompare(originalState.clientId) > 0
|
|
120
|
+
) {
|
|
107
121
|
return false;
|
|
108
122
|
}
|
|
109
123
|
|
|
@@ -112,14 +126,30 @@ export interface ITransportStateChangeEvent extends IEvent {
|
|
|
112
126
|
|
|
113
127
|
// Trigger transport change
|
|
114
128
|
const playerState = this._getMediaPlayerState().playbackState;
|
|
115
|
-
if (
|
|
116
|
-
|
|
117
|
-
|
|
129
|
+
if (
|
|
130
|
+
originalState.playbackState == state.playbackState &&
|
|
131
|
+
playerState != "ended"
|
|
132
|
+
) {
|
|
133
|
+
this.emit(GroupTransportStateEvents.transportStateChange, {
|
|
134
|
+
type: GroupTransportStateEvents.transportStateChange,
|
|
135
|
+
action: "seekto",
|
|
136
|
+
seekTime: state.startPosition,
|
|
137
|
+
});
|
|
138
|
+
} else if (state.playbackState == "playing") {
|
|
118
139
|
const now = LiveEvent.getTimestamp();
|
|
119
|
-
const projectedPosition =
|
|
120
|
-
|
|
140
|
+
const projectedPosition =
|
|
141
|
+
state.startPosition + (now - state.timestamp) / 1000;
|
|
142
|
+
this.emit(GroupTransportStateEvents.transportStateChange, {
|
|
143
|
+
type: GroupTransportStateEvents.transportStateChange,
|
|
144
|
+
action: "play",
|
|
145
|
+
seekTime: projectedPosition,
|
|
146
|
+
});
|
|
121
147
|
} else {
|
|
122
|
-
this.emit(GroupTransportStateEvents.transportStateChange, {
|
|
148
|
+
this.emit(GroupTransportStateEvents.transportStateChange, {
|
|
149
|
+
type: GroupTransportStateEvents.transportStateChange,
|
|
150
|
+
action: "pause",
|
|
151
|
+
seekTime: state.startPosition,
|
|
152
|
+
});
|
|
123
153
|
}
|
|
124
154
|
|
|
125
155
|
return true;
|