<?php include ("./link/session.php") ?><!doctype html><html lang="uft-8"><head><?php include ("./public/head.inc") ?><link href="assets/plugins/timepicker/css/bootstrap-timepicker.min.css" rel="stylesheet"></head><body><?php include ("./public/menu.inc") ?><div data-simplebar><main class="page-content push" id="app" v-cloak><div class="row"><div class="calc-24-9 lp-equal-height-container"><div class="card lp-equal-height-item mb-0"><div class="card-body"><div class="row"><div class="d-flex align-items-center gap-3 px-3 py-1"><div class="flex-grow-0"><label class="fw-bold"><cn>预览1</cn><en>Preview1</en>:</label></div><div class="flex-grow-0"><select class="form-select" v-model="previewInfo.preview1.index" @change="event => onChangePreview('preview1',event)"><option v-for="(item,index) in pushConf.url" :value="index">{{item.des}}</option><option cn="关闭预览" en="close" :value="-1" v-language-option></option></select></div></div></div><div class="row lp-align-center"><div v-if="previewInfo.preview1.type !== 'none'" class="col-lg-12"><h5-player :url="previewInfo.preview1.url" :codec="previewInfo.preview1.codec" :audio="previewInfo.preview1.audio" :protocol="previewInfo.preview1.type === 'webrtc'?'webrtc':'rtmp'" :canplay="previewInfo.preview1.play"></h5-player></div><div v-else class="col-lg-12"><div class="card-img-content"><img v-once src="assets/img/nosignal.jpg" class="card-img-top" style="visibility: hidden"><div class="lp-align-center push-tips" :style="handleAutoStyle()"><label style="font-size: 3.3rem"><cn style="letter-spacing: 5px">关闭预览</cn><en>No Preview</en></label></div></div></div></div></div></div></div><div class="calc-24-9 lp-equal-height-container"><div class="card lp-equal-height-item mb-0"><div class="card-body"><div class="row"><div class="d-flex align-items-center gap-3 px-3 py-1"><div class="flex-grow-0"><label class="fw-bold"><cn>预览2</cn><en>Preview2</en>:</label></div><div class="flex-grow-0"><select class="form-select" v-model="previewInfo.preview2.index" @change="event => onChangePreview('preview2',event)"><option v-for="(item,index) in pushConf.url" :value="index">{{item.des}}</option><option cn="关闭预览" en="close" :value="-1" v-language-option></option></select></div></div></div><div class="row lp-align-center"><div v-if="previewInfo.preview2.type !== 'none'" class="col-lg-12"><h5-player :url="previewInfo.preview2.url" :codec="previewInfo.preview2.codec" :audio="previewInfo.preview2.audio" :protocol="previewInfo.preview2.type=== 'webrtc'?'webrtc':'rtmp'" :canplay="previewInfo.preview2.play"></h5-player></div><div v-else class="col-lg-12"><div class="card-img-content"><img v-once src="assets/img/nosignal.jpg" class="card-img-top" style="visibility: hidden"><div class="lp-align-center push-tips" :style="handleAutoStyle()"><label style="font-size: 3.3rem"><cn style="letter-spacing: 5px">关闭预览</cn><en>No Preview</en></label></div></div></div></div></div></div></div><div class="calc-24-6 lp-equal-height-container"><div class="lp-equal-height-item d-flex flex-column"><div class="row flex-grow-1"><div class="col-lg-12"><div class="card h-100 d-flex flex-column mb-0"><div class="card-header bg-transparent"><div class="p-2 mb-0 d-flex align-items-end"><cn>基本设置</cn><en>Basic config</en></div></div><div class="card-body"><div class="d-flex flex-column justify-content-between h-100" v-if="loadConf"><div class="row"></div><div class="row"><div class="col-lg-4 lp-align-center ps-0"><label><cn>定时开启</cn><en>start time</en></label></div><div class="col-lg-4 ps-0"><select class="form-select" v-model="pushCron.start.day"><option cn="从不" en="never" value="x" v-language-option></option><option cn="每天" en="everyday" value="*" v-language-option></option><option cn="每周一" en="monday" value="1" v-language-option></option><option cn="每周二" en="tuesday" value="2" v-language-option></option><option cn="每周三" en="wednesday" value="3" v-language-option></option><option cn="每周四" en="thursday" value="4" v-language-option></option><option cn="每周五" en="friday" value="5" v-language-option></option><option cn="每周六" en="saturday" value="6" v-language-option></option><option cn="每周日" en="sunday" value="0" v-language-option></option></select></div><div class="col-lg-4 ps-0"><time-picker v-model="pushCron.start.time"></time-picker></div></div><div class="row"><div class="col-lg-4 lp-align-center ps-0"><label><cn>定时结束</cn><en>stop time</en></label></div><div class="col-lg-4 ps-0"><select class="form-select" v-model="pushCron.stop.day"><option cn="从不" en="never" value="x" v-language-option></option><option cn="每天" en="everyday" value="*" v-language-option></option><option cn="每周一" en="monday" value="1" v-language-option></option><option cn="每周二" en="tuesday" value="2" v-language-option></option><option cn="每周三" en="wednesday" value="3" v-language-option></option><option cn="每周四" en="thursday" value="4" v-language-option></option><option cn="每周五" en="friday" value="5" v-language-option></option><option cn="每周六" en="saturday" value="6" v-language-option></option><option cn="每周日" en="sunday" value="0" v-language-option></option></select></div><div class="col-lg-4 ps-0"><time-picker v-model="pushCron.stop.time"></time-picker></div></div><div class="row"><div class="col-lg-4 lp-align-center ps-0"><label><cn>开机推流</cn><en>auto push</en></label></div><div class="col-lg-4 ps-0"><select class="form-select" v-model="pushConf.autorun"><option cn="关闭" en="OFF" value="false" v-language-option></option><option cn="开启" en="ON" value="true" v-language-option></option></select></div></div><div class="row"><div class="col-lg-12 text-center"><button type="button" class="btn border-3 btn-primary px-4" @click="savePushConf" style="width: 95px;"><cn>保存</cn><en>Save</en></button></div></div><div class="row"></div></div></div></div></div></div></div></div></div><div class="row mt-4"><div class="col-lg-7 mx-auto lp-align-center"><div class="push-bar w-100"><strong style="margin-right: 45px;font-size: 15px;">{{formatPushTimeCount(pushState.duration)}}</strong><button type="button" :class="['btn border-3 px-4',{'btn-primary':!pushState.pushing},{'btn-default disabled':pushState.pushing}]" @click="onPushStart"><i class="fa-solid fa-video me-1"></i><cn>推流</cn><en>Push</en></button><button type="button" :class="['btn border-3 ms-3 px-4',{'btn-primary':pushState.pushing},{'btn-default disabled':!pushState.pushing}]" @click="onPushStop"><i class="fa-solid fa-stop me-1"></i><cn>停止</cn><en>Stop</en></button></div></div></div><div class="row mt-4"><div class="col-lg-12"><div class="card"><div class="card-body py-3"><div class="row mt-3"><div class="calc-24-3 text-center"><cn>描述</cn><en>Description</en></div><div class="calc-24-2 text-center"><cn>推流模式</cn><en>Mode</en></div><div class="calc-24-2 text-center"><cn>视频源</cn><en>Video source</en></div><div class="calc-24-2 text-center"><cn>音频源</cn><en>Audio source</en></div><div class="calc-24-2 text-center"><cn>码流</cn><en>Stream</en></div><div class="calc-24-6 text-center"><cn>推流地址</cn><en>Push Url</en></div><div class="calc-24-2 text-center"><cn>兼容性</cn><en>Compatible</en></div><div class="calc-24-2 text-center"><cn>速度</cn><en>Speed</en></div><div class="calc-24-1 text-center"><cn>启用</cn><en>Enable</en></div><div class="calc-24-1 text-center"><cn>删除</cn><en>Delete</en></div></div><hr class="my-3"><div class="row" v-if="loadConf" v-for="(item,index) in pushConf.url"><div class="co-lg-12"><div class="row d-flex flex-nowrap"><div class="calc-24-3"><input type="text" class="form-control" v-model.trim.lazy="item.des"></div><div class="calc-24-2 text-center"><select class="form-select" v-model="item.type"><option cn="常规模式" en="Normal" value="normal" v-language-option></option><option v-if="Object.keys(hardwareConf).length>0 && hardwareConf.chip!=='HI3520DV400' && hardwareConf.chip!=='HI3521DV100' && hardwareConf.chip!=='HI3531DV100'" value="webrtc">WebRTC</option><option v-if="Object.keys(hardwareConf).length>0 && hardwareConf.chip==='SS524V100'" value="trtc">TRTC</option></select></div><div class="calc-24-2 text-center"><select class="form-select" v-model="item.srcV"><option v-for="(it,idx) in handleChnV(item.type)" :key="idx" :value="it.id">{{it.name}}</option></select></div><div class="calc-24-2 text-center"><select class="form-select" v-model="item.srcA"><option cn="关闭" en="Close" value="close" v-language-option></option><option v-for="(it,idx) in handleChnA(item.type,item.path)" :key="idx" :value="it.id">{{it.name}}</option></select></div><div class="calc-24-2 text-center"><select class="form-select" v-model="item.stream"><option cn="主码流" en="Main" value="main" v-language-option></option><option v-if="handleEnable2(item.srcV)" cn="辅码流" en="Sub" value="sub" v-language-option></option></select></div><div class="calc-24-6 push-url"><input type="text" class="form-control" v-model.trim.lazy="item.path"></div><div class="calc-24-2"><select class="form-select" v-model="item.flvflags"><option cn="标准" en="normal" value="" v-language-option></option><option v-if="handleCompatible(item.path)" value="ext_header">enhanced-rtmp</option></select></div><div class="calc-24-2 text-center">{{handlePushSpeed(index)}} kb/s</div><div class="calc-24-1 lp-align-center"><bs-switch v-model="item.enable"></bs-switch></div><div class="calc-24-1 text-center"><button type="button" class="btn border-3 btn-primary" :disabled="pushConf.url.length <= 2" @click="delPushUrl(index)"><cn>删除</cn><en>delete</en></button></div></div><hr class="my-3"></div></div><div class="row"><div class="col-lg-12 tips"><cn>1、点击推流按钮,可以向全部已启用的通道推流,推流速度大于0表示推流成功。</cn><en>1. Click the push button to stream to all enabled channels. A speed greater than 0 indicates a successful stream.</en></div><div class="col-lg-12 tips"><cn>2、切换推流模式,会自动匹配可用的视频源和音频源。</cn><en>2. Switching the push mode,and auto matches available video and audio sources.</en></div><div class="col-lg-12 tips"><cn>3、常规模式下,推送rtmp协议流时不支持OPUS音频编码格式</cn><en>3. In normal mode, OPUS audio encoding format is not supported when pushing RTMP protocol streams.</en></div><div class="col-lg-12 tips"><cn>4、WebRTC模式下,仅支持H264视频编码格式,和OPUS/Close音频编码格式。</cn><en>4. In WebRTC mode, only the H264 video encoding format and OPUS/Close audio encoding formats are supported.</en></div></div><div class="row mt-3"><div class="col-lg-12 lp-align-center"><button type="button" class="btn border-3 btn-primary px-5" @click="addPushUrl"><cn>添加</cn><en>Add</en></button><button type="button" class="btn border-3 btn-primary px-5 ms-2 me-5" @click="savePushConf"><cn>保存</cn><en>Save</en></button></div></div></div></div></div></div></main></div><?php include ("./public/foot.inc") ?><script type="module">import {rpc, func, alertMsg, isEmpty} from "./assets/js/lp.utils.js?hash=e82520f08";import { useDefaultConf,useHardwareConf,usePushConf } from "./assets/js/vue.hooks.js?hash=89523e901";import { ignoreCustomElementPlugin,filterKeywordPlugin,bootstrapSwitchComponent,h5PlayerComponent,timepickerComponent,languageOptionDirective } from "./assets/js/vue.helper.js?hash=68eefc36f"import vue from "./assets/js/vue.build.js?hash=879ea7dbc";import {clearReactiveObject} from "./assets/js/lp.utils.js?hash=e82520f08";const {createApp,ref,reactive,watchEffect,computed,onMounted} = vue;const app = createApp({directives: {"language-option": languageOptionDirective},components:{"bs-switch" : bootstrapSwitchComponent,"h5-player": h5PlayerComponent,"time-picker": timepickerComponent},setup(props,context) {const { defaultConf } = useDefaultConf();const { hardwareConf } = useHardwareConf();const { pushConf,updatePushConf } = usePushConf();const state = {previewInfo: reactive({preview1: {url:"",play: false,type: "",index: -1,audio: false,codec: "h264"},preview2: {url:"",play: false,type: "",index: -1,audio: false,codec: "h264"}}),loadConf: ref(false),updateTime:0,pushState:reactive({}),pushCron: reactive({start:{day:"x",time:"00:00"},stop: {day:"x",time:"00:00"}})}const unwatch = watchEffect(() => {if(Object.keys(pushConf).length > 0) {const newConf = {...pushConf,url: pushConf.url.filter(item =>item.type !== "webrtc" ||!["HI3520DV400", "HI3521DV100", "HI3531DV100"].includes(hardwareConf.chip))};clearReactiveObject(pushConf);Object.assign(pushConf,newConf)state.loadConf.value = true;unwatch();}})const handleChnV = type =>{return defaultConf.filter(item => {if(item.enable) {if(type !== "normal")return (item.encv.codec !== "close" && item.encv.codec !== "h265") || item.encv2.codec !== "close" && item.encv2.codec !== "h265";elsereturn true;}})}const handleChnA = (type,path) =>{return defaultConf.filter(item => {if(item.enable) {if(type !== "normal")return item.enca.codec === "close" || item.enca.codec === "opus";else if(type === "normal" && path.toLowerCase().startsWith("rtmp"))return item.enca.codec !== "opus";elsereturn true;}})}const handleEnable2 = chnId => {let hadSub = false;defaultConf.forEach(item => {if(item.id === parseInt(chnId))hadSub = !!item.enable2;})return hadSub;}const handleCompatible = path => {let hadFlvFlags = false;path = path.toLowerCase();if(path.startsWith("rtmp://") || path.startsWith("rtmps://"))hadFlvFlags = true;return hadFlvFlags;}const handlePreviewInfo = () => {rpc("push.getPreview").then(data => {if(!isEmpty(pushConf) && !isEmpty(defaultConf)) {let keys = Object.keys(data);for(let i=0;i<keys.length;i++) {const key = keys[i];let curPreIndex = data[key].index;if(curPreIndex >= 0) {let srcV = pushConf.url[curPreIndex].srcV;let srcA = pushConf.url[curPreIndex].srcA;let stream = pushConf.url[curPreIndex].stream;let conf = defaultConf.find(item => item.id === parseInt(srcV));data[key].codec = conf[stream==="main"?"encv":"encv2"].codec;data[key].audio = false;if(srcA !== "close") {conf = defaultConf.find(item => item.id === parseInt(srcA));data[key].audio = !!(conf.enca && conf.enca.codec !== "close");}data[key].url = 'http://'+window.location.host+'/'+(data[key].type!=='normal'?'webrtc':'flv')+'?app=live&stream='+key;data[key].play = true;} else {data[key].codec = "";data[key].audio = false;data[key].url = "";data[key].play = false;}}if(JSON.stringify(state.previewInfo) !== JSON.stringify(data)) {clearReactiveObject(state.previewInfo);Object.assign(state.previewInfo,data);}}setTimeout(handlePreviewInfo,1000);});}const handlePushCrontab = () => {func("/system/getPushCrontab").then(result => {const keys = Object.keys(result.data);keys.forEach(key => {const value = result.data[key];if(value === null)return;let list = value.split("");if(list.length === 8) {state.pushCron[key].day = list[4];state.pushCron[key].time = list[1]+":"+list[0];}});})}const onChangePreview = (preType,event) => {const selectElement = event.target;const selectedOption = selectElement.options[selectElement.selectedIndex];state.previewInfo[preType].play = false;rpc("push.updatePreview",[preType,selectedOption.value]).then(() => state.previewInfo[preType].play = true);}const handlePushState = () => {rpc("push.getState").then(data => {Object.assign(state.pushState, data);state.updateTime = new Date().getTime() / 1000;if(state.pushState.pushing)setTimeout(handlePushState,1000);});}const handlePushSpeed = idx => {if(isEmpty(state.pushState))return 0;const { speed } = state.pushState;return speed[idx];}const formatPushTimeCount = count => {if (state.pushState.pushing && count > 0) {const fix = num => {if ( num < 10 )return '0' + num;elsereturn num;}let now = new Date();let diff = now.getTime() / 1000 - state.updateTime + count/1000;let h = Math.floor(diff / 3600);let m = Math.floor( diff % 3600 / 60 );let s = Math.floor( diff % 60 );return fix(h) + ":" + fix( m ) + ":" + fix( s );}return "[--:--:--]";}const onPushStart = () => {rpc("push.start").then(()=>{handlePushState();});}const onPushStop = () => {rpc("push.stop").then(()=>{handlePushState();});}const addPushUrl = () => {pushConf.url.push({"des": "new platform","enable": false,"type":"normal","srcV": 0,"srcA":0,"stream":"main","path": "","flvflags":""})}const delPushUrl = (index) => pushConf.url.splice(index, 1);const savePushConf = () => {updatePushConf("noTip").then(()=>{func("/system/setPushCrontab",state.pushCron).then(data => {if(data.status === "success")alertMsg('<cn>保存设置成功</cn><en>Save config success!</en>', 'success');});})}const handleAutoStyle = () => {let width = 1920;let height = 1080;let ww = (9 * width) / (height * 16) * 100;let hh = (16 * height) / (width * 9) * 100;[ww, hh] = ww > hh ? ["100%", hh + "%"] : [ww + "%", "100%"];console.log(`position: absolute;width: ${ww};height: ${hh};`)return `position: absolute;width: ${ww};height: ${hh};`;};onMounted(() => {handlePushCrontab();handlePushState();handlePreviewInfo();})return {...state,defaultConf,hardwareConf,pushConf,formatPushTimeCount,onChangePreview,handleChnV,handleAutoStyle,handleChnA,handleEnable2,handleCompatible,handlePushSpeed,onPushStart,onPushStop,addPushUrl,delPushUrl,savePushConf}}});app.use(ignoreCustomElementPlugin);app.use(filterKeywordPlugin);app.mount('#app');</script></body></html>
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。