Skip to content

Instantly share code, notes, and snippets.

@westonruter
Last active December 26, 2015 02:09
Show Gist options
  • Save westonruter/7075722 to your computer and use it in GitHub Desktop.
Save westonruter/7075722 to your computer and use it in GitHub Desktop.
Trying to get WebRTC DataChannel to work via STUN.
var RTCPeerConnection = null;
var getUserMedia = null;
var attachMediaStream = null;
var reattachMediaStream = null;
var webrtcDetectedBrowser = null;
var webrtcDetectedVersion = null;
function trace(text) {
// This function is used for logging.
if (text[text.length - 1] == '\n') {
text = text.substring(0, text.length - 1);
}
console.log((performance.now() / 1000).toFixed(3) + ": " + text);
}
if (navigator.mozGetUserMedia) {
console.log("This appears to be Firefox");
webrtcDetectedBrowser = "firefox";
webrtcDetectedVersion =
parseInt(navigator.userAgent.match(/Firefox\/([0-9]+)\./)[1]);
// The RTCPeerConnection object.
RTCPeerConnection = mozRTCPeerConnection;
// The RTCSessionDescription object.
RTCSessionDescription = mozRTCSessionDescription;
// The RTCIceCandidate object.
RTCIceCandidate = mozRTCIceCandidate;
// Get UserMedia (only difference is the prefix).
// Code from Adam Barth.
getUserMedia = navigator.mozGetUserMedia.bind(navigator);
// Creates iceServer from the url for FF.
createIceServer = function(url, username, password) {
var iceServer = null;
var url_parts = url.split(':');
if (url_parts[0].indexOf('stun') === 0) {
// Create iceServer with stun url.
iceServer = { 'url': url };
} else if (url_parts[0].indexOf('turn') === 0 &&
(url.indexOf('transport=udp') !== -1 ||
url.indexOf('?transport') === -1)) {
// Create iceServer with turn url.
// Ignore the transport parameter from TURN url.
var turn_url_parts = url.split("?");
iceServer = { 'url': turn_url_parts[0],
'credential': password,
'username': username };
}
return iceServer;
};
// Attach a media stream to an element.
attachMediaStream = function(element, stream) {
console.log("Attaching media stream");
element.mozSrcObject = stream;
element.play();
};
reattachMediaStream = function(to, from) {
console.log("Reattaching media stream");
to.mozSrcObject = from.mozSrcObject;
to.play();
};
// Fake get{Video,Audio}Tracks
MediaStream.prototype.getVideoTracks = function() {
return [];
};
MediaStream.prototype.getAudioTracks = function() {
return [];
};
} else if (navigator.webkitGetUserMedia) {
console.log("This appears to be Chrome");
webrtcDetectedBrowser = "chrome";
webrtcDetectedVersion =
parseInt(navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./)[2]);
// Creates iceServer from the url for Chrome.
createIceServer = function(url, username, password) {
var iceServer = null;
var url_parts = url.split(':');
if (url_parts[0].indexOf('stun') === 0) {
// Create iceServer with stun url.
iceServer = { 'url': url };
} else if (url_parts[0].indexOf('turn') === 0) {
if (webrtcDetectedVersion < 28) {
// For pre-M28 chrome versions use old TURN format.
var url_turn_parts = url.split("turn:");
iceServer = { 'url': 'turn:' + username + '@' + url_turn_parts[1],
'credential': password };
} else {
// For Chrome M28 & above use new TURN format.
iceServer = { 'url': url,
'credential': password,
'username': username };
}
}
return iceServer;
};
// The RTCPeerConnection object.
RTCPeerConnection = webkitRTCPeerConnection;
// Get UserMedia (only difference is the prefix).
// Code from Adam Barth.
getUserMedia = navigator.webkitGetUserMedia.bind(navigator);
// Attach a media stream to an element.
attachMediaStream = function(element, stream) {
if (typeof element.srcObject !== 'undefined') {
element.srcObject = stream;
} else if (typeof element.mozSrcObject !== 'undefined') {
element.mozSrcObject = stream;
} else if (typeof element.src !== 'undefined') {
element.src = URL.createObjectURL(stream);
} else {
console.log('Error attaching stream to element.');
}
};
reattachMediaStream = function(to, from) {
to.src = from.src;
};
// The representation of tracks in a stream is changed in M26.
// Unify them for earlier Chrome versions in the coexisting period.
if (!webkitMediaStream.prototype.getVideoTracks) {
webkitMediaStream.prototype.getVideoTracks = function() {
return this.videoTracks;
};
webkitMediaStream.prototype.getAudioTracks = function() {
return this.audioTracks;
};
}
// New syntax of getXXXStreams method in M26.
if (!webkitRTCPeerConnection.prototype.getLocalStreams) {
webkitRTCPeerConnection.prototype.getLocalStreams = function() {
return this.localStreams;
};
webkitRTCPeerConnection.prototype.getRemoteStreams = function() {
return this.remoteStreams;
};
}
} else {
console.log("Browser does not appear to be WebRTC-capable");
}
<!DOCTYPE html>
<html>
<head>
<title>Data Channel Demo 1</title>
<style>
button {
font: 18px sans-serif;
padding: 8px;
}
textarea {
font-family: monospace;
margin: 2px;
height: 400px;
width: 300px;
}
div#send {
float: left;
margin-right: 20px;
}
div#receive {
}
div#sendreceive {
margin: 0 0 20px 0;
}
h2 {
margin: 0 0 10px 0;
}
</style>
</head>
<body>
<div id="sendreceive">
<div id="send">
<h2>Send data</h2>
<textarea id="dataChannelSendId" rows="5" cols="15" disabled="true"
placeholder="Press Start, enter some text, then press Send Data.">
</textarea>
</div>
<div id="receive">
<h2>Received Data</h2>
<textarea id="dataChannelReceiveId" rows="5" cols="15" disabled="true">
</textarea>
</div>
</div>
<form>
<p>Choose SCTP or RTP for transmitting data.</p>
<input type="radio" id="useSctp" name="transportbtn" checked/>
<label for="useSctp">Use SCTP</label>
<input type="radio" id="useRtp" name="transportbtn"/>
<label for="useRtp">Use RTP</label>
</form>
<button id="startButton">Start</button>
<button id="sendButton" disabled>Send Data</button>
<button id="closeButton" disabled>Stop</button>
<!-- Load the polyfill to switch-hit between Chrome and Firefox -->
<script src='adapter.js'></script>
<script>
var pc1, pc2, sendChannel, receiveChannel, pcConstraint, dataConstraint;
var dataChannelSend = document.getElementById("dataChannelSendId");
var dataChannelReceive = document.getElementById("dataChannelReceiveId");
var sctp_select = document.getElementById('useSctp');
var rtp_select = document.getElementById('useRtp');
var startButton = document.querySelector('button#startButton');
var sendButton = document.querySelector('button#sendButton');
var closeButton = document.querySelector('button#closeButton');
startButton.onclick = createConnection;
sendButton.onclick = sendData;
closeButton.onclick = closeDataChannels;
rtp_select.onclick = enableStartButton;
sctp_select.onclick = enableStartButton;
function enableStartButton() {
startButton.disabled = false;
}
function createConnection() {
dataChannelSendId.placeholder = "";
var servers = null;
pcConstraint = {optional: [{RtpDataChannels: true}]};
dataConstraint = {reliable: false};
if (sctp_select.checked &&
webrtcDetectedBrowser === 'chrome' &&
webrtcDetectedVersion >= 31) {
// SCTP is supported from Chrome M31. In current canary builds,
// you might need to enable it through flag #enable-sctp-data-channels.
// Use SCTP with reliable set to true.
pcConstraint = {optional: [{DtlsSrtpKeyAgreement: true}]};
dataConstraint = {reliable: true};
trace('Using SCTP based Data Channels');
} else {
if (!rtp_select.checked) {
// Use rtp data channels for chrome versions older than M31.
trace('Using RTP based Data Channels,' +
'as you are on an older version than M31.');
alert('Reverting to RTP based data channels,' +
'as you are on an older version than M31.');
rtp_select.checked = true;
}
}
pc1 = new RTCPeerConnection(servers, pcConstraint);
trace('Created local peer connection object pc1');
try {
// Data Channel api supported from Chrome M25.
// You might need to start chrome with --enable-data-channels flag.
sendChannel = pc1.createDataChannel("sendDataChannel", dataConstraint);
trace('Created send data channel');
} catch (e) {
alert('Failed to create data channel. ' +
'You need Chrome M25 or later with --enable-data-channels flag');
trace('Create Data channel failed with exception: ' + e.message);
}
pc1.onicecandidate = iceCallback1;
sendChannel.onopen = onSendChannelStateChange;
sendChannel.onclose = onSendChannelStateChange;
pc2 = new RTCPeerConnection(servers, pcConstraint);
trace('Created remote peer connection object pc2');
pc2.onicecandidate = iceCallback2;
pc2.ondatachannel = receiveChannelCallback;
pc1.createOffer(gotDescription1);
startButton.disabled = true;
closeButton.disabled = false;
}
function sendData() {
var data = dataChannelSend.value;
sendChannel.send(data);
trace('Sent Data: ' + data);
}
function closeDataChannels() {
trace('Closing data Channels');
sendChannel.close();
trace('Closed data channel with label: ' + sendChannel.label);
receiveChannel.close();
trace('Closed data channel with label: ' + receiveChannel.label);
pc1.close();
pc2.close();
pc1 = null;
pc2 = null;
trace('Closed peer connections');
startButton.disabled = false;
sendButton.disabled = true;
closeButton.disabled = true;
dataChannelSend.value = "";
dataChannelReceive.value = "";
dataChannelSend.disabled = true;
}
function gotDescription1(desc) {
pc1.setLocalDescription(desc);
trace('Offer from pc1 \n' + desc.sdp);
pc2.setRemoteDescription(desc);
pc2.createAnswer(gotDescription2);
}
function gotDescription2(desc) {
pc2.setLocalDescription(desc);
trace('Answer from pc2 \n' + desc.sdp);
pc1.setRemoteDescription(desc);
}
function iceCallback1(event) {
trace('local ice callback');
if (event.candidate) {
pc2.addIceCandidate(event.candidate);
trace('Local ICE candidate: \n' + event.candidate.candidate);
}
}
function iceCallback2(event) {
trace('remote ice callback');
if (event.candidate) {
pc1.addIceCandidate(event.candidate);
trace('Remote ICE candidate: \n ' + event.candidate.candidate);
}
}
function receiveChannelCallback(event) {
trace('Receive Channel Callback');
receiveChannel = event.channel;
receiveChannel.onmessage = onReceiveMessageCallback;
receiveChannel.onopen = onReceiveChannelStateChange;
receiveChannel.onclose = onReceiveChannelStateChange;
}
function onReceiveMessageCallback(event) {
trace('Received Message');
dataChannelReceive.value = event.data;
}
function onSendChannelStateChange() {
var readyState = sendChannel.readyState;
trace('Send channel state is: ' + readyState);
if (readyState == "open") {
dataChannelSend.disabled = false;
dataChannelSendId.focus();
sendButton.disabled = false;
closeButton.disabled = false;
} else {
dataChannelSend.disabled = true;
sendButton.disabled = true;
closeButton.disabled = true;
}
}
function onReceiveChannelStateChange() {
var readyState = receiveChannel.readyState;
trace('Receive channel state is: ' + readyState);
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment