2. Dan Jenkins
@dan_jenkins
Google Developer Expert (Web Technologies specialising in WebRTC)
Founder Nimble Ape
Web Developer - not a typical telecommunications developer
General Geek
Lego Geek
3. Nimble Ape
@nimbleapeltd
WebRTC Development & Consulting
IoT | Microservices | Docker
Web APIs & Scalable Web Services
Asterisk Applications
Node.js Development & Consulting
6. Draft or Spec?
It's currently a Working Draft and not a Specification.
Expect a Specification soon.
Don't let this stop you building things with it.
Both the IETF & W3C involved.
https://www.w3.org/TR/webrtc/
10. Data Channel
{
"code": 200,
"status": "Ok",
"copyright": "Ž 2016 MARVEL",
"attributionText": "Data provided by Marvel. Ž 2016 MARVEL",
"attributionHTML": "<a href="http://marvel.com">Data provided by Marvel. Ž 2016 MARVEL</a>",
"etag": "051a4391a8d0f1ca2c9a8da7bf3345e2e8a47f0f",
"data": {
"offset": 0,
"limit": 20,
"total": 1,
"count": 1,
"results": [
{
"id": 1009220,
"name": "Captain America",
"description": "Vowing to serve his country any way he could, young Steve Rogers took the super soldier serum to become America's
one-man army. Fighting for the red, white and blue for over 60 years, Captain America is the living, breathing symbol of freedom and
liberty.",
"modified": "2014-06-10T16:13:04-0400",
"thumbnail": {
"path": "http://i.annihil.us/u/prod/marvel/i/mg/3/50/537ba56d31087",
"extension": "jpg"
},
"resourceURI": "http://gateway.marvel.com/v1/public/characters/1009220"
}
]
}
}
Strings
15. How your WebRTC endpoints communicate vital bits
of information to one another
WebRTC is a NEW Web API so you'd think you'd have a
nice JSON blob explaining all the important things so
a connection can be made.
SDP
(Session Description Protocol)
30. An open source adapter to level out all of the browsers to the "standard"
Use it somewhere within your codebase
https://github.com/webrtc/adapter
adapter.js
31. You'll hear about ORTC
But don't worry about it.
There's an adapter! (https://github.com/twilio/ortc-adapter)
But Microsoft have committed to introducing WebRTC "1.0" into Edge...
so expect that soon!
ORTC
(Object Real Time Communications)
32. getUserMedia() now requires a trusted origin
Localhost or HTTPS
(But not self-signed TLS certs - letsencrypt.org)
Trusted Origins
41. Twiliovar accessManager = new Twilio.AccessManager(token);
conversationsClient = new Twilio.Conversations.Client(accessManager);
conversationsClient.listen().then(clientConnected, function (error) {
log('Could not connect to Twilio: ' + error.message);
});
function clientConnected() {
conversationsClient.on('invite', function (invite) {
invite.accept().then(conversationStarted);
});
}
function conversationStarted(conversation) {
activeConversation = conversation;
if (!previewMedia) {
conversation.localMedia.attach('#local-media');
}
conversation.on('participantConnected', function (participant) {
participant.media.attach('#remote-media');
});
conversation.on('disconnected', function (conversation) {
conversation.localMedia.stop();
conversation.disconnect();
activeConversation = null;
});
}
https://www.twilio.com/docs/api/video/guide/quickstart-js
42. Tokbox
<!DOCTYPE HTML>
<html>
<body>
<script src="https://static.opentok.com/v2/js/opentok.js" charset="utf-8"></script>
<script charset="utf-8">
var apiKey = 'YOUR-API-KEY';
var sessionId = 'YOUR-SESSION-ID';
var token = 'YOUR-TOKEN';
var session = OT.initSession(apiKey, sessionId)
.on('streamCreated', function(event) {
session.subscribe(event.stream);
})
.connect(token, function(error) {
var publisher = OT.initPublisher();
session.publish(publisher);
});
</script>
</body>
</html>
https://tokbox.com/developer/quickstart/
43. Respoke
// App ID from the Respoke Dashboard for your App
var appId = "c10a2075-3f3d-466f-82f9-d2285e64c5d4";
// The unique username identifying the user
var endpointId = "spock@enterprise.com";
// Create an instance of the Respoke client using your App ID
var client = respoke.createClient({
appId: appId,
developmentMode: true
});
// "connect" event fired after successful connection to Respoke
client.listen("connect", function(e) {
console.log("Connected to Respoke!", e);
});
// Execute some signin event, then connect to Respoke with
client.connect({
endpointId: endpointId
});
https://docs.respoke.io/client/javascript/getting-started.html
44. Xura (Was Forge, Acision, Crocodile? I lose track...)
apidaze
plivo
kandy.io
sinch
Cisco
Frozen Mountain
Voxbone
And many more...
45. Vanila JS
'use strict';
var startButton = document.getElementById('startButton');
var callButton = document.getElementById('callButton');
var hangupButton = document.getElementById('hangupButton');
callButton.disabled = true;
hangupButton.disabled = true;
startButton.onclick = start;
callButton.onclick = call;
hangupButton.onclick = hangup;
var startTime;
var localVideo = document.getElementById('localVideo');
var remoteVideo = document.getElementById('remoteVideo');
localVideo.addEventListener('loadedmetadata', function() {
trace('Local video videoWidth: ' + this.videoWidth +
'px, videoHeight: ' + this.videoHeight + 'px');
});
remoteVideo.addEventListener('loadedmetadata', function() {
trace('Remote video videoWidth: ' + this.videoWidth +
'px, videoHeight: ' + this.videoHeight + 'px');
});
remoteVideo.onresize = function() {
trace('Remote video size changed to ' +
remoteVideo.videoWidth + 'x' + remoteVideo.videoHeight);
// We'll use the first onsize callback as an indication that video has started
// playing out.
if (startTime) {
var elapsedTime = window.performance.now() - startTime;
trace('Setup time: ' + elapsedTime.toFixed(3) + 'ms');
startTime = null;
}
};
var localStream;
var pc1;
var pc2;
var offerOptions = {
offerToReceiveAudio: 1,
offerToReceiveVideo: 1
};
function getName(pc) {
return (pc === pc1) ? 'pc1' : 'pc2';
}
function getOtherPc(pc) {
return (pc === pc1) ? pc2 : pc1;
}
function gotStream(stream) {
trace('Received local stream');
localVideo.srcObject = stream;
localStream = stream;
callButton.disabled = false;
}
function start() {
trace('Requesting local stream');
startButton.disabled = true;
navigator.mediaDevices.getUserMedia({
audio: true,
video: true
})
.then(gotStream)
.catch(function(e) {
alert('getUserMedia() error: ' + e.name);
});
}
function call() {
callButton.disabled = true;
hangupButton.disabled = false;
trace('Starting call');
startTime = window.performance.now();
var videoTracks = localStream.getVideoTracks();
var audioTracks = localStream.getAudioTracks();
if (videoTracks.length > 0) {
trace('Using video device: ' + videoTracks[0].label);
}
if (audioTracks.length > 0) {
trace('Using audio device: ' + audioTracks[0].label);
}
var servers = null;
pc1 = new RTCPeerConnection(servers);
trace('Created local peer connection object pc1');
pc1.onicecandidate = function(e) {
onIceCandidate(pc1, e);
};
pc2 = new RTCPeerConnection(servers);
trace('Created remote peer connection object pc2');
pc2.onicecandidate = function(e) {
onIceCandidate(pc2, e);
};
pc1.oniceconnectionstatechange = function(e) {
onIceStateChange(pc1, e);
};
pc2.oniceconnectionstatechange = function(e) {
onIceStateChange(pc2, e);
};
pc2.onaddstream = gotRemoteStream;
pc1.addStream(localStream);
trace('Added local stream to pc1');
trace('pc1 createOffer start');
pc1.createOffer(onCreateOfferSuccess, onCreateSessionDescriptionError,
offerOptions);
}
209 lines of code
function onCreateSessionDescriptionError(error) {
trace('Failed to create session description: ' + error.toString());
}
function onCreateOfferSuccess(desc) {
trace('Offer from pc1n' + desc.sdp);
trace('pc1 setLocalDescription start');
pc1.setLocalDescription(desc, function() {
onSetLocalSuccess(pc1);
}, onSetSessionDescriptionError);
trace('pc2 setRemoteDescription start');
pc2.setRemoteDescription(desc, function() {
onSetRemoteSuccess(pc2);
}, onSetSessionDescriptionError);
trace('pc2 createAnswer start');
// Since the 'remote' side has no media stream we need
// to pass in the right constraints in order for it to
// accept the incoming offer of audio and video.
pc2.createAnswer(onCreateAnswerSuccess, onCreateSessionDescriptionError);
}
function onSetLocalSuccess(pc) {
trace(getName(pc) + ' setLocalDescription complete');
}
function onSetRemoteSuccess(pc) {
trace(getName(pc) + ' setRemoteDescription complete');
}
function onSetSessionDescriptionError(error) {
trace('Failed to set session description: ' + error.toString());
}
function gotRemoteStream(e) {
remoteVideo.srcObject = e.stream;
trace('pc2 received remote stream');
}
function onCreateAnswerSuccess(desc) {
trace('Answer from pc2:n' + desc.sdp);
trace('pc2 setLocalDescription start');
pc2.setLocalDescription(desc, function() {
onSetLocalSuccess(pc2);
}, onSetSessionDescriptionError);
trace('pc1 setRemoteDescription start');
pc1.setRemoteDescription(desc, function() {
onSetRemoteSuccess(pc1);
}, onSetSessionDescriptionError);
}
function onIceCandidate(pc, event) {
if (event.candidate) {
getOtherPc(pc).addIceCandidate(new RTCIceCandidate(event.candidate),
function() {
onAddIceCandidateSuccess(pc);
},
function(err) {
onAddIceCandidateError(pc, err);
}
);
trace(getName(pc) + ' ICE candidate: n' + event.candidate.candidate);
}
}
function onAddIceCandidateSuccess(pc) {
trace(getName(pc) + ' addIceCandidate success');
}
function onAddIceCandidateError(pc, error) {
trace(getName(pc) + ' failed to add ICE Candidate: ' + error.toString());
}
function onIceStateChange(pc, event) {
if (pc) {
trace(getName(pc) + ' ICE state: ' + pc.iceConnectionState);
console.log('ICE state change event: ', event);
}
}
function hangup() {
trace('Ending call');
pc1.close();
pc2.close();
pc1 = null;
pc2 = null;
hangupButton.disabled = true;
callButton.disabled = false;
}
https://github.com/webrtc/samples/blob/gh-pages/src/content/peerconnection/pc1/js/main.js
46. Vanila JS
OK, slight exaggeration
You can probably do it in around 70 lines of really
bad JavaScript...