electron客户端中视频监控的实现HLS/HTTP-FLV

前言

  1. 对于前端方面对于视频监控实现 我的浅见是获取拉流视频地址 再将流视频地址推到video标签。推流过程主要体现在摄像头至服务端的过程 和前端无关。

  2. 基于第一点后 就是从直播(监控)协议入手 主要是三个RTMPHLSHTTP-FLV 具体的差异和优缺点可以参考文章https://zhuanlan.zhihu.com/p/485406496。简而言之就是RTMP低延迟且兼容性差(浏览器中只能通过Flash使用)、HTTP-FLV承接RTMP的优点 对于仅在flash使用 也有flv.js插件来解决、HLS是苹果推出的 供苹果系统使用。而在electron开发的客户端 不仅仅在windows中使用 也要兼容mac系统 这边就是仅考虑HLS 、HTTP-FLV协议的直播

效果图

electron客户端中视频监控的实现HLS/HTTP-FLV_第1张图片

组件代码

<template>
  <div class="monitor-items">
    <video :id="name" :controls="false" autoplay muted class="monitor-video-player" @click="showVideo" v-if="url && url !== ''"></video>
    <div class="no-info" v-else>暂无视频监控权限</div>

    <vxe-modal title="视频监控" :width="1000" v-model="visible" @close="closeVideo">
      <div style="padding: 20px">
        <video :id="name + 'modal'" controls="controls" autoplay muted class="modal-video-player"></video>
      </div>
    </vxe-modal>
  </div>
</template>
<script lang="ts">
import { defineComponent, nextTick, onUnmounted, watch } from 'vue';
import flvjs from 'flv.js';
import useVisible from '@/hooks/useVisible';
import Hls from 'hls.js';
let videoElement;

export default defineComponent({
  props: ['name', 'url'],
  setup(props) {
    let flvPlayer: any = null;
    let modalFlvPlayer: any = null;
    let { visible, showVisible, closeVisible } = useVisible();
    // 判断是否是flv视频流判断去加载使用什么库
    const isFlv = (url: string) => {
      return url.indexOf('.flv') > -1;
    };
    const initVideo = (url) => {
      nextTick(() => {
        videoElement = document.getElementById(props.name);
        if (videoElement) {
          if (isFlv(url)) {
            // flv.js
            if (flvjs.isSupported()) {
              flvPlayer = flvjs.createPlayer({
                type: 'flv',
                isLive: true,
                hasAudio: false,
                url: url,
              });
              flvPlayer.attachMediaElement(videoElement);
              flvPlayer.load();
              flvPlayer.play();
            }
          } else {
            // hls
            if (Hls.isSupported()) {
              flvPlayer = new Hls();
              flvPlayer.loadSource(url);
              flvPlayer.attachMedia(videoElement);
              flvPlayer.on(Hls.Events.MANIFEST_PARSED, () => {
                videoElement.play();
              });
              flvPlayer.on(Hls.Events.ERROR, (event, data) => {
                // 监听出错事件
              });
            }
          }
        }
      });
    };
    const closeVideo = () => {
      if (modalFlvPlayer && videoElement) {
        // flv
        videoElement.pause();
        if (isFlv(props.url)) {
          modalFlvPlayer.unload();
          modalFlvPlayer.destroy();
          modalFlvPlayer = null;
        } else {
          // Hls
          modalFlvPlayer.stopLoad();
          modalFlvPlayer.destroy();
          modalFlvPlayer = null;
        }
      }
      closeVisible();
    };
    const destroyVideo = () => {
      if (flvPlayer && videoElement) {
        videoElement.pause();
        if (isFlv(props.url)) {
          //flv
          flvPlayer.unload();
          flvPlayer.destroy();
          flvPlayer = null;
        } else {
          // Hls
          flvPlayer.stopLoad();
          flvPlayer.destroy();
          flvPlayer = null;
        }
      }
      closeVideo();
    };
    const showVideo = () => {
      showVisible();
      nextTick(() => {
        videoElement = document.getElementById(props.name + 'modal') as any;
        if (isFlv(props.url)) {
          if (flvjs.isSupported()) {
            modalFlvPlayer = flvjs.createPlayer({
              type: 'flv',
              isLive: true,
              hasAudio: false,
              url: props.url,
            });
            modalFlvPlayer.attachMediaElement(videoElement);
            modalFlvPlayer.load();
            modalFlvPlayer.play();
          }
        } else {
          if (Hls.isSupported()) {
            flvPlayer = new Hls();
            flvPlayer.loadSource(props.url);
            flvPlayer.attachMedia(videoElement);
            flvPlayer.on(Hls.Events.MANIFEST_PARSED, () => {
              videoElement.play();
            });
            flvPlayer.on(Hls.Events.ERROR, (event, data) => {
              // 监听出错事件
            });
          }
        }
      });
    };
    if (props.url && props.url !== '') {
      initVideo(props.url);
    }
    watch(
      () => props.url,
      () => {
        if (props.url && props.url !== '') {
          initVideo(props.url);
        }
      }
    );
    onUnmounted(() => {
      destroyVideo();
    });
    return { visible, showVisible, closeVisible, showVideo, closeVideo };
  },
});
</script>
<style lang="scss" scoped>
.monitor-items {
  width: 100%;
  height: 265px;
  background: #181a20;
}
.monitor-video-player {
  width: 100%;
  height: 265px;
  object-fit: fill;
  cursor: pointer;
}
.modal-video-player {
  width: 100%;
  height: 600px;
  object-fit: fill;
}
.no-info {
  display: flex;
  align-items: center;
  justify-content: center;
  color: #d3d8dc;
  width: 100%;
  height: 100%;
}
</style>

vue2

<template>
  <div class="video-detail-page">
    <div class="erp-form__header pd-16 header-bar__sticky">
      <div class="erp-form__title">视频详情</div>
      <div class="erp-form-bar">
        <div class="erp-default__btn" @click="back">返回</div>
      </div>
    </div>
    <div class="video-title">{{ videoParam.deviceName }}</div>
    <div class="video-wrap">
      <video id="video-webrtc" controls style="width: 970px; height: 545px; background-color: #fff"></video>
    </div>
  </div>
</template>
<script>
import flvjs from 'flv.js/dist/flv.min.js';
let flvPlayer = null;

export default {
  props: {
    videoParam: {
      type: [Object],
      default() {
        return {};
      }
    }
  },
  data() {
    return {
      player: null
    };
  },
  mounted() {
    if (flvjs.isSupported()) {
      this.$nextTick(() => {
        var videoElement = document.getElementById('video-webrtc');
        var url = this.videoParam.url;
        flvPlayer = flvjs.createPlayer({
          type: 'flv',
          isLive: true,
          hasAudio: false,
          url: url
        });
        flvPlayer.attachMediaElement(videoElement);
        flvPlayer.load();
        flvPlayer.play();
      });
    }
  },
  destroyed() {
    flvPlayer.unload();
    flvPlayer = null;
  },
  methods: {
    // 返回
    back() {
      this.$emit('cancel', false);
    }
  }
};
</script>
<style lang="scss">
.video-detail-page {
  .video-title {
    font-size: 16px;
    text-indent: 20px;
  }
  .video-wrap {
    width: 970px !important;
    height: 545px;
    margin-left: auto;
    margin-right: auto;
    margin-top: 30px;
    background: #000;
    position: relative;
  }
}
</style>

你可能感兴趣的:(electron,音视频,vue)