feat(liveView): 添加视频播放器控件并优化媒体流处理

- 新增video元素用于统一播放音视频流
- 合并音频和视频轨道到单一媒体流
- 优化播放器控件设置和样式
- 移除冗余代码并简化DOM操作
This commit is contained in:
yindongqi 2025-08-13 17:11:14 +08:00
parent 2103d3fcc1
commit 51da660509

View File

@ -4,64 +4,34 @@
<div id="liveContent" class="liveContent" ref="liveContent">
<div id="screenAudio" style="display: none" ref="screenAudio"></div>
<div class="live-status" v-show="liveStatus == 'scheduled'">
<svg
t="1733991622296"
class="icon"
viewBox="0 0 1024 1024"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
p-id="7587"
width="38"
height="38"
data-spm-anchor-id="a313x.search_index.0.i3.59a33a81UKNXHQ"
>
<svg t="1733991622296" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
p-id="7587" width="38" height="38" data-spm-anchor-id="a313x.search_index.0.i3.59a33a81UKNXHQ">
<path
d="M526.933333 288c-23.466667 0-42.666667 19.2-42.666666 42.666667v213.333333c0 23.466667 19.2 42.666667 42.666666 42.666667s42.666667-19.2 42.666667-42.666667v-213.333333c0-23.466667-17.066667-42.666667-42.666667-42.666667zM526.933333 629.333333c-12.8 0-21.333333 4.266667-29.866666 12.8-8.533333 8.533333-12.8 17.066667-12.8 29.866667 0 12.8 4.266667 23.466667 12.8 29.866667 8.533333 8.533333 19.2 12.8 29.866666 12.8 12.8 0 21.333333-4.266667 29.866667-12.8s12.8-19.2 12.8-32-4.266667-23.466667-12.8-29.866667c-6.4-6.4-17.066667-10.666667-29.866667-10.666667z"
p-id="7588"
fill="#ffffff"
></path>
p-id="7588" fill="#ffffff"></path>
<path
d="M526.933333 74.666667c-234.666667 0-426.666667 192-426.666666 426.666666s192 426.666667 426.666666 426.666667 426.666667-192 426.666667-426.666667-189.866667-426.666667-426.666667-426.666666z m0 768c-187.733333 0-341.333333-153.6-341.333333-341.333334s153.6-341.333333 341.333333-341.333333 341.333333 153.6 341.333334 341.333333-151.466667 341.333333-341.333334 341.333334z"
p-id="7589"
fill="#ffffff"
></path>
p-id="7589" fill="#ffffff"></path>
</svg>
オンライン授業未开始
</div>
<div class="live-status" v-show="liveStatus == 'finished'">
<svg
t="1733991622296"
class="icon"
viewBox="0 0 1024 1024"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
p-id="7587"
width="38"
height="38"
data-spm-anchor-id="a313x.search_index.0.i3.59a33a81UKNXHQ"
>
<svg t="1733991622296" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
p-id="7587" width="38" height="38" data-spm-anchor-id="a313x.search_index.0.i3.59a33a81UKNXHQ">
<path
d="M526.933333 288c-23.466667 0-42.666667 19.2-42.666666 42.666667v213.333333c0 23.466667 19.2 42.666667 42.666666 42.666667s42.666667-19.2 42.666667-42.666667v-213.333333c0-23.466667-17.066667-42.666667-42.666667-42.666667zM526.933333 629.333333c-12.8 0-21.333333 4.266667-29.866666 12.8-8.533333 8.533333-12.8 17.066667-12.8 29.866667 0 12.8 4.266667 23.466667 12.8 29.866667 8.533333 8.533333 19.2 12.8 29.866666 12.8 12.8 0 21.333333-4.266667 29.866667-12.8s12.8-19.2 12.8-32-4.266667-23.466667-12.8-29.866667c-6.4-6.4-17.066667-10.666667-29.866667-10.666667z"
p-id="7588"
fill="#ffffff"
></path>
p-id="7588" fill="#ffffff"></path>
<path
d="M526.933333 74.666667c-234.666667 0-426.666667 192-426.666666 426.666666s192 426.666667 426.666666 426.666667 426.666667-192 426.666667-426.666667-189.866667-426.666667-426.666667-426.666666z m0 768c-187.733333 0-341.333333-153.6-341.333333-341.333334s153.6-341.333333 341.333333-341.333333 341.333333 153.6 341.333334 341.333333-151.466667 341.333333-341.333334 341.333334z"
p-id="7589"
fill="#ffffff"
></path>
p-id="7589" fill="#ffffff"></path>
</svg>
オンライン授業が終わる
</div>
<div
class="watermark-container"
v-if="isVisible"
@animationend="onAnimationEnd"
:key="animationKey"
>
<div class="watermark-container" v-if="isVisible" @animationend="onAnimationEnd" :key="animationKey">
{{ loginInfo.member_realname }}_{{ loginInfo.member_passport }}
</div>
<div id="teacherCamera" class="teacherCamera" ref="teacherCamera"></div>
<video ref="player" controls style="width: 100%; height: 100%;"></video>
<!-- <div class="custom-controls" v-show="showControlBar">
<div class="teacherAudioList" v-show="teacherAudioList.length > 0">
<div v-for="(teacherAudio, index) in teacherAudioList" class="teacherAudio">
@ -212,13 +182,8 @@
<img :src="message.avatar" alt="avatar" class="avatar" />
<div class="message-body">
<div class="message-header">
<span class="message-sender"
><span
class="teacher-tag"
v-if="message.sender.slice(0, 7) === 'teacher'"
>講師</span
>{{ message.sender }}</span
>
<span class="message-sender"><span class="teacher-tag"
v-if="message.sender.slice(0, 7) === 'teacher'">講師</span>{{ message.sender }}</span>
<span class="message-time">{{
"\u00A0\u00A0\u00A0" + formatTimestamp(message.time)
}}</span>
@ -231,11 +196,7 @@
<div ref="scrollAnchor"></div>
</div>
<div class="message-input">
<input
v-model="newMessage"
@keyup.enter="sendMessage"
placeholder="メッセージを入力..."
/>
<input v-model="newMessage" @keyup.enter="sendMessage" placeholder="メッセージを入力..." />
<button @click="sendMessage">送信</button>
</div>
</div>
@ -252,46 +213,27 @@
<div class="info-content-item-title">開始時間</div>
<div class="info-content-item-value">{{ liveInfo.start_time }}</div>
</div>
<div
class="info-content-item"
v-show="liveInfo.end_time != '' && liveInfo.end_time != null"
>
<div class="info-content-item" v-show="liveInfo.end_time != '' && liveInfo.end_time != null">
<div class="info-content-item-title">終了時間</div>
<div class="info-content-item-value">{{ liveInfo.end_time }}</div>
</div>
<div class="info-content-item" style="">
<div class="info-content-item-title">詳細</div>
<div
class="info-content-item-value"
style="
<div class="info-content-item-value" style="
display: flex;
flex-direction: column;
align-items: center;
padding-top: 10px;
"
>
<svg
t="1733988199430"
class="icon"
viewBox="0 0 1024 1024"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
p-id="6925"
data-spm-anchor-id="a313x.search_index.0.i0.14ef3a81TzRpck"
width="64"
height="64"
xmlns:xlink="http://www.w3.org/1999/xlink"
>
">
<svg t="1733988199430" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
p-id="6925" data-spm-anchor-id="a313x.search_index.0.i0.14ef3a81TzRpck" width="64" height="64"
xmlns:xlink="http://www.w3.org/1999/xlink">
<path
d="M892.5 304.6L689.4 101.4c-22.7-22.7-52.8-35.1-84.9-35.1H218.7c-66.2 0-120 53.8-120 120v654.1c0 66.2 53.8 120 120 120h589c66.2 0 120-53.8 120-120v-451c0-32-12.5-62.2-35.2-84.8zM667.7 192.9l123.4 123.4h-83.4c-22.1 0-40-17.9-40-40v-83.4z m140 687.5h-589c-22.1 0-40-17.9-40-40V186.3c0-22.1 17.9-40 40-40h369v130c0 66.2 53.8 120 120 120h140v444.1c0 22-17.9 40-40 40z"
p-id="6926"
fill="#8a8a8a"
></path>
p-id="6926" fill="#8a8a8a"></path>
<path
d="M310 378.1h177.1c22.1 0 40-17.9 40-40s-17.9-40-40-40H310c-22.1 0-40 17.9-40 40s17.9 40 40 40zM716.4 473.3H310c-22.1 0-40 17.9-40 40s17.9 40 40 40h406.4c22.1 0 40-17.9 40-40s-17.9-40-40-40zM716.4 648.6H310c-22.1 0-40 17.9-40 40s17.9 40 40 40h406.4c22.1 0 40-17.9 40-40s-17.9-40-40-40z"
p-id="6927"
fill="#8a8a8a"
></path>
p-id="6927" fill="#8a8a8a"></path>
</svg>
詳細なし
</div>
@ -440,16 +382,25 @@ export default {
audio.remove(); // <video>
});
}
audioTracks[index].play(this.$refs.screenAudio);
// audioTracks[index].play(this.$refs.screenAudio);
} else {
this.teacherAudioList.push({
name: audioTracks[index].userID,
isMute: false,
showVolumeControl: false,
});
// console.log("teacherAudioList:", this.teacherAudioList);
audioTracks[index].play(this.liveContent);
// audioTracks[index].play(this.liveContent);
}
// tracktrack
if (this.$refs.player.srcObject) {
//
const existingStream = this.$refs.player.srcObject;
existingStream.addTrack(audioTracks[index]._track.mediaTrack);
} else {
//
const newStream = new MediaStream();
newStream.addTrack(audioTracks[index]._track.mediaTrack);
this.$refs.player.srcObject = newStream;
}
}
@ -460,37 +411,61 @@ export default {
// <video>
// console.log("", videoElements);
if (videoElements.length > 0) {
videoElements.forEach((video) => {
video.remove(); // <video>
});
// if (videoElements.length > 0) {
// videoElements.forEach((video) => {
// video.remove(); // <video>
// });
// }
// videoTracks[index].play(this.liveContent);
//tracktrack
if (this.$refs.player.srcObject) {
//
const existingStream = this.$refs.player.srcObject;
existingStream.addTrack(videoTracks[index]._track.mediaTrack);
} else {
//
const newStream = new MediaStream();
newStream.addTrack(videoTracks[index]._track.mediaTrack);
this.$refs.player.srcObject = newStream;
}
videoTracks[index].play(this.liveContent);
// 使 setTimeout
setTimeout(() => {
const videos = document.querySelectorAll(
"video.qnrtc-video-player.qnrtc-stream-player"
);
// controls
for (let i = 0; i < videos.length; i++) {
videos[i].controls = true;
const videos = this.$refs.player;
videos.controls = true;
//
videos[i].style.pointerEvents = "auto";
videos.style.pointerEvents = "auto";
//
videos[i].disablePictureInPicture = true;
videos.disablePictureInPicture = true;
//
videos[i].style.objectFit = "contain";
videos.style.objectFit = "contain";
//
videos[i].addEventListener("contextmenu", function (e) {
videos.addEventListener("contextmenu", function (e) {
e.preventDefault();
});
// const videos = document.querySelectorAll(
// "video.qnrtc-video-player.qnrtc-stream-player"
// );
// controls
// for (let i = 0; i < videos.length; i++) {
// videos[i].controls = true;
// //
// videos[i].style.pointerEvents = "auto";
// //
// videos[i].disablePictureInPicture = true;
// //
// videos[i].style.objectFit = "contain";
// //
// videos[i].addEventListener("contextmenu", function (e) {
// e.preventDefault();
// });
//
setTimeout(() => {
this.isStop = false;
this.isVisible = true;
}, 10000);
}
// }
}, 100);
} else if (videoTracks[index].tag === "video") {
const teacherCameraElement = this.$refs.teacherCamera;
@ -859,7 +834,7 @@ export default {
width: 100%;
position: relative;
height: 35vh;
background-color: gray;
background-color: black;
overflow: hidden;
margin: auto;
margin-top: 5rem;