@needle-tools/engine 3.0.1-alpha.4 → 3.1.0-alpha
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/CHANGELOG.md +51 -0
- package/dist/needle-engine.js +13110 -12429
- package/dist/needle-engine.min.js +378 -365
- package/dist/needle-engine.umd.cjs +366 -353
- package/lib/engine/api.d.ts +2 -1
- package/lib/engine/api.js +2 -1
- package/lib/engine/api.js.map +1 -1
- package/lib/engine/codegen/register_types.js +8 -0
- package/lib/engine/codegen/register_types.js.map +1 -1
- package/lib/engine/debug/debug_overlay.js +3 -0
- package/lib/engine/debug/debug_overlay.js.map +1 -1
- package/lib/engine/engine_addressables.d.ts +21 -1
- package/lib/engine/engine_addressables.js +83 -7
- package/lib/engine/engine_addressables.js.map +1 -1
- package/lib/engine/engine_context.d.ts +2 -0
- package/lib/engine/engine_context.js +3 -0
- package/lib/engine/engine_context.js.map +1 -1
- package/lib/engine/engine_element.d.ts +1 -1
- package/lib/engine/engine_element.js +2 -2
- package/lib/engine/engine_element.js.map +1 -1
- package/lib/engine/engine_element_loading.d.ts +7 -2
- package/lib/engine/engine_element_loading.js +67 -22
- package/lib/engine/engine_element_loading.js.map +1 -1
- package/lib/engine/engine_license.d.ts +1 -1
- package/lib/engine/engine_license.js +6 -19
- package/lib/engine/engine_license.js.map +1 -1
- package/lib/engine/engine_networking.d.ts +1 -0
- package/lib/engine/engine_networking.js +3 -0
- package/lib/engine/engine_networking.js.map +1 -1
- package/lib/engine/engine_serialization.d.ts +2 -2
- package/lib/engine/engine_serialization.js +2 -3
- package/lib/engine/engine_serialization.js.map +1 -1
- package/lib/engine/engine_serialization_builtin_serializer.d.ts +5 -0
- package/lib/engine/engine_serialization_builtin_serializer.js +16 -0
- package/lib/engine/engine_serialization_builtin_serializer.js.map +1 -1
- package/lib/engine/engine_serialization_core.d.ts +1 -0
- package/lib/engine/engine_serialization_core.js +26 -20
- package/lib/engine/engine_serialization_core.js.map +1 -1
- package/lib/engine/engine_utils.d.ts +9 -0
- package/lib/engine/engine_utils.js +33 -13
- package/lib/engine/engine_utils.js.map +1 -1
- package/lib/engine/extensions/NEEDLE_animator_controller_model.d.ts +1 -0
- package/lib/engine/extensions/NEEDLE_animator_controller_model.js.map +1 -1
- package/lib/engine/extensions/NEEDLE_progressive.js +2 -2
- package/lib/engine/extensions/NEEDLE_progressive.js.map +1 -1
- package/lib/engine-components/AnimatorController.js +22 -3
- package/lib/engine-components/AnimatorController.js.map +1 -1
- package/lib/engine-components/AudioSource.d.ts +2 -3
- package/lib/engine-components/AudioSource.js +28 -32
- package/lib/engine-components/AudioSource.js.map +1 -1
- package/lib/engine-components/CameraUtils.js.map +1 -1
- package/lib/engine-components/Component.js.map +1 -1
- package/lib/engine-components/ParticleSystem.d.ts +5 -2
- package/lib/engine-components/ParticleSystem.js +49 -10
- package/lib/engine-components/ParticleSystem.js.map +1 -1
- package/lib/engine-components/ParticleSystemModules.d.ts +2 -0
- package/lib/engine-components/ParticleSystemModules.js +23 -12
- package/lib/engine-components/ParticleSystemModules.js.map +1 -1
- package/lib/engine-components/ScreenCapture.d.ts +1 -0
- package/lib/engine-components/ScreenCapture.js +145 -46
- package/lib/engine-components/ScreenCapture.js.map +1 -1
- package/lib/engine-components/Skybox.js +2 -2
- package/lib/engine-components/Skybox.js.map +1 -1
- package/lib/engine-components/SyncedRoom.js +1 -2
- package/lib/engine-components/SyncedRoom.js.map +1 -1
- package/lib/engine-components/VideoPlayer.d.ts +4 -2
- package/lib/engine-components/VideoPlayer.js +35 -12
- package/lib/engine-components/VideoPlayer.js.map +1 -1
- package/lib/engine-components/WebXR.d.ts +4 -1
- package/lib/engine-components/WebXR.js +10 -2
- package/lib/engine-components/WebXR.js.map +1 -1
- package/lib/engine-components/WebXRController.js +2 -2
- package/lib/engine-components/WebXRController.js.map +1 -1
- package/lib/engine-components/WebXRImageTracking.d.ts +39 -0
- package/lib/engine-components/WebXRImageTracking.js +173 -0
- package/lib/engine-components/WebXRImageTracking.js.map +1 -0
- package/lib/engine-components/codegen/components.d.ts +4 -0
- package/lib/engine-components/codegen/components.js +4 -0
- package/lib/engine-components/codegen/components.js.map +1 -1
- package/lib/engine-components/postprocessing/Effects/DepthOfField.d.ts +2 -0
- package/lib/engine-components/postprocessing/Effects/DepthOfField.js +19 -1
- package/lib/engine-components/postprocessing/Effects/DepthOfField.js.map +1 -1
- package/lib/engine-components/postprocessing/Effects/TiltShiftEffect.d.ts +13 -0
- package/lib/engine-components/postprocessing/Effects/TiltShiftEffect.js +63 -0
- package/lib/engine-components/postprocessing/Effects/TiltShiftEffect.js.map +1 -0
- package/lib/engine-components/postprocessing/VolumeParameter.d.ts +1 -0
- package/lib/engine-components/postprocessing/VolumeParameter.js +4 -0
- package/lib/engine-components/postprocessing/VolumeParameter.js.map +1 -1
- package/lib/engine-components/timeline/PlayableDirector.d.ts +3 -1
- package/lib/engine-components/timeline/PlayableDirector.js +40 -2
- package/lib/engine-components/timeline/PlayableDirector.js.map +1 -1
- package/lib/engine-components/timeline/TimelineTracks.d.ts +2 -2
- package/lib/engine-components/timeline/TimelineTracks.js +14 -16
- package/lib/engine-components/timeline/TimelineTracks.js.map +1 -1
- package/lib/engine-components/ui/Text.js +7 -8
- package/lib/engine-components/ui/Text.js.map +1 -1
- package/lib/include/three/ARButton.d.ts +1 -1
- package/lib/include/three/ARButton.js +2 -1
- package/lib/include/three/ARButton.js.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -4
- package/plugins/vite/config.js +8 -0
- package/plugins/vite/copyfiles.js +12 -3
- package/plugins/vite/drop.js +1 -0
- package/plugins/vite/index.js +4 -0
- package/plugins/vite/license.js +24 -0
- package/plugins/vite/transform-codegen.js +45 -0
- package/src/engine/api.ts +2 -2
- package/src/engine/codegen/register_types.js +10 -2
- package/src/engine/debug/debug_overlay.ts +2 -0
- package/src/engine/engine_addressables.ts +102 -8
- package/src/engine/engine_context.ts +4 -1
- package/src/engine/engine_element.ts +2 -2
- package/src/engine/engine_element_loading.ts +68 -22
- package/src/engine/engine_license.ts +6 -17
- package/src/engine/engine_networking.ts +4 -0
- package/src/engine/engine_serialization.ts +5 -4
- package/src/engine/engine_serialization_builtin_serializer.ts +23 -3
- package/src/engine/engine_serialization_core.ts +27 -20
- package/src/engine/engine_utils.ts +35 -14
- package/src/engine/extensions/NEEDLE_animator_controller_model.ts +1 -0
- package/src/engine/extensions/NEEDLE_progressive.ts +2 -2
- package/src/engine-components/AnimatorController.ts +18 -3
- package/src/engine-components/AudioSource.ts +29 -38
- package/src/engine-components/CameraUtils.ts +2 -2
- package/src/engine-components/Component.ts +1 -1
- package/src/engine-components/ParticleSystem.ts +52 -10
- package/src/engine-components/ParticleSystemModules.ts +24 -12
- package/src/engine-components/ScreenCapture.ts +149 -49
- package/src/engine-components/Skybox.ts +2 -2
- package/src/engine-components/SyncedRoom.ts +1 -2
- package/src/engine-components/VideoPlayer.ts +33 -11
- package/src/engine-components/WebXR.ts +11 -2
- package/src/engine-components/WebXRController.ts +2 -2
- package/src/engine-components/WebXRImageTracking.ts +192 -0
- package/src/engine-components/codegen/components.ts +4 -0
- package/src/engine-components/postprocessing/Effects/DepthOfField.ts +21 -6
- package/src/engine-components/postprocessing/Effects/TiltShiftEffect.ts +56 -0
- package/src/engine-components/postprocessing/VolumeParameter.ts +5 -0
- package/src/engine-components/timeline/PlayableDirector.ts +38 -2
- package/src/engine-components/timeline/TimelineTracks.ts +15 -18
- package/src/engine-components/ui/Text.ts +7 -8
- package/src/include/three/ARButton.js +2 -2
- package/lib/engine/codegen/license.json +0 -1
- package/license-2447137e.js +0 -4
- package/src/engine/codegen/license.json +0 -1
|
@@ -35,7 +35,7 @@ export interface IParticleSystem {
|
|
|
35
35
|
|
|
36
36
|
export enum ParticleSystemRenderMode {
|
|
37
37
|
Billboard = 0,
|
|
38
|
-
|
|
38
|
+
Stretch = 1,
|
|
39
39
|
HorizontalBillboard = 2,
|
|
40
40
|
VerticalBillboard = 3,
|
|
41
41
|
Mesh = 4,
|
|
@@ -608,12 +608,10 @@ export class ShapeModule implements EmitterShape {
|
|
|
608
608
|
this.randomConePoint(this.position, this.angle, radius, this.radiusThickness, this.arc, this.arcMode, this._vector);
|
|
609
609
|
break;
|
|
610
610
|
case ParticleSystemShapeType.Sphere:
|
|
611
|
-
this.randomSpherePoint(this.position, radius, this.radiusThickness, this.arc, this._vector
|
|
611
|
+
this.randomSpherePoint(this.position, radius, this.radiusThickness, this.arc, this._vector);
|
|
612
612
|
break;
|
|
613
613
|
case ParticleSystemShapeType.Circle:
|
|
614
|
-
this.
|
|
615
|
-
this._temp.z = 0;
|
|
616
|
-
this.randomSpherePoint(this.position, radius, this.radiusThickness, this.arc, this._vector, this._temp);
|
|
614
|
+
this.randomCirclePoint(this.position, radius, this.radiusThickness, this.arc, this._vector);
|
|
617
615
|
break;
|
|
618
616
|
default:
|
|
619
617
|
this._vector.set(0, 0, 0);
|
|
@@ -718,15 +716,27 @@ export class ShapeModule implements EmitterShape {
|
|
|
718
716
|
dir.lerp(v, amount);
|
|
719
717
|
}
|
|
720
718
|
|
|
721
|
-
private randomSpherePoint(pos: Vec3, radius: number, thickness: number, arc: number, vec: Vec3
|
|
719
|
+
private randomSpherePoint(pos: Vec3, radius: number, thickness: number, arc: number, vec: Vec3) {
|
|
722
720
|
const u = Math.random();
|
|
723
721
|
const v = Math.random();
|
|
724
722
|
const theta = 2 * Math.PI * u * (arc / 360);
|
|
725
723
|
const phi = Math.acos(2 * v - 1);
|
|
726
724
|
const r = Mathf.lerp(1, 1 - (Math.pow(1 - Math.random(), Math.PI)), thickness) * (radius);
|
|
727
|
-
const x = pos.x +
|
|
728
|
-
const y = pos.y +
|
|
729
|
-
const z = pos.z +
|
|
725
|
+
const x = pos.x + (-r * Math.sin(phi) * Math.cos(theta));
|
|
726
|
+
const y = pos.y + (r * Math.sin(phi) * Math.sin(theta));
|
|
727
|
+
const z = pos.z + (r * Math.cos(phi));
|
|
728
|
+
vec.x = x;
|
|
729
|
+
vec.y = y;
|
|
730
|
+
vec.z = z;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
private randomCirclePoint(pos:Vec3, radius:number, thickness:number, arg:number, vec:Vec3){
|
|
734
|
+
const u = Math.random();
|
|
735
|
+
const theta = 2 * Math.PI * u * (arg / 360);
|
|
736
|
+
const r = Mathf.lerp(1, 1 - (Math.pow(1 - Math.random(), Math.PI)), thickness) * (radius);
|
|
737
|
+
const x = pos.x + r * Math.cos(theta);
|
|
738
|
+
const y = pos.y + r * Math.sin(theta);
|
|
739
|
+
const z = pos.z;
|
|
730
740
|
vec.x = x;
|
|
731
741
|
vec.y = y;
|
|
732
742
|
vec.z = z;
|
|
@@ -1096,6 +1106,8 @@ export class TextureSheetAnimationModule {
|
|
|
1096
1106
|
switch (this.frameOverTime.mode) {
|
|
1097
1107
|
case ParticleSystemCurveMode.Constant:
|
|
1098
1108
|
case ParticleSystemCurveMode.TwoConstants:
|
|
1109
|
+
case ParticleSystemCurveMode.TwoCurves:
|
|
1110
|
+
case ParticleSystemCurveMode.Curve:
|
|
1099
1111
|
return true;
|
|
1100
1112
|
}
|
|
1101
1113
|
}
|
|
@@ -1104,7 +1116,8 @@ export class TextureSheetAnimationModule {
|
|
|
1104
1116
|
|
|
1105
1117
|
getStartIndex(): number {
|
|
1106
1118
|
if (this.sampleOnceAtStart()) {
|
|
1107
|
-
|
|
1119
|
+
const start = Math.random();
|
|
1120
|
+
return start * (this.numTilesX * this.numTilesY);
|
|
1108
1121
|
}
|
|
1109
1122
|
return 0;
|
|
1110
1123
|
}
|
|
@@ -1118,13 +1131,12 @@ export class TextureSheetAnimationModule {
|
|
|
1118
1131
|
|
|
1119
1132
|
private getIndex(t01: number): number {
|
|
1120
1133
|
const tiles = this.numTilesX * this.numTilesY;
|
|
1121
|
-
|
|
1134
|
+
t01 = t01 * this.cycleCount;
|
|
1122
1135
|
let index = this.frameOverTime.evaluate(t01 % 1);
|
|
1123
1136
|
index *= this.frameOverTimeMultiplier;
|
|
1124
1137
|
index *= tiles;
|
|
1125
1138
|
index = index % tiles;
|
|
1126
1139
|
index = Math.floor(index);
|
|
1127
|
-
// console.log(index);
|
|
1128
1140
|
return index;
|
|
1129
1141
|
}
|
|
1130
1142
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Behaviour, GameObject } from "./Component";
|
|
2
2
|
import { VideoPlayer } from "./VideoPlayer";
|
|
3
|
-
import Peer from "peerjs"
|
|
3
|
+
import Peer, { MediaConnection } from "peerjs"
|
|
4
4
|
import { Context } from "../engine/engine_setup";
|
|
5
5
|
import { RoomEvents } from "../engine/engine_networking";
|
|
6
6
|
import { UserJoinedOrLeftRoomModel } from "../engine/engine_networking";
|
|
@@ -10,8 +10,9 @@ import { EventDispatcher } from "three";
|
|
|
10
10
|
import { AudioSource } from "./AudioSource";
|
|
11
11
|
import { getParam } from "../engine/engine_utils";
|
|
12
12
|
import { IModel } from "../engine/engine_networking_types";
|
|
13
|
+
import { showBalloonWarning } from "../engine/debug";
|
|
13
14
|
|
|
14
|
-
const debug = getParam("
|
|
15
|
+
const debug = getParam("debugscreensharing");
|
|
15
16
|
|
|
16
17
|
export enum ScreenCaptureDevice {
|
|
17
18
|
Screen = 0,
|
|
@@ -42,10 +43,11 @@ export class ScreenCapture extends Behaviour implements IPointerClickHandler {
|
|
|
42
43
|
onPointerClick(evt : PointerEventData) {
|
|
43
44
|
if(evt && evt.pointerId !== 0) return;
|
|
44
45
|
if(this.context.connection.isInRoom === false) return;
|
|
45
|
-
if (this.isReceiving) {
|
|
46
|
+
if (this.isReceiving && this.videoPlayer?.isPlaying) {
|
|
46
47
|
if (this.videoPlayer)
|
|
47
48
|
this.videoPlayer.screenspace = !this.videoPlayer.screenspace;
|
|
48
49
|
return;
|
|
50
|
+
|
|
49
51
|
}
|
|
50
52
|
if (this.isSending) {
|
|
51
53
|
this.close();
|
|
@@ -91,7 +93,7 @@ export class ScreenCapture extends Behaviour implements IPointerClickHandler {
|
|
|
91
93
|
|
|
92
94
|
awake() {
|
|
93
95
|
if (debug)
|
|
94
|
-
console.log(this);
|
|
96
|
+
console.log("Screensharing", this.name, this);
|
|
95
97
|
AudioSource.registerWaitForAllowAudio(() => {
|
|
96
98
|
if (this.videoPlayer && this._currentStream && this._currentMode === ScreenCaptureMode.Receiving) {
|
|
97
99
|
this.videoPlayer.setVideo(this._currentStream);
|
|
@@ -99,6 +101,11 @@ export class ScreenCapture extends Behaviour implements IPointerClickHandler {
|
|
|
99
101
|
});
|
|
100
102
|
}
|
|
101
103
|
|
|
104
|
+
|
|
105
|
+
onDisable(): void {
|
|
106
|
+
this.close();
|
|
107
|
+
}
|
|
108
|
+
|
|
102
109
|
start() {
|
|
103
110
|
if (!this.videoPlayer) {
|
|
104
111
|
this.videoPlayer = GameObject.getComponent(this.gameObject, VideoPlayer) ?? undefined;
|
|
@@ -131,6 +138,20 @@ export class ScreenCapture extends Behaviour implements IPointerClickHandler {
|
|
|
131
138
|
video: settings,
|
|
132
139
|
audio: settings,
|
|
133
140
|
};
|
|
141
|
+
const videoOptions = displayMediaOptions.video;
|
|
142
|
+
if (videoOptions !== undefined && typeof videoOptions !== "boolean") {
|
|
143
|
+
// Set default video settings
|
|
144
|
+
if (!videoOptions.width)
|
|
145
|
+
videoOptions.width = { max: 1920 };
|
|
146
|
+
if (!videoOptions.height)
|
|
147
|
+
videoOptions.height = { max: 1920 };
|
|
148
|
+
if (!videoOptions.aspectRatio)
|
|
149
|
+
videoOptions.aspectRatio = { ideal: 1.7777777778 };
|
|
150
|
+
if (!videoOptions.frameRate)
|
|
151
|
+
videoOptions.frameRate = { ideal: 24 };
|
|
152
|
+
if (!videoOptions.facingMode)
|
|
153
|
+
videoOptions.facingMode = { ideal: "user" };
|
|
154
|
+
}
|
|
134
155
|
|
|
135
156
|
switch (this.device) {
|
|
136
157
|
// Capture a connected camera
|
|
@@ -176,7 +197,8 @@ export class ScreenCapture extends Behaviour implements IPointerClickHandler {
|
|
|
176
197
|
close() {
|
|
177
198
|
this._requestOpen = false;
|
|
178
199
|
if (this._currentStream) {
|
|
179
|
-
|
|
200
|
+
if (debug)
|
|
201
|
+
console.warn("Close current stream / disposing resources, stream was active?", this._currentStream.active);
|
|
180
202
|
this._net?.stopSendingVideo(this._currentStream);
|
|
181
203
|
disposeStream(this._currentStream);
|
|
182
204
|
this._currentMode = ScreenCaptureMode.Idle;
|
|
@@ -196,15 +218,32 @@ export class ScreenCapture extends Behaviour implements IPointerClickHandler {
|
|
|
196
218
|
const isSending = mode === ScreenCaptureMode.Sending;
|
|
197
219
|
if (isSending) {
|
|
198
220
|
this._net?.startSendingVideo(stream);
|
|
221
|
+
}
|
|
199
222
|
|
|
223
|
+
// Mute audio for the video we are sending
|
|
224
|
+
if (mode === ScreenCaptureMode.Sending)
|
|
225
|
+
this.videoPlayer.muted = true;
|
|
226
|
+
|
|
227
|
+
for (const track of stream.getTracks()) {
|
|
228
|
+
track.addEventListener("ended", () => {
|
|
229
|
+
if (debug) console.log("Track ended", track);
|
|
230
|
+
this.close();
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
if (debug) {
|
|
234
|
+
if (track.kind === "video") {
|
|
235
|
+
if (isSending)
|
|
236
|
+
console.log("Video →", track.getSettings());
|
|
237
|
+
else
|
|
238
|
+
console.log("Video ←", track.getSettings());
|
|
239
|
+
}
|
|
240
|
+
}
|
|
200
241
|
}
|
|
201
242
|
|
|
202
|
-
stream.addEventListener("ended", () => {
|
|
203
|
-
this.close();
|
|
204
|
-
});
|
|
205
243
|
}
|
|
206
244
|
|
|
207
245
|
private onReceiveVideo(evt: ReceiveVideoEvent) {
|
|
246
|
+
if (evt.stream?.active !== true) return;
|
|
208
247
|
this.setVideo(evt.stream, ScreenCaptureMode.Receiving);
|
|
209
248
|
}
|
|
210
249
|
|
|
@@ -219,7 +258,8 @@ export class ScreenCapture extends Behaviour implements IPointerClickHandler {
|
|
|
219
258
|
|
|
220
259
|
// TODO: allow user to select device
|
|
221
260
|
const devices = (await navigator.mediaDevices.enumerateDevices()).filter(d => d.kind === "videoinput");
|
|
222
|
-
|
|
261
|
+
if (debug)
|
|
262
|
+
console.log("Request camera", devices);
|
|
223
263
|
for (const dev of devices) {
|
|
224
264
|
try {
|
|
225
265
|
if (!this._requestOpen) break;
|
|
@@ -240,11 +280,20 @@ export class ScreenCapture extends Behaviour implements IPointerClickHandler {
|
|
|
240
280
|
this.setVideo(userMedia, ScreenCaptureMode.Sending);
|
|
241
281
|
}
|
|
242
282
|
else disposeStream(userMedia);
|
|
243
|
-
|
|
283
|
+
if (debug)
|
|
284
|
+
console.log("Selected camera", dev);
|
|
244
285
|
break;
|
|
245
286
|
}
|
|
246
|
-
catch (err) {
|
|
247
|
-
|
|
287
|
+
catch (err: any) {
|
|
288
|
+
// First message is firefox, second is chrome when the video source is already in use by another app
|
|
289
|
+
if (err.message === "Failed to allocate videosource" || err.message === "Could not start video source") {
|
|
290
|
+
showBalloonWarning("Failed to start video: Try another camera (Code " + err.code + ")");
|
|
291
|
+
console.warn(err);
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
else {
|
|
295
|
+
console.error("Failed to get user media", err.message, err.code, err);
|
|
296
|
+
}
|
|
248
297
|
}
|
|
249
298
|
}
|
|
250
299
|
}
|
|
@@ -261,6 +310,7 @@ enum PeerEvent {
|
|
|
261
310
|
Connected = "peer-user-connected",
|
|
262
311
|
ReceiveVideo = "receive-video",
|
|
263
312
|
Disconnected = "peer-user-disconnected",
|
|
313
|
+
UserJoined = "user-joined",
|
|
264
314
|
}
|
|
265
315
|
|
|
266
316
|
class ReceiveVideoEvent {
|
|
@@ -293,7 +343,7 @@ enum CallDirection {
|
|
|
293
343
|
class CallHandle extends EventDispatcher {
|
|
294
344
|
readonly userId: string;
|
|
295
345
|
readonly direction: CallDirection;
|
|
296
|
-
readonly call:
|
|
346
|
+
readonly call: MediaConnection;
|
|
297
347
|
get stream() { return this._stream; };
|
|
298
348
|
|
|
299
349
|
private _stream: MediaStream | null = null;
|
|
@@ -314,14 +364,15 @@ class CallHandle extends EventDispatcher {
|
|
|
314
364
|
return !this.isOpen;
|
|
315
365
|
}
|
|
316
366
|
|
|
317
|
-
constructor(userId: string, call:
|
|
367
|
+
constructor(userId: string, call: MediaConnection, direction: CallDirection) {
|
|
318
368
|
super();
|
|
319
369
|
this.userId = userId;
|
|
320
370
|
this.call = call;
|
|
321
371
|
this.direction = direction;
|
|
322
372
|
this._stream = null;
|
|
323
373
|
call.on("stream", stream => {
|
|
324
|
-
|
|
374
|
+
if (debug)
|
|
375
|
+
console.log("Receive video", stream.getAudioTracks(), stream.getVideoTracks());
|
|
325
376
|
this._stream = stream;
|
|
326
377
|
if (direction === CallDirection.Incoming) {
|
|
327
378
|
const args: ReceiveVideoEvent = new ReceiveVideoEvent(stream, this);
|
|
@@ -392,7 +443,7 @@ class PeerHandle extends EventDispatcher {
|
|
|
392
443
|
private _enabled: boolean = false;
|
|
393
444
|
private _enabledPeer: boolean = false;
|
|
394
445
|
private onConnectRoomFn: Function = this.onConnectRoom.bind(this);
|
|
395
|
-
private onUserJoinedOrLeftRoomFn: Function = this.onUserJoinedOrLeftRoom.bind(this);
|
|
446
|
+
// private onUserJoinedOrLeftRoomFn: Function = this.onUserJoinedOrLeftRoom.bind(this);
|
|
396
447
|
private onPeerConnectFn: (id) => void = this.onPeerConnect.bind(this);
|
|
397
448
|
private onPeerReceiveCallFn: (call) => void = this.onPeerReceivingCall.bind(this);
|
|
398
449
|
// private _connectionPeerIdMap : Map<string, string> = new Map();
|
|
@@ -401,8 +452,8 @@ class PeerHandle extends EventDispatcher {
|
|
|
401
452
|
if (this._enabled) return;
|
|
402
453
|
this._enabled = true;
|
|
403
454
|
this.context.connection.beginListen(RoomEvents.JoinedRoom, this.onConnectRoomFn);
|
|
404
|
-
this.context.connection.beginListen(RoomEvents.UserJoinedRoom, this.onUserJoinedOrLeftRoomFn);
|
|
405
|
-
this.context.connection.beginListen(RoomEvents.UserLeftRoom, this.onUserJoinedOrLeftRoomFn);
|
|
455
|
+
// this.context.connection.beginListen(RoomEvents.UserJoinedRoom, this.onUserJoinedOrLeftRoomFn);
|
|
456
|
+
// this.context.connection.beginListen(RoomEvents.UserLeftRoom, this.onUserJoinedOrLeftRoomFn);
|
|
406
457
|
this.subscribePeerEvents();
|
|
407
458
|
}
|
|
408
459
|
|
|
@@ -410,8 +461,8 @@ class PeerHandle extends EventDispatcher {
|
|
|
410
461
|
if (!this._enabled) return;
|
|
411
462
|
this._enabled = false;
|
|
412
463
|
this.context.connection.stopListen(RoomEvents.JoinedRoom, this.onConnectRoomFn);
|
|
413
|
-
this.context.connection.stopListen(RoomEvents.UserJoinedRoom, this.onUserJoinedOrLeftRoomFn);
|
|
414
|
-
this.context.connection.stopListen(RoomEvents.UserLeftRoom, this.onUserJoinedOrLeftRoomFn);
|
|
464
|
+
// this.context.connection.stopListen(RoomEvents.UserJoinedRoom, this.onUserJoinedOrLeftRoomFn);
|
|
465
|
+
// this.context.connection.stopListen(RoomEvents.UserLeftRoom, this.onUserJoinedOrLeftRoomFn);
|
|
415
466
|
this.unsubscribePeerEvents();
|
|
416
467
|
}
|
|
417
468
|
|
|
@@ -419,8 +470,8 @@ class PeerHandle extends EventDispatcher {
|
|
|
419
470
|
this.setupPeer();
|
|
420
471
|
};
|
|
421
472
|
|
|
422
|
-
private onUserJoinedOrLeftRoom(_: UserJoinedOrLeftRoomModel): void {
|
|
423
|
-
};
|
|
473
|
+
// private onUserJoinedOrLeftRoom(_: UserJoinedOrLeftRoomModel): void {
|
|
474
|
+
// };
|
|
424
475
|
|
|
425
476
|
private setupPeer() {
|
|
426
477
|
if (!this.context.connection.connectionId) return;
|
|
@@ -428,7 +479,9 @@ class PeerHandle extends EventDispatcher {
|
|
|
428
479
|
this._enabledPeer = true;
|
|
429
480
|
if (!this._peer) {
|
|
430
481
|
const peerId = this.getMyPeerId();
|
|
431
|
-
|
|
482
|
+
if(peerId)
|
|
483
|
+
this._peer = new Peer(peerId);
|
|
484
|
+
else console.error("Failed to setup peerjs because we dont have a connection id", this.context.connection.connectionId);
|
|
432
485
|
}
|
|
433
486
|
if (this._enabled)
|
|
434
487
|
this.subscribePeerEvents();
|
|
@@ -439,10 +492,14 @@ class PeerHandle extends EventDispatcher {
|
|
|
439
492
|
this._peer.on("open", this.onPeerConnectFn);
|
|
440
493
|
this._peer.on("call", this.onPeerReceiveCallFn);
|
|
441
494
|
// this.context.connection.beginListen(PeerEvent.Connected, this.onRemotePeerConnect.bind(this));
|
|
495
|
+
// TODO: make connection to all current active calls even if the user is not anymore in the needle room
|
|
442
496
|
}
|
|
443
497
|
|
|
444
498
|
private unsubscribePeerEvents() {
|
|
445
|
-
|
|
499
|
+
if (!this._peer) return;
|
|
500
|
+
this._peer.off("open", this.onPeerConnectFn);
|
|
501
|
+
this._peer.off("call", this.onPeerReceiveCallFn);
|
|
502
|
+
// this.context.connection.stopListen(PeerEvent.Connected, this.onRemotePeerConnect.bind(this));
|
|
446
503
|
}
|
|
447
504
|
|
|
448
505
|
private onPeerConnect(id): void {
|
|
@@ -451,12 +508,12 @@ class PeerHandle extends EventDispatcher {
|
|
|
451
508
|
this.context.connection.send(PeerEvent.Connected, new PeerUserConnectedModel(this, id));
|
|
452
509
|
}
|
|
453
510
|
|
|
454
|
-
private onPeerReceivingCall(call:
|
|
511
|
+
private onPeerReceivingCall(call: MediaConnection): void {
|
|
455
512
|
call.answer();
|
|
456
513
|
this.registerCall(call, CallDirection.Incoming);
|
|
457
514
|
}
|
|
458
515
|
|
|
459
|
-
private registerCall(call:
|
|
516
|
+
private registerCall(call: MediaConnection, direction: CallDirection): CallHandle {
|
|
460
517
|
|
|
461
518
|
const meta = call.metadata;
|
|
462
519
|
if (!meta || !meta.userId) {
|
|
@@ -464,8 +521,8 @@ class PeerHandle extends EventDispatcher {
|
|
|
464
521
|
}
|
|
465
522
|
const userId = meta.userId;
|
|
466
523
|
|
|
467
|
-
if (direction === CallDirection.Incoming) console.log("Receive call from", call.metadata);
|
|
468
|
-
else console.log("Make call to", call.metadata);
|
|
524
|
+
if (direction === CallDirection.Incoming && debug) console.log("Receive call from", call.metadata);
|
|
525
|
+
else if (debug) console.log("Make call to", call.metadata);
|
|
469
526
|
|
|
470
527
|
const arr = direction === CallDirection.Incoming ? this._incomingCalls : this._outgoingCalls;
|
|
471
528
|
const handle = new CallHandle(userId, call, direction);
|
|
@@ -474,7 +531,8 @@ class PeerHandle extends EventDispatcher {
|
|
|
474
531
|
console.error("Call error", err);
|
|
475
532
|
});
|
|
476
533
|
call.on("close", () => {
|
|
477
|
-
|
|
534
|
+
if (debug)
|
|
535
|
+
console.log("Call ended", call.metadata);
|
|
478
536
|
call.close();
|
|
479
537
|
const index = arr.indexOf(handle);
|
|
480
538
|
if (index !== -1)
|
|
@@ -489,8 +547,11 @@ class PeerHandle extends EventDispatcher {
|
|
|
489
547
|
|
|
490
548
|
call.on("stream", () => {
|
|
491
549
|
// workaround for https://github.com/peers/peerjs/issues/636
|
|
550
|
+
let intervalCounter = 0;
|
|
492
551
|
let closeInterval = setInterval(() => {
|
|
493
|
-
|
|
552
|
+
const isFirstInterval = intervalCounter === 0;
|
|
553
|
+
if (!handle.isOpen && isFirstInterval) {
|
|
554
|
+
intervalCounter += 1;
|
|
494
555
|
clearInterval(closeInterval);
|
|
495
556
|
handle.close();
|
|
496
557
|
}
|
|
@@ -542,46 +603,68 @@ class NetworkedVideo extends EventDispatcher {
|
|
|
542
603
|
if (_steam) {
|
|
543
604
|
const calls = this._sendingVideoStreams.get(_steam);
|
|
544
605
|
if (calls) {
|
|
545
|
-
|
|
606
|
+
if (debug)
|
|
607
|
+
console.log("Closing calls", calls);
|
|
546
608
|
for (const call of calls) {
|
|
547
609
|
call.close();
|
|
548
610
|
}
|
|
549
611
|
}
|
|
550
612
|
this._sendingVideoStreams.delete(_steam);
|
|
551
|
-
if (calls)
|
|
613
|
+
if (calls && debug)
|
|
552
614
|
console.log("Currently sending", this._sendingVideoStreams);
|
|
553
615
|
}
|
|
554
616
|
}
|
|
555
617
|
|
|
556
|
-
private onConnectRoomFn: Function = this.onConnectRoom.bind(this);
|
|
557
|
-
private onUserConnectedFn: Function = this.onUserConnected.bind(this);
|
|
558
|
-
private onUserLeftFn: Function = this.onUserLeft.bind(this);
|
|
618
|
+
// private onConnectRoomFn: Function = this.onConnectRoom.bind(this);
|
|
619
|
+
// private onUserConnectedFn: Function = this.onUserConnected.bind(this);
|
|
620
|
+
// private onUserLeftFn: Function = this.onUserLeft.bind(this);
|
|
559
621
|
|
|
560
622
|
enable() {
|
|
561
623
|
this.peer.enable();
|
|
562
|
-
this.
|
|
563
|
-
this.peer.addEventListener(
|
|
624
|
+
this.peer.addEventListener(PeerEvent.ReceiveVideo, this.onReceiveVideo);
|
|
625
|
+
// this.peer.addEventListener(PeerEvent.UserJoined, this.onUserJoinedPeer);
|
|
626
|
+
this.context.connection.beginListen(PeerEvent.Connected, this.onUserConnected);
|
|
627
|
+
this.context.connection.beginListen(RoomEvents.JoinedRoom, this.onJoinedRoom);
|
|
628
|
+
this.context.connection.beginListen(RoomEvents.UserJoinedRoom, this.onJoinedRoom);
|
|
629
|
+
this.context.connection.beginListen(RoomEvents.UserLeftRoom, this.onUserLeft);
|
|
564
630
|
}
|
|
565
631
|
|
|
566
632
|
disable() {
|
|
567
633
|
this.peer.disable();
|
|
568
|
-
|
|
569
|
-
// this.
|
|
570
|
-
|
|
634
|
+
this.peer.removeEventListener(PeerEvent.ReceiveVideo, this.onReceiveVideo);
|
|
635
|
+
// this.peer.removeEventListener(PeerEvent.UserJoined, this.onUserJoinedPeer);
|
|
636
|
+
this.context.connection.stopListen(PeerEvent.Connected, this.onUserConnected);
|
|
637
|
+
this.context.connection.stopListen(RoomEvents.JoinedRoom, this.onJoinedRoom);
|
|
638
|
+
this.context.connection.stopListen(RoomEvents.UserJoinedRoom, this.onJoinedRoom);
|
|
639
|
+
this.context.connection.stopListen(RoomEvents.UserLeftRoom, this.onUserLeft);
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
// private onUserJoinedPeer = (evt) => {
|
|
643
|
+
// if (!this.context.connection.isConnected && evt.userId) {
|
|
644
|
+
// this.startCallWithUserIfNotAlready(evt.userId);
|
|
645
|
+
// }
|
|
646
|
+
// }
|
|
571
647
|
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
648
|
+
// When either we ourselves OR someone else is joining the room we want to make sure to re-establish all calls
|
|
649
|
+
// and if the user that joined is not yet receiving our video stream we want to start a stream with them
|
|
650
|
+
// https://github.com/needle-tools/needle-tiny/issues/697#issuecomment-1510425539
|
|
651
|
+
private onJoinedRoom = (evt) => {
|
|
652
|
+
if (debug) console.log(`${evt.userId} joined room and I'm currently sending ${this._sendingVideoStreams.size} streams`);
|
|
653
|
+
if (this._sendingVideoStreams.size > 0)
|
|
654
|
+
this.updateSendingCalls();
|
|
575
655
|
}
|
|
576
656
|
|
|
577
|
-
private
|
|
578
|
-
|
|
657
|
+
private onReceiveVideo = (evt) => {
|
|
658
|
+
if (debug)
|
|
659
|
+
console.log("RECEIVE VIDEO", evt);
|
|
660
|
+
this.dispatchEvent({ type: PeerEvent.ReceiveVideo, target: this, stream: evt.stream, userId: evt.userId });
|
|
579
661
|
}
|
|
580
662
|
|
|
581
|
-
private onUserConnected(user: PeerUserConnectedModel) {
|
|
663
|
+
private onUserConnected = (user: PeerUserConnectedModel) => {
|
|
582
664
|
// console.log(this.peer.id, user.guid)
|
|
583
665
|
if (this.peer.id === user.guid) {
|
|
584
|
-
|
|
666
|
+
if (debug)
|
|
667
|
+
console.log("USER CONNECTED", user.guid, user);
|
|
585
668
|
const stream = this._sendingVideoStreams.keys().next().value;
|
|
586
669
|
this.peer.makeCall(user.peerId, stream);
|
|
587
670
|
}
|
|
@@ -598,7 +681,8 @@ class NetworkedVideo extends EventDispatcher {
|
|
|
598
681
|
for (const userId of this.context.connection.usersInRoom()) {
|
|
599
682
|
if (userId === this.context.connection.connectionId) continue;
|
|
600
683
|
const existing = calls.find(c => c.userId === userId);
|
|
601
|
-
if (!existing) {
|
|
684
|
+
if (!existing || existing.stream?.active === false) {
|
|
685
|
+
if (debug) console.log("Starting call to", userId)
|
|
602
686
|
const handle = this.peer.makeCall(this.peer.getPeerIdFromUserId(userId), stream);
|
|
603
687
|
if (handle) {
|
|
604
688
|
startedNewCall = true;
|
|
@@ -610,11 +694,27 @@ class NetworkedVideo extends EventDispatcher {
|
|
|
610
694
|
this._sendingVideoStreams.set(stream, calls);
|
|
611
695
|
}
|
|
612
696
|
this.stopCallsToUsersThatAreNotInTheRoomAnymore();
|
|
613
|
-
if (startedNewCall) {
|
|
697
|
+
if (startedNewCall && debug) {
|
|
614
698
|
console.log("Currently sending", this._sendingVideoStreams);
|
|
615
699
|
}
|
|
616
700
|
}
|
|
617
701
|
|
|
702
|
+
// private startCallWithUserIfNotAlready(userId: string) {
|
|
703
|
+
// for (const stream of this._sendingVideoStreams.keys()) {
|
|
704
|
+
// const calls = this._sendingVideoStreams.get(stream) || [];
|
|
705
|
+
// const existing = calls.find(c => c.userId === userId);
|
|
706
|
+
// if (!existing || existing.stream?.active === false) {
|
|
707
|
+
// if (debug) console.log("Starting call to", userId)
|
|
708
|
+
// const handle = this.peer.makeCall(this.peer.getPeerIdFromUserId(userId), stream);
|
|
709
|
+
// if (handle) {
|
|
710
|
+
// calls.push(handle);
|
|
711
|
+
// return true;
|
|
712
|
+
// }
|
|
713
|
+
// }
|
|
714
|
+
// }
|
|
715
|
+
// return false;
|
|
716
|
+
// }
|
|
717
|
+
|
|
618
718
|
private stopCallsToUsersThatAreNotInTheRoomAnymore() {
|
|
619
719
|
for (const stream of this._sendingVideoStreams.keys()) {
|
|
620
720
|
const calls = this._sendingVideoStreams.get(stream);
|
|
@@ -5,7 +5,7 @@ import { EXRLoader } from "three/examples/jsm/loaders/EXRLoader";
|
|
|
5
5
|
import { EquirectangularRefractionMapping, sRGBEncoding, Texture, TextureLoader } from "three"
|
|
6
6
|
import { syncField } from "../engine/engine_networking_auto";
|
|
7
7
|
import { Camera } from "./Camera";
|
|
8
|
-
import { getParam,
|
|
8
|
+
import { getParam, resolveUrl } from "../engine/engine_utils";
|
|
9
9
|
|
|
10
10
|
const debug = getParam("debugskybox");
|
|
11
11
|
|
|
@@ -56,7 +56,7 @@ export class RemoteSkybox extends Behaviour {
|
|
|
56
56
|
if(debug) console.log("Remote skybox url?: " + url);
|
|
57
57
|
|
|
58
58
|
if (!url.startsWith("http") && !url.startsWith("www.") && !url.startsWith("data:")) {
|
|
59
|
-
url =
|
|
59
|
+
url = resolveUrl(this.sourceId, url);
|
|
60
60
|
if(debug) console.log("Remote skybox resolved to " + url);
|
|
61
61
|
}
|
|
62
62
|
|
|
@@ -104,8 +104,7 @@ export class SyncedRoom extends Behaviour {
|
|
|
104
104
|
if (this.context.connection.isConnected) {
|
|
105
105
|
if (this.context.time.time - this._lastPingTime > 3) {
|
|
106
106
|
this._lastPingTime = this.context.time.time;
|
|
107
|
-
|
|
108
|
-
this.context.connection.send("ping", { time: this.context.time.time });
|
|
107
|
+
this.context.connection.sendPing();
|
|
109
108
|
}
|
|
110
109
|
|
|
111
110
|
if (this.context.connection.isInRoom) {
|
|
@@ -3,7 +3,7 @@ import * as THREE from "three";
|
|
|
3
3
|
import { serializable } from "../engine/engine_serialization_decorator";
|
|
4
4
|
import { LinearFilter, Material, Mesh, Object3D, RawShaderMaterial, ShaderMaterial, Texture, TextureLoader, Vector2, Vector4, VideoTexture } from "three";
|
|
5
5
|
import { awaitInput } from "../engine/engine_input_utils";
|
|
6
|
-
import { getParam } from "../engine/engine_utils";
|
|
6
|
+
import { getParam, resolveUrl } from "../engine/engine_utils";
|
|
7
7
|
import { Renderer } from "./Renderer";
|
|
8
8
|
import { getWorldScale } from "../engine/engine_three_utils";
|
|
9
9
|
import { ObjectUtils, PrimitiveType } from "../engine/engine_create_objects";
|
|
@@ -49,11 +49,12 @@ export class VideoPlayer extends Behaviour {
|
|
|
49
49
|
renderer: THREE.Object3D | null = null;
|
|
50
50
|
@serializable()
|
|
51
51
|
playOnAwake: boolean = true;
|
|
52
|
-
@serializable()
|
|
53
|
-
playOnEnable?: boolean;
|
|
54
52
|
|
|
55
53
|
@serializable()
|
|
56
54
|
aspectMode: AspectMode = AspectMode.None;
|
|
55
|
+
|
|
56
|
+
@serializable(URL)
|
|
57
|
+
private clip?: string | MediaStream | null = null;
|
|
57
58
|
|
|
58
59
|
@serializable()
|
|
59
60
|
private renderMode?: VideoRenderMode;
|
|
@@ -105,8 +106,13 @@ export class VideoPlayer extends Behaviour {
|
|
|
105
106
|
get isPlaying(): boolean {
|
|
106
107
|
const video = this._videoElement;
|
|
107
108
|
if (video) {
|
|
108
|
-
|
|
109
|
-
&& video.readyState > video.HAVE_CURRENT_DATA
|
|
109
|
+
if(video.currentTime > 0 && !video.paused && !video.ended
|
|
110
|
+
&& video.readyState > video.HAVE_CURRENT_DATA)
|
|
111
|
+
return true;
|
|
112
|
+
else if(video.srcObject){
|
|
113
|
+
const stream = video.srcObject as MediaStream;
|
|
114
|
+
if(stream.active) return true;
|
|
115
|
+
}
|
|
110
116
|
}
|
|
111
117
|
return false;
|
|
112
118
|
}
|
|
@@ -134,12 +140,20 @@ export class VideoPlayer extends Behaviour {
|
|
|
134
140
|
return this._videoElement;
|
|
135
141
|
}
|
|
136
142
|
|
|
143
|
+
get muted() {
|
|
144
|
+
return this._videoElement?.muted ?? this._muted;
|
|
145
|
+
}
|
|
146
|
+
set muted(val: boolean) {
|
|
147
|
+
this._muted = val;
|
|
148
|
+
if (this._videoElement) this._videoElement.muted = val;
|
|
149
|
+
}
|
|
150
|
+
private _muted: boolean = false;
|
|
151
|
+
|
|
137
152
|
private _crossOrigin: string | null = "anonymous";
|
|
138
153
|
|
|
139
154
|
private audioOutputMode: VideoAudioOutputMode = VideoAudioOutputMode.AudioSource;
|
|
140
155
|
|
|
141
156
|
private source!: VideoSource;
|
|
142
|
-
private clip?: string | MediaStream | null = null;
|
|
143
157
|
private url?: string | null = null;
|
|
144
158
|
|
|
145
159
|
private _videoElement: HTMLVideoElement | null = null;
|
|
@@ -157,7 +171,7 @@ export class VideoPlayer extends Behaviour {
|
|
|
157
171
|
// TODO: how to prevent interruption error when another video is already playing
|
|
158
172
|
this._videoElement.srcObject = video;
|
|
159
173
|
if (this._isPlaying)
|
|
160
|
-
this.
|
|
174
|
+
this.play();
|
|
161
175
|
this.updateAspect();
|
|
162
176
|
}
|
|
163
177
|
}
|
|
@@ -195,7 +209,7 @@ export class VideoPlayer extends Behaviour {
|
|
|
195
209
|
}
|
|
196
210
|
|
|
197
211
|
onEnable(): void {
|
|
198
|
-
if (this.
|
|
212
|
+
if (this.playOnAwake === true) {
|
|
199
213
|
this.handleBeginPlaying(true);
|
|
200
214
|
}
|
|
201
215
|
if (this.screenspace) {
|
|
@@ -245,7 +259,13 @@ export class VideoPlayer extends Behaviour {
|
|
|
245
259
|
if (!this._receivedInput) this._videoElement.muted = true;
|
|
246
260
|
this.updateVideoElementSettings();
|
|
247
261
|
this._videoElement?.play().catch(err => {
|
|
248
|
-
|
|
262
|
+
// https://developer.chrome.com/blog/play-request-was-interrupted/
|
|
263
|
+
if(debug)
|
|
264
|
+
console.error("Error playing video", err, "CODE=" + err.code, this.videoElement?.src, this);
|
|
265
|
+
setTimeout(() => {
|
|
266
|
+
if (this._isPlaying && !this.destroyed && this.activeAndEnabled)
|
|
267
|
+
this.play();
|
|
268
|
+
}, 1000);
|
|
249
269
|
});
|
|
250
270
|
if (debug) console.log("play", this._videoElement);
|
|
251
271
|
}
|
|
@@ -399,8 +419,10 @@ export class VideoPlayer extends Behaviour {
|
|
|
399
419
|
this._videoElement.playbackRate = this._playbackSpeed;
|
|
400
420
|
// dont open in fullscreen on ios
|
|
401
421
|
this._videoElement.playsInline = true;
|
|
402
|
-
|
|
403
|
-
if
|
|
422
|
+
let muted = !this._receivedInput && this.audioOutputMode !== VideoAudioOutputMode.None;
|
|
423
|
+
if(!muted && this._muted) muted = true;
|
|
424
|
+
this._videoElement.muted = muted;
|
|
425
|
+
if (this.playOnAwake)
|
|
404
426
|
this._videoElement.autoplay = true;
|
|
405
427
|
}
|
|
406
428
|
|