diff --git a/applications/luci-app-mjpg-streamer/htdocs/luci-static/resources/view/mjpg-streamer/mjpg-streamer.js b/applications/luci-app-mjpg-streamer/htdocs/luci-static/resources/view/mjpg-streamer/mjpg-streamer.js deleted file mode 100644 index f4533f7c49..0000000000 --- a/applications/luci-app-mjpg-streamer/htdocs/luci-static/resources/view/mjpg-streamer/mjpg-streamer.js +++ /dev/null @@ -1,264 +0,0 @@ -'use strict'; -'require view'; -'require form'; -'require uci'; -'require ui'; -'require poll'; - -/* Copyright 2014 Roger D < rogerdammit@gmail.com> -Licensed to the public under the Apache License 2.0. */ - -return view.extend({ - load: function () { - var self = this; - poll.add(function () { - self.render(); - }, 5); - - document - .querySelector('head') - .appendChild( - E('style', { type: 'text/css' }, [ - '.img-preview {display: inline-block !important;height: auto;width: 640px;padding: 4px;line-height: 1.428571429;background-color: #fff;border: 1px solid #ddd;border-radius: 4px;-webkit-transition: all .2s ease-in-out;transition: all .2s ease-in-out;margin-bottom: 5px;display: none;}', - ]), - ); - - return Promise.all([uci.load('mjpg-streamer')]); - }, - render: function () { - let m, s, o; - - m = new form.Map('mjpg-streamer', 'MJPG-streamer', _('mjpg streamer is a streaming application for Linux-UVC compatible webcams')); - - //General settings - - var section_gen = m.section(form.TypedSection, 'mjpg-streamer', _('General')); - section_gen.addremove = false; - section_gen.anonymous = true; - - var enabled = section_gen.option(form.Flag, 'enabled', _('Enabled'), _('Enable MJPG-streamer')); - - var input = section_gen.option(form.ListValue, 'input', _('Input plugin')); - input.depends('enabled', '1'); - input.value('uvc', 'UVC'); - // input: value("file", "File") - input.optional = false; - - var output = section_gen.option(form.ListValue, 'output', _('Output plugin')); - output.depends('enabled', '1'); - output.value('http', 'HTTP'); - output.value('file', 'File'); - output.optional = false; - - //Plugin settings - - s = m.section(form.TypedSection, 'mjpg-streamer', _('Plugin settings')); - s.addremove = false; - s.anonymous = true; - - s.tab('output_http', _('HTTP output')); - s.tab('output_file', _('File output')); - s.tab('input_uvc', _('UVC input')); - // s: tab("input_file", _("File input")) - - // Input UVC settings - - var this_tab = 'input_uvc'; - - var device = s.taboption(this_tab, form.Value, 'device', _('Device')); - device.default = '/dev/video0'; - //device.datatype = "device" - device.value('/dev/video0', '/dev/video0'); - device.value('/dev/video1', '/dev/video1'); - device.value('/dev/video2', '/dev/video2'); - device.optional = false; - - var resolution = s.taboption(this_tab, form.Value, 'resolution', _('Resolution')); - resolution.default = '640x480'; - resolution.value('320x240', '320x240'); - resolution.value('640x480', '640x480'); - resolution.value('800x600', '800x600'); - resolution.value('864x480', '864x480'); - resolution.value('960x544', '960x544'); - resolution.value('960x720', '960x720'); - resolution.value('1280x720', '1280x720'); - resolution.value('1280x960', '1280x960'); - resolution.value('1920x1080', '1920x1080'); - resolution.optional = true; - - var fps = s.taboption(this_tab, form.Value, 'fps', _('Frames per second')); - fps.datatype = 'and(uinteger, min(1))'; - fps.placeholder = '5'; - fps.optional = true; - - var yuv = s.taboption(this_tab, form.Flag, 'yuv', _('Enable YUYV format'), _('Automatic disabling of MJPEG mode')); - - var quality = s.taboption( - this_tab, - form.Value, - 'quality', - _('JPEG compression quality'), - _('Set the quality in percent. This setting activates YUYV format, disables MJPEG'), - ); - quality.datatype = 'range(0, 100)'; - - var minimum_size = s.taboption( - this_tab, - form.Value, - 'minimum_size', - _('Drop frames smaller than this limit'), - _('Set the minimum size if the webcam produces small-sized garbage frames. May happen under low light conditions'), - ); - minimum_size.datatype = 'uinteger'; - - var no_dynctrl = s.taboption(this_tab, form.Flag, 'no_dynctrl', _("Don't initialize dynctrls"), _('Do not initialize dynctrls of Linux-UVC driver')); - - var led = s.taboption(this_tab, form.ListValue, 'led', _('Led control')); - led.value('on', _('On')); - led.value('off', _('Off')); - led.value('blink', _('Blink')); - led.value('auto', _('Auto')); - led.optional = true; - - // Output HTTP settings - - this_tab = 'output_http'; - - var port = s.taboption(this_tab, form.Value, 'port', _('Port'), _('TCP port for this HTTP server')); - port.datatype = 'port'; - port.placeholder = '8080'; - - var enable_auth = s.taboption(this_tab, form.Flag, 'enable_auth', _('Authentication required'), _('Ask for username and password on connect')); - enable_auth.default = false; - - var username = s.taboption(this_tab, form.Value, 'username', _('Username')); - username.depends('enable_auth', '1'); - username.optional = false; - - var password = s.taboption(this_tab, form.Value, 'password', _('Password')); - password.depends('enable_auth', '1'); - password.password = true; - password.optional = false; - password.default = false; - - var www = s.taboption(this_tab, form.Value, 'www', _('WWW folder'), _('Folder that contains webpages')); - www.datatype = 'directory'; - www.default = '/www/webcam/'; - www.optional = false; - - - function init_stream() { - console.log('init_stream'); - start_stream(); - } - - function _start_stream() { - console.log('_start_stream'); - - var port = uci.get('mjpg-streamer', 'core', 'port'); - - if (uci.get('mjpg-streamer', 'core', 'enable_auth') == '1') { - var user = uci.get('mjpg-streamer', 'core', 'username'); - var pass = uci.get('mjpg-streamer', 'core', 'password'); - var login = user + ':' + pass + '@'; - } else { - var login = ''; - } - - var img = document.getElementById('video_preview') || video_preview; - img.src = 'http://' + login + location.hostname + ':' + port + '/?action=snapshot' + '&t=' + new Date().getTime(); - } - - function start_stream() { - console.log('start_stream'); - - setTimeout(function () { - _start_stream(); - }, 500); - } - - function on_error() { - console.log('on_error'); - - var img = video_preview; - img.style.display = 'none'; - - var stream_stat = document.getElementById('stream_status') || stream_status; - stream_stat.style.display = 'block'; - - start_stream(); - } - - function on_load() { - console.log('on_load'); - - var img = video_preview; - img.style.display = 'block'; - - var stream_stat = stream_status; - stream_stat.style.display = 'none'; - } - - //HTTP preview - var video_preview = E('img', { - 'id': 'video_preview', - 'class': 'img-preview', - 'error': on_error, - 'load': on_load, - }); - - var stream_status = E( - 'p', - { - 'id': 'stream_status', - 'style': 'text-align: center; color: orange; font-weight: bold;', - }, - _('Stream unavailable'), - ); - - - init_stream(); - - var preview = s.taboption(this_tab, form.DummyValue, '_dummy'); - preview.render = L.bind(function (view, section_id) { - return E([], [ - video_preview, - stream_status - ]); - }, preview, this); - preview.depends('output', 'http'); - - //Output file settings - - this_tab = 'output_file'; - - var folder = s.taboption(this_tab, form.Value, 'folder', _('Folder'), _('Set folder to save pictures')); - folder.placeholder = '/tmp/images'; - folder.datatype = 'directory'; - - //mjpeg=s.taboption(this_tab, Value, "mjpeg", _("Mjpeg output"), _("Check to save the stream to an mjpeg file")) - - var delay = s.taboption(this_tab, form.Value, 'delay', _('Interval between saving pictures'), _('Set the interval in millisecond')); - delay.placeholder = '5000'; - delay.datatype = 'uinteger'; - - var ringbuffer = s.taboption(this_tab, form.Value, 'ringbuffer', _('Ring buffer size'), _('Max. number of pictures to hold')); - ringbuffer.placeholder = '10'; - ringbuffer.datatype = 'uinteger'; - - var exceed = s.taboption(this_tab, form.Value, 'exceed', _('Exceed'), _('Allow ringbuffer to exceed limit by this amount')); - exceed.datatype = 'uinteger'; - - var command = s.taboption( - this_tab, - form.Value, - 'command', - _('Command to run'), - _('Execute command after saving picture. Mjpg-streamer parses the filename as first parameter to your script.'), - ); - - var link = s.taboption(this_tab, form.Value, 'link', _('Link newest picture to fixed file name'), _('Link the last picture in ringbuffer to fixed named file provided.')); - - return m.render(); - }, -}); diff --git a/applications/luci-app-mjpg-streamer/root/usr/share/luci/menu.d/luci-app-mjpg-streamer.json b/applications/luci-app-mjpg-streamer/root/usr/share/luci/menu.d/luci-app-mjpg-streamer.json deleted file mode 100644 index 9a5a8bd9eb..0000000000 --- a/applications/luci-app-mjpg-streamer/root/usr/share/luci/menu.d/luci-app-mjpg-streamer.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "admin/services/mjpg-streamer": { - "title": "MJPG-streamer", - "action": { - "type": "view", - "path": "mjpg-streamer/mjpg-streamer" - }, - "depends": { - "acl": [ - "luci-app-mjpg-streamer" - ], - "uci": { - "mjpg-streamer": true - } - } - } -} diff --git a/applications/luci-app-mjpg-streamer/root/usr/share/rpcd/acl.d/luci-app-mjpg-streamer.json b/applications/luci-app-mjpg-streamer/root/usr/share/rpcd/acl.d/luci-app-mjpg-streamer.json deleted file mode 100644 index 4a2f1df55a..0000000000 --- a/applications/luci-app-mjpg-streamer/root/usr/share/rpcd/acl.d/luci-app-mjpg-streamer.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "luci-app-mjpg-streamer": { - "description": "Grant UCI access for luci-app-mjpg-streamer", - "read": { - "uci": [ - "mjpg-streamer" - ] - }, - "write": { - "uci": [ - "mjpg-streamer" - ] - } - } -} \ No newline at end of file diff --git a/applications/luci-app-mjpg-streamer/Makefile b/applications/luci-app-ustreamer/Makefile similarity index 72% rename from applications/luci-app-mjpg-streamer/Makefile rename to applications/luci-app-ustreamer/Makefile index fd536350aa..b161b27701 100644 --- a/applications/luci-app-mjpg-streamer/Makefile +++ b/applications/luci-app-ustreamer/Makefile @@ -6,12 +6,14 @@ include $(TOPDIR)/rules.mk -LUCI_TITLE:=MJPG-Streamer service configuration module -LUCI_DEPENDS:=+luci-base +mjpg-streamer +LUCI_TITLE:=ustreamer service configuration module +LUCI_DEPENDS:=+luci-base +ustreamer PKG_LICENSE:=Apache-2.0 PKG_MAINTAINER:=Jo-Philipp Wich +PROVIDES:=luci-app-mjpeg-streamer + include ../../luci.mk # call BuildPackage - OpenWrt buildroot signature diff --git a/applications/luci-app-ustreamer/htdocs/luci-static/resources/view/ustreamer/ustreamer.js b/applications/luci-app-ustreamer/htdocs/luci-static/resources/view/ustreamer/ustreamer.js new file mode 100644 index 0000000000..cc98bed3fd --- /dev/null +++ b/applications/luci-app-ustreamer/htdocs/luci-static/resources/view/ustreamer/ustreamer.js @@ -0,0 +1,528 @@ +'use strict'; +'require form'; +'require fs'; +'require poll'; +'require uci'; +'require ui'; +'require view'; + +/* Licensed to the public under the Apache License 2.0. */ + +return view.extend({ + load() { + document + .querySelector('head') + .appendChild( + E('style', { type: 'text/css' }, [ + '.img-preview {display: inline-block !important;height: auto;width: 640px;padding: 4px;line-height: 1.428571429;background-color: #fff;border: 1px solid #ddd;border-radius: 4px;-webkit-transition: all .2s ease-in-out;transition: all .2s ease-in-out;margin-bottom: 5px;display: none;}', + ]), + ); + + return Promise.all([ + L.resolveDefault(fs.list('/dev/'), []).then(entries => entries.filter(e => /^video.*$/.test(e.name)) ), + uci.load('ustreamer'), + ]); + }, + render([video_devs]) { + let m, s, o; + + let self = this; + poll.add(() => { + self.load().then(([video_devs]) => { + self.render([video_devs]); + }); + }, 5); + + m = new form.Map('ustreamer', 'ustreamer', + _('µStreamer is a lightweight and very quick server to stream MJPEG video from any V4L2 device to the net.')); + + //General settings + + const section_gen = m.section(form.TypedSection, 'ustreamer', _('General')); + section_gen.addremove = false; + section_gen.anonymous = true; + + const enabled = section_gen.option(form.Flag, 'enabled', _('Enabled')); + + const log_level = section_gen.option(form.Value, 'log_level', _('Log level')); + log_level.placeholder = _('info'); + log_level.value('0', _('info')); + log_level.value('1', _('performance')); + log_level.value('2', _('verbose')); + log_level.value('3', _('debug')); + + //Plugin settings + + s = m.section(form.TypedSection, 'ustreamer', _('Plugin settings')); + s.addremove = true; + s.anonymous = true; + + s.tab('h264_sink', _('H264 sink')); + s.tab('output_http', _('HTTP output')); + s.tab('image_control', _('Image control')); + s.tab('jpeg_sink', _('JPEG sink')); + s.tab('raw_sink', _('RAW sink')); + s.tab('input_uvc', _('UVC input')); + + // Input UVC settings + + let this_tab = 'input_uvc'; + + const device = s.taboption(this_tab, form.Value, 'device', _('Device')); + device.placeholder = '/dev/video0'; + for (const dev of video_devs) + device.value(`/dev/${dev.name}`); + device.optional = false; + device.rmempty = false; + + const dtimeout = s.taboption(this_tab, form.Value, 'timeout', _('Timeout'), _('units: seconds')); + dtimeout.placeholder = '5'; + dtimeout.datatype = 'uinteger'; + + const input = s.taboption(this_tab, form.Flag, 'input', _('Input')); + input.default = input.disabled; + + const resolution = s.taboption(this_tab, form.Value, 'resolution', _('Resolution')); + resolution.placeholder = '640x480'; + resolution.value('320x240', '320x240'); + resolution.value('640x480', '640x480'); + resolution.value('800x600', '800x600'); + resolution.value('864x480', '864x480'); + resolution.value('960x544', '960x544'); + resolution.value('960x720', '960x720'); + resolution.value('1280x720', '1280x720'); + resolution.value('1280x960', '1280x960'); + resolution.value('1920x1080', '1920x1080'); + resolution.optional = true; + + const fps = s.taboption(this_tab, form.Value, 'desired_fps', _('Frames per second'), + _('Default: maximum possible.')); + fps.datatype = 'and(uinteger, min(1))'; + fps.placeholder = '5'; + fps.optional = true; + + const format = s.taboption(this_tab, form.Value, 'format', _('Format')); + format.placeholder = 'YUYV'; + format.value('BGR24'); + format.value('GREY'); + format.value('JPEG'); + format.value('MJPEG'); + format.value('RGB24'); + format.value('RGB565'); + format.value('UYVY'); + format.value('YUV420'); + format.value('YUYV'); + format.value('YVU420'); + format.value('YVYU'); + + const encoder = s.taboption(this_tab, form.Value, 'encoder', _('Encoder')); + encoder.value('CPU'); + encoder.value('HW'); + + const quality = s.taboption( + this_tab, + form.Value, + 'quality', + _('Quality'), + _('Set the quality in percent.'), + ); + quality.datatype = 'range(0, 100)'; + + + const allow_truncated_frames = s.taboption(this_tab, form.Flag, 'allow_truncated_frames', _('Allow truncated frames')); + allow_truncated_frames.default = allow_truncated_frames.disabled; + + const format_swap_rgb = s.taboption(this_tab, form.Flag, 'format_swap_rgb', _('Format: Swap RGB'), + _('Enable R-G-B order swapping: RGB to BGR and vice versa.')); + format_swap_rgb.default = format_swap_rgb.disabled; + + const persistent = s.taboption(this_tab, form.Flag, 'persistent', _('Persistent'), + _("Don't re-initialize device on timeout. Default: disabled.")); + persistent.default = persistent.disabled; + + const dv_timings = s.taboption(this_tab, form.Flag, 'dv_timings', _('DV Timings'), + _("Enable DV-timings querying and events processing to automatic resolution change. Default: disabled.")); + dv_timings.default = dv_timings.disabled; + + const tv_standard = s.taboption(this_tab, form.Value, 'tv_standard', _('TV standard')); + tv_standard.placeholder = ''; + tv_standard.value('PAL'); + tv_standard.value('NTSC'); + tv_standard.value('SECAM'); + + const io_method = s.taboption(this_tab, form.Value, 'io_method', _('IO method')); + io_method.placeholder = 'MMAP'; + io_method.value('MMAP'); + io_method.value('USERPTR'); + + const buffers = s.taboption(this_tab, form.Value, 'buffers', _('Buffers'), + _('The number of buffers to receive data from the device.') + '
' + + _('Each buffer may processed using an independent thread.') + '
' + + _('Default: 3 (the number of CPU cores (but not more than 4) + 1).')); + buffers.datatype = 'and(uinteger, min(1))'; + buffers.placeholder = '3'; + buffers.optional = true; + + const workers = s.taboption(this_tab, form.Value, 'workers', _('Workers'), + _('The number of worker threads but not more than buffers.') + '
' + + _('Default: 2 (the number of CPU cores (but not more than 4)).')); + workers.datatype = 'and(uinteger, min(1))'; + workers.placeholder = '2'; + workers.optional = true; + + const m2m_device = s.taboption(this_tab, form.FileUpload, 'm2m_device', _('M2M device')); + m2m_device.root_directory = '/dev'; + m2m_device.show_hidden = true; + m2m_device.directory_create = false; + m2m_device.enable_download = false; + m2m_device.enable_upload = false; + m2m_device.enable_remove = false; + m2m_device.optional = true; + m2m_device.datatype = 'file'; + + const min_frame_size = s.taboption( + this_tab, + form.Value, + 'min_frame_size', + _('Drop frames smaller than this limit'), + _('Set the minimum size if the webcam produces small-sized garbage frames. May happen under low light conditions'), + ); + min_frame_size.datatype = 'uinteger'; + min_frame_size.placeholder = '128'; + + const device_error_delay = s.taboption(this_tab, form.Value, 'device_error_delay', _('Device error delay')); + device_error_delay.datatype = 'and(uinteger, min(1))'; + device_error_delay.placeholder = '1'; + device_error_delay.optional = true; + + // Output HTTP settings + + this_tab = 'output_http'; + + const host = s.taboption(this_tab, form.Value, 'host', _('Host'), _('TCP host for this HTTP server')); + host.datatype = 'host'; + host.placeholder = '::'; + host.datatype = 'or(hostname,ipaddr)'; + host.optional = false; + + const port = s.taboption(this_tab, form.Value, 'port', _('Port'), _('TCP port for this HTTP server')); + port.datatype = 'port'; + port.placeholder = '8080'; + port.optional = false; + + const enable_auth = s.taboption(this_tab, form.Flag, 'enable_auth', _('Authentication required'), _('Ask for username and password on connect')); + enable_auth.default = false; + + const username = s.taboption(this_tab, form.Value, 'user', _('Username')); + username.depends('enable_auth', '1'); + username.optional = false; + + const password = s.taboption(this_tab, form.Value, 'pass', _('Password')); + password.depends('enable_auth', '1'); + password.password = true; + password.optional = false; + password.default = false; + + const staticres = s.taboption(this_tab, form.Value, 'static', _('WWW folder'), _('Folder that contains webpages')); + staticres.datatype = 'directory'; + staticres.placeholder = '/www/webcam/'; + staticres.optional = false; + + const unix = s.taboption(this_tab, form.Value, 'unix', _('Socket'), _('Folder that contains the socket')); + unix.datatype = 'file'; + unix.placeholder = '/path/to/socket'; + + const unix_mode = s.taboption(this_tab, form.Value, 'unix_mode', _('Socket Permissions')); + unix_mode.datatype = 'string'; + unix_mode.placeholder = '660'; + + const drop_same_frames = s.taboption(this_tab, form.Flag, 'drop_same_frames', _('Drop same frames')); + drop_same_frames.default = drop_same_frames.disabled; + + const fake_resolution = s.taboption(this_tab, form.Value, 'fake_resolution', _('Fake resolution')); + fake_resolution.placeholder = '640x480'; + fake_resolution.keylist = resolution.keylist; + fake_resolution.vallist = resolution.vallist; + + const allow_origin = s.taboption(this_tab, form.Value, 'allow_origin', _('Allow origin')); + allow_origin.values = resolution.values; + + const instance_id = s.taboption(this_tab, form.Value, 'instance_id', _('Instance ID')); + + const server_timeout = s.taboption(this_tab, form.Value, 'server_timeout', _('Server timeout')); + server_timeout.datatype = 'uinteger'; + server_timeout.placeholder = '10'; + + + + function init_stream() { + console.debug('init_stream'); + start_stream(); + } + + function _start_stream() { + console.debug('_start_stream'); + + const port = uci.get('ustreamer', 'core', 'port'); + let login; + + if (uci.get('ustreamer', 'core', 'enable_auth') == '1') { + const user = uci.get('ustreamer', 'core', 'username'); + const pass = uci.get('ustreamer', 'core', 'password'); + login = `${user}:${pass}@`; + } else { + login = ''; + } + + const img = document.getElementById('video_preview') || video_preview; + img.src = 'http://' + login + location.hostname + ':' + port + '/?action=snapshot' + '&t=' + new Date().getTime(); + } + + function start_stream() { + console.debug('start_stream'); + + setTimeout(function () { + _start_stream(); + }, 5000); + } + + function on_error() { + console.warn('on_error'); + + const img = video_preview; + img.style.display = 'none'; + + const stream_stat = document.getElementById('stream_status') || stream_status; + stream_stat.style.display = 'block'; + + // start_stream(); + } + + function on_load() { + console.debug('on_load'); + + const img = video_preview; + img.style.display = 'block'; + + const stream_stat = stream_status; + stream_stat.style.display = 'none'; + } + + //HTTP preview + const video_preview = E('img', { + 'id': 'video_preview', + 'class': 'img-preview', + 'error': on_error, + 'load': on_load, + }); + + const stream_status = E('p', { + 'id': 'stream_status', + 'style': 'text-align: center; color: orange; font-weight: bold;', + }, + _('Stream unavailable'), + ); + + + init_stream(); + + const preview = s.taboption(this_tab, form.DummyValue, '_dummy'); + preview.render = L.bind(function (view, section_id) { + return E([], [ + video_preview, + stream_status + ]); + }, preview, this); + preview.depends('output', 'http'); + + // JPEG sink settings + + this_tab = 'jpeg_sink'; + + const jpeg_sink = s.taboption(this_tab, form.Value, 'jpeg_sink', _('JPEG sink'), + _('Use the shared memory to sink JPEG frames. Default: disabled.') + '
' + + _('The name should end with a suffix ".jpeg".') + '
' + + _('Default: disabled.')); + jpeg_sink.placeholder = 'name.jpeg'; + jpeg_sink.optional = true; + + const jpeg_sink_mode = s.taboption(this_tab, form.Value, 'jpeg_sink_mode', _('JPEG sink mode'), + _('Set JPEG sink permissions (like 777). Default: 660.')); + jpeg_sink_mode.datatype = 'string'; + jpeg_sink_mode.placeholder = '660'; + jpeg_sink_mode.optional = true; + + const jpeg_sink_client_ttl = s.taboption(this_tab, form.Value, 'jpeg_sink_client_ttl', _('Client TTL'), + _('Default: 10.')); + jpeg_sink_client_ttl.datatype = 'uinteger'; + jpeg_sink_client_ttl.placeholder = '10'; + jpeg_sink_client_ttl.optional = true; + + const jpeg_sink_timeout = s.taboption(this_tab, form.Value, 'jpeg_sink_timeout', _('JPEG sink timeout'), + _('Timeout for lock. Default: 1.')); + jpeg_sink_timeout.datatype = 'uinteger'; + jpeg_sink_timeout.placeholder = '1'; + jpeg_sink_timeout.optional = true; + + const jpeg_sink_rm = s.taboption(this_tab, form.Flag, 'jpeg_sink_rm', _('Remove JPEG sink'), + _('Remove JPEG sink file on exit')); + jpeg_sink_rm.default = jpeg_sink_rm.disabled; + + // RAW sink settings + + this_tab = 'raw_sink'; + + const raw_sink = s.taboption(this_tab, form.Value, 'raw_sink', _('RAW sink'), + _('Use the shared memory to sink RAW frames. Default: disabled.') + '
' + + _('The name should end with a suffix ".raw".') + '
' + + _('Default: disabled.')); + raw_sink.placeholder = 'name.raw'; + raw_sink.optional = true; + + const raw_sink_mode = s.taboption(this_tab, form.Value, 'raw_sink_mode', _('RAW sink mode'), + _('Set RAW sink permissions (like 777). Default: 660.')); + raw_sink_mode.datatype = 'string'; + raw_sink_mode.placeholder = '660'; + raw_sink_mode.optional = true; + + const raw_sink_client_ttl = s.taboption(this_tab, form.Value, 'raw_sink_client_ttl', _('RAW sink client TTL'), + _('Client TTL. Default: 10.')); + raw_sink_client_ttl.datatype = 'uinteger'; + raw_sink_client_ttl.placeholder = '10'; + raw_sink_client_ttl.optional = true; + + const raw_sink_timeout = s.taboption(this_tab, form.Value, 'raw_sink_timeout', _('RAW sink timeout'), + _('Timeout for lock. Default: 1.')); + raw_sink_timeout.datatype = 'uinteger'; + raw_sink_timeout.placeholder = '1'; + raw_sink_timeout.optional = true; + + const raw_sink_rm = s.taboption(this_tab, form.Flag, 'raw_sink_rm', _('Remove RAW sink'), + _('Remove shared memory on stop. Default: disabled.')); + raw_sink_rm.default = raw_sink_rm.disabled; + + // H264 sink settings + + this_tab = 'h264_sink'; + + const h264_sink = s.taboption(this_tab, form.Value, 'h264_sink', _('H264 sink'), + _('Use the shared memory to sink H264 frames. Default: disabled.') + '
' + + _('The name should end with a suffix ".h264"') + '
' + + _('Default: disabled.')); + h264_sink.placeholder = 'name.h264'; + h264_sink.optional = true; + + const h264_sink_mode = s.taboption(this_tab, form.Value, 'h264_sink_mode', _('H264 sink mode'), + _('Set H264 sink permissions (like 777). Default: 660.')); + h264_sink_mode.datatype = 'string'; + h264_sink_mode.placeholder = '660'; + h264_sink_mode.optional = true; + + const h264_sink_rm = s.taboption(this_tab, form.Flag, 'h264_sink_rm', _('Remove'), + _('Remove shared memory on stop. Default: disabled.')); + h264_sink_rm.default = h264_sink_rm.disabled; + + const h264_sink_client_ttl = s.taboption(this_tab, form.Value, 'h264_sink_client_ttl', _('Sink client TTL'), + _('Client TTL. Default: 10.')); + h264_sink_client_ttl.datatype = 'uinteger'; + h264_sink_client_ttl.placeholder = '10'; + h264_sink_client_ttl.optional = true; + + const h264_sink_timeout = s.taboption(this_tab, form.Value, 'h264_sink_timeout', _('Sink timeout'), + _('Timeout for lock. Default: 1.')); + h264_sink_timeout.datatype = 'uinteger'; + h264_sink_timeout.placeholder = '1'; + h264_sink_timeout.optional = true; + + const h264_bitrate = s.taboption(this_tab, form.Value, 'h264_bitrate', _('Bitrate'), + _('H264 bitrate in Kbps. Default: 5000.')); + h264_bitrate.datatype = 'uinteger'; + h264_bitrate.placeholder = '5000'; + h264_bitrate.optional = true; + + const h264_gop = s.taboption(this_tab, form.Value, 'h264_gop', _('H264 GOP'), + _('Interval between keyframes. Default: 30.')); + h264_gop.datatype = 'uinteger'; + h264_gop.placeholder = '30'; + h264_gop.optional = true; + + const h264_m2m_device = s.taboption(this_tab, form.FileUpload, 'h264_m2m_device', _('H264 M2M device'), + _('Path to V4L2 M2M encoder device. Default: auto select.')); + h264_m2m_device.root_directory = '/dev'; + h264_m2m_device.show_hidden = true; + h264_m2m_device.directory_create = false; + h264_m2m_device.enable_download = false; + h264_m2m_device.enable_upload = false; + h264_m2m_device.enable_remove = false; + h264_m2m_device.optional = true; + h264_m2m_device.datatype = 'file'; + + const h264_boost = s.taboption(this_tab, form.Flag, 'h264_boost', _('H264 boost'), + _('Increase encoder performance on PiKVM V4. Default: disabled.')); + h264_boost.default = h264_boost.disabled; + + const exit_on_no_clients = s.taboption(this_tab, form.Flag, 'exit_on_no_clients', _('Exit on no clients'), + _('Exit the program if there have been no stream or sink clients ') + + _('or any HTTP requests in the last N seconds. Default: 0 (disabled).')); + exit_on_no_clients.default = exit_on_no_clients.disabled; + + // Image control settings + + this_tab = 'image_control'; + + const image_default = s.taboption(this_tab, form.Flag, 'image_default', _('Use device defaults')); + image_default.default = image_default.disabled; + + const brightness = s.taboption(this_tab, form.Value, 'brightness', _('Brightness')); + brightness.placeholder = '128 | auto'; + brightness.optional = true; + + const contrast = s.taboption(this_tab, form.Value, 'contrast', _('Contrast')); + contrast.placeholder = '128'; + contrast.optional = true; + + const saturation = s.taboption(this_tab, form.Value, 'saturation', _('Saturation')); + saturation.placeholder = '128'; + saturation.optional = true; + + const hue = s.taboption(this_tab, form.Value, 'hue', _('Hue')); + hue.placeholder = '128 | auto'; + hue.optional = true; + + const gamma = s.taboption(this_tab, form.Value, 'gamma', _('Gamma')); + gamma.placeholder = '128'; + gamma.optional = true; + + const sharpness = s.taboption(this_tab, form.Value, 'sharpness', _('Sharpness')); + sharpness.placeholder = '128'; + sharpness.optional = true; + + const backlight_compensation = s.taboption(this_tab, form.Value, 'backlight_compensation', _('Backlight compensation')); + backlight_compensation.placeholder = '128'; + backlight_compensation.optional = true; + + const white_balance = s.taboption(this_tab, form.Value, 'white_balance', _('White balance')); + white_balance.placeholder = '128 | auto'; + white_balance.optional = true; + + const gain = s.taboption(this_tab, form.Value, 'gain', _('Gain')); + gain.placeholder = '128 | auto'; + gain.optional = true; + + const color_effect = s.taboption(this_tab, form.Value, 'color_effect', _('Color effect')); + color_effect.placeholder = '128'; + color_effect.optional = true; + + const rotate = s.taboption(this_tab, form.Value, 'rotate', _('Rotate')); + rotate.datatype = 'uinteger'; + rotate.optional = true; + + const flip_horizontal = s.taboption(this_tab, form.Flag, 'flip_horizontal', _('Flip horizontally')); + flip_horizontal.default = flip_horizontal.disabled; + + const flip_vertical = s.taboption(this_tab, form.Flag, 'flip_vertical', _('Flip vertically')); + flip_vertical.default = flip_vertical.disabled; + + return m.render(); + }, +}); diff --git a/applications/luci-app-mjpg-streamer/po/ar/mjpg-streamer.po b/applications/luci-app-ustreamer/po/ar/ustreamer.po similarity index 100% rename from applications/luci-app-mjpg-streamer/po/ar/mjpg-streamer.po rename to applications/luci-app-ustreamer/po/ar/ustreamer.po diff --git a/applications/luci-app-mjpg-streamer/po/bg/mjpg-streamer.po b/applications/luci-app-ustreamer/po/bg/ustreamer.po similarity index 100% rename from applications/luci-app-mjpg-streamer/po/bg/mjpg-streamer.po rename to applications/luci-app-ustreamer/po/bg/ustreamer.po diff --git a/applications/luci-app-mjpg-streamer/po/bn_BD/mjpg-streamer.po b/applications/luci-app-ustreamer/po/bn_BD/ustreamer.po similarity index 100% rename from applications/luci-app-mjpg-streamer/po/bn_BD/mjpg-streamer.po rename to applications/luci-app-ustreamer/po/bn_BD/ustreamer.po diff --git a/applications/luci-app-mjpg-streamer/po/ca/mjpg-streamer.po b/applications/luci-app-ustreamer/po/ca/ustreamer.po similarity index 100% rename from applications/luci-app-mjpg-streamer/po/ca/mjpg-streamer.po rename to applications/luci-app-ustreamer/po/ca/ustreamer.po diff --git a/applications/luci-app-mjpg-streamer/po/cs/mjpg-streamer.po b/applications/luci-app-ustreamer/po/cs/ustreamer.po similarity index 100% rename from applications/luci-app-mjpg-streamer/po/cs/mjpg-streamer.po rename to applications/luci-app-ustreamer/po/cs/ustreamer.po diff --git a/applications/luci-app-mjpg-streamer/po/da/mjpg-streamer.po b/applications/luci-app-ustreamer/po/da/ustreamer.po similarity index 100% rename from applications/luci-app-mjpg-streamer/po/da/mjpg-streamer.po rename to applications/luci-app-ustreamer/po/da/ustreamer.po diff --git a/applications/luci-app-mjpg-streamer/po/de/mjpg-streamer.po b/applications/luci-app-ustreamer/po/de/ustreamer.po similarity index 100% rename from applications/luci-app-mjpg-streamer/po/de/mjpg-streamer.po rename to applications/luci-app-ustreamer/po/de/ustreamer.po diff --git a/applications/luci-app-mjpg-streamer/po/el/mjpg-streamer.po b/applications/luci-app-ustreamer/po/el/ustreamer.po similarity index 100% rename from applications/luci-app-mjpg-streamer/po/el/mjpg-streamer.po rename to applications/luci-app-ustreamer/po/el/ustreamer.po diff --git a/applications/luci-app-mjpg-streamer/po/es/mjpg-streamer.po b/applications/luci-app-ustreamer/po/es/ustreamer.po similarity index 100% rename from applications/luci-app-mjpg-streamer/po/es/mjpg-streamer.po rename to applications/luci-app-ustreamer/po/es/ustreamer.po diff --git a/applications/luci-app-mjpg-streamer/po/et/mjpg-streamer.po b/applications/luci-app-ustreamer/po/et/ustreamer.po similarity index 100% rename from applications/luci-app-mjpg-streamer/po/et/mjpg-streamer.po rename to applications/luci-app-ustreamer/po/et/ustreamer.po diff --git a/applications/luci-app-mjpg-streamer/po/fi/mjpg-streamer.po b/applications/luci-app-ustreamer/po/fi/ustreamer.po similarity index 100% rename from applications/luci-app-mjpg-streamer/po/fi/mjpg-streamer.po rename to applications/luci-app-ustreamer/po/fi/ustreamer.po diff --git a/applications/luci-app-mjpg-streamer/po/fr/mjpg-streamer.po b/applications/luci-app-ustreamer/po/fr/ustreamer.po similarity index 100% rename from applications/luci-app-mjpg-streamer/po/fr/mjpg-streamer.po rename to applications/luci-app-ustreamer/po/fr/ustreamer.po diff --git a/applications/luci-app-mjpg-streamer/po/ga/mjpg-streamer.po b/applications/luci-app-ustreamer/po/ga/ustreamer.po similarity index 100% rename from applications/luci-app-mjpg-streamer/po/ga/mjpg-streamer.po rename to applications/luci-app-ustreamer/po/ga/ustreamer.po diff --git a/applications/luci-app-mjpg-streamer/po/he/mjpg-streamer.po b/applications/luci-app-ustreamer/po/he/ustreamer.po similarity index 100% rename from applications/luci-app-mjpg-streamer/po/he/mjpg-streamer.po rename to applications/luci-app-ustreamer/po/he/ustreamer.po diff --git a/applications/luci-app-mjpg-streamer/po/hi/mjpg-streamer.po b/applications/luci-app-ustreamer/po/hi/ustreamer.po similarity index 100% rename from applications/luci-app-mjpg-streamer/po/hi/mjpg-streamer.po rename to applications/luci-app-ustreamer/po/hi/ustreamer.po diff --git a/applications/luci-app-mjpg-streamer/po/hu/mjpg-streamer.po b/applications/luci-app-ustreamer/po/hu/ustreamer.po similarity index 100% rename from applications/luci-app-mjpg-streamer/po/hu/mjpg-streamer.po rename to applications/luci-app-ustreamer/po/hu/ustreamer.po diff --git a/applications/luci-app-mjpg-streamer/po/it/mjpg-streamer.po b/applications/luci-app-ustreamer/po/it/ustreamer.po similarity index 100% rename from applications/luci-app-mjpg-streamer/po/it/mjpg-streamer.po rename to applications/luci-app-ustreamer/po/it/ustreamer.po diff --git a/applications/luci-app-mjpg-streamer/po/ja/mjpg-streamer.po b/applications/luci-app-ustreamer/po/ja/ustreamer.po similarity index 100% rename from applications/luci-app-mjpg-streamer/po/ja/mjpg-streamer.po rename to applications/luci-app-ustreamer/po/ja/ustreamer.po diff --git a/applications/luci-app-mjpg-streamer/po/ko/mjpg-streamer.po b/applications/luci-app-ustreamer/po/ko/ustreamer.po similarity index 100% rename from applications/luci-app-mjpg-streamer/po/ko/mjpg-streamer.po rename to applications/luci-app-ustreamer/po/ko/ustreamer.po diff --git a/applications/luci-app-mjpg-streamer/po/lt/mjpg-streamer.po b/applications/luci-app-ustreamer/po/lt/ustreamer.po similarity index 100% rename from applications/luci-app-mjpg-streamer/po/lt/mjpg-streamer.po rename to applications/luci-app-ustreamer/po/lt/ustreamer.po diff --git a/applications/luci-app-mjpg-streamer/po/mr/mjpg-streamer.po b/applications/luci-app-ustreamer/po/mr/ustreamer.po similarity index 100% rename from applications/luci-app-mjpg-streamer/po/mr/mjpg-streamer.po rename to applications/luci-app-ustreamer/po/mr/ustreamer.po diff --git a/applications/luci-app-mjpg-streamer/po/ms/mjpg-streamer.po b/applications/luci-app-ustreamer/po/ms/ustreamer.po similarity index 100% rename from applications/luci-app-mjpg-streamer/po/ms/mjpg-streamer.po rename to applications/luci-app-ustreamer/po/ms/ustreamer.po diff --git a/applications/luci-app-mjpg-streamer/po/nb_NO/mjpg-streamer.po b/applications/luci-app-ustreamer/po/nb_NO/ustreamer.po similarity index 100% rename from applications/luci-app-mjpg-streamer/po/nb_NO/mjpg-streamer.po rename to applications/luci-app-ustreamer/po/nb_NO/ustreamer.po diff --git a/applications/luci-app-mjpg-streamer/po/nl/mjpg-streamer.po b/applications/luci-app-ustreamer/po/nl/ustreamer.po similarity index 100% rename from applications/luci-app-mjpg-streamer/po/nl/mjpg-streamer.po rename to applications/luci-app-ustreamer/po/nl/ustreamer.po diff --git a/applications/luci-app-mjpg-streamer/po/pl/mjpg-streamer.po b/applications/luci-app-ustreamer/po/pl/ustreamer.po similarity index 100% rename from applications/luci-app-mjpg-streamer/po/pl/mjpg-streamer.po rename to applications/luci-app-ustreamer/po/pl/ustreamer.po diff --git a/applications/luci-app-mjpg-streamer/po/pt/mjpg-streamer.po b/applications/luci-app-ustreamer/po/pt/ustreamer.po similarity index 100% rename from applications/luci-app-mjpg-streamer/po/pt/mjpg-streamer.po rename to applications/luci-app-ustreamer/po/pt/ustreamer.po diff --git a/applications/luci-app-mjpg-streamer/po/pt_BR/mjpg-streamer.po b/applications/luci-app-ustreamer/po/pt_BR/ustreamer.po similarity index 100% rename from applications/luci-app-mjpg-streamer/po/pt_BR/mjpg-streamer.po rename to applications/luci-app-ustreamer/po/pt_BR/ustreamer.po diff --git a/applications/luci-app-mjpg-streamer/po/ro/mjpg-streamer.po b/applications/luci-app-ustreamer/po/ro/ustreamer.po similarity index 100% rename from applications/luci-app-mjpg-streamer/po/ro/mjpg-streamer.po rename to applications/luci-app-ustreamer/po/ro/ustreamer.po diff --git a/applications/luci-app-mjpg-streamer/po/ru/mjpg-streamer.po b/applications/luci-app-ustreamer/po/ru/ustreamer.po similarity index 100% rename from applications/luci-app-mjpg-streamer/po/ru/mjpg-streamer.po rename to applications/luci-app-ustreamer/po/ru/ustreamer.po diff --git a/applications/luci-app-mjpg-streamer/po/sk/mjpg-streamer.po b/applications/luci-app-ustreamer/po/sk/ustreamer.po similarity index 100% rename from applications/luci-app-mjpg-streamer/po/sk/mjpg-streamer.po rename to applications/luci-app-ustreamer/po/sk/ustreamer.po diff --git a/applications/luci-app-mjpg-streamer/po/sv/mjpg-streamer.po b/applications/luci-app-ustreamer/po/sv/ustreamer.po similarity index 100% rename from applications/luci-app-mjpg-streamer/po/sv/mjpg-streamer.po rename to applications/luci-app-ustreamer/po/sv/ustreamer.po diff --git a/applications/luci-app-mjpg-streamer/po/ta/mjpg-streamer.po b/applications/luci-app-ustreamer/po/ta/ustreamer.po similarity index 100% rename from applications/luci-app-mjpg-streamer/po/ta/mjpg-streamer.po rename to applications/luci-app-ustreamer/po/ta/ustreamer.po diff --git a/applications/luci-app-mjpg-streamer/po/templates/mjpg-streamer.pot b/applications/luci-app-ustreamer/po/templates/ustreamer.pot similarity index 100% rename from applications/luci-app-mjpg-streamer/po/templates/mjpg-streamer.pot rename to applications/luci-app-ustreamer/po/templates/ustreamer.pot diff --git a/applications/luci-app-mjpg-streamer/po/tr/mjpg-streamer.po b/applications/luci-app-ustreamer/po/tr/ustreamer.po similarity index 100% rename from applications/luci-app-mjpg-streamer/po/tr/mjpg-streamer.po rename to applications/luci-app-ustreamer/po/tr/ustreamer.po diff --git a/applications/luci-app-mjpg-streamer/po/uk/mjpg-streamer.po b/applications/luci-app-ustreamer/po/uk/ustreamer.po similarity index 100% rename from applications/luci-app-mjpg-streamer/po/uk/mjpg-streamer.po rename to applications/luci-app-ustreamer/po/uk/ustreamer.po diff --git a/applications/luci-app-mjpg-streamer/po/vi/mjpg-streamer.po b/applications/luci-app-ustreamer/po/vi/ustreamer.po similarity index 100% rename from applications/luci-app-mjpg-streamer/po/vi/mjpg-streamer.po rename to applications/luci-app-ustreamer/po/vi/ustreamer.po diff --git a/applications/luci-app-mjpg-streamer/po/zh_Hans/mjpg-streamer.po b/applications/luci-app-ustreamer/po/zh_Hans/ustreamer.po similarity index 100% rename from applications/luci-app-mjpg-streamer/po/zh_Hans/mjpg-streamer.po rename to applications/luci-app-ustreamer/po/zh_Hans/ustreamer.po diff --git a/applications/luci-app-mjpg-streamer/po/zh_Hant/mjpg-streamer.po b/applications/luci-app-ustreamer/po/zh_Hant/ustreamer.po similarity index 100% rename from applications/luci-app-mjpg-streamer/po/zh_Hant/mjpg-streamer.po rename to applications/luci-app-ustreamer/po/zh_Hant/ustreamer.po diff --git a/applications/luci-app-ustreamer/root/usr/share/luci/menu.d/luci-app-ustreamer.json b/applications/luci-app-ustreamer/root/usr/share/luci/menu.d/luci-app-ustreamer.json new file mode 100644 index 0000000000..ad68670f68 --- /dev/null +++ b/applications/luci-app-ustreamer/root/usr/share/luci/menu.d/luci-app-ustreamer.json @@ -0,0 +1,17 @@ +{ + "admin/services/ustreamer": { + "title": "ustreamer", + "action": { + "type": "view", + "path": "ustreamer/ustreamer" + }, + "depends": { + "acl": [ + "luci-app-ustreamer" + ], + "uci": { + "ustreamer": true + } + } + } +} diff --git a/applications/luci-app-ustreamer/root/usr/share/rpcd/acl.d/luci-app-ustreamer.json b/applications/luci-app-ustreamer/root/usr/share/rpcd/acl.d/luci-app-ustreamer.json new file mode 100644 index 0000000000..a62f4aff04 --- /dev/null +++ b/applications/luci-app-ustreamer/root/usr/share/rpcd/acl.d/luci-app-ustreamer.json @@ -0,0 +1,18 @@ +{ + "luci-app-ustreamer": { + "description": "Grant UCI access for luci-app-ustreamer", + "read": { + "file": [ + "/dev/*" + ], + "uci": [ + "ustreamer" + ] + }, + "write": { + "uci": [ + "ustreamer" + ] + } + } +} \ No newline at end of file