头图

theme: channing-cyan

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

官网地址

先看下封装效果

image.png

快速上手

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

#安装

# 最新稳定版
$ npm install xgplayer

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

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

注意

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

使用

1. 在页面提供占位 DOM

<div id="mse"></div>

2. 实例化

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

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

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

封装

具备功能

  1. 网络断线重连,xgplayer-flv,自带
  2. 播放错误重连
  3. 播放网速展示
  4. 自定义插件
  5. 通用事件封装、开始播放、播放成功、播放失败、播放不稳定、播放销毁、播放暂停等等事件
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

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()
            }
        })
    })
}

使用

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

羊先生
1.9k 声望821 粉丝