动手实现一个流媒体播放器

851 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第1天,点击查看活动详情

前言

上个项目中需要实现一个播放监控的视频的页面,播放的内容是流媒体,播放控制条除了正常的播放,暂停按钮外,还需要抓拍(录制一段内容,保存未jif)、分屏、全屏等功能。

先看一张效果图:画面是ffmpeg推的一个mp4的流。红框部分为控制条。

image-20220401200951484

第一次实现这样的功能,一时间有点懵。梳理过需求,发现以下可能遇到的技术难点。

  • 流媒体的播放(推拉流,以及相关状态的处理)
  • 如何对播放的画面进行截图
  • 全屏功能的实现

下面来逐一解决这些问题。

如何播放流

几种视频流比较以及在H5中播放的插件。

协议http-flvrtmphls
传输方式http流tcp流http流
视频封装格式flvflvTs文件
延迟
数据分段连续流连续流切片文件
h5播放flv.jsvideo.jshls.js

什么是 HTTP FLV ?

HTTP FLV 是将RTMP封装在HTTP协议之上的,可以更好的穿透防火墙等。rtmp和http-flv的视频格式都是flv格式的,只是传输协议而不同。rtmp是tcp的传输协议,而http-flv是http长链接的传输协议。

跟后端同学讨论后决定使用 http-flv 协议作为流的传输方式。前端的话使用 flvjs 来处理流。

什么是 flvjs ?

flvjs是 HTML5 视频播放器,纯原生 JavaScript 开发,不需要 Flash 插件。由 bilibili 网站开源(Github)。

flv.js的优点:

  • 具有 H.264 + AAC 编解码器播放功能
  • 多部分分段视频播放
  • HTTP FLV 低延迟实时流播放
  • FLV 通过 WebSocket 实时流播放
  • 兼容 Chrome, FireFox, Safari 10, IE11 和 Edge
  • 十分低开销,并且通过你的浏览器进行硬件加速

播放器的实现

在 vue 使用 flv 创建一个可复用的播放器组件。

  1. 安装

    npm install --save flv.js
    
  2. 新建 FlvLive.vue ,在模板中添加以下代码

<figure
   ref="videoContainer"
   data-fullscreen="false"
 >
   <video
     ref="video"
     autoplay
   >
     Your browser is too old which doesn't support HTML5 video.
   </video>
</figure>
  1. 在created中加入以下代码
created(){
    this.$nextTick(() => {
        if (flvjs.isSupported()) {
            const flvPlayer = flvjs.createPlayer({
                type: 'flv',
                isLive: true,
                url: this.src,
            });
            flvPlayer.attachMediaElement(this.$refs.video);
            flvPlayer.load();
            flvPlayer.play();
            this.flvPlayer = flvPlayer;
        }
    });
}

flvjs.isSupported()判断当前浏览器是否支持flv。

flvPlayer.attachMediaElement(this.$refs.video) 挂载video标签。

全屏功能的实现

这里使用webkitRequestFullScreen()API实现全屏功能。

来自MDN教程的解释。

webkitRequestFullScreen() 方法用于发出异步请求使元素进入全屏模式。

需要注意的是:如果全屏的元素是video,自定义的控制条会被全屏后的video元素覆盖住,更改z-index也不能解决。所以要放大video的父级元素。

this.$refs.videoContainer.webkitRequestFullScreen();

判断当前页面是否处于全屏状态

isFullScreen() {
    return Boolean(
        document.fullScreen ||
        document.webkitIsFullScreen ||
        document.mozFullScreen ||
        document.msFullscreenElement ||
        document.fullscreenElement,
    );
},

CSS部分

设置全屏时的背景,隐藏全屏时video标签中的控制条。

/* fullscreen */
html:-ms-fullscreen {
    width: 100%;
}
:-webkit-full-screen {
    background: #666;
}
figure[data-fullscreen='true'] {
    max-width: 100%;
    width: 100%;
    margin: 0;
    padding: 0;
    /* hide controls on fullscreen with WebKit */
    video::-webkit-media-controls {
        display: none !important;
    }
}

在点击全屏按钮时会改变figure[data-fullscreen='true'] {...} 这个属性。体现在如下代码中。

setFullscreenData(state) {
    this.$refs.videoContainer.setAttribute(
        'data-fullscreen',
        Boolean(state),
    );
},

抓拍功能的实现

主要实现方式是截取一段视频到canvas中然后使用RecordRTC对其进行录制,具体的实现方法在我的这篇文章中可以看到。 使用RecordRTC对video视频进行录制

可能会遇到的报错

Uncaught (in promise) DOMException:The play() request was interrupted by a new load request

报错信息表示:视频还没有准备好,就已经开始播放了。这种情况会出现在,视频地址错误的情况下。常常是地址为空,或者格式错误。

Uncaught (in promise) DOMException: The play() request was interrupted by a call to pause().

调用play()的时候,音频文件还没有加载完成导致的问题。建议给video标签加上autoplay。不然老是出现这个问题。到底是什么原因导致的还不知道。

同一页面中引入4个画面时,其他三个画面会报错。video标签的id一致导致的。使用ref以解决这个问题。

总结

通过实现一个自定义控制条的H5播放器,学习到了flvjs在vue中的使用。js的全屏API。以及一些document内置对象的使用。

这里有一些实现过程中写的Demo:gitee