319 lines
10 KiB
JavaScript
319 lines
10 KiB
JavaScript
(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');
|
|
},
|
|
};
|
|
})(); |