vue项目基于vue-video-player实现rtmp直播流

vue播放器插件

项目要求:

我们做的是一个监控项目,需要实时监控某机器,播放器在监控页面可拖动

插件选择:

项目是vue框架,播放器选择了vue-video-player插件, vue-video-player,其实就是 video.js 集成到 vue 中 ,视频流选择rtmp格式,实时性高,稳定性也高,但是依赖flash插件。

使用步骤

  • 安装 vue-video-player
npm install vue-video-player -S
  • 引用插件
import 'video.js/dist/video-js.css';
import 'vue-video-player/src/custom-theme.css';
import { videoPlayer } from 'vue-video-player';
import 'videojs-flash';

注意:
1、vue-video-player 其实就是 video.js 集成到 vue 中,所以千万不要再安装 video.js,可能会出错。
2、播放 HLS 流,需要 videojs-contrib-hls 插件,(!直接引用,因为在安装vue-video-player插件时,hls插件是一并下载下来的),如果需要 RTMP 流,需要 videojs-flash 插件,也是直接引用就可以了( flash 插件需要在 hls 之前引用)

  • 使用插件播放器
<template>
  <div
    id="videoBox"
    ref="videoBox"
    v-drag
    class="video-box"
  >
    <div class="video-box-header">
      <div>
        <svg-icon
          class="video-box-header-mobile"
          icon-class="mobile"
        />
        <span>AGV监控视窗</span>
      </div>
      <a
        class="el-icon-arrow-up"
        @click="show=!show"
      />
    </div>
    <el-collapse-transition>
      <div v-if="show">
        <videoPlayer
          ref="videoPlayer"
          :options="videoOptions"
          class="vjs-custom-skin videoPlayer"
          :playsinline="false"
        />
      </div>
    </el-collapse-transition>
  </div>
