技术栈Vue+webRtc+webSocket
webRtc(网页实时通讯)
MediaStream(通过设备的摄像头及话筒获得视频、音频的同步流)
var p = navigator.mediaDevices.getUserMedia({ audio: true, video: true })
p.then(function (mediaStream) {
var video = document.querySelector('video')
video.srcObject = mediaStream
video.onloadedmetadata = function (e) {
// Do something with the video here.
}
})
p.catch(function (err) {
console.log(err.name)
})
在某些情况下webrtc受限带宽传输,可以使用低帧率
var constraints = { video: { frameRate: { ideal: 10, max: 15 } } };
在移动设备(电话)上,前置或者后置摄像头
var front = false;
document.getElementById('flip-button').onclick = function() { front = !front; };
var constraints = { video: { facingMode: (front? "user" : "environment") } };
RTCPeerConnection(建立远端连接)
呼叫方发送一个offer,被呼叫方发出一个answer
//offer
var pc = new PeerConnection();
if (pc.addTrack !== undefined) {
pc.ontrack = (ev) => {
ev.streams.forEach((stream) => doAddStream(stream))
}
} else {
pc.onaddstream = (ev) => {
doAddStream(ev.stream)
}
}
pc.createOffer(function(desc){
pc.setLocalDescription(desc, function() {
// send the offer to a server that can negotiate with a remote client
})
});
//answer
var pc = new PeerConnection();
pc.setRemoteDescription(new RTCSessionDescription(offer), function() {
pc.createAnswer(function(answer) {
pc.setLocalDescription(answer, function() {
// send the answer to the remote connection
})
})
});
通过peerconnection建立一条数据信道,用于发送非视频音频信息。
var pc = new PeerConnection();
var channel = pc.createDataChannel("Mydata");
channel.onopen = function(event) {
channel.send('sending a message');
}
channel.onmessage = function(event) { console.log(event.data); }
通过RTCDataChannel接收和发送数据
<button id="connectButton" name="connectButton" class="buttonleft">
连接
</button>
<button id="disconnectButton" name="disconnectButton" class="buttonright" disabled>
断开
</button>
<div class="messagebox">
<label for="message">输入的消息:
<input type="text" name="message" id="message" placeholder="Message text"
inputmode="latin" size=60 maxlength=120 disabled>
</label>
<button id="sendButton" name="sendButton" class="buttonright" disabled>
发送
</button>
</div>
<div class="messagebox" id="receivebox">
<p>收到的消息</p>
</div>
//设置事件监听
function startup() {
connectButton = document.getElementById('connectButton');
disconnectButton = document.getElementById('disconnectButton');
sendButton = document.getElementById('sendButton');
messageInputBox = document.getElementById('message');
receiveBox = document.getElementById('receivebox');
// Set event listeners for user interface widgets
connectButton.addEventListener('click', connectPeers, false);
disconnectButton.addEventListener('click', disconnectPeers, false);
sendButton.addEventListener('click', sendMessage, false);
}
//建立本地连接
localConnection = new RTCPeerConnection();
sendChannel = localConnection.createDataChannel("sendChannel");
sendChannel.onopen = handleSendChannelStatusChange;
sendChannel.onclose = handleSendChannelStatusChange;
//建立远程连接
remoteConnection = new RTCPeerConnection();
remoteConnection.ondatachannel = receiveChannelCallback;
//设置多个连接
localConnection.onicecandidate = e => !e.candidate
|| remoteConnection.addIceCandidate(e.candidate)
.catch(handleAddCandidateError);
remoteConnection.onicecandidate = e => !e.candidate
|| localConnection.addIceCandidate(e.candidate)
.catch(handleAddCandidateError);
//尝试连接
localConnection.createOffer()
.then(offer => localConnection.setLocalDescription(offer))
.then(() => remoteConnection.setRemoteDescription(localConnection.localDescription))
.then(() => remoteConnection.createAnswer())
.then(answer => remoteConnection.setLocalDescription(answer))
.then(() => localConnection.setRemoteDescription(remoteConnection.localDescription))
.catch(handleCreateDescriptionError);
//处理成功的连接
function handleLocalAddCandidateSuccess() {
connectButton.disabled = true;
}
function handleRemoteAddCandidateSuccess() {
disconnectButton.disabled = false;
}
//连接数据通道
function receiveChannelCallback(event) {
receiveChannel = event.channel;
receiveChannel.onmessage = handleReceiveMessage;
receiveChannel.onopen = handleReceiveChannelStatusChange;
receiveChannel.onclose = handleReceiveChannelStatusChange;
}
//处理频道的更改
function handleSendChannelStatusChange(event) {
if (sendChannel) {
var state = sendChannel.readyState;
if (state === "open") {
messageInputBox.disabled = false;
messageInputBox.focus();
sendButton.disabled = false;
disconnectButton.disabled = false;
connectButton.disabled = true;
} else {
messageInputBox.disabled = true;
sendButton.disabled = true;
connectButton.disabled = false;
disconnectButton.disabled = true;
}
}
}
function handleReceiveChannelStatusChange(event) {
if (receiveChannel) {
console.log("Receive channel's status has changed to " +
receiveChannel.readyState);
}
}
//传送消息
function sendMessage() {
var message = messageInputBox.value;
sendChannel.send(message);
messageInputBox.value = "";
messageInputBox.focus();
}
//接收消息
function handleReceiveMessage(event) {
var el = document.createElement("p");
var txtNode = document.createTextNode(event.data);
el.appendChild(txtNode);
receiveBox.appendChild(el);
}
//断开连接
function disconnectPeers() {
// Close the RTCDataChannels if they're open.
sendChannel.close();
receiveChannel.close();
// Close the RTCPeerConnections
localConnection.close();
remoteConnection.close();
sendChannel = null;
receiveChannel = null;
localConnection = null;
remoteConnection = null;
// Update user interface elements
connectButton.disabled = false;
disconnectButton.disabled = true;
sendButton.disabled = true;
messageInputBox.value = "";
messageInputBox.disabled = true;
}