视频播放器初探

245 阅读12分钟

前言

最近在项目中做了一个知识学习模块,里面有个简易播放器,基本的功能包括:支持m3u8流媒体播放,断点继播,上传播放进度(暂停、退出界面、app位于后台、闪退),统计播放时长,全屏事件横竖屏处理,在此记录一下,供后续有同样问题的小伙伴参考。

技术选型

以上的库都基于video.js。video.js是一款优秀的HTML5视频播放器,有很多人使用而且生态很好,github上有30K的Star。

1、项目中用到的vue-video-player是一款对video.js进行二次封装的库,由于最新版本的vue-video-player只支持vue3,因此要想兼容vue2只能选用旧版的vue-video-player5,vue-video-player5使用的video.js是6.x的版本。

2、videojs-landscape-fullscreen是一款可以使视频横竖屏切换的插件,支持感应手机的旋转角度自动横竖屏 。一开始发现视频播放器不能横向全屏播放,使用了这款插件也不生效,后面发现是原生将webview固定为横屏了,解决方法是让原生提供一个可以横竖屏切换的方法。

3、@videojs/http-streaming是一款可以使视频支持播放m3u8这类HLS流媒体资源的库。网上很多写着使用videojs-contrib-hls库去支持播放m3u8,但是实际使用videojs-contrib-hls库是不生效的,videojs-contrib-hls已经被弃用换成了 @videojs/http-streaming。

我之前还担忧使用vue-video-player5(这个库用的video.js6.x)会不会没有最新的video.js7.x稳定,后面雁冰大佬帮忙比较了video.js7和video.js6的区别,发现video.js7相比video.js6只是删了些旧版IE浏览器的支持和直接支持HLS流媒体资源,不需要我们去额外的安装 @videojs/http-streaming 两个版本的差别不是很大。因此项目所需功能使用vue-video-player5已经足够。

