I started developing with WebRTC, but that thing never gives me ICE candidates. I set up everything, I'm exchanging the descriptions and stuff, I also made a super-ugly function narrow down there to make sure everything runs correctly, one after another. Signaling state is stable for both, onError is never fired (as expected), but onIceCandidate also (not as expected), and when I want to send a random, empty MediaStream object pc1.addStream(new webkitMediaStream());
, it always fires onNegotiationNeeded.
Does anyone have an idea what the heck is wrong with my code? I spent hours of browsing Stack Overflow, HTML5 Rocks, and W3C docs, but I don't understand that. Here is my entire code:
var config={
'iceServers':[{
'url':'stun:stun.l.google.com:19302'
},{
'url':'stun:stun1.l.google.com:19302'
},{
'url':'stun:stun2.l.google.com:19302'
},{
'url':'stun:stun3.l.google.com:19302'
},{
'url':'stun:stun4.l.google.com:19302'
}]
};
var pc1=new webkitRTCPeerConnection(config);
var pc2=new webkitRTCPeerConnection(config);
var onError=function(error)
{
console.error(error);
}
pc1.onicecandidate=function()
{
console.log('PC1 onIceCandidate (finally) fired!');
}
pc2.onicecandidate=function()
{
console.log('PC2 onIceCandidate (finally) fired!');
}
pc1.oniceconnectionstatechange=function()
{
console.log('PC1 oniceconnectionstatechange fired!');
}
pc2.oniceconnectionstatechange=function()
{
console.log('PC2 oniceconnectionstatechange fired!');
}
pc1.onnegotiationneeded=function()
{
console.log('PC1 onnegotiationneeded fired!');
}
pc2.onnegotiationneeded=function()
{
console.log('PC2 onnegotiationneeded fired!');
}
pc1.createOffer(function(offer){
pc1.setLocalDescription(offer,function(){
pc2.setRemoteDescription(new RTCSessionDescription(offer),function(){
pc2.createAnswer(function(answer){
pc2.setLocalDescription(answer,function(){
pc1.setRemoteDescription(new RTCSessionDescription(answer),new Function()/*I don't need you, bro*/,onError);
},onError);
},onError);
},onError);
},onError);
},onError);
BTW I'm developing with Google Chrome. I will make sure it also runs in Firefox, but right now the problem should be cross-browser. I want to make it to data channels before... (but I have nothing against a working solution with Firefox or cross-browser code)
In Chrome 38 and earlier,
OfferToReceiveAudio
defaulted totrue
. Starting from Chrome 39,OfferToReceiveAudio
defaults to false, as announced by a WebRTC engineer at PSA: Behavior change to PeerConnection.createOffer constraint OfferToReceiveAudio (quoted below).Because of this change, the SDP returned by
createOffer
does not contain any media, and therefore the ICE gathering process never starts. You can notice the consequences of this change by observing that the ICE events are never triggered, and the PeerConnection'siceGatheringState
andiceConnectionState
stay "new".To make sure that the ICE gathering starts and complete, you have to add media to your offer, e.g. by setting
OfferToReceiveAudio:true
in the following constraints to your offer (either as a parameter of thePeerConnection
constructor, or as a parameter to thepeerConnection.createOffer
method):(other ways to get media in the SDP include setting
OfferToReceiveVideo:true
, or callingpeerConnection.addStream
with a media stream that you got fromgetUserMedia
)webrtc-discuss: PSA: Behavior change to PeerConnection.createOffer constraint OfferToReceiveAudio:
I found a solution. If you add this code after the config declaration:
and modify the last line like this
it just works. Don't ask me why.
The above solution of Rob W from Jan 3 '15 worked for me at first but led to another problem.
I included
{'offerToReceiveAudio':true,'offerToReceiveVideo':true}
in thecreateOffer
andcreateAnswer
calls andonIceCandidate
was called as described.However, if I got it right,
offerToReceive...
means to only receive but not to send a stream. I figured that out because of thea=recvonly
in the sdp offer and thea=sendonly
in the sdp answer. As a result, only the caller could see the callees video but not vice versa.As Rob states correctly:
Adding the stream was what I had done already in the first place. However, my sending of the sdp happened before that adding because my logical flow was mixed up. Bringing that into the right order (
addStream -> sdp = getLocalDescription -> send(sdp)
) and removing the offerOptions did the trick for me.Hope this helps anyone.