使用zlm作为新的流服务器

master
LAPTOP-S9HJSOEB\昊天 2 years ago
parent 7c8cb04802
commit 960ca2c322

@ -10,6 +10,7 @@
<!-- <script src="/video_play_plugins/video.js"></script> -->
<script src="/video_play_plugins/webrtcplayer.js"></script>
<script src="webrtc/ZLMRTCClient.js"></script>
<title>堆垛机视觉系统</title>
</head>

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

@ -0,0 +1,336 @@
<html>
<meta charset="utf-8">
<head>
<title>ZLM RTC demo</title>
<script src="./ZLMRTCClient.js"></script>
</head>
<body>
<div style="text-align: center;">
<div>
<video id='video' controls autoplay style="text-align:left;">
Your browser is too old which doesn't support HTML5 video.
</video>
<video id='selfVideo' controls autoplay style="text-align:right;">
Your browser is too old which doesn't support HTML5 video.
</video>
</div>
<div style="float: left; width:30%;">
<span>已存在的流列表,点击自动播放:</span>
<ol id="olstreamlist">
<li>初始演示</li>
<li>每秒自动刷新</li>
</ol>
</div>
<div style="float: right; width: 70%">
<p>
<label for="streamUrl">url:</label>
<input type="text" style="co; width:70%" id='streamUrl' value="http://192.168.1.101/index/api/webrtc?app=live&stream=xiong&type=play">
</p>
<p>
<label for="simulcast">simulcast:</label>
<input type="checkbox" id='simulcast'>
</p>
<p>
<label for="useCamera">useCamera:</label>
<input type="checkbox" id='useCamera' checked="checked">
</p>
<p>
<label for="audioEnable">audioEnable:</label>
<input type="checkbox" id='audioEnable' checked="checked">
</p>
<p>
<label for="videoEnable">videoEnable:</label>
<input type="checkbox" id='videoEnable' checked="checked">
</p>
<p>
<label for="method">method(play or push or echo):</label>
<input type="radio" name="method" value="echo" >echo
<input type="radio" name="method" value="push" >push
<input type="radio" name="method" value="play" checked = true>play
</p>
<p>
<label for="resolution">resolution:</label>
<select id="resolution">
</select>
</p>
<p>
<label for="datachannel">datachannel:</label>
<input id='datachannel' name="datachannel" type="checkbox" value="0">
</p>
<button onclick="start()">开始(start)</button>
<button onclick="stop()">停止(stop)</button>
<p>
<label for="msgsend">msgsend:</label>
<input type="text" id='msgsend' value="hello word !">
</p>
<p>
<label for="msgrecv">msgrecv:</label>
<input type="text" id='msgrecv' disabled>
</p>
<button onclick="send()">发送(send by datachannel)</button>
<button onclick="close()">关闭(close datachannel)</button>
</div>
</div>
<script>
var player = null;
var recvOnly = true;
var resArr = [];
var ishttps = 'https:' == document.location.protocol ? true : false;
var isLocal = "file:" == document.location.protocol ? true : false;
const searchParams = new URL(document.location.href).searchParams;
let type = searchParams.get('type');
if (!['echo','push','play'].includes(type)) {
type = 'play';
}
recvOnly = type === 'play';
const apiPath = `/index/api/webrtc?app=${searchParams.get('app') ?? 'live'}&stream=${searchParams.get('stream') ?? 'test'}&type=${type}`;
if(!ishttps && !isLocal){
alert('本demo需要在https的网站访问 ,如果你要推流的话(this demo must access in site of https if you want push stream)');
}
const apiHost = isLocal ? "http://127.0.0.1" : `${document.location.protocol}//${window.location.host}`;
var url = apiHost + apiPath;
document.getElementById('streamUrl').value = url;
document.getElementsByName("method").forEach((el,idx) => {
el.checked = el.value === type;
el.onclick = function(e) {
let url = new URL(document.getElementById('streamUrl').value);
url.searchParams.set("type",el.value);
document.getElementById('streamUrl').value = url.toString();
if(el.value == "play"){
recvOnly = true;
}else if(el.value == "echo"){
recvOnly = false;
}else{
recvOnly = false;
}
};
});
ZLMRTCClient.GetAllScanResolution().forEach((r,i) => {
opt = document.createElement('option');
opt.text = `${r.label}(${r.width}x${r.height})`;
opt.value = r;
if (1080*720 <= r.width * r.height && r.width * r.height <= 1280*720) {
opt.selected = true;
}
document.getElementById("resolution").add(opt,null);
});
function start_play(){
let elr = document.getElementById("resolution");
let res = elr.options[elr.selectedIndex].text.match(/\d+/g);
let h = parseInt(res.pop());
let w = parseInt(res.pop());
player = new ZLMRTCClient.Endpoint(
{
element: document.getElementById('video'),// video 标签
debug: true,// 是否打印日志
zlmsdpUrl:document.getElementById('streamUrl').value,//流地址
simulcast:document.getElementById('simulcast').checked,
useCamera:document.getElementById('useCamera').checked,
audioEnable:document.getElementById('audioEnable').checked,
videoEnable:document.getElementById('videoEnable').checked,
recvOnly:recvOnly,
resolution:{w:w,h:h},
usedatachannel:document.getElementById('datachannel').checked,
}
);
player.on(ZLMRTCClient.Events.WEBRTC_ICE_CANDIDATE_ERROR,function(e)
{// ICE 协商出错
console.log('ICE 协商出错');
});
player.on(ZLMRTCClient.Events.WEBRTC_ON_REMOTE_STREAMS,function(e)
{//获取到了远端流,可以播放
console.log('播放成功',e.streams);
});
player.on(ZLMRTCClient.Events.WEBRTC_OFFER_ANWSER_EXCHANGE_FAILED,function(e)
{// offer anwser 交换失败
console.log('offer anwser 交换失败',e);
stop();
});
player.on(ZLMRTCClient.Events.WEBRTC_ON_LOCAL_STREAM,function(s)
{// 获取到了本地流
document.getElementById('selfVideo').srcObject=s;
document.getElementById('selfVideo').muted = true;
//console.log('offer anwser 交换失败',e)
});
player.on(ZLMRTCClient.Events.CAPTURE_STREAM_FAILED,function(s)
{// 获取本地流失败
console.log('获取本地流失败');
});
player.on(ZLMRTCClient.Events.WEBRTC_ON_CONNECTION_STATE_CHANGE,function(state)
{// RTC 状态变化 ,详情参考 https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/connectionState
console.log('当前状态==>',state);
});
player.on(ZLMRTCClient.Events.WEBRTC_ON_DATA_CHANNEL_OPEN,function(event)
{
console.log('rtc datachannel 打开 :',event);
});
player.on(ZLMRTCClient.Events.WEBRTC_ON_DATA_CHANNEL_MSG,function(event)
{
console.log('rtc datachannel 消息 :',event.data);
document.getElementById('msgrecv').value = event.data;
});
player.on(ZLMRTCClient.Events.WEBRTC_ON_DATA_CHANNEL_ERR,function(event)
{
console.log('rtc datachannel 错误 :',event);
});
player.on(ZLMRTCClient.Events.WEBRTC_ON_DATA_CHANNEL_CLOSE,function(event)
{
console.log('rtc datachannel 关闭 :',event);
});
}
function start()
{
stop();
let elr = document.getElementById("resolution");
let res = elr.options[elr.selectedIndex].text.match(/\d+/g);
let h = parseInt(res.pop());
let w = parseInt(res.pop());
if(document.getElementById('useCamera').checked && !recvOnly)
{
ZLMRTCClient.isSupportResolution(w,h).then(e=>{
start_play();
}).catch(e=>{
alert("not support resolution");
});
}else{
start_play();
}
}
function stop()
{
if(player)
{
player.close();
player = null;
var remote = document.getElementById('video');
if(remote)
{
remote.srcObject = null;
remote.load();
}
var local = document.getElementById('selfVideo');
local.srcObject = null;
local.load();
}
}
function send(){
if(player){
//send msg refernece https://developer.mozilla.org/en-US/docs/Web/API/RTCDataChannel/send
player.sendMsg(document.getElementById('msgsend').value);
}
}
function close(){
if(player){
player.closeDataChannel();
}
}
function on_click_to_play(app, stream) {
console.log(`on_click_to_play: ${app}/${stream}`);
var url = `${document.location.protocol}//${window.location.host}/index/api/webrtc?app=${app}&stream=${stream}&type=play`;
document.getElementById('streamUrl').value = url;
start();
}
function clearStreamList() {
let content = document.getElementById("olstreamlist");
while (content.hasChildNodes()) {
content.removeChild(content.firstChild);
}
}
function fillStreamList(json) {
clearStreamList();
if (json.code != 0 || !json.data) {
return;
}
let ss = {};
for (let o of json.data) {
if (ss[o.app]) {
ss[o.app].add(o.stream);
} else {
let set = new Set();
set.add(o.stream);
ss[o.app] = set;
}
}
for (let o in ss) {
let app = o;
for (let s of ss[o]) {
if (s) {
//console.log(app, s);
let content = document.getElementById("olstreamlist");
let child = `<li app="${app}" stream="${s}" onmouseover="this.style.color='blue';" onclick="on_click_to_play('${app}', '${s}')">${app}/${s}</li>`;
content.insertAdjacentHTML("beforeend", child);
}
}
}
}
async function getData(url) {
const response = await fetch(url, {
method: 'GET'
});
const data = await response.json();
//console.log(data);
return data;
}
function get_media_list() {
let url = document.location.protocol+"//"+window.location.host+"/index/api/getMediaList?secret=035c73f7-bb6b-4889-a715-d9eb2d1925cc";
let json = getData(url);
json.then((json)=> fillStreamList(json));
}
setInterval(() => {
// get_media_list();
}, 5000);
</script>
</body>
<script>
</script>
</html>

