代码拉取完成,页面将自动刷新
<?php include ("./link/session.php") ?>
<!doctype html>
<html lang="uft-8">
<head>
<?php include ("./public/head.inc") ?>
<link href="assets/plugins/nouislider/css/nouislider.min.css" rel="stylesheet">
</head>
<body>
<?php include ("./public/menu.inc") ?>
<div data-simplebar>
<main class="page-content v4l2cam" id="app" v-cloak>
<div class="row">
<div class="col-lg-12" v-for="item in usbConfs">
<div class="card">
<div class="card-body">
<div class="row">
<div class="col-lg-7 lp-equal-height-container">
<div class="mb-0 lp-equal-height-item">
<div class="card h-100">
<div class="card-body">
<div class="row h-100">
<div class="col-lg-6 h-100" style="display:flex;align-items:center;">
<div class="row w-100">
<div class="col-lg-12">
<div class="row">
<div class="col-lg-3 lp-align-center">
<label>
<cn>启用</cn>
<en>Enable</en>
</label>
</div>
<div class="col-lg-9">
<bs-switch v-model="item.enable" :size="'normal'"></bs-switch>
</div>
</div>
<div class="row mt-3">
<div class="col-lg-3 lp-align-center">
<label>
<cn>频道名称</cn>
<en>Channel name</en>
</label>
</div>
<div class="col-lg-9">
<input type="text" class="form-control" v-model.trim.lazy="item.name">
</div>
</div>
<div class="row mt-3">
<div class="col-lg-3 lp-align-center">
<label>
<cn>厂商名称</cn>
<en>Camera name</en>
</label>
</div>
<div class="col-lg-9">
<input v-if="getCamInfo(item.id).size" type="text" class="form-control" disabled v-model.trim.lazy="getCamInfo(item.id).name">
<input v-else type="text" class="form-control" disabled value="None">
</div>
</div>
<div class="row mt-3">
<div class="col-lg-3 lp-align-center">
<label>
<cn>采集分辨率</cn>
<en>Video size</en>
</label>
</div>
<div class="col-lg-9">
<multiple-select v-if="getCamInfo(item.id).size" v-model:value1="item.capture.width" v-model:value2="item.capture.height" split="," :add-val="false">
<option v-for="opt in getCamInfo(item.id).size || []" :value="opt.width+','+opt.height">{{opt.width + 'x' + opt.height}}</option>
</multiple-select>
<select v-else class="form-select">
<option>None</option>
</select>
</div>
</div>
<div class="row mt-3">
<div class="col-lg-3 lp-align-center">
<label>
<cn>采集帧率</cn>
<en>Framerate</en>
</label>
</div>
<div class="col-lg-9">
<select v-if="handleCurFramerate(item.id,item.capture).length > 0" class="form-select" v-model="item.capture.framerate">
<option v-for="opt in handleCurFramerate(item.id,item.capture)" :value="opt">{{opt}}</option>
</select>
<select v-else class="form-select">
<option>None</option>
</select>
</div>
</div>
<div class="row mt-3">
<div class="col-lg-12 text-center">
<button type="button" class="btn btn-primary border-3 px-4 me-1" @click="updateDefaultConf">
<cn>保存</cn>
<en>Save</en>
</button>
<button v-if="getPtzArg(item.id).type === 'insta360'" type="button" class="btn btn-primary border-3 px-4 me-1" @click="onAdvSetting(item.id)">
<cn>高级</cn>
<en>Advanced</en>
</button>
<button type="button" class="btn btn-primary border-3 px-2 me-1" @click="onDisplayHdmi(item.id)">
<div v-if="Object.keys(hardwareConf).length > 0">
<div v-if="hardwareConf.fac === 'VGA1'">
<cn>VGA输出</cn>
</div>
<div v-else-if="hardwareConf.fac === 'ENCS1'">
<cn>SDI输出</cn>
</div>
<div v-else>
<cn>HDMI输出</cn>
</div>
</div>
<en>Display</en>
</button>
</div>
</div>
</div>
</div>
</div>
<div class="col-lg-6 h-100 lp-align-center">
<div v-if="window && defaultConf.length > 0" class="col-lg-12 my-2">
<div v-if="defaultConf[item.id].enable || defaultConf[item.id].enable2">
<div v-if="item.encv.codec === 'close' || (!item.stream.rtmp && !item.stream.webrtc)" class="col-lg-12 mt-2">
<div class="card-img-content">
<div class="card-img-background"></div>
<img :src="chnImgUrl" class="card-img preview" :style="handleAutoStyle(item.id)">
<img v-once src="assets/img/nosignal.jpg" class="card-img" style="visibility: hidden">
</div>
</div>
<div v-else class="col-lg-12 mt-2">
<h5-player :url="`http://${window.location.host}/${item.stream.rtmp ? 'flv' : 'webrtc'}?app=live&stream=${item.stream.suffix}`" :codec="item.encv.codec" :audio="false" :protocol="item.stream.rtmp ? 'rtmp' : 'webrtc'" :buffer="200"></h5-player>
</div>
</div>
<div v-else class="card-img-content">
<div class="lp-align-center v4l2-tips" :style="handleAutoStyle(item.id)">
<label style="font-size: 3rem">
<cn style="letter-spacing: 5px">通道未开启</cn>
<en>Not Enable</en>
</label>
</div>
<img v-once src="assets/img/nosignal.jpg" class="card-img" style="visibility: hidden">
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-lg-5 lp-equal-height-container">
<div class="mb-0 lp-equal-height-item">
<div class="card h-100">
<div class="card-body d-flex align-items-center">
<ptz-direct :arrow-class="'arrow-direct'" :home-class="'home-direct'" :gop="5" :sticks="['up','down','left','right','home']"
:zoom-val="getPtzArg(item.id).ptz.z" :zoom-min="100" :zoom-max="400"
@ptz-move="type => handlePtzMove(type,item.id)" @zoom-change="zoomVal => handleZoomChange(zoomVal,item.id)"
@call-preset="presetVal => handleCallPreset(presetVal,item.id)" @set-preset="presetVal => handleSetPreset(presetVal,item.id)"></ptz-direct>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<adv-modal :had-header="true" :modal-title="'高级设置&Advanced'" :had-footer="false" :modal-show="advanced.showAdvModal"
:confirm-btn-name="'关闭&Close'" @modal-visible="onAdvModalVisible">
<div class="row h-100 d-flex align-items-center">
<div class="col-lg-10 offset-lg-1">
<div class="row">
<div class="col-lg-4 text-center">
<cn>自动跟踪</cn>
<en>auto track</en>
</div>
<div class="col-lg-4 text-center">
<cn>白板跟踪</cn>
<en>whiteboard</en>
</div>
<div class="col-lg-4 text-center">
<cn>俯拍模式</cn>
<en>Overhead</en>
</div>
</div>
<hr>
<div class="row">
<div class="col-lg-4 lp-align-center">
<bs-switch v-model="advanced.options.tracking" @switch-change="onUsbCamOptionChange"></bs-switch>
</div>
<div class="col-lg-4 lp-align-center">
<bs-switch v-model="advanced.options.whiteboard" @switch-change="onUsbCamOptionChange"></bs-switch>
</div>
<div class="col-lg-4 lp-align-center">
<bs-switch v-model="advanced.options.overhead" @switch-change="onUsbCamOptionChange"></bs-switch>
</div>
</div>
</div>
<div class="col-lg-10 offset-lg-1 mt-2 mb-4">
<div class="row">
<div class="col-lg-4 text-center">
<cn>桌面模式</cn>
<en>desktop mode</en>
</div>
<div class="col-lg-4 text-center">
HDR
</div>
<div class="col-lg-4 text-center">
<cn>镜像</cn>
<en>mirror</en>
</div>
</div>
<hr>
<div class="row">
<div class="col-lg-4 lp-align-center">
<bs-switch v-model="advanced.options.deskview" @switch-change="onUsbCamOptionChange"></bs-switch>
</div>
<div class="col-lg-4 lp-align-center">
<bs-switch v-model="advanced.options.hdr" @switch-change="onUsbCamOptionChange"></bs-switch>
</div>
<div class="col-lg-4 lp-align-center">
<bs-switch v-model="advanced.options.mirror" @switch-change="onUsbCamOptionChange"></bs-switch>
</div>
</div>
</div>
</div>
</adv-modal>
</main>
</div>
<?php include ("./public/foot.inc") ?>
<script type="module">
import {rpc, msleep, isEmpty, clearReactiveArray} from "./assets/js/lp.utils.js?hash=e82520f08";
import { useDefaultConf,useHardwareConf,usePresetConf } from "./assets/js/vue.hooks.js?hash=89523e901";
import { ignoreCustomElementPlugin,filterKeywordPlugin,languageOptionDirective,bootstrapSwitchComponent,h5PlayerComponent,ptzDirectComponent,multipleSelectComponent,customModalComponent } from "./assets/js/vue.helper.js?hash=68eefc36f"
import vue from "./assets/js/vue.build.js?hash=879ea7dbc";
const {createApp,ref,reactive,watchEffect,computed,onMounted,nextTick} = vue;
const app = createApp({
directives:{
"language-option": languageOptionDirective
},
components:{
"h5-player": h5PlayerComponent,
"bs-switch" : bootstrapSwitchComponent,
"ptz-direct": ptzDirectComponent,
"multiple-select": multipleSelectComponent,
"adv-modal": customModalComponent
},
setup(props,context) {
const { defaultConf,updateDefaultConf } = useDefaultConf();
const { hardwareConf } = useHardwareConf();
const { presetConf,presetLoad,updatePresetConf } = usePresetConf();
const state = {
camInfos: reactive([]),
usbConfs: reactive([]),
ptzArgs: reactive([]),
inputState: reactive([]),
stepP: 0,
stepT: 0,
timerId: 0,
advanced: reactive({
chnId: -1,
showAdvModal: false,
options:{tracking: false, whiteboard: false, overhead: false, deskview: false, hdr: false, mirror: false}
})
}
const DeviceType = {
INSTA360: 'insta360',
DJPOCKET: 'djpocket',
OTHER: 'other'
};
const unwatch = watchEffect(()=>{
if(defaultConf.length > 0 && presetLoad.value && state.camInfos.length > 0 && state.ptzArgs.length > 0 && state.inputState.length > 0) {
for(let i=0;i<defaultConf.length;i++) {
let chnConf = defaultConf[i];
if(chnConf.type === "usb") {
const input = state.inputState.find(item => item.chnId === chnConf.id);
if(input && input.avalible) {
chnConf.capture = {
width : input.width,
height: input.height,
framerate: input.framerate
}
}
const preset = presetConf.find(item => item.id === chnConf.id);
if(!preset) {
const posAry = Array.from({ length: 9 }, () => [0, 0, 100]);
presetConf.push({
id: chnConf.id,
pos: posAry
})
}
state.usbConfs.push(chnConf);
}
}
unwatch();
}
})
const handleUsbCamInfos = () => {
rpc("enc.getUvcInfo").then(data => {
state.camInfos.splice(0, state.camInfos.length, ...data);
state.ptzArgs.splice(0);
data.forEach(item => {
const ptzArg = reactive({
id: item.id,
type: item.info.name?.includes("Insta360 Link") ? DeviceType.INSTA360
: item.info.name?.includes("DJIPocket") ? DeviceType.DJPOCKET
: DeviceType.OTHER,
ptz: { p: 0, t: 0, z: 0 },
options: {}
});
const getPtz = () => rpc("enc.uvcInvoke", [item.id, { func: "ptz_get" }])
.then(ret => ret.data && Object.assign(ptzArg.ptz, {
p: ret.data[0],
t: ret.data[1],
z: ret.data[2]
}));
ptzArg.type === "insta360"
? Promise.all([
getPtz(),
rpc("enc.uvcInvoke", [item.id, { func: "insta360_get" }])
.then(ret => Object.assign(ptzArg.options, ret))
])
: getPtz();
state.ptzArgs.push(ptzArg);
});
})
}
const updateChnImage = () => {
rpc("enc.snap").then(() => {
setTimeout(() => {
document.querySelectorAll("img.preview").forEach((img,index) => {
const chnConf = state.usbConfs[index];
if(chnConf.enable)
img.src = "snap/snap" + chnConf.id + ".jpg" + "?rnd=" + Math.floor(Date.now());
else
img.src = "assets/img/nosignal.jpg";
})
},100);
})
setTimeout(updateChnImage,500);
}
const onAdvSetting = chnId => {
const ptzArg = state.ptzArgs.find(item => item.id === chnId) || {};
if(!isEmpty(ptzArg)) {
state.advanced.options = ptzArg.options;
}
state.advanced.chnId = chnId;
state.advanced.showAdvModal = true;
}
const getCamInfo = chnId => {
return state.camInfos.find(item => item.id === chnId)?.info || {};
}
const getPtzArg = chnId => {
return state.ptzArgs.find(item => item.id === chnId) || {};
}
const handleCurFramerate = (chnId, { width, height }) =>
state.camInfos.find(c => c.id === chnId)?.info?.size
?.find(s => s.width === width && s.height === height)
?.framerate || [];
const handleInputState = () => {
rpc("enc.getInputState").then(inputlist => {
clearReactiveArray(state.inputState);
state.inputState.push(...inputlist);
setTimeout(handleInputState,1000);
})
}
const delayPtz = async (chnId,param) => {
const ptzAbsFunctions = ['ptz_abs_z','ptz_abs_p', 'ptz_abs_t'];
for (const func of ptzAbsFunctions) {
let value = 0;
if(func === 'ptz_abs_p')
value = param.p;
if(func === 'ptz_abs_t')
value = param.t;
if(func === 'ptz_abs_z')
value = param.z;
await rpc("enc.uvcInvoke", [chnId, { func, arg: value }]);
let previousResult = null;
let matches = 0;
let loopCount = 0;
while (matches < 5 && loopCount < 50) {
await msleep(50);
const result = await rpc("enc.uvcInvoke",[chnId,{"func":"ptz_get"}]);
if (previousResult === null || JSON.stringify(result) === JSON.stringify(previousResult))
matches++;
else
matches = 0;
previousResult = result;
loopCount++;
}
}
}
const updatePtz = (type,chnId) => {
const ptzArg = state.ptzArgs.find(item => item.id === chnId);
if(type === "left" || type === "right") {
let ptz_rlt_p = 0;
if(ptzArg.type === DeviceType.DJPOCKET)
ptz_rlt_p = state.stepP*3600*3;
else
ptz_rlt_p = state.stepP*3600;
rpc("enc.uvcInvoke",[chnId,{func:"ptz_rlt_p",arg:ptz_rlt_p}]);
}
if(type === "up" || type === "down") {
let ptz_rlt_t = 0;
if(ptzArg.type === DeviceType.DJPOCKET)
ptz_rlt_t = state.stepT*3600*3;
else
ptz_rlt_t = state.stepT*3600;
rpc("enc.uvcInvoke",[chnId,{func:"ptz_rlt_t",arg:ptz_rlt_t}]);
}
}
const ptzMoveStart = (type,chnId) => {
if(state.timerId === 0)
state.timerId=setInterval(() => updatePtz(type,chnId),50);
}
const ptzMoveStop = () => {
if(state.timerId !== 0) {
clearInterval(state.timerId);
state.timerId = 0;
}
state.stepP = 0;
state.stepT = 0;
}
const handlePtzMove = (type,chnId) => {
if(type === "up") {
ptzMoveStart(type,chnId);
state.stepT = 1;
}
if(type === "right") {
ptzMoveStart(type,chnId);
state.stepP = 1;
}
if(type === "down") {
ptzMoveStart(type,chnId);
state.stepT = -1;
}
if(type === "left") {
ptzMoveStart(type,chnId);
state.stepP = -1;
}
if(type === "home") {
ptzMoveStop();
let ptzArg = state.ptzArgs.find(item => item.id === chnId);
ptzArg.ptz = {p:0,t:0,z:0}
if(ptzArg.type !== DeviceType.DJPOCKET) {
rpc("enc.uvcInvoke",[chnId,{func:"ptz_abs_p",arg:0}]);
rpc("enc.uvcInvoke",[chnId,{func:"ptz_abs_t",arg:0}]);
rpc("enc.uvcInvoke",[chnId,{func:"ptz_abs_z",arg:0}]);
} else {
delayPtz(chnId,ptzArg.ptz);
}
}
if(type === "move-stop")
ptzMoveStop();
}
const handleZoomChange = (zoomVal,chnId) => {
rpc("enc.uvcInvoke",[chnId,{func:"ptz_abs_z",arg:zoomVal}]);
}
const handleCallPreset = (presetVal,chnId) => {
ptzMoveStop();
presetVal = Number(presetVal);
const preset = presetConf.find(item => item.id === chnId);
const pos = preset.pos[presetVal-1];
let ptzArg = state.ptzArgs.find(item => item.id === chnId);
ptzArg.ptz = {p:pos[0],t:pos[1],z:pos[2]}
if(ptzArg.type !== DeviceType.DJPOCKET) {
rpc("enc.uvcInvoke",[chnId,{func:"ptz_abs_p",arg:ptzArg.ptz.p}]);
rpc("enc.uvcInvoke",[chnId,{func:"ptz_abs_t",arg:ptzArg.ptz.t}]);
rpc("enc.uvcInvoke",[chnId,{func:"ptz_abs_z",arg:ptzArg.ptz.z}]);
} else {
delayPtz(chnId,ptzArg.ptz);
delayPtz(chnId,ptzArg.ptz);
delayPtz(chnId,ptzArg.ptz);
}
}
const handleSetPreset = (presetVal,chnId) => {
presetVal = Number(presetVal);
rpc("enc.uvcInvoke",[chnId,{"func":"ptz_get"}]).then(ret => {
let ptzArg = state.ptzArgs.find(item => item.id === chnId);
ptzArg.ptz.p = ret.data[0];
ptzArg.ptz.t = ret.data[1];
ptzArg.ptz.z = ret.data[2];
const preset = presetConf.find(item => item.id === chnId);
preset.pos[presetVal - 1] = ret.data;
updatePresetConf();
});
}
const onUsbCamOptionChange = () => {
rpc("enc.uvcInvoke",[state.advanced.chnId,{func:"insta360_set",arg:state.advanced.options}]);
}
const onAdvModalVisible = visible => {
if(!visible)
state.advanced.showAdvModal = false;
}
const onDisplayHdmi = chnId => {
defaultConf.forEach(item => {
if(item.type === "mix")
item.output.src = chnId;
});
updateDefaultConf();
}
const handleAutoStyle = chnId => {
const encv = defaultConf[chnId].encv;
let { width, height} = encv;
width = Number(width) > 0 ? Number(width) : 1920;
height = Number(height) > 0 ? Number(height) : 1080;
let ww = (9 * width) / (height * 16) * 100;
let hh = (16 * height) / (width * 9) * 100;
[ww, hh] = ww > hh ? ["100%", hh + "%"] : [ww + "%", "100%"];
return `position: absolute;width: ${ww};height: ${hh};`;
};
onMounted(()=>{
handleUsbCamInfos();
updateChnImage();
handleInputState();
});
return {...state,defaultConf,hardwareConf,handleAutoStyle,handlePtzMove,handleCurFramerate,handleZoomChange,onAdvModalVisible,onAdvSetting,
getCamInfo,getPtzArg,handleCallPreset,handleSetPreset,onUsbCamOptionChange,updateDefaultConf,onDisplayHdmi,window}
}
});
app.use(ignoreCustomElementPlugin);
app.use(filterKeywordPlugin);
app.mount('#app');
</script>
</body>
</html>
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。