</template>
<script>
import 'video.js/dist/video-js.css';
import 'vue-video-player/src/custom-theme.css';
import { videoPlayer } from 'vue-video-player';
import 'videojs-contrib-hls';
import 'videojs-flash';
import ResizeMixin from '@/layout/mixin/ResizeHandler';
export default {
  components: {
    videoPlayer,
  },
  //注册局部组件指令
  directives: {
    drag: function(el) {
      let dragBox = el; //获取当前元素
      const sliderMenuWidth = 210;
      const navBarHeight = 50;
      dragBox.onmousedown = e => {
        //算出鼠标相对元素的位置
        let disX = e.clientX - dragBox.offsetLeft;
        let disY = e.clientY - dragBox.offsetTop;
        document.onmousemove = e => {
          //用鼠标的位置减去鼠标相对元素的位置,得到元素的位置
          let left = e.clientX - disX;
          let top = e.clientY - disY;
          //移动当前元素

          if (left <= 0) {
            left = 0;
          } else if (left >= document.documentElement.clientWidth - dragBox.offsetWidth - sliderMenuWidth) {
            //document.documentElement.clientWidth 屏幕的可视宽度
            left = document.documentElement.clientWidth - dragBox.offsetWidth - sliderMenuWidth;
          }

          if (top <= 0) {
            top = 0;
          } else if (top >= document.documentElement.clientHeight - dragBox.offsetHeight - navBarHeight) {
            // document.documentElement.clientHeight 屏幕的可视高度
            top = document.documentElement.clientHeight - dragBox.offsetHeight - navBarHeight;
          }

          dragBox.style.right = 0;
          dragBox.style.left = left + 'px';
          dragBox.style.top = top + 'px';
        };
        document.onmouseup = e => {
          //鼠标弹起来的时候不再移动
          document.onmousemove = null;
          //预防鼠标弹起来后还会循环(即预防鼠标放上去的时候还会移动)
          document.onmouseup = null;
        };
      };
    },
  },
  mixins: [ResizeMixin],
  props: {
    videoSrc: { type: String, default: 'rtmp://127.0.0.1/oflaDemo/B0_2' },
    poster: {
      type: String,
      default: '',
    },
  },
  data() {
    return {
      show: true,
      player: null,
      // 视频播放
      videoOptions: {
        live: true,
        playbackRates: [0.7, 1.0, 1.5, 2.0], //播放速度
        autoplay: true, //如果true,浏览器准备好时开始回放。
        muted: false, // 默认情况下将会消除任何音频。
        loop: false, // 导致视频一结束就重新开始。
        preload: 'auto', // 建议浏览器在<video>加载元素后是否应该开始下载视频数据。auto浏览器选择最佳行为,立即开始加载视频(如果浏览器支持)
        language: 'zh-CN',
        aspectRatio: '4:3', // 将播放器置于流畅模式,并在计算播放器的动态大小时使用该值。值应该代表一个比例 - 用冒号分隔的两个数字(例如"16:9"或"4:3")
        techOrder: ['flash', 'html5'], // 兼容顺序'flash', 'html5'
        sources: [
          {
            // 流配置,数组形式,会根据兼容顺序自动切换
            type: 'rtmp/flv', //application/x-mpegURL,'rtmp/flv',
            src: this.videoSrc,
          },
        ],
        poster: this.poster, //你的封面地址
        notSupportedMessage: '此视频暂无法播放,请稍后再试', // 允许覆盖Video.js无法播放媒体源时显示的默认信息。
        controls: true, // 是否拥有控制条 【默认true】,如果设为false ,那么只能通过api进行控制了。也就是说界面上不会出现任何控制按钮
        controlBar: {
          children: [{ name: 'FullscreenToggle' }],
        },
      },
    };
  },

  watch: {
    containerSize: {
      handler() {
        this.handlerVideoPostion();
      },
      deep: true,
    },
    show(value) {
      if (!value) {
        this.$refs.videoPlayer.dispose();
      }
    },
  },
 beforeDestroy() {
  this.$refs.videoPlayer.dispose();
  },
  created() {
    this.show = true;
  },
  mounted() {
    this.player = this.$refs.videoPlayer.player;
  },
  methods: {
    handlerVideoPostion() {
      const videoBox = this.$refs.videoBox;
      if (videoBox.offsetTop + videoBox.offsetHeight >= this.containerSize.height) {
        videoBox.style.top = this.containerSize.height - videoBox.offsetHeight + 'px';
      }
      if (videoBox.offsetLeft + videoBox.offsetWidth >= this.containerSize.width) {
        videoBox.style.left = this.containerSize.width - videoBox.offsetWidth + 'px';
      }
    },
  },
};
</script>
<style   lang="scss"  scoped>
$width: 420px;
$headerHeight: 40px;
$textColor: #999;
.video-box {
  width: $width;
  /* padding: 20px; */
  position: absolute;
  right: 0;
  top: calc(100% - (#{$width} * 3 / 4) - #{$headerHeight});
  left: calc(100% - #{$width});
  border-radius: 4px;
  background: #fff;
  box-shadow: 0px 2px 8px 0px rgba(0, 0, 0, 0.1);
  /deep/.vjs-custom-skin > .video-js .vjs-big-play-button {
    display: none;
  }
  &-header {
    // height: $headerHeight;
    line-height: $headerHeight;
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 0 15px;
    font-size: 14px;
    color: $textColor;

    & > div {
      display: flex;
      align-items: center;
    }
    &-mobile {
      width: 20px;
      height: 20px;
      margin-right: 5px;
    }
  }
}
</style>

注意事项:

1、autoplay: true,不能自动播放,因为flash插件内部的问题吧,视频的宽度不能小于400,如果小于400自动播放就不生效。

解决办法
设置 video 播放器的 宽度大于等于 400;

2、因为我这边有可以动态折叠video播放器的功能,之前用的v-show去实现的,发现每次折叠隐藏播放组件后都会报错:this.el_.vjs_getProperty is not a function

在这里插入图片描述

videojs-flash.es.js?b5c1:582 Uncaught TypeError: this.el_.vjs_getProperty is not a function
    at Flash.buffered (videojs-flash.es.js?b5c1:582)
    at Flash.bufferedPercent$$1 [as bufferedPercent] (video.cjs.js?3d33:8176)
    at Flash.eval (video.cjs.js?3d33:8118)
    at Flash.bound (video.cjs.js?3d33:2344)
    at bound (video.cjs.js?3d33:2344)

猜测应该是折叠后flash获取不到他的视频流报的错。

解决办法:
使用v-if代替v-show,并且在不显示的时候使用 dispose() 方法销毁 VideoPlayer 实例组件, this.$refs.videoPlayer.dispose(); v-if 为 true 的时候去自动重建 VideoPlayer 实例组件。

3、“The “flash” tech is undefined. Skipped browser support check for that tech”报错
VIDEOJS: ERROR: The "flash" tech is undefined. Skipped browser support check for that tech.

初级解决方案:

使用cnpm安装就会报错,使用npm安装video相关模块没问题,如果对使用 cnpm 还是npm 没有特定要求的话,可以使用这种方法解决。不过这不是最好的解决方案,只会治标不治本。

最终解决方案:

不管是删除node_module还是使用npm都不是根本的解决方案,根本原因是videojs和videojs-flash里的各有一个video.js,如果两个版本不一样可能就会报错了,终极解决方案就是配置第三方模块的查找顺序,优先查找本身安装的videojs就可以了

修改 webpack.config.js 文件

resolve: {
   modules: [path.resolve('node_modules'), 'node_modules'],
	...
}

注意: 如果是用vue-cli3项目脚手架搭建的项目,修改vue.config.js 文件

configureWebpack: (config) => {
    config.resolve.modules[0] = path.resolve(__dirname, 'node_modules');
    config.resolve.modules[1] = 'node_modules';
  },

这里为什么要用这个方法呐,是因为vue-cli3 构建出来的项目里面,modules会存在三个不同的node_modules:

第一个:自身某块里的node_modules;
第二个:根目录里面的node_modules;
第三个:使用vue-cli3 构建项目,项目运行的时候会创建一个node_modules;

所以这里我们不能用覆盖的方法,只能使用更改modules数组某个元素的方法去更改查找顺序

至此问题解决!


版权声明:本文为weixin_39863895原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。