2、业务描述
功能说明:
实现摄像设备的管理列表。 当您点击查看设备列表中的视频时elementui 音频播放,会弹出页面浮动窗口,实时播放摄像机的视频和音频。
视频弹窗下方有一个自行实现的控制栏,实现播放/暂停控制,可以显示播放时间、切换码率、是否全屏等。
疗效如图:
视频浮窗-悬停状态、显示控制栏
视频悬浮窗-非悬停状态,隐藏控制栏
3.代码实现 3.1 Html模板代码
{{errorMessage}}
播放时长:{{currentTime}}
分辨率:
{{item}}
3.2 建立连接并接收音频
getVideo() {
let that = this;
that.isLoading = true;
that.pc = new RTCPeerConnection();
that.pc.addTransceiver("video");
that.pc.addTransceiver("audio");
that.pc.ontrack = function (event) {
var el = document.createElement(event.track.kind);
el.srcObject = event.streams[0];
el.autoplay = true;
document.getElementById("video-wrap").appendChild(el);
if (el.nodeName === "VIDEO") {
el.oncanplay = () => {
that.isLoading = false;
// 播放状态设置为true
that.isPlaying = true;
that.getVideoDuration();
};
} else if (el.nodeName === "AUDIO") {
el.oncanplay = () => {
};
}
};
that.pc
.createOffer()
.then((offer) => {
that.pc.setLocalDescription(offer);
let req = {
webrtc: offer,
};
console.log(offer);
return that.$api.device.getSignaling(
that.deviceData.id,
that.origin,
that.selectResolution,
req
);
})
.then((res) => {
if (res.code === 0) {
that.isSuccess = true;
that.pc.setRemoteDescription(res.body.webrtc);
that.connId = res.body.connId;
} else {
that.errorMessage = res.message || "视频加载错误";
}
})
.catch(alert);
}
参考PeerConnection建立连接的流程。
参考RTCPeerConnection支持的socket
createOffer()方法:主动与其他peer建立P2P连接,整理出自己的SDP信息,通过信令服务器转发给其他peer。
前面的代码中,认证交换是通过向前端发送POST请求来实现的。
that.pc.addTransceiver("video");
that.pc.addTransceiver("audio");
表示音频和视频均已接收。
that.pc.ontrack = function(event){
}
该方法接收音频和视频,并使用接收到的数据创建视频和音频元素。
只监听PC状态无法监听到实际视频可以播放的状态,所以需要给视频添加一个监听方法:
el.oncanplay = () => {
that.isLoading = false;
// 播放状态设置为true
that.isPlaying = true;
that.getVideoDuration();
};
当视频可以播放时,加载状态被取消并开始视频持续时间。
3.3 控制音视频的JS代码
获取视频播放时长的方法:
getVideoDuration() {
var video = document.getElementsByTagName("video")[0];
// 如果没有获取到视频元素
if (!video) {
return;
}
let that = this;
video.addEventListener("timeupdate", () => {
that.currentTime = getTime(video.currentTime);
});
var getTime = function (time) {
let hour =
Math.floor(time / 3600) < 10
? "0" + Math.floor(time / 3600)
: Math.floor(time / 3600);
let min =
Math.floor((time % 3600) / 60) < 10
? "0" + Math.floor((time % 3600) / 60)
: Math.floor((time % 3600) / 60);
var sec =
Math.floor(time % 60) < 10
? "0" + Math.floor(time % 60)
: Math.floor(time % 60);
return hour + ":" + min + ":" + sec;
};
}
如何控制音视频同步暂停:
playOrPauseVideo() {
var video = document.getElementsByTagName("video")[0];
var audio = document.getElementsByTagName("audio")[0];
if (this.isPlaying) {
video.pause();
audio.pause();
} else {
// audio
video.play();
audio.play();
}
this.isPlaying = !this.isPlaying;
}
全屏模式
onClickFullScreen() {
let dialogElement = document.getElementById("dialog-wrap");
dialogElement.webkitRequestFullScreen();
}
3.4 样式表
样式部分比较简单,有以下几点值得注意:
video::-webkit-media-controls {
/* 去掉全屏时显示的自带控制条 */
display: none !important;
}
$controlFontColor: rgb(136 141 150);
$backgroundColor: rgba(0, 0, 0, 0.8);
$height: 60px;
.el-dialog .el-dialog__body {
padding: 0 !important;
margin-bottom: 0 !important;
width: unset !important;
}
.video-onloading {
min-height: 500px;
background-color: $backgroundColor;
span {
width: 100%;
display: block;
line-height: 500px;
text-align: center;
color: $controlFontColor;
i {
margin-right: 5px;
}
i::before {
font-size: 17px;
}
}
}
.cover {
bottom: 0px;
height: 300px;
position: absolute;
width: 100%;
z-index: 2;
&:hover,
&:focus,
&:focus-within {
.controls {
display: flex;
}
}
}
.controls {
width: 100%;
height: $height;
line-height: $height;
font-size: 15px;
display: none;
z-index: 2;
background-color: $backgroundColor;
color: $controlFontColor;
position: absolute;
bottom: 0
justify-content: space-between;
& > [class^="el-icon-"] {
&::before {
font-size: 26px;
line-height: $height;
padding: 0 15px;
cursor: pointer;
}
}
.playStatus {
width: 64px;
height: $height;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
#currentTime {
width: 140px;
height: $height;
text-align: center;
}
.control-resolution {
line-height: $height;
.el-input__inner {
background: $backgroundColor;
}
.el-input {
width: 95px;
}
input {
border: none;
font-size: 15px !important;
color: $controlFontColor;
&::-webkit-input-placeholder {
color: $controlFontColor;
}
}
}
#fullScreen {
width: 32px;
height: 32px;
position: relative;
top: 16px;
}
}
总结
这一次elementui 音频播放,后端业务WebRTC才有了共同的认识和应用。 它只应用了接收流,并没有使用推送流。 WebRTC还有更多的用途,比如实时视频通话、语音通话等,其实后续的业务中都会用到,所以借这个博客做个介绍记录一下~