123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414 |
- <template>
- <div id="devicePosition" :style="`width:100vw; height: ${isFullScreen?'100vh':'91vh'}`" ref="container">
- <el-container v-loading="loading" style="height: 100%;" :element-loading-text="$t('loading')" >
- <el-aside width="300px" v-show="asideHide" :style="`background-color: ${isFullScreen?'#eee':'#ffffff'}`">
- <DeviceTree :clickEvent="clickEvent" :contextMenuEvent="contextMenuEvent"></DeviceTree>
- </el-aside>
- <el-container>
- <el-header height="5vh"
- :style="`text-align: left;font-size: 17px;line-height:5vh;
- background-color:${isFullScreen?'#000':'#fff'};
- color:${!isFullScreen?'#000':'#fff'}`
- ">
- <div class="b-left">
- <el-button :icon="`el-icon-s-${asideHide?'fold':'unfold'}`" circle size="small" @click="asideHide=!asideHide" :type="isFullScreen?'goon':''"></el-button>
- <el-button :icon="`el-icon-${isFullScreen?'files':'full-screen'}`" circle size="small" @click="switchFullScreenHandle" :type="isFullScreen?'goon':''"></el-button>
- {{ $t('menu.liveMonitor') }}:
- <i class="el-icon-full-screen btn" :class="{active:spilt==1}" @click="spilt=1"/>
- <i class="el-icon-menu btn" :class="{active:spilt==4}" @click="spilt=4"/>
- <i class="el-icon-s-grid btn" :class="{active:spilt==9}" @click="spilt=9"/>
- </div>
- <div class="b-right">
- <el-button></el-button>
- </div>
- </el-header>
- <el-main style="padding: 0;background-color: #000;">
- <div :style="`width: 100%;height: ${isFullScreen?'94vh':'85vh'};display: flex;flex-wrap: wrap;background-color: #000;`">
- <div v-for="i in spilt" :key="i" class="play-box"
- :style="liveStyle" :class="{redborder:playerIdx == (i-1)}"
- @click="playerIdx = (i-1)">
- <div v-if="!videoUrl[i-1]" style="color: #ffffff;font-size: 30px;font-weight: bold;">{{ i }}</div>
- <!-- <player ref="player" v-else :videoUrl="videoUrl[i-1]" fluent autoplay @screenshot="shot"-->
- <!-- @destroy="destroy"/>-->
- <rtc-player ref="player" @eventCallback="rtcPlayHandle" v-else :videoUrl="videoUrl[i-1]" fluent autoplay @screenshot="shot"
- @destroy="destroy"/>
- </div>
- </div>
- </el-main>
- </el-container>
- </el-container>
- </div>
- </template>
- <script>
- import uiHeader from "../layout/UiHeader.vue";
- import player from './common/jessibuca.vue'
- import DeviceTree from './common/DeviceTree.vue'
- import {exitFullscreen, launchIntoFullscreen} from "@/until/dom";
- import rtcPlayer from "@/components/dialog/rtcPlayer.vue";
- import handle from "@/until/handle";
- import {sleep} from "@/until/time";
- export default {
- name: "live",
- components: {
- uiHeader, player, DeviceTree, rtcPlayer
- },
- data() {
- return {
- videoUrl: [''],
- spilt: 1,//分屏
- playerIdx: 0,//激活播放器
- player: {
- webRTC: ["rtc", "rtcs"],
- flv: ["ws_flv", "wss_flv"],
- h265: ["ws_flv", "wss_flv"]
- },
- updateLooper: 0, //数据刷新轮训标志
- count: 15,
- total: 0,
- asideHide: true,// 是否隐藏侧边栏
- isFullScreen: false,// 是否全屏
- //channel
- loading: false,
- isPlay: false,// 是否已经开始推流
- };
- },
- mounted() {
- },
- created() {
- this.checkPlayByParam()
- },
- computed: {
- liveStyle() {
- let style = {width: '100%', height: '100%'}
- switch (this.spilt) {
- case 4:
- style = {width: '49%', height: '49%','margin-left':'0.5%'}
- break
- case 9:
- style = {width: '32%', height: '32%','margin-left':'0.8%'}
- break
- }
- this.$nextTick(() => {
- for (let i = 0; i < this.spilt; i++) {
- const player = this.$refs.player
- player && player[i] && player[i].updatePlayerDomSize()
- }
- })
- return style
- }
- },
- watch: {
- spilt(newValue) {
- console.log("切换画幅;" + newValue)
- let that = this
- for (let i = 1; i <= newValue; i++) {
- if (!that.$refs['player' + i]) {
- continue
- }
- this.$nextTick(() => {
- if (that.$refs['player' + i] instanceof Array) {
- that.$refs['player' + i][0].resize()
- } else {
- that.$refs['player' + i].resize()
- }
- })
- }
- window.localStorage.setItem('split', newValue)
- },
- '$route.fullPath': 'checkPlayByParam'
- },
- destroyed() {
- clearTimeout(this.updateLooper);
- },
- methods: {
- destroy(idx) {
- console.log(idx);
- this.clear(idx.substring(idx.length - 1))
- },
- clickEvent: function (device, data, isCatalog) {
- if (data.channelId && !isCatalog) {
- if (device.online === 0) {
- this.$message.error(this.$t('device.offlineNotAllowLive'));
- }else {
- this.sendDevicePush(data)
- }
- }
- },
- contextMenuEvent: function (device, event, data, isCatalog) {
- },
- //通知设备上传媒体流
- async sendDevicePush(itemData) {
- // if (itemData.status === 0) {
- // this.$message.error('设备离线!');
- // return
- // }
- this.save(itemData)
- let deviceId = itemData.deviceId;
- // this.isLoging = true;
- let channelId = itemData.channelId;
- console.log(itemData);
- console.log("通知设备推流1:" + deviceId + " : " + channelId);
- let idxTmp = this.playerIdx
- this.loading = true;
- let [err,res] = await handle(this.$axios.axios({
- method: 'get',
- url: '/api/play/start/' + deviceId + '/' + channelId
- }))
- this.loading = false
- if(err){
- this.$message.error(this.$t('device.pushFail'));
- return
- }
- if (res.data.code === 0 && res.data.data) {
- let videoUrl;
- if (location.protocol === "https:") {
- videoUrl = res.data.data[this.player.webRTC[1]];
- } else {
- videoUrl = res.data.data[this.player.webRTC[0]];
- }
- console.log("推流成功:" + videoUrl)
- itemData.playUrl = videoUrl;
- this.setPlayUrl(videoUrl, idxTmp);
- // 添加监听
- setTimeout(() => {
- this.loadSsrcList();
- this.autoLoad();
- }, 10 * 1000)
- } else {
- this.$message.error(res.data.msg);
- }
- },
- async setPlayUrl(url, idx) {
- console.log("设置播放地址:" + url + " : " + idx);
- this.$set(this.videoUrl, idx, "");
- await sleep(700);
- this.$set(this.videoUrl, idx, url)
- let _this = this
- await sleep(100);
- window.localStorage.setItem('videoUrl', JSON.stringify(_this.videoUrl))
- },
- checkPlayByParam() {
- let {deviceId, channelId} = this.$route.query
- if (deviceId && channelId) {
- this.sendDevicePush({deviceId, channelId})
- }
- },
- shot(e) {
- // console.log(e)
- // send({code:'image',data:e})
- var base64ToBlob = function (code) {
- let parts = code.split(';base64,');
- let contentType = parts[0].split(':')[1];
- let raw = window.atob(parts[1]);
- let rawLength = raw.length;
- let uInt8Array = new Uint8Array(rawLength);
- for (let i = 0; i < rawLength; ++i) {
- uInt8Array[i] = raw.charCodeAt(i);
- }
- return new Blob([uInt8Array], {
- type: contentType
- });
- };
- let aLink = document.createElement('a');
- let blob = base64ToBlob(e); //new Blob([content]);
- let evt = document.createEvent("HTMLEvents");
- evt.initEvent("click", true, true); //initEvent 不加后两个参数在FF下会报错 事件类型,是否冒泡,是否阻止浏览器的默认行为
- aLink.download = '截图';
- aLink.href = URL.createObjectURL(blob);
- aLink.click();
- },
- save(item) {
- let dataStr = window.localStorage.getItem('playData') || '[]'
- let data = JSON.parse(dataStr);
- data[this.playerIdx] = item
- window.localStorage.setItem('playData', JSON.stringify(data))
- },
- clear(idx) {
- let dataStr = window.localStorage.getItem('playData') || '[]'
- let data = JSON.parse(dataStr);
- data[idx - 1] = null;
- console.log(data);
- window.localStorage.setItem('playData', JSON.stringify(data))
- },
- switchFullScreenHandle(){
- if(this.isFullScreen){
- // 退出全屏
- exitFullscreen();
- this.isFullScreen= false;
- }else{
- let el = this.$refs.container;
- launchIntoFullscreen(el);
- this.isFullScreen= true;
- }
- },
- rtcPlayHandle(e){
- console.log(e);
- },
- addEventHandle(){
- },
- autoLoad(){
- if(!this.isPlay){
- return console.log(`暂未开始推流`)
- }
- this.updateLooper = setTimeout(() => {
- this.loadSsrcList();
- this.autoLoad();
- }, 10 * 1000);
- },
- // 加载当前推流的情况
- async loadSsrcList(){
- let [err,res] = await handle(this.$axios.axios({
- method: 'get',
- url: '/api/play/ssrc'
- }))
- if(err){
- this.$message.error(this.$t('device.getPushListFail'));
- return
- }
- let response = res.data;
- console.log(response);
- if (response.code === 0){
- }else{
- this.$message.error(response.msg);
- }
- }
- }
- };
- </script>
- <style>
- .btn {
- margin: 0 10px;
- }
- .btn:hover {
- color: #409EFF;
- }
- .btn.active {
- color: #409EFF;
- }
- .redborder {
- border: 2px solid red !important;
- }
- .play-box {
- background-color: #000000;
- border: 2px solid #505050;
- display: flex;
- align-items: center;
- justify-content: center;
- }
- </style>
- <style>
- .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-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-title {
- position: absolute;
- bottom: 0;
- color: #000000;
- background-color: #ffffff;
- line-height: 1.5rem;
- padding: 0.3rem;
- width: 14.4rem;
- }
- .baidumap {
- width: 100%;
- height: 100%;
- border: none;
- position: absolute;
- left: 0;
- top: 0;
- right: 0;
- bottom: 0;
- margin: auto;
- }
- /* 去除百度地图版权那行字 和 百度logo */
- .baidumap > .BMap_cpyCtrl {
- display: none !important;
- }
- .baidumap > .anchorBL {
- display: none !important;
- }
- .el-button--goon.is-active,
- .el-button--goon:active {
- background: #20B2AA;
- border-color: #20B2AA;
- color: #fff;
- }
- .el-button--goon:focus,
- .el-button--goon:hover {
- background: #48D1CC;
- border-color: #48D1CC;
- color: #fff;
- }
- .el-button--goon {
- color: #FFF;
- background-color: #292a2a;
- border-color: #295656;
- }
- </style>
|