@videojs-player/vue 直播fiv 组件封装

包的版本

 

"@videojs-player/vue": "^1.0.0",
"flv.js": "1.6.2",
"video.js": "^8.22.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  文件

 


// 引入 flv.js 库,用于处理 FLV 视频流
import flvjs from 'flv.js'
// 引入 Video.js 库,用于创建视频播放器
import videojs from 'video.js'

/**
 * 获取 Video.js 的 Html5 技术类实例
 * @type {any}
 */
const Html5 = videojs.getTech('Html5')! as any

/**
 * 自定义的 FlvJsTech 类,继承自 Video.js 的 Html5 技术类
 * 用于支持 FLV 视频的播放
 */
export class FlvJsTech extends Html5 {
  private flvPlayer: flvjs.Player | null = null;

  constructor(options: any, ready: any) {
    super(options, ready);
    this.flvPlayer = null;
  }

  /**
   * 设置视频源
   * @param {string} src - 视频源的 URL
   */
  setSrc(src: string) {
    this.dispose(); // 销毁之前的播放器

    // 创建新的 flvPlayer 实例
    this.flvPlayer = flvjs.createPlayer({ url: src, type: 'flv' }, this.options_);

    // 确保播放器和媒体元素正确绑定
    if (this.el_ && this.flvPlayer) {
      this.flvPlayer.attachMediaElement(this.el_);
      this.flvPlayer.load();

      // 确保播放器加载完成后再开始播放
      this.flvPlayer.on(flvjs.Events.LOADING_COMPLETE, () => {
        if (this.flvPlayer) {
          this.flvPlayer.play();
        }
      });

      this.flvEvent(); // 绑定事件
    } else {
      console.error('Media element or flvPlayer is not ready');
    }
  }

  // flvjs播放器事件侦听
  flvEvent() {
    if (this.flvPlayer) {
      // 错误信息回调
      this.flvPlayer.on(flvjs.Events.ERROR, (errorType: any, errorDetail: any, errorInfo: any) => {
        // console.error('FLV Player Error:', errorType, errorDetail, errorInfo);
      });

      // 播放统计信息回调
      this.flvPlayer.on(flvjs.Events.STATISTICS_INFO, (errorType: any, errorDetail: any, errorInfo: any) => {
        // console.log('FLV Player Statistics:', errorType, errorDetail, errorInfo);
      });
    }
  }

  /**
   * 销毁实例并清理资源
   */
  dispose() {
    // 销毁 flvPlayer 实例
    if (this.flvPlayer) {
      this.flvPlayer.pause();
      this.flvPlayer.unload();
      this.flvPlayer.detachMediaElement();
      this.flvPlayer.destroy();
      this.flvPlayer = 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 = function () {
    return flvjs.isSupported();
  };

  /**
   * 检查指定的视频类型是否可以播放
   * @param {string} type - 视频类型
   * @returns {string} - 如果支持则返回 'maybe',否则返回空字符串
   */
  static canPlayType = function (type: string) {
    return FlvJsTech.isSupported() && type in FlvJsTech.formats ? 'maybe' : '';
  };

  /**
   * 检查指定的视频源是否可以播放
   * @param {any} source - 视频源对象
   * @returns {string} - 如果支持则返回 'maybe',否则返回空字符串
   */
  static canPlaySource = function (source: any) {
    return 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'

 

posted @ 2025-03-22 13:47  前端搬运工bug  阅读(151)  评论(0)    收藏  举报