关于webrtc的icecandidate和sdp的几个问题
a few questions about icecandidate and sdp of webrtc
假设有3个用户A、B、C。我使用 Ajax 而不是 websockets 来发送和接收消息。
问题一击:
1,在这种情况下我只需要(B和C)可以看到A,而不需要A可以看到(B和C)。我已将 A 的 sdp(offer) 添加到 B 。那么A有必要加上B的sdp(answer)吗?
2,我发现在A createOffer()的时候生成了很多(大约17个)icecandidate。而且我不知道如何处理这些 icecandidate 。 B(和C)应该把这些所有的icecandidate一个一个地加17次吗?
3,正如我所说的 3 个用户。这是一个一对多的应用程序。如何实施?我的主要问题是如何处理每个人的 sdp 和 icecandidate one.Does 需要添加其他人的 sdp 和 icecandidate .
这是 icecandidates 的打击:
{"candidate":"candidate:1754064501 1 udp 2122255103 2001::5ef5:79fd:8c8:c74e:f166:22f 52522 typ host generation 0","sdpMid":"audio","sdpMLineIndex":0}
{"candidate":"candidate:1026183099 1 udp 2122194687 192.168.2.100 52523 typ host generation 0","sdpMid":"audio","sdpMLineIndex":0}
{"candidate":"candidate:1754064501 2 udp 2122255102 2001::5ef5:79fd:8c8:c74e:f166:22f 52524 typ host generation 0","sdpMid":"audio","sdpMLineIndex":0}
{"candidate":"candidate:1026183099 2 udp 2122194686 192.168.2.100 52525 typ host generation 0","sdpMid":"audio","sdpMLineIndex":0}
{"candidate":"candidate:1754064501 1 udp 2122255103 2001::5ef5:79fd:8c8:c74e:f166:22f 52526 typ host generation 0","sdpMid":"video","sdpMLineIndex":1}
{"candidate":"candidate:1026183099 1 udp 2122194687 192.168.2.100 52527 typ host generation 0","sdpMid":"video","sdpMLineIndex":1}
{"candidate":"candidate:1754064501 2 udp 2122255102 2001::5ef5:79fd:8c8:c74e:f166:22f 52528 typ host generation 0","sdpMid":"video","sdpMLineIndex":1}
{"candidate":"candidate:1026183099 2 udp 2122194686 192.168.2.100 52529 typ host generation 0","sdpMid":"video","sdpMLineIndex":1}
{"candidate":"candidate:638524037 1 tcp 1518275327 2001::5ef5:79fd:8c8:c74e:f166:22f 0 typ host tcptype active generation 0","sdpMid":"audio","sdpMLineIndex":0}
{"candidate":"candidate:1940501323 1 tcp 1518214911 192.168.2.100 0 typ host tcptype active generation 0","sdpMid":"audio","sdpMLineIndex":0}
{"candidate":"candidate:638524037 2 tcp 1518275326 2001::5ef5:79fd:8c8:c74e:f166:22f 0 typ host tcptype active generation 0","sdpMid":"audio","sdpMLineIndex":0}
{"candidate":"candidate:1940501323 2 tcp 1518214910 192.168.2.100 0 typ host tcptype active generation 0","sdpMid":"audio","sdpMLineIndex":0}
{"candidate":"candidate:638524037 1 tcp 1518275327 2001::5ef5:79fd:8c8:c74e:f166:22f 0 typ host tcptype active generation 0","sdpMid":"video","sdpMLineIndex":1}
{"candidate":"candidate:1940501323 1 tcp 1518214911 192.168.2.100 0 typ host tcptype active generation 0","sdpMid":"video","sdpMLineIndex":1}
{"candidate":"candidate:638524037 2 tcp 1518275326 2001::5ef5:79fd:8c8:c74e:f166:22f 0 typ host tcptype active generation 0","sdpMid":"video","sdpMLineIndex":1}
{"candidate":"candidate:1940501323 2 tcp 1518214910 192.168.2.100 0 typ host tcptype active generation 0","sdpMid":"video","sdpMLineIndex":1}
null
这是我的应用程序代码:您会发现我使用了一个临时按钮,该按钮调用 "Get Answer" 让 A 获取 B 或 C
的 sdp 和 icecandidate(已注释掉)
var getUserMedia = (navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia);
//use Google's stun server
var iceServer = {
"iceServers": [{
"url": "stun:stun.l.google.com:19302"
}]
};
var PeerConnection = (window.PeerConnection ||
window.webkitPeerConnection00 ||
window.webkitRTCPeerConnection ||
window.mozRTCPeerConnection);
var pc = new PeerConnection(iceServer);
var iceTimes = 1;
function getAnswer(cid)
{
teacherLoop(cid);
/*for(i=1;i<4;i++)
{
$.ajax({
url:'/get_webrtc_info.php',
type:'POST',
async:false,
data:{role:'get_answer_ice',times:i,"cid": cid},
success:function(msg)
{
var msg = JSON.parse(msg);
pc.addIceCandidate(new RTCIceCandidate(msg));
}
});
}*/
}
//start live
function startLive(cid)
{
pc.onicecandidate = function(event){
$.ajax({
url:"/webrtc.php",
type:'POST',
async:false,
data:{
"event": "__ice_candidate",
"times": iceTimes,
"cid": cid,
"data": {
"candidate": JSON.stringify(event.candidate)
}
},
});
iceTimes++;
};
$(".course-item-play-block").slideDown();
getUserMedia.call(navigator, {
"audio": true,
"video": true
}, function(stream){
var myselfVideoElement = document.getElementById('video');
myselfVideoElement.src = URL.createObjectURL(stream);
pc.addStream(stream);
pc.createOffer(function(desc){
pc.setLocalDescription(desc);
$.ajax({
url:'/webrtc.php',
type:'POST',
async:false,
data:{
"event": "__offer",
"cid": cid,
"data": {
"sdp": JSON.stringify(desc)
}
},
success:function()
{
//var timer = setInterval(teacherLoop,4000);
}
});
});
}, function(error){
//
});
}
function joinLive(cid)
{
$(".course-item-play-block").slideDown();
pc.onaddstream = function(event)
{
var remote_video = document.getElementById('video');
remote_video.src = URL.createObjectURL(event.stream);
}
pc.onicecandidate = function(event){
$.ajax({
url:"/webrtc.php",
type:'POST',
async:false,
data:{
"event": "__ice_candidate_answer",
"times": answerIceTimes,
"cid": cid,
"data": {
"candidate": JSON.stringify(event.candidate)
}
},
});
answerIceTimes++;
};
studentLoop(cid)
getIce(cid)
}
function getIce(cid)
{
for(i=1;i<17;i++)
{
$.ajax({
url:'/get_webrtc_info.php',
type:'POST',
async:false,
data:{role:'get_ice',times:i,"cid": cid},
success:function(msg)
{
var msg = JSON.parse(msg);
pc.addIceCandidate(new RTCIceCandidate(msg));
}
});
}
}
function teacherLoop(cid)
{
$.ajax({
url:'/get_webrtc_info.php',
type:'POST',
data:{role:'teacher',"cid": cid},
success:function(msg)
{
var msg = JSON.parse(msg);
pc.setRemoteDescription(new RTCSessionDescription(msg));
}
});
}
function studentLoop(cid)
{
$.ajax({
url:'/get_webrtc_info.php',
type:'POST',
async:false,
data:{role:'student',"cid": cid},
success:function(msg)
{
var msg = JSON.parse(msg);
pc.setRemoteDescription(new RTCSessionDescription(msg));
pc.createAnswer(function(answer) {
pc.setLocalDescription(new RTCSessionDescription(answer), function() {
$.ajax({
url:'/webrtc.php',
type:'POST',
async:false,
data:{
"event": "__answer",
"cid": cid,
"data": {
"sdp": JSON.stringify(answer)
}
},
success:function()
{
//var timer = setInterval(student,4000);
//studentLoop()
}
});
});
});
});
}
<div class="block course-item-play-block">
<h3>Living:</h3>
<video id="video" autoplay></video>
</div>
<?php if ($isTeacher) {?>
<div class="btn edit-course-btn common-btn common-btn-6x18 common-btn-blue" onclick="startLive(<?=$Data['cid']?>)">Start Live</div>
<div class="btn edit-course-btn common-btn common-btn-6x18 common-btn-blue" onclick="getAnswer(<?=$Data['cid']?>)">Get Answer</div>
<?php }else{?>
<div class="btn edit-course-btn common-btn common-btn-6x18 common-btn-blue" onclick="joinLive(<?=$Data['cid']?>)">Join Live</div>
<?php }?>
我没有在 php 上工作过,但就 WebRTC 而言:
- 使用 Web 套接字或 Ajax 在对等点之间中继数据没有任何区别。
- 即使B只是消费者,A是提供者,B还是需要提供
sdp-answer
,其实谁调用谁接听并不重要,提供者需要在之前加上他的MediaStream制作 sdp offer/answer.
- ICE candidates 就像物理地址,它们帮助 peer 找到彼此,A 不是唯一创建它们的 peer,B(和 C)也创建了它们。您需要将它们传递给两侧适当的
PeerConnection
对象。如果不交换,就无法开始通话。
- 虽然它是一对多的,但是对于每个新的消费者(B,C,...),提供者(A)需要创建一个单独的
PeerConnection
,您也可以查看Muaz Khan 的 one-to-many boradcasting. If you want to protect the provider's bandwidth, you need to look into MCU, here 是来自 Kurento 的一对多广播示例。
假设有3个用户A、B、C。我使用 Ajax 而不是 websockets 来发送和接收消息。
问题一击:
1,在这种情况下我只需要(B和C)可以看到A,而不需要A可以看到(B和C)。我已将 A 的 sdp(offer) 添加到 B 。那么A有必要加上B的sdp(answer)吗?
2,我发现在A createOffer()的时候生成了很多(大约17个)icecandidate。而且我不知道如何处理这些 icecandidate 。 B(和C)应该把这些所有的icecandidate一个一个地加17次吗?
3,正如我所说的 3 个用户。这是一个一对多的应用程序。如何实施?我的主要问题是如何处理每个人的 sdp 和 icecandidate one.Does 需要添加其他人的 sdp 和 icecandidate .
这是 icecandidates 的打击:
{"candidate":"candidate:1754064501 1 udp 2122255103 2001::5ef5:79fd:8c8:c74e:f166:22f 52522 typ host generation 0","sdpMid":"audio","sdpMLineIndex":0}
{"candidate":"candidate:1026183099 1 udp 2122194687 192.168.2.100 52523 typ host generation 0","sdpMid":"audio","sdpMLineIndex":0}
{"candidate":"candidate:1754064501 2 udp 2122255102 2001::5ef5:79fd:8c8:c74e:f166:22f 52524 typ host generation 0","sdpMid":"audio","sdpMLineIndex":0}
{"candidate":"candidate:1026183099 2 udp 2122194686 192.168.2.100 52525 typ host generation 0","sdpMid":"audio","sdpMLineIndex":0}
{"candidate":"candidate:1754064501 1 udp 2122255103 2001::5ef5:79fd:8c8:c74e:f166:22f 52526 typ host generation 0","sdpMid":"video","sdpMLineIndex":1}
{"candidate":"candidate:1026183099 1 udp 2122194687 192.168.2.100 52527 typ host generation 0","sdpMid":"video","sdpMLineIndex":1}
{"candidate":"candidate:1754064501 2 udp 2122255102 2001::5ef5:79fd:8c8:c74e:f166:22f 52528 typ host generation 0","sdpMid":"video","sdpMLineIndex":1}
{"candidate":"candidate:1026183099 2 udp 2122194686 192.168.2.100 52529 typ host generation 0","sdpMid":"video","sdpMLineIndex":1}
{"candidate":"candidate:638524037 1 tcp 1518275327 2001::5ef5:79fd:8c8:c74e:f166:22f 0 typ host tcptype active generation 0","sdpMid":"audio","sdpMLineIndex":0}
{"candidate":"candidate:1940501323 1 tcp 1518214911 192.168.2.100 0 typ host tcptype active generation 0","sdpMid":"audio","sdpMLineIndex":0}
{"candidate":"candidate:638524037 2 tcp 1518275326 2001::5ef5:79fd:8c8:c74e:f166:22f 0 typ host tcptype active generation 0","sdpMid":"audio","sdpMLineIndex":0}
{"candidate":"candidate:1940501323 2 tcp 1518214910 192.168.2.100 0 typ host tcptype active generation 0","sdpMid":"audio","sdpMLineIndex":0}
{"candidate":"candidate:638524037 1 tcp 1518275327 2001::5ef5:79fd:8c8:c74e:f166:22f 0 typ host tcptype active generation 0","sdpMid":"video","sdpMLineIndex":1}
{"candidate":"candidate:1940501323 1 tcp 1518214911 192.168.2.100 0 typ host tcptype active generation 0","sdpMid":"video","sdpMLineIndex":1}
{"candidate":"candidate:638524037 2 tcp 1518275326 2001::5ef5:79fd:8c8:c74e:f166:22f 0 typ host tcptype active generation 0","sdpMid":"video","sdpMLineIndex":1}
{"candidate":"candidate:1940501323 2 tcp 1518214910 192.168.2.100 0 typ host tcptype active generation 0","sdpMid":"video","sdpMLineIndex":1}
null
这是我的应用程序代码:您会发现我使用了一个临时按钮,该按钮调用 "Get Answer" 让 A 获取 B 或 C
的 sdp 和 icecandidate(已注释掉)var getUserMedia = (navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia);
//use Google's stun server
var iceServer = {
"iceServers": [{
"url": "stun:stun.l.google.com:19302"
}]
};
var PeerConnection = (window.PeerConnection ||
window.webkitPeerConnection00 ||
window.webkitRTCPeerConnection ||
window.mozRTCPeerConnection);
var pc = new PeerConnection(iceServer);
var iceTimes = 1;
function getAnswer(cid)
{
teacherLoop(cid);
/*for(i=1;i<4;i++)
{
$.ajax({
url:'/get_webrtc_info.php',
type:'POST',
async:false,
data:{role:'get_answer_ice',times:i,"cid": cid},
success:function(msg)
{
var msg = JSON.parse(msg);
pc.addIceCandidate(new RTCIceCandidate(msg));
}
});
}*/
}
//start live
function startLive(cid)
{
pc.onicecandidate = function(event){
$.ajax({
url:"/webrtc.php",
type:'POST',
async:false,
data:{
"event": "__ice_candidate",
"times": iceTimes,
"cid": cid,
"data": {
"candidate": JSON.stringify(event.candidate)
}
},
});
iceTimes++;
};
$(".course-item-play-block").slideDown();
getUserMedia.call(navigator, {
"audio": true,
"video": true
}, function(stream){
var myselfVideoElement = document.getElementById('video');
myselfVideoElement.src = URL.createObjectURL(stream);
pc.addStream(stream);
pc.createOffer(function(desc){
pc.setLocalDescription(desc);
$.ajax({
url:'/webrtc.php',
type:'POST',
async:false,
data:{
"event": "__offer",
"cid": cid,
"data": {
"sdp": JSON.stringify(desc)
}
},
success:function()
{
//var timer = setInterval(teacherLoop,4000);
}
});
});
}, function(error){
//
});
}
function joinLive(cid)
{
$(".course-item-play-block").slideDown();
pc.onaddstream = function(event)
{
var remote_video = document.getElementById('video');
remote_video.src = URL.createObjectURL(event.stream);
}
pc.onicecandidate = function(event){
$.ajax({
url:"/webrtc.php",
type:'POST',
async:false,
data:{
"event": "__ice_candidate_answer",
"times": answerIceTimes,
"cid": cid,
"data": {
"candidate": JSON.stringify(event.candidate)
}
},
});
answerIceTimes++;
};
studentLoop(cid)
getIce(cid)
}
function getIce(cid)
{
for(i=1;i<17;i++)
{
$.ajax({
url:'/get_webrtc_info.php',
type:'POST',
async:false,
data:{role:'get_ice',times:i,"cid": cid},
success:function(msg)
{
var msg = JSON.parse(msg);
pc.addIceCandidate(new RTCIceCandidate(msg));
}
});
}
}
function teacherLoop(cid)
{
$.ajax({
url:'/get_webrtc_info.php',
type:'POST',
data:{role:'teacher',"cid": cid},
success:function(msg)
{
var msg = JSON.parse(msg);
pc.setRemoteDescription(new RTCSessionDescription(msg));
}
});
}
function studentLoop(cid)
{
$.ajax({
url:'/get_webrtc_info.php',
type:'POST',
async:false,
data:{role:'student',"cid": cid},
success:function(msg)
{
var msg = JSON.parse(msg);
pc.setRemoteDescription(new RTCSessionDescription(msg));
pc.createAnswer(function(answer) {
pc.setLocalDescription(new RTCSessionDescription(answer), function() {
$.ajax({
url:'/webrtc.php',
type:'POST',
async:false,
data:{
"event": "__answer",
"cid": cid,
"data": {
"sdp": JSON.stringify(answer)
}
},
success:function()
{
//var timer = setInterval(student,4000);
//studentLoop()
}
});
});
});
});
}
<div class="block course-item-play-block">
<h3>Living:</h3>
<video id="video" autoplay></video>
</div>
<?php if ($isTeacher) {?>
<div class="btn edit-course-btn common-btn common-btn-6x18 common-btn-blue" onclick="startLive(<?=$Data['cid']?>)">Start Live</div>
<div class="btn edit-course-btn common-btn common-btn-6x18 common-btn-blue" onclick="getAnswer(<?=$Data['cid']?>)">Get Answer</div>
<?php }else{?>
<div class="btn edit-course-btn common-btn common-btn-6x18 common-btn-blue" onclick="joinLive(<?=$Data['cid']?>)">Join Live</div>
<?php }?>
我没有在 php 上工作过,但就 WebRTC 而言:
- 使用 Web 套接字或 Ajax 在对等点之间中继数据没有任何区别。
- 即使B只是消费者,A是提供者,B还是需要提供
sdp-answer
,其实谁调用谁接听并不重要,提供者需要在之前加上他的MediaStream制作 sdp offer/answer. - ICE candidates 就像物理地址,它们帮助 peer 找到彼此,A 不是唯一创建它们的 peer,B(和 C)也创建了它们。您需要将它们传递给两侧适当的
PeerConnection
对象。如果不交换,就无法开始通话。 - 虽然它是一对多的,但是对于每个新的消费者(B,C,...),提供者(A)需要创建一个单独的
PeerConnection
,您也可以查看Muaz Khan 的 one-to-many boradcasting. If you want to protect the provider's bandwidth, you need to look into MCU, here 是来自 Kurento 的一对多广播示例。