docs/WebRTC-PeerConnection.html
This tutorial is out-dated (written in 2013). Please try a LIVE demo here instead: https://webrtc.github.io/samples/src/content/peerconnection/pc1/
In case if above link gets broken: https://github.com/webrtc/samples
Please check this tutorial (seems written by Sam Dutton): https://codelabs.developers.google.com/codelabs/webrtc-web/#0
An PeerConnection object can be initialized like this:
var connection = new [webkit|moz]RTCPeerConnection(
'ice-servers',
'optional-arguments'
);
Here is a simple example to create offer:
var connection = new [webkit|moz]RTCPeerConnection(
'ice-servers',
'optional-arguments'
);
connection.createOffer(getOfferSDP, onfailure,[sdpConstraints](#sdpConstraints));
function getOfferSDP(offerSDP) {
connection.setLocalDescription(offerSDP [, successCallback, failureCallback]);
console.log('offer sdp', offerSDP.sdp);
console.log('type', offerSDP.type);
};
Here is a simple example to create answer:
var connection = new [webkit|moz]RTCPeerConnection(
'ice-servers',
'optional-arguments'
);
// "setRemoteDescription" is quickly called for answerer
var remoteSessionDescription = new RTCSessionDescription(offerSDP);
connection.setRemoteDescription(remoteSessionDescription, successCallback, failureCallback);
connection.createAnswer(getAnswerSDP, onfailure,[sdpConstraints](#sdpConstraints));
function getAnswerSDP(answerSDP) {
connection.setLocalDescription(answerSDP);
console.log('answer sdp', answerSDP.sdp);
console.log('type', answerSDP.type);
};
You created both offer and answer; now complete the handshake:
// "setRemoteDescription" is called "later" for offerer
// it will complete the offer/answer handshake
var remoteSessionDescription = new RTCSessionDescription(answerSDP);
connection.setRemoteDescription(remoteSessionDescription, successCallback, failureCallback);
So, 1st step is to open websocket connection:
var socket = new WebSocket('ws://domain.com:80/');
socket.onopen = function() {};
socket.onmessage = function() {};
Assuming that there is a "Start Peer Connection" button; when offerer will click it; we will create offer on his side; and share with other user:
<button id="start-peer-connection">Start Peer Connection</button>
// javascript code
var button = document.getElementById('start-peer-connection');
button.onclick = function() {
this.disabled = true;
var peer = new [webkit|moz]RTCPeerConnection(iceServers, optional);
peer.createOffer(function(offerSDP) {
peer.setLocalDescription(offerSDP);
socket.send({
targetUser: 'target-user-id',
offerSDP: offerSDP
});
}, onfailure,[sdpConstraints](#sdpConstraints));
};
Defining "iceServers" and "optional" variables:
var STUN = {
urls: 'stun:stun.l.google.com:19302'
};
var TURN = {
urls: 'turn:turn.bistri.com:80',
credential: 'homeo',
username: 'homeo'
};
var iceServers = {
iceServers: [STUN, TURN]
};
// DTLS/SRTP is preferred on chrome
// to interop with Firefox
// which supports them by default
var DtlsSrtpKeyAgreement = {
DtlsSrtpKeyAgreement: true
};
var optional = {
optional: [DtlsSrtpKeyAgreement]
};
Here is a list of events and methods can be used with RTCPeerConnection object:
Each method and event has its own importance; however, for the sake of simplicity, we will try only "onicecandidate" and "onaddstream":
ICE trackling is not necessary; even it is not part of WebRTC specification. Alternatives will be explained later in this document.
peer.onaddstream = function(mediaStream) {
video.src = webkitURL.createObjectURL(mediaStream);
};
peer.onicecandidate = function(event) {
var candidate = event.candidate;
if(candidate) {
socket.send({
targetUser: 'target-user-id',
candidate: candidate
});
}
};
A media-stream can be attached using "addStream" method:
// it is suggested to use gumadapter.js instead:
//[https://github.com/muaz-khan/gumadapter](https://github.com/muaz-khan/gumadapter)navigator.webkitGetUserMedia(MediaConstraints, OnMediaSuccess, OnMediaError);
var MediaConstraints = {
audio: true,
video: true
};
function OnMediaError(error) {
console.error(error);
}
function OnMediaSuccess(mediaStream) {
peer.addStream(mediaStream);
}
Remeber, chrome now supports attachment of multiple media streams:
peer.addStream(audioStream);
peer.addStream(videoStream);
peer.addStream(screenCapturingStream);
It will fire "onaddstream" multiple times according to number of media streams attached. Duplicate streams attachment is not allowed.
Putting above all together:
<button id="start-peer-connection">Start Peer Connection</button>
// javascript code
var button = document.getElementById('start-peer-connection');
button.onclick = function() {
this.disabled = true;
// it is suggested to use gumadapter.js instead:
//[https://github.com/muaz-khan/gumadapter](https://github.com/muaz-khan/gumadapter)navigator.webkitGetUserMedia(MediaConstraints, OnMediaSuccess, OnMediaError);
var MediaConstraints = {
audio: true,
video: true
};
function OnMediaError(error) {
console.error(error);
}
function OnMediaSuccess(mediaStream) {
var peer = new [webkit|moz]RTCPeerConnection(iceServers, optional);
peer.addStream(mediaStream);
peer.onaddstream = function(mediaStream) {
video.src = webkitURL.createObjectURL(mediaStream);
};
peer.onicecandidate = function(event) {
var candidate = event.candidate;
if(candidate) {
socket.send({
targetUser: 'target-user-id',
candidate: candidate
});
}
};
peer.createOffer(function(offerSDP) {
peer.setLocalDescription(offerSDP, successCallback, failureCallback);
socket.send({
targetUser: 'target-user-id',
offerSDP: offerSDP
});
}, onfailure,[sdpConstraints](#sdpConstraints));
}
};
var STUN = {
urls: 'stun:stun.l.google.com:19302'
};
var TURN = {
urls: 'turn:turn.bistri.com:80',
credential: 'homeo',
username: 'homeo'
};
var iceServers = {
iceServers: [STUN, TURN]
};
// DTLS/SRTP is preferred on chrome
// to interop with Firefox
// which supports them by default
var DtlsSrtpKeyAgreement = {
DtlsSrtpKeyAgreement: true
};
var optional = {
optional: [DtlsSrtpKeyAgreement]
};
OK. Offer is completed. Now, it is time to create answer.
socket.onmessage = function(e) {
var data = e.data;
if(data.targetUser !== self && data.offerSDP) {
createAnswer(offerSDP);
}
};
function createAnswer(offerSDP) {
// it is suggested to use gumadapter.js instead:
//[https://github.com/muaz-khan/gumadapter](https://github.com/muaz-khan/gumadapter)navigator.webkitGetUserMedia(MediaConstraints, OnMediaSuccess, OnMediaError);
var MediaConstraints = {
audio: true,
video: true
};
function OnMediaError(error) {
console.error(error);
}
function OnMediaSuccess(mediaStream) {
var peer = new [webkit|moz]RTCPeerConnection(iceServers, optional);
peer.addStream(mediaStream);
peer.onaddstream = function(mediaStream) {
video.src = webkitURL.createObjectURL(mediaStream);
};
peer.onicecandidate = function(event) {
var candidate = event.candidate;
if(candidate) {
socket.send({
targetUser: 'target-user-id',
candidate: candidate
});
}
};
// remote-descriptions should be set earlier
// using offer-sdp provided by the offerer
var remoteDescription = new RTCSessionDescription(offerSDP);
peer.setRemoteDescription(remoteDescription, successCallback, failureCallback);
peer.createAnswer(function(answerSDP) {
peer.setLocalDescription(answerSDP, successCallback, failureCallback);
socket.send({
targetUser: 'target-user-id',
answerSDP: answerSDP
});
}, onfailure,[sdpConstraints](#sdpConstraints));
}
};
Answer is also completed. Now, second last step is to set remote descriptions for offerer; same as we did for answerer:
socket.onmessage = function(e) {
var data = e.data;
if(data.targetUser !== self && data.answerSDP) {
// completing the handshake; this code is for offerer
var remoteDescription = new RTCSessionDescription(answerSDP);
peer.setRemoteDescription(remoteDescription, successCallback, failureCallback);
}
};
Now, last step is to "add ICE candidate" for each peer. It works like this:
Again, ICE trickling is not "officially" included in WebRTC specification; so, it is chrome-only feature. Firefox merges all ice candidates in session descriptions. You can merge candidates in offerer/answer sdp on chrome too; see next section.
socket.onmessage = function(e) {
var data = e.data;
if(data.targetUser !== self && data.candidate) {
var candidate = data.candidate.candidate;
var sdpMLineIndex = data.candidate.sdpMLineIndex;
peer.addIceCandidate(new [moz]RTCIceCandidate({
sdpMLineIndex: sdpMLineIndex,
candidate : candidate
}), successCallback, failureCallback);
}
};
peer.onicecandidate = function(e) {
var candidate = e.candidate;
// typeof candidate == 'undefined'
// !candidate -or- !!candidate == false
if(typeof candidate == 'undefined') {
send_SDP();
}
};
peer.ongatheringchange = function(e) {
if (e.currentTarget &&
e.currentTarget.iceGatheringState === 'complete') {
send_SDP();
}
};
function send_SDP() {
socket.send({
targetUser: 'target-user-id',
sdp : peer.localDescription
});
}
var socket = io.connect('http://domain.com:80/');
socket.on('message', function(data) {
if(data.offerSDP) {}
if(data.answerSDP) {}
if(data.candidate) {}
});
You can use either "send" or "emit" method to send the data. However, it is recommended to override "send" method like this:
socket.send = function(data) {
socket.emit('message', data);
});
// Now you can send "any-kind" of data like this:
socket.send('string');
socket.send([array]);
socket.send({
targetUser: 'target-user-id',
sdp : 'offerSDP || answerSDP'
});
// for Chrome:
var sdpConstraints = {
optional: [],
mandatory: {
OfferToReceiveAudio: true,
OfferToReceiveVideo: true
}
};
// for Firefox:
var sdpConstraints = {
OfferToReceiveAudio: true,
OfferToReceiveVideo: true
};
// http://cdn.webrtc-experiment.com/socket.io/PeerConnection.js
var offerer = new **PeerConnection** (' **http://domain:port**', ' **message**', ' **offerer**');
offerer. **onStreamAdded** = function(e) {
document.body.appendChild(e.mediaElement);
};
var answerer = new **PeerConnection** (' **http://domain:port**', ' **message**', ' **answerer**');
answerer. **onStreamAdded** = function(e) {
document.body.appendChild(e.mediaElement);
};
answerer. **sendParticipationRequest** ( **'offerer'** );
Send MessageEnter your email too; if you want "direct" reply!