Browse Source

增加视频播放功能

liling 2 weeks ago
parent
commit
4bf878cd21

+ 1 - 0
index.html

@@ -210,6 +210,7 @@
     </div>
   </div>
   <script type="module" src="/src/main.js"></script>
+  <script type="module" src="./jessibuca.js"></script>
 </body>
 
 </html>

File diff suppressed because it is too large
+ 0 - 0
public/decoder.js


BIN
public/decoder.wasm


File diff suppressed because it is too large
+ 0 - 0
public/jessibuca.js


+ 30 - 6
src/views/components/housecamer.vue

@@ -44,7 +44,9 @@
                 <div class="middle">
                   <div v-for="item in video_list" :class="item.contrlset ? 'video contrlset type'+videocount_type : 'video type'+videocount_type" @click="videocontrl(item)">
                     <div class="video-title"><img src="/images/ico-video.png"><span>{{ item.name }}</span><span style="float: right;margin-right: 42px;">{{ item.statedesc }}</span></div>
-                    <div class="video-play"></div>
+                    <div class="video-play">
+                      <Player :ref="(el)=>handleSetTempMap(el,item)"/>
+                    </div>
                     <div class="cloudset" v-if="item.contrlset==true">
                       <div><span class="close" @click.stop="closeCloudset(item)" style="top:2px"></span></div>
                       <div class="camername">{{ item.name }}摄像头</div>
@@ -73,7 +75,7 @@
 <script setup>
 import {shallowRef,onMounted,defineExpose} from "vue";
 import * as echarts from "echarts";
-
+import Player from './player.vue'
 const {proxy} = getCurrentInstance();
 const showHouse=ref(false);
 const provinceValue = ref('')
@@ -93,6 +95,8 @@ const cc_params1=ref(0);
 const cc_params2=ref(0);
 const cc_params3=ref(0);
 const cc_params4=ref(50);
