问题描述
我一直在阅读这篇文章是一个信号解决方案.作者提到了在建立连接时使用 RTCDataChannel 进行信令.
使用 RTCDataChannel 进行信令
启动 WebRTC 会话需要信令服务.
但是,一旦在两个对等方之间建立了连接,理论上 RTCDataChannel 可以接管作为信令通道.这可能会减少信令的延迟——因为消息是直接传输的——并有助于降低信令服务器带宽和处理成本.我们没有演示,但请观看此空间!
既然已经建立了连接,为什么还需要信令?
每一方最初声明它要发送哪些音频和/或视频轨道,以便可以打开正确数量的端口,以及分辨率和格式可以确定双方的工作.需要一个信令通道将生成的 SDP 提供/应答以及每个端口的涓流 ICE 候选发送到另一端.
一旦连接,如果你不理会这个设置——基本上永远不要向连接添加轨道、删除任何轨道或显着改变轨道属性——那么你就不再需要信令服务器了.
但是,如果您确实更改了其中任何一项,则需要重新协商,这听起来就像:在信令通道上进行另一轮与第一轮非常相似.>
添加轨道的原因可能是第二个摄像头、另一个视频源(可能来自另一个参与者),或者可能是屏幕共享,诸如此类.
这篇文章是正确的,可以使用数据通道.这是演示!(目前仅适用于 Firefox.)
这篇关于需要信令服务的文章是错误的——前提是你有另一种发现方式——正如这个演示蹩脚地证明的那样.
初始连接仅限聊天,但任何一方都可以推送以将视频添加到混合中.为此重新协商是通过数据通道完成的(因为没有信令服务器!)
小提琴使用说明:
- 没有服务器(因为它是一个小提琴),所以按
Offer
按钮并复制报价. - 将优惠粘贴到另一个选项卡或另一台机器上同一小提琴中的同一位置.
- 按 ENTER,然后复制您得到的答案并将其粘贴回第一个小提琴.
- 再次按 ENTER(还不是
addTrack
!) - 您现在连接了两个数据通道:一个用于聊天,另一个用于发送信号.
- 现在在任一端按
addTrack
,视频应显示在另一端. - 在另一个方向按
addTrack
,您应该可以双向播放视频.
var dc = null, sc = null, pc = new mozRTCPeerConnection(), live = false;pc.onaddstream = e =>v2.mozSrcObject = e.stream;pc.ondatachannel = e =>直流?scInit(sc = e.channel) : dcInit(dc = e.channel);v2.onloadedmetadata = e =>{日志(面对时间!");};函数 addTrack() {navigator.mediaDevices.getUserMedia({video:true, audio:true}).then(stream => pc.addStream(v1.mozSrcObject = stream));}pc.onnegotiationneeded = e =>{pc.createOffer().then(d => pc.setLocalDescription(d)).then(() => {if (live) sc.send(JSON.stringify({ "sdp": pc.localDescription }));}).catch(失败);};函数 scInit() {sc.onmessage = e =>{var msg = JSON.parse(e.data);如果(msg.sdp){var desc = new mozRTCSessionDescription(JSON.parse(e.data).sdp);if (desc.type == "offer") {pc.setRemoteDescription(desc).then(() => pc.createAnswer()).then(answer => pc.setLocalDescription(answer)).then(() => {sc.send(JSON.stringify({ "sdp": pc.localDescription }));}).catch(失败);} 别的 {pc.setRemoteDescription(desc).catch(failed);}} else if (msg.candidate) {pc.addIceCandidate(new mozRTCIceCandidate(msg.candidate)).catch(failed);}};}函数 dcInit() {dc.onopen = () =>{ 现场 = 真;日志(聊天!");};dc.onmessage = e =>日志(电子数据);}函数 createOffer() {button.disabled = true;dcInit(dc = pc.createDataChannel("chat"));scInit(sc = pc.createDataChannel("signaling"));pc.createOffer().then(d => pc.setLocalDescription(d)).catch(failed);pc.onicecandidate = e =>{如果(e.candidate)返回;如果(!直播){offer.value = pc.localDescription.sdp;优惠.选择();answer.placeholder = "在这里粘贴答案";} 别的 {sc.send(JSON.stringify({ "candidate": e.candidate }));}};};offer.onkeypress = e =>{if (e.keyCode != 13 || pc.signalingState != "stable") 返回;button.disabled = offer.disabled = true;var obj = { type:"offer", sdp:offer.value };pc.setRemoteDescription(new mozRTCSessionDescription(obj)).then(() => pc.createAnswer()).then(d => pc.setLocalDescription(d)).catch(失败);pc.onicecandidate = e =>{如果(e.candidate)返回;如果(!直播){answer.focus();answer.value = pc.localDescription.sdp;answer.select();} 别的 {sc.send(JSON.stringify({ "candidate": e.candidate }));}};};answer.onkeypress = e =>{if (e.keyCode != 13 || pc.signalingState != "have-local-offer") 返回;answer.disabled = true;var obj = { type:"answer", sdp:answer.value };pc.setRemoteDescription(new mozRTCSessionDescription(obj)).catch(failed);};chat.onkeypress = e =>{if (e.keyCode != 13) 返回;dc.send(chat.value);日志(聊天.值);chat.value = "";};var log = msg =>div.innerHTML += ""+ msg + "</p>";var 失败 = e =>log(e.name + ":" + e.message + " line " + e.lineNumber);
I've been reading this article for a signaling solution. The author mentions about signaling with RTCDataChannel when connections are established.
Why is signaling needed since connections are already established?
Each side initially declares which audio and/or video tracks it is going to send, so that the right number of ports can be opened, and resolutions and formats that work for both peers can be determined. A signaling channel is needed to send the resulting SDP offer/answer, as well as trickle ICE candidates for each of the ports, to the other side.
Once connected, if you leave this setup alone - basically never add tracks to the connection, remove any, or alter track attributes significantly - then you wont need the signaling server again.
If you do change any of those things however, then a re-negotiation is needed, which is just what it sounds like: another round over the signaling channel much like the first one.
Reasons to add a track may be a second camera, another video-source (from another participant perhaps), or maybe screen-sharing, something like that.
The article is correct that a data channel may be used. Here's a demo! (Firefox only for now.)
The article is wrong about a signaling service being required - provided you have another means of discovery - as this demo lamely proves.
The initial connection is chat-only, but either side can push to add video to the mix. The re-negotiation for this is done over a data channel (since there's no signaling server!)
Instructions for using the fiddle:
- There is no server (since it's a fiddle), so press the
Offer
button and copy the offer. - Paste the offer to the same spot in the same fiddle in another tab or on another machine.
- Press ENTER, then copy the answer you get and paste it back in the first fiddle.
- Press ENTER again (not
addTrack
yet!) - You are now connected with two data-channels: one for chat and another for signaling.
- Now press
addTrack
on either end and video should show up on the other end. - Press
addTrack
in the other direction, and you should have video going both ways.
var dc = null, sc = null, pc = new mozRTCPeerConnection(), live = false;
pc.onaddstream = e => v2.mozSrcObject = e.stream;
pc.ondatachannel = e => dc? scInit(sc = e.channel) : dcInit(dc = e.channel);
v2.onloadedmetadata = e => { log("Face time!"); };
function addTrack() {
navigator.mediaDevices.getUserMedia({video:true, audio:true})
.then(stream => pc.addStream(v1.mozSrcObject = stream));
}
pc.onnegotiationneeded = e => {
pc.createOffer().then(d => pc.setLocalDescription(d)).then(() => {
if (live) sc.send(JSON.stringify({ "sdp": pc.localDescription }));
}).catch(failed);
};
function scInit() {
sc.onmessage = e => {
var msg = JSON.parse(e.data);
if (msg.sdp) {
var desc = new mozRTCSessionDescription(JSON.parse(e.data).sdp);
if (desc.type == "offer") {
pc.setRemoteDescription(desc).then(() => pc.createAnswer())
.then(answer => pc.setLocalDescription(answer)).then(() => {
sc.send(JSON.stringify({ "sdp": pc.localDescription }));
}).catch(failed);
} else {
pc.setRemoteDescription(desc).catch(failed);
}
} else if (msg.candidate) {
pc.addIceCandidate(new mozRTCIceCandidate(msg.candidate)).catch(failed);
}
};
}
function dcInit() {
dc.onopen = () => { live = true; log("Chat!"); };
dc.onmessage = e => log(e.data);
}
function createOffer() {
button.disabled = true;
dcInit(dc = pc.createDataChannel("chat"));
scInit(sc = pc.createDataChannel("signaling"));
pc.createOffer().then(d => pc.setLocalDescription(d)).catch(failed);
pc.onicecandidate = e => {
if (e.candidate) return;
if (!live) {
offer.value = pc.localDescription.sdp;
offer.select();
answer.placeholder = "Paste answer here";
} else {
sc.send(JSON.stringify({ "candidate": e.candidate }));
}
};
};
offer.onkeypress = e => {
if (e.keyCode != 13 || pc.signalingState != "stable") return;
button.disabled = offer.disabled = true;
var obj = { type:"offer", sdp:offer.value };
pc.setRemoteDescription(new mozRTCSessionDescription(obj))
.then(() => pc.createAnswer()).then(d => pc.setLocalDescription(d))
.catch(failed);
pc.onicecandidate = e => {
if (e.candidate) return;
if (!live) {
answer.focus();
answer.value = pc.localDescription.sdp;
answer.select();
} else {
sc.send(JSON.stringify({ "candidate": e.candidate }));
}
};
};
answer.onkeypress = e => {
if (e.keyCode != 13 || pc.signalingState != "have-local-offer") return;
answer.disabled = true;
var obj = { type:"answer", sdp:answer.value };
pc.setRemoteDescription(new mozRTCSessionDescription(obj)).catch(failed);
};
chat.onkeypress = e => {
if (e.keyCode != 13) return;
dc.send(chat.value);
log(chat.value);
chat.value = "";
};
var log = msg => div.innerHTML += "<p>" + msg + "</p>";
var failed = e => log(e.name + ": " + e.message + " line " + e.lineNumber);
<video id="v1" height="120" width="160" autoplay muted></video>
<video id="v2" height="120" width="160" autoplay></video><br>
<button id="button" onclick="createOffer()">Offer:</button>
<textarea id="offer" placeholder="Paste offer here"></textarea><br>
Answer: <textarea id="answer"></textarea><br>
<button id="button" onclick="addTrack()">AddTrack</button>
<div id="div"></div><br>
Chat: <input id="chat"></input><br>
这篇关于RTCDataChannel 用于信令?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!