以上是本项目的技术选型,如果了解后对vue-video-player5这个技术选型还是不满意,你还可以试试字节跳动的西瓜播放器 :xgplayer(github.com/bytedance/x…)。这个播放器是我在写这篇文章时才发现的(如果早些发现我可能会用这个播放器试试),文档非常详细,可以快速配置的功能非常多,包括记忆播放弹幕功能,画中画,微信横竖屏控制等等,具体可以看文档了解下。( v2.h5player.bytedance.com/config/#%E8…

以下是西瓜播放器的背景:

几款播放器的优缺点

库名优点缺点
vue-video-player5(基于video.js6)基于video.js的二次封装,可以通过可配置项快速开发,可以引入定义好的样式,支持播放mp4和m3u8等常见格式,使用人数多,资料多。1、新版的vue-video-player只支持vue3和react,使用vue2只能用旧版vue-video-player5。2、由于2020年浏览器禁用flash,因此现在已不支持flv这类rtmp适合直播视频的格式,要支持需要使用flv.js,但是要集成好像很麻烦,网上也没有找到集成方法,可扩展型相对较弱。
xgplayer可配置项相比vue-video-player更多,功能也更多,文档也很全面。这个播放器对mp4格式的视频做了很多优化,可以实现了mp4分段播放,支持播放mp4、m3u8等常见视频格式,可以使用flv.js相关插件支持flv视频格式。
video.js7可自行封装,生态好,可扩展性强。自定义UI成本高,视频的清晰度无缝切换没有xgplayer好
DPlayer一个很好看的弹幕视频播放器,持播放mp4、m3u8等常见视频格式,可以使用flv.js相关插件支持flv视频格式,很多中国公司在用,像小红书,bilibili。文档内容比较简单

为什么使用m3u8格式的视频,如何使用?

m3u8是一种常见的流媒体格式,主要以文件列表的形式存在,既支持直播又支持点播,尤其在Android、iOS等平台最为常用。

m3u8准确来说是一种索引文件,使用m3u8文件实际上是通过它来解析对应的放在服务器上的视频网络地址,从而实现在线播放。它可以实现多码率视频的适配,视频网站可以根据用户的网络带宽情况,自动为客户端匹配一个合适的码率文件进行播放,从而保证视频的流畅度。

现在的视频网站很多采用的是流媒体传输协议,就是将一段视频切成无数个小段,每一个小段都是ts格式的视频文件,用户可以一段一段在网站上加载播放(如下图所示,一个m3u8被分成多个ts文件)。如果直接加载mp4或者flv文件,当文件很大时,比如一集电影2个小时,1080p的可能有2G的大小,直接加载会很慢。而采用m3u8格式视频文件就可以一段段下载播放,加载速度会更快,而且对用户下载有了限制了,基本用户下载m3u8格式视频文件都不知道怎么播放的,每块分片也可以使用加密算法进行加密。由此可见,m3u8格式相对MP4格式视频加载会更快、占用带宽少以及更为安全,以前每个视频网站的网络视频播放地址基本都为MP4格式视频文件,现在逐渐被m3u8格式视频文件取代了。

我在这个项目一开始采用mp4格式的视频,测试了20分钟的短视频预加载速度还好,一个多小时的长视频预加载的时候会有明显的卡顿,拖动也会有明显的卡顿。后面换成m3u8的视频格式,预加载和拖动的流畅度会明显好很多。

让vue-video-player5要播放m3u8的视频需要引入@videojs/http-streaming插件,注意该插件要写在引用完vue-video-player之后。(如下图)

在使用的过程中还出现过跨域的问题,出现在同一个域上面mp4的视频播放不会跨域,而m3u8的视频播放会提示跨域,而且你甚至可以在浏览器上访问该m3u8路径的视频,很奇怪的问题。后面经过排查,让后端对该路径下的视频文件都设置允许跨域的请求头成功解决问题。

本项目后端对m3u8的视频是按照每30s一段去切割的,流畅度还可以。小伙伴可以根据自己的实际需要去切割。使用m3u8还有一个优势就是可以实现多码率适配,可以根据不同网络情况自动适配不同分辨率的视频源,保证视频流畅播放,这是mp4所不能做到的,如果时间充足的话,可以让后端的小伙伴配合实现一下~

上传进度和续播视频

实现续播的关键就是在特定的时刻上传视频当前进度给后端。

一开始观看视频的时候视频的进度是0,开始播放之后默认每隔一分钟上传一次当前的播放进度,当点击暂停按钮,返回键(包括安卓物理返回键),拖动视频进度时时同样上传一次当前进度,这样子就算app闪退也能保证下次播放该视频的进度误差在一分钟内之内,这个间隔时间是通过后端接口返回的,后续可以根据需要自行设置默认上传进度时间。

有了这个视频的播放进度,之后重新观看该视频的时候就可以通过视频基本信息接口获取当前的视频进度,然后当点击开始播放时设置视频当前的播放进度,这样子就实现了续播的功能。

在实现续播的功能时,出现了安卓和iphoneX可以正常续播而iphone6s和iphone13等苹果机型不能续播且一直加载视频的情况,后面雁冰大佬排查发现视频的当前进度在ios系统上不能在ready事件里面设置,应该放到canplay事件或者点击开始播放的时候设置。

用到的一些事件

事件名触发时机
@play点击开始播放时触发
@pause点击暂停时触发
@timeupdate播放进度更新时触发
@ready实例注册好时触发
@fullscreenchange点击全屏按钮时触发
@seeking拖动进度条时触发
@canplay加载完一部分内容可以开始播放时触发

播放器的一些属性和方法

属性名作用
playbackRate(value)设置当前的播放速率
currentTime(value)设置当前进度
duration()获取视频的总时长,不知道是不是获取的时机不对,有些时候获取到的这个是空的,后面我取的视频基本信息接口返回的视频的总时长
isFullscreen()判断当前是否为全屏

横向全屏播放

横向全屏功能一开始不知道原生把webview固定成竖屏了,试了几种方法都不生效,后面将代码搬到浏览器上发现是能横屏的,才定位到时原生那边的问题。之后让原生那边提供了一个横竖屏的方法,我们只需要在点击全屏按钮的时候调用即可。

需要注意的是我本来使用fullscreenchange.native在组件上面去触发,后面发现这个方法在ios上面不触发,

网上查到的原因是fullscreenchange事件在不同的浏览器上面需要加上前缀,但是实际上只要使用on监听fullscreenchange事件就可以触发了安卓和ios上触发了,webkitfullscreenchange不监听也可以,可能video.js内部已经做了兼容。

对于安卓在全屏的时候如果使用物理返回键返回的话记得触发原生的方法,让webview变为竖屏状态,ios没有物理返回键不用考虑。

computed: {
  player() {
     return this.$refs.videoPlayer.player;
  },
},
methods: {
  this.player.on('fullscreenchange', () => {
  	  this.fullScreenHandle();
	});
  //不监听也可以
	this.player.on('webkitfullscreenchange', () => {
    this.fullScreenHandle();
	});
	fullScreenHandle() {
  	let orientation = 'landscape';
  	//判断当前横竖屏设置转向
  	if (!this.player.isFullscreen()) {
  	  orientation = 'portrait';
  	}
	  const obj = {
  	  orientation,
  	  name: this.$options.name,
  	};
 	 window.mt.changeScreenRotation(obj);
},
}

vue-video-player的一些配置


<template>
  <div class="video-wrap video-player vjs-big-play-centered">
        <video-player
              ref="videoPlayer"
              class="video-player-box"
              :options="playerOptions"
              :playsinline="true"
              custom-event-name="customstatechangedeventname"
              @play="onPlayerPlay($event)"
              @pause="onPlayerPause($event)"
              @timeupdate="onPlayerTimeupdate($event)"
              @ready="playerReadied"
            >
  			</video-player>
    </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/http-streaming'; //videojs-contrib-hls弃用用这个新的播放m3u8
import 'videojs-landscape-fullscreen'; //横屏插件
export default {
  data() {
    return {
      playerOptions : {
				playbackRates: [0.7, 1.0, 1.5, 2.0], //播放速度
				autoplay: false, //如果true,浏览器准备好时开始自动播放。
				muted: false, // 默认情况下将会消除任何音频。
				loop: false, // 导致视频一结束就重新开始。
				preload: 'auto', // 建议浏览器在<video>加载元素后是否应该开始下载视频数据。auto浏览器选择最佳行为,立即开始加载视频(如果浏览器支持)
				language: 'zh-CN',
				aspectRatio: '16:9', // 视频个比例 - 用冒号分隔的两个数字(例如"16:9"或"4:3"),现在移动端的视频基本是16:9或者21:9
				fluid: true, // 当true时,Video.js player将拥有流体大小。换句话说,它将按比例缩放以适应其容器。
				sources: [{
					type: "application/x-mpegUR",//这里的种类支持很多种:基本视频格式、直播、流媒体等,具体可以参看git网址项目
					src: 'https://1257120875.vod2.myqcloud.com/0ef121cdvodtransgzp1257120875/3055695e5285890780828799271/v.f230.m3u8' //url地址
				}],
				poster: "", //你的封面地址
				notSupportedMessage: '此视频暂无法播放,请稍后再试', //允许覆盖Video.js无法播放媒体源时显示的默认信息。
  			html5: {
     			vhs: {
      			withCredentials: false,
      		},
  			controlBar: {
      		// timeDivider: true, // 时间分割线
      		durationDisplay: true, // 显示总时间
      		remainingTimeDisplay: true, // 剩余播放时间
  			},
  			controls: true, //显示控制条
    	}
    },
	}

</script>

扩展

简单了解视频的几种流:可以参考这条链接: juejin.cn/post/684490…

视频流大致分为三种:HLS,RTSP, RTMP,下面依次介绍下三种视频流
HLS流
HLS(Http Live Streaming) 是一个由苹果公司提出的基于HTTP的流媒体网络传输协议,直接把流媒体切片成一段段,信息保存到m3u列表文件中, 可以将不同速率的版本切成相应的片;播放器可以直接使用http协议请求流数据。
优势:

  • 可以在不同速率的版本间自由切换,实现无缝播放
  • 省去使用其他协议的烦恼

劣势:

  • 延迟大小受切片大小影响,不适合直播,适合直播回放和视频点播。
  • 实时性差,延迟高。HLS 的延迟基本在 10s+ 以上
  • 文件碎片。特性的双刃剑,ts 切片较小,会造成海量小文件,对存储和缓存都有一定的挑战

RTMP流
RTMP(Real Time Message Protocol)由 Adobe 公司提出流媒体协议,并且是私有协议,未完全公开,用来解决多媒体数据传输流的多路复用(Multiplexing)和分包(packetizing)的问题,RTMP协议一般传输的是 flv,f4v 格式流。一般在 TCP 1个通道上传输命令和数据。
优势:

  • 在于低延迟,稳定性高,支持所有摄像头格式
  • 专为流媒体开发的协议,对底层的优化比其它协议更加优秀

劣势:

  • 浏览器需要加载 flash插件才能播放。
  • RTMP 为 Adobe 私有协议,很多设备无法播放,特别是在 iOS 端,需要使用第三方解码器才能播放
  • 基于 TCP 传输,非公共端口,可能会被防火墙阻拦

2020年浏览器已经全面禁用flash,rtmp流可以使用flv.js去支持播放。

RTSP流

RTSP( Real-Time Stream Protocol)由Real Networks 和Netscape共同提出的流媒体协议,RTSP协议是共有协议,并有专门机构做维护。是TCP/IP协议体系中的一个应用层协议 . RTSP协议一般传输的是 ts、mp4 格式的流,RTSP传输一般需要 2-3 个通道,命令和数据通道分离。基于文本的多媒体播放控制协议. RTSP定义流格式,流数据经由RTP传输;
优势: RTSP实时效果非常好,适合视频聊天,视频监控等方向。

劣势:浏览器不能直接播放,只能通过插件或者转码。