+// 存储动态 ref 的对象
+const tempPlayerRefs = ref({});
 const alarm_list = ref([{
   code:"none",
   count:0
@@ -116,20 +120,25 @@ const load=(houseinfo,v_alarm_type)=>{
     provinceList.value=[{name:"四川省",code:"510000"}];
     provinceValue.value = provinceList.value[0].code;
     houselist.value=([{
-      name:"自贡中心仓库",
+      name:"自贡中心仓库-001",
+      camer_code:"zg-001",
       code:"zg",
       city:"510300",
       addr:"自贡市沿滩区金川路东段28号高新仓储物流园一期",
       lnglat:[0,0],
       alarmtype:"none",
+      camername:"1-10001号摄像头",
+      video_url:"http://47.108.159.150:8080/flv/34020000001110000001/34020000001310000055",
     },{
-      name:"成都中心仓库",
+      name:"自贡中心仓库-002",
+      camer_code:"zg-002",
       code:"cd",
       city:"510100",
       addr:"简阳市石桥镇羊羊小镇大华国际B1栋13楼1322号",
       lnglat:[0,0],
       alarmtype:"smoke",
       camername:"1-10002号摄像头",
+      video_url:"http://47.108.159.150:8080/flv/34020000001110000001/34020000001310000055",
     }]);
     for (let index = 0; index < houselist.value.length; index++) {
         const element = houselist.value[index];
@@ -144,6 +153,9 @@ const load=(houseinfo,v_alarm_type)=>{
 }
 const closeDlg=() =>{
     //销毁资源
+    for(let k in tempPlayerRefs.value){
+      tempPlayerRefs.value[k].destroy();
+    }
     showHouse.value = false;
 }
 const selectedHouse=(item)=>{
@@ -311,11 +323,23 @@ const loadEchars03=()=>{
 }
 const initvideodata=()=>{
   let tmp = [];
-  for (let index = 0; index < videocount_type.value*1; index++) {
-    tmp.push({name:'视频'+index,code:"code"+index,contrlset:false,state:'offline',statedesc:'离线',url:""});
+  for (let index = 0; index < Math.min(videocount_type.value*1,houselist.value.length); index++) {
+    let ele = houselist.value[index];
+    tmp.push({name:ele.camername,code:ele.camer_code,contrlset:false,state:'offline',statedesc:'离线',url:ele.video_url});
   }
   video_list.value = tmp;
+  setTimeout(() => {
+    for(let i=0;i<tmp.length;i++){
+      tempPlayerRefs.value[tmp[i].code].play(tmp[i].url);
+    }
+  }, 200);
 }
+// 动态设置 ref 的函数
+const handleSetTempMap = (el, item) => {
+  if (el) {
+  tempPlayerRefs.value[item.code] = el;
+  }
+};
 const videocontrl=(item)=>{
   item['contrlset'] = true
 }

+ 446 - 0
src/views/components/player.vue

@@ -0,0 +1,446 @@
+<template>
+    <div class="root">
+      <div class="container-shell">
+        <div class="container-shell-title" v-show="false">jessibuca demo player <span class="tag-version" v-if="version">({{
+            version
+          }})</span></div>
+        <div class="option" v-show="false">
+          <span>缓冲(秒):</span>
+          <input
+              style="width: 50px"
+              type="number"
+              ref="buffer"
+              value="0.2"
+              @change="changeBuffer"
+          />
+          <input
+              type="checkbox"
+              v-model="useMSE"
+              ref="vod"
+              @change="restartPlay('mse')"
+          /><span>MediaSource</span>
+          <input
+              type="checkbox"
+              v-model="useWCS"
+              ref="vod"
+              @change="restartPlay('wcs')"
+          /><span>webcodecs</span>
+  
+        </div>
+        <div id="container" ref="container"></div>
+        <div class="input" v-show="false">
+          <div>输入URL:</div>
+          <input
+              type="input"
+              autocomplete="on"
+              ref="playUrl"
+              value=""
+          />
+          <button v-if="!playing" @click.stop="play">播放</button>
+          <button v-else @click="pause">停止</button>
+        </div>
+        <div class="input" v-if="false && loaded" style="line-height: 30px">
+          <button @click="destroy">销毁</button>
+          <button v-if="quieting" @click="cancelMute">取消静音</button>
+          <template v-else>
+            <button @click="mute">静音</button>
+            音量
+            <select v-model="volume" @change="volumeChange">
+              <option value="1">100</option>
+              <option value="0.75">75</option>
+              <option value="0.5">50</option>
+              <option value="0.25">25</option>
+            </select>
+          </template>
+          <span>旋转</span>
+          <select v-model="rotate" @change="rotateChange">
+            <option value="0">0</option>
+            <option value="90">90</option>
+            <option value="270">270</option>
+          </select>
+  
+          <button @click="fullscreen">全屏</button>
+          <button @click="screenShot">截图</button>
+          <div style="line-height: 30px">
+            <input
+                type="checkbox"
+                ref="operateBtns"
+                v-model="showOperateBtns"
+                @change="restartPlay"
+            /><span>操作按钮</span>
+            <input
+                type="checkbox"
+                ref="operateBtns"
+                v-model="showBandwidth"
+                @change="restartPlay"
+            /><span>网速</span>
+            <span v-if="performance">性能:{{ performance }}</span>
+          </div>
+        </div>
+        <div class="input" v-if="false && loaded">
+          <input
+              type="checkbox"
+              ref="offscreen"
+              v-model="useOffscreen"
+              @change="restartPlay('offscreen')"
+          /><span>离屏渲染</span>
+  
+          <select v-model="scale" @change="scaleChange">
+            <option value="0">完全填充(拉伸)</option>
+            <option value="1">等比缩放</option>
+            <option value="2">完全填充(未拉伸)</option>
+          </select>
+          <button v-if="!playing" @click="clearView">清屏</button>
+          <template v-if="playing">
+            <select v-model="recordType">
+              <option value="webm">webm</option>
+              <option value="mp4">mp4</option>
+            </select>
+            <button v-if="!recording" @click="startRecord">录制</button>
+            <button v-if="!recording" @click="stopAndSaveRecord">暂停录制</button>
+          </template>
+  
+        </div>
+      </div>
+    </div>
+  </template>
+  <script>
+  
+  export default {
+    name: "DemoPlayer",
+    props: {},
+    data() {
+      return {
+        jessibuca: null,
+        version: '',
+        wasm: false,
+        vc: "ff",
+        playing: false,
+        quieting: true,
+        loaded: false, // mute
+        showOperateBtns: false,
+        showBandwidth: false,
+        err: "",
+        speed: 0,
+        performance: "",
+        volume: 1,
+        rotate: 0,
+        useWCS: false,
+        useMSE: true,
+        useOffscreen: false,
+        recording: false,
+        recordType: 'webm',
+        scale: 0
+      };
+    },
+    mounted() {
+      this.create();
+      window.onerror = (msg) => (this.err = msg);
+    },
+    async unmounted() {
+      if(this.jessibuca){
+        await this.jessibuca.destroy();
+        this.jessibuca = null;
+      }
+    },
+    methods: {
+      create(options) {
+        options = options || {};
+        this.jessibuca = new window.Jessibuca(
+            Object.assign(
+                {
+                  container: this.$refs.container,
+                  videoBuffer: Number(this.$refs.buffer.value), // 缓存时长
+                  isResize: false,
+                  useWCS: this.useWCS,
+                  useMSE: this.useMSE,
+                  text: "",
+                  // background: "bg.jpg",
+                  loadingText: "疯狂加载中...",
+                  // hasAudio:false,
+                  debug: true,
+                  supportDblclickFullscreen: true,
+                  showBandwidth: this.showBandwidth, // 显示网速
+                  operateBtns: {
+                    fullscreen: this.showOperateBtns,
+                    screenshot: this.showOperateBtns,
+                    play: this.showOperateBtns,
+                    audio: this.showOperateBtns,
+                  },
+                  vod: this.vod,
+                  forceNoOffscreen: !this.useOffscreen,
+                  isNotMute: true,
+                  timeout: 10
+                },
+                options
+            )
+        );
+        var _this = this;
+        this.jessibuca.on("load", function () {
+          console.log("on load");
+        });
+  
+        this.jessibuca.on("log", function (msg) {
+          console.log("on log", msg);
+        });
+        this.jessibuca.on("record", function (msg) {
+          console.log("on record:", msg);
+        });
+        this.jessibuca.on("pause", function () {
+          console.log("on pause");
+          _this.playing = false;
+        });
+        this.jessibuca.on("play", function () {
+          console.log("on play");
+          _this.playing = true;
+        });
+        this.jessibuca.on("fullscreen", function (msg) {
+          console.log("on fullscreen", msg);
+        });
+  
+        this.jessibuca.on("mute", function (msg) {
+          console.log("on mute", msg);
+          _this.quieting = msg;
+        });
+  
+        this.jessibuca.on("mute", function (msg) {
+          console.log("on mute2", msg);
+        });
+  
+        this.jessibuca.on("audioInfo", function (msg) {
+          console.log("audioInfo", msg);
+        });
+  
+        // this.jessibuca.on("bps", function (bps) {
+        //   // console.log('bps', bps);
+        // });
+        // let _ts = 0;
+        // this.jessibuca.on("timeUpdate", function (ts) {
+        //     console.log('timeUpdate,old,new,timestamp', _ts, ts, ts - _ts);
+        //     _ts = ts;
+        // });
+  
+        this.jessibuca.on("videoInfo", function (info) {
+          console.log("videoInfo", info);
+        });
+  
+        this.jessibuca.on("error", function (error) {
+          console.log("error", error);
+        });
+  
+        this.jessibuca.on("timeout", function () {
+          console.log("timeout");
+        });
+  
+        this.jessibuca.on('start', function () {
+          console.log('frame start');
+        })
+  
+        this.jessibuca.on("performance", function (performance) {
+          var show = "卡顿";
+          if (performance === 2) {
+            show = "非常流畅";
+          } else if (performance === 1) {
+            show = "流畅";
+          }
+          _this.performance = show;
+        });
+        this.jessibuca.on('buffer', function (buffer) {
+          console.log('buffer', buffer);
+        })
+  
+        this.jessibuca.on('stats', function (stats) {
+          console.log('stats', stats);
+        })
+  
+        this.jessibuca.on('kBps', function (kBps) {
+          console.log('kBps', kBps);
+        });
+  
+        this.jessibuca.on("play", () => {
+          this.playing = true;
+          this.loaded = true;
+          this.quieting = this.jessibuca.isMute();
+        });
+  
+        this.jessibuca.on('recordingTimestamp', (ts) => {
+          console.log('recordingTimestamp', ts);
+        })
+  
+  
+        // console.log(this.jessibuca);
+      },
+      play(url) {
+        // this.jessibuca.onPlay = () => (this.playing = true);
+        console.log("==========开始播放:"+url)
+        if (url) {
+          if(!this.loaded){
+            this.create();
+          }
+          this.jessibuca.play(url || this.$refs.playUrl.value);
+        }
+      },
+      mute() {
+        this.jessibuca.mute();
+      },
+      cancelMute() {
+        this.jessibuca.cancelMute();
+      },
+  
+      pause() {
+        this.jessibuca.pause();
+        this.playing = false;
+        this.err = "";
+        this.performance = "";
+      },
+      volumeChange() {
+        this.jessibuca.setVolume(this.volume);
+      },
+      rotateChange() {
+        this.jessibuca.setRotate(this.rotate);
+      },
+      async destroy() {
+        if (this.jessibuca) {
+          await this.jessibuca.destroy();
+        }
+        //this.create();
+        this.playing = false;
+        this.loaded = false;
+        this.performance = "";
+      },
+  
+      fullscreen() {
+        this.jessibuca.setFullscreen(true);
+      },
+  
+      clearView() {
+        this.jessibuca.clearView();
+      },
+  
+      startRecord() {
+        const time = new Date().getTime();
+        this.jessibuca.startRecord(time, this.recordType);
+      },
+  
+      stopAndSaveRecord() {
+        this.jessibuca.stopRecordAndSave();
+      },
+  
+  
+      screenShot() {
+        this.jessibuca.screenshot();
+      },
+  
+  
+      async restartPlay(type) {
+  
+        if (type === 'mse') {
+          this.useWCS = false;
+          this.useOffscreen = false;
+        } else if (type === 'wcs') {
+          this.useMSE = false
+        } else if (type === 'offscreen') {
+          this.useMSE = false
+        }
+  
+        await this.destroy();
+        this.create();
+        setTimeout(() => {
+          this.play();
+        }, 100)
+      },
+  
+      changeBuffer() {
+        this.jessibuca.setBufferTime(Number(this.$refs.buffer.value));
+      },
+  
+      scaleChange() {
+        this.jessibuca.setScaleMode(this.scale);
+      },
+    },
+  };
+  </script>
+  <style>
+  .root {
+    display: flex;
+    place-content: center;
+    margin-top: 0;
+    height: 100%;
+    width: 100%;
+  }
+  
+  .container-shell {
+    position: relative;
+    backdrop-filter: blur(5px);
+    background: hsla(0, 0%, 50%, 0.5);
+    padding: 0;
+    /* border: 2px solid black; */
+    height: 100%;
+    width: 100%;
+    position: relative;
+    border-radius: 5px;
+    box-shadow: 0 10px 20px;
+  }
+  
+  .container-shell-title {
+    position: absolute;
+    color: darkgray;
+    top: 4px;
+    left: 10px;
+    text-shadow: 1px 1px black;
+  }
+  
+  .tag-version {
+  }
+  
+  #container {
+    background: rgba(13, 14, 27, 0.7);
+    width: 100%;
+    height: 100%;
+  }
+  
+  .input {
+    display: flex;
+    align-items: center;
+    margin-top: 10px;
+    color: white;
+    place-content: stretch;
+  }
+  
+  .input2 {
+    bottom: 0px;
+  }
+  
+  .input input[type='input'] {
+    flex: auto;
+  }
+  
+  .err {
+    position: absolute;
+    top: 40px;
+    left: 10px;
+    color: red;
+  }
+  
+  .option {
+    position: absolute;
+    top: 4px;
+    right: 10px;
+    display: flex;
+    place-content: center;
+    font-size: 12px;
+  }
+  
+  .option span {
+    color: white;
+  }
+  
+  
+  @media (max-width: 720px) {
+    #container {
+      width: 90vw;
+      height: 52.7vw;
+    }
+  }
+  </style>
+  

+ 14 - 3
src/views/index.vue

@@ -36,6 +36,7 @@
           <div class="linkpoint leftbottompoint"></div>
           <div class="linkpoint rightbottompoint"></div>
           <div class="camername">{{ currentHouse.camername }}</div>
+          <Player ref="playercontainer"/>
         </div>
       </div>
       <house ref="houseComp" />
@@ -46,6 +47,8 @@ import AMapLoader from '@amap/amap-jsapi-loader';
 import {shallowRef,onMounted} from "vue";
 import * as echarts from "echarts";
 import House from './components/housecamer.vue'
+import Player from './components/player.vue'
+const playercontainer=ref(null)
 const map = shallowRef(null);
 const AMap = ref();
 const echarts01 = ref(null);
@@ -112,6 +115,7 @@ const houselist=ref([{
   addr:"自贡市沿滩区金川路东段28号高新仓储物流园一期",
   lnglat:[0,0],
   alarmtype:"none",
+  video_url:"http://47.108.159.150:8080/flv/34020000001110000001/34020000001310000055",
 },{
   name:"成都中心仓库",
   code:"cd",
@@ -120,6 +124,7 @@ const houselist=ref([{
   lnglat:[0,0],
   alarmtype:"smoke",
   camername:"1-10002号摄像头",
+  video_url:"http://47.108.159.150:8080/flv/34020000001110000001/34020000001310000055",
 }]);
 
 const initHouseLoca=()=>{
@@ -135,6 +140,9 @@ const toHouse=(item)=>{
 }
 
 const inHouse=()=>{
+  if(playercontainer.value!=null){
+    playercontainer.value.pause();
+  }
   houseComp.value.load(currentHouse,alarm_type.value);
 }
 
@@ -177,8 +185,10 @@ const addMarker=(houseinfo,lnglat) =>{
       let extdata = e.target.getExtData();
       currentHouse.value = extdata;
       infoWindow.setContent(infoWindowContent.value)
-      infoWindow.open(map.value,e.target.getPosition());      
+      infoWindow.open(map.value,e.target.getPosition());
+      playercontainer.value.play(currentHouse.value.video_url)
     }else{
+      playercontainer.value.destroy();
       document.getElementById(currentHouseCode.value).className = "custom-marker";
       infoWindow.close();
       showInfoWindow.value=false;
@@ -811,13 +821,14 @@ onMounted(() => {
         width: auto;
         line-height: 16px;
         position: absolute;
-        left: 5px;
+        right: 5px;
         top: 5px;
         font-size: 12px;
-        background-color: #0000004f;
+        background-color: #00000080;
         color: #fff;
         padding: 5px 15px;
         border-radius: 20px;
+        z-index: 100;
   }
   .linkpoint{
     width: 10px;

Some files were not shown because too many files changed in this diff