* VRFS-3530, VRFS-3531 - allow user to test and disable video

This commit is contained in:
Seth Call 2015-09-08 20:30:47 -05:00
parent ba99f88048
commit a4f465b6d1
16 changed files with 359 additions and 89 deletions

View File

@ -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()
}

View File

@ -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;

View File

@ -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 {

View File

@ -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

View File

@ -11,6 +11,8 @@ context = window
videoWindowClosed : {}
howToUseVideoPopupClosed: {}
toggleVideo: {}
testVideo: {}
configureVideoPopupClosed: {}
checkPromptConfigureVideo: {}
setVideoEnabled: {}
})

View File

@ -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'
}
)

View File

@ -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")

View File

@ -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

View File

@ -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 `<option key="none" value="" selected={noneSelected}>None Selected</option>`
context._.each @state.deviceNames, (deviceName, deviceGuid) ->
selected = deviceName == selectedDevice
selected = deviceGuid == selectedDevice
webcams.push `<option key={deviceGuid} value={deviceGuid} selected={selected}>{deviceName}</option>`
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 `<option key={value} value={value} selected={selected}>{text}</option>`
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 = `<a className={testBtnClasses} onClick={this.toggleWebcam}>TEST VIDEO</a>`
else
testBtn = null
else if @state.videoShared
testBtnClassNames.disabled = !@state.videoEnabled
testBtnClasses = classNames(testBtnClassNames)
testBtn = `<a className={testBtnClasses} onClick={this.toggleWebcam}>STOP WEBCAM</a>`
else
toggleText = 'TEST WEBCAM'
testBtnClassNames.disabled = !@state.videoEnabled || noneSelected
testBtnClasses = classNames(testBtnClassNames)
testBtn = `<a className={testBtnClasses} onClick={this.toggleWebcam}>TEST WEBCAM</a>`
if @state.rescanning
rescanning =
@ -122,26 +143,66 @@ mixins.push(Reflux.listenTo(VideoStore, 'onVideoStateChanged'))
CHECKING GEAR
</span>`
`<form className="video">
<h2 className="sub-header select-webcam">select webcam:</h2>
<div className="webcam-select-container wizard_control">
<select onChange={this.selectWebcam}>
{webcams}
</select>
</div>
<h2 className="sub-header select-resolution">select video capture resolution & frame rate:</h2>
<div className="webcam-resolution-select-container wizard_control">
<select onChange={this.selectResolution}>
{captureResolutions}
</select>
<a className="ftue-video-settings-help">[?]</a>
</div>
<div className="configure-webcam wizard_control">
{backBtn}
<a className="button-orange webcam-test-btn" onClick={this.toggleWebcam}>{toggleText}</a>
</div>
{rescanning}
</form>`
if @props.show_header
if noWebcams
if PlatformStore.isWindows()
testVideoHelpText = `<span>The TEST VIDEO button will open the JamKazam video window to verify that receiving video works on your system.</span>`
header = `<div className="video-header">
<h2 className="subcaption">video gear:</h2>
<div className="subcaption">
JamKazam does not detect any webcams. You will not be able to send video, but you can still receive it from others. {testVideoHelpText}
</div>
</div>`
else
header =
`<div className="video-header">
<h2 className="subcaption">video gear:</h2>
<div className="subcaption">
Select webcam to use for video in sessions. Verify that you see video from webcam in the external application window (it may be behind this window).
</div>
</div>`
if @state.videoEnabled
disableVideoBtnText = "DISABLE VIDEO"
else
disableVideoBtnText = "ENABLE VIDEO"
if @props.show_disable || !@state.videoEnabled || @state.everDisabled
if @state.videoEnabled
disableHelpBtn = `<a className="ftue-video-disable-help">[?]</a>`
disableBtnClasses = classNames({'button-grey' : true, 'disable-video' : true, 'disabled' : @state.videoShared})
disableVideo =
`<div className="webcam-select-container wizard_control">
<a className={disableBtnClasses} onClick={this.disableVideo}>{disableVideoBtnText}</a>
{disableHelpBtn}
</div>`
`<div className="webcam-viewer">
{header}
<form className="video">
<h2 className="sub-header select-webcam">select webcam:</h2>
<div className="webcam-select-container wizard_control">
<select onChange={this.selectWebcam} disabled={noWebcams || !this.state.videoEnabled}>
{webcams}
</select>
</div>
<h2 className="sub-header select-resolution">select video capture resolution & frame rate:</h2>
<div className="webcam-resolution-select-container wizard_control">
<select onChange={this.selectResolution} disabled={noWebcams || !this.state.videoEnabled}>
{captureResolutions}
</select>
<a className="ftue-video-settings-help">[?]</a>
</div>
<div className="configure-webcam wizard_control">
{backBtn}
{testBtn}
</div>
{rescanning}
</form>
{disableVideo}
</div>`
componentDidMount: () ->
@ -149,10 +210,16 @@ mixins.push(Reflux.listenTo(VideoStore, 'onVideoStateChanged'))
@beforeShow()
$root = $(@getDOMNode())
$videoSettingsHelp = $root.find('.ftue-video-settings-help')
context.JK.helpBubble($videoSettingsHelp, 'ftue-video-settings', {}, {width:300}) if $videoSettingsHelp.length > 0
$videoSettingsHelp.click(false)
$videoDisableHelp = $root.find('.ftue-video-disable-help')
context.JK.helpBubble($videoDisableHelp, 'ftue-video-disable', {}, {width:300}) if $videoDisableHelp.length > 0
$videoDisableHelp.click(false)
componentWillUpdate: (nextProps, nextState) ->
# protect against non-video clients pointed at video-enabled server from getting into a session
@ -225,6 +292,22 @@ mixins.push(Reflux.listenTo(VideoStore, 'onVideoStateChanged'))
VideoActions.selectDevice(device, {})
disableVideo: (e) ->
e.preventDefault()
return if @state.videoShared
if @state.videoEnabled
context.JK.Banner.showYesNo({
title: "Disable Video?",
html: "You will not be able to send or receive video.",
yes: =>
VideoActions.setVideoEnabled(false)
})
else
VideoActions.setVideoEnabled(true)
updateBackend: (selectedResolution, selectedFps) ->
@logger.debug 'Selecting webcam resolution: ', selectedResolution
@logger.debug 'Selecting webcam fps: ', selectedFps
@ -253,15 +336,20 @@ mixins.push(Reflux.listenTo(VideoStore, 'onVideoStateChanged'))
toggleWebcam:(e) ->
e.preventDefault()
return unless this.state.videoEnabled
$toggleBtn = $(e.target)
# we should only do this if no device is currently selected
$root = $(@getDOMNode())
$select = $root.find('.webcam-select-container select')
device = $select.val()
VideoActions.selectDevice(device, {})
VideoActions.toggleVideo()
if Object.keys(@state.deviceNames).length == 0
VideoActions.testVideo()
else
device = $select.val()
#VideoActions.selectDevice(device, {})
VideoActions.toggleVideo()
#if this.isVideoShared()
# $toggleBtn.removeClass("selected")
@ -273,7 +361,7 @@ mixins.push(Reflux.listenTo(VideoStore, 'onVideoStateChanged'))
# @setState({videoShared: true})
selectedDeviceName:(state) ->
webcamName="None Configured"
webcamName = null
# protect against non-video clients pointed at video-enabled server from getting into a session
webcam = state.currentDevice
@logger.debug("currently selected video device", webcam)

View File

@ -6,12 +6,38 @@
context.JK.StepVideoGear = function (app, $dialog) {
var $step = null
var webcamViewerReact = null;
var $instructions = null;
var $noWebcamList = null;
function initialize(_$step) {
$step = _$step
var reactElement = React.createElement(window.WebcamViewer, {isVisible: false});
var reactDomNode = $step.find(".webcam-container").get(0)
webcamViewerReact = React.render(reactElement, reactDomNode)
$instructions = $step.find('.instructions')
$noWebcamList = $step.find('ul.no-webcam')
window.VideoStore.listen(onVideoStoreUpdated);
}
function onVideoStoreUpdated(videoState) {
var noWebcams = Object.keys(videoState.deviceNames).length == 0
var isWindows = window.PlatformStore.isWindows();
$instructions.removeClass('has-webcam no-webcam')
if(noWebcams) {
$instructions.addClass('no-webcam')
}
else {
$instructions.addClass('has-webcam')
}
$noWebcamList.removeClass('is-windows is-not-windows')
if(isWindows) {
$noWebcamList.addClass('is-windows')
}
else {
$noWebcamList.addClass('is-not-windows')
}
}
function beforeShow() {

View File

@ -2,11 +2,16 @@
#account-video-profile {
h2.subcaption {
margin-top:20px;
font-size: 23px;
font-weight: 400;
margin-bottom:20px !important;
}
.video-header {
margin-bottom:20px;
}
div.subcaption {
font-size:14px;
}
@ -18,7 +23,7 @@
}
.webcam-container {
margin-top:20px;
// margin-top:20px;
}
select {
@ -42,4 +47,8 @@
.configure-webcam {
float:right;
}
.disable-video {
margin-left:1px;
}
}

View File

@ -328,6 +328,11 @@
}
}
.disable-video {
margin-left: 5px;
margin-top: 17px;
}
&:nth-of-type(1) {
width: 50%;
height: 350px;
@ -375,6 +380,47 @@
color:$ColorLinkHover;
}
}
ul.has-webcam {
display:none;
}
ul.no-webcam {
display:none;
li.is-windows {
display:none;
}
li.is-not-windows {
display:none;
}
&.is-windows {
li.is-windows {
display:list-item;
}
}
&.is-not-windows {
li.is-not-windows {
display:list-item;
}
}
}
.ftue-box.instructions {
&.has-webcam {
ul.has-webcam {
display:block;
}
}
&.no-webcam {
ul.no-webcam {
display:block;
}
}
}
select {
width:350px;
}
}
}

View File

@ -18,13 +18,6 @@
<div id="account-video-content-scroller" class="content-body-scroller account-content-scroller">
<!-- content wrapper -->
<div class="content-wrapper account-video">
<div class="video-header">
<h2 class="subcaption">video gear:</h2>
<div class="subcaption">
Select webcam to use for video in sessions. Verify that you see video from webcam in the external application window (it may be behind this window).
</div>
</div>
<div class="webcam-container">
</div>

View File

@ -336,8 +336,17 @@ script type="text/template" id="template-help-new-webcam-found"
p
| {{data.name}} is now available to use.
script type="text/template" id="template-help-old-webcam-lost"
.old-webcam-lost
p
| {{data.name}} is no longer connected.
| {{data.name}} is no longer connected.
script type="text/template" id="template-help-ftue-video-disable"
.ftue-video-disable
p
| You can disable video entirely. There are two reasons why you might do that:
ul
li If you know you never want to see anyone else's video.
li If you are experiencing technical problems with others send you video.

View File

@ -19,7 +19,7 @@
.center
%a.button-orange.start-network-test{href:'#'} START NETWORK TEST
%br
%a.button-orange.forever-network-test{href:'#'} THE FOREVER TEST (ADMIN ONLY)
%a.hidden.button-orange.forever-network-test{href:'#'} THE FOREVER TEST (ADMIN ONLY)
.wizard-step-column
%h2 Test Results
.network-test-results.ftue-box

View File

@ -1,12 +1,17 @@
.help-text In this step, you will select your video gear. Please watch the video for best instructions.
.help-text In this step, you will select your video gear.
.wizard-step-content
.wizard-step-column
%h2 Instructions
.ftue-box.instructions
%ul
%ul.has-webcam
%li Select webcam to use for video in sessions.
%li Select video capture settings.
%li Click the Test webcam button to open a window and verify that your webcam is properly capturing video. Then use the Window / Close Video Window menu command to close the window, and click the Next button to move forward.
%ul.no-webcam
%li JamKazam does not detect any webcams.
%li You will not be able to send video, but you can still receive it from others.
%li.is-windows The TEST VIDEO button will open the JamKazam video window to verify that receiving video works on your system.
%li.is-not-windows You can skip this step.
.wizard-step-column
.webcam-container
.clearall