@ -0,0 +1,2 @@
感谢[big panda](<2381267071@qq.com>) 开发并贡献此webrtc js测试客户端,
其开源项目地址为https://gitee.com/xiongguangjie/zlmrtcclient.js

@ -0,0 +1,138 @@
<template>
<!-- webrtc播放器 -->
<video :id="videoId"
ref="jswebrtc"
:controls="controls"
style="width: 100%; height: 100%;margin: 5px;"
muted></video>
</template>
<script>
export default {
name: 'webrtcPlayer',
props: {
videoId: {
type: String,
default: 'jswebrtc'
},
videoSrc: {
type: String,
default: ''
},
controls: {
type: Boolean,
default: true
}
},
data () {
return {
player: null
}
},
mounted () {
this.$watch('videoSrc', () => {
//console.log('videoSrc', this.videoSrc)
if (this.videoSrc) {
this.initVideo(this.videoSrc);
} else {
this.stop()
}
}, { immediate: true, deep: true })
},
methods: {
initVideo (url) {
// console.log('', this.player)
//
if (this.player) {
this.player.pc.close()
this.player = null;
}
let videoDom = document.getElementById(this.videoId);
this.player = new ZLMRTCClient.Endpoint({
element: videoDom, // video
debug: true, //
zlmsdpUrl: url, //
simulcast: true,
useCamera: true,
audioEnable: true,
videoEnable: true,
recvOnly: true,
resolution: {
w: 600,
h: 340
},
usedatachannel: true,
});
this.player.on(ZLMRTCClient.Events.WEBRTC_ICE_CANDIDATE_ERROR, function (e) { // ICE
console.log('ICE 协商出错')
});
this.player.on(ZLMRTCClient.Events.WEBRTC_ON_REMOTE_STREAMS, function (e) { //
console.log('播放成功', e.streams)
videoDom.addEventListener('canplay', function (e) {
videoDom.play();
})
});
this.player.on(ZLMRTCClient.Events.WEBRTC_OFFER_ANWSER_EXCHANGE_FAILED, function (e) { // offer anwser
console.log('offer anwser 交换失败', e)
// this.player.destroy();
// this.player = null;
});
this.player.on(ZLMRTCClient.Events.WEBRTC_ON_LOCAL_STREAM, function (s) { //
console.log('offer anwser 交换失败', e)
});
this.player.on(ZLMRTCClient.Events.CAPTURE_STREAM_FAILED, function (s) { //
console.log('获取本地流失败')
});
this.player.on(ZLMRTCClient.Events.WEBRTC_ON_CONNECTION_STATE_CHANGE, function (
state
) { // RTC , https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/connectionState
console.log('当前状态==>', state)
});
this.player.on(ZLMRTCClient.Events.WEBRTC_ON_DATA_CHANNEL_OPEN, function (event) {
console.log('rtc datachannel 打开 :', event)
});
this.player.on(ZLMRTCClient.Events.WEBRTC_ON_DATA_CHANNEL_MSG, function (event) {
console.log('rtc datachannel 消息 :', event.data)
});
this.player.on(ZLMRTCClient.Events.WEBRTC_ON_DATA_CHANNEL_ERR, function (event) {
console.log('rtc datachannel 错误 :', event)
});
this.player.on(ZLMRTCClient.Events.WEBRTC_ON_DATA_CHANNEL_CLOSE, function (event) {
console.log('rtc datachannel 关闭 :', event)
});
},
stop () {
let videoDom = document.getElementById(this.videoId);
videoDom.pause()
//
this.player.pc.close()
this.player = null
}
},
beforeDestroy () {
if (this.player) {
//
this.player.pc.close()
this.player = null;
}
}
}
</script>
<style>
</style>

