From 7058d0f5063dc9b065248d0a906cf874788caecf Mon Sep 17 00:00:00 2001 From: Evgeny Zinoviev Date: Wed, 13 Sep 2023 09:34:49 +0300 Subject: save --- web/kbn_assets/app.js | 349 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 349 insertions(+) create mode 100644 web/kbn_assets/app.js (limited to 'web/kbn_assets/app.js') diff --git a/web/kbn_assets/app.js b/web/kbn_assets/app.js new file mode 100644 index 0000000..c187f89 --- /dev/null +++ b/web/kbn_assets/app.js @@ -0,0 +1,349 @@ +(function() { +var RE_WHITESPACE = /[\t\r\n\f]/g + +window.ajax = { + get: function(url, data) { + if (typeof data == 'object') { + var index = 0; + for (var key in data) { + var val = data[key]; + url += index === 0 && url.indexOf('?') === -1 ? '?' : '&'; + url += encodeURIComponent(key) + '=' + encodeURIComponent(val); + } + } + return this.raw(url); + }, + + post: function(url, body) { + var opts = { + method: 'POST' + }; + if (body) + opts.body = body; + return this.raw(url, opts); + }, + + raw: function(url, options) { + if (!options) + options = {} + + return fetch(url, Object.assign({ + headers: { + 'X-Requested-With': 'XMLHttpRequest', + } + }, options)) + .then(resp => { + return resp.json() + }) + } +}; + +window.extend = function(a, b) { + return Object.assign(a, b); +} + +window.ge = function(id) { + return document.getElementById(id); +} + +var ua = navigator.userAgent.toLowerCase(); +window.browserInfo = { + version: (ua.match(/.+(?:me|ox|on|rv|it|ra|ie)[\/: ]([\d.]+)/) || [0,'0'])[1], + //opera: /opera/i.test(ua), + msie: (/msie/i.test(ua) && !/opera/i.test(ua)) || /trident/i.test(ua), + mozilla: /firefox/i.test(ua), + android: /android/i.test(ua), + mac: /mac/i.test(ua), + samsungBrowser: /samsungbrowser/i.test(ua), + chrome: /chrome/i.test(ua), + safari: /safari/i.test(ua), + mobile: /iphone|ipod|ipad|opera mini|opera mobi|iemobile|android/i.test(ua), + operaMini: /opera mini/i.test(ua), + ios: /iphone|ipod|ipad|watchos/i.test(ua) || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1), +}; + +window.isTouchDevice = function() { + return 'ontouchstart' in window || navigator.msMaxTouchPoints; +} + +window.hasClass = function(el, name) { + if (!el) + throw new Error('hasClass: invalid element') + + if (el.nodeType !== 1) + throw new Error('hasClass: expected nodeType is 1, got' + el.nodeType) + + if (window.DOMTokenList && el.classList instanceof DOMTokenList) { + return el.classList.contains(name) + } else { + return (" " + el.className + " ").replace(RE_WHITESPACE, " ").indexOf(" " + name + " ") >= 0 + } +} + +window.addClass = function(el, name) { + if (!hasClass(el, name)) { + el.className = (el.className ? el.className + ' ' : '') + name; + return true + } + return false +} + +window.Cameras = { + hlsOptions: null, + h265webjsOptions: null, + host: null, + proto: null, + hlsDebugVideoEvents: false, + + getUrl: function(name) { + return this.proto + '://' + this.host + '/ipcam/' + name + '/live.m3u8'; + }, + + setupHls: function(video, name, useHls) { + var src = this.getUrl(name); + + // hls.js is not supported on platforms that do not have Media Source Extensions (MSE) enabled. + + // When the browser has built-in HLS support (check using `canPlayType`), we can provide an HLS manifest (i.e. .m3u8 URL) directly to the video element through the `src` property. + // This is using the built-in support of the plain video element, without using hls.js. + + if (useHls) { + var config = this.hlsOptions; + config.xhrSetup = function (xhr,url) { + xhr.withCredentials = true; + }; + + var hls = new Hls(config); + hls.loadSource(src); + hls.attachMedia(video); + hls.on(Hls.Events.MEDIA_ATTACHED, function () { + video.muted = true; + video.play(); + }); + } else { + console.warn('hls.js is not supported, trying the native way...') + + video.autoplay = true; + video.muted = true; + video.playsInline = true; + video.autoplay = true; + if (window.browserInfo.ios) + video.setAttribute('controls', 'controls'); + + video.src = src; + + var events = ['canplay']; + if (this.hlsDebugVideoEvents) + events.push('canplay', 'canplaythrough', 'durationchange', 'ended', 'loadeddata', 'loadedmetadata', 'pause', 'play', 'playing', 'progress', 'seeked', 'seeking', 'stalled', 'suspend', 'timeupdate', 'waiting'); + + for (var i = 0; i < events.length; i++) { + var evt = events[i]; + (function(evt, video, name) { + video.addEventListener(evt, function(e) { + if (this.debugVideoEvents) + console.log(name + ': ' + evt, e); + + if (!window.browserInfo.ios && ['canplay', 'loadedmetadata'].includes(evt)) + video.play(); + }) + })(evt, video, name); + } + } + }, + + setupH265WebJS: function(videoContainer, name) { + var containerHeightFixed = false; + var config = { + player: 'video-'+name, + width: videoContainer.offsetWidth, + height: parseInt(videoContainer.offsetWidth * 9 / 16, 10), + accurateSeek: true, + token: this.h265webjsOptions.token, + extInfo: { + moovStartFlag: true, + readyShow: true, + autoPlay: true, + rawFps: 15, + } + }; + + var mediaInfo; + var player = window.new265webjs(this.getUrl(name), config); + + player.onSeekStart = (pts) => { + console.log(name + ": onSeekStart:" + pts); + }; + + player.onSeekFinish = () => { + console.log(name + ": onSeekFinish"); + }; + + player.onPlayFinish = () => { + console.log(name + ": onPlayFinish"); + }; + + player.onRender = (width, height, imageBufferY, imageBufferB, imageBufferR) => { + // console.log(name + ": onRender"); + if (!containerHeightFixed) { + var ratio = height / width; + videoContainer.style.width = parseInt(videoContainer.offsetWidth * ratio, 10)+'px'; + containerHeightFixed = true; + } + }; + + player.onOpenFullScreen = () => { + console.log(name + ": onOpenFullScreen"); + }; + + player.onCloseFullScreen = () => { + console.log(name + ": onCloseFullScreen"); + }; + + player.onSeekFinish = () => { + console.log(name + ": onSeekFinish"); + }; + + player.onLoadCache = () => { + console.log(name + ": onLoadCache"); + }; + + player.onLoadCacheFinshed = () => { + console.log(name + ": onLoadCacheFinshed"); + }; + + player.onReadyShowDone = () => { + // console.log(name + ": onReadyShowDone:【You can play now】"); + player.play() + }; + + player.onLoadFinish = () => { + console.log(name + ": onLoadFinish"); + + player.setVoice(1.0); + + mediaInfo = player.mediaInfo(); + console.log("onLoadFinish mediaInfo===========>", mediaInfo); + + var codecName = "h265"; + if (mediaInfo.meta.isHEVC === false) { + console.log(name + ": onLoadFinish is Not HEVC/H.265"); + codecName = "h264"; + } else { + console.log(name + ": onLoadFinish is HEVC/H.265"); + } + + console.log(name + ": onLoadFinish media Codec:" + codecName); + console.log(name + ": onLoadFinish media FPS:" + mediaInfo.meta.fps); + console.log(name + ": onLoadFinish media size:" + mediaInfo.meta.size.width + "x" + mediaInfo.meta.size.height); + + if (mediaInfo.meta.audioNone) { + console.log(name + ": onLoadFinish media no Audio"); + } else { + console.log(name + ": onLoadFinish media sampleRate:" + mediaInfo.meta.sampleRate); + } + + if (mediaInfo.videoType == "vod") { + console.log(name + ": onLoadFinish media is VOD"); + console.log(name + ": onLoadFinish media dur:" + Math.ceil(mediaInfo.meta.durationMs) / 1000.0); + } else { + console.log(name + ": onLoadFinish media is LIVE"); + } + }; + + player.onCacheProcess = (cPts) => { + console.log(name + ": onCacheProcess:" + cPts); + }; + + player.onPlayTime = (videoPTS) => { + if (mediaInfo.videoType == "vod") { + console.log(name + ": onPlayTime:" + videoPTS); + } else { + // LIVE + } + }; + + player.do(); + // console.log('setupH265WebJS: video: ', video.offsetWidth, video.offsetHeight) + }, + + init: function(opts) { + this.proto = opts.proto; + this.host = opts.host; + this.hlsOptions = opts.hlsConfig; + this.h265webjsOptions = opts.h265webjsConfig; + + var useHls; + if (opts.hlsConfig !== undefined) { + useHls = Hls.isSupported(); + if (!useHls && !this.hasFallbackSupport()) { + alert('Neither HLS nor vnd.apple.mpegurl is not supported by your browser.'); + return; + } + } + + for (var camId in opts.camsByType) { + var name = camId + ''; + if (opts.isLow) + name += '-low'; + var type = opts.camsByType[camId]; + + switch (type) { + case 'h265': + var videoContainer = document.createElement('div'); + videoContainer.setAttribute('id', 'video-'+name); + videoContainer.setAttribute('style', 'position: relative'); // a hack to fix an error in h265webjs lib + videoContainer.className = 'video-container'; + document.getElementById('videos').appendChild(videoContainer); + try { + this.setupH265WebJS(videoContainer, name); + } catch (e) { + console.error('cam'+camId+': error', e) + } + break; + + case 'h264': + var video = document.createElement('video'); + video.setAttribute('id', 'video-'+name); + document.getElementById('videos').appendChild(video); + this.setupHls(video, name, useHls); + break; + } + } + }, + + hasFallbackSupport: function() { + var video = document.createElement('video'); + return video.canPlayType('application/vnd.apple.mpegurl'); + }, +}; +})(); + + +var ModemStatus = { + _modems: [], + + init: function(modems) { + for (var i = 0; i < modems.length; i++) { + var modem = modems[i]; + this._modems.push(new ModemStatusUpdater(modem)); + } + } +}; + +function ModemStatusUpdater(id) { + this.id = id; + this.elem = ge('modem_data_'+id); + this.fetch(); +} +extend(ModemStatusUpdater.prototype, { + fetch: function() { + ajax.get('/modem/get.ajax', { + id: this.id + }).then(({response}) => { + var {html} = response; + this.elem.innerHTML = html; + + // TODO enqueue rerender + }); + }, +}); \ No newline at end of file -- cgit v1.2.3