webrtc是一个由google发起的开源实时通信方案,其中包括视频/音频采集、编解码、数据传输、音视频展示的功能。在浏览器,桌面应用,移动设备或者lot设备上都有可以运行的api接口,均可实现实时通信能力。web开发者可以基于web api开发基于视频、音频的实时通信应用,如视频会议,远程教育,视频通话,视频直播,游戏直播,远程协助,互动游戏,实时人脸识别等功能。
webrtc主要应用在实时通信方面,其优点总结为如下几点。
1、跨平台:可以在Web,Android,iOS,Windows,MacOS、Linux等环境下运行
2、实时传输:传输速度快,低延迟,适合实时性要求较高的场景
3、音视频引擎:有强大的音视频处理能力
4、免插件:不需要安装任何插件,兼容性高,打开浏览器即可使用,并且免费开源
5、强大的打洞能力:webrtc技术包含了使用STUN、ICE、TURN、RTP-over-TCP的关键NAT和防火墙穿透技术
本次将通过webrtc实现一对一视频对话的功能,首先要进行本地媒体资源的操作
<body>
<input type="button" title="开启摄像头" value="开启前置摄像头" v-on:click="getMedia(0)"/>
<input type="button" title="开启摄像头" value="开启后置摄像头" v-on:click="getMedia(1)"/>
<video height="120px" autoplay="autoplay" :src="video_1_url" crossOrigin="anonymous" muted="muted" controls>video>
<hr/>
body>
<script src="/static/js/Vue.2.5.3.js">script>
<script type="text/javascript">
// 兼容版本
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
window.URL = window.URL || window.webkitURL || window.mozURL || window.msURL;
getMedia: function (a) {
let that = this;
if (that.exArray.length <= a) {
alert("设备不存在")
return
}
if (navigator.getUserMedia) {
// 获取用户媒体数据,
navigator.getUserMedia({
'video': {
'optional': [{
'sourceId': that.exArray[a] //0为前置摄像头,1为后置
}]
},
'audio': true // 不需要音频的话为false
}, that.successFunc, that.errorFunc); //success是获取成功的回调函数,也可以使用await来处理异步
} else {
alert('Native device media streaming (getUserMedia) not supported in this browser.');
}
},
// 摄像头加载成功
successFunc: function (stream) {
if (this.video.mozSrcObject !== undefined) {
//Firefox中,video.mozSrcObject最初为null,而不是未定义的,我们可以靠这个来检测Firefox的支持
this.video.mozSrcObject = stream;
} else {
// 将流媒体复制给video标签进行播放
this.video.srcObject = stream
}
this.stream = stream
// 获取track 视频轨道
let deviceInfo = stream.getVideoTracks()
let tracks = deviceInfo[0]
// 可以对轨道进行操作
console.log(tracks.getConstraints())
this.deviceName = deviceInfo[0].label
this.video.play();
// 音频
this.audio = new Audio();
this.audioType = this.getAudioType(this.audio);
if (this.audioType) {
this.audio.src = 'polaroid.' + this.audioType;
this.audio.play();
}
}
loadDevice: function () {
let that = this;
navigator.mediaDevices.enumerateDevices()
.then(function (sourceInfos) {
// 获取设备列表
console.log(sourceInfos)
for (let i = 0; i != sourceInfos.length; ++i) {
let sourceInfo = sourceInfos[i];
//这里会遍历audio,video,所以要加以区分
if (sourceInfo.kind === 'videoinput') {
that.exArray.push(sourceInfo.deviceId);
}
}
}
).catch(function (err) {
alert("获取设备列表错误")
});
},
<script type="text/javascript">
本次只展示部分重要代码,部分代码可以自行补充,打开摄像头流程如下
1先使用getUserMedia 约束条件获取到媒体, 其中 ‘audio’: true 表示获取视频,video:turn 表示获取声音
2.使用navigator.mediaDevices.enumerateDevices 方法获取到设备
3.获取到媒体资源后使用video标签进行播放
4.在媒体流中通过getVideoTracks 方法可以获取到当前流媒体的轨道即正在使用的设备。
<body>
<input type="button" value="开启麦克风" v-on:click="checkVoice()"/>
body>
<script type="text/javascript">
// 开启声音
checkVoice: function () {
if (navigator.getUserMedia) {
window.audioContext = new AudioContext()
navigator.getUserMedia({
'video': false,
'audio': true
}, this.onVoice, this.errorFunc); //success是获取成功的回调函数
} else {
alert('Native device media streaming (getUserMedia) not supported in this browser.');
}
},
// 获取到媒体声音后 音量展示 stream表示流媒体
onVoice: function (stream) {
let that = this;
//创建一个管理、播放声音的对象
let audioContext = new AudioContext()
let analyser = audioContext.createAnalyser()
liveSource = audioContext.createMediaStreamSource(stream); //将麦克风的声音输入这个对象\
// 音频连入分析器
liveSource.connect(analyser)
// 分析器再将音频连入麦克风-这样播放的音频就会通过分析器
analyser.connect(audioContext.destination)
analyser.fftSize = 128
setInterval(() => {
let dataArray = new Uint8Array(analyser.frequencyBinCount)
analyser.getByteFrequencyData(dataArray)
// console.log(dataArray)
let maxVal = 0;
for (let i = 0; i < dataArray.length; i++) {
if (maxVal < dataArray[i]) {
maxVal = dataArray[i];
}
}
that.voiceWidth = Math.round(maxVal) + "px"
that.drow(dataArray)
}, 50)
},
// 画图案
drow: function (array) {
var canvas = document.getElementById('canvas3'),
cwidth = canvas.width,
cheight = canvas.height - 2,
meterWidth = 10, //能量条的宽度
gap = 2, //能量条间的间距
capHeight = 2,
meterNum = 800 / (10 + 2), //计算当前画布上能画多少条
ctx = canvas.getContext('2d')
var step = Math.round(array.length / meterNum);
ctx.clearRect(0, 0, cwidth, cheight); //清理画布准备画画
gradient = ctx.createLinearGradient(0, 0, 0, 300);
gradient.addColorStop(1, '#0f0');
gradient.addColorStop(0.5, '#ff0');
gradient.addColorStop(0, '#f00');
ctx.fillStyle = gradient;
for (var i = 0; i < meterNum; i++) {
var value = array[i * step];
ctx.fillRect(i * 12, cheight - value + capHeight, meterWidth, cheight);
}
},
script>
和打开视频流程一样,
1.同样使用getUserMedia()方法 约束条件’audio’: true , 即可请求麦克风。
2.当获取到音频流后传递给audio对象的srcObject方法即可。
3.音频可视化,audioContext 方法可以创建一个音频分析器,将音频流载入后就能展示音频的大小
<body>
<input type="button" title="截屏" value="截屏" v-on:click="getPhoto()"/><br/>
<canvas id="canvas1" height="120px">canvas>
body>
<script>
getPhoto: function () {
// 创建canvas画布
this.canvas1 = document.getElementById('canvas1');
this.context1 = this.canvas1.getContext('2d');
//将video对象内指定的区域捕捉绘制到画布上指定的区域,实现拍照。
this.context1.drawImage(this.video, 0, 0, 90, 120);
// 转图片下载
// 抓取照片数据
let imgData = this.canvas1.toDataURL(); //将图像转换为base64数据
let base64Data = imgData.substr(22); //在前端截取22位之后的字符串作为图像数据
imgData = imgData.replace(this.changeImageType("png"), 'image/octet-stream');
let save_link = document.createElementNS('http://www.w3.org/1999/xhtml', 'a');
save_link.href = imgData;
save_link.download = (new Date()).getTime() + '.' + "png";
let event = document.createEvent('MouseEvents');
event.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
save_link.dispatchEvent(event);
}
// 修改图片类型操作
changeImageType: function (type) {
type = type.toLowerCase().replace(/jpg/i, 'jpeg');
let r = type.match(/png|jpeg|bmp|gif/)[0];
return 'image/' + r;
},
script>
在摄像头正常可以播放的时候,可以使用canvas进行图像捕捉
1.使用媒体约束条件,调用getUserMedia()方法,实现摄像头播放正常
2.获取到媒体流时,传递给video的srcObject对象进行播放
3.创建canvas 标签,调用canvas的drawImage 方法,传递video对象可以生成一张2d图片。
4.将canvas 图转换格式实现本地下载
<body>
<input type="button" title="视频" value="视频" v-on:click="getVideo()"/><br/>
<canvas id="canvas2" height="120px">canvas>
body>
<script>
//视频
getVideo: function () {
this.canvas2 = document.getElementById('canvas2');
this.context2 = this.canvas2.getContext('2d');
this.drawVideoAtCanvas(this.video, this.context2);
},
// 将视频帧绘制到Canvas对象上,Canvas每60ms切换帧,形成肉眼视频效果
drawVideoAtCanvas: function (video, context) {
window.setInterval(function () {
context.drawImage(video, 0, 0, 90, 120);
}, 60);
},
script>
在摄像头正常可以播放的时候,可以使用canvas进行图像捕捉
1.使用媒体约束条件,调用getUserMedia()方法,实现摄像头播放正常
2.获取到媒体流时,传递给video的srcObject对象进行播放
3.将video的视频源载入canvas ,实现图案绘制,最后每隔60毫秒,绘制一张图片实现视频的同步
1.访问相关设备API
方法名 | 参数 | 说明 |
---|---|---|
getUserMedia | 定义约束对象,是否调用音频或者视频 | 采集摄像头或者麦克风设备 |
getDisplayMedia | 定义约束对象,设置视频采集宽高,是否调用视频 | 捕获计算机屏幕的方法(本章未用到) |
MediaStreamConstanints | video,audio | 调用getUserMedia和getDisplayMedia方法的约束条件,ture表示采集,false表示不采集 |
2.在本章学习使用媒体设备的基本操作,首先我们要解决的兼容性,因为不同的浏览器的不同版本对webrtc的兼容性不同,因此在使用前首先要解决兼容性。
3.再次我们学回了如何访问媒体设备,使用设置约束调剂调用音频或者视频流。
4.最后我们将媒体流以不同的形式进行了展示,做到了可视化。每一步都很有意思
5.在使用webrtc的时候需要我们部署https服务访问,否则可能会因为安全问题而操作失败
6.当然在后面还有一些分享桌面,捕捉等操作没在本次示例中演示。具体可以查看代码仓库。
感谢作者 亢少军老师的 《WebRTC音视频开发 》 课本指导