webrtc和rtp 一 pion的datachannel示例
上次文章一已经说明了webrtc 和 rtp 协议的关系,说明了 而sctp 协议 Stream Control Transmission Protocol(SCTP)协议 在chrome中打开如下地址: chrome://webrtc-internals/,可以看到很多信息,里面channel-data-1 是数据通道 可以看到google peer 2 peer 是使用的libjingle 时间戳像是一个服务一样,一直在更改。 这不得不引起我们的注意,webrtc不断更改,技术迭代更新太快了,为什么?基础好,这三个字分量太重,中国有句俗话:勿以恶小而为之,勿以善小而不为,这句话并非只适用在我们定义的善恶之上,也适用在程序员身上,程序员的恶习之一就是不求甚解,如果基础打好,我们的代码便是一片江山。
zoom公司大家其实都知道,会议的体验很好,因为他们善用了datachannel,有兴趣大家可以仔细研究,推敲,到了今天,技术和核心技术得不到大家的理解,很多人依然认为技术可以获取,缺的是钱,事实上,两个都缺。sctp协议可以适用tcp,也可以使用udp,zoom公司在一个版本中使用datachannel传输RTP数据,原因是什么?为了体验和效率。
web示例
Realtime communication with WebRTC
Realtime communication with WebRTC
Start
Send
Stop
'use strict';
var localConnection;
var remoteConnection;
var sendChannel;
var receiveChannel;
var pcConstraint;
var dataConstraint;
var dataChannelSend = document.querySelector('textarea#dataChannelSend');
var dataChannelReceive = document.querySelector('textarea#dataChannelReceive');
var startButton = document.querySelector('button#startButton');
var sendButton = document.querySelector('button#sendButton');
var closeButton = document.querySelector('button#closeButton');
startButton.onclick = createConnection;
sendButton.onclick = sendData;
closeButton.onclick = closeDataChannels;
function enableStartButton() {
startButton.disabled = false;
}
function disableSendButton() {
sendButton.disabled = true;
}
function createConnection() {
dataChannelSend.placeholder = '';
var servers = null;
pcConstraint = null;
dataConstraint = null;
trace('Using SCTP based data channels');
// For SCTP, reliable and ordered delivery is true by default.
// Add localConnection to global scope to make it visible
// from the browser console.
window.localConnection = localConnection =
new RTCPeerConnection(servers, pcConstraint);
trace('Created local peer connection object localConnection');
sendChannel = localConnection.createDataChannel('sendDataChannel',
dataConstraint);
trace('Created send data channel');
localConnection.onicecandidate = iceCallback1;
sendChannel.onopen = onSendChannelStateChange;
sendChannel.onclose = onSendChannelStateChange;
// Add remoteConnection to global scope to make it visible
// from the browser console.
window.remoteConnection = remoteConnection =
new RTCPeerConnection(servers, pcConstraint);
trace('Created remote peer connection object remoteConnection');
remoteConnection.onicecandidate = iceCallback2;
remoteConnection.ondatachannel = receiveChannelCallback;
localConnection.createOffer().then(
gotDescription1,
onCreateSessionDescriptionError
);
startButton.disabled = true;
closeButton.disabled = false;
}
function onCreateSessionDescriptionError(error) {
trace('Failed to create session description: ' + error.toString());
}
function sendData() {
var data = dataChannelSend.value;
sendChannel.send(data);
trace('Sent Data: ' + data);
}
function closeDataChannels() {
trace('Closing data channels');
sendChannel.close();
trace('Closed data channel with label: ' + sendChannel.label);
receiveChannel.close();
trace('Closed data channel with label: ' + receiveChannel.label);
localConnection.close();
remoteConnection.close();
localConnection = null;
remoteConnection = null;
trace('Closed peer connections');
startButton.disabled = false;
sendButton.disabled = true;
closeButton.disabled = true;
dataChannelSend.value = '';
dataChannelReceive.value = '';
dataChannelSend.disabled = true;
disableSendButton();
enableStartButton();
}
function gotDescription1(desc) {
localConnection.setLocalDescription(desc);
trace('Offer from localConnection \n' + desc.sdp);
remoteConnection.setRemoteDescription(desc);
remoteConnection.createAnswer().then(
gotDescription2,
onCreateSessionDescriptionError
);
}
function gotDescription2(desc) {
remoteConnection.setLocalDescription(desc);
trace('Answer from remoteConnection \n' + desc.sdp);
localConnection.setRemoteDescription(desc);
}
function iceCallback1(event) {
trace('local ice callback');
if (event.candidate) {
remoteConnection.addIceCandidate(
event.candidate
).then(
onAddIceCandidateSuccess,
onAddIceCandidateError
);
trace('Local ICE candidate: \n' + event.candidate.candidate);
}
}
function iceCallback2(event) {
trace('remote ice callback');
if (event.candidate) {
localConnection.addIceCandidate(
event.candidate
).then(
onAddIceCandidateSuccess,
onAddIceCandidateError
);
trace('Remote ICE candidate: \n ' + event.candidate.candidate);
}
}
function onAddIceCandidateSuccess() {
trace('AddIceCandidate success.');
}
function onAddIceCandidateError(error) {
trace('Failed to add Ice Candidate: ' + error.toString());
}
function receiveChannelCallback(event) {
trace('Receive Channel Callback');
receiveChannel = event.channel;
receiveChannel.onmessage = onReceiveMessageCallback;
receiveChannel.onopen = onReceiveChannelStateChange;
receiveChannel.onclose = onReceiveChannelStateChange;
}
function onReceiveMessageCallback(event) {
trace('Received Message');
dataChannelReceive.value = event.data;
}
function onSendChannelStateChange() {
var readyState = sendChannel.readyState;
trace('Send channel state is: ' + readyState);
if (readyState === 'open') {
dataChannelSend.disabled = false;
dataChannelSend.focus();
sendButton.disabled = false;
closeButton.disabled = false;
} else {
dataChannelSend.disabled = true;
sendButton.disabled = true;
closeButton.disabled = true;
}
}
function onReceiveChannelStateChange() {
var readyState = receiveChannel.readyState;
trace('Receive channel state is: ' + readyState);
}
function trace(text) {
if (text[text.length - 1] === '\n') {
text = text.substring(0, text.length - 1);
}
if (window.performance) {
var now = (window.performance.now() / 1000).toFixed(3);
console.log(now + ': ' + text);
} else {
console.log(text);
}
}
上面一段代码是可以运行的,不过大意义没有,他无法产品化,接下去的第三课,我们会大刀去修改他,让他真正实用起来,读者可以先研究以下这一段示例代码。 后面我们会使用rtp协议和信令服务继续改写,让他实用并且产品化。