@ -25,6 +25,11 @@ export default {
url: "/street/list",
name: "获取全部巷道列表"
},
cameraRecord: {
method: "POST",
url: "/cameraRecord/record",
name: "球机回放信息"
},
addStreet: {
method: "POST",
url: "/street",

@ -93,6 +93,15 @@ const routes = [{
},
component: () => import('@/views/historyCheck/index'),
},
{
path: 'videoPlayback',
name: 'videoPlayback',
meta: {
icon: 'tag',
name: '记录回放',
},
component: () => import('@/views/videoPlayback/index'),
},
// {
// path: 'stockHistory',
// name: 'stockHistory',

@ -102,11 +102,18 @@
:centered="true"
class="config-model"
>
<video width="900"
<WebrtcPlayer
autoplay class="video-window"
:videoSrc="'http://127.0.0.1/index/api/webrtc?app=live&stream='+id+'&type=play'"
:videoId="'videoId'+colIndex+'-'+rowIndex"
ref="videoWindow"
:key="colIndex+'-'+rowIndex"
muted :style="{ width:'900px'}">
</WebrtcPlayer>
<!-- <video width="900"
height="600"
:id="`config-camera${id}`"
autoplay muted ></video>
autoplay muted ></video> -->
<div class="operation-list">
<div class="operation-item">
<img :src="zoomSubUrl" alt="" @mousedown="zoomDecStart"
@ -202,9 +209,12 @@ const columns = [
scopedSlots: { customRender: 'position' }
}
];
import WebRtcPlayer from "../../../public/static/webrtcplayer"
import WebrtcPlayer from '../../../public/webrtc/webrtcPlayer.vue'
//import WebRtcPlayer from "../../../public/static/webrtcplayer"
export default {
props:[ 'visible', 'modelType', 'modelData'],
components: {WebrtcPlayer},
watch: {
//visibleisShowprops
visible: function(newVal){
@ -857,7 +867,7 @@ export default {
}
.config-table {
width: 800px;
height: 600px;
height: 500px;
overflow: hidden;
h2 {

@ -154,7 +154,7 @@ export default {
handleChange(value) {
this.select = value
console.log("1654asdfasf"+value);
this.getStreetDetail(value)
},

@ -5,7 +5,7 @@
<a-row :gutter="24">
<a-col :span="4">
<a-form-item label="巷道">
<a-select @change="handleChange" :value="select" style="width:100px">
<a-select @change="handleChange" v-model="select" style="width:100px">
<a-select-option v-for="i in listData" :key="i.name" :value="i.id">
{{i.name}}
</a-select-option>
@ -41,11 +41,8 @@
<a-select-option :key=2>
</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :span="2" style="text-align: left">
<a-form-item label="行号">
@ -71,8 +68,7 @@
:show-time="{
defaultValue: [moment('00:00', 'HH:mm'), moment('23:59', 'HH:mm')],
//defaultValue: [moment('00:00', 'YYYY-MM-DD HH:mm'), moment('23:59', 'YYYY-MM-DD HH:mm')],
}"
>
}">
<a-icon slot="suffixIcon" type="calendar"/>
</a-range-picker>
</a-form-item>

@ -3,6 +3,16 @@
<div class="ant-advanced-search-form">
<a-form layout="inline" :form="queryParam">
<a-row :gutter="24">
<a-col :span="4">
<a-form-item label="巷道">
<a-select @change="handleChange" style="width:150px" v-model="streetName">
<a-select-option v-for="i in listData" :key="i.name" :value="i.id">
{{i.name}}
</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :span="6" style="text-align: left">
<a-form-item label="工单号">
<a-input v-model="queryParam.orderNum" placeholder="请输入" style="width:270px;"/>
@ -23,7 +33,7 @@
</a-range-picker>
</a-form-item>
</a-col>
<a-col :span="10" style="text-align: right">
<a-col :span="5" style="text-align: right">
<a-button type="primary" @click="handleSearch"></a-button>
<a-button style="margin-left: 15px" @click="reset"></a-button>
</a-col>
@ -95,9 +105,12 @@ export default {
data() {
return {
queryParam: {
streetId: '',
orderNum: ''
},
streetName: '',
time:[],
listData:[],
pageNum: 1,
pageSize: 10,
data: [],
@ -163,6 +176,8 @@ export default {
}
},
mounted() {
this.getStreetList();
this.handleGetHistoryList()
console.log(this.imgUrl)
if (this.$route.params.orderNum) {
@ -172,6 +187,23 @@ export default {
},
methods: {
moment,
//
getStreetList() {
this.$api.httpApi.getAllStreet({
}).then(res => {
this.listData = res.data;
}).catch(err => {
console.error(err);
});
},
handleChange(value) {
this.queryParam.streetId = value
this.streetName = value
this.getStreetDetail(value)
},
range(start, end) {
const result = [];
for (let i = start; i < end; i++) {

@ -114,5 +114,7 @@ export default {
.ant-table td {
white-space: nowrap;
}
}
</style>

@ -1,6 +1,7 @@
<template>
<div class="realTime bg-white">
<a-tabs default-active-key="1" slot="headerContent" v-model="tabKey" @change="tabsChange" padding:10px>
<div class="realTime bg-white" style="width: 81.5VW">
<a-tabs default-active-key="1" slot="headerContent" v-model="tabKey"
@change="tabsChange" padding:10px>
<a-tab-pane :key="item.streetId.toString()" :tab="item.streetName" v-for="item in realTimeListData">
</a-tab-pane>
@ -12,13 +13,20 @@
:key="index"
:style="{
height: videoHeight,
marginBottom: ((selectTab.videoStyleRow * selectTab.videoStyleColumn - index - 1) >= selectTab.videoStyleColumn) ? '20px': 0
}"
>
<div style="position: relative;width: 100%;">
<video :style="{ height: videoHeight,}" :id="`camera${item.id}`" autoplay muted controls>
</video>
<WebrtcPlayer
autoplay class="video-window"
:videoSrc="'http://127.0.0.1/index/api/webrtc?app=live&stream='+item.id+'&type=play'"
:videoId="'videoId'+colIndex+'-'+rowIndex"
ref="videoWindow"
:key="colIndex+'-'+rowIndex"
muted :style="{ height: videoHeight,}">
</WebrtcPlayer>
<!-- <video :style="{ height: videoHeight,}" :id="`camera${item.id}`" autoplay muted controls>
</video> -->
<div class="operation-list">
<div class="operation-item">
<img :src="zoomSubUrl" alt="" @mousedown=zoomDecStart($event,item.id) @mouseleave=zoomDecStop($event,item.id)
@ -69,12 +77,12 @@
<script>
import WebRtcPlayer from "../../../public/static/webrtcplayer"
//import WebRtcPlayer from "../../../public/static/webrtcplayer"
import WebrtcPlayer from '../../../public/webrtc/webrtcPlayer.vue'
export default {
name: "realTimeMonitoring",
components: {
//VideoPlayer
},
components: {WebrtcPlayer},
data() {
return {
realTimeListData: [],
@ -108,6 +116,7 @@ export default {
return (this.realTimeListData.filter(item=> item.streetId == this.tabKey)[0] || {})
},
cameras() {
console.log("cameras" +this.selectTab);
return this.selectTab.cameras || [];
},
videoHeight() {

@ -0,0 +1,409 @@
<template>
<a-row type="flex" :gutter="30">
<a-col :flex="1">
时间<a-date-picker v-model="date" format="YYYY-MM-DD" placeholder="请选择时间" @change="cameraRecord"/>
<section style="height: 80vh;overflow: hidden;
overflow-y: scroll;">
<a-tree
:tree-data="treeData"
:check-strictly="true"
@select="handleCheck">
</a-tree>
</section>
</a-col>
<a-col :flex="13">
<div>
<div class="time-display">
<!-- 视频 -->
<video ref="videoPlayer" muted style=" display: flex;" width="100%" >
<source :src="currentVideoUrl" type="video/mp4" />
</video>
</div>
<!-- 滑动轴 -->
<div class="zoom-buttons" width="100%" >
<a-button-group>
<a-button icon="arrows-alt" :disabled="zoom" @click="handleZoomIn"></a-button>
<a-button icon="shrink" :disabled="shrink" @click="handleZoomOut"></a-button>
</a-button-group>
</div>
<a-slider
ref="slider"
:marks="marks"
class="progress-bar"
:max="totalDuration"
:tip-formatter="formatter"
:value="currentTime"
:disabled="Object.keys(urls).length === 0"
@change="onSliderChange"
/>
<div class="time-display">
<a-button-group>
<a-button icon="step-backward" :disabled="Object.keys(urls).length === 0" @click="moveForward(-5)" />
<a-button v-if="isPlaying" :disabled="Object.keys(urls).length === 0" icon="pause" @click="pauseVideo" />
<a-button v-else icon="play-circle" :disabled="Object.keys(urls).length === 0" @click="playVideo" />
<a-button icon="step-forward" :disabled="Object.keys(urls).length === 0" @click="moveForward(5)" />
</a-button-group>
</div>
</div>
</a-col>
</a-row>
</template>
<script>
import { Slider, Button, ButtonGroup } from 'ant-design-vue';
import moment from 'moment'
import { message } from 'ant-design-vue';
export default {
components: {
ASlider: Slider,
AButton: Button,
AButtonGroup: ButtonGroup,
},
data() {
return {
totalDuration: 24*60*60, // 60s
//
currentTime: 0,
//
startTimeLong: 0,
//
endTimeLong: 24*60*60,
//
maxTimeLong:24*60*60,
//
minTimeLong: 0*60*60,
date: this.getYesterday() ,
currentVideoUrl: '', // URL
value1: '2023-10-11',
urls:{
},
URLStartTime: 0,
isPlaying: false, //
marks:{},
zoom:false,
shrink:true,
treeData: [],
cameraId:'',
};
},
mounted() {
const video = this.$refs.videoPlayer;
video.addEventListener('timeupdate', this.onVideoTimeUpdate);
video.addEventListener('ended', this.playNextVideo);
console.log(this.getTimeMap());
this.totalDuration = this.endTimeLong -this.startTimeLong;
this.marks = this.getTimeMap();
console.log(this.date);
this.getTree()
this.cameraRecord()
},
beforeDestroy() {
const video = this.$refs.videoPlayer;
video.removeEventListener('timeupdate', this.onVideoTimeUpdate);
video.removeEventListener('ended', this.playNextVideo);
},
methods: {
getYesterday(){
const now = moment();
return now.subtract(1, 'days')
},
scaleTime(rate) {
const middle = this.startTimeLong +this.currentTime;
//
const startToMiddleMs = middle - this.startTimeLong;
//
const endToMiddleMs = this.endTimeLong - middle;
//
const scaledStart = middle -Math.floor( startToMiddleMs * rate);
console.log("开始缩小时间"+ scaledStart);
//
const scaledEnd =middle +Math.floor( endToMiddleMs * rate);
console.log("结束缩小时间"+ scaledEnd);
//
this.startTimeLong = this.minTimeLong>scaledStart?this.minTimeLong : scaledStart;
this.endTimeLong = this.maxTimeLong<scaledEnd?this.maxTimeLong : scaledEnd;
//
if(this.minTimeLong<scaledStart || this.maxTimeLong>scaledEnd){
this.shrink=false;
}else{
this.shrink=true;
}
console.log("zhou"+middle);
this.updateslider(middle)
},
formatTime(seconds) {
seconds = seconds%(60*60*24);
const hour = Math.floor(seconds / 3600);
const minute = Math.floor((seconds % 3600) / 60);
const second = seconds % 60;
return `${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}:${second.toString().padStart(2, '0')}`;
},
//
handleZoomIn() {
this.scaleTime(0.5)
},
//
handleZoomOut() {
this.scaleTime(2)
},
//
updateslider(middle){
this.totalDuration = this.endTimeLong - this.startTimeLong;
console.log("轴上数值为:"+(middle-this.startTimeLong));
this.currentTime = (middle-this.startTimeLong);
this.marks = this.getTimeMap();
},
getTree(){
this.$api.httpApi.getAllStreet({
}).then(res => {
console.log("update data")
for(let i = 0;i<res.data.length;i++){
//
const obj = {};
//
obj["title"] = res.data[i].name;
obj["key"] = res.data[i].id;
obj["type"] = 0
obj["children"] =[];
if( res.data[i].camera1Id !=""){
const obj1 = {};
obj1["title"] = res.data[i].camera1Name
obj1["key"] = res.data[i].camera1Id
obj1["type"] = 1
obj["children"].push(obj1)
}
if( res.data[i].camera2Id !=""){
const obj1 = {};
obj1["title"] = res.data[i].camera2Name
obj1["key"] = res.data[i].camera2Id
obj1["type"] = 1
obj["children"].push(obj1)
}
this.treeData.push(obj)
}
console.log(this.treeData);
}).catch(err => {
});
},
getTimeMap() {
const diff = this.endTimeLong - this.startTimeLong; //
console.log(diff + " qqq");
const map = {};
if (diff >= 60 * 60 * 3) {
this.TimeMapErgodic(60*60,map);
} else if (diff >= 60 * 60) {
// 1315
this.TimeMapErgodic(15*60,map);
} else if (diff >= 60*20) {
this.zoom = false;
// 2015
this.TimeMapErgodic(5*60,map);
} else if (diff >= 5* 60) {
this.zoom = false;
// 520
this.TimeMapErgodic(60,map);
} else if (diff >= 30) {
this.zoom = false;
// 51m30s
console.log("如果小于5分钟大于1m每30s一个标识");
console.log(startTime +" "+endTime)
this.TimeMapErgodic(30,map);
} else if (diff >= 10) {
this.zoom = true;
// 1m10s 5s
this.TimeMapErgodic(5,map);
} else {
this.zoom = true;
// 10s 1s
this.TimeMapErgodic(1,map);
}
return map;
},
//time
TimeMapErgodic(interval,map){
const startTime =Math.ceil(this.startTimeLong/(interval)) *interval;
const endTime =Math.floor(this.endTimeLong/(interval))*interval;
for (let i = startTime ; i<= endTime; i+=interval) {
const minuteDiff = (i - this.startTimeLong) ;
map[minuteDiff] = `${String(Math.floor(i/(60*60))).padStart(2, '0')}:${String(Math.floor((i%(60*60))/(60))).padStart(2, '0')}:${String((i%(60))).padStart(2, '0')}`;
}
return map;
},
//
formatter(value) {
const i = this.startTimeLong + value
//
const hours = String(Math.floor(i/(60*60))).padStart(2, '0');
const minutes = String(Math.floor((i%(60*60))/(60))).padStart(2, '0');
const seconds = String(Math.floor((i%(60)))).padStart(2, '0');
return `${hours}:${minutes}:${seconds}`;
},
//
binarySearchMax(arr, target) {
let left = 0;
let right = arr.length - 1;
let max = -Infinity;
console.log(arr);
while (left <= right) {
const mid = Math.floor((left + right) / 2);
if (arr[mid] <= target) {
max = Math.max(max, arr[mid]);
left = mid + 1;
} else {
right = mid - 1;
}
}
return max;
},
//
onSliderChange(value) {
console.log(value);
this.URLStartTime = this.binarySearchMax(Object.keys(this.urls),value+this.minTimeLong);
this.currentVideoUrl = this.urls[this.URLStartTime]
this.currentTime = value;
const video = this.$refs.videoPlayer;
video.currentTime = value +this.startTimeLong-this.URLStartTime ;
},
//
onVideoTimeUpdate() {
const video = this.$refs.videoPlayer;
console.log("视频时间"+(this.URLStartTime + video.currentTime));
this.currentTime =this.URLStartTime + video.currentTime-this.startTimeLong;
},
//
moveForward(duration) {
console.log("111");
const newTime = this.currentTime + duration;
if (newTime >= 0 ||newTime <= this.totalDuration) {
this.currentTime = newTime;
const video = this.$refs.videoPlayer;
video.currentTime = newTime;
}
},
playVideo() {
const video = this.$refs.videoPlayer;
this.isPlaying = true;
console.log( this.currentTime +this.startTimeLong - this.URLStartTime);
video.currentTime = this.currentTime +this.startTimeLong - this.URLStartTime
video.play();
},
pauseVideo() {
const video = this.$refs.videoPlayer;
this.isPlaying = false;
video.pause();
},
playNextVideo() {
let nextKey =this.URLStartTime;
const keys = Object.keys(this.urls);
//
const currentIndex = keys.indexOf(this.URLStartTime.toString());
console.log(keys);
if (currentIndex !== -1 && currentIndex < keys.length - 1) {
nextKey = Number(keys[currentIndex + 1]);
const video = this.$refs.videoPlayer;
this.URLStartTime = nextKey;
this.currentVideoUrl = this.urls[nextKey] ;
this.currentTime =this.URLStartTime;
video.currentTime =0;
video.play();
}
},
handleCheck(selectedKeys, e){
if(e.selectedNodes[0].data.props.type == 1){
this.cameraId = e.selectedNodes[0].data.key;
console.log(e.selectedNodes[0].data);
this.cameraRecord()
}
console.log(e.selectedNodes[0].data.props);
},
cameraRecord(){
this.$api.httpApi.cameraRecord({
data: {
date: this.date._d.getFullYear() + '-' + (this.date._d.getMonth() + 1) + '-' + this.date._d.getDate(),
cameraId: this.cameraId,
}
}).then(res => {
console.log("update data")
if( (res.data.startTimeLong !== null && res.data.startTimeLong !== undefined && res.data.startTimeLong !== "")
||( res.data.endTimeLong !== null && res.data.endTimeLong!== undefined &&res.data.endTimeLong !== '')){
}
if(res.data.urls !== null && res.data.urls !== undefined && Object.keys(res.data.urls).length >0){
this.startTimeLong = res.data.startTimeLong
this.endTimeLong = res.data.endTimeLong
this.minTimeLong = res.data.startTimeLong
this.maxTimeLong = res.data.endTimeLong
this.currentTime = 0
this.totalDuration = res.data.endTimeLong - res.data.startTimeLong
this.currentVideoUrl = res.data.urls[0].url
const video = this.$refs.videoPlayer;
video.currentTime = 0 ;
const map = {};
for(let i = 0;i<res.data.urls.length;i++){
map[res.data.urls[i].startTimeLong] = res.data.urls[i].url
}
this.urls = map
this.marks = this.getTimeMap();
}else{
if(this.cameraId !== null && this.cameraId !== undefined && this.cameraId !== ""){
message.info('此摄像头该日期未录像');
}
}
}).catch(err => {
console.log(err);
});
}
},
};
</script>
<style>
.progress-bar .ant-slider-rail {
background-color: lightgray;
}
.progress-bar .ant-slider-track {
background-color: dodgerblue;
}
.time-display {
margin-top: 10px;
display: flex;
justify-content: center;
align-items: center;
}
.zoom-buttons {
text-align: right;
margin-bottom: 16px;
}
</style>

@ -1,17 +1,64 @@
<template>
<div id="videos">
<a-tree-select
v-model="value"
show-search
style="width: 80%"
:dropdown-style="{ maxHeight: '400px', overflow: 'auto' }"
placeholder="Please select"
allow-clear
multiple
tree-default-expand-all
>
<a-tree-node key="0-1" value="parent 1" title="parent 1">
<a-tree-node key="0-1-1" :selectable="false" value="parent 1-0" title="parent 1-0">
<a-tree-node key="random" value="leaf1" title="my leaf" />
<a-tree-node key="random1" value="leaf2" title="your leaf" />
</a-tree-node>
<a-tree-node key="random2" value="parent 1-1" title="parent 1-1">
<a-tree-node key="random3" value="sss">
<b slot="title" style="color: #08c">sss</b>
</a-tree-node>
</a-tree-node>
</a-tree-node>
<a-tree-node key="random2" value="leaqf1" title="my 1leaf" />
<a-tree-node key="random13" value="leawf2" title="your2 leaf" />
<a-tree-node key="random4" value="leaef1" title="my l3eaf" />
<a-tree-node key="random15" value="learf2" title="your4 leaf" />
<a-tree-node key="random6" value="leatf1" title="my 6leaf" />
<a-tree-node key="random17" value="leayuf2" title="you5r leaf" />
<a-tree-node key="random8" value="leauf1" title="my 7leaf" />
<a-tree-node key="random19" value="loeaf2" title="yousr leaf" />
<a-tree-node key="random0" value="lieaf1" title="myf leaf" />
<a-tree-node key="randoma1" value="lexaf2" title="yodur leaf" />
<a-tree-node key="randoms" value="leacf1" title="my cleaf" />
<a-tree-node key="randoms1" value="leavf2" title="yobur leaf" />
<a-tree-node key="randomd" value="lnaf1" title="mvy leaf" />
<a-tree-node key="random1f" value="lebaf2" title="y our leaf" />
<a-tree-node key="randomg" value="leamf1" title="myx leaf" />
<a-tree-node key="random1h" value="leaff2" title="your leaf" />
<a-tree-node key="randomz" value="wleaf2" title="your leaf" />
<a-tree-node key="randomc" value="wleaf1" title="my cleaf" />
<a-tree-node key="random1v" value="eleaf2" title="your levaf" />
</a-tree-select>
<a-button type="primary" @click="full" v-if="!isFullscreen"> <a-icon type="right" /> </a-button>
<a-button type="primary" @click="full" v-if="isFullscreen"><a-icon type="left" /> 退出全屏 </a-button>
<!-- @change="tabsChange" v-model="tabKey"-->
<a-tabs v-model="activeKey" slot="headerContent" @change="tabChange" v-if="areas" padding:10px>
<a-tab-pane :key="item.toString()" :tab="item" v-for="item in areas">
</a-tab-pane>
</a-tabs>
<a-row v-for='rowIndex in row' :key='rowIndex'>
<a-col v-for='colIndex in column' :key='colIndex' :span="24/column" :style="{height: videoHeight}" >
<video class="camera" :id="rowIndex+'-'+colIndex" autoplay muted :style="{'height': '100%',width:'100%','object-fit':'fill'}" ></video>
<WebrtcPlayer
v-if=" webrtcPlayers[rowIndex-1] !==undefined && webrtcPlayers[rowIndex-1] != null && webrtcPlayers[rowIndex-1].length > 0 && webrtcPlayers[rowIndex-1][colIndex-1] !==undefined && webrtcPlayers[rowIndex-1][colIndex-1] != null "
autoplay class="video-window"
:videoSrc="'http://127.0.0.1/index/api/webrtc?app=live&stream='+webrtcPlayers[rowIndex-1][colIndex-1].id+'&type=play'"
:videoId="'videoId'+colIndex+'-'+rowIndex"
ref="videoWindow"
:key="colIndex+'-'+rowIndex"
muted
:style="{'height': '100%',width:'100%','object-fit':'fill'}">
</WebrtcPlayer>
<!-- <video class="camera" :id="rowIndex+'-'+colIndex" autoplay muted :style="{'height': '100%',width:'100%','object-fit':'fill'}" ></video> -->
</a-col>
</a-row>
@ -19,10 +66,17 @@
</template>
<script>
import WebRtcPlayer from "../../../public/static/webrtcplayer"
//import ATreeSelect from "../../../public/static/webrtcplayer"
import { TreeSelect } from 'ant-design-vue';
import { defineComponent, ref, watch } from 'vue';
import WebrtcPlayer from '../../../public/webrtc/webrtcPlayer.vue'
export default {
name: 'top',
components: {},
components: {
WebrtcPlayer,
ATreeSelect: TreeSelect
},
props: {
id: {
type: String,
@ -35,6 +89,8 @@ export default {
return {
row : 0,
column : 0,
value: undefined,
treeExpandedKeys: [],
a: 0,
clientHeight: 0,
videoH: 0,
@ -43,6 +99,7 @@ export default {
fullHeight: 0,
players: [],
areas:[],
webrtcPlayers: [],
activeKey: ''
}
},
@ -60,7 +117,7 @@ export default {
this.$nextTick(() => {
this.getAllAreas()
this.getAllCameras()
});
let isFullscreen =
document.fullscreenElement ||
@ -112,6 +169,7 @@ export default {
}).then(res => {
if(res.code == 200) {
let cameras = res.data;
this.webrtcPlayers = Array.from({ length: this.row }, () => []);
for(let i = 1;i<=cameras.length;i++){
let rowIndex = Math.floor((i-1) / this.column) + 1;
if(rowIndex > this.row){
@ -121,12 +179,14 @@ export default {
if(columnIndex == 0){
columnIndex = this.column;
}
this.webrtcPlayers[rowIndex-1].push(cameras[i-1]);
let idName = rowIndex + "-" + columnIndex;
console.log("idName:"+idName);
let server = cameras[i-1].rtcServer+":"+ cameras[i-1].rtcServerPort
let video = document.getElementById(idName);
let player = new WebRtcPlayer(server,video,"camera"+cameras[i-1].id);
this.players.push(player);
//console.log(this.webrtcPlayers);
//let server = cameras[i-1].rtcServer+":"+ cameras[i-1].rtcServerPort
//let video = document.getElementById(idName);
//let player = new WebRtcPlayer(server,video,"camera"+cameras[i-1].id);
//this.players.push(player);
}
}
}).catch(err => {

Loading…
Cancel
Save