1. 什么是WebRTC
1.1 WebRTC简介
WebRTC,名称源自网页即时通信(英语:Web Real-Time Communication)的缩写,是一个支持网页浏览器进行实时语音对话或视频对话的实时通信框架,提供了一系列页面可调用API。
参考定义: 谷歌开放实时通信框架
在上一篇博客Vue +WebSocket + WaveSurferJS 实现H5聊天对话交互 中,已经涉及到WebRTC接口的使用,使用到了getUserMedia方法,用于通过浏览器获取设备麦克风,从而采集音频。
最近项目中的需求则是与服务端建立即时通信,实现低延迟音视频直播。
RTC的特征是(参考来源:https://www.zhihu.com/question/22301898)
复杂度较高
半可靠传输,对于特定情境(比如网络环境较差时)可以对音视频进行有损传输,降低延迟
音视频友好:可针对音视频做定制化优化
提供端对端优化方案。 对于传统连接模式,使用C/S架构,A=>服务端=>B,而WebRTC使用的是peer-to-peer模式,A=>B,一旦点和点之间的连接形成,它们之间的数据传输是不经过服务端的,大大降低了服务端的压力。
理论延迟较低,能应用在各种低延迟场景。
2. 业务描述
功能描述:
实现对摄像设备的管理列表,在设备列表点击查看视频时,弹出页面浮窗,进行摄像机摄像的视频和音频实时转播。
视频弹窗下方有自己实现的控制条,实现播放/暂停控制,能显示播放时间、切换分辨率、是否全屏等。
效果如图:
3. 代码实现
3.1 Html模板代码
<el-dialog ref="videoDialog" title="视频播放" :visible.sync="dialogShowed" :close-on-click-modal="false">
<div>
<div v-if="isSuccess" v-loading="isLoading" element-loading-text="视频加载中" element-loading-spinner="el-icon-loading"
element-loading-background="rgba(0, 0, 0, 0.8)" />
<div v-else v-loading="isLoading" element-loading-text="视频加载中" element-loading-spinner="el-icon-loading"
element-loading-background="rgba(0, 0, 0, 0.8)">
<span><i v-if="!isLoading" />{{errorMessage}}</span>
</div>
<!-- 遮罩层 -->
<div v-if="isSuccess">
<div>
<i v-if="!isPlaying" @click="playOrPauseVideo" />
<i v-else @click="playOrPauseVideo" />
<div>播放时长:{{currentTime}}</div>
<div>
分辨率:
<el-select v-model="selectResolution" @change="changeResolution">
<el-option v-for="item in resolutions" :key="item" :value="item">
{{item}}
</el-option>
</el-select>
</div>
<i @click="onClickFullScreen"></i>
</div>
</div>
</div>
</el-dialog>
使用了Element-UI框架提供的v-loading指令,该指令根据isLoading属性决定是否在区域内加载loading动画
若视频加载失败,则显示错误信息
预留标签,用于挂载`video和audio DOM元素
<div ></div>
注意该标签内最好不要再加其他元素,这样后续判断比较简单。
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);
}