diff --git a/.babelrc b/.babelrc index 4ffef06dbfb53d6210bc68ec4218f3b3d74108fc..88f0c0ac53f9a7036d9ad906f256c6668ca51d98 100644 --- a/.babelrc +++ b/.babelrc @@ -1,3 +1,9 @@ { - "presets": ["env", "react"] + "presets": [ + "react", + ["env", { + "exclude": ["transform-regenerator"], + "useBuiltins": true + }] + ] } diff --git a/package.json b/package.json index b3d4b301e5e9732b26a197efee373e863e722a7c..88d79f36448c22f3ecfbd76bc4102b02eed1808a 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "aframe-input-mapping-component": "https://github.com/fernandojsg/aframe-input-mapping-component#6ebc38f", "aframe-teleport-controls": "https://github.com/netpro2k/aframe-teleport-controls#feature/teleport-origin", "minijanus": "^0.1.6", - "naf-janus-adapter": "^0.1.5", + "naf-janus-adapter": "^0.1.6", "networked-aframe": "https://github.com/netpro2k/networked-aframe#bugfix/chrome/audio", "nipplejs": "^0.6.7", "query-string": "^5.0.1", diff --git a/public/room.html b/public/room.html index d13ff038a2dd1a0bf8fa7b8a5f0eaa34811907fd..0b96981873dda6a51ecfb559275a31adbbc4a051 100644 --- a/public/room.html +++ b/public/room.html @@ -88,6 +88,10 @@ ></a-entity> </script> + <script id="video-template" type="text/html"> + <a-entity class="video" geometry="primitive: plane;" material="side: double" networked-video-player></a-entity> + </script> + <script id="nametag-template" type="text/html"> <a-entity class="nametag" diff --git a/src/components/audio-feedback.js b/src/components/audio-feedback.js index 2b24abd946d7d8fe2ec35c018c4262f64fd00229..4f5a5c9e53fdf54ad22b9c90dbce4024606e6555 100644 --- a/src/components/audio-feedback.js +++ b/src/components/audio-feedback.js @@ -18,7 +18,6 @@ AFRAME.registerComponent("networked-audio-analyser", { ); } const ownerId = networkedEl.components.networked.data.owner; - console.log("audio Analyser for " + ownerId); return NAF.connection.adapter.getMediaStream(ownerId); }) .then(stream => { @@ -60,11 +59,9 @@ AFRAME.registerComponent("matcolor-audio-feedback", { this.onAudioFrequencyChange = this.onAudioFrequencyChange.bind(this); this.el.addEventListener("model-loaded", () => { - console.log(this.data.objectName); this.mat = this.el.object3D.getObjectByName( this.data.objectName ).material; - console.log("mat", this.mat); }); }, diff --git a/src/components/mute-mic.js b/src/components/mute-mic.js index 12c1ad397229b512606b40e4f5cfa86637935396..272b40e3b41f5d4aa49d7107b6aa41785b8d5596 100644 --- a/src/components/mute-mic.js +++ b/src/components/mute-mic.js @@ -32,7 +32,6 @@ AFRAME.registerComponent("mute-mic", { play: function() { const { eventSrc, toggleEvents, muteEvents, unmuteEvents } = this.data; - console.log(eventSrc); bindAllEvents(eventSrc, toggleEvents, this.onToggle); bindAllEvents(eventSrc, muteEvents, this.onMute); bindAllEvents(eventSrc, unmuteEvents, this.onUnmute); diff --git a/src/components/networked-video-player.css b/src/components/networked-video-player.css new file mode 100644 index 0000000000000000000000000000000000000000..6c10dd330f8265dff0da39de02ba854b550a9e36 --- /dev/null +++ b/src/components/networked-video-player.css @@ -0,0 +1,7 @@ +:local(.video) { + position: absolute; + bottom: 0; + height: 100px; + background: black; + display: none; +} diff --git a/src/components/networked-video-player.js b/src/components/networked-video-player.js new file mode 100644 index 0000000000000000000000000000000000000000..e00dfbf15296c5d52736254706ad3ca44e73ca6f --- /dev/null +++ b/src/components/networked-video-player.js @@ -0,0 +1,52 @@ +import styles from "./networked-video-player.css"; + +const nafConnected = function() { + return new Promise(resolve => { + NAF.clientId + ? resolve() + : document.body.addEventListener("connected", resolve); + }); +}; + +AFRAME.registerComponent("networked-video-player", { + schema: {}, + async init() { + await nafConnected(); + + const networkedEl = NAF.utils.getNetworkedEntity(this.el); + if (!networkedEl) { + throw new Error( + "Video player must be added on a node, or a child of a node, with the `networked` component." + ); + } + + const ownerId = networkedEl.components.networked.data.owner; + const stream = await NAF.connection.adapter.getMediaStream(ownerId); + if (!stream) { + return; + } + + const v = document.createElement("video"); + v.classList.add(styles.video); + v.srcObject = stream; + document.body.appendChild(v); + v.play(); + + this.videoEl = v; + + v.onloadedmetadata = () => { + const ratio = v.videoWidth / v.videoHeight; + this.el.setAttribute("geometry", { + width: ratio * 1, + height: 1 + }); + this.el.setAttribute("material", "src", v); + }; + }, + + remove() { + if (this.videoEl) { + this.videoEl.parent.removeChild(this.videoEl); + } + } +}); diff --git a/src/components/offset-relative-to.js b/src/components/offset-relative-to.js new file mode 100644 index 0000000000000000000000000000000000000000..923eae3e5673488e8eceb726b9e91f592285f546 --- /dev/null +++ b/src/components/offset-relative-to.js @@ -0,0 +1,32 @@ +AFRAME.registerComponent("offset-relative-to", { + schema: { + target: { + type: "selector" + }, + offset: { + type: "vec3" + }, + on: { + type: "string" + } + }, + init() { + this.updateOffset(); + this.el.sceneEl.addEventListener( + this.data.on, + this.updateOffset.bind(this) + ); + }, + updateOffset() { + const offsetVector = new THREE.Vector3().copy(this.data.offset); + this.data.target.object3D.localToWorld(offsetVector); + this.el.setAttribute("position", offsetVector); + + const headWorldRotation = this.data.target.object3D.getWorldRotation(); + this.el.setAttribute("rotation", { + x: headWorldRotation.x * THREE.Math.RAD2DEG, + y: headWorldRotation.y * THREE.Math.RAD2DEG, + z: headWorldRotation.z * THREE.Math.RAD2DEG + }); + } +}); diff --git a/src/config.js b/src/config.js index eed5c042fc564ebe1fdd872632de7ad835517e66..194137a54fcc529032ea94d3c62a7581f6d4fd84 100644 --- a/src/config.js +++ b/src/config.js @@ -1,5 +1,5 @@ export default { - janus_server_url: "wss://quander.me:8989", + janus_server_url: "wss://dev-janus.reticulum.io", public_rooms: [1, 2, 3, 4, 5], default_room: 1 }; diff --git a/src/index.js b/src/index.js index 96783d19760c19480eb9aa703b35cf076eaf0440..cbb4c3a1a6b7b78f5712809c461b4fd6846f0181 100644 --- a/src/index.js +++ b/src/index.js @@ -19,6 +19,8 @@ import "./components/body-controller"; import "./components/hand-controls2"; import "./components/character-controller"; import "./components/split-axis-events"; +import "./components/networked-video-player"; +import "./components/offset-relative-to"; import "./systems/personal-space-bubble"; import registerNetworkScheams from "./network-schemas"; @@ -29,8 +31,30 @@ import Config from "./config"; registerNetworkScheams(); registerInputMappings(); +function shareScreen() { + const track = NAF.connection.adapter.localMediaStream.getVideoTracks()[0]; + + const id = `${NAF.clientId}-screen`; + let entity = document.getElementById(id); + if (!entity) { + const sceneEl = document.querySelector("a-scene"); + entity = document.createElement("a-entity"); + entity.id = id; + entity.setAttribute("offset-relative-to", { + target: "#head", + offset: "0 0 -2", + on: "action_share_screen" + }); + entity.setAttribute("networked", { template: "#video-template" }); + sceneEl.appendChild(entity); + } + + track.enabled = !track.enabled; + entity.setAttribute("visible", track.enabled); +} + window.App = { - onSceneLoad() { + async onSceneLoad() { const qs = queryString.parse(location.search); const scene = document.querySelector("a-scene"); @@ -69,7 +93,25 @@ window.App = { document.body.addEventListener("connected", App.onConnect); + scene.addEventListener("action_share_screen", shareScreen); + + const mediaStream = await navigator.mediaDevices.getUserMedia({ + audio: true, + video: + qs.screen === "true" + ? { mediaSource: "screen", height: 720, frameRate: 30 } + : false + }); + + // Don't send video by deafult + const videoTracks = mediaStream.getVideoTracks(); + if (videoTracks.length) { + videoTracks[0].enabled = false; + } + scene.components["networked-scene"].connect(); + // @TODO ideally the adapter should exist before connect, but it currently doesnt so we have to do this after calling connect. This might be a race condition in other adapters. + NAF.connection.adapter.setLocalMediaStream(mediaStream); }, onConnect() { diff --git a/src/input-mappings.js b/src/input-mappings.js index b4a0dbe6dd3dfcc3eaa6238f297f8ec69c335d2b..d57ff6496b1d35bf0a435b06cfdb74dd807008e6 100644 --- a/src/input-mappings.js +++ b/src/input-mappings.js @@ -31,6 +31,7 @@ export default function registerInputMappings() { m_press: "action_mute", q_press: "action_snap_rotate_left", e_press: "action_snap_rotate_right", + v_press: "action_share_screen", w_down: "action_move_forward", w_up: "action_dont_move_forward", a_down: "action_move_left", diff --git a/src/network-schemas.js b/src/network-schemas.js index 987f0afe385a141f519621cb016e812b841b8bfa..d93c552cb6b90a68ee158c66cd93475338c60408 100644 --- a/src/network-schemas.js +++ b/src/network-schemas.js @@ -19,6 +19,11 @@ function registerNetworkSchemas() { template: "#left-hand-template", components: ["position", "rotation", "visible"] }); + + NAF.schemas.add({ + template: "#video-template", + components: ["position", "rotation", "visible"] + }); } export default registerNetworkSchemas; diff --git a/yarn.lock b/yarn.lock index ef0c717dacd6ff8e82bed56f238917614cee86fd..69a2e8869ced7c034108996d7fd3cbd2fbb54404 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3722,9 +3722,9 @@ mute-stream@0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" -naf-janus-adapter@^0.1.5: - version "0.1.5" - resolved "https://registry.yarnpkg.com/naf-janus-adapter/-/naf-janus-adapter-0.1.5.tgz#f10c8cf390e226ddd62a0058c19f072547bd783a" +naf-janus-adapter@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/naf-janus-adapter/-/naf-janus-adapter-0.1.6.tgz#4c2837f5c5b6b3ecacea8ddc5357d452f8009e23" dependencies: minijanus "^0.1.6"