I have added a simple webRTC application where it will connect a browser window to itself, streaming video data from the my camera. The end goal is to get two video streams
Add failure callbacks to make it work. Not only won't you see errors otherwise, but doing so will actually make it work, for a really weird reason:
You're a victim of something called WebIDL overloading. What's happening is there are two versions of the WebRTC API, and you're mixing them.
There's a modern promise API, e.g.:
pc.createOffer(options).then(successCallback, failureCallback);
and a deprecated callback version, e.g.:
pc.createOffer(successCallback, failureCallback, options);
In other words, there are two createOffer
functions that take different number of arguments.
Unfortunately, you're hitting the first createOffer
because you're only passing one argument! The first createOffer
expects an options object which unfortunately in WebIDL is indistinguishable from a function. It is therefore treated as a valid argument (an empty options object). Even if this had caused a TypeError
, it wouldn't have caused an exception, because promise APIs reject the returned promise instead of throwing an exception:
pc.createOffer(3).catch(e => console.log("Here: "+ e.name)); // Here: TypeError
You're not checking the returned promise either, so errors are lost.
Here's a working version (https fiddle for Chrome):
navigator.getUserMedia = navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia;
window.RTCPeerConnection = window.RTCPeerConnection ||
window.webkitRTCPeerConnection;
var yourConnection, theirConnection;
navigator.getUserMedia({ video: true, audio: false }, function(stream) {
yourVideo.src = window.URL.createObjectURL(stream);
var config = { "iceServers": [{ "urls": "stun:stun.1.google.com:19302"}] };
yourConnection = new RTCPeerConnection(config);
theirConnection = new RTCPeerConnection(config);
yourConnection.addStream(stream);
theirConnection.onaddstream = function (event) {
theirVideo.src = window.URL.createObjectURL(event.stream);
};
yourConnection.onicecandidate = function (e) {
if (e.candidate) {
theirConnection.addIceCandidate(new RTCIceCandidate(e.candidate),
success, failure);
}
};
theirConnection.onicecandidate = function (e) {
if (e.candidate) {
yourConnection.addIceCandidate(new RTCIceCandidate(e.candidate),
success, failure);
}
};
yourConnection.createOffer(function (offer) {
yourConnection.setLocalDescription(offer, success, failure);
theirConnection.setRemoteDescription(offer, success, failure);
theirConnection.createAnswer(function (offer) {
theirConnection.setLocalDescription(offer, success, failure);
yourConnection.setRemoteDescription(offer, success, failure);
}, failure);
}, failure);
}, failure);
function success() {};
function failure(e) { console.log(e); };
<video id="yourVideo" width="160" height="120" autoplay></video>
<video id="theirVideo" width="160" height="120" autoplay></video>
But callbacks are laborious. I highly recommend the new promise API instead (https for Chrome):
var pc1 = new RTCPeerConnection(), pc2 = new RTCPeerConnection();
navigator.mediaDevices.getUserMedia({video: true, audio: true})
.then(stream => pc1.addStream(video1.srcObject = stream))
.catch(log);
var add = (pc, can) => pc.addIceCandidate(can).catch(log);
pc1.onicecandidate = e => add(pc2, e.candidate);
pc2.onicecandidate = e => add(pc1, e.candidate);
pc2.ontrack = e => video2.srcObject = e.streams[0];
pc1.oniceconnectionstatechange = e => log(pc1.iceConnectionState);
pc1.onnegotiationneeded = e =>
pc1.createOffer().then(d => pc1.setLocalDescription(d))
.then(() => pc2.setRemoteDescription(pc1.localDescription))
.then(() => pc2.createAnswer()).then(d => pc2.setLocalDescription(d))
.then(() => pc1.setRemoteDescription(pc2.localDescription))
.catch(log);
var log = msg => console.log(msg);
<video id="video1" height="120" width="160" autoplay muted></video>
<video id="video2" height="120" width="160" autoplay></video><br>
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>