使用西瓜视频的播放器的一点见解

让组员封装一个可用性高的直播组件,技术选型使用的是西瓜视频,结果封装出来的不是定时器到处飞,就是实用性太差,后来自己写,发现就是在网上抄袭的代码,自己也没没抄好,没办法了,只有自己封装了

官网地址

先看下封装效果

快速上手

需三步:安装、DOM占位、实例化即可完成播放器的使用。

#安装

ruby 复制代码
# 最新稳定版
$ npm install xgplayer

对于已有项目也可以通过 CDN 引入,代码如下:

xml 复制代码
<script src="//unpkg.byted-static.com/xgplayer/2.31.2/browser/index.js" type="text/javascript"></script>

注意

生产环境使用时请在CDN地址中锁定版本,CDN使用方法参考 jsdelivr

使用

1. 在页面提供占位 DOM

bash 复制代码
<div id="mse"></div>

2. 实例化

bash 复制代码
let player = new Player({
  id: 'mse',
  url: '//abc.com/**/*.mp4'
});

就两步完成最简单的视频播放(mp4点播),播放器提供了较丰富的配置选项,如自动播放、贴图、音量控制、内置控件关闭等等,更多配置参考 配置

播放器核心包和插件都使用 babel 编译到 ES5。

封装

具备功能

  1. 网络断线重连,xgplayer-flv,自带
  2. 播放错误重连
  3. 播放网速展示
  4. 自定义插件
  5. 通用事件封装、开始播放、播放成功、播放失败、播放不稳定、播放销毁、播放暂停等等事件
js 复制代码
import "xgPlayer/dist/xgplayer.css";
import Player, {Events} from "xgplayer";
import FlvPlayer from "xgplayer-flv";
import HlsPlugin from "xgplayer-hls";
import poster from "@/packages/assets/image/poster.png";
import {customPageTurning} from "@/streaming/plugin"


class PmStreaming {
    constructor(params = {}) {
        const {
            playSuccess,
            playError,
            playStabilize,
            playStart,
            playInfo,
            playPrev,
            playNext,
            playDestroy,
            playPause,
            ...options
        } = params;
        this.handle = {
            playInfo, // 播放信息,网速
            playStart,// 开始准备工作,已经在获取拉流信息
            playSuccess, // 播放成功
            playError, // 播放失败
            playStabilize, // 播放不稳定
            playPrev, // 上一页
            playNext, // 下一页
            playDestroy, // 销毁的回调
            playPause, // 播放暂停
        }
        this.options = Object.assign({
            url: '',
            poster,
            isLive: true, // 是否直播
            preloadTime: 45, // 预加载时长(秒)
            minCachedTime: 10, // 当前播放时间距离已缓存资源片段结束点剩多长时间时开始请求新片段(秒)
            cors: true,
            autoplay: true,
            autoplayMuted: true,
            height: 300,
            width: 500,
            playbackRate: [], // 关闭播放速度切换
            ignores: ['replay'],
            lang: 'zh-cn',
            flv: {
                disconnectTime: 0,
                retryDelay: 2000,
                loadTimeout: 5000,
                bufferBehind: 1000,
                retryCount: 3,
            },
            plugins: [FlvPlayer],
            initShow: false,
            pageTurning: true,// 自定开发插件 上一页 下一页
            screenShot: {
                 saveImg: true,
                 quality: 0.92,
                 type: 'image/png',
                 format: '.png'
            },
        }, options)
        this.RECONNECTS = 0;
        this.RECONNECTSMAX = 10;
        this.init()
    }

    init() {
        if (FlvPlayer.isSupported()) {
            this.player = new Player(this.options)
            this.player.once('ready', () => {
                setTimeout(() => {
                    this.handleRegister()
                }, 1000);
            })
        }
    }

    handleRegister() {
        this.options.pageTurning && customPageTurning(this.player, this.options, this.handle)
        // 准备阶段
        this.player.on(Events.LOAD_START, (res) => {
            this.handle.playStart && this.handle.playStart(res)
        })

        // 播放成功
        this.player.on(Events.PLAY, (res) => {
            this.RECONNECTS = 0;
            this.handle.playSuccess && this.handle.playSuccess(res)
        })

        // 播放暂停
        this.player.on(Events.PAUSE, (res) => {
            this.handle.playPause && this.handle.playPause(res)
        })

        // 播放错误
        this.player.on(Events.ERROR, (err) => {
            this.disconnectionReconnects()
            this.handle.playError && this.handle.playError(err)
        })

        // 断开,重新播放,不稳定
        this.player.on(Events.PLAYING, (res) => {
            this.handle.playStabilize && this.handle.playStabilize(res)
        })

        this.player.on('core_event', ({eventName, ...rest}) => { // eventName: flv 事件名; rest: flv 事件回调函数参数
            this.handle.playInfo && this.handle.playInfo(this.getPlayInfo())
        })

        this.player.once('destroy', () => {
            this.clearStorage()
            this.handle.playDestroy && this.handle.playDestroy()
        })

        // 自动播放失败
        this.player.on(Events.AUTOPLAY_PREVENTED, () => {
            try {
                this.player.play()
                setTimeout(() => {
                    if (!this.player.hasStart && this.player.currentTime === 0) {
                        console.log('自动播放失败')
                    }
                }, 500)
            } catch (e) {
                console.log(e);
            }
        })

        // 关于自动播放问题,https://h5player.bytedance.com/guide/extends/aautoplay.html#%E7%A7%BB%E5%8A%A8%E7%AB%AF%E8%87%AA%E5%8A%A8%E6%92%AD%E6%94%BE
        this.player.on(Events.AUTOPLAY_STARTED, () => {
            console.log('autoplay success!!')
        })
    }

