一、依赖引入:
①使用 CDN 的播放器代码
<!-- 引入 xgplayer 核心 -->
<script src="https://unpkg.byted-static.com/xgplayer/3.0.10/dist/index.min.js" charset="utf-8"></script><!-- 引入 xgplayer mp4 插件 -->
<script src="https://unpkg.byted-static.com/xgplayer-mp4/3.0.10/dist/index.min.js" charset="utf-8"></script><!-- 引入 xgplayer 样式 -->
<link rel="stylesheet" href="https://unpkg.byted-static.com/xgplayer/3.0.10/dist/index.min.css" />
<!-- 支持 danmu 的插件 -->
<script src="https://unpkg.byted-static.com/xgplayer/3.0.10/dist/index.min.js"></script>
<script src="https://unpkg.com/xgplayer-danmu/2.1.0/dist/index.min.js"></script>
②命令行安装 xgplayer
npm install xgplayer
npm install xgplayer-mp4
npm install xgplayer-danmu
二、组件封装(弹幕功能暂不支持)
<template><div class="player-container"><div id="mse"></div></div>
</template><script setup>
import { onMounted, onBeforeUnmount, ref, nextTick } from 'vue';// 引入 Player 和 Mp4Plugin (假设通过 CDN 引入,挂载在 window 上)
import Player from 'xgplayer';
// import 'xgplayer-danmu';
import Mp4Plugin from 'xgplayer-mp4';const player = ref(null);
// 弹幕数据
const danmuComments = [{duration: 5000, // 弹幕持续时间id: '1', // 弹幕唯一标识start: 0, // 视频开始时出现txt: '这是第一条弹幕', // 弹幕内容style: {color: '#fff', // 弹幕文字颜色fontSize: '20px', // 弹幕文字大小border: 'solid 1px #ff9500', // 弹幕边框borderRadius: '50px', // 弹幕圆角padding: '5px 11px', // 弹幕内边距backgroundColor: 'rgba(255, 255, 255, 0.1)', // 弹幕背景颜色},},{duration: 3000, // 弹幕持续时间id: '2',start: 2, // 视频播放到 2 秒时出现txt: '第二条弹幕,展示更多内容',style: {color: '#00ff00',fontSize: '18px',backgroundColor: 'rgba(0, 0, 0, 0.5)',},},
];
let previousFrame = null; // 用于存储上一帧数据// 插值函数:生成中间帧
const interpolateFrames = (frame1, frame2) => {if (!frame1 || !frame2) return frame2; // 如果没有上一帧,直接返回当前帧const width = frame1.width;const height = frame1.height;const interpolated = new ImageData(width, height); // 创建空白帧for (let i = 0; i < frame1.data.length; i++) {interpolated.data[i] = (frame1.data[i] + frame2.data[i]) / 2; // 简单线性插值}return interpolated;
};// 初始化播放器配置
const playerConfig = {id: 'mse',// 容器autoplay: true, // 自动播放volume: 0.3,// 音量url: '//sf1-cdn-tos.huoshanstatic.com/obj/media-fe/xgplayer_doc_video/mp4/xgplayer-demo-360p.mp4', // 视频地址poster: '//lf3-static.bytednsdoc.com/obj/eden-cn/nupenuvpxnuvo/xgplayer_doc/poster.jpg',// 封面playsinline: true,// 小窗播放thumbnail: {pic_num: 44,// 每张图片的帧数width: 160, // 每张图片的宽度height: 90, // 每张图片的高度col: 10,// 每行显示的图片数row: 10,// 每列显示的图片数urls: ['//lf3-static.bytednsdoc.com/obj/eden-cn/nupenuvpxnuvo/xgplayer_doc/xgplayer-demo-thumbnail.jpg'], // 图片地址},// 弹幕配置danmu: {comments: danmuComments,area: {start: 0, // 开始时间end: 1, // 范围从 0 到 1,表示全视频范围内展示},},// plugins: [window.Mp4Plugin], // cnd引入使用plugins: [Mp4Plugin],// import 导入使用 Mp4Plugin 插件mp4plugin: {maxBufferLength: 50, // 缓冲区最大长度minBufferLength: 10, // 缓冲区最小长度},height: 500,// 播放器高度width: 800, // 播放器宽度controls: true,// 启用控制栏
};// 初始化播放器
const initPlayer = () => {try {console.log('Mp4Plugin:', Mp4Plugin);if (!Mp4Plugin) {throw new Error('Mp4Plugin is not loaded. Please check the CDN or script reference.');}// 手动加载弹幕数据player.value = new Player(playerConfig);console.log('Player danmu:', Player.danmu);// 检查 danmu 插件是否可用// 检查 danmu 插件是否可用if (player.value.danmu) {console.log('danmu initialized:', player.value.danmu);player.value.danmu.load(danmuComments);} else {console.warn('danmu 功能未初始化');}// 监听事件player.value.on('play', () => {if (player.value.danmu) {player.value.danmu.start();}});player.value.on('pause', () => {if (player.value.danmu) {player.value.danmu.pause();}});// 动态加载资源player.value.emit('resourceReady', [{ name: '超清', definition: '1080p', url: '//sf1-cdn-tos.huoshanstatic.com/obj/media-fe/xgplayer_doc_video/mp4/xgplayer-demo-720p.mp4' },{ name: '高清', definition: '720p', url: '//sf1-cdn-tos.huoshanstatic.com/obj/media-fe/xgplayer_doc_video/mp4/xgplayer-demo-480p.mp4' },{ name: '标清', definition: '480p', url: '//sf1-cdn-tos.huoshanstatic.com/obj/media-fe/xgplayer_doc_video/mp4/xgplayer-demo-360p.mp4' },]);// 插帧逻辑:监听帧渲染事件player.value.on('frameRender', () => {const videoElement = document.querySelector('video');if (!videoElement || !videoElement.videoWidth || !videoElement.videoHeight) {console.warn('视频容器未加载完成,无法获取视频尺寸。');return;}try {// 创建 canvas 和上下文const canvas = document.createElement('canvas');const ctx = canvas.getContext('2d');canvas.width = videoElement.videoWidth;canvas.height = videoElement.videoHeight;// 获取当前帧ctx.drawImage(videoElement, 0, 0, canvas.width, canvas.height);const currentFrame = ctx.getImageData(0, 0, canvas.width, canvas.height);// 生成插值帧const interpolatedFrame = interpolateFrames(previousFrame, currentFrame);previousFrame = currentFrame; // 更新上一帧// 渲染到 canvas(如需可视化)ctx.putImageData(interpolatedFrame, 0, 0);} catch (error) {console.error('错误动画帧渲染:', error);}});// 监听播放器错误player.value.on('error', (error) => {console.error('播放连接失败:', error);alert('视频加载失败,请检查网络或视频资源路径');});} catch (error) {console.error('初始化容器失败:', error.message, '\nStack:', error.stack, '\nConfig:', playerConfig);alert('播放器初始化失败,请联系技术支持');}
};// 调整播放器大小
const resizePlayer = () => {const resizeObserver = new ResizeObserver(() => {if (player.value) {player.value.resize(window.innerWidth, window.innerHeight);}});resizeObserver.observe(document.body);
};// 生命周期钩子
onMounted(() => {nextTick(() => {initPlayer();resizePlayer();})});onBeforeUnmount(() => {if (player.value) {player.value.destroy(); // 销毁播放器}
});
</script><style scoped>
.player-container {width: 100%;height: 100%;display: flex;justify-content: center;align-items: center;background-color: #000;/* 确保背景色为黑色 */
}#MSE {width: 100%;height: 100%;
}
</style>
三、应用:
<template><VideoPlayer />
</template><script setup>
import VideoPlayer from './components/VideoPlayer.vue'
</script>
四、效果:
五、学习啦:
MP4 是一种广泛使用的数字多媒体容器格式,用于存储视频、音频、字幕以及其他数据(例如元数据)
扩展名:通常为
.mp4
用途:
- 存储和分发音视频数据。
- 支持流媒体(渐进式下载和适应性流)。
- 广泛应用于互联网、移动设备和流媒体服务。
特点
- 高度灵活:支持多种视频和音频编码格式(如 H.264、AAC)。
- 容量小:相较于其他格式,MP4 提供更高的压缩率。
- 兼容性强:在大多数平台和设备上可以直接播放。