Last active
December 26, 2015 02:09
-
-
Save westonruter/7075722 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Trying to get WebRTC DataChannel to work via STUN. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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