I'm trying to implement webRTC with the API's that are available in the browser. I'm using this tutorial as my guide: https://www.webrtc-experiment.com/docs/WebRTC-PeerConnection.html
Here's what I'm currently doing. First I get the audio element in the page. I also have an audioStream
variable for storing the stream that I get from navigator.webkitGetUserMedia
when call button is clicked by the initiating user or when answer button is clicked by the receiving user. Then a variable for storing the current call.
var audio = document.querySelector('audio');
var audioStream;
var call = {};
Then I have the following settings:
var iceServers = [
{ url: 'stun:stun1.l.google.com:19302' },
{ url: 'turn:numb.viagenie.ca', credential: 'muazkh', username: '[email protected]' }
];
var sdpConstraints = {
optional: [],
mandatory: {
OfferToReceiveAudio: true,
OfferToReceiveVideo: false
}
};
var DtlsSrtpKeyAgreement = {
DtlsSrtpKeyAgreement: true
};
On page load I create a new peer:
var peer = new webkitRTCPeerConnection({
'iceServers': iceServers
});
On the add stream event I just assign the event to the call variable.
peer.onaddstream = function(event){
call = event;
};
On ice candidate event, I send the candidate to the peer.
peer.onicecandidate = function(event){
var candidate = event.candidate;
if(candidate){
SocketService.emit('message', {
'conversation_id': me.conversation_id,
'targetUser': to,
'candidate': candidate
});
}
if(typeof candidate == 'undefined'){
send_SDP();
}
};
Once the gathering state is completed, I send the SDP.
peer.ongatheringchange = function(e){
if(e.currentTarget && e.currentTarget.iceGatheringState === 'complete'){
send_SDP();
}
};
What the send_SDP
method does is send the local description to the peer.
function send_SDP(){
SocketService.emit('message', {
'conversation_id': me.conversation_id,
'targetUser': to,
'sdp': peer.localDescription
});
}
Here's what I have inside the event listener for the CALL button. So first it gets the audio then assigns the stream to the current peer object. Then it creates a new offer, it that's successful, the local description is set and once that's done, it sends an offer to the other peer.
getAudio(
function(stream){
peer.addStream(stream);
audioStream = stream;
peer.createOffer(function(offerSDP){
peer.setLocalDescription(offerSDP, function(){
SocketService.emit('message', {
'conversation_id': me.conversation_id,
'targetUser': to,
'offerSDP': offerSDP
});
},
function(){});
},
function(){},
sdpConstraints
);
},
function(err){});
On the receiving peer, the offer is captured, so it shows the modal that somebody is calling. The receiving peer can then click on the ANSWER button. But here I'm setting the session description using the offer even before the ANSWER button is clicked.
SocketService.on('message', function(msg){
if(msg.offerSDP){
//show calling modal on the receiving peer
var remoteDescription = new RTCSessionDescription(msg.offerSDP);
peer.setRemoteDescription(remoteDescription, function(){
createAnswer(msg.offerSDP);
},
function(){});
}
});
Once the remote description is set, the answer is created. First by getting the audio then adding the stream to the local peer object. Next the remote session description is created by using the offerSDP
, this remote session description is then set to the local peer object. After that, the answer is created, the local description is set on the local peer and then it sends the answerSDP to the peer who initiated the call.
function createAnswer(offerSDP) {
getAudio(
function(stream){
peer.addStream(stream);
audioStream = stream;
var remoteDescription = new RTCSessionDescription(offerSDP);
peer.setRemoteDescription(remoteDescription);
peer.createAnswer(function(answerSDP) {
peer.setLocalDescription(answerSDP, function(){
SocketService.emit('message', {
'conversation_id': me.conversation_id,
'targetUser': to,
'answerSDP': answerSDP
});
},
function(){});
}, function(err){}, sdpConstraints);
},
function(err){
}
);
};
The peer who initiated the call receives the answerSDP
. Once it does, it creates a remote description using the answerSDP
and uses it to set the remote description for its local peer object
if(msg.answerSDP){
var remoteDescription = new RTCSessionDescription(msg.answerSDP);
peer.setRemoteDescription(remoteDescription, function(){
},
function(){});
}
After that, I'm not really sure what happens next. Based on what I understand, the onicecandidate event is fired on the intiating peer (caller) and it sends a candidate to the receiving peer. Which then executes the following code:
if(msg.candidate){
var candidate = msg.candidate.candidate;
var sdpMLineIndex = msg.candidate.sdpMLineIndex;
peer.addIceCandidate(new RTCIceCandidate({
sdpMLineIndex: sdpMLineIndex,
candidate: candidate
}));
}
Now once the ANSWER button is clicked, a message is sent to the initiating peer that the receiving peer picked up. And it uses the call stream as source for the audio element, once all meta data is load it plays the audio.
SocketService.emit('message', {
'answerCall': true,
'conversation_id': me.conversation_id,
'targetUser': to
});
audio.src = window.URL.createObjectURL(call.stream);
audio.onloadedmetadata = function(e){
audio.play();
}
Something might be wrong here. That's why the audio is only one-way. Only the user who initiated the call can hear the input from the receiving user. Sounds produced by the initiating user can also be heard, so its like echoing what you're saying. Any ideas?
If you know any tutorial or book that shows how to implement webRTC using native API calls, that will also help. Thanks in advance.