Vue2 结合 vue-video-player 播放 FLV 直播流
一. 依赖包版本
vue-video-player@5.0.2video.js@6.13.0videojs-flvjs@0.3.1flv.js@1.6.2
二. main.js 的配置及加载顺序:
import videojs from 'video.js';
import flvjs from 'flv.js';
window.videojs = videojs;
window.flvjs = flvjs;
import 'videojs-flvjs';
import VideoPlayer from 'vue-video-player';
Vue.use(VideoPlayer);
如果使用 video.js@7.x,可能会出现 No compatible source was found for this media. 的兼容问题。
三. FlvVideoPlayer 组件代码
<template>
<div class="flv-player-box">
<video-player
ref="videoPlayer"
class="vjs-big-play-centered"
:options="playerOptions"
@playerReady="onPlayerReady"
/>
</div>
</template>
<script>
export default {
name: 'FlvVideoPlayer',
props: {
flvUrl: {
type: String,
required: true,
},
autoplay: {
type: Boolean,
default: true,
},
muted: {
type: Boolean,
default: true,
},
controls: {
type: Boolean,
default: true,
},
fluid: {
type: Boolean,
default: true,
},
preload: {
type: String,
default: 'auto',
},
poster: {
type: String,
default: '',
},
playsinline: {
type: Boolean,
default: true,
},
controlBar: {
type: Object,
default: () => ({
volumePanel: { inline: false },
progressControl: false,
}),
},
retryOnError: {
type: Boolean,
default: true,
},
retryDelay: {
type: Number,
default: 3000,
},
maxRetry: {
type: Number,
default: 3,
},
techOrder: {
type: Array,
default: () => ['flvjs', 'html5'],
},
},
data() {
return {
player: null,
retryCount: 0,
retryTimer: null,
};
},
computed: {
playerOptions() {
return {
autoplay: this.autoplay,
muted: this.muted,
controls: this.controls,
fluid: this.fluid,
preload: this.preload,
poster: this.poster,
playsinline: this.playsinline,
techOrder: this.techOrder,
sources: [
{
src: this.flvUrl,
type: 'video/x-flv',
},
],
controlBar: this.controlBar,
};
},
},
watch: {
flvUrl(newUrl, oldUrl) {
if (newUrl && newUrl !== oldUrl) {
this.retryCount = 0;
this.updateSource(newUrl);
}
},
},
beforeDestroy() {
this.destroyPlayer();
},
methods: {
onPlayerReady(player) {
this.player = player;
this.player.on('error', this.handleError);
this.player.on('play', () => this.$emit('play'));
this.player.on('pause', () => this.$emit('pause'));
this.player.on('loadedmetadata', () => this.$emit('loadedmetadata'));
this.$emit('ready', player);
this.updateSource(this.flvUrl);
if (this.autoplay) {
this.player.play().catch(() => {});
}
},
updateSource(url) {
if (!this.player) {
return;
}
this.clearRetryTimer();
this.player.src({
src: url,
type: 'video/x-flv',
});
this.player.load();
if (this.autoplay) {
this.player.play().catch(() => {});
}
},
handleError() {
this.$emit('error', this.player.error());
if (!this.retryOnError) {
return;
}
if (this.maxRetry >= 0 && this.retryCount >= this.maxRetry) {
this.$emit('retry-failed', this.retryCount);
return;
}
this.retryCount += 1;
this.retryTimer = setTimeout(() => {
if (this.player) {
this.updateSource(this.flvUrl);
this.$emit('retry', this.retryCount);
}
}, this.retryDelay);
},
clearRetryTimer() {
if (this.retryTimer) {
clearTimeout(this.retryTimer);
this.retryTimer = null;
}
},
destroyPlayer() {
this.clearRetryTimer();
if (this.player) {
this.player.off('error', this.handleError);
this.player.dispose();
this.player = null;
}
},
reload() {
this.retryCount = 0;
this.updateSource(this.flvUrl);
},
},
};
</script>
<style scoped>
.flv-player-box {
width: 100%;
background: #000;
}
</style>
四. 页面使用
<FlvVideoPlayer
flvUrl="https://example.com/live.flv"
:autoplay="true"
:muted="true"
:retryOnError="true"
:retryDelay="3000"
:maxRetry="3"
/>
五. 拓展: m3u8 流播放组件
<template>
<div class="hls-player-box">
<video-player
ref="videoPlayer"
class="vjs-big-play-centered"
:options="playerOptions"
@playerReady="onPlayerReady"
/>
</div>
</template>
<script>
export default {
name: 'HlsLivePlayer',
props: {
m3u8Url: {
type: String,
required: true,
},
autoplay: {
type: Boolean,
default: true,
},
muted: {
type: Boolean,
default: true,
},
controls: {
type: Boolean,
default: true,
},
fluid: {
type: Boolean,
default: true,
},
preload: {
type: String,
default: 'auto',
},
poster: {
type: String,
default: '',
},
playsinline: {
type: Boolean,
default: true,
},
isLive: {
type: Boolean,
default: true,
},
retryOnError: {
type: Boolean,
default: true,
},
retryDelay: {
type: Number,
default: 3000,
},
maxRetry: {
type: Number,
default: 3,
},
techOrder: {
type: Array,
default: () => ['html5'],
},
controlBar: {
type: Object,
default: () => ({
volumePanel: { inline: false },
progressControl: false,
}),
},
},
data() {
return {
player: null,
retryCount: 0,
retryTimer: null,
};
},
computed: {
playerOptions() {
return {
autoplay: this.autoplay,
muted: this.muted,
controls: this.controls,
fluid: this.fluid,
preload: this.preload,
poster: this.poster,
playsinline: this.playsinline,
techOrder: this.techOrder,
isLive: this.isLive,
sources: [
{
src: this.m3u8Url,
type: 'application/x-mpegURL',
},
],
controlBar: this.controlBar,
};
},
},
watch: {
m3u8Url(newUrl, oldUrl) {
if (newUrl && newUrl !== oldUrl) {
this.retryCount = 0;
this.updateSource(newUrl);
}
},
},
beforeDestroy() {
this.destroyPlayer();
},
methods: {
onPlayerReady(player) {
this.player = player;
this.player.on('error', this.handleError);
this.player.on('play', () => this.$emit('play'));
this.player.on('pause', () => this.$emit('pause'));
this.player.on('loadedmetadata', () => this.$emit('loadedmetadata'));
this.$emit('ready', player);
if (this.autoplay) {
this.player.play().catch(() => {});
}
},
updateSource(url) {
if (!this.player) {
return;
}
this.clearRetryTimer();
this.player.src({
src: url,
type: 'application/x-mpegURL',
});
this.player.load();
if (this.autoplay) {
this.player.play().catch(() => {});
}
},
handleError() {
this.$emit('error', this.player.error());
if (!this.retryOnError) {
return;
}
if (this.maxRetry >= 0 && this.retryCount >= this.maxRetry) {
this.$emit('retry-failed', this.retryCount);
return;
}
this.retryCount += 1;
this.retryTimer = setTimeout(() => {
if (this.player) {
this.$emit('retry', this.retryCount);
this.updateSource(this.m3u8Url);
}
}, this.retryDelay);
},
clearRetryTimer() {
if (this.retryTimer) {
clearTimeout(this.retryTimer);
this.retryTimer = null;
}
},
destroyPlayer() {
this.clearRetryTimer();
if (this.player) {
this.player.off('error', this.handleError);
this.player.dispose();
this.player = null;
}
},
reload() {
this.retryCount = 0;
this.updateSource(this.m3u8Url);
},
},
};
</script>
<style scoped>
.hls-player-box {
width: 100%;
max-width: 1200px;
margin: 0 auto;
background: #000;
}
</style>