church_streamer/app/page_encoder.js

202 lines
5.8 KiB
JavaScript
Raw Permalink Normal View History

const m = require('mithril')
const client = require('./api/client')
const Encoder = {
oninit: function(vnode) {
this.list_devices = {
command: 'ffmpeg.exe',
arguments: '-hide_banner -sources decklink',
}
this.list_formats = {
command: '',
arguments: '<missing decklink device>',
}
this.resetStatus()
client.registerComponent(this)
},
resetStatus() {
this.settingsChanged = false
this.settings = {
device: '',
format_code: '',
command: '',
}
this.encoderStats= null
this.encoderStatus = {
starting: false,
running: false,
log: '...',
}
},
onremove: function(vnode) {
client.unregisterComponent(this)
},
ioInit: function() {
client.on(this, 'encoder.status', status => {
this.encoderStatus = status
if (!this.settingsChanged) {
this.settings = status.settings
}
this.updateListFormatsHelper()
})
client.on(this, 'encoder.stats', stats => {
this.encoderStats = stats
})
client.emit('encoder.status')
},
ioConnectionChanged(connected) {
if (!connected) {
this.resetStatus()
} else {
client.emit('encoder.status')
}
},
listDecklinkDevices() {
client.emit('encoder.run', this.list_devices)
},
listDecklinkFormats() {
if (!this.settings.device) return
client.emit('encoder.run', this.list_formats)
},
startClicked() {
client.emit('encoder.start')
},
stopClicked() {
client.emit('encoder.stop')
},
saveClicked() {
this.settingsChanged = false
client.emit('encoder.settings', this.settings)
},
updateListFormatsHelper() {
if (this.settings.device) {
this.list_formats.command = this.list_devices.command
this.list_formats.arguments = `-hide_banner -f decklink -list_formats 1 ${this.settings.device}`
}
},
updateSettings(key, vnode) {
this.settingsChanged = true
this.settings[key] = vnode.target.value
this.updateListFormatsHelper()
},
view: function(vnode) {
let stats = this.encoderStats
return [
m('div.column.settings', [
m('h2', 'Encoder status'),
m('p', 'Status'),
m('input', {
type: 'text',
readonly: true,
class: this.encoderStatus.running ? 'red'
: this.encoderStatus.starting ? 'green' : '',
value: (!client.isConnected
? '<Unknown>'
: this.encoderStatus.stopError ? `Forcefully stopped: ${this.encoderStatus.stopError}`
: stats?.errors ? `Streaming: ${stats.errors} Errors`
: stats?.showSlowSpeed ? `Streaming (Slow speed)`
: this.encoderStatus.running ? 'Streaming'
: this.encoderStatus.starting ? 'Starting'
: 'Not streaming'
)
+ ' '
+ (stats ? `(fps=${stats.fps} bitrate=${stats.bitrate}kbps speed=${stats.speed % 1 === 0 ? stats.speed : stats.speed.toFixed(3)}x)` : ''),
}),
m('.row', [
m('button.button', {
hidden: this.encoderStatus.running || this.encoderStatus.starting,
onclick: this.startClicked.bind(this),
}, 'Start'),
m('button.button', {
hidden: !this.encoderStatus.running && !this.encoderStatus.starting,
onclick: this.stopClicked.bind(this),
}, 'Stop'),
]),
// Decklink device section
m('h2', 'Settings'),
m('p', [
'Decklink device, example: ',
m('span.pre', '-i "DeckLink Mini Recorder"'),
]),
m('div.row', [
m('input', {
oncreate: (vnode) => {vnode.dom.setAttribute('spellcheck', 'false')},
type: 'text',
autocomplete: 'off',
value: this.settings.device,
oninput: this.updateSettings.bind(this, 'device'),
}),
m('button.button', {
onclick: this.listDecklinkDevices.bind(this),
}, 'List devices'),
]),
m('.row.meta', [
'Clicking List devices runs ',
m('.pre', `${this.list_devices.command} ${this.list_devices.arguments}`)
]),
// Decklink format section
m('p', [
'Input code, example: ',
m('span.pre', '-format_code Hi50'),
]),
m('div.row', [
m('input', {
oncreate: (vnode) => {vnode.dom.setAttribute('spellcheck', 'false')},
type: 'text',
autocomplete: 'off',
value: this.settings.format_code,
oninput: this.updateSettings.bind(this, 'format_code'),
}),
m('button.button', {
disabled: !Boolean(this.settings.device),
onclick: this.listDecklinkFormats.bind(this),
}, 'List format'),
]),
m('.row.meta', [
'Clicking List format runs ',
m('.pre', `${this.list_formats.command} ${this.list_formats.arguments}`)
]),
// Encoding options
m('p', 'Full encode command'),
m('textarea', {
oncreate: (vnode) => {vnode.dom.setAttribute('spellcheck', 'false')},
value: this.settings.command,
autocomplete: 'off',
oninput: this.updateSettings.bind(this, 'command'),
}),
m('.row.meta', [
'Any ',
m('.pre', 'ffmpeg.exe ... -f decklink ...'),
'will be replaced with ',
m('.pre', `ffmpeg.exe ... ${this.settings.format_code} -f decklink ${this.settings.device} ...`),
]),
// Controls
m('.row', [
m('button.button', {
hidden: !this.settingsChanged,
onclick: this.saveClicked.bind(this),
}, 'Save'),
]),
m('p', 'Log'),
m('pre', this.encoderStatus.log || '...'),
]),
]
},
}
module.exports = Encoder