241 lines
11 KiB
CoffeeScript
241 lines
11 KiB
CoffeeScript
context = window
|
|
ChannelGroupIds = context.JK.ChannelGroupIds
|
|
MixerActions = @MixerActions
|
|
ptrCount = 0
|
|
|
|
StatsInfo = {
|
|
aggregate: {
|
|
latency: {
|
|
good: (user, stats) -> "#{user.possessive} total latency from them to you is very low.",
|
|
warn: (user, stats) -> "#{user.possessive} total latency from them to you is typical.",
|
|
poor: (user, stats) -> "#{user.possessive} total latency from them to you is poor."
|
|
}
|
|
},
|
|
system: {
|
|
cpu: {
|
|
good: (user, stats) -> "#{user.possessive} computer processor is not overworked by JamKazam or the your system.",
|
|
warn: (user, stats) -> "#{user.possessive} computer processor is being heavily used. There is little spare capacity, and this means your processor may not be able to handle all of it's tasks, causing audio quality, latency, and other issues.",
|
|
poor: (user, stats) -> "#{user.possessive} computer processor is being very heavily used. There is little spare capacity, and this means your processor may not be able to handle all of it's tasks, causing audio quality, latency, and other issues."
|
|
}
|
|
},
|
|
network: {
|
|
wifi: {
|
|
good: (user, stats) -> "#{user.name} is using wired ethernet.",
|
|
warn: (user, stats) -> "#{user.name} is using Wi-Fi, which will create audio quality issues and additional latency.",
|
|
poor: (user, stats) -> "#{user.name} is using Wi-Fi, which will create audio quality issues and additional latency.",
|
|
},
|
|
net_bitrate: {
|
|
good: (user, stats) -> "#{user.name} has enough bandwidth to send you a high quality audio stream.",
|
|
warn: (user, stats) -> "#{user.name} has bandwidth to send you a degraded, but sufficient, audio stream.",
|
|
poor: (user, stats) -> "#{user.name} has not enough bandwidth to send you a decent quality audio stream.",
|
|
},
|
|
ping: {
|
|
good: (user, stats) -> "The internet connection between you and #{user.name} has very low latency.",
|
|
warn: (user, stats) -> "The internet connection between you and #{user.name} has average latency, which may affect staying in sync.",
|
|
poor: (user, stats) -> "The internet connection between you and #{user.name} has high latency, making it very difficult to stay in sync.",
|
|
},
|
|
pkt_loss: {
|
|
good: (user, stats) -> "The internet connection between you and #{user.name} loses a small % of packets. It should not affect your audio quality.",
|
|
warn: (user, stats) -> "The internet connection between you and #{user.name} loses a significant % of packets. This may result in periodical audio artifacts.",
|
|
poor: (user, stats) -> "The internet connection between you and #{user.name} loses a high % of packets. This will result in frequent audio artifacts.",
|
|
},
|
|
audiojq_median: {
|
|
good: (user, stats) -> "JamKazam has to maintain a only a small buffer of audio to preserve audio quality, resulting in minimal added latency.",
|
|
warn: (user, stats) -> "JamKazam has to maintain a significant buffer of audio to preserve audio quality, resulting in potentially noticeable additional latency.",
|
|
poor: (user, stats) -> "JamKazam has to maintain a large buffer of audio to preserve audio quality, resulting in noticeabley added latency.",
|
|
}
|
|
},
|
|
audio: {
|
|
framesize: {
|
|
good: (user, stats) -> "#{user.possessive} gear is reading and writing audio data at a very high rate, keeping gear-added latency low.",
|
|
warn: (user, stats) -> "#{user.possessive} gear is reading and writing audio at a average rate, causing a few milliseconds extra latency compared to a 2.5 Frame Size.",
|
|
poor: (user, stats) -> "#{user.possessive} gear is reading and writing audio at a slow rate, causing a decent amount of latency before the internet is involved.",
|
|
},
|
|
latency: {
|
|
good: (user, stats) -> "#{user.possessive} gear has a small amount of latency.",
|
|
warn: (user, stats) -> "#{user.possessive} gear has a significant amount of latency.",
|
|
poor: (user, stats) -> "#{user.possessive} gear has a large amount of latency, making it difficult to play in time."
|
|
},
|
|
input_jitter: {
|
|
good: (user, stats) -> "#{user.possessive} gear has a small amount of input jitter, meaning it is keeping good time with JamKazam as it reads in your input signal.",
|
|
warn: (user, stats) -> "#{user.possessive} gear has a significant amount of input jitter, meaning it might be periodically adding delay or audio artifacts to your inputs.",
|
|
poor: (user, stats) -> "#{user.possessive} gear has a large amount of input jitter, meaning it likely adding delay and audio artifacts to your inputs.",
|
|
},
|
|
output_jitter: {
|
|
good: (user, stats) -> "#{user.possessive} gear has a small amount of output jitter, meaning it is keeping good time with your JamKazam as writes out your audio output.",
|
|
warn: (user, stats) -> "#{user.possessive} gear has a significant amount of output jitter, meaning it might be periodically adding delay and audio artifacts to your output.",
|
|
poor: (user, stats) -> "#{user.possessive} gear has a large amount of output jitter, meaning it likely adding delay and audio artifacts to your output.",
|
|
},
|
|
audio_in_type: {
|
|
good: (user, stats) -> "#{user.name} using an ideal driver type for #{user.possessive.toLowerCase()} gear.",
|
|
warn: (user, stats) -> "#{user.name} using a problematic driver type for #{user.possessive.toLowerCase()} gear.",
|
|
poor: (user, stats) -> "#{user.name} using a driver type considered problematic.",
|
|
}
|
|
}
|
|
}
|
|
|
|
@SessionStatsHover = React.createClass({
|
|
|
|
propTypes: {
|
|
clientId: React.PropTypes.string
|
|
}
|
|
|
|
mixins: [Reflux.listenTo(@SessionStatsStore, "onStatsChanged")]
|
|
|
|
hover: (type, field) ->
|
|
logger.debug("hover! #{type} #{field}")
|
|
@setState({hoverType: type, hoverField:field})
|
|
|
|
hoverOut: () ->
|
|
logger.debug("hover out!")
|
|
@setState({hoverType: null, hoverField: null})
|
|
|
|
|
|
stat: (properties, type, name, field, value) ->
|
|
classes = {'status-icon': true}
|
|
classifier = properties[field + '_level']
|
|
classes[classifier] = true
|
|
`<div className='stat' onMouseOver={this.hover.bind(this, type, field)}><span className="title">- {name}</span><div className={classNames(classes)}></div><span className="stat-value">{value}</span></div>`
|
|
|
|
render: () ->
|
|
extraInfo = 'Hover over a stat to learn more.'
|
|
|
|
if @state.hoverType?
|
|
type = @state.hoverType
|
|
field = @state.hoverField
|
|
|
|
extraInfo = 'No extra info for this metric.'
|
|
|
|
classifier = @state.stats?[type]?[field + '_level']
|
|
|
|
if classifier?
|
|
info = StatsInfo[type]?[field]?[classifier](@props.participant.user, @state.stats)
|
|
if info?
|
|
extraInfo = info
|
|
|
|
computerStats = []
|
|
networkStats = []
|
|
audioStats = []
|
|
aggregateStats = []
|
|
|
|
aggregate = @state.stats?.aggregate
|
|
network = @state.stats?.network
|
|
system = @state.stats?.system
|
|
audio = @state.stats?.audio
|
|
|
|
aggregateTag = null
|
|
if aggregate?
|
|
if aggregate.latency
|
|
aggregateStats.push(@stat(aggregate, 'aggregate', 'Latency', 'latency', Math.round(aggregate.latency)))
|
|
|
|
aggregateTag =
|
|
`<div className="aggregate-stats stats-holder">
|
|
<h3>Aggregate</h3>
|
|
{aggregateStats}
|
|
</div>`
|
|
|
|
if system?
|
|
if system.cpu?
|
|
computerStats.push(@stat(system, 'system', 'Processor', 'cpu', Math.round(system.cpu) + ' %'))
|
|
|
|
if audio?
|
|
if audio.latency?
|
|
audioStats.push(@stat(audio, 'audio', 'Latency', 'latency', audio.latency.toFixed(1) + ' ms'))
|
|
if audio.input_jitter?
|
|
audioStats.push(@stat(audio, 'audio', 'Input Jitter', 'input_jitter', audio.input_jitter.toFixed(2)))
|
|
if audio.output_jitter?
|
|
audioStats.push(@stat(audio, 'audio', 'Output Jitter', 'output_jitter', audio.output_jitter.toFixed(2)))
|
|
|
|
if audio.audio_in_type?
|
|
audio_type = '?'
|
|
audio_long = audio.audio_in_type.toLowerCase()
|
|
if audio_long.indexOf('asio') > -1
|
|
audio_type = 'ASIO'
|
|
else if audio_long.indexOf('wdm') > -1
|
|
audio_type = 'WDM'
|
|
else if audio_long.indexOf('core') > -1
|
|
audio_type = 'CoreAudio'
|
|
audioStats.push(@stat(audio, 'audio', 'Gear Driver', 'audio_in_type', audio_type))
|
|
if audio.framesize?
|
|
framesize = '?'
|
|
if audio.framesize == 2.5
|
|
framesize = '2.5'
|
|
else if audio.framesize == 5
|
|
framesize = '5'
|
|
else if audio.framesize == 10
|
|
framesize = '10'
|
|
audioStats.push(@stat(audio, 'audio', 'Frame Size', 'framesize', framesize))
|
|
|
|
networkTag = null
|
|
if network?
|
|
if network.ping?
|
|
networkStats.push(@stat(network, 'network', 'Latency', 'ping', (network.ping / 2).toFixed(1) + ' ms'))
|
|
if network.audiojq_median?
|
|
networkStats.push(@stat(network, 'network', 'Jitter Queue', 'audiojq_median', network.audiojq_median.toFixed(1)))
|
|
if network.jitter_var?
|
|
networkStats.push(@stat(network, 'network', 'Jitter', 'jitter_var', network.jitter_var.toFixed(1)))
|
|
if network.pkt_loss?
|
|
networkStats.push(@stat(network, 'network', 'Packet Loss', 'pkt_loss', network.pkt_loss.toFixed(1) + ' %'))
|
|
if network.wifi?
|
|
if network.wifi
|
|
value = 'Wi-Fi'
|
|
else
|
|
value = 'Ethernet'
|
|
networkStats.push(@stat(network, 'network', 'Connectivity', 'wifi', value))
|
|
if network.audio_bitrate_rx?
|
|
networkStats.push(@stat(network, 'network', 'Audio Bw Rx', 'audio_bitrate_rx', Math.round(network.net_bitrate_rx) + ' k'))
|
|
if network.audio_bitrate_tx?
|
|
networkStats.push(@stat(network, 'network', 'Audio Bw Tx', 'audio_bitrate_tx', Math.round(network.net_bitrate_tx) + ' k'))
|
|
if network.video_rtpbw_rx?
|
|
networkStats.push(@stat(network, 'network', 'Video Bw Rx', 'video_rtpbw_rx', Math.round(network.video_rtpbw_rx) + ' k'))
|
|
if network.video_rtpbw_tx?
|
|
networkStats.push(@stat(network, 'network', 'Video Bw Tx', 'video_rtpbw_tx', Math.round(network.video_rtpbw_tx) + ' k'))
|
|
|
|
networkTag =
|
|
`<div className="network-stats stats-holder">
|
|
<h3>Internet</h3>
|
|
{networkStats}
|
|
</div>`
|
|
|
|
`<div className="stats-hover">
|
|
<h3>Session Diagnostics & Stats: {this.props.participant.user.name}</h3>
|
|
|
|
<div className="stats-area">
|
|
{aggregateTag}
|
|
<div className="computer-stats stats-holder">
|
|
<h3>Computer</h3>
|
|
{computerStats}
|
|
</div>
|
|
<div className="audio-stats stats-holder">
|
|
<h3>Audio Interface</h3>
|
|
{audioStats}
|
|
</div>
|
|
{networkTag}
|
|
</div>
|
|
<div className="stats-info">
|
|
{extraInfo}
|
|
</div>
|
|
</div>`
|
|
|
|
onStatsChanged: (stats) ->
|
|
stats = window.SessionStatsStore.stats
|
|
if stats?
|
|
clientStats = stats[@props.participant.client_id]
|
|
else
|
|
clientStats = null
|
|
@setState({stats: clientStats})
|
|
|
|
getInitialState: () ->
|
|
stats = window.SessionStatsStore.stats
|
|
if stats?
|
|
clientStats = stats[@props.participant.client_id]
|
|
else
|
|
clientStats = null
|
|
{stats: clientStats, hoverType: null, hoverField: null}
|
|
|
|
|
|
closeHover: (e) ->
|
|
e.preventDefault()
|
|
$container = $(this.getDOMNode()).closest('.react-holder')
|
|
$container.data('bt').btOff()
|
|
}) |