一、依赖引入:
①使用 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 提供更高的压缩率。
- 兼容性强:在大多数平台和设备上可以直接播放。