<?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 class="p-0"><main class="page-content mix" id="app" v-cloak><div class="row"><div class="col-lg-6 mx-auto lp-equal-height-container"><div class="card lp-equal-height-item"><div class="card-body"><div class="row"><div class="d-flex align-items-center gap-3 px-2 py-1"><div class="flex-grow-0"><label class="fw-bold"><cn>频道</cn><en>Channel</en>:</label></div><div class="flex-grow-0"><select class="form-select"><option v-if="defaultConf.length > 0 && mixIndex > -1" :value="defaultConf[mixIndex].id">{{defaultConf[mixIndex].name}}</option></select></div><div class="flex-grow-0"><label class="fw-bold"><cn>布局</cn><en>Layout</en>:</label></div><div class="flex-grow-0"><select class="form-select" v-model="curLayId" @change="onChangeLayout"><option v-for="(item,index) in defLaysConf" :value="item.layId">{{item.layName}}</option></select></div><div class="flex-grow-0" v-if="Object.keys(hardwareConf).length > 0 && hardwareConf.chip !== 'HI3516CV610'"><label class="fw-bold"><cn>预览</cn><en>Preview</en>:</label></div><div class="flex-grow-0" v-show="Object.keys(hardwareConf).length > 0 && hardwareConf.chip !== 'HI3516CV610'"><select class="form-select" v-model="preType" ref="preTypeEle"></select></div><div class="flex-grow-0" v-if="playerCodec === 'h265'"><label class="fw-bold"><cn>缓冲</cn><en>Buffer</en>:</label></div><div class="flex-grow-0" v-if="playerCodec === 'h265'" style="width: 100px"><div class="input-group"><input type="text" class="form-control" v-model="bufferTime" @change="onChangeBufferTime"><span class="input-group-text">ms</span></div></div><div class="flex-grow-1 d-flex justify-content-end pe-3"><i class="fa-solid fa-gear fa-lg lp-cursor-pointer" @click="hrefDefLayout"></i></div></div></div><div class="row"><div v-if="defaultConf.length > 0 && mixIndex > -1" class="col-lg-12 mt-2 mb-2"><div v-if="defaultConf[mixIndex].enable"><div v-if="!hadCarousel"><div v-show="preType === 'img'" class="col-lg-12 mt-2"><div class="card-img-content"><div class="card-img-background"></div><img :src="chnImgUrl" class="card-img" :style="handleAutoStyle()"><img :src="chnImgUrl" class="card-img" style="visibility: hidden"></div></div><div v-show="preType === 'vdo'" class="col-lg-12 mt-2"><h5-player :url="playerUrl" :codec="playerCodec" :audio="playerAudio" :protocol="playerProtocol" :buffer="bufferTime"></h5-player></div></div><div v-else><div class="card-img-content"><div class="lp-align-center mix-tips" :style="handleAutoStyle()"><label style="font-size: 3.3rem"><cn style="letter-spacing: 5px">视频轮播中,禁止操作</cn><en>Disabled in video carousel</en></label></div><img :src="chnImgUrl" class="card-img" style="visibility: hidden"><img v-once src="assets/img/nosignal.jpg" class="card-img-top" style="visibility: hidden"></div></div></div><div v-else class="card-img-content"><div class="lp-align-center mix-tips" :style="handleAutoStyle()"><label style="font-size: 3.3rem"><cn style="letter-spacing: 5px">通道未开启</cn><en>Not Enable</en></label></div><img :src="chnImgUrl" class="card-img" style="visibility: hidden"><img v-once src="assets/img/nosignal.jpg" class="card-img-top" style="visibility: hidden"></div></div></div></div></div></div><div class="col-lg-6 mx-auto lp-equal-height-container"><div class="card lp-equal-height-item d-flex flex-column"><div class="card-header bg-transparent flex-grow-0"><div class="p-2 mb-0 d-flex align-items-end"><cn>布局设定</cn><en>Layout config</en></div></div><div class="card-body pb-4 flex-grow-1"><div class="row flex-grow-1 h-100"><div class="col-lg-12 mt-2 mb-2"><div class="layout-bg card-img-content pb-0 h-100"><div class="bg-black" :style="handleAutoStyle()"><div class="lay-border" v-for="(item,index) in handleActiveDefLayConf.layouts" :style="{position:'absolute',width:item.pos.w * 100+'%',height:item.pos.h*100+'%',left:item.pos.x*100+'%',top:item.pos.y*100+'%',zIndex:item.pos.index}"><div :style="{width:'100%',height:'100%',backgroundColor: handleLayBackColor(index)}"><div class="d-flex align-items-center gap-1 border-0 px-2 py-1"><div class="flex-grow-1"><select class="form-select" v-model="defaultConf[mixIndex].srcV[index]" @change="updateDefaultConfig('noTip')"><option value="-1" cn="空" en="none" v-language-option></option><option v-for="(it,index) in handleLayoutChnSelect(defaultConf[mixIndex].srcV[index])" :value="it.id">{{it.name}}</option></select></div></div></div></div></div></div></div></div></div></div></div></div><div class="row"><div class="col-lg-6"><div class="card"><div class="card-header bg-transparent flex-grow-0"><div class="p-2 mb-0 d-flex align-items-end"><cn>混音</cn><en>Audio Mixer</en></div></div><div class="card-body py-4"><div class="row px-3"><div class="col-lg-4 px-1 py-1" v-for="(item,index) in handleAudioChnConf"><div class="d-flex gap-1 border-0 p-2"><div class="flex-grow-1"><input class="form-control lp-cursor-pointer" type="text" v-model.trim.lazy="item.name" readonly></div><div class="flex-grow-0"><button :class="['btn',{'btn-default':!handleActiveVolume(item.id)},{'px-2 btn-primary':handleActiveVolume(item.id)}]" @click="onUpdateActiveVolume(item.id)"><i :class="['fa-solid',{'fa-volume-off':!handleActiveVolume(item.id)},{'fa-volume-high':handleActiveVolume(item.id)}]"></i></button></div></div></div></div></div></div></div></div></main></div><?php include ("./public/foot.inc") ?><script type="module">import { rpc,confirm,alertMsg,isEmpty } from "./assets/js/lp.utils.js?hash=e82520f08";import { useDefaultConf,useDefLaysConf,useHardwareConf } from "./assets/js/vue.hooks.js?hash=89523e901";import { ignoreCustomElementPlugin,filterKeywordPlugin,languageOptionDirective,h5PlayerComponent } from "./assets/js/vue.helper.js?hash=68eefc36f"import mutationObserver from './assets/plugins/polyfill/mutationobserver.esm.js';import vue from "./assets/js/vue.build.js?hash=879ea7dbc";const {createApp,ref,reactive,watchEffect,computed,onMounted} = vue;const app = createApp({directives:{"language-option": languageOptionDirective},components:{"h5-player": h5PlayerComponent},setup(props,context) {const { defaultConf,updateDefaultConf } = useDefaultConf();const { defLaysConf } = useDefLaysConf();const { hardwareConf } = useHardwareConf();const state = {chnImgUrl: ref(""),curLayId: ref(-1),mixIndex: ref(-1),curTheme: ref("default"),playerUrl: ref(""),playerCodec: ref(""),playerAudio: ref(false),playerProtocol:ref(""),bufferTime:ref(200),preType:ref("img"),preTypeEle:ref(null),hadCarousel:ref(false)}const unwatch = watchEffect(()=>{if(defaultConf.length > 0 && Object.keys(defLaysConf).length > 0 && state.preTypeEle.value) {for(let i=0;i<defaultConf.length;i++) {if(defaultConf[i].type !== "mix")continue;state.mixIndex.value = i;const html = document.querySelector('html');let lang = html.getAttribute('data-bs-language');var layList = [];let mixChn = defaultConf[i];for (let j = 0; j < mixChn.layout.length; j++) {let layout = mixChn.layout[j];let layObj = {"a": layout.a,"x": layout.x,"y": layout.y,"w": layout.w,"h": layout.h,"index": layout.index}layList.push(layObj);}let curLayStr = JSON.stringify(layList);for (let j = 0; j < defLaysConf.length; j++) {let las = defLaysConf[j].layouts;let layout = [];for (let k = 0; k < las.length; k++) {layout.push(las[k].pos);}if(curLayStr === JSON.stringify(layout)) {state.curLayId.value = defLaysConf[j].layId;break;}}if ((mixChn.stream.rtmp || mixChn.stream.webrtc) && mixChn.encv.codec !== "close") {if(lang === 'cn') {state.preTypeEle.value.add(new Option("视频", "vdo"));state.preTypeEle.value.add(new Option("图片", "img"));} else {state.preTypeEle.value.add(new Option("video", "vdo"));state.preTypeEle.value.add(new Option("image", "img"));}const protocol = mixChn.stream.rtmp ? "rtmp" : "webrtc";if(protocol === 'webrtc')state.playerUrl.value = `http://${window.location.host}/webrtc?app=live&stream=${mixChn.stream.suffix}`;elsestate.playerUrl.value = `http://${window.location.host}/flv?app=live&stream=${mixChn.stream.suffix}`;state.playerCodec.value = mixChn.encv.codec;state.playerAudio.value = mixChn.enca.codec!=="close";state.playerProtocol.value = protocol;state.preType.value = "vdo";}else{if(lang === 'cn')state.preTypeEle.value.add(new Option("图片", "img"));elsestate.preTypeEle.value.add(new Option("image", "img"));state.preType.value = "img";state.playerUrl.value = "";state.playerProtocol.value = "";}}updateChnImage();unwatch();}})const updateChnImage = () => {if(defaultConf[state.mixIndex.value].enable) {state.chnImgUrl.value = "snap/snap" + defaultConf[state.mixIndex.value].id + ".jpg?rnd=" + Math.random();setTimeout(() => { rpc( "enc.snap" ) },200)setTimeout(updateChnImage,500);}}const handleAudioChnConf = computed(()=>{let chnlistA = defaultConf.filter((item,index)=>{return item.type !== "mix" && item.type !== "ndi";})chnlistA.unshift({id: "usbAlsa",name: "USB Mic"});if(!isEmpty(hardwareConf) && hardwareConf.function.line) {chnlistA.unshift({id: "line",name: "Line Input"});}return chnlistA;})const handleActiveDefLayConf = computed(()=>{return defLaysConf.find((item) => item.layId === state.curLayId.value) || {};});const handleLayoutChnSelect = computed(() => {return (chnId) => {let srcV = defaultConf[state.mixIndex.value].srcV;return defaultConf.filter((item,index)=>{return !((srcV.indexOf(item.id) > -1 && item.id !== chnId) || item.type === "ndi" || item.type === "mix");});};});const handleActiveVolume = chnId => {const mixChn = defaultConf[state.mixIndex.value];if(mixChn) {chnId = chnId.toString();return mixChn.srcA.includes(chnId) || mixChn.srcA.includes(Number(chnId));}return false;};const onUpdateActiveVolume = chnId => {if(chnId === "-1")return;chnId = chnId.toString();defaultConf[state.mixIndex.value].srcA = defaultConf[state.mixIndex.value].srcA.map(item => item = item.toString());defaultConf[state.mixIndex.value].srcV = defaultConf[state.mixIndex.value].srcV.map(item => item = item.toString());let idx = defaultConf[state.mixIndex.value].srcA.indexOf(chnId);if(idx === -1)defaultConf[state.mixIndex.value].srcA.push(chnId);elsedefaultConf[state.mixIndex.value].srcA.splice(idx, 1);if(!isEmpty(hardwareConf) && !hardwareConf.function.line) {let lineIdx = defaultConf[state.mixIndex.value].srcA.indexOf("line");if(lineIdx !== -1)defaultConf[state.mixIndex.value].srcA.splice(lineIdx, 1);}updateDefaultConfig("noTip");};const handleAutoStyle = () => {if(state.mixIndex.value < 0)return "";const encv = defaultConf[state.mixIndex.value].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};`;};const hrefDefLayout = () => {confirm({title: '<cn>布局</cn><en>Layout</en>',content: '<cn>是否打开布局管理器?</cn><en>Jump to Layout Manager?</en>',buttons: {ok: {text: "<cn>打开</cn><en>Confirm</en>",btnClass: 'btn-primary',keys: ['enter'],action: () => window.open('defLayout.php', '_blank')},cancel: {text: "<cn>取消</cn><en>Cancel</en>",action: () => {}}}});}const onChangeLayout = () => {let layout = [];let srcV = [];let srcA = [];let markV = false;let markA = false;for(let i=0;i<defLaysConf.length;i++) {if(state.curLayId.value === defLaysConf[i].layId) {let las = defLaysConf[i].layouts;for (let j = 0; j < las.length; j++) {layout.push(las[j].pos);if(las[j].id < 0) {srcV.push("-1");} else {srcV.push(las[j].id + "");markV = true;if(las[j].ado) {srcA.push(las[j].id + "")markA = true;}}}}}if(!markV) {if (srcV.length >= defaultConf[state.mixIndex.value].srcV.length)srcV.splice(0, defaultConf[state.mixIndex.value].srcV.length, ...defaultConf[state.mixIndex.value].srcV);elsesrcV = defaultConf[state.mixIndex.value].srcV.slice(0, srcV.length);}defaultConf[state.mixIndex.value].srcV.splice(0, defaultConf[state.mixIndex.value].srcV.length, ...srcV);defaultConf[state.mixIndex.value].layout.splice(0, defaultConf[state.mixIndex.value].layout.length, ...layout);if(markA)defaultConf[state.mixIndex.value].srcA.splice(0, defaultConf[state.mixIndex.value].srcA.length, ...srcA);updateDefaultConfig("noTip");const options = document.querySelectorAll(`option[cn]`);options.forEach(option => {option.textContent = option.getAttribute('cn');});}const updateDefaultConfig = tip => {if(state.hadCarousel.value) {alertMsg('<cn>视频轮播中,禁止操作</cn><en>Disabled in video carousel!</en>', 'error');return;}updateDefaultConf(tip);}const handleCarouselState = () => {rpc("carousel.getState").then(data => {state.hadCarousel.value = data["hadCarousel"];setTimeout(handleCarouselState,1000);});}const handleLayBackColor = (idx) => {let baseColor = state.curTheme.value !== "dark" ? [100, 100, 100] : [63, 69, 75];let colorDecreaseStep = state.curTheme.value !== "dark" ? [5, 5, 5] : [3, 3, 3];//let colorDecreaseStep = [5, 5, 5];let adjustedColor = baseColor.map((value, index) => Math.max(0, value - (idx * colorDecreaseStep[index])));return `rgb(${adjustedColor[0]},${adjustedColor[1]},${adjustedColor[2]})`;}onMounted(()=>{const html = document.querySelector('html');const observer = new mutationObserver(mutations => {mutations.forEach(mutation => {if (mutation.type === 'attributes' && mutation.attributeName === "data-bs-theme")state.curTheme.value = mutation.target.getAttribute("data-bs-theme");});});const config = {attributes: true,attributeFilter: ["data-bs-theme"],subtree: false};observer.observe(html, config);handleCarouselState();});return {...state,defaultConf,defLaysConf,hardwareConf,handleAudioChnConf,handleActiveDefLayConf,hrefDefLayout,onChangeLayout,handleLayBackColor,handleActiveVolume,onUpdateActiveVolume,handleAutoStyle,handleLayoutChnSelect,updateDefaultConfig}}});app.use(ignoreCustomElementPlugin);app.use(filterKeywordPlugin);app.mount('#app');</script></body></html>
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。