diff --git a/web/app/assets/javascripts/accounts_video_profile.js b/web/app/assets/javascripts/accounts_video_profile.js index 628b1e870..ca5e667ad 100644 --- a/web/app/assets/javascripts/accounts_video_profile.js +++ b/web/app/assets/javascripts/accounts_video_profile.js @@ -12,14 +12,13 @@ }; app.bindScreen('account/video', screenBindings); - var reactElement = React.createElement(window.WebcamViewer, {isVisible: false}); + var reactElement = React.createElement(window.WebcamViewer, {isVisible: false, show_header: true, show_disable: true}); var reactDomNode = $("#account-video-profile .webcam-container").get(0) webcamViewerReact = React.render(reactElement, reactDomNode) } function beforeShow() { - console.log("webcamViewerReact", webcamViewerReact) webcamViewerReact.beforeShow() } diff --git a/web/app/assets/javascripts/fakeJamClient.js b/web/app/assets/javascripts/fakeJamClient.js index 464f992b8..7ff48dd44 100644 --- a/web/app/assets/javascripts/fakeJamClient.js +++ b/web/app/assets/javascripts/fakeJamClient.js @@ -74,17 +74,21 @@ function FTUESelectVideoCaptureDevice(device, settings) { - + return true; } function FTUESetVideoEncodeResolution(resolution) { - } + } + + function testVideoRender() { + + } function FTUEGetVideoCaptureDeviceNames() { return {"xy323ss": "Built-in Webcam HD"} } function FTUECurrentSelectedVideoDevice() { - return {"xy323ss": "Built-in Webcam HD"} //return {} + return {"xy323ss": "Built-in Webcam HD"} } function FTUEGetAvailableEncodeVideoResolutions() { return { @@ -117,6 +121,14 @@ return 30; } + function FTUESetVideoShareEnable(){ + + } + + function FTUEGetVideoShareEnable() { + return false; + } + function isSessVideoShared() { return videoShared; } @@ -357,8 +369,8 @@ function GetOS() { return 100000000; } function GetOSAsString() { - return "Win32"; - //return "MacOSX"; + //return "Win32"; + return "MacOSX"; } function LatencyUpdated(map) { dbg('LatencyUpdated:' + JSON.stringify(map)); } @@ -1252,7 +1264,8 @@ this.OnDownloadAvailable = OnDownloadAvailable; // Video functionality: - this.FTUESelectVideoCaptureDevice = FTUESelectVideoCaptureDevice + this.testVideoRender = testVideoRender; + this.FTUESelectVideoCaptureDevice = FTUESelectVideoCaptureDevice; this.FTUESetVideoEncodeResolution = FTUESetVideoEncodeResolution; this.FTUEGetVideoCaptureDeviceNames = FTUEGetVideoCaptureDeviceNames; this.FTUECurrentSelectedVideoDevice = FTUECurrentSelectedVideoDevice; @@ -1262,7 +1275,8 @@ this.FTUESetSendFrameRates = FTUESetSendFrameRates; this.GetCurrentVideoResolution = GetCurrentVideoResolution; this.GetCurrentVideoFrameRate = GetCurrentVideoFrameRate; - + this.FTUESetVideoShareEnable = FTUESetVideoShareEnable; + this.FTUEGetVideoShareEnable = FTUEGetVideoShareEnable; this.isSessVideoShared = isSessVideoShared; this.SessStopVideoSharing = SessStopVideoSharing; this.SessStartVideoSharing = SessStartVideoSharing; diff --git a/web/app/assets/javascripts/networkTestHelper.js b/web/app/assets/javascripts/networkTestHelper.js index 64de82b54..73170644b 100644 --- a/web/app/assets/javascripts/networkTestHelper.js +++ b/web/app/assets/javascripts/networkTestHelper.js @@ -394,10 +394,18 @@ if (success) { - startVideoTest(); + if(window.VideoStore.videoEnabled) { + startVideoTest(); + } + else { + // don't test video if it's disabled + renderStopTestVideo(null, null) - if(forever) { - prepareNetworkTest(); + numClientToTestVideo = STARTING_NUM_CLIENTS_VIDEO; + videoScoring = false; + configureStartButton(); + + $self.triggerHandler(NETWORK_TEST_DONE) } } else { diff --git a/web/app/assets/javascripts/react-components.js b/web/app/assets/javascripts/react-components.js index a7ca5b73f..118242dda 100644 --- a/web/app/assets/javascripts/react-components.js +++ b/web/app/assets/javascripts/react-components.js @@ -4,6 +4,7 @@ //= require_directory ./react-components/actions //= require ./react-components/stores/AppStore //= require ./react-components/stores/RecordingStore +//= require ./react-components/stores/VideoStore //= require ./react-components/stores/SessionStore //= require ./react-components/stores/MixerStore //= require ./react-components/stores/JamTrackStore @@ -12,7 +13,7 @@ //= require ./react-components/stores/SessionMyTracksStore //= require ./react-components/stores/SessionOtherTracksStore //= require ./react-components/stores/SessionMediaTracksStore -//= require ./react-components/stores/VideoStore +//= require ./react-components/stores/PlatformStore //= require_directory ./react-components/stores //= require_directory ./react-components/mixins //= require_directory ./react-components diff --git a/web/app/assets/javascripts/react-components/actions/VideoActions.js.coffee b/web/app/assets/javascripts/react-components/actions/VideoActions.js.coffee index 5879f124f..90bc00bbd 100644 --- a/web/app/assets/javascripts/react-components/actions/VideoActions.js.coffee +++ b/web/app/assets/javascripts/react-components/actions/VideoActions.js.coffee @@ -11,6 +11,8 @@ context = window videoWindowClosed : {} howToUseVideoPopupClosed: {} toggleVideo: {} + testVideo: {} configureVideoPopupClosed: {} checkPromptConfigureVideo: {} + setVideoEnabled: {} }) \ No newline at end of file diff --git a/web/app/assets/javascripts/react-components/stores/PlatformStore.js.coffee b/web/app/assets/javascripts/react-components/stores/PlatformStore.js.coffee new file mode 100644 index 000000000..b7ea92db4 --- /dev/null +++ b/web/app/assets/javascripts/react-components/stores/PlatformStore.js.coffee @@ -0,0 +1,21 @@ +$ = jQuery +context = window +logger = context.JK.logger + +@PlatformStore = Reflux.createStore( + { + logger: context.JK.logger + os: null + + init: -> + this.listenTo(context.AppStore, this.onAppInit) + + onAppInit: (@app) -> + @os = context.jamClient.GetOSAsString() + this.trigger({os: @os, isWindows: @isWindows}) + + isWindows: -> + @os == 'Win32' + + } +) \ No newline at end of file diff --git a/web/app/assets/javascripts/react-components/stores/SessionStore.js.coffee b/web/app/assets/javascripts/react-components/stores/SessionStore.js.coffee index c91ce7e0c..f20649cc7 100644 --- a/web/app/assets/javascripts/react-components/stores/SessionStore.js.coffee +++ b/web/app/assets/javascripts/react-components/stores/SessionStore.js.coffee @@ -42,6 +42,7 @@ NotificationActions = @NotificationActions # Register with the app store to get @app this.listenTo(context.AppStore, this.onAppInit) this.listenTo(context.RecordingStore, this.onRecordingChanged) + this.listenTo(context.VideoStore, this.onVideoChanged) onAppInit: (@app) -> @@ -57,6 +58,8 @@ NotificationActions = @NotificationActions @webcamViewer.init($sessionLayout, false) @webcamViewer.setVideoOff() + onVideoChanged: (@videoState) -> + issueChange: () -> @helper = new context.SessionHelper(@app, @currentSession, @participantsEverSeen, @isRecording, @downloadingJamTrack) this.trigger(@helper) @@ -182,8 +185,16 @@ NotificationActions = @NotificationActions @issueChange() onToggleSessionVideo: () -> - logger.debug("toggle session video") - @webcamViewer.toggleWebcam() if @webcamViewer? + + if @videoState?.videoEnabled + logger.debug("toggle session video") + @webcamViewer.toggleWebcam() if @webcamViewer? + else + context.JK.Banner.showAlert({ + title: "Video Is Disabled", + html: "To re-enable video, you must go your video settings in your account settings and enable video.", + }) + onAudioResync: () -> logger.debug("audio resyncing") diff --git a/web/app/assets/javascripts/react-components/stores/VideoStore.js.coffee b/web/app/assets/javascripts/react-components/stores/VideoStore.js.coffee index c989d3972..b2902863d 100644 --- a/web/app/assets/javascripts/react-components/stores/VideoStore.js.coffee +++ b/web/app/assets/javascripts/react-components/stores/VideoStore.js.coffee @@ -31,47 +31,62 @@ BackendToFrontendFPS = { videoShared: false videoOpen : false state : null + everDisabled : false init: -> - this.listenTo(context.SessionStore, this.onSessionChange) + this.listenTo(context.AppStore, this.onAppInit) + + onAppInit: (@app) -> - # someone has requested us to refresh our config + # someone has requested us to refresh our config onRefresh: -> # don't do any check if this is a client with no video enabled return unless context.jamClient.FTUECurrentSelectedVideoDevice? - currentDevice = context.jamClient.FTUECurrentSelectedVideoDevice() - deviceNames = context.jamClient.FTUEGetVideoCaptureDeviceNames() - #deviceCaps = context.jamClient.FTUEGetVideoCaptureDeviceCapabilities() - currentResolution = context.jamClient.GetCurrentVideoResolution() - currentFrameRate = context.jamClient.GetCurrentVideoFrameRate() - encodeResolutions = context.jamClient.FTUEGetAvailableEncodeVideoResolutions() - frameRates = context.jamClient.FTUEGetSendFrameRates() + videoEnabled = context.jamClient.FTUEGetVideoShareEnable() + if videoEnabled + currentDevice = context.jamClient.FTUECurrentSelectedVideoDevice() + deviceNames = context.jamClient.FTUEGetVideoCaptureDeviceNames() + #deviceCaps = context.jamClient.FTUEGetVideoCaptureDeviceCapabilities() + currentResolution = context.jamClient.GetCurrentVideoResolution() + currentFrameRate = context.jamClient.GetCurrentVideoFrameRate() + encodeResolutions = context.jamClient.FTUEGetAvailableEncodeVideoResolutions() + frameRates = context.jamClient.FTUEGetSendFrameRates() - autoSelect = false - if currentResolution == 0 - @logger.warn("current resolution not specified; defaulting to VGA") - autoSelect = true - currentResolution = 2 + autoSelect = false + if currentResolution == 0 + @logger.warn("current resolution not specified; defaulting to VGA") + autoSelect = true + currentResolution = 2 - if currentFrameRate == 0 - autoSelect = true - @logger.warn("current frame rate not specified; defaulting to 30") - currentFrameRate = 30 + if currentFrameRate == 0 + autoSelect = true + @logger.warn("current frame rate not specified; defaulting to 30") + currentFrameRate = 30 + else + # backend accepts 10,20,30 etc for FPS, but returns an indexed value (1, 2, 3). + convertedFrameRate = BackendToFrontendFPS[currentFrameRate] + @logger.debug("translating FPS: backend numeric #{currentFrameRate} to #{convertedFrameRate}") + currentFrameRate = convertedFrameRate + + # backend needs to be same as frontend + if autoSelect + context.jamClient.FTUESetVideoEncodeResolution(currentResolution) + context.jamClient.FTUESetSendFrameRates(currentFrameRate) else - # backend accepts 10,20,30 etc for FPS, but returns an indexed value (1, 2, 3). - convertedFrameRate = BackendToFrontendFPS[currentFrameRate] - @logger.debug("translating FPS: backend numeric #{currentFrameRate} to #{convertedFrameRate}") - currentFrameRate = convertedFrameRate + @everDisabled = true + # don't talk to the backend when video is disabled; avoiding crashes + currentDevice = null + deviceNames = {} + currentResolution: 0 + currentFrameRate: 0 + encodeResolutions: {} + frameRates: {} - # backend needs to be same as frontend - if autoSelect - context.jamClient.FTUESetVideoEncodeResolution(currentResolution) - context.jamClient.FTUESetSendFrameRates(currentFrameRate) #deviceCaps: deviceCaps, @@ -83,11 +98,21 @@ BackendToFrontendFPS = { encodeResolutions: encodeResolutions, frameRates: frameRates, videoShared: @videoShared - videoOpen: @videoOpen + videoOpen: @videoOpen, + videoEnabled: videoEnabled, + everDisabled: @everDisabled } this.trigger(@state) - onSessionChange: (@session) -> + onSetVideoEnabled: (enable) -> + + return unless context.jamClient.FTUESetVideoShareEnable? + + context.jamClient.FTUESetVideoShareEnable(enable) + + # keep state in sync + @state.videoEnabled = enable + @onRefresh() onStartVideo: -> if @howtoWindow? @@ -111,6 +136,14 @@ BackendToFrontendFPS = { @state.videoShared = @videoShared this.trigger(@state) + onTestVideo: () -> + + return unless context.jamClient.testVideoRender? + result = context.jamClient.testVideoRender() + + if !result + @app.layout.notify({title: 'Unable to initialize video window', text: "Please contact support@jamkazam.com"}) + onToggleVideo: () -> if @videoShared @onStopVideo() @@ -130,16 +163,21 @@ BackendToFrontendFPS = { this.trigger(@state) onSelectDevice: (device, caps) -> + result = context.jamClient.FTUESelectVideoCaptureDevice(device, caps) if(!result) @logger.error("onSelectDevice failed with device #{device}") + @app.layout.notify({title: 'Unable to select webcam', text: "Please try reconnecting webcam."}) + else + @state.currentDevice = context.jamClient.FTUECurrentSelectedVideoDevice(); + this.trigger(@state) onVideoWindowOpened: () -> @onRefresh() unless @state? - @logger.debug("in session? #{@session.inSession()}, currentDevice? #{@state?.currentDevice?}, videoShared? #{@videoShared}") + @logger.debug("in session? #{context.SessionStore.inSession()}, currentDevice? #{@state?.currentDevice?}, videoShared? #{@videoShared}") - if @session.inSession() && @state.currentDevice? && Object.keys(@state.currentDevice).length > 0 && !@videoShared + if context.SessionStore.inSession() && @state.currentDevice? && Object.keys(@state.currentDevice).length > 0 && !@videoShared context.JK.ModUtils.shouldShow(NAMED_MESSAGES.HOWTO_USE_VIDEO_NOSHOW).done((shouldShow) => @logger.debug("checking if user has 'should show' on video howto: #{shouldShow}") if shouldShow diff --git a/web/app/assets/javascripts/react-components/stores/WebcamViewer.js.jsx.coffee b/web/app/assets/javascripts/react-components/stores/WebcamViewer.js.jsx.coffee index 7da4b74a8..675eac076 100644 --- a/web/app/assets/javascripts/react-components/stores/WebcamViewer.js.jsx.coffee +++ b/web/app/assets/javascripts/react-components/stores/WebcamViewer.js.jsx.coffee @@ -11,6 +11,7 @@ if window.opener? VideoStore = reactContext.VideoStore VideoActions = reactContext.VideoActions +PlatformStore = reactContext.PlatformStore ALERT_NAMES = context.JK.ALERT_NAMES; @@ -47,7 +48,6 @@ for key, value of BackendToFrontend mixins = [] mixins.push(Reflux.listenTo(VideoStore, 'onVideoStateChanged')) - @WebcamViewer = React.createClass({ mixins: mixins @@ -79,10 +79,18 @@ mixins.push(Reflux.listenTo(VideoStore, 'onVideoStateChanged')) # build list of webcams webcams = [] + noneSelected = selectedDevice == null || selectedDevice.length == 0 + + # the backend does not allow setting no video camera. So if a webcam is selected, prevent un-selecting + if noneSelected + webcams.push `` + context._.each @state.deviceNames, (deviceName, deviceGuid) -> - selected = deviceName == selectedDevice + selected = deviceGuid == selectedDevice webcams.push `` + noWebcams = Object.keys(@state.deviceNames).length == 0 + # build list of capture resolutions captureResolutions = [] @@ -110,10 +118,23 @@ mixins.push(Reflux.listenTo(VideoStore, 'onVideoStateChanged')) captureResolutions.push `` - if @state.videoShared - toggleText = 'STOP WEBCAM' + + testBtnClassNames = {'button-orange' : true, 'webcam-test-btn' : true} + if noWebcams + if PlatformStore.isWindows() + testBtnClassNames.disabled = !@state.videoEnabled + testBtnClasses = classNames(testBtnClassNames) + testBtn = `TEST VIDEO` + else + testBtn = null + else if @state.videoShared + testBtnClassNames.disabled = !@state.videoEnabled + testBtnClasses = classNames(testBtnClassNames) + testBtn = `STOP WEBCAM` else - toggleText = 'TEST WEBCAM' + testBtnClassNames.disabled = !@state.videoEnabled || noneSelected + testBtnClasses = classNames(testBtnClassNames) + testBtn = `TEST WEBCAM` if @state.rescanning rescanning = @@ -122,26 +143,66 @@ mixins.push(Reflux.listenTo(VideoStore, 'onVideoStateChanged')) CHECKING GEAR ` - `
` + if @props.show_header + if noWebcams + if PlatformStore.isWindows() + testVideoHelpText = `The TEST VIDEO button will open the JamKazam video window to verify that receiving video works on your system.` + header = `