包的版本
"@videojs-player/vue": "^1.0.0",
"flv.js": "1.6.2",
"video.js": "^8.22.0",
"flv-extend": "^0.3.0",
<template><!-- 封装的视频播放器组件 --><video-player ref="videRef" :options="state.playerOptions" :src="videoSrc" @mounted="handleMounted" @unmounted="handleUnmounted" />
</template><script setup lang="ts">
import { reactive, defineProps, defineEmits, getCurrentInstance, watch,ref } from "vue";
import { VideoPlayer } from '@videojs-player/vue'
import videojs from 'video.js'
import 'video.js/dist/video-js.css'
import video_zhCN from 'video.js/dist/lang/zh-CN.json'
import { FlvJsTech } from './flv-video-tech'
videojs.addLanguage('zh-CN', video_zhCN)
const { proxy } = getCurrentInstance();
// 注册 FlvJsTech
videojs.registerTech('Flvjs', FlvJsTech);
// 定义 props 类型
type Props = {// 视频源地址videoSrc: string;// 是否显示控制栏showControls: boolean;// 是否自动播放autoplay: boolean;// 是否静音muted: boolean;// 是否循环播放loop: boolean;// 音量大小volume: number;// 是否禁用画中画disablePictureInPicture: boolean;// v-model 绑定的值,用于控制播放状态modelValue: boolean;
};// 定义 props
const props = defineProps<Props>();/*** 定义视频播放器可能触发的所有事件列表* 这些事件将用于监听视频播放器的各种状态变化* 例如加载开始、播放、暂停、结束等*/
const eventsArr = [// 视频开始加载时触发'loadstart',// 视频加载暂停时触发'suspend',// 视频加载中止时触发'abort',// 视频加载出错时触发'error',// 视频元素被清空时触发'emptied',// 视频加载停滞时触发'stalled',// 视频元数据加载完成时触发'loadedmetadata',// 视频的第一帧数据加载完成时触发'loadeddata',// 视频可以开始播放时触发'canplay',// 视频可以流畅播放时触发'canplaythrough',// 视频开始播放时触发'playing',// 视频暂停等待数据时触发'waiting',// 视频开始跳转时触发'seeking',// 视频跳转完成时触发'seeked',// 视频播放结束时触发'ended',// 视频时长发生变化时触发'durationchange',// 视频播放时间更新时触发'timeupdate',// 视频加载进度更新时触发'progress',// 视频开始播放时触发'play',// 视频暂停时触发'pause',// 视频播放速率改变时触发'ratechange',// 视频播放器尺寸改变时触发'resize',// 视频音量改变时触发'volumechange',// 视频海报改变时触发'posterchange',// 视频语言设置改变时触发'languagechange',// 视频全屏状态改变时触发'fullscreenchange',// 视频播放速率选项改变时触发'playbackrateschange',// 视频控制栏禁用时触发'controlsdisabled',// 视频控制栏启用时触发'controlsenabled',// 视频进入全屏窗口时触发'enterFullWindow',// 视频退出全屏窗口时触发'exitFullWindow',// 视频进入画中画模式时触发'enterpictureinpicture',// 视频离开画中画模式时触发'leavepictureinpicture',// 视频源设置完成时触发'sourceset',// 视频文本轨道改变时触发'texttrackchange',// 视频文本数据更新时触发'textdata',// 用户活动时触发'useractive',// 用户不活动时触发'userinactive',// 视频使用自定义控制栏时触发'usingcustomcontrols',// 视频使用原生控制栏时触发'usingnativecontrols',// 视频播放器销毁时触发'dispose',// 视频插件设置前触发'beforepluginsetup',// 视频插件设置完成时触发'pluginsetup',// 视频组件尺寸改变时触发'componentresize',// 视频播放器尺寸改变时触发'playerresize',// 视频播放器被点击时触发'tap',// 视频播放器准备好时触发'ready'
];// 定义事件
const emits = defineEmits();type VideoJsPlayer = ReturnType<typeof videojs>;
const state = reactive({playerOptions: {// 是否显示控制栏,从 props 获取controls: props.showControls,// 是否等浏览器准备好后自动播放,从 props 获取autoplay: props.autoplay,// 是否静音,从 props 获取muted: props.muted,// 结束后是否重新开始,从 props 获取loop: props.loop,// 播放视频源,从 props 获取// sources: [{ type: 'video/flv', src: props.videoSrc }],// 为 true 时,播放器具有流畅的大小fluid: true,// 播放顺序techOrder: [ 'flvjs','html5'],// 音量,从 props 获取volume: props.volume,language: 'zh-CN',// 禁用画中画,从 props 获取disablePictureInPicture: props.disablePictureInPicture}
});const handleMounted = ({ player }: { player: VideoJsPlayer }) => {// 设置视频源// 触发自定义事件,将 player 对象传递给父组件emits('update:playerReady', player);eventsArr.forEach(event => {player.on(event, (e) => {let parameter = {event: e,theNameOfTheEvent: event,videoJsPlayer: player,}emits('update:' + event, parameter);});});// 监听播放和暂停事件,更新 v-model 绑定的值player.on('play', () => {emits('update:modelValue', true);});player.on('pause', () => {emits('update:modelValue', false);});// 根据 v-model 绑定的值控制播放状态if (props.modelValue) {player.play();} else {player.pause();}
};const handleUnmounted = () => {};</script><style lang="scss" scoped>
/* 组件样式 */
</style>
flv-video-tech.ts 文件
import flvjs from "flv.js";
import FlvExtend from "flv-extend";
import videojs from "video.js";// 获取 Video.js 的 Html5 技术类实例
const Html5 = videojs.getTech("Html5")! as any;/*** 自定义的 FlvJsTech 类,继承自 Video.js 的 Html5 技术类* 用于支持 FLV 视频的播放,并集成 FlvExtend 以实现自动追帧等功能*/
export class FlvJsTech extends Html5 {private flvPlayer: flvjs.Player | null = null;private flvExtendInstance: FlvExtend | null = null; // 新增 FlvExtend 实例constructor(options: any, ready: any) {super(options, ready);}/*** 设置视频源* @param {string} src - 视频源的 URL*/setSrc(src: string) {this.dispose(); // 销毁之前的播放器// 初始化 FlvExtend 实例this.flvExtendInstance = new FlvExtend({element: this.el_, // 绑定的 HTML 元素frameTracking: true, // 开启追帧设置updateOnStart: true, // 点击播放后更新视频updateOnFocus: true, // 获得焦点后更新视频reconnect: true, // 开启断流重连trackingDelta: 1, // 追帧最大延迟,延迟超过1s即开启追帧showLog: false // 是否显示插件的 log 信息});// 初始化 flvPlayer 实例,并将其与 FlvExtend 关联this.flvPlayer = this.flvExtendInstance.init({type: "flv", // 视频类型url: src, // 视频源 URLisLive: true, // 是否为直播hasAudio: false, // 是否包含音频cors: true // 显式启用跨域},{enableStashBuffer: true, // 是否启用缓存缓冲区autoCleanupSourceBuffer: true, // 是否自动清理源缓冲区stashInitialSize: 128, // 初始缓存大小enableWorker: false // 是否启用 Web Worker}) as unknown as flvjs.Player;this.flvEvent(); // 绑定事件this.flvPlayer.load(); // 加载视频this.flvPlayer.play(); // 开始播放}/*** 绑定 flvPlayer 的事件*/flvEvent() {if (this.flvPlayer) {this.flvPlayer.on(flvjs.Events.ERROR, (errorType, errorDetail, errorInfo) => {this.trigger("error"); // 触发错误事件});}}/*** 销毁实例并清理资源*/dispose() {if (this.flvPlayer) {this.flvPlayer.pause();this.flvPlayer.unload();this.flvPlayer.detachMediaElement();this.flvPlayer.destroy();this.flvPlayer = null;}if (this.flvExtendInstance) {// 清理 FlvExtend 实例(this.flvExtendInstance as any).destroy();this.flvExtendInstance = null;}}/*** 支持的视频格式* @type {{ 'video/flv': string; 'video/x-flv': string }}*/static formats = {"video/flv": "FLV","video/x-flv": "FLV"};/*** 检查当前环境是否支持 FLV 视频播放* @returns {boolean} - 如果支持则返回 true,否则返回 false*/static isSupported = () => flvjs.isSupported();/*** 检查指定的视频类型是否可以播放* @param {string} type - 视频类型* @returns {string} - 如果支持则返回 'maybe',否则返回空字符串*/static canPlayType = (type: string) =>FlvJsTech.isSupported() && type in FlvJsTech.formats ? "maybe" : "";/*** 检查指定的视频源是否可以播放* @param {any} source - 视频源对象* @returns {string} - 如果支持则返回 'maybe',否则返回空字符串*/static canPlaySource = (source: any) =>FlvJsTech.isSupported() && source.src.endsWith(".flv") ? "maybe" : "";
}
使用方式
<VideoPlayerWrapper v-model="state.isPlaying" :videoSrc="state.videoSrc" :showControls="true":autoplay="true" :muted="true" :loop="false" :volume="0.6" :disablePictureInPicture="true"@update:usingnativecontrols="onTap" />
import VideoPlayerWrapper from '@/components/VideoPlayerWrapper/index.vue'