    getFlvPlayer() {
        return FlvPlayer;
    }

    getPlayInfo() {
        const {speed} = this.player.plugins.flv.core.speedInfo();
        const {downloadSpeed, avgSpeed} = this.player.plugins.flv.core.getStats();
        const kb = 8 * 1024;
        return {
            speed: speed / kb,
            downloadSpeed: downloadSpeed / kb,
            avgSpeed: avgSpeed / kb
        }
    }

    disconnectionReconnects() {
        this.RECONNECTS++
        if (this.RECONNECTS <= this.RECONNECTSMAX) {
            console.log('断线重连中' + this.RECONNECTS + '次')
            this.init()
        }
    }

    clearStorage() {
        if (this.RECONNECTS >= this.RECONNECTSMAX) {
            console.log(`超出最大链接次数${this.RECONNECTSMAX},刷新页面重新请求`)
        }
        this.player = null;
    }

    destroy() {
        this.player && this.player.destroy() // 销毁播放器
    }
}


export default PmStreaming;

自定义控制播放条,加入自己定义的按钮等等customPageTurning

js 复制代码
import Player from "xgplayer";
import $ from "jquery"

function createDom(el = 'div', tpl = '', attrs = {}, cname = '') {
    let dom = document.createElement(el)
    dom.className = cname
    dom.innerHTML = tpl
    Object.keys(attrs).forEach(item => {
        let key = item
        let value = attrs[item]
        if (el === 'video' || el === 'audio') {
            if (value) {
                dom.setAttribute(key, value)
            }
        } else {
            dom.setAttribute(key, value)
        }
    })
    return dom
}

const htmlVirtual = []
export const customPageTurning = function (player, options, handle) {
    if (options.controls === false) return;
    const btn = createDom('xg-right-grid',
        `<div class="custom-page-turning-container" style="position: relative;bottom: -5px"><span style="margin-right: 16px;padding: 3px 10px;border: 1px solid #ffffffd9;" class="custom-page-turning">上一页</span><span  style="padding: 3px 10px;border: 1px solid #ffffffd9;" class="custom-page-turning">下一页</span></div>`,
        {}, 'xg-right-grid');
    $(player.controls.innerRoot).find('.xg-center-grid').last().before(btn)
    const list = ['click', 'touchend']
    list.forEach((item) => {
        btn.addEventListener(item, function (e) {
            e.preventDefault()
            e.stopPropagation()
            if ($(e.target).text() === '下一页') {
                handle.playNext && handle.playNext()
            }
            if ($(e.target).text() === '上一页') {
                handle.playPrev && handle.playPrev()
            }
        })
    })
}

使用

html 复制代码
<template>
    <div class="streaming">
        <div v-if="shows" id="mse"></div>
        <n-button type="success" @click="handleTest">销毁</n-button>
        <p style="color: white">当前播放速度:{{ speed.toFixed(2) }} kb/s</p>
    </div>
</template>
<script>
import PmStreaming from "@/streaming/src";
import FlvPlayer from "xgplayer-flv";
import {defineComponent} from "vue"
export default defineComponent({
    setup(){
        const shows = ref(true)
        let pmStreaming = null
        let speed = ref(0);
        onMounted(()=>{
            pmStreaming = new PmStreaming({
                url:'/video/12312121221211.flv',
                el:document.querySelector('#mse'),
                playPrev:()=>{
                    console.log('上一页')
                },
                playNext:()=>{
                    console.log('下一页')
                },
                playStart(){
                    console.log('开始准备')
                },
                playSuccess(){
                    console.log('播放成功')
                },
                playPause(){
                    console.log('播放暂停')
                },
                playError(){
                    console.log('播放失败或者断开')
                },
                playInfo(res){
                    speed.value = res.speed
                },
                playStabilize(){
                    console.log('播放不稳定')
                }
            })
        })
        const handleTest = ()=>{
            console.log(pmStreaming.destroy())
        }
        return {
            shows,
            speed,
            handleTest
        }
    }
})
</script>
<style lang="less" scoped>
.streaming {

}
</style>
相关推荐
星就前端叭42 分钟前
【开源】一款基于Vue3 + WebRTC + Node + SRS + FFmpeg搭建的直播间项目
前端·后端·开源·webrtc
m0_748234521 小时前
前端Vue3字体优化三部曲(webFont、font-spider、spa-font-spider-webpack-plugin)
前端·webpack·node.js
Web阿成1 小时前
3.学习webpack配置 尝试打包ts文件
前端·学习·webpack·typescript
苹果醋31 小时前
Golang的文件加密工具
运维·vue.js·spring boot·nginx·课程设计
jwensh2 小时前
【Jenkins】Declarative和Scripted两种脚本模式有什么具体的区别
运维·前端·jenkins
关你西红柿子2 小时前
小程序app封装公用顶部筛选区uv-drop-down
前端·javascript·vue.js·小程序·uv
益达是我2 小时前
【Chrome】浏览器提示警告Chrome is moving towards a new experience
前端·chrome
济南小草根2 小时前
把一个Vue项目的页面打包后再另一个项目中使用
前端·javascript·vue.js
聪小陈2 小时前
圣诞节:记一次掘友让我感动的时刻
前端·程序员
LUwantAC2 小时前
CSS(一):选择器
前端·css