pom.xml | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/main/java/com/genersoft/iot/vmp/gb28181/event/offline/OfflineEventListener.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/MessageRequestProcessor.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMUtils.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/main/resources/application.yml | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
web_src/index.html | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
web_src/src/components/channelList.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
web_src/src/components/gb28181/devicePlayer.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
pom.xml
@@ -1,5 +1,5 @@ <?xml version="1.0"?> <project <project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <modelVersion>4.0.0</modelVersion> @@ -12,7 +12,7 @@ <groupId>com.genersoft</groupId> <artifactId>wvp</artifactId> <name>web video platform</name> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> @@ -42,15 +42,15 @@ <artifactId>spring-boot-starter-tomcat</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> </dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> </dependency> <!-- druid --> <dependency> <groupId>com.alibaba</groupId> @@ -62,7 +62,7 @@ <artifactId>mysql-connector-java</artifactId> <version>5.1.30</version> </dependency> <!--Mybatis --> <dependency> <groupId>org.mybatis</groupId> @@ -74,7 +74,7 @@ <artifactId>mybatis-spring</artifactId> <version>${mybatis.spring.version}</version> </dependency> <!--分页插件 --> <dependency> <groupId>com.github.pagehelper</groupId> @@ -99,7 +99,7 @@ <artifactId>fastjson</artifactId> <version>1.2.33</version> </dependency> <!--Swagger2 --> <!--在线文档 --> <dependency> @@ -112,13 +112,13 @@ <artifactId>springfox-swagger-ui</artifactId> <version>2.6.1</version> </dependency> <!-- 日志相关 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <dependency> <groupId>javax.sip</groupId> <artifactId>jain-sip-ri</artifactId> src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java
@@ -103,7 +103,7 @@ ListeningPoint udpListeningPoint = sipStack.createListeningPoint(sipConfig.getSipIp(), sipConfig.getSipPort(), "UDP"); SipProvider udpSipProvider = sipStack.createSipProvider(udpListeningPoint); udpSipProvider.addSipListener(this); logger.info("Sip Server TCP 启动成功 port {" + sipConfig.getSipPort() + "}"); logger.info("Sip Server UDP 启动成功 port {" + sipConfig.getSipPort() + "}"); return udpSipProvider; } src/main/java/com/genersoft/iot/vmp/gb28181/event/offline/OfflineEventListener.java
@@ -54,7 +54,5 @@ // 处理离线监听 storager.outline(event.getDeviceId()); // } } src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/MessageRequestProcessor.java
@@ -203,7 +203,7 @@ deviceChannel.setLongitude(itemDevice.element("Longitude") == null? 0.00:Double.parseDouble(XmlUtil.getText(itemDevice,"Longitude"))); deviceChannel.setLatitude(itemDevice.element("Latitude") == null? 0.00:Double.parseDouble(XmlUtil.getText(itemDevice,"Latitude"))); deviceChannel.setPTZType(itemDevice.element("PTZType") == null? 0:Integer.parseInt(XmlUtil.getText(itemDevice,"PTZType"))); deviceChannel.setHasAudio(false); // 默认含有音频为false deviceChannel.setHasAudio(true); // 默认含有音频,播放时再检查是否有音频及是否AAC storager.updateChannel(device.getDeviceId(), deviceChannel); } src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMUtils.java
@@ -36,7 +36,7 @@ System.out.println(newPort); System.out.println(jsonObject.toJSONString()); return newPort; }else { } else { return getNewRTPPort(ssrc); } } @@ -48,11 +48,14 @@ udpPortRangeArray[1] = Integer.parseInt(udpPortRangeStrArray[1]); } if (currentPort == 0 || currentPort ++ > udpPortRangeArray[1]) { if (currentPort == 0 || currentPort++ > udpPortRangeArray[1]) { currentPort = udpPortRangeArray[0]; return udpPortRangeArray[0]; }else { return currentPort ++; } else { if (currentPort % 2 == 1) { currentPort++; } return currentPort++; } } } src/main/resources/application.yml
@@ -46,6 +46,6 @@ streamNoneReaderDelayMS: 1800000 # 无人观看多久自动关闭流 rtp: # 启用udp多端口模式 enable: true udpPortRange: 30000,300500 # 端口范围 udpPortRange: 30000,30500 # 端口范围 web_src/index.html
@@ -3,7 +3,7 @@ <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width,initial-scale=1.0"> <title>gb_web</title> <title>GB28181服务器</title> </head> <body> <script type="text/javascript" src="./js/liveplayer-lib.min.js"></script> web_src/src/components/channelList.vue
@@ -1,359 +1,346 @@ <template> <div id="channelList"> <el-container> <div id="channelList"> <el-container> <el-header> <uiHeader></uiHeader> </el-header> <el-main> <div style="background-color: #FFFFFF; position: relative; padding: 1rem 0.5rem 0.5rem 0.5rem; text-align: center;"> <span style="font-size: 1rem; font-weight: 500; ">通道列表({{parentChannelId ==0 ? deviceId:parentChannelId}})</span> <el-header> <uiHeader></uiHeader> </el-header> <el-main> <div style="background-color: #FFFFFF; position: relative; padding: 1rem 0.5rem 0.5rem 0.5rem; text-align: center;"> <span style="font-size: 1rem; font-weight: 500; ">通道列表({{parentChannelId ==0 ? deviceId:parentChannelId}})</span> </div> <div style="background-color: #FFFFFF; margin-bottom: 1rem; position: relative; padding: 0.5rem; text-align: left;font-size: 14px;"> <el-button icon="el-icon-arrow-left" size="mini" style="margin-right: 1rem;" @click="showDevice">返回</el-button> 搜索: <el-input @input="search" style="margin-right: 1rem; width: auto;" size="mini" placeholder="关键字" prefix-icon="el-icon-search" v-model="searchSrt" clearable> </el-input> </div> <div style="background-color: #FFFFFF; margin-bottom: 1rem; position: relative; padding: 0.5rem; text-align: left;font-size: 14px;"> <el-button icon="el-icon-arrow-left" size="mini" style="margin-right: 1rem;" type="primary" @click="showDevice">返回</el-button> 搜索: <el-input @input="search" style="margin-right: 1rem; width: auto;" size="mini" placeholder="关键字" prefix-icon="el-icon-search" v-model="searchSrt" clearable> </el-input> 通道类型: <el-select size="mini" @change="search" style="margin-right: 1rem;" v-model="channelType" placeholder="请选择" default-first-option> <el-option label="全部" value="" ></el-option> <el-option label="设备" value="false"></el-option> <el-option label="子目录" value="true" ></el-option> </el-select> 在线状态: <el-select size="mini" @change="search" v-model="online" placeholder="请选择" default-first-option> <el-option label="全部" value=""></el-option> <el-option label="在线" value="on"></el-option> <el-option label="离线" value="off"></el-option> </el-select> 通道类型: <el-select size="mini" @change="search" style="margin-right: 1rem;" v-model="channelType" placeholder="请选择" default-first-option> <el-option label="全部" value=""></el-option> <el-option label="设备" value="false"></el-option> <el-option label="子目录" value="true"></el-option> </el-select> 在线状态: <el-select size="mini" @change="search" v-model="online" placeholder="请选择" default-first-option> <el-option label="全部" value=""></el-option> <el-option label="在线" value="on"></el-option> <el-option label="离线" value="off"></el-option> </el-select> </div> <devicePlayer ref="devicePlayer"></devicePlayer> <!--设备列表--> <el-table ref="channelListTable" :data="deviceChannelList" :height="winHeight" border style="width: 100%"> <el-table-column prop="channelId" label="通道编号" width="210"> </el-table-column> <el-table-column prop="name" label="通道名称"> </el-table-column> <el-table-column prop="subCount" label="子节点数"> </el-table-column> <el-table-column label="开启音频" align="center"> <template slot-scope="scope"> <el-switch @change="updateChannel(scope.row)" v-model="scope.row.hasAudio" active-color="#409EFF"> </el-switch> </template> </el-table-column> <el-table-column label="状态" width="180" align="center"> <template slot-scope="scope"> <div slot="reference" class="name-wrapper"> <el-tag size="medium" v-if="scope.row.status == 1">在线</el-tag> <el-tag size="medium" type="info" v-if="scope.row.status == 0">离线</el-tag> </div> </template> </el-table-column> <el-table-column prop="ptztypeText" label="云台类型"> </el-table-column> <el-table-column label="操作" width="280" align="center" fixed="right"> <template slot-scope="scope"> <el-button-group> <el-button size="mini" icon="el-icon-video-play" v-if="scope.row.parental == 0" @click="sendDevicePush(scope.row)">播放</el-button> <el-button size="mini" icon="el-icon-switch-button" type="danger" v-if="scope.row.play" @click="stopDevicePush(scope.row)">停止</el-button> <el-button size="mini" icon="el-icon-s-open" type="primary" v-if="scope.row.parental == 1" @click="changeSubchannel(scope.row)">查看</el-button> <!-- <el-button size="mini" icon="el-icon-video-camera" type="primary" >设备录象</el-button>--> <!-- <el-button size="mini" @click="sendDevicePush(scope.row)">录像查询</el-button> --> </el-button-group> </template> </el-table-column> </el-table> <el-pagination style="float: right" @size-change="handleSizeChange" @current-change="currentChange" :current-page="currentPage" :page-size="count" :page-sizes="[15, 20, 30, 50]" layout="total, sizes, prev, pager, next" :total="total"> </el-pagination> </div> <devicePlayer ref="devicePlayer"></devicePlayer> <!--设备列表--> <el-table ref="channelListTable" :data="deviceChannelList" :height="winHeight" border style="width: 100%"> <el-table-column prop="channelId" label="通道编号" width="210"> </el-table-column> <el-table-column prop="name" label="通道名称"> </el-table-column> <el-table-column prop="subCount" label="子节点数"> </el-table-column> <el-table-column label="开启音频" align="center"> <template slot-scope="scope"> <el-switch @change="updateChannel(scope.row)" v-model="scope.row.hasAudio" active-color="#409EFF"> </el-switch> </template> </el-table-column> <el-table-column label="状态" width="180" align="center"> <template slot-scope="scope"> <div slot="reference" class="name-wrapper"> <el-tag size="medium" v-if="scope.row.status == 1">在线</el-tag> <el-tag size="medium" type="info" v-if="scope.row.status == 0">离线</el-tag> </div> </template> </el-table-column> <el-table-column prop="ptztypeText" label="云台类型"> </el-table-column> <el-table-column label="操作" width="280" align="center" fixed="right"> <template slot-scope="scope"> <el-button-group> <el-button size="mini" icon="el-icon-video-play" v-if="scope.row.parental == 0" @click="sendDevicePush(scope.row)">播放</el-button> <el-button size="mini" icon="el-icon-switch-button" type="danger" v-if="scope.row.play" @click="stopDevicePush(scope.row)">停止</el-button> <el-button size="mini" icon="el-icon-s-open" type="primary" v-if="scope.row.parental == 1" @click="changeSubchannel(scope.row)">查看</el-button> <!-- <el-button size="mini" icon="el-icon-video-camera" type="primary" >设备录象</el-button>--> <!-- <el-button size="mini" @click="sendDevicePush(scope.row)">录像查询</el-button> --> </el-button-group> </template> </el-table-column> </el-table> <el-pagination style="float: right" @size-change="handleSizeChange" @current-change="currentChange" :current-page="currentPage" :page-size="count" :page-sizes="[15, 20, 30, 50]" layout="total, sizes, prev, pager, next" :total="total"> </el-pagination> </el-main> </el-container> </el-main> </el-container> <Loading v-if="isLoging" marginTop="-50%"></Loading> </div> </div> </template> <script> import devicePlayer from './gb28181/devicePlayer.vue' import uiHeader from './UiHeader.vue' import Loading from './Loading.vue' export default { name: 'channelList', components: { devicePlayer, uiHeader, Loading }, data() { return { deviceId: this.$route.params.deviceId, parentChannelId: this.$route.params.parentChannelId, deviceChannelList: [], videoComponentList: [], currentPlayerInfo: {}, //当前播放对象 updateLooper: 0, //数据刷新轮训标志 searchSrt: "", channelType: "", online: "", winHeight: window.innerHeight - 250, currentPage: parseInt(this.$route.params.page), count: parseInt(this.$route.params.count), total:0, beforeUrl:"/videoList", isLoging: false }; }, import devicePlayer from './gb28181/devicePlayer.vue' import uiHeader from './UiHeader.vue' import Loading from './Loading.vue' export default { name: 'channelList', components: { devicePlayer, uiHeader, Loading }, data() { return { deviceId: this.$route.params.deviceId, parentChannelId: this.$route.params.parentChannelId, deviceChannelList: [], videoComponentList: [], currentPlayerInfo: {}, //当前播放对象 updateLooper: 0, //数据刷新轮训标志 searchSrt: "", channelType: "", online: "", winHeight: window.innerHeight - 250, currentPage: parseInt(this.$route.params.page), count: parseInt(this.$route.params.count), total: 0, beforeUrl: "/videoList", isLoging: false }; }, mounted() { this.initData(); // this.updateLooper = setInterval(this.initData, 10000); }, destroyed() { this.$destroy('videojs'); clearTimeout(this.updateLooper); }, methods: { initData: function() { if (this.parentChannelId == "" || this.parentChannelId == 0 ) { this.getDeviceChannelList(); }else{ this.showSubchannels(); } mounted() { this.initData(); // this.updateLooper = setInterval(this.initData, 10000); }, destroyed() { this.$destroy('videojs'); clearTimeout(this.updateLooper); }, methods: { initData: function () { if (this.parentChannelId == "" || this.parentChannelId == 0) { this.getDeviceChannelList(); } else { this.showSubchannels(); } }, initParam: function(){ this.deviceId= this.$route.params.deviceId; this.parentChannelId= this.$route.params.parentChannelId; this.currentPage= parseInt(this.$route.params.page); this.count= parseInt(this.$route.params.count); if (this.parentChannelId == "" || this.parentChannelId == 0 ) { this.beforeUrl = "/videoList" } }, initParam: function () { this.deviceId = this.$route.params.deviceId; this.parentChannelId = this.$route.params.parentChannelId; this.currentPage = parseInt(this.$route.params.page); this.count = parseInt(this.$route.params.count); if (this.parentChannelId == "" || this.parentChannelId == 0) { this.beforeUrl = "/videoList" } }, currentChange: function(val){ var url = `/${this.$router.currentRoute.name}/${this.deviceId}/${this.parentChannelId}/${this.count}/${val}` console.log(url) this.$router.push(url).then(()=>{ this.initParam(); this.initData(); }) }, handleSizeChange: function(val){ var url = `/${this.$router.currentRoute.name}/${this.$router.params.deviceId}/${this.$router.params.parentChannelId}/${val}/1` this.$router.push(url).then(()=>{ this.initParam(); this.initData(); }) }, currentChange: function (val) { var url = `/${this.$router.currentRoute.name}/${this.deviceId}/${this.parentChannelId}/${this.count}/${val}` console.log(url) this.$router.push(url).then(() => { this.initParam(); this.initData(); }) }, handleSizeChange: function (val) { var url = `/${this.$router.currentRoute.name}/${this.$router.params.deviceId}/${this.$router.params.parentChannelId}/${val}/1` this.$router.push(url).then(() => { this.initParam(); this.initData(); }) }, getDeviceChannelList: function() { let that = this; console.log(this.currentPage - 1) }, getDeviceChannelList: function () { let that = this; console.log(this.currentPage - 1) this.$axios.get(`/api/devices/${this.$route.params.deviceId}/channels`,{ params: { page: that.currentPage - 1, count: that.count, query: that.searchSrt, online: that.online, channelType: that.channelType } } ) .then(function (res) { console.log(res); that.total = res.data.total; that.deviceChannelList = res.data.data; // 防止出现表格错位 that.$nextTick(()=>{ that.$refs.channelListTable.doLayout(); }) }) .catch(function (error) { console.log(error); }); this.$axios.get(`/api/devices/${this.$route.params.deviceId}/channels`, { params: { page: that.currentPage - 1, count: that.count, query: that.searchSrt, online: that.online, channelType: that.channelType } }) .then(function (res) { console.log(res); that.total = res.data.total; that.deviceChannelList = res.data.data; // 防止出现表格错位 that.$nextTick(() => { that.$refs.channelListTable.doLayout(); }) }) .catch(function (error) { console.log(error); }); }, }, //gb28181平台对接 //刷新设备信息 refDevice: function (itemData) { ///api/devices/{deviceId}/sync console.log("刷新对应设备:" + itemData.deviceId); this.$axios({ method: 'post', url: '/api/devices/' + itemData.deviceId + '/sync' }).then(function (res) { // console.log("刷新设备结果:"+JSON.stringify(res)); }).catch(function (e) { that.$message({ showClose: true, message: '请求成功', type: 'success' }); }); }, //通知设备上传媒体流 sendDevicePush: function (itemData) { console.log(itemData) let deviceId = this.deviceId; this.isLoging = true; let channelId = itemData.channelId; console.log("通知设备推流1:" + deviceId + " : " + channelId); let that = this; this.$axios({ method: 'get', url: '/api/play/' + deviceId + '/' + channelId }).then(function (res) { console.log(res.data) let ssrc = res.data.ssrc; that.isLoging = false; if (!!ssrc) { that.$refs.devicePlayer.play(res.data, deviceId, channelId, itemData.hasAudio); that.initData(); } else { that.$message.error(res.data); } }).catch(function (e) {}); }, stopDevicePush: function (itemData) { console.log(itemData) var that = this; this.$axios({ method: 'post', url: '/api/play/' + itemData.ssrc + '/stop' }).then(function (res) { console.log(JSON.stringify(res)); that.initData(); }); }, //gb28181平台对接 //刷新设备信息 refDevice: function(itemData) { ///api/devices/{deviceId}/sync console.log("刷新对应设备:" + itemData.deviceId); this.$axios({ method: 'post', url: '/api/devices/' + itemData.deviceId + '/sync' }).then(function(res) { // console.log("刷新设备结果:"+JSON.stringify(res)); }).catch(function(e) { that.$message({ showClose: true, message: '请求成功', type: 'success' }); }); }, //通知设备上传媒体流 sendDevicePush: function(itemData) { console.log(itemData) let deviceId = this.deviceId; this.isLoging = true; let channelId = itemData.channelId; console.log("通知设备推流1:" + deviceId + " : " + channelId); let that = this; this.$axios({ method: 'get', url: '/api/play/' + deviceId + '/' + channelId }).then(function(res) { console.log(res.data) let ssrc = res.data.ssrc; that.isLoging = false if (!!ssrc) { that.$refs.devicePlayer.play(res.data,deviceId,channelId,itemData.hasAudio); that.initData(); }else { that.$message.error(res.data); } }).catch(function(e) { }); }, stopDevicePush: function(itemData) { console.log(itemData) var that = this; this.$axios({ method: 'post', url: '/api/play/' + itemData.ssrc + '/stop' }).then(function(res) { console.log(JSON.stringify(res)); that.initData(); }); }, showDevice: function () { this.$router.push(this.beforeUrl).then(() => { this.initParam(); this.initData(); }) }, changeSubchannel(itemData) { console.log(this.$router.currentRoute) this.beforeUrl = this.$router.currentRoute.path; showDevice: function(){ this.$router.push(this.beforeUrl).then(()=>{ this.initParam(); this.initData(); }) }, changeSubchannel(itemData) { console.log(this.$router.currentRoute) this.beforeUrl = this.$router.currentRoute.path; var url = `/${this.$router.currentRoute.name}/${this.$router.currentRoute.params.deviceId}/${itemData.channelId}/${this.$router.currentRoute.params.count}/1` this.$router.push(url).then(() => { this.searchSrt = ""; this.channelType = ""; this.online = ""; this.initParam(); this.initData(); }) }, showSubchannels: function (channelId) { let that = this; var url = `/${this.$router.currentRoute.name}/${this.$router.currentRoute.params.deviceId}/${itemData.channelId}/${this.$router.currentRoute.params.count}/1` this.$router.push(url).then(()=>{ this.searchSrt= ""; this.channelType= ""; this.online= ""; this.initParam(); this.initData(); }) }, showSubchannels: function(channelId){ let that = this; this.$axios.get(`/api/subChannels/${this.deviceId}/${this.parentChannelId}/channels`, { params: { page: that.currentPage - 1, count: that.count, query: that.searchSrt, online: that.online, channelType: that.channelType } }) .then(function (res) { that.total = res.data.total; that.deviceChannelList = res.data.data; // 防止出现表格错位 that.$nextTick(() => { that.$refs.channelListTable.doLayout(); }) }) .catch(function (error) { console.log(error); }); }, search: function () { console.log(this.searchSrt) this.currentPage = 1; this.total = 0; this.initData(); }, updateChannel: function (row) { console.log(row) this.$axios({ method: 'post', url: `/api/channel/update/${this.deviceId}`, params: row }).then(function (res) { console.log(JSON.stringify(res)); }); } this.$axios.get(`/api/subChannels/${this.deviceId}/${this.parentChannelId}/channels`,{ params: { page: that.currentPage - 1, count: that.count, query: that.searchSrt, online: that.online, channelType: that.channelType } } ) .then(function (res) { that.total = res.data.total; that.deviceChannelList = res.data.data; // 防止出现表格错位 that.$nextTick(()=>{ that.$refs.channelListTable.doLayout(); }) }) .catch(function (error) { console.log(error); }); }, search: function() { console.log(this.searchSrt) this.currentPage = 1; this.total = 0; this.initData(); }, updateChannel: function(row) { console.log(row) this.$axios({ method: 'post', url: `/api/channel/update/${this.deviceId}`, params: row }).then(function(res) { console.log(JSON.stringify(res)); }); } } }; } }; </script> <style> .videoList { display: flex; flex-wrap: wrap; align-content: flex-start; } .videoList { display: flex; flex-wrap: wrap; align-content: flex-start; } .video-item { position: relative; width: 15rem; height: 10rem; margin-right: 1rem; background-color: #000000; } .video-item { position: relative; width: 15rem; height: 10rem; margin-right: 1rem; background-color: #000000; } .video-item-img { position: absolute; top: 0; bottom: 0; left: 0; right: 0; margin: auto; width: 100%; height: 100%; } .video-item-img { position: absolute; top: 0; bottom: 0; left: 0; right: 0; margin: auto; width: 100%; height: 100%; } .video-item-img:after { content: ""; display: inline-block; position: absolute; z-index: 2; top: 0; bottom: 0; left: 0; right: 0; margin: auto; width: 3rem; height: 3rem; background-image: url("../assets/loading.png"); background-size: cover; background-color: #000000; } .video-item-img:after { content: ""; display: inline-block; position: absolute; z-index: 2; top: 0; bottom: 0; left: 0; right: 0; margin: auto; width: 3rem; height: 3rem; background-image: url("../assets/loading.png"); background-size: cover; background-color: #000000; } .video-item-title { position: absolute; bottom: 0; color: #000000; background-color: #ffffff; line-height: 1.5rem; padding: 0.3rem; width: 14.4rem; } .video-item-title { position: absolute; bottom: 0; color: #000000; background-color: #ffffff; line-height: 1.5rem; padding: 0.3rem; width: 14.4rem; } </style> web_src/src/components/gb28181/devicePlayer.vue
@@ -1,379 +1,375 @@ <template> <div id="devicePlayer"> <el-dialog title="视频播放" top="0" :visible.sync="showVideoDialog" :destroy-on-close="true" @close="close()"> <LivePlayer v-if="showVideoDialog" ref="videoPlayer" :videoUrl="videoUrl" :error="videoError" :hasaudio="hasaudio" fluent autoplay live ></LivePlayer> <div id="shared" style="text-align: right; margin-top: 1rem;"> <el-tabs v-model="tabActiveName"> <el-tab-pane label="媒体流信息" name="media"> <div style="margin-bottom: 0.5rem;"> <el-button type="primary" size="small" @click="playRecord(true, '')">播放</el-button> <el-button type="primary" size="small" @click="startRecord()">录制</el-button> <el-button type="primary" size="small" @click="stopRecord()">停止录制</el-button> </div> <div style="display: flex; margin-bottom: 0.5rem; height: 2.5rem;"> <span style="width: 5rem; line-height: 2.5rem; text-align: right;">播放地址:</span> <el-input v-model="getPlayerShared.sharedUrl" :disabled="true" v-on:click.native="copySharedInfo(getPlayerShared.sharedUrl)"></el-input> </div> <div style="display: flex; margin-bottom: 0.5rem; height: 2.5rem;"> <span style="width: 5rem; line-height: 2.5rem; text-align: right;">iframe:</span> <el-input v-model="getPlayerShared.sharedIframe" :disabled="true" v-on:click.native="copySharedInfo(getPlayerShared.sharedIframe)"></el-input> </div> <div style="display: flex; margin-bottom: 0.5rem; height: 2.5rem;"> <span style="width: 5rem; line-height: 2.5rem; text-align: right;">资源地址:</span> <el-input v-model="getPlayerShared.sharedRtmp" :disabled="true" v-on:click.native="copySharedInfo(getPlayerShared.sharedRtmp)"></el-input> </div> </el-tab-pane> <!--{"code":0,"data":{"paths":["22-29-30.mp4"],"rootPath":"/home/kkkkk/Documents/ZLMediaKit/release/linux/Debug/www/record/hls/kkkkk/2020-05-11/"}}--> <el-tab-pane label="录像查询" name="second"> <el-date-picker v-model="videoHistory.startTime" type="datetime" value-format="yyyy-MM-dd HH:mm:ss" placeholder="开始时间" @change="recordList()"></el-date-picker> <el-date-picker v-model="videoHistory.endTime" type="datetime" value-format="yyyy-MM-dd HH:mm:ss" placeholder="结束时间" @change="recordList()"></el-date-picker> <el-table :data="videoHistory.searchHistoryResult" style="width: 100%"> <el-table-column label="名称" prop="name" width="150"></el-table-column> <el-table-column label="文件" prop="filePath" width="300"></el-table-column> <el-table-column label="开始时间" prop="startTime" width="160"></el-table-column> <el-table-column label="结束时间" prop="endTime" width="160"></el-table-column> <div id="devicePlayer"> <el-dialog title="视频播放" top="0" :visible.sync="showVideoDialog" :destroy-on-close="true" @close="close()"> <LivePlayer v-if="showVideoDialog" ref="videoPlayer" :videoUrl="videoUrl" :error="videoError" :hasaudio="hasaudio" fluent autoplay live></LivePlayer> <div id="shared" style="text-align: right; margin-top: 1rem;"> <el-tabs v-model="tabActiveName"> <el-tab-pane label="媒体流信息" name="media"> <div style="margin-bottom: 0.5rem;"> <el-button type="primary" size="small" @click="playRecord(true, '')">播放</el-button> <el-button type="primary" size="small" @click="startRecord()">录制</el-button> <el-button type="primary" size="small" @click="stopRecord()">停止录制</el-button> </div> <div style="display: flex; margin-bottom: 0.5rem; height: 2.5rem;"> <span style="width: 5rem; line-height: 2.5rem; text-align: right;">播放地址:</span> <el-input v-model="getPlayerShared.sharedUrl" :disabled="true" v-on:click.native="copySharedInfo(getPlayerShared.sharedUrl)"></el-input> </div> <div style="display: flex; margin-bottom: 0.5rem; height: 2.5rem;"> <span style="width: 5rem; line-height: 2.5rem; text-align: right;">iframe:</span> <el-input v-model="getPlayerShared.sharedIframe" :disabled="true" v-on:click.native="copySharedInfo(getPlayerShared.sharedIframe)"></el-input> </div> <div style="display: flex; margin-bottom: 0.5rem; height: 2.5rem;"> <span style="width: 5rem; line-height: 2.5rem; text-align: right;">资源地址:</span> <el-input v-model="getPlayerShared.sharedRtmp" :disabled="true" v-on:click.native="copySharedInfo(getPlayerShared.sharedRtmp)"></el-input> </div> </el-tab-pane> <!--{"code":0,"data":{"paths":["22-29-30.mp4"],"rootPath":"/home/kkkkk/Documents/ZLMediaKit/release/linux/Debug/www/record/hls/kkkkk/2020-05-11/"}}--> <el-tab-pane label="录像查询" name="second"> <el-date-picker v-model="videoHistory.startTime" type="datetime" value-format="yyyy-MM-dd HH:mm:ss" placeholder="开始时间" @change="recordList()"></el-date-picker> <el-date-picker v-model="videoHistory.endTime" type="datetime" value-format="yyyy-MM-dd HH:mm:ss" placeholder="结束时间" @change="recordList()"></el-date-picker> <el-table :data="videoHistory.searchHistoryResult" style="width: 100%"> <el-table-column label="名称" prop="name" width="150"></el-table-column> <el-table-column label="文件" prop="filePath" width="300"></el-table-column> <el-table-column label="开始时间" prop="startTime" width="160"></el-table-column> <el-table-column label="结束时间" prop="endTime" width="160"></el-table-column> <el-table-column label="操作"> <template slot-scope="scope"> <el-button type="primary" size="mini" @click="playRecord(false, scope.row)">播放</el-button> </template> </el-table-column> </el-table> </el-tab-pane> <!--遥控界面--> <el-tab-pane label="云台控制" name="third"> <div style="display: flex; justify-content: center;"> <div class="control-wrapper"> <div class="control-btn control-top" @mousedown="ptzCamera(0, 1, 0)" @mouseup="ptzCamera(0, 0, 0)"> <i class="el-icon-caret-top"></i> <div class="control-inner-btn control-inner"></div> </div> <div class="control-btn control-left" @mousedown="ptzCamera(1, 0, 0)" @mouseup="ptzCamera(0, 0, 0)"> <i class="el-icon-caret-left"></i> <div class="control-inner-btn control-inner"></div> </div> <div class="control-btn control-bottom" @mousedown="ptzCamera(0, 2, 0)" @mouseup="ptzCamera(0, 0, 0)"> <i class="el-icon-caret-bottom"></i> <div class="control-inner-btn control-inner"></div> </div> <div class="control-btn control-right" @mousedown="ptzCamera(2, 0, 0)" @mouseup="ptzCamera(0, 0, 0)"> <i class="el-icon-caret-right"></i> <div class="control-inner-btn control-inner"></div> </div> <div class="control-round"> <div class="control-round-inner"><i class="fa fa-pause-circle"></i></div> </div> <el-table-column label="操作"> <template slot-scope="scope"> <el-button type="primary" size="mini" @click="playRecord(false, scope.row)">播放</el-button> </template> </el-table-column> </el-table> </el-tab-pane> <!--遥控界面--> <el-tab-pane label="云台控制" name="third"> <div style="display: flex; justify-content: center;"> <div class="control-wrapper"> <div class="control-btn control-top" @mousedown="ptzCamera(0, 1, 0)" @mouseup="ptzCamera(0, 0, 0)"> <i class="el-icon-caret-top"></i> <div class="control-inner-btn control-inner"></div> </div> <div class="control-btn control-left" @mousedown="ptzCamera(1, 0, 0)" @mouseup="ptzCamera(0, 0, 0)"> <i class="el-icon-caret-left"></i> <div class="control-inner-btn control-inner"></div> </div> <div class="control-btn control-bottom" @mousedown="ptzCamera(0, 2, 0)" @mouseup="ptzCamera(0, 0, 0)"> <i class="el-icon-caret-bottom"></i> <div class="control-inner-btn control-inner"></div> </div> <div class="control-btn control-right" @mousedown="ptzCamera(2, 0, 0)" @mouseup="ptzCamera(0, 0, 0)"> <i class="el-icon-caret-right"></i> <div class="control-inner-btn control-inner"></div> </div> <div class="control-round"> <div class="control-round-inner"><i class="fa fa-pause-circle"></i></div> </div> <div style="position: absolute; left: 7.25rem; top: 1.25rem" @mousedown="ptzCamera(0, 0, 2)" @mouseup="ptzCamera(0, 0, 0)"><i class="el-icon-zoom-in" style="font-size: 1.875rem;"></i></div> <div style="position: absolute; left: 7.25rem; top: 3.25rem; font-size: 1.875rem;" @mousedown="ptzCamera(0, 0, 1)" @mouseup="ptzCamera(0, 0, 0)"><i class="el-icon-zoom-out"></i></div> </div> </div> <div style="position: absolute; left: 7.25rem; top: 1.25rem" @mousedown="ptzCamera(0, 0, 2)" @mouseup="ptzCamera(0, 0, 0)"><i class="el-icon-zoom-in" style="font-size: 1.875rem;"></i></div> <div style="position: absolute; left: 7.25rem; top: 3.25rem; font-size: 1.875rem;" @mousedown="ptzCamera(0, 0, 1)" @mouseup="ptzCamera(0, 0, 0)"><i class="el-icon-zoom-out"></i></div> </div> </div> </el-tab-pane> </el-tabs> </div> </el-dialog> </div> </el-tab-pane> </el-tabs> </div> </el-dialog> </div> </template> <script> import LivePlayer from '@liveqing/liveplayer' export default { name: 'devicePlayer', props: {}, components: { LivePlayer }, computed: { getPlayerShared: function() { return { sharedUrl: window.location.host + '/' + this.videoUrl, sharedIframe: '<iframe src="' + window.location.host + '/' + this.videoUrl + '"></iframe>', sharedRtmp: this.videoUrl }; } }, created() { // this.videoHistory.searchHistoryResult = falsificationData.recordData.recordList; }, data() { return { video:'http://lndxyj.iqilu.com/public/upload/2019/10/14/8c001ea0c09cdc59a57829dabc8010fa.mp4', videoUrl: '', videoHistory: { startTime: '', endTime: '', searchHistoryResult: [] //媒体流历史记录搜索结果 }, showVideoDialog: false, normalssrc: '', ssrc: '', deviceId: '', channelId: '', tabActiveName: 'media', hasaudio: false }; }, methods: { play: function(streamInfo, deviceId, channelId, hasAudio) { this.hasaudio = hasAudio; // 根据媒体流信息二次判断 if( this.hasaudio && !!streamInfo.tracks && streamInfo.tracks.length > 0) { var realHasAudio = false; for (let i = 0; i < streamInfo.tracks; i++) { if (streamInfo.tracks[i].codec_type == 1) { // 判断为音频 realHasAudio = true; } } this.hasaudio = realHasAudio && this.hasaudio; import LivePlayer from '@liveqing/liveplayer' export default { name: 'devicePlayer', props: {}, components: { LivePlayer }, computed: { getPlayerShared: function () { return { sharedUrl: window.location.host + '/' + this.videoUrl, sharedIframe: '<iframe src="' + window.location.host + '/' + this.videoUrl + '"></iframe>', sharedRtmp: this.videoUrl }; } this.ssrc = streamInfo.ssrc; this.deviceId = deviceId; this.channelId = channelId; // this.$refs.videoPlayer.hasaudio = hasAudio; // this.videoUrl = streamInfo.flv + "?" + new Date().getTime(); this.videoUrl = streamInfo.ws_flv; this.showVideoDialog = true; console.log(this.ssrc); }, close: function() { console.log('关闭视频'); this.$refs.videoPlayer.pause(); this.videoUrl = ''; this.showVideoDialog = false; }, copySharedInfo: function(data) { console.log('复制内容:' + data); let _this = this; this.$copyText(data).then( function(e) { _this.$message({ showClose: true, message: '复制成功', type: 'success' }); }, function(e) { _this.$message({ showClose: true, message: '复制失败,请手动复制', type: 'error' }); } ); }, }, created() { // this.videoHistory.searchHistoryResult = falsificationData.recordData.recordList; }, data() { return { video: 'http://lndxyj.iqilu.com/public/upload/2019/10/14/8c001ea0c09cdc59a57829dabc8010fa.mp4', videoUrl: '', videoHistory: { startTime: '', endTime: '', searchHistoryResult: [] //媒体流历史记录搜索结果 }, showVideoDialog: false, normalssrc: '', ssrc: '', deviceId: '', channelId: '', tabActiveName: 'media', hasaudio: false recordList: function() { if (!this.videoHistory.startTime || !this.videoHistory.endTime) { return; } let that = this; this.$axios({ method: 'get', url: '/api/record/' + this.deviceId + '/' + this.channelId + '?startTime=' + this.videoHistory .startTime + '&endTime=' + this.videoHistory.endTime }).then(function(res) { console.log(JSON.stringify(res)); }).catch(function(e) { // that.videoHistory.searchHistoryResult = falsificationData.recordData; }); }; }, methods: { }, playRecord: function(isBackLive, rowData) { let that = this; if(isBackLive){ this.videoUrl=this.getVideoUrlBySsrc(this.normalssrc); return; } this.$axios({ method: 'get', url: '/api/playback/' + this.deviceId + '/' + this.channelId + '?startTime=' + rowData.startTime + '&endTime=' + rowData.endTime }).then(function(res) { let ssrc = res.data.ssrc; that.videoUrl = that.getVideoUrlBySsrc(ssrc); //that.videoUrl='http://hls.cntv.kcdnvip.com/asp/hls/main/0303000a/3/default/f466089412c04a759c5515dbfcc3ac3d/main.m3u8?maxbr=2048'; }); play: function (streamInfo, deviceId, channelId, hasAudio) { this.hasaudio = hasAudio; // 根据媒体流信息二次判断 if (!!streamInfo.tracks && streamInfo.tracks.length > 0) { var realHasAudio = false; for (let i = 0; i < streamInfo.tracks.length; i++) { if (streamInfo.tracks[i].codec_type == 1 && streamInfo.tracks[i].codec_id_name == "CodecAAC") { // 判断为AAC音频 realHasAudio = true; } } this.hasaudio = realHasAudio; //&& this.hasaudio; } this.ssrc = streamInfo.ssrc; this.deviceId = deviceId; this.channelId = channelId; // this.$refs.videoPlayer.hasaudio = hasAudio; // this.videoUrl = streamInfo.flv + "?" + new Date().getTime(); this.videoUrl = streamInfo.ws_flv; this.showVideoDialog = true; console.log(this.ssrc); }, close: function () { console.log('关闭视频'); this.$refs.videoPlayer.pause(); this.videoUrl = ''; this.showVideoDialog = false; }, copySharedInfo: function (data) { console.log('复制内容:' + data); let _this = this; this.$copyText(data).then( function (e) { _this.$message({ showClose: true, message: '复制成功', type: 'success' }); }, function (e) { _this.$message({ showClose: true, message: '复制失败,请手动复制', type: 'error' }); } ); }, }, ptzCamera: function(leftRight, upDown, zoom) { console.log('云台控制:' + leftRight + ' : ' + upDown + " : " + zoom); let that = this; this.$axios({ method: 'post', url: '/api/ptz/' + this.deviceId + '/' + this.channelId + '?leftRight=' + leftRight + '&upDown=' + upDown + '&inOut=' + zoom + '&moveSpeed=50&zoomSpeed=50' }).then(function(res) {}); }, //////////////////////播放器事件处理////////////////////////// videoError:function(e){ console.log("播放器错误:"+JSON.stringify(e)); } } }; recordList: function () { if (!this.videoHistory.startTime || !this.videoHistory.endTime) { return; } let that = this; this.$axios({ method: 'get', url: '/api/record/' + this.deviceId + '/' + this.channelId + '?startTime=' + this.videoHistory .startTime + '&endTime=' + this.videoHistory.endTime }).then(function (res) { console.log(JSON.stringify(res)); }).catch(function (e) { // that.videoHistory.searchHistoryResult = falsificationData.recordData; }); }, playRecord: function (isBackLive, rowData) { let that = this; if (isBackLive) { this.videoUrl = this.getVideoUrlBySsrc(this.normalssrc); return; } this.$axios({ method: 'get', url: '/api/playback/' + this.deviceId + '/' + this.channelId + '?startTime=' + rowData.startTime + '&endTime=' + rowData.endTime }).then(function (res) { let ssrc = res.data.ssrc; that.videoUrl = that.getVideoUrlBySsrc(ssrc); //that.videoUrl='http://hls.cntv.kcdnvip.com/asp/hls/main/0303000a/3/default/f466089412c04a759c5515dbfcc3ac3d/main.m3u8?maxbr=2048'; }); }, ptzCamera: function (leftRight, upDown, zoom) { console.log('云台控制:' + leftRight + ' : ' + upDown + " : " + zoom); let that = this; this.$axios({ method: 'post', url: '/api/ptz/' + this.deviceId + '/' + this.channelId + '?leftRight=' + leftRight + '&upDown=' + upDown + '&inOut=' + zoom + '&moveSpeed=50&zoomSpeed=50' }).then(function (res) {}); }, //////////////////////播放器事件处理////////////////////////// videoError: function (e) { console.log("播放器错误:" + JSON.stringify(e)); } } }; </script> <style> .control-wrapper { position: relative; width: 6.25rem; height: 6.25rem; max-width: 6.25rem; max-height: 6.25rem; margin: 0 auto; border-radius: 100%; float: left; } .control-wrapper { position: relative; width: 6.25rem; height: 6.25rem; max-width: 6.25rem; max-height: 6.25rem; margin: 0 auto; border-radius: 100%; float: left; } .control-btn { display: flex; justify-content: center; position: absolute; width: 44%; height: 44%; border-radius: 5px; border: 1px solid #78aee4; box-sizing: border-box; transition: all 0.3s linear; } .control-btn { display: flex; justify-content: center; position: absolute; width: 44%; height: 44%; border-radius: 5px; border: 1px solid #78aee4; box-sizing: border-box; transition: all 0.3s linear; } .control-btn i { font-size: 20px; color: #78aee4; display: flex; justify-content: center; align-items: center; } .control-btn i { font-size: 20px; color: #78aee4; display: flex; justify-content: center; align-items: center; } .control-round { position: absolute; top: 21%; left: 21%; width: 58%; height: 58%; background: #fff; border-radius: 100%; } .control-round { position: absolute; top: 21%; left: 21%; width: 58%; height: 58%; background: #fff; border-radius: 100%; } .control-round-inner { position: absolute; left: 15%; top: 15%; display: flex; justify-content: center; align-items: center; width: 70%; height: 70%; font-size: 40px; color: #78aee4; border: 1px solid #78aee4; border-radius: 100%; transition: all 0.3s linear; } .control-round-inner { position: absolute; left: 15%; top: 15%; display: flex; justify-content: center; align-items: center; width: 70%; height: 70%; font-size: 40px; color: #78aee4; border: 1px solid #78aee4; border-radius: 100%; transition: all 0.3s linear; } .control-inner-btn { position: absolute; width: 60%; height: 60%; background: #fafafa; } .control-inner-btn { position: absolute; width: 60%; height: 60%; background: #fafafa; } .control-top { top: -8%; left: 27%; transform: rotate(-45deg); border-radius: 5px 100% 5px 0; } .control-top { top: -8%; left: 27%; transform: rotate(-45deg); border-radius: 5px 100% 5px 0; } .control-top i { transform: rotate(45deg); border-radius: 5px 100% 5px 0; } .control-top i { transform: rotate(45deg); border-radius: 5px 100% 5px 0; } .control-top .control-inner { left: -1px; bottom: 0; border-top: 1px solid #78aee4; border-right: 1px solid #78aee4; border-radius: 0 100% 0 0; } .control-top .control-inner { left: -1px; bottom: 0; border-top: 1px solid #78aee4; border-right: 1px solid #78aee4; border-radius: 0 100% 0 0; } .control-top .fa { transform: rotate(45deg) translateY(-7px); } .control-top .fa { transform: rotate(45deg) translateY(-7px); } .control-left { top: 27%; left: -8%; transform: rotate(45deg); border-radius: 5px 0 5px 100%; } .control-left { top: 27%; left: -8%; transform: rotate(45deg); border-radius: 5px 0 5px 100%; } .control-left i { transform: rotate(-45deg); } .control-left i { transform: rotate(-45deg); } .control-left .control-inner { right: -1px; top: -1px; border-bottom: 1px solid #78aee4; border-left: 1px solid #78aee4; border-radius: 0 0 0 100%; } .control-left .control-inner { right: -1px; top: -1px; border-bottom: 1px solid #78aee4; border-left: 1px solid #78aee4; border-radius: 0 0 0 100%; } .control-left .fa { transform: rotate(-45deg) translateX(-7px); } .control-left .fa { transform: rotate(-45deg) translateX(-7px); } .control-right { top: 27%; right: -8%; transform: rotate(45deg); border-radius: 5px 100% 5px 0; } .control-right { top: 27%; right: -8%; transform: rotate(45deg); border-radius: 5px 100% 5px 0; } .control-right i { transform: rotate(-45deg); } .control-right i { transform: rotate(-45deg); } .control-right .control-inner { left: -1px; bottom: -1px; border-top: 1px solid #78aee4; border-right: 1px solid #78aee4; border-radius: 0 100% 0 0; } .control-right .control-inner { left: -1px; bottom: -1px; border-top: 1px solid #78aee4; border-right: 1px solid #78aee4; border-radius: 0 100% 0 0; } .control-right .fa { transform: rotate(-45deg) translateX(7px); } .control-right .fa { transform: rotate(-45deg) translateX(7px); } .control-bottom { left: 27%; bottom: -8%; transform: rotate(45deg); border-radius: 0 5px 100% 5px; } .control-bottom { left: 27%; bottom: -8%; transform: rotate(45deg); border-radius: 0 5px 100% 5px; } .control-bottom i { transform: rotate(-45deg); } .control-bottom i { transform: rotate(-45deg); } .control-bottom .control-inner { top: -1px; left: -1px; border-bottom: 1px solid #78aee4; border-right: 1px solid #78aee4; border-radius: 0 0 100% 0; } .control-bottom .control-inner { top: -1px; left: -1px; border-bottom: 1px solid #78aee4; border-right: 1px solid #78aee4; border-radius: 0 0 100% 0; } .control-bottom .fa { transform: rotate(-45deg) translateY(7px); } .control-bottom .fa { transform: rotate(-45deg) translateY(7px); } </style>