Vue中使用DPlayer播放Hls(m3u8)视频

H5中有很多播放器可以使用,比如DPlayer、Video.js,这里不是去做播放器之间的对比,而是基于DPlayer,总结一下在Vue中如何使用,以及相关的一些关键点,比如播放Hls(m3u8)视频,以及播放成功和失败的事件监听。

为什么不用vue-dplayer

vue有一个现成的开源组件,vue-dplayer,来在vue中集成使用dplayer。但建议不用去研究和使用,vue-dplayer有几个问题:

  • 本身不支持动态加载视频

    很多功能的实现中,视频的src url是基于ajax请求获取,然后交给dplayer组件进行播放。直接使用dplayer会比较简单,但如果使用vue-dplayer组件,就不得不搞成这样。

    <template>
      <dPlayer ref="player" :options="options"></dPlayer>
    </template>
      
    <script>
    import dPlayer from 'vue-dplayer'
    export default {
        name: 'example',
        components: {
            dPlayer
        },
        data () {
            return {
                player: null,
                options: {
                    video: {
                        url: ''
                    }
                }
            }
        },
        mounted() {
            this.player = this.$refs.player.dp
        },
        created() {
            // request video url, such as src
            this.playVideo(src)
        },
        methods: {
            playVideo(src) {
                this.player.switchVideo({
                    url: src
                })
            }
        }
    }
    </script>
    

    必须使用vue-dplayer的switchVideo接口来切换src,因为options不是一个双向绑定的数据。如果request数据的时间偏长,尤其伴随请求和设置视频封面时,可能会出现一个黑色不可用的播放器,之后有一个刷新的过程,看到图片,然后等待视频加载,用户体验上受到影响。

  • 不支持HLS视频

    虽然按照vue-dplayer的说明中,可以设置options来播放Hls。但实际上行不通,因为vue-dplayer没有处理好hls.js一些需要配合播放Hls视频的操作。

    options: {
        video: {
          url: 'http://xxxxx.xx.xxx/xx.m3u8',
          type: 'hls'
        }
    }
    
  • vue-player只是简单将dplayer进行vue组件化,使用起来没有太大必要性

    使用dplayer非vue组件化的方式进行绑定和初始化,没有什么开发成本。

正确使用DPlayer播放Hls的方式

安装dplayer

npm install -S dplayer

安装hls.js,播放Hls必须使用

npm install -S hls.js

vue组件代码(**.vue)

<template>
	<div>
        <div id="dplayer" ref="player"></div>
    </div>
</template>

<script>
import Hls from 'hls.js'
import Dplyaer from 'dplayer'

export default {
    name: 'example1',
    data () {
        dp: null,
        video: {}
    },
    methods: {
        loadVideo (videoInfo) {
            this.dp = new Dplayer({
                element: this.$refs.player,
                video: {
                    pic: videoInfo.img, // 封面
                    url: videoInfo.video,
                    type: 'customHls',
                    customType: {
                        customHls: function (video, player) {
                            const hls = new Hls()
                  			hls.loadSource(video.src)
                            hls.attachMedia(video)
                        }
                    }
                }
            })
        }
    },
    mounted () {
        // getVideo: ajax request for getting videoInfo
        getVideo().then(res => {
            this.video = res.data.video
            this.laodVideo(this.video)
        })
    }
}
</script>

视频播放事件监控

一般要监控的事件,是播放错误(往往是资源下载错误)、视频下载成功可以播放。

  • Hls视频资源下载错误
	this.dp = new Dplayer({
        element: this.$refs.player,
        video: {
          pic: videoInfo.img,
          url: videoInfo.video,
          type: 'customHls',
          customType: {
            customHls: function (video, player) {
              const hls = new Hls()
			  // 监听Hls.Events.ERROR事件,
			  // DNS解析、下载超时,都会触发manifestLoadError错误
              hls.on(Hls.Events.ERROR, function (eventName, data) {
                // 埋点上报,可以追踪data.details
				// track()
              })
              hls.loadSource(video.src)
              hls.attachMedia(video)
            }
          }
        }
      })
  • 视频下载成功可以播放

    播放器从开始下载到可以播放,dplayer延续H5 video的事件,依次会触发loadstart、durationchange、loadedmetadata、loadeddata、progress、canplay、canplaythrough。

    虽然直觉上是监听canplay,但是canplay在iOS上行不通,只能使用loadedmetadata。

	methods: {
        loadVideo (videoInfo) {
            this.dp = new Dplayer({
                element: this.$refs.player,
                video: {
                    pic: videoInfo.img, // 封面
                    url: videoInfo.video,
                    type: 'customHls',
                    customType: {
                        customHls: function (video, player) {
                            const hls = new Hls()
                  			hls.loadSource(video.src)
                            hls.attachMedia(video)
                        }
                    }
                }
            })

			// 监听loadedmetadata事件
            this.dp.on('loadedmetadata', function () {
        		// 埋点上报
        		// track()
      		})
        }
    },