@microsoft/live-share-media 0.1.2 → 0.2.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/.vscode/settings.json +2 -0
- package/LICENSE +87 -0
- package/README.md +108 -92
- package/bin/EphemeralMediaSession.d.ts +51 -4
- package/bin/EphemeralMediaSession.d.ts.map +1 -1
- package/bin/EphemeralMediaSession.js +91 -44
- package/bin/EphemeralMediaSession.js.map +1 -1
- package/bin/EphemeralMediaSessionCoordinator.d.ts +173 -6
- package/bin/EphemeralMediaSessionCoordinator.d.ts.map +1 -1
- package/bin/EphemeralMediaSessionCoordinator.js +170 -23
- package/bin/EphemeralMediaSessionCoordinator.js.map +1 -1
- package/bin/EphemeralMediaSessionCoordinatorSuspension.d.ts +1 -1
- package/bin/EphemeralMediaSessionCoordinatorSuspension.js +1 -1
- package/bin/IMediaPlayer.d.ts +1 -1
- package/bin/IMediaPlayer.js +1 -1
- package/bin/MediaPlayerSynchronizer.d.ts +95 -6
- package/bin/MediaPlayerSynchronizer.d.ts.map +1 -1
- package/bin/MediaPlayerSynchronizer.js +93 -31
- package/bin/MediaPlayerSynchronizer.js.map +1 -1
- package/bin/MediaSessionActionThrottler.d.ts +1 -1
- package/bin/MediaSessionActionThrottler.js +1 -1
- package/bin/MediaSessionExtensions.d.ts +1 -1
- package/bin/MediaSessionExtensions.js +1 -1
- package/bin/RepeatedActionThrottler.d.ts +1 -1
- package/bin/RepeatedActionThrottler.js +1 -1
- package/bin/VolumeLimiter.d.ts +2 -2
- package/bin/VolumeLimiter.js +2 -2
- package/bin/index.d.ts +1 -1
- package/bin/index.js +1 -1
- package/bin/internals/GroupCoordinatorState.d.ts +1 -1
- package/bin/internals/GroupCoordinatorState.js +1 -1
- package/bin/internals/GroupPlaybackPosition.d.ts +1 -1
- package/bin/internals/GroupPlaybackPosition.js +1 -1
- package/bin/internals/GroupPlaybackTrack.d.ts +1 -1
- package/bin/internals/GroupPlaybackTrack.js +1 -1
- package/bin/internals/GroupPlaybackTrackData.d.ts +1 -1
- package/bin/internals/GroupPlaybackTrackData.js +1 -1
- package/bin/internals/GroupTransportState.d.ts +1 -1
- package/bin/internals/GroupTransportState.js +1 -1
- package/bin/internals/consts.d.ts +1 -1
- package/bin/internals/consts.js +1 -1
- package/bin/internals/index.d.ts +1 -1
- package/bin/internals/index.js +1 -1
- package/package.json +13 -4
- package/src/EphemeralMediaSession.ts +95 -48
- package/src/EphemeralMediaSessionCoordinator.ts +190 -26
- package/src/EphemeralMediaSessionCoordinatorSuspension.ts +1 -1
- package/src/IMediaPlayer.ts +1 -1
- package/src/MediaPlayerSynchronizer.ts +101 -33
- package/src/MediaSessionActionThrottler.ts +1 -1
- package/src/MediaSessionExtensions.ts +1 -1
- package/src/RepeatedActionThrottler.ts +1 -1
- package/src/VolumeLimiter.ts +2 -2
- package/src/index.ts +1 -1
- package/src/internals/GroupCoordinatorState.ts +1 -1
- package/src/internals/GroupPlaybackPosition.ts +1 -1
- package/src/internals/GroupPlaybackTrack.ts +1 -1
- package/src/internals/GroupPlaybackTrackData.ts +1 -1
- package/src/internals/GroupTransportState.ts +1 -1
- package/src/internals/consts.ts +1 -1
- package/src/internals/index.ts +1 -1
- package/src/test/GroupPlaybackPosition.spec.ts +1 -1
- package/src/test/GroupPlaybackTrack.spec.ts +1 -1
- package/src/test/SharedMediaSession.spec.ts +1 -1
- package/src/test/SharedMediaSessionCoordinator.spec.ts +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
|
-
* Licensed under the
|
|
3
|
+
* Licensed under the Microsoft Live Share SDK License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { EphemeralTelemetryLogger, IEvent, TimeInterval } from '@microsoft/live-share';
|
|
@@ -11,11 +11,23 @@ import { VolumeLimiter } from './VolumeLimiter';
|
|
|
11
11
|
import { IMediaPlayer } from './IMediaPlayer';
|
|
12
12
|
import { TelemetryEvents } from './internals';
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Event data returned by `MediaPlayerSynchronizer` object.
|
|
16
|
+
*/
|
|
14
17
|
export interface IMediaPlayerSynchronizerEvent extends IEvent {
|
|
18
|
+
/**
|
|
19
|
+
* Event details.
|
|
20
|
+
*/
|
|
15
21
|
details: ExtendedMediaSessionActionDetails;
|
|
16
22
|
}
|
|
17
23
|
|
|
24
|
+
/**
|
|
25
|
+
* Events supported by `MediaPlayerSynchronizer` object.
|
|
26
|
+
*/
|
|
18
27
|
export enum MediaPlayerSynchronizerEvents {
|
|
28
|
+
/**
|
|
29
|
+
*
|
|
30
|
+
*/
|
|
19
31
|
coordinatorstatechange = 'coordinatorstatechange',
|
|
20
32
|
groupaction = 'groupaction',
|
|
21
33
|
useraction = 'useraction'
|
|
@@ -40,7 +52,6 @@ export class MediaPlayerSynchronizer extends EventEmitter {
|
|
|
40
52
|
private _volumeLimiter: VolumeLimiter;
|
|
41
53
|
private _onEnd?: () => void;
|
|
42
54
|
private _onPlayerEvent: EventListener;
|
|
43
|
-
private _catchupFastSeekPeriod: TimeInterval = new TimeInterval(0);
|
|
44
55
|
private _seekSuspension?: MediaSessionCoordinatorSuspension;
|
|
45
56
|
private _viewOnly = false;
|
|
46
57
|
private _expectedPlaybackState: ExtendedMediaSessionPlaybackState = 'none';
|
|
@@ -61,7 +72,7 @@ export class MediaPlayerSynchronizer extends EventEmitter {
|
|
|
61
72
|
this._onEnd = onEnd;
|
|
62
73
|
|
|
63
74
|
// Listen for player state requests
|
|
64
|
-
mediaSession.
|
|
75
|
+
mediaSession.setRequestPlayerStateHandler(() => {
|
|
65
76
|
// Find playback state
|
|
66
77
|
let playbackState: ExtendedMediaSessionPlaybackState;
|
|
67
78
|
const src = this._player.currentSrc || this._player.src;
|
|
@@ -87,7 +98,7 @@ export class MediaPlayerSynchronizer extends EventEmitter {
|
|
|
87
98
|
};
|
|
88
99
|
|
|
89
100
|
return state;
|
|
90
|
-
}
|
|
101
|
+
});
|
|
91
102
|
|
|
92
103
|
// Listen for player events
|
|
93
104
|
this._onPlayerEvent = (evt: Event) => {
|
|
@@ -104,7 +115,7 @@ export class MediaPlayerSynchronizer extends EventEmitter {
|
|
|
104
115
|
// Handle case for YouTube player where user can pause/play video by clicking on it.
|
|
105
116
|
// - Videos don't always start at 0.0 seconds.
|
|
106
117
|
if (this._expectedPlaybackState != 'playing') {
|
|
107
|
-
if (
|
|
118
|
+
if (this._mediaSession.coordinator.canPlayPause && this._player.currentTime < 1.0) {
|
|
108
119
|
this._logger.sendTelemetryEvent(TelemetryEvents.MediaPlayerSynchronizer.UserTappedVideoToPlay);
|
|
109
120
|
this.play();
|
|
110
121
|
}
|
|
@@ -185,22 +196,30 @@ export class MediaPlayerSynchronizer extends EventEmitter {
|
|
|
185
196
|
this._logger.sendTelemetryEvent(TelemetryEvents.MediaPlayerSynchronizer.SynchronizationStarted);
|
|
186
197
|
}
|
|
187
198
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
public set catchupFastSeekPeriod(value: number) {
|
|
193
|
-
this._catchupFastSeekPeriod.seconds = value;
|
|
194
|
-
}
|
|
195
|
-
|
|
199
|
+
/**
|
|
200
|
+
* Media player being synchronized.
|
|
201
|
+
*/
|
|
196
202
|
public get player(): IMediaPlayer {
|
|
197
203
|
return this._player;
|
|
198
204
|
}
|
|
199
205
|
|
|
206
|
+
/**
|
|
207
|
+
* Synchronizers media session.
|
|
208
|
+
*/
|
|
200
209
|
public get mediaSession(): EphemeralMediaSession {
|
|
201
210
|
return this._mediaSession;
|
|
202
211
|
}
|
|
203
212
|
|
|
213
|
+
/**
|
|
214
|
+
* If true the client is in a view only mode.
|
|
215
|
+
*
|
|
216
|
+
* @remarks
|
|
217
|
+
* Toggling this value to true results in `mediaSession.coordinator.canPlayPause`,
|
|
218
|
+
* `mediaSession.coordinator.canSeek`, `mediaSession.coordinator.canSetTrack`, and
|
|
219
|
+
* `mediaSession.coordinator.canSetTrackData` all being set to false. For more fine
|
|
220
|
+
* grained control over the local clients policies, call the `mediaSession.coordinator`
|
|
221
|
+
* directly.
|
|
222
|
+
*/
|
|
204
223
|
public get viewOnly(): boolean {
|
|
205
224
|
return this._viewOnly;
|
|
206
225
|
}
|
|
@@ -209,22 +228,40 @@ export class MediaPlayerSynchronizer extends EventEmitter {
|
|
|
209
228
|
this._viewOnly = value;
|
|
210
229
|
this.mediaSession.coordinator.canPlayPause = !value;
|
|
211
230
|
this.mediaSession.coordinator.canSeek = !value;
|
|
231
|
+
this.mediaSession.coordinator.canSetTrack = !value;
|
|
232
|
+
this.mediaSession.coordinator.canSetTrackData = !value;
|
|
212
233
|
}
|
|
213
234
|
|
|
235
|
+
/**
|
|
236
|
+
* Volume limiter used to temporarily reduce the videos volume when someone speaks in a meeting.
|
|
237
|
+
*/
|
|
214
238
|
public get volumeLimiter(): VolumeLimiter {
|
|
215
239
|
return this._volumeLimiter;
|
|
216
240
|
}
|
|
217
241
|
|
|
218
|
-
|
|
242
|
+
/**
|
|
243
|
+
* Registers a new event listener.
|
|
244
|
+
* @param event Name of the event to add.
|
|
245
|
+
* @param listener Function to call when the event is triggered.
|
|
246
|
+
*/
|
|
247
|
+
public addEventListener(event: MediaPlayerSynchronizerEvents, listener: (evt: IMediaPlayerSynchronizerEvent) => void): this {
|
|
219
248
|
this.on(event, listener);
|
|
220
249
|
return this;
|
|
221
250
|
}
|
|
222
251
|
|
|
223
|
-
|
|
252
|
+
/**
|
|
253
|
+
* Un-registers an existing event listener.
|
|
254
|
+
* @param event Name of the event to remove.
|
|
255
|
+
* @param listener Function that was registered in call to `addEventListener()`.
|
|
256
|
+
*/
|
|
257
|
+
public removeEventListener(event: MediaPlayerSynchronizerEvents, listener: (evt: IMediaPlayerSynchronizerEvent) => void): this {
|
|
224
258
|
this.off(event, listener);
|
|
225
259
|
return this;
|
|
226
260
|
}
|
|
227
261
|
|
|
262
|
+
/**
|
|
263
|
+
* Ends synchronization of the current media player.
|
|
264
|
+
*/
|
|
228
265
|
public end(): void {
|
|
229
266
|
if (this._onEnd) {
|
|
230
267
|
try {
|
|
@@ -246,6 +283,15 @@ export class MediaPlayerSynchronizer extends EventEmitter {
|
|
|
246
283
|
}
|
|
247
284
|
}
|
|
248
285
|
|
|
286
|
+
/**
|
|
287
|
+
* Begin a local seek operation.
|
|
288
|
+
*
|
|
289
|
+
* @remarks
|
|
290
|
+
* UI can call this when a user grabs a timeline scrubber and starts scrubbing the video to a
|
|
291
|
+
* new playback position. The synchronizer will being a new suspension which temporarily
|
|
292
|
+
* disconnects the client for the rest of the group for synchronization purposes. Calling
|
|
293
|
+
* `endSeek()` will end the suspension and seek the group to the users final seek position.
|
|
294
|
+
*/
|
|
249
295
|
public beginSeek(): void {
|
|
250
296
|
if (this._seekSuspension) {
|
|
251
297
|
throw new Error(`MediaPlayerSynchronizer: cannot begin seek. A seek is already in progress.`);
|
|
@@ -255,6 +301,10 @@ export class MediaPlayerSynchronizer extends EventEmitter {
|
|
|
255
301
|
this._seekSuspension = this._mediaSession.coordinator.beginSuspension();
|
|
256
302
|
}
|
|
257
303
|
|
|
304
|
+
/**
|
|
305
|
+
* Ends a seek operation that was started by calling `beginSeek()`.
|
|
306
|
+
* @param seekTo Playback position in seconds to seek the group to.
|
|
307
|
+
*/
|
|
258
308
|
public endSeek(seekTo: number): void {
|
|
259
309
|
if (!this._seekSuspension) {
|
|
260
310
|
throw new Error(`MediaPlayerSynchronizer: cannot end seek. No seek is in progress.`);
|
|
@@ -274,6 +324,13 @@ export class MediaPlayerSynchronizer extends EventEmitter {
|
|
|
274
324
|
this.dispatchUserAction({action: 'seekto', seekTime: seekTo});
|
|
275
325
|
}
|
|
276
326
|
|
|
327
|
+
/**
|
|
328
|
+
* Tells the group to begin playing the current video.
|
|
329
|
+
*
|
|
330
|
+
* @remarks
|
|
331
|
+
* For proper operation apps should avoid calling `mediaSession.coordinator.play()` directly
|
|
332
|
+
* and instead use the synchronizers `play()` method.
|
|
333
|
+
*/
|
|
277
334
|
public play(): void {
|
|
278
335
|
this._logger.sendTelemetryEvent(TelemetryEvents.MediaPlayerSynchronizer.PlayCalled);
|
|
279
336
|
this._expectedPlaybackState = 'playing';
|
|
@@ -282,6 +339,13 @@ export class MediaPlayerSynchronizer extends EventEmitter {
|
|
|
282
339
|
this.dispatchUserAction({action: 'play'});
|
|
283
340
|
}
|
|
284
341
|
|
|
342
|
+
/**
|
|
343
|
+
* Tells the group to pause the current video.
|
|
344
|
+
*
|
|
345
|
+
* @remarks
|
|
346
|
+
* For proper operation apps should avoid calling `mediaSession.coordinator.pause()` directly
|
|
347
|
+
* and instead use the synchronizers `pause()` method.
|
|
348
|
+
*/
|
|
285
349
|
public pause(): void {
|
|
286
350
|
this._logger.sendTelemetryEvent(TelemetryEvents.MediaPlayerSynchronizer.PauseCalled);
|
|
287
351
|
this._expectedPlaybackState = 'paused';
|
|
@@ -290,6 +354,13 @@ export class MediaPlayerSynchronizer extends EventEmitter {
|
|
|
290
354
|
this.dispatchUserAction({action: 'pause'});
|
|
291
355
|
}
|
|
292
356
|
|
|
357
|
+
/**
|
|
358
|
+
* Tells the group to seek the current video to a new playback position.
|
|
359
|
+
*
|
|
360
|
+
* @remarks
|
|
361
|
+
* For proper operation apps should avoid calling `mediaSession.coordinator.seekTo()` directly
|
|
362
|
+
* and instead use the synchronizers `seekTo()` method.
|
|
363
|
+
*/
|
|
293
364
|
public seekTo(time: number): void {
|
|
294
365
|
// Always seek player to new time.
|
|
295
366
|
// - This resolves an issue where the timeline scrubber can temporarily snap back to the original
|
|
@@ -302,6 +373,13 @@ export class MediaPlayerSynchronizer extends EventEmitter {
|
|
|
302
373
|
this.dispatchUserAction({action: 'seekto', seekTime: time});
|
|
303
374
|
}
|
|
304
375
|
|
|
376
|
+
/**
|
|
377
|
+
* Tells the group to change to a new track.
|
|
378
|
+
*
|
|
379
|
+
* @remarks
|
|
380
|
+
* For proper operation apps should avoid calling `mediaSession.coordinator.setTrack()` directly
|
|
381
|
+
* and instead use the synchronizers `setTrack()` method.
|
|
382
|
+
*/
|
|
305
383
|
public setTrack(track: ExtendedMediaMetadata, waitPoints?: CoordinationWaitPoint[]): void {
|
|
306
384
|
this._logger.sendTelemetryEvent(TelemetryEvents.MediaPlayerSynchronizer.SetTrackCalled);
|
|
307
385
|
this._mediaSession.coordinator.setTrack(track, waitPoints);
|
|
@@ -309,6 +387,13 @@ export class MediaPlayerSynchronizer extends EventEmitter {
|
|
|
309
387
|
this.dispatchUserAction({action: 'settrack', metadata: track});
|
|
310
388
|
}
|
|
311
389
|
|
|
390
|
+
/**
|
|
391
|
+
* Updates the current tracks data object.
|
|
392
|
+
*
|
|
393
|
+
* @remarks
|
|
394
|
+
* For proper operation apps should avoid calling `mediaSession.coordinator.setTrackData()` directly
|
|
395
|
+
* and instead use the synchronizers `setTrackData()` method.
|
|
396
|
+
*/
|
|
312
397
|
public setTrackData(data: object|null): void {
|
|
313
398
|
this._logger.sendTelemetryEvent(TelemetryEvents.MediaPlayerSynchronizer.SetTrackDataCalled);
|
|
314
399
|
this._trackData = data;
|
|
@@ -334,23 +419,6 @@ export class MediaPlayerSynchronizer extends EventEmitter {
|
|
|
334
419
|
}
|
|
335
420
|
|
|
336
421
|
private async catchupPlayer(time: number): Promise<void> {
|
|
337
|
-
|
|
338
|
-
// - more then 2 seconds behind.
|
|
339
|
-
// - fast seek hasn't been disabled.
|
|
340
|
-
// - the current playback rate is 1.0.
|
|
341
|
-
if (time >= (this._player.currentTime + 2.0) && this.catchupFastSeekPeriod > 0 && this._player.playbackRate == 1.0) {
|
|
342
|
-
// Fast seek to new position
|
|
343
|
-
const suspension = await this._mediaSession.coordinator.beginSuspension();
|
|
344
|
-
const seekPeriod = Math.min(time - this._player.currentTime, this.catchupFastSeekPeriod);
|
|
345
|
-
const startTime = time - seekPeriod;
|
|
346
|
-
this._player.currentTime = startTime;
|
|
347
|
-
this._player.playbackRate = 4.0;
|
|
348
|
-
setTimeout(async () => {
|
|
349
|
-
this._player.playbackRate = 1.0;
|
|
350
|
-
await suspension.end();
|
|
351
|
-
}, (seekPeriod * 0.25) * 1000)
|
|
352
|
-
} else {
|
|
353
|
-
this._player.currentTime = time;
|
|
354
|
-
}
|
|
422
|
+
this._player.currentTime = time;
|
|
355
423
|
}
|
|
356
424
|
}
|
package/src/VolumeLimiter.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
|
-
* Licensed under the
|
|
3
|
+
* Licensed under the Microsoft Live Share SDK License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { TimeInterval } from '@microsoft/live-share';
|
|
@@ -33,7 +33,7 @@ export class VolumeLimiter {
|
|
|
33
33
|
* Time, in seconds, that the volume should be lowered.
|
|
34
34
|
*
|
|
35
35
|
* @remarks
|
|
36
|
-
* Applications should periodically call `lowerVolume()` to keep
|
|
36
|
+
* Applications should periodically call `lowerVolume()` to keep the volume lowered.
|
|
37
37
|
*/
|
|
38
38
|
public get duration(): number {
|
|
39
39
|
return this._duration.seconds;
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
|
-
* Licensed under the
|
|
3
|
+
* Licensed under the Microsoft Live Share SDK License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { IEvent, IEphemeralEvent, EphemeralEvent, TimeInterval, IRuntimeSignaler, EphemeralTelemetryLogger } from '@microsoft/live-share';
|
package/src/internals/consts.ts
CHANGED
package/src/internals/index.ts
CHANGED