单个引入依赖到index.html的组件封装:
<template>
<div id="rtcPlayer">
<video id='webRtcPlayerBox' controls autoplay style="text-align:left;">
</video>
</div>
</template>
<script>
/* global ZLMRTCClient */
let webrtcPlayer = null;
export default {
name: 'rtcPlayer',
data () {
return {
timer: null,
videoUrl: ''
};
},
mounted () {
let paramUrl = decodeURIComponent(this.$route.params.url)
this.$nextTick(() => {
if (typeof (this.videoUrl) == "undefined") {
this.videoUrl = paramUrl;
}
console.log("初始化时的地址为: " + this.videoUrl)
this.play(this.videoUrl)
})
},
watch: {
videoUrl (newData, oldData) {
this.pause();
this.play(newData);
},
immediate: true
},
methods: {
setVideoUrl (url) {
this.videoUrl = url
},
play: function (url) {
webrtcPlayer = new ZLMRTCClient.Endpoint({
element: document.getElementById('webRtcPlayerBox'),// video 标签
debug: true,// 是否打印日志
zlmsdpUrl: url,//流地址
simulecast: false,
useCamera: false,
audioEnable: true,
videoEnable: true,
recvOnly: true,
usedatachannel: false,
})
webrtcPlayer.on(ZLMRTCClient.Events.WEBRTC_ICE_CANDIDATE_ERROR, (e) => {// ICE 协商出错
console.error('ICE 协商出错')
this.eventcallbacK("ICE ERROR", "ICE 协商出错")
});
webrtcPlayer.on(ZLMRTCClient.Events.WEBRTC_ON_REMOTE_STREAMS, (e) => {//获取到了远端流,可以播放
console.log('播放成功', e.streams)
this.eventcallbacK("playing", "播放成功")
});
webrtcPlayer.on(ZLMRTCClient.Events.WEBRTC_OFFER_ANWSER_EXCHANGE_FAILED, (e) => {// offer anwser 交换失败
console.error('offer anwser 交换失败', e)
this.eventcallbacK("OFFER ANSWER ERROR ", "offer anwser 交换失败")
if (e.code == -400 && e.msg == "流不存在") {
console.log("流不存在")
this.timer = setTimeout(() => {
this.webrtcPlayer.close();
this.play(url)
}, 100)
}
});
webrtcPlayer.on(ZLMRTCClient.Events.WEBRTC_ON_LOCAL_STREAM, (s) => {// 获取到了本地流
// document.getElementById('selfVideo').srcObject=s;
this.eventcallbacK("LOCAL STREAM", "获取到了本地流")
});
},
pause: function () {
if (webrtcPlayer != null) {
webrtcPlayer.close();
webrtcPlayer = null;
}
},
eventcallbacK: function (type, message) {
console.log("player 事件回调")
console.log(type)
console.log(message)
}
},
unmounted () {
clearTimeout(this.timer);
},
}
</script>
<style>
.LodingTitle {
min-width: 70px;
}
#rtcPlayer {
width: 100%;
background-color: #0f2229;
padding: 5px;
}
#webRtcPlayerBox {
width: 100%;
height: 130px;
object-fit: fill;
}
</style>
应用:
<template>
<div class="video-play-cls">
<WebRTC v-if="styleStr === 'webrtc'" ref="webRTCRef" />
</div>
</template>
<script setup>
import WebRTC from '../components/FlvPlayerTest.vue'
import { ref, computed, onMounted, nextTick } from 'vue'
// 视频源路径
const videoUrl = ref('url播放地址')
// 根据视频源决定使用哪个视频组件
const styleStr = computed(() => {
return /webrtc/.test(videoUrl.value) ? 'webrtc' : '';
})
const webRTCRef = ref(null)
onMounted(async () => {
// 等待 DOM 渲染完成后再设置视频源
await nextTick(); // 等待 DOM 更新完成
if (styleStr.value === 'webrtc' && webRTCRef.value) {
// 只有在 styleStr 是 'webrtc' 的时候才会调用 setVideoUrl
webRTCRef.value.setVideoUrl(videoUrl.value)
}
})
</script>
<style scoped>
.video-play-cls {
height: 150px;
}
</style>
效果:
多个webrtc格式视频流播放组件封装:
<template>
<div class="webrtc-play" v-for="(videoId, index) in videoIds" :key="index">
<video :ref="'videoPlayer' + videoId" :class="videoId" :id="videoId" controls autoplay></video>
<div v-if="uavText" class="uav-text">{{ uavText }}</div>
</div>
</template>
<script>
/* global ZLMRTCClient */
export default {
name: 'WebRTC',
data () {
return {
timers: {},
videoIds: [], // 存储视频流的ID
webrtcPlayers: {} // 存储多个 webrtcPlayer 实例
};
},
props: {
uavText: {
type: String,
default: ''
},
},
methods: {
play (url, id) {
console.log(this.videoIds, 'this.videoIds');
// 添加新的视频 ID
if (!this.videoIds.includes(id)) {
this.videoIds.push(id);
}
// 使用 nextTick 确保 DOM 更新完成
this.$nextTick(() => {
const videoElement = this.$refs['videoPlayer' + id][0];
if (!videoElement) {
console.error(`未找到视频元素 ${id}`);
return;
}
// 初始化新的 ZLMRTCClient.Endpoint 实例
const webrtcPlayer = new ZLMRTCClient.Endpoint({
element: videoElement,
debug: true,
zlmsdpUrl: url,
simulecast: false,
useCamera: false,
audioEnable: true,
videoEnable: true,
recvOnly: true,
usedatachannel: false,
});
// 监听事件
webrtcPlayer.on(ZLMRTCClient.Events.WEBRTC_ICE_CANDIDATE_ERROR, () => {
console.error('ICE 协商出错');
this.eventCallback(id, "ICE ERROR", "ICE 协商出错");
});
webrtcPlayer.on(ZLMRTCClient.Events.WEBRTC_ON_REMOTE_STREAMS, (e) => {
console.log(`播放成功: ${id}`, e.streams);
this.eventCallback(id, "playing", "播放成功");
});
webrtcPlayer.on(ZLMRTCClient.Events.WEBRTC_OFFER_ANWSER_EXCHANGE_FAILED, (e) => {
console.error(`offer answer 交换失败: ${id}`, e);
this.eventCallback(id, "OFFER ANSWER ERROR", "offer answer 交换失败");
if (e.code === -400 && e.msg === "流不存在") {
console.log(`流不存在: ${id}`);
this.timers[id] = setTimeout(() => {
webrtcPlayer.close();
this.play(url, id);
}, 100);
}
});
webrtcPlayer.on(ZLMRTCClient.Events.WEBRTC_ON_LOCAL_STREAM, () => {
this.eventCallback(id, "LOCAL STREAM", "获取到了本地流");
});
// 将 webrtcPlayer 实例存储在对象中
this.webrtcPlayers[id] = webrtcPlayer;
});
},
pause (id) {
const player = this.webrtcPlayers[id];
if (player) {
player.close();
delete this.webrtcPlayers[id];
if (this.timers[id]) {
clearTimeout(this.timers[id]);
}
}
},
eventCallback (id, type, message) {
console.log(`播放器事件回调 - ${id}`);
console.log(type);
console.log(message);
}
},
beforeUnmount () {
// 清理所有定时器和 webrtcPlayer 实例
Object.keys(this.timers).forEach((id) => clearTimeout(this.timers[id]));
Object.keys(this.webrtcPlayers).forEach((id) => this.webrtcPlayers[id].close());
}
};
</script>
<style scoped lang="scss">
.webrtc-play {
position: relative;
width: 100%;
height: 100%;
video {
width: 100%;
height: 100%;
background-color: #000;
}
.uav-text {
position: absolute;
bottom: 5%;
left: 5%;
font-family: 'YouSheBiaoTiHei';
font-size: 20px;
color: #fff;
}
}
</style>
应用:
<template>
<div class="video-play-cls">
<!-- 使用 v-for 渲染多个 WebRTC 组件 -->
<WebRTC
v-for="(item, index) in webRTCItems"
:key="index"
:id="item.id"
:ref="webRTCRefs[index]"
/>
</div>
</template>
<script setup>
import WebRTC from '@/components/video/WebRTC.vue'
import { ref, computed, onMounted, nextTick } from 'vue'
// 根据视频源决定使用哪个视频组件
const styleStr = computed(() => {
return /flv/.test(videoUrl.value) ? 'webrtc' : '';
})
// WebRTC 组件实例的 refs
const webRTCRefs = ref([])
// WebRTC 组件项数据
const webRTCItems = ref([
{ id: 'videoId1', videoUrl: '视频源路径1' },
{ id: 'videoId2', videoUrl: '视频源路径2' }
])
onMounted(async () => {
await nextTick() // 等待 DOM 更新完成
if (styleStr.value === 'webrtc') {
// 遍历每个 WebRTC 组件,传递不同的 id 和视频源
webRTCItems.value.forEach((item, index) => {
const webRTCInstance = webRTCRefs.value[index]
if (webRTCInstance && webRTCInstance.value) {
webRTCInstance.value.pause() // 停止当前播放
webRTCInstance.value.play(item.videoUrl, item.id) // 播放对应的视频
}
})
}
})
</script>
<style scoped lang="scss">
.video-play-cls {
height: 150px;
}
</style>