实现videojs接入m3u8视频,踩了多少坑啊

实现vue3通过videojs接入m3u8视频,一天多总算搞成功了。下面就扒一扒我在实现过程中踩的那些坑。。。。。。

前言

我们最常见的mp4类型的视频,直接通过vue原生的video引入就可以了。

<video :src="video" autoPlay controls muted loop />

但是当接入的视频是直播视频流时,video是不支持的,所以我们需要通过一定的插件辅助,在实现功能前先介绍几个概念。

1.HLS,M3U8

一听需求是要接入海康的视频,什么hls,m3u8视频,孤陋寡闻的我立马去找度娘。

HLS是一个基于http的流媒体网络传输协议,传输内容包括两部分,M3U8描述文件和TS媒体文件。

M3U8文件是指UTF-8编码格式的M3U文件。M3U文件是一个文本文件,记录视频文件的索引。

概括说,hls协议将流媒体切片成的ts文件和m3u8文件,通过m3u8索引文件按序访问ts文件,客户端不停地从服务器获取视频文件,进而实现在线播放音视频的功能。

本文是通过videojs插件来实现视频接入的。

功能实现

1.安装依赖和引入

npm install --save video.js
npm install --save videojs-contrib-hls

在index.html引入

    <!-- 视频取流 -->
    <link href="https://cdn.bootcss.com/video.js/7.20.3/alt/video-js-cdn.min.css" rel="stylesheet">
    <script src="https://cdn.bootcss.com/video.js/7.20.3/video.js"></script>
    <script src="https://cdn.bootcss.com/videojs-contrib-hls/5.15.0/videojs-contrib-hls.min.js"></script>

注:这里是第一个坑,看了很多方法都是直接安装依赖就可以,没有这一步。但是,如果不引入请求是成功的,但是视频却加载不出来。

在这里插入图片描述

加上后就能正常显示了
在这里插入图片描述
文件内局部引入

import videojs from 'video.js';
import 'videojs-contrib-hls';
import 'video.js/dist/video-js.css';

2.代码实现

template代码

  <div id="video-box" class="video-item" v-show="showFlag">
    <video
      id="my-video"
      class="video-js vjs-default-skin"
      controls
      preload="auto"
      style="width: 100%;height:100%"
    >
      <source id="source" :src="xxxx视频请求网址xxxx" type="application/x-mpegURL" />
    </video>
  </div>

初始化

const myPlayer = ref(null);
const initVideo = () => {
//初始化配置
  myPlayer.value = videojs('my-video',{
    bigPlayButton: false,
    textTrackDisplay: false,
    posterImage: true,
    errorDisplay: false,
    controlBar: true,
  });
 // 播放
  myPlayer.value.play();
};

onMounted(() => {
  setTimeout(() => {
      initVideo();
  }, 300);
});

注:第二个坑来了!当视频组件放到弹窗里,如果报错
TypeError: The element or ID supplied is not valid. (videojs)

这时就需要注意两点:

  1. 组件外层的div不能使用v-if,要使用v-show控制显隐
  2. videojs的初始化要在dom全部挂载后的,所以可以采用使用setTimeout延迟加载

以上是固定地址的写法,但是需求是要切换不同视频地址来源,还需要进一步优化

先将视频地址设为变量

 <source id="source" :src="cameraURL" type="application/x-mpegURL" />
  
 const srcPath = ref<string>('');
 const initVideo = (url:string) => {
  myPlayer.value = videojs('my-video',{
    bigPlayButton: false,
    textTrackDisplay: false,
    posterImage: true,
    errorDisplay: false,
    controlBar: true,
  });
  // 设置url
  myPlayer.value.src(url);
  myPlayer.value.play();
};

注:第三个坑来了!在网上学习的过程中有些是下面这种写法,直接赋值属性值并且不在template里写source部分。我开始用这种方法,发现怎么都不生效。最后换成了上面的写法。(如果有大神知道原因还请指教)

    myPlayer.src([
        {
            type: "application/x-mpegURL",
            src: url 
        }
    ]);

注:紧接着问题又来了,切换url后视频加载不出来,查看网络发现切换url后的第一个请求会显示已取消
在这里插入图片描述
解决方法是将这条请求过滤

  videojs.hook('beforeerror', (player, err) => {
    // Video.js 在切换/指定 source 后立即会触发一个 err=null 的错误,这里过滤一下
    if (err !== null) {
      myPlayer.value.src(url);
    }

    // 清除错误,避免 error 事件在控制台抛出错误
    return null;
  });

请求都正常发现视频还是加载不出来,还会报错
解决办法:只能在切换来源的时候销毁原来的组件,再重新添加一个videojs并赋值。

补充:控制台报错但不影响视频正常显示。原因:video.js and videojs-contrib-hls版本冲突,解决:将videojs版本从7.20.3降为videojs 7.5.5\videojs-contrib-hls 5.15.0,无报错。

在这里插入图片描述

最后附上成功实现的完整代码

template部分

<template>
  <div id="video-box" class="video-item" v-show="showVideo">
  </div>
</template>

Js部分

const myPlayer = ref<any>(null);
const mountedFlag = ref<boolean>(false);
// 初始化视频组件
const initVideo = (url:string) => {
  if (myPlayer.value) {
    myPlayer.value.pause();
    myPlayer.value.dispose();
  }
  // 向Dom中写入视频组件
  document.getElementById('video-box').innerHTML = '';
  const html = `<video id="fire-video" class="video-js vjs-default-skin vjs-big-play-centered" controls preload="auto" style="width: 100%;height:100%">
        <source id="source" src="${url}" type="application/x-mpegURL">
    </video>`;

  document.getElementById('video-box').innerHTML = html;
  // 初始化声明
  myPlayer.value = videojs('fire-video',{
    autoplay: 'muted',
    bigPlayButton: false,
    textTrackDisplay: false,
    posterImage: true,
    errorDisplay: false,
    controlBar: true,
  });
  videojs.hook('beforeerror', (player, err) => {
    // Video.js 在切换/指定 source 后立即会触发一个 err=null 的错误,这里过滤一下
    if (err !== null) {
      myPlayer.value.src(url);
    }

    // 清除错误,避免 error 事件在控制台抛出错误
    return null;
  });

  myPlayer.value.play();
};

// 监听,需要视频url由其他方法赋值且组件挂载完成后,才能进行初始化
watch([cameraURL,mountedFlag,], ([url,flag,]): void => {
  if (url && flag) {
    initVideo(url);
  }
});

onMounted(() => {
  setTimeout(() => {
    mountedFlag.value = true;
  }, 500);
});
// 组件销毁
onUnmounted(() => {
  if (myPlayer.value) {
    mountedFlag.value = false;
  }
});

参考文档:

https://cloud.tencent.com/developer/article/1782402
https://blog.csdn.net/u012961419/article/details/120989905

猜你喜欢

转载自blog.csdn.net/layliangbo/article/details/127075607