diff --git a/README.md b/README.md index 1182a0ebec1fe7acab1ed8b8b537d0d9d7c7f0b8..46331c746b0cf0bdd2484bbf91b26aa9baf42f61 100644 --- a/README.md +++ b/README.md @@ -51,8 +51,6 @@ This will allow the CSP checks to pass that are served up by Reticulum so you ca ## Query Params - `allow_multi` - Allow multiple instances off the app in the same browser session -- `enable_screen_sharing` - Enable screen sharing -- `accept_screen_shares` - Display screens shared by other users - `avatar_scale` - Scale your self! - `quality` - Either "low" or "high". Force assets to a certain quality level - `mobile` - Force mobile mode diff --git a/src/components/networked-video-player.js b/src/components/networked-video-player.js index 912c15bad2e2587960ad6ca484ffc13626009b24..0fc058fcddde16d2c3c60bab4297f7dbe91b3d52 100644 --- a/src/components/networked-video-player.js +++ b/src/components/networked-video-player.js @@ -23,15 +23,6 @@ AFRAME.registerComponent("networked-video-player", { const ownerId = networkedEl.components.networked.data.owner; - const qs = new URLSearchParams(location.search); - const rejectScreenShares = !qs.has("accept_screen_shares"); - if (ownerId !== NAF.clientId && rejectScreenShares) { - // Toggle material visibility since object visibility is network-synced - // TODO: There ought to be a better way to disable network syncs on a remote entity - this.el.setAttribute("material", { visible: false }); - return; - } - let container = document.getElementById("nvp-debug-container"); if (!container) { container = document.createElement("div"); diff --git a/src/hub.html b/src/hub.html index ce88ff5d12c25bce33f3ddf4ef0b7e46841511a4..e85da6fda54f1b67e1eb067b174e19c20f2f1983 100644 --- a/src/hub.html +++ b/src/hub.html @@ -32,6 +32,7 @@ vr-mode-ui="enabled: false" stats-plus="false" action-to-event__mute="path: /actions/muteMic; event: action_mute;" + action-to-event__screenshare="path: /actions/toggleScreenShare; event: action_share_screen;" > <a-assets> @@ -71,8 +72,30 @@ <img id="water-normal-map" crossorigin="anonymous" src="./assets/waternormals.jpg"> <!-- Templates --> - <template id="video-template"> - <a-entity class="video" geometry="primitive: plane;" material="side: double; shader: flat;" networked-video-player></a-entity> + <template id="screen-template"> + <a-entity + class="interactable" + geometry="primitive: plane;" + material="side: double; shader: flat;" + super-networked-interactable="counter: #screen-counter;" + body="type: dynamic; shape: none; mass: 1;" + grabbable + stretchable="useWorldPosition: true; usePhysics: never" + hoverable + auto-scale-cannon-physics-body + sticky-object="autoLockOnRelease: true; autoLockOnLoad: true;" + position-at-box-shape-border="target:.freeze-menu" + destroy-at-extreme-distances + set-yxz-order + networked-video-player> + + <a-entity class="interactable-ui"> + <a-entity class="freeze-menu" visible-while-frozen> + <a-entity mixin="rounded-text-button" remove-networked-object-button position="0 -0.125 0.01"> </a-entity> + <a-entity text=" value:remove; width:1.75; align:center;" text-raycast-hack position="0 -0.125 0.02"></a-entity> + </a-entity> + </a-entity> + </a-entity> </template> <template id="remote-avatar-template"> @@ -294,6 +317,8 @@ <a-entity id="camera-counter" networked-counter="max: 1;"></a-entity> + <a-entity id="screen-counter" networked-counter="max: 1;"></a-entity> + <a-entity id="drawing-manager" drawing-manager></a-entity> <a-entity diff --git a/src/hub.js b/src/hub.js index 9ffcc05046c34502883eb43c20514dcfb083a3f4..812759e5741c0f8d666e68600865008de89b3e37 100644 --- a/src/hub.js +++ b/src/hub.js @@ -184,7 +184,6 @@ function mountUI(props = {}) { const scene = document.querySelector("a-scene"); const disableAutoExitOnConcurrentLoad = qsTruthy("allow_multi"); const forcedVREntryType = qs.get("vr_entry_type"); - const enableScreenSharing = qsTruthy("enable_screen_sharing"); ReactDOM.render( <UIRoot @@ -194,7 +193,6 @@ function mountUI(props = {}) { concurrentLoadDetector, disableAutoExitOnConcurrentLoad, forcedVREntryType, - enableScreenSharing, store, ...props }} diff --git a/src/network-schemas.js b/src/network-schemas.js index f460f6e5ddce701e584b79b8f87b822114a04a31..686b91e484a7fa8e29efd14a41a9a3d4c05b9207 100644 --- a/src/network-schemas.js +++ b/src/network-schemas.js @@ -73,7 +73,7 @@ function registerNetworkSchemas() { }); NAF.schemas.add({ - template: "#video-template", + template: "#screen-template", components: [ { component: "position", @@ -83,7 +83,7 @@ function registerNetworkSchemas() { component: "rotation", requiresNetworkUpdate: vectorRequiresUpdate(0.5) }, - "visible" + "scale" ] }); diff --git a/src/react-components/ui-root.js b/src/react-components/ui-root.js index bc249694ad94a002fd141c005f98d483a1c4df93..c0156b72f8fee6bade1def35ab8beaafbbd0c749 100644 --- a/src/react-components/ui-root.js +++ b/src/react-components/ui-root.js @@ -73,7 +73,6 @@ class UIRoot extends Component { concurrentLoadDetector: PropTypes.object, disableAutoExitOnConcurrentLoad: PropTypes.bool, forcedVREntryType: PropTypes.string, - enableScreenSharing: PropTypes.bool, isBotMode: PropTypes.bool, store: PropTypes.object, scene: PropTypes.object, @@ -106,7 +105,6 @@ class UIRoot extends Component { shareScreen: false, requestedScreen: false, mediaStream: null, - videoTrack: null, audioTrack: null, entryPanelCollapsed: false, @@ -323,30 +321,6 @@ class UIRoot extends Component { return { hasAudio }; }; - setStateAndRequestScreen = async e => { - const checked = e.target.checked; - await this.setState({ requestedScreen: true, shareScreen: checked }); - if (checked) { - this.fetchVideoTrack({ - video: { - mediaSource: "screen", - // Work around BMO 1449832 by calculating the width. This will break for multi monitors if you share anything - // other than your current monitor that has a different aspect ratio. - width: 720 * (screen.width / screen.height), - height: 720, - frameRate: 30 - } - }); - } else { - this.setState({ videoTrack: null }); - } - }; - - fetchVideoTrack = async constraints => { - const mediaStream = await navigator.mediaDevices.getUserMedia(constraints); - this.setState({ videoTrack: mediaStream.getVideoTracks()[0] }); - }; - fetchAudioTrack = async constraints => { if (this.state.audioTrack) { this.state.audioTrack.stop(); @@ -368,10 +342,6 @@ class UIRoot extends Component { await this.fetchMicDevices(); - if (this.state.videoTrack) { - mediaStream.addTrack(this.state.videoTrack); - } - // we should definitely have an audioTrack at this point unless they denied mic access if (this.state.audioTrack) { mediaStream.addTrack(this.state.audioTrack); @@ -711,11 +681,11 @@ class UIRoot extends Component { renderDevicePanel = () => { // Only screen sharing in desktop firefox since other browsers/platforms will ignore the "screen" media constraint and will attempt to share your webcam instead! - const isFireFox = /firefox/i.test(navigator.userAgent); - const isNonMobile = !AFRAME.utils.device.isMobile(); + //const isFireFox = /firefox/i.test(navigator.userAgent); + //const isNonMobile = !AFRAME.utils.device.isMobile(); - const screenSharingCheckbox = - this.props.enableScreenSharing && isNonMobile && isFireFox && this.renderScreensharing(); + //const screenSharingCheckbox = + // this.props.enableScreenSharing && isNonMobile && isFireFox && this.renderScreensharing(); return ( <div className={entryStyles.entryPanel}> @@ -742,7 +712,6 @@ class UIRoot extends Component { <FormattedMessage id="entry.cardboard" /> </div> )} - {screenSharingCheckbox} </div> </div> ); diff --git a/src/scene-entry-manager.js b/src/scene-entry-manager.js index 45ae819bd133276e9e1876da5c0b290101ef2b77..e12a0bc37c637f15e1e80f75dd49e6b0f92a7e74 100644 --- a/src/scene-entry-manager.js +++ b/src/scene-entry-manager.js @@ -61,9 +61,9 @@ export default class SceneEntryManager { } this._setupPlayerRig(); - this._setupScreensharing(mediaStream); this._setupBlocking(); this._setupMedia(); + this._setupScreenShare(mediaStream); this._setupCamera(); if (qsTruthy("offline")) return; @@ -147,40 +147,55 @@ export default class SceneEntryManager { this.scene.emit("username-changed", { username: displayName }); }; - _setupScreensharing = mediaStream => { - const videoTracks = mediaStream ? mediaStream.getVideoTracks() : []; - let sharingScreen = videoTracks.length > 0; - - const screenEntityId = `${NAF.clientId}-screen`; - let screenEntity = document.getElementById(screenEntityId); - - if (screenEntity) { - screenEntity.setAttribute("visible", sharingScreen); - } else if (sharingScreen) { - screenEntity = document.createElement("a-entity"); - screenEntity.id = screenEntityId; - screenEntity.setAttribute("offset-relative-to", { - target: "#player-camera", - offset: "0 0 -2", - on: "action_share_screen" - }); - screenEntity.setAttribute("networked", { template: "#video-template" }); - this.scene.appendChild(screenEntity); - } - - this.scene.addEventListener("action_share_screen", () => { - sharingScreen = !sharingScreen; - if (sharingScreen) { - for (const track of videoTracks) { - mediaStream.addTrack(track); + _setupScreenShare = mediaStream => { + let isSharing = false; + let isToggling = false; + let screenEntity = null; + + this.scene.addEventListener("action_share_screen", async () => { + if (isToggling) return; + isToggling = true; + + if (!isSharing) { + const constraints = { + video: { + mediaSource: "screen", + // Work around BMO 1449832 by calculating the width. This will break for multi monitors if you share anything + // other than your current monitor that has a different aspect ratio. + width: 720 * (screen.width / screen.height), + height: 720, + frameRate: 30 + } + }; + + console.log("a"); + const newStream = await navigator.mediaDevices.getUserMedia(constraints); + console.log(newStream); + const videoTracks = newStream ? newStream.getVideoTracks() : []; + + if (videoTracks.length > 0) { + newStream.getVideoTracks().forEach(track => mediaStream.addTrack(track)); + NAF.connection.adapter.setLocalMediaStream(mediaStream); + console.log("b"); + + screenEntity = document.createElement("a-entity"); + screenEntity.setAttribute("offset-relative-to", { target: "#player-camera", offset: "0 0 -1.5" }); + screenEntity.setAttribute("networked", { template: "#screen-template" }); + this.scene.appendChild(screenEntity); + isSharing = true; } } else { + screenEntity.parentNode.removeChild(screenEntity); + for (const track of mediaStream.getVideoTracks()) { mediaStream.removeTrack(track); } + + NAF.connection.adapter.setLocalMediaStream(mediaStream); + isSharing = false; } - NAF.connection.adapter.setLocalMediaStream(mediaStream); - screenEntity.setAttribute("visible", sharingScreen); + + isToggling = false; }); };