From b729195eb06ce4473e2b7786cd4261e9146c574b Mon Sep 17 00:00:00 2001 From: Mark Banner Date: Mon, 2 Feb 2015 21:53:19 +0000 Subject: [PATCH] Bug 1122032 Part 2 - Show the Loop screenshare video in place of the remote video for now. r=mikedeboer --- .../loop/content/js/conversationViews.js | 2 +- .../loop/content/js/conversationViews.jsx | 2 +- .../components/loop/content/js/roomViews.js | 2 +- .../components/loop/content/js/roomViews.jsx | 2 +- .../loop/content/shared/css/conversation.css | 4 +- .../loop/content/shared/js/actions.js | 7 + .../loop/content/shared/js/activeRoomStore.js | 11 +- .../loop/content/shared/js/mixins.js | 13 +- .../loop/content/shared/js/otSdkDriver.js | 55 +++++++- .../loop/content/shared/js/views.js | 2 +- .../loop/content/shared/js/views.jsx | 2 +- .../content/js/standaloneRoomViews.js | 20 ++- .../content/js/standaloneRoomViews.jsx | 20 ++- .../components/loop/test/functional/config.py | 1 + .../test/functional/test_1_browser_call.py | 31 +++-- .../loop/test/shared/activeRoomStore_test.js | 10 ++ .../loop/test/shared/mixins_test.js | 32 ++++- .../loop/test/shared/otSdkDriver_test.js | 121 +++++++++++++++++- 18 files changed, 297 insertions(+), 40 deletions(-) diff --git a/browser/components/loop/content/js/conversationViews.js b/browser/components/loop/content/js/conversationViews.js index 027c3c15652..5fd20dc9096 100644 --- a/browser/components/loop/content/js/conversationViews.js +++ b/browser/components/loop/content/js/conversationViews.js @@ -898,7 +898,7 @@ loop.conversationViews = (function(mozL10n) { React.createElement("div", {className: "conversation"}, React.createElement("div", {className: "media nested"}, React.createElement("div", {className: "video_wrapper remote_wrapper"}, - React.createElement("div", {className: "video_inner remote"}) + React.createElement("div", {className: "video_inner remote remote-stream"}) ), React.createElement("div", {className: localStreamClasses}) ), diff --git a/browser/components/loop/content/js/conversationViews.jsx b/browser/components/loop/content/js/conversationViews.jsx index 53b8e9871af..c3573ab252d 100644 --- a/browser/components/loop/content/js/conversationViews.jsx +++ b/browser/components/loop/content/js/conversationViews.jsx @@ -898,7 +898,7 @@ loop.conversationViews = (function(mozL10n) {
-
+
diff --git a/browser/components/loop/content/js/roomViews.js b/browser/components/loop/content/js/roomViews.js index 37dcdecd584..15eda16b711 100644 --- a/browser/components/loop/content/js/roomViews.js +++ b/browser/components/loop/content/js/roomViews.js @@ -274,7 +274,7 @@ loop.roomViews = (function(mozL10n) { React.createElement("div", {className: "conversation room-conversation"}, React.createElement("div", {className: "media nested"}, React.createElement("div", {className: "video_wrapper remote_wrapper"}, - React.createElement("div", {className: "video_inner remote"}) + React.createElement("div", {className: "video_inner remote remote-stream"}) ), React.createElement("div", {className: localStreamClasses}), React.createElement("div", {className: "screen hide"}) diff --git a/browser/components/loop/content/js/roomViews.jsx b/browser/components/loop/content/js/roomViews.jsx index b2793edf8b1..9a6c0670310 100644 --- a/browser/components/loop/content/js/roomViews.jsx +++ b/browser/components/loop/content/js/roomViews.jsx @@ -274,7 +274,7 @@ loop.roomViews = (function(mozL10n) {
-
+
diff --git a/browser/components/loop/content/shared/css/conversation.css b/browser/components/loop/content/shared/css/conversation.css index d6f91344c24..50098bbf298 100644 --- a/browser/components/loop/content/shared/css/conversation.css +++ b/browser/components/loop/content/shared/css/conversation.css @@ -231,7 +231,7 @@ /* Side by side video elements */ -.conversation .media.side-by-side .remote { +.conversation .media.side-by-side .remote-stream { width: 50%; float: left; } @@ -509,7 +509,7 @@ max-height: none; } -.conversation .media.nested .remote { +.conversation .media.nested .remote-stream { display: inline-block; position: absolute; /* workaround for lack of object-fit; see bug 1020445 */ width: 100%; diff --git a/browser/components/loop/content/shared/js/actions.js b/browser/components/loop/content/shared/js/actions.js index 4cc702c4878..7ff1951d7f7 100644 --- a/browser/components/loop/content/shared/js/actions.js +++ b/browser/components/loop/content/shared/js/actions.js @@ -218,6 +218,13 @@ loop.shared.actions = (function() { state: String }), + /** + * Used to notify that a shared screen is being received (or not). + */ + ReceivingScreenShare: Action.define("receivingScreenShare", { + receiving: Boolean + }), + /** * Creates a new room. * XXX: should move to some roomActions module - refs bug 1079284 diff --git a/browser/components/loop/content/shared/js/activeRoomStore.js b/browser/components/loop/content/shared/js/activeRoomStore.js index 9b875a41def..71ca3c557ce 100644 --- a/browser/components/loop/content/shared/js/activeRoomStore.js +++ b/browser/components/loop/content/shared/js/activeRoomStore.js @@ -73,7 +73,8 @@ loop.store.ActiveRoomStore = (function() { used: false, localVideoDimensions: {}, remoteVideoDimensions: {}, - screenSharingState: SCREEN_SHARE_STATES.INACTIVE + screenSharingState: SCREEN_SHARE_STATES.INACTIVE, + receivingScreenShare: false }; }, @@ -121,6 +122,7 @@ loop.store.ActiveRoomStore = (function() { "connectionFailure", "setMute", "screenSharingState", + "receivingScreenShare", "remotePeerDisconnected", "remotePeerConnected", "windowUnload", @@ -380,6 +382,13 @@ loop.store.ActiveRoomStore = (function() { this.setStoreState({screenSharingState: actionData.state}); }, + /** + * Used to note the current state of receiving screenshare data. + */ + receivingScreenShare: function(actionData) { + this.setStoreState({receivingScreenShare: actionData.receiving}); + }, + /** * Handles recording when a remote peer has connected to the servers. */ diff --git a/browser/components/loop/content/shared/js/mixins.js b/browser/components/loop/content/shared/js/mixins.js index 6595193e41d..064e9bbc502 100644 --- a/browser/components/loop/content/shared/js/mixins.js +++ b/browser/components/loop/content/shared/js/mixins.js @@ -259,7 +259,8 @@ loop.shared.mixins = (function() { }, /** - * Retrieve the dimensions of the remote video stream. + * Retrieve the dimensions of the active remote video stream. This assumes + * that if screens are being shared, the remote camera stream is hidden. * Example output: * { * width: 680, @@ -270,6 +271,8 @@ loop.shared.mixins = (function() { * offsetY: 0 * } * + * Note: This expects a class on the element that has the name "remote" or the + * same name as the possible video types (currently only "screen"). * Note: Once we support multiple remote video streams, this function will * need to be updated. * @return {Object} contains the remote stream dimension properties of its @@ -320,7 +323,7 @@ loop.shared.mixins = (function() { // Calculate the size of each individual letter- or pillarbox for convenience. remoteVideoDimensions.offsetX = remoteVideoDimensions.width - - remoteVideoDimensions.streamWidth + remoteVideoDimensions.streamWidth; if (remoteVideoDimensions.offsetX > 0) { remoteVideoDimensions.offsetX /= 2; } @@ -351,18 +354,22 @@ loop.shared.mixins = (function() { this._bufferedUpdateVideo = null; var localStreamParent = this._getElement(".local .OT_publisher"); var remoteStreamParent = this._getElement(".remote .OT_subscriber"); + var screenShareStreamParent = this._getElement('.screen .OT_subscriber'); if (localStreamParent) { localStreamParent.style.width = "100%"; } if (remoteStreamParent) { remoteStreamParent.style.height = "100%"; } + if (screenShareStreamParent) { + screenShareStreamParent.style.height = "100%"; + } // Update the position and dimensions of the containers of local video // streams, if necessary. The consumer of this mixin should implement the // actual updating mechanism. Object.keys(this._videoDimensionsCache.local).forEach(function(videoType) { - var ratio = this._videoDimensionsCache.local[videoType].aspectRatio + var ratio = this._videoDimensionsCache.local[videoType].aspectRatio; if (videoType == "camera" && this.updateLocalCameraPosition) { this.updateLocalCameraPosition(ratio); } diff --git a/browser/components/loop/content/shared/js/otSdkDriver.js b/browser/components/loop/content/shared/js/otSdkDriver.js index f9de6f75555..4ee8c59068e 100644 --- a/browser/components/loop/content/shared/js/otSdkDriver.js +++ b/browser/components/loop/content/shared/js/otSdkDriver.js @@ -136,6 +136,7 @@ loop.OTSdkDriver = (function() { this.session.on("connectionCreated", this._onConnectionCreated.bind(this)); this.session.on("streamCreated", this._onRemoteStreamCreated.bind(this)); + this.session.on("streamDestroyed", this._onRemoteStreamDestroyed.bind(this)); this.session.on("connectionDestroyed", this._onConnectionDestroyed.bind(this)); this.session.on("sessionDisconnected", @@ -280,6 +281,30 @@ loop.OTSdkDriver = (function() { this.dispatcher.dispatch(new sharedActions.RemotePeerConnected()); }, + /** + * Handles when a remote screen share is created, subscribing to + * the stream, and notifying the stores that a share is being + * received. + * + * @param {Stream} stream The SDK Stream: + * https://tokbox.com/opentok/libraries/client/js/reference/Stream.html + */ + _handleRemoteScreenShareCreated: function(stream) { + if (!this.getScreenShareElementFunc) { + return; + } + + // Let the stores know first so they can update the display. + this.dispatcher.dispatch(new sharedActions.ReceivingScreenShare({ + receiving: true + })); + + var remoteElement = this.getScreenShareElementFunc(); + + this.session.subscribe(stream, + remoteElement, this._getCopyPublisherConfig()); + }, + /** * Handles the event when the remote stream is created. * @@ -295,14 +320,13 @@ loop.OTSdkDriver = (function() { })); } - var remoteElement; if (event.stream.videoType === "screen") { - // XXX Implement in part 2. - remoteElement = "null"; - } else { - remoteElement = this.getRemoteElement(); + this._handleRemoteScreenShareCreated(event.stream); + return; } + var remoteElement = this.getRemoteElement(); + this.session.subscribe(event.stream, remoteElement, this._getCopyPublisherConfig()); @@ -315,7 +339,7 @@ loop.OTSdkDriver = (function() { /** * Handles the event when the local stream is created. * - * @param {StreamEvent} event The event details: + * @param {StreamEvent} event The event details: * https://tokbox.com/opentok/libraries/client/js/reference/StreamEvent.html */ _onLocalStreamCreated: function(event) { @@ -328,6 +352,25 @@ loop.OTSdkDriver = (function() { } }, + + /** + * Handles the event when the remote stream is destroyed. + * + * @param {StreamEvent} event The event details: + * https://tokbox.com/opentok/libraries/client/js/reference/StreamEvent.html + */ + _onRemoteStreamDestroyed: function(event) { + if (event.stream.videoType !== "screen") { + return; + } + + // All we need to do is notify the store we're no longer receiving, + // the sdk should do the rest. + this.dispatcher.dispatch(new sharedActions.ReceivingScreenShare({ + receiving: false + })); + }, + /** * Called from the sdk when the media access dialog is opened. * Prevents the default action, to prevent the SDK's "allow access" diff --git a/browser/components/loop/content/shared/js/views.js b/browser/components/loop/content/shared/js/views.js index da00f3e7fcc..9f090853f2b 100644 --- a/browser/components/loop/content/shared/js/views.js +++ b/browser/components/loop/content/shared/js/views.js @@ -358,7 +358,7 @@ loop.shared.views = (function(_, l10n) { React.createElement("div", {className: "conversation"}, React.createElement("div", {className: "media nested"}, React.createElement("div", {className: "video_wrapper remote_wrapper"}, - React.createElement("div", {className: "video_inner remote"}) + React.createElement("div", {className: "video_inner remote remote-stream"}) ), React.createElement("div", {className: localStreamClasses}) ), diff --git a/browser/components/loop/content/shared/js/views.jsx b/browser/components/loop/content/shared/js/views.jsx index 326bc44a2f9..936575e755c 100644 --- a/browser/components/loop/content/shared/js/views.jsx +++ b/browser/components/loop/content/shared/js/views.jsx @@ -358,7 +358,7 @@ loop.shared.views = (function(_, l10n) {
-
+
diff --git a/browser/components/loop/standalone/content/js/standaloneRoomViews.js b/browser/components/loop/standalone/content/js/standaloneRoomViews.js index 54781e191e4..5320eb708e1 100644 --- a/browser/components/loop/standalone/content/js/standaloneRoomViews.js +++ b/browser/components/loop/standalone/content/js/standaloneRoomViews.js @@ -251,7 +251,8 @@ loop.standaloneRoomViews = (function(mozL10n) { this.props.dispatcher.dispatch(new sharedActions.SetupStreamElements({ publisherConfig: this.getDefaultPublisherConfig({publishVideo: true}), getLocalElementFunc: this._getElement.bind(this, ".local"), - getRemoteElementFunc: this._getElement.bind(this, ".remote") + getRemoteElementFunc: this._getElement.bind(this, ".remote"), + getScreenShareElementFunc: this._getElement.bind(this, ".screen") })); } @@ -339,6 +340,19 @@ loop.standaloneRoomViews = (function(mozL10n) { "local-stream-audio": this.state.videoMuted }); + var remoteStreamClasses = React.addons.classSet({ + "video_inner": true, + "remote": true, + "remote-stream": true, + hide: this.state.receivingScreenShare + }); + + var screenShareStreamClasses = React.addons.classSet({ + "screen": true, + "remote-stream": true, + hide: !this.state.receivingScreenShare + }); + return ( React.createElement("div", {className: "room-conversation-wrapper"}, React.createElement("div", {className: "beta-logo"}), @@ -357,11 +371,13 @@ loop.standaloneRoomViews = (function(mozL10n) { mozL10n.get("self_view_hidden_message") ), React.createElement("div", {className: "video_wrapper remote_wrapper"}, - React.createElement("div", {className: "video_inner remote"}) + React.createElement("div", {className: remoteStreamClasses}), + React.createElement("div", {className: screenShareStreamClasses}) ), React.createElement("div", {className: localStreamClasses}) ), React.createElement(sharedViews.ConversationToolbar, { + dispatcher: this.props.dispatcher, video: {enabled: !this.state.videoMuted, visible: this._roomIsActive()}, audio: {enabled: !this.state.audioMuted, diff --git a/browser/components/loop/standalone/content/js/standaloneRoomViews.jsx b/browser/components/loop/standalone/content/js/standaloneRoomViews.jsx index 4aca9aba4f6..ac2e3288d66 100644 --- a/browser/components/loop/standalone/content/js/standaloneRoomViews.jsx +++ b/browser/components/loop/standalone/content/js/standaloneRoomViews.jsx @@ -251,7 +251,8 @@ loop.standaloneRoomViews = (function(mozL10n) { this.props.dispatcher.dispatch(new sharedActions.SetupStreamElements({ publisherConfig: this.getDefaultPublisherConfig({publishVideo: true}), getLocalElementFunc: this._getElement.bind(this, ".local"), - getRemoteElementFunc: this._getElement.bind(this, ".remote") + getRemoteElementFunc: this._getElement.bind(this, ".remote"), + getScreenShareElementFunc: this._getElement.bind(this, ".screen") })); } @@ -339,6 +340,19 @@ loop.standaloneRoomViews = (function(mozL10n) { "local-stream-audio": this.state.videoMuted }); + var remoteStreamClasses = React.addons.classSet({ + "video_inner": true, + "remote": true, + "remote-stream": true, + hide: this.state.receivingScreenShare + }); + + var screenShareStreamClasses = React.addons.classSet({ + "screen": true, + "remote-stream": true, + hide: !this.state.receivingScreenShare + }); + return (
@@ -357,11 +371,13 @@ loop.standaloneRoomViews = (function(mozL10n) { {mozL10n.get("self_view_hidden_message")}
-
+
+