@clockworkdog/cogs-client 3.0.0-alpha.9 → 3.0.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/README.md +20 -9
- package/dist/CogsConnection.d.ts +0 -4
- package/dist/CogsConnection.js +0 -10
- package/dist/browser/index.mjs +1901 -2707
- package/dist/browser/index.umd.js +13 -13
- package/dist/index.d.ts +1 -5
- package/dist/index.js +1 -3
- package/dist/state-based/MediaClipManager.d.ts +66 -0
- package/dist/state-based/MediaClipManager.js +420 -0
- package/dist/state-based/MediaPreloader.d.ts +14 -0
- package/dist/state-based/MediaPreloader.js +93 -0
- package/dist/state-based/SurfaceManager.d.ts +8 -1
- package/dist/state-based/SurfaceManager.js +36 -9
- package/dist/types/MediaSchema.d.ts +6 -0
- package/dist/types/MediaSchema.js +2 -0
- package/dist/utils/device.d.ts +2 -0
- package/dist/utils/device.js +4 -0
- package/dist/utils/getStateAtTime.d.ts +12 -2
- package/dist/utils/getStateAtTime.js +6 -1
- package/dist/utils/modulo.d.ts +6 -0
- package/dist/utils/modulo.js +17 -0
- package/package.json +3 -6
- package/dist/AudioPlayer.d.ts +0 -49
- package/dist/AudioPlayer.js +0 -474
- package/dist/VideoPlayer.d.ts +0 -49
- package/dist/VideoPlayer.js +0 -385
- package/dist/state-based/AudioManager.d.ts +0 -15
- package/dist/state-based/AudioManager.js +0 -116
- package/dist/state-based/ClipManager.d.ts +0 -22
- package/dist/state-based/ClipManager.js +0 -53
- package/dist/state-based/ImageManager.d.ts +0 -9
- package/dist/state-based/ImageManager.js +0 -53
- package/dist/state-based/VideoManager.d.ts +0 -15
- package/dist/state-based/VideoManager.js +0 -129
- package/dist/types/AllMediaClipStatesMessage.d.ts +0 -5
- package/dist/types/AllMediaClipStatesMessage.js +0 -1
- package/dist/types/AudioState.d.ts +0 -39
- package/dist/types/AudioState.js +0 -1
- package/dist/types/MediaClipStateMessage.d.ts +0 -7
- package/dist/types/MediaClipStateMessage.js +0 -1
- package/dist/types/VideoState.d.ts +0 -26
- package/dist/types/VideoState.js +0 -5
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
import { defaultVideoOptions } from '../types/MediaSchema';
|
|
2
|
-
import { getStateAtTime } from '../utils/getStateAtTime';
|
|
3
|
-
import { ClipManager } from './ClipManager';
|
|
4
|
-
const NO_VIDEO_POLLING = 1_000;
|
|
5
|
-
const VIDEO_PLAYBACK_POLLING = 100;
|
|
6
|
-
const SEEKING_POLLING = 10;
|
|
7
|
-
const TARGET_SYNC_THRESHOLD_MS = 10; // If we're closer than this we're good enough
|
|
8
|
-
const MAX_SYNC_THRESHOLD_MS = 1_000; // If we're further away than this, we'll seek instead
|
|
9
|
-
const SEEK_LOOKAHEAD_MS = 200; // We won't seek ahead instantly, so lets seek ahead
|
|
10
|
-
const MAX_PLAYBACK_RATE_ADJUSTMENT = 0.5;
|
|
11
|
-
// We smoothly ramp playbackRate up and down
|
|
12
|
-
const PLAYBACK_ADJUSTMENT_SMOOTHING = 0.3;
|
|
13
|
-
function playbackSmoothing(deltaTime) {
|
|
14
|
-
return Math.sign(deltaTime) * Math.pow(Math.abs(deltaTime) / MAX_SYNC_THRESHOLD_MS, PLAYBACK_ADJUSTMENT_SMOOTHING) * MAX_PLAYBACK_RATE_ADJUSTMENT;
|
|
15
|
-
}
|
|
16
|
-
export class VideoManager extends ClipManager {
|
|
17
|
-
videoElement;
|
|
18
|
-
isSeeking = false;
|
|
19
|
-
constructor(surfaceElement, clipElement, state) {
|
|
20
|
-
super(surfaceElement, clipElement, state);
|
|
21
|
-
this.clipElement = clipElement;
|
|
22
|
-
}
|
|
23
|
-
updateVideoElement() {
|
|
24
|
-
this.destroy();
|
|
25
|
-
this.videoElement = document.createElement('video');
|
|
26
|
-
this.clipElement.replaceChildren(this.videoElement);
|
|
27
|
-
this.videoElement.style.position = 'absolute';
|
|
28
|
-
this.videoElement.style.width = '100%';
|
|
29
|
-
this.videoElement.style.height = '100%';
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* Helper function to seek to a specified time.
|
|
33
|
-
* Works with the update loop to poll until seeked event has fired.
|
|
34
|
-
*/
|
|
35
|
-
seekTo(ms) {
|
|
36
|
-
if (!this.videoElement)
|
|
37
|
-
return;
|
|
38
|
-
this.delay = SEEKING_POLLING;
|
|
39
|
-
this.isSeeking = true;
|
|
40
|
-
this.videoElement.addEventListener('seeked', () => {
|
|
41
|
-
this.isSeeking = false;
|
|
42
|
-
}, { once: true, passive: true });
|
|
43
|
-
this.videoElement.currentTime = ms / 1_000;
|
|
44
|
-
}
|
|
45
|
-
update() {
|
|
46
|
-
// Update loop used to poll until seek finished
|
|
47
|
-
if (this.isSeeking)
|
|
48
|
-
return;
|
|
49
|
-
this.delay = NO_VIDEO_POLLING;
|
|
50
|
-
// Does the <video /> element need adding/removing?
|
|
51
|
-
const currentState = getStateAtTime(this._state, Date.now());
|
|
52
|
-
if (currentState) {
|
|
53
|
-
if (!this.videoElement || !this.isConnected(this.videoElement)) {
|
|
54
|
-
this.updateVideoElement();
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
else {
|
|
58
|
-
this.videoElement?.remove();
|
|
59
|
-
this.videoElement = undefined;
|
|
60
|
-
}
|
|
61
|
-
if (!currentState || !this.videoElement)
|
|
62
|
-
return;
|
|
63
|
-
const { t, rate, volume } = { ...defaultVideoOptions, ...currentState };
|
|
64
|
-
// this.videoElement.src will be a fully qualified URL
|
|
65
|
-
if (!this.videoElement.src.endsWith(this._state.file)) {
|
|
66
|
-
this.videoElement.src = this._state.file;
|
|
67
|
-
}
|
|
68
|
-
if (this.videoElement.style.objectFit !== this._state.fit) {
|
|
69
|
-
this.videoElement.style.objectFit = this._state.fit;
|
|
70
|
-
}
|
|
71
|
-
const opacityString = String(currentState.opacity);
|
|
72
|
-
if (this.videoElement.style.opacity !== opacityString) {
|
|
73
|
-
this.videoElement.style.opacity = opacityString;
|
|
74
|
-
}
|
|
75
|
-
const zIndex = Math.round(currentState.zIndex ?? defaultVideoOptions.zIndex);
|
|
76
|
-
if (parseInt(this.videoElement.style.zIndex) !== zIndex) {
|
|
77
|
-
this.videoElement.style.zIndex = String(zIndex);
|
|
78
|
-
}
|
|
79
|
-
if (this.videoElement.volume !== volume) {
|
|
80
|
-
this.videoElement.volume = volume;
|
|
81
|
-
}
|
|
82
|
-
// Should the element be playing?
|
|
83
|
-
if (this.videoElement.paused && rate > 0) {
|
|
84
|
-
this.videoElement.play().catch(() => {
|
|
85
|
-
// Do nothing - this will be retried in the next loop
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
const currentTime = this.videoElement.currentTime * 1000;
|
|
89
|
-
const deltaTime = currentTime - t;
|
|
90
|
-
const deltaTimeAbs = Math.abs(deltaTime);
|
|
91
|
-
this.delay = VIDEO_PLAYBACK_POLLING;
|
|
92
|
-
switch (true) {
|
|
93
|
-
case deltaTimeAbs <= TARGET_SYNC_THRESHOLD_MS:
|
|
94
|
-
// We are on course:
|
|
95
|
-
// - The video is within accepted latency of the server time
|
|
96
|
-
// - The playback rate is aligned with the server rate
|
|
97
|
-
if (this.videoElement.playbackRate !== rate) {
|
|
98
|
-
this.videoElement.playbackRate = rate;
|
|
99
|
-
}
|
|
100
|
-
break;
|
|
101
|
-
case rate > 0 && deltaTimeAbs > TARGET_SYNC_THRESHOLD_MS && deltaTimeAbs <= MAX_SYNC_THRESHOLD_MS: {
|
|
102
|
-
// We are close, we can smoothly adjust with playbackRate:
|
|
103
|
-
// - The video must be playing
|
|
104
|
-
// - We must be close in time to the server time
|
|
105
|
-
const playbackRateAdjustment = playbackSmoothing(deltaTime);
|
|
106
|
-
const adjustedPlaybackRate = Math.max(0, rate - playbackRateAdjustment);
|
|
107
|
-
if (this.videoElement.playbackRate !== adjustedPlaybackRate) {
|
|
108
|
-
this.videoElement.playbackRate = adjustedPlaybackRate;
|
|
109
|
-
}
|
|
110
|
-
break;
|
|
111
|
-
}
|
|
112
|
-
default: {
|
|
113
|
-
// We cannot smoothly recover:
|
|
114
|
-
// - We seek just ahead of server time
|
|
115
|
-
if (this.videoElement.playbackRate !== rate) {
|
|
116
|
-
this.videoElement.playbackRate = rate;
|
|
117
|
-
}
|
|
118
|
-
this.seekTo(t + rate * SEEK_LOOKAHEAD_MS);
|
|
119
|
-
break;
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
destroy() {
|
|
124
|
-
if (this.videoElement) {
|
|
125
|
-
this.videoElement.src = '';
|
|
126
|
-
this.videoElement.remove();
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
export type ActiveAudioClipState = {
|
|
2
|
-
type: 'paused';
|
|
3
|
-
} | {
|
|
4
|
-
type: 'pause_requested';
|
|
5
|
-
fade: number | undefined;
|
|
6
|
-
} | {
|
|
7
|
-
type: 'pausing';
|
|
8
|
-
} | {
|
|
9
|
-
type: 'playing';
|
|
10
|
-
} | {
|
|
11
|
-
type: 'play_requested';
|
|
12
|
-
} | {
|
|
13
|
-
type: 'stopping';
|
|
14
|
-
} | {
|
|
15
|
-
type: 'stop_requested';
|
|
16
|
-
fade: number | undefined;
|
|
17
|
-
};
|
|
18
|
-
export interface AudioClip {
|
|
19
|
-
config: {
|
|
20
|
-
preload: boolean;
|
|
21
|
-
ephemeral: boolean;
|
|
22
|
-
};
|
|
23
|
-
activeClips: {
|
|
24
|
-
[soundId: number]: ActiveClip;
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
export interface ActiveClip {
|
|
28
|
-
state: ActiveAudioClipState;
|
|
29
|
-
loop: boolean;
|
|
30
|
-
volume: number;
|
|
31
|
-
playId: string;
|
|
32
|
-
}
|
|
33
|
-
export interface AudioState {
|
|
34
|
-
isPlaying: boolean;
|
|
35
|
-
globalVolume: number;
|
|
36
|
-
clips: {
|
|
37
|
-
[path: string]: AudioClip;
|
|
38
|
-
};
|
|
39
|
-
}
|
package/dist/types/AudioState.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { MediaObjectFit } from '..';
|
|
2
|
-
export declare enum ActiveVideoClipState {
|
|
3
|
-
Paused = "paused",
|
|
4
|
-
Playing = "playing"
|
|
5
|
-
}
|
|
6
|
-
export interface VideoClip {
|
|
7
|
-
config: {
|
|
8
|
-
preload: 'auto' | 'metadata' | 'none';
|
|
9
|
-
ephemeral: boolean;
|
|
10
|
-
fit: MediaObjectFit;
|
|
11
|
-
};
|
|
12
|
-
}
|
|
13
|
-
export interface ActiveClip {
|
|
14
|
-
path: string;
|
|
15
|
-
state: ActiveVideoClipState;
|
|
16
|
-
loop: boolean;
|
|
17
|
-
volume: number;
|
|
18
|
-
}
|
|
19
|
-
export interface VideoState {
|
|
20
|
-
isPlaying: boolean;
|
|
21
|
-
globalVolume: number;
|
|
22
|
-
clips: {
|
|
23
|
-
[path: string]: VideoClip;
|
|
24
|
-
};
|
|
25
|
-
activeClip?: ActiveClip;
|
|
26
|
-
}
|