草庐IT

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

寒潮_C 2023-06-07 原文

实现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

有关实现videojs接入m3u8视频,踩了多少坑啊的更多相关文章

  1. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  2. ruby - 可以通过多少种方法将方法添加到 ruby​​ 对象? - 2

    当谈到运行时自省(introspection)和动态代码生成时,我认为ruby​​没有任何竞争对手,可能除了一些lisp方言。前几天,我正在做一些代码练习来探索ruby​​的动态功能,我开始想知道如何向现有对象添加方法。以下是我能想到的3种方法:obj=Object.new#addamethoddirectlydefobj.new_method...end#addamethodindirectlywiththesingletonclassclass这只是冰山一角,因为我还没有探索instance_eval、module_eval和define_method的各种组合。是否有在线/离线资

  3. 华为OD机试用Python实现 -【明明的随机数】 2023Q1A - 2

    华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o

  4. unity---接入Admob - 2

    目录1.AdmobSDK下载地址2.将下载好的unityPackagesdk导入到unity里​编辑 3.解析依赖到项目中

  5. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  6. MIMO-OFDM无线通信技术及MATLAB实现(1)无线信道:传播和衰落 - 2

     MIMO技术的优缺点优点通过下面三个增益来总体概括:阵列增益。阵列增益是指由于接收机通过对接收信号的相干合并而活得的平均SNR的提高。在发射机不知道信道信息的情况下,MIMO系统可以获得的阵列增益与接收天线数成正比复用增益。在采用空间复用方案的MIMO系统中,可以获得复用增益,即信道容量成倍增加。信道容量的增加与min(Nt,Nr)成正比分集增益。在采用空间分集方案的MIMO系统中,可以获得分集增益,即可靠性性能的改善。分集增益用独立衰落支路数来描述,即分集指数。在使用了空时编码的MIMO系统中,由于接收天线或发射天线之间的间距较远,可认为它们各自的大尺度衰落是相互独立的,因此分布式MIMO

  7. 【Java入门】使用Java实现文件夹的遍历 - 2

    遍历文件夹我们通常是使用递归进行操作,这种方式比较简单,也比较容易理解。本文为大家介绍另一种不使用递归的方式,由于没有使用递归,只用到了循环和集合,所以效率更高一些!一、使用递归遍历文件夹整体思路1、使用File封装初始目录,2、打印这个目录3、获取这个目录下所有的子文件和子目录的数组。4、遍历这个数组,取出每个File对象4-1、如果File是否是一个文件,打印4-2、否则就是一个目录,递归调用代码实现publicclassSearchFile{publicstaticvoidmain(String[]args){//初始目录Filedir=newFile("d:/Dev");Datebeg

  8. ruby - Arrays Sets 和 SortedSets 在 Ruby 中是如何实现的 - 2

    通常,数组被实现为内存块,集合被实现为HashMap,有序集合被实现为跳跃列表。在Ruby中也是如此吗?我正在尝试从性能和内存占用方面评估Ruby中不同容器的使用情况 最佳答案 数组是Ruby核心库的一部分。每个Ruby实现都有自己的数组实现。Ruby语言规范只规定了Ruby数组的行为,并没有规定任何特定的实现策略。它甚至没有指定任何会强制或至少建议特定实现策略的性能约束。然而,大多数Rubyist对数组的性能特征有一些期望,这会迫使不符合它们的实现变得默默无闻,因为实际上没有人会使用它:插入、前置或追加以及删除元素的最坏情况步骤复

  9. ruby - 使用 Ruby,计算 n x m 数组的每一列中有多少个 true 的简单方法是什么? - 2

    给定一个nxmbool数组:[[true,true,false],[false,true,true],[false,true,true]]有什么简单的方法可以返回“该列中有多少个true?”结果应该是[1,3,2] 最佳答案 使用转置得到一个数组,其中每个子数组代表一列,然后将每一列映射到其中的true数:arr.transpose.map{|subarr|subarr.count(true)}这是一个带有inject的版本,应该在1.8.6上运行,没有任何依赖:arr.transpose.map{|subarr|subarr.in

  10. ruby - "public/protected/private"方法是如何实现的,我该如何模拟它? - 2

    在ruby中,你可以这样做:classThingpublicdeff1puts"f1"endprivatedeff2puts"f2"endpublicdeff3puts"f3"endprivatedeff4puts"f4"endend现在f1和f3是公共(public)的,f2和f4是私有(private)的。内部发生了什么,允许您调用一个类方法,然后更改方法定义?我怎样才能实现相同的功能(表面上是创建我自己的java之类的注释)例如...classThingfundeff1puts"hey"endnotfundeff2puts"hey"endendfun和notfun将更改以下函数定

随机推荐