hurricane/arducam/resources/src/js/index_ui.js

var stream = VideoStream()
var host = window.location.host
if(!host) {
host = '10.10.10.1'
}
var imgElement = null
var cameraSettings = {
brightness: 0,
contrast: 0,
saturation: 0,
mirror: false,
flip: false,
filter: 0,
quality: 6,
resolution: 4,
clients: 0
}
var RESOLUTION_STR = [
'160x120',
'176x144',
'320x240',
'352x288',
'640x480',
'800x600',
'1024x768',
'1280x1024',
'1600x1200'
]
var stats = {
start_time: -1,
data_count: -1,
frame_count: -1
}
/** -----------------------------
* These are GUI helpers
*/
var $ = function(id) {
return document.getElementById(id)
}
var addClass = function(id, clazz) {
$(id).classList.add(clazz)
}
var removeClass = function(id, clazz) {
$(id).classList.remove(clazz)
}
var setClass = function(id, clazz) {
$(id).classList = clazz.split(' ')
}
var setText = function(id, txt) {
$(id).innerHTML = txt
}
var setVisible = function(id, visible) {
var el = $(id)
if(visible) {
el.classList.remove('d-none')
el.classList.add('d-block')
} else {
el.classList.remove('d-block')
el.classList.add('d-none')
}
}
/** -----------------------------
* Update the GUI's connection state
*
* @param state - the connection state:
* disconnected, connecting, connected
*/
var setConnectionState = function(state) {
if(state == 'disconnected') {
setVisible('dlg-connect', true)
setVisible('pn-display', false)
setText('txt-connect-title', 'Not connected')
setText('txt-connect-subtitle', 'Press here to connect')
setText('btn-connect', 'Connect')
removeClass('icn-loading', 'loading')
} else if(state == 'connecting') {
setConnectionState('disconnected')
setText('txt-connect-title', 'Connecting')
setText('txt-connect-subtitle', 'Opening connection to device ...')
setText('btn-connect', 'Cancel')
addClass('icn-loading', 'loading')
} else if(state == 'connected') {
$('image').src = 'data:image/gif;base64,R0lGODlhAQABAAAAACwAAAAAAQABAAA='
setVisible('dlg-connect', false)
setVisible('pn-display', true)
}
}
/** -----------------------------
* Update the throughput statistics
*/
var updateStats = function(imageData) {
stats.data_count += imageData.length
stats.frame_count += 1
var time_diff = ((Date.now() - stats.start_time) / 1000) || 1
var frame_rate = stats.frame_count / time_diff
var data_rate = stats.data_count / time_diff
var rate = ''
if(data_rate > 1e6) {
data_rate = data_rate / 1e6
rate = data_rate.toFixed(1) + 'MB/s'
} else if(data_rate > 1e3) {
data_rate = data_rate / 1e3
rate = data_rate.toFixed(1) + 'KB/s'
} else {
rate = data_rate.toFixed(1) + 'B/s'
}
$('txt-frame-rate').innerHTML = frame_rate.toFixed(1)
$('txt-data-rate').innerHTML = rate
}
/** -----------------------------
* Reset the throughput statistics
*/
var resetStats = function() {
stats.data_count = 0
stats.frame_count = 0
stats.start_time = Date.now()
}
/** -----------------------------
* Get the camera setting name from control element ID
*/
var getSettingFromId = function(id) {
var toks = id.split('-')
return toks[1]
}
/** -----------------------------
* Update camera setting on device
*/
var updateSetting = function(id, value) {
var controls = $('pn-controls').elements
var settingName = getSettingFromId(id)
value = Number(value)
var callback = function(result, data) {
// If we failed to update the setting
if(!result) {
// Then update the GUI based on the previously known camera settings
// This will revert the GUI back if the device failed to update
updateGuiSettings()
} else {
stream.getSettings()
}
// If the setting was successfully updated AND modified
// Then the device will broadcast the new settings to all connected clients
// and onSettingsUpdated() will be invoked with the new settings
// Re-enable all controls
for(var i = 0; i < controls.length; i++) {
controls[i].disabled = false
}
}
var setting = {}
setting[settingName] = value
for(var i = 0; i < controls.length; i++) {
controls[i].disabled = true
}
stream.updateSettings(setting, callback)
}
/** -----------------------------
* Update GUI camera setting controls based on the currently known camera settings
*/
var updateGuiSettings = function() {
// Update the sliders
var sliders = document.getElementsByClassName('slider')
for(var i = 0; i < sliders.length; i++) {
var slider = sliders[i]
var settingName = getSettingFromId(slider.id)
slider.value = cameraSettings[settingName]
if(slider.id === 'ctl-resolution') {
$(slider.id + '-val').innerHTML = RESOLUTION_STR[slider.value]
} else {
$(slider.id + '-val').innerHTML = slider.value
}
}
var filters = document.getElementsByClassName('btn-filter')
// Disable all filters
for(var i = 0; i < filters.length; i++) {
var filter = filters[i]
filter.classList.remove('active')
}
// Enable the filter that is enabled
filters[cameraSettings['filter']].classList.add('active')
// Update the toggles
var toggles = document.getElementsByClassName('toggle')
for(var i = 0; i < toggles.length; i++) {
var toggle = toggles[i]
var settingName = getSettingFromId(toggle.id)
toggle.value = !!cameraSettings[settingName]
}
$('txt-clients').innerHTML = cameraSettings['clients']
}
/**********************************************
* Set GUI element callbacks
*/
imgElement = $('image')
/** -----------------------------
* Add the 'connect' button event handler
*/
$('btn-connect').onclick = function() {
if(stream.isConnecting()) {
stream.stop()
} else if(!stream.isConnected()) {
setConnectionState('connecting')
stream.start(host)
}
}
$('btn-reset-stats').onclick = resetStats
/** -----------------------------
* Add the slider onChange event handlers
*/
var sliders = document.getElementsByClassName('slider')
for(var i = 0; i < sliders.length; i++) {
sliders[i].onchange = function() {
updateSetting(this.id, this.value)
}
sliders[i].oninput = function() {
if(this.id === 'ctl-resolution') {
this.setAttribute('value', RESOLUTION_STR[this.value])
} else {
this.setAttribute('value', this.value)
}
}
}
/** -----------------------------
* Add the image filter onclick event handlers
*/
var filters = document.getElementsByClassName('btn-filter')
for(var i = 0; i < filters.length; i++) {
filters[i].onclick = function() {
var value = this.id.split('-')[2]
updateSetting(this.id, Number(value))
}
}
/** -----------------------------
* Add the toggle onchange event handlers
*/
var toggles = document.getElementsByClassName('toggle')
for(var i = 0; i < toggles.length; i++) {
toggles[i].onchange = function() {
updateSetting(this.id, this.checked ? 1 : 0)
}
}
/**********************************************
* Set the video stream callbacks
*/
/** -----------------------------
* This is called when the stream connects
*/
stream.onConnected = function() {
resetStats()
setConnectionState('connected')
}
/** -----------------------------
* This is called when the stream disconnects
*/
stream.onDisconnected = function() {
setConnectionState('disconnected')
}
/** -----------------------------
* This is called when the device sends a new image
*/
stream.onImageAvailable = function(data) {
var blob = new Blob([data], {type: 'image/jpeg'});
updateStats(data)
var reader = new FileReader();
reader.onload = function(e) {
imgElement.src = e.target.result
}
reader.readAsDataURL(blob)
}
/** -----------------------------
* This is called when the device's settings are updated
*/
stream.onSettingsUpdated = function(settings) {
console.log('New settings: ' + JSON.stringify(settings))
cameraSettings = settings
updateGuiSettings()
}
// Set the GUI camera settings to their default value
resetStats()
updateGuiSettings()
// Put the GUI into a connecting state by default
setConnectionState('connecting')
// Then start the stream
stream.start(host)
console.log('Ready.')