Skip to content

Instantly share code, notes, and snippets.

@mvenkatesh431
Forked from xeoncross/Webrtc.html
Last active August 31, 2015 11:00
Show Gist options
  • Select an option

  • Save mvenkatesh431/23eea38a8f356aaf9e02 to your computer and use it in GitHub Desktop.

Select an option

Save mvenkatesh431/23eea38a8f356aaf9e02 to your computer and use it in GitHub Desktop.

Revisions

  1. @xeoncross xeoncross revised this gist May 28, 2015. 1 changed file with 220 additions and 149 deletions.
    369 changes: 220 additions & 149 deletions Webrtc.html
    Original file line number Diff line number Diff line change
    @@ -1,7 +1,7 @@
    <h1>WebRTC Demo</h1>
    <h1>WebRTC</h1>


    <script>
    <script>
    // This is only needed for browser testing
    window.RTCPeerConnection = window.mozRTCPeerConnection || window.webkitRTCPeerConnection;
    window.RTCSessionDescription = window.mozRTCSessionDescription || window.RTCSessionDescription;
    @@ -21,29 +21,40 @@ <h1>WebRTC Demo</h1>
    var clients;
    var remoteVideoViews = [];
    var ourClientId = '' + randomInt(1,1000);
    console.log('ourClientId', ourClientId);
    console.log('ourClientId', ourClientId);

    // Actual RTCPeerConnections based on clients
    var peers = {};

    var mediaConstraints = {
    optional: [],
    mandatory: {
    OfferToReceiveAudio: true,
    OfferToReceiveVideo: true
    }
    optional: [],
    mandatory: {
    OfferToReceiveAudio: true,
    OfferToReceiveVideo: true
    }
    };

    // http://www.w3.org/TR/webrtc/#idl-def-RTCOfferOptions
    var RTC_Offer_Options = {
    offerToReceiveVideo:true,
    offerToReceiveAudio: true
    offerToReceiveVideo: true,
    offerToReceiveAudio: true
    };

    var peerconnectionOptions = {
    'optional': [
    {'DtlsSrtpKeyAgreement': true},
    {'RtpDataChannels': true }
    ]
    };

    window.iceServers = {
    iceServers: [
    {url: 'stun:stun.services.mozilla.com'},
    {url: 'stun:stun.l.google.com:19302'}
    ]
    // http://w3c.github.io/webrtc-pc/#idl-def-RTCConfiguration
    var RTC_Configuration = {
    // more options here
    // ...
    iceServers: [
    // {url: 'stun:stun.services.mozilla.com'},
    {url: 'stun:stun.l.google.com:19302'}
    ]
    };

    function randomInt(min, max) {
    @@ -59,238 +70,298 @@ <h1>WebRTC Demo</h1>
    var signaling = io.connect('http://localhost:8000/');

    signaling.on('clients', function(clients) {
    console.log('clients', clients);
    console.log('clients', clients);
    });

    signaling.on('call list', function(room, usernames) {
    clients = usernames;
    startCalling();
    clients = usernames;
    startCalling();
    });


    signaling.emit('username', ourClientId, function(error) {
    console.log('username', error);
    signaling.emit('join', 'testing', function(error) {
    console.log('join', error);
    });
    console.log('username', error);
    signaling.emit('join', 'testing', function(error) {
    console.log('join', error);
    });
    });

    function send(name, message) {
    console.log('send', name, message);
    signaling.emit('sendMessage', name, message);
    console.log('send', name, message);
    signaling.emit('sendMessage', name, message);
    }
    </script>


    <script>



    // And and remove videos as needed
    var remoteVideoViews = [];
    function addStream(stream, me) {
    var videoView = document.createElement('video');
    videoView.autoplay = true;
    videoView.setAttribute('muted', true);
    videoView.setAttribute('autoplay', true);
    videoView.setAttribute('controls', true);
    videoView.style.width = '300px';
    // videoView.id = me || +new Date();
    videoView.className = me ? 'me' : 'them';
    videoView.src = URL.createObjectURL(stream);
    videoView.load();

    remoteVideoViews.push(videoView);
    document.body.appendChild(videoView);
    return videoView;
    var videoView = document.createElement('video');
    videoView.autoplay = true;
    videoView.setAttribute('muted', true);
    videoView.setAttribute('autoplay', true);
    videoView.setAttribute('controls', true);
    videoView.style.width = '300px';
    // videoView.id = me || +new Date();
    videoView.className = me ? 'me' : 'them';
    videoView.src = URL.createObjectURL(stream);
    videoView.load();

    remoteVideoViews.push(videoView);
    document.body.appendChild(videoView);
    return videoView;
    }

    function removeStream(videoView) {
    document.body.removeChild(videoView);
    remoteVideoViews.splice(videoView, 1);
    document.body.removeChild(videoView);
    remoteVideoViews.splice(videoView, 1);
    }




    var startCalling = function() {

    console.log('startCalling', clients, !!localStream);
    console.log('startCalling', clients, !!localStream);

    // Wait to recive a client list and access to the local video view
    if(! clients || !localStream) {
    return;
    }

    signaling.on('messageReceived', function(name, message) {

    console.log('messageReceived from', name, message);

    if(message.type === 'offer') {
    var peer = peerConnectionTo(name);

    // Wait to recive a client list and access to the local video view
    if(! clients || !localStream) {
    return;
    }
    var state = peer.signalingState;

    signaling.on('messageReceived', function(name, message) {
    var offerSDP = new RTCSessionDescription(message.data);
    peer.setRemoteDescription(offerSDP);

    console.log('messageReceived from', name, message);
    // We have been called so we need to send the SDP and ICE Candidate
    if(state === 'stable') {

    if(message.type === 'offer') {
    var peer = peerConnectionTo(name);
    console.log('state', state);

    var state = peer.signalingState;
    peer.createAnswer(function (answerSDP) {

    var offerSDP = new RTCSessionDescription(message.data);
    peer.setRemoteDescription(offerSDP);
    // Upgrade bandwidth from 30k to 5000k
    // console.log('answerSDP', answerSDP.sdp);
    // var Bandwidth = 5000;
    // answerSDP.sdp = answerSDP.sdp.replace(/b=AS:([0-9]+)/g, 'b=AS:'+Bandwidth);
    // console.log('answerSDP', answerSDP.sdp);

    // We have been called so we need to send the SDP and ICE Candidate
    if(state === 'stable') {

    console.log('state', state);
    peer.setLocalDescription(answerSDP);

    peer.createAnswer(function (answerSDP) {
    peer.setLocalDescription(answerSDP);

    // use XHR/WebSocket/etc. to exchange answer-sdp with "offerer"
    signaling.emit('sendMessage', name, {
    type: 'offer',
    data: answerSDP
    });
    // use XHR/WebSocket/etc. to exchange answer-sdp with "offerer"
    signaling.emit('sendMessage', name, {
    type: 'offer',
    data: answerSDP
    });

    }, console.log, mediaConstraints);
    }, console.log, mediaConstraints);


    peer.onicecandidate = function (event) {
    if (!event || !event.candidate) return;
    // answerer.addIceCandidate(event.candidate);
    peer.onicecandidate = function (event) {
    if (!event || !event.candidate) return;
    // answerer.addIceCandidate(event.candidate);

    signaling.emit('sendMessage', client, {
    type: 'candidate',
    data: event.candidate
    });
    signaling.emit('sendMessage', client, {
    type: 'candidate',
    data: event.candidate
    });

    // We only need the first candidate...?
    peer.onicecandidate = null;
    };
    // We only need the first candidate...?
    peer.onicecandidate = null;
    };

    }
    }

    return;
    }
    return;
    }

    if(message.type === 'candidate') {
    var peer = peerConnectionTo(name);
    peer.addIceCandidate(new RTCIceCandidate(message.data));
    }
    });
    if(message.type === 'candidate') {
    var peer = peerConnectionTo(name);
    peer.addIceCandidate(new RTCIceCandidate(message.data));
    }
    });


    for (var i = 0; i < clients.length; i++) {
    var client = clients[i];
    for (var i = 0; i < clients.length; i++) {
    var client = clients[i];

    // Skip ourselves
    if(client === ourClientId) continue;
    // Skip ourselves
    if(client === ourClientId) continue;

    if(peers[client]) {
    console.log('already have a peer with', client);
    continue;
    }
    if(peers[client]) {
    console.log('already have a peer with', client);
    continue;
    }

    call(client);
    call(client);

    };
    };
    }


    // We are calling something
    function call(name) {

    if(peers[name]) {
    console.log('already have a peer with', name);
    return;
    }
    if(peers[name]) {
    console.log('already have a peer with', name);
    return;
    }

    console.log('call', name);
    console.log('call', name);

    var peer = peerConnectionTo(name);

    // let the "negotiationneeded" event trigger offer generation
    peer.onnegotiationneeded = function () {
    console.log('peer.negotiationneeded for ', name);
    var peer = peerConnectionTo(name);

    peer.createOffer(function (offer) {
    // let the "negotiationneeded" event trigger offer generation
    peer.onnegotiationneeded = function () {
    console.log('peer.negotiationneeded for ', name);

    console.log('createOffer', name, offer);
    peer.setLocalDescription(offer);
    peer.createOffer(function (offer) {

    signaling.emit('sendMessage', name, {
    type: 'offer',
    data: offer // peer.localDescription
    });
    console.log('createOffer', name, offer);
    peer.setLocalDescription(offer);

    }, console.log, RTC_Offer_Options);
    signaling.emit('sendMessage', name, {
    type: 'offer',
    data: offer // peer.localDescription
    });

    };
    }, console.log, RTC_Offer_Options);

    };

    peer.onicecandidate = function (event) {

    console.log('onicecandidate for', name, event.candidate);

    if (!event || !event.candidate) return;

    signaling.emit('sendMessage', name, {
    type: 'candidate',
    data: event.candidate
    });

    peer.onicecandidate = function (event) {
    console.log('onicecandidate for', name, event.candidate);
    // We only need the first candidate...?
    // peer.onicecandidate = null;
    };

    if (!event || !event.candidate) return;

    signaling.emit('sendMessage', name, {
    type: 'candidate',
    data: event.candidate
    });
    // Caller creates the datachannel
    var dc = peer.createDataChannel("chats");
    peer.dc = dc; // save for later
    console.log("created DataChannel", dc.id, name);

    dc.onmessage = function (event) {
    console.log("dc.received: " + event.data, name);
    };

    dc.onopen = function () {
    console.log("dc.open", name);

    var readyState = dc.readyState;
    console.log('dc.readyState', dc.readyState);

    if (readyState == "open") {
    dc.send('dc.send to', name, 'from', ourClientId);
    } else {
    console.log('channel not open yet, dc.send fail');
    }

    // setTimeout(function() {
    // dc.send('dc.send to', name, 'from', ourClientId);
    // }, 3000);

    };

    dc.onclose = function () {
    console.log("dc.close", name);
    };

    // We only need the first candidate...?
    // peer.onicecandidate = null;
    };

    }

    function peerConnectionTo(name) {

    if( ! peers[name]) {
    console.log('creating peerConnectionTo', name);
    if( ! peers[name]) {

    console.log('creating peerConnectionTo', name);

    peers[name] = new RTCPeerConnection(window.iceServers);
    // http://www.w3.org/TR/webrtc/#rtcpeerconnection-interface
    peers[name] = new RTCPeerConnection(RTC_Configuration, peerconnectionOptions);

    // Add our video stream
    peers[name].addStream(localStream);
    // Add our video stream
    peers[name].addStream(localStream);

    // When we get access to their stream show it
    peers[name].onaddstream = function (event) {
    addStream(event.stream);
    };
    // When we get access to their stream show it
    peers[name].onaddstream = function (event) {
    console.log('adding stream', name);
    addStream(event.stream);
    };

    // When we get access to their stream show it
    peers[name].onremovestream = function (event) {
    console.log('removing stream', name);
    removeStream(event.stream);
    };

    // Both caller and callee get this but only callee uses it
    // since the caller creates the channel in call
    peers[name].ondatachannel = function (evt) {
    console.log('ondatachannel', evt);
    peers[name].dc = evt.channel;

    }
    peers[name].dc.send('First Message');
    };

    }

    return peers[name];
    return peers[name];
    }


    function getUserMedia(callback) {

    navigator.getUserMedia({
    audio: true,
    video: true
    }, callback, function(e) {
    console.log(e);
    });
    navigator.getUserMedia({
    audio: true,
    video: true
    }, callback, function(e) {
    console.log(e);
    });
    }

    getUserMedia(function (stream) {
    localStreamView = addStream(stream);
    localStream = stream;
    startCalling();
    localStreamView = addStream(stream);
    localStream = stream;
    startCalling();
    });


    function waitUntilRemoteStreamStartsFlowing(remote_video, callback)
    {
    if (!(remote_video.readyState <= HTMLMediaElement.HAVE_CURRENT_DATA
    || remote_video.paused || remote_video.currentTime <= 0))
    if (!(remote_video.readyState <= HTMLMediaElement.HAVE_CURRENT_DATA
    || remote_video.paused || remote_video.currentTime <= 0))
    {
    // remote stream started flowing!
    callback();
    return;
    }
    }

    setTimeout(function() {
    waitUntilRemoteStreamStartsFlowing(remote_video, callback)
    waitUntilRemoteStreamStartsFlowing(remote_video, callback)
    }, 50);
    }


    </script>
    </script>
  2. @xeoncross xeoncross revised this gist May 27, 2015. 1 changed file with 0 additions and 7000 deletions.
    7,000 changes: 0 additions & 7,000 deletions socket-io.js
    0 additions, 7,000 deletions not shown because the diff is too large. Please use a local Git client to view these changes.
  3. @xeoncross xeoncross revised this gist May 27, 2015. 1 changed file with 227 additions and 108 deletions.
    335 changes: 227 additions & 108 deletions Webrtc.html
    Original file line number Diff line number Diff line change
    @@ -1,9 +1,5 @@
    <h1>WebRTC Demo</h1>

    <!--
    http://stackoverflow.com/a/18876640/99923
    https://github.com/shanet/WebRTC-Example/blob/master/client%2Fwebrtc.js
    -->

    <script>
    // This is only needed for browser testing
    @@ -17,161 +13,284 @@ <h1>WebRTC Demo</h1>



    <script>

    // Globals that will be object properties once we wrap this up as a module
    var localStream;
    var localStreamView;
    var clients;
    var remoteVideoViews = [];
    var ourClientId = '' + randomInt(1,1000);
    console.log('ourClientId', ourClientId);

    // Actual RTCPeerConnections based on clients
    var peers = {};

    var mediaConstraints = {
    optional: [],
    mandatory: {
    OfferToReceiveAudio: true,
    OfferToReceiveVideo: true
    }
    };

    var RTC_Offer_Options = {
    offerToReceiveVideo:true,
    offerToReceiveAudio: true
    };

    window.iceServers = {
    iceServers: [
    {url: 'stun:stun.services.mozilla.com'},
    {url: 'stun:stun.l.google.com:19302'}
    ]
    };

    function randomInt(min, max) {
    return Math.floor(Math.random() * (max - min + 1)) + min;
    }
    </script>



    <!-- Sample signaling server using socket.io -->
    <script src="socket.io.js"></script>
    <script>
    var signaling = io.connect('http://localhost:8000/');

    signaling.on('messageRecived', function(message) {
    console.log(message);
    });

    var clients = [];
    signaling.on('clients', function(clients) {
    clients = clients;
    console.log('clients', clients);
    console.log('clients', clients);
    });

    signaling.emit('messageRecived', function(message) {
    console.log(message);
    signaling.on('call list', function(room, usernames) {
    clients = usernames;
    startCalling();
    });

    signaling.emit('username', 'foo'+ new Date(), function(error) {

    console.log('username', error);

    signaling.emit('join', 'testing', function(error) {
    console.log('join', error);
    });
    signaling.emit('username', ourClientId, function(error) {
    console.log('username', error);
    signaling.emit('join', 'testing', function(error) {
    console.log('join', error);
    });
    });

    function send(name, message) {
    console.log('send', name, message);
    signaling.emit('sendMessage', name, message);
    console.log('send', name, message);
    signaling.emit('sendMessage', name, message);
    }
    </script>


    <script>

    var mediaConstraints = {
    optional: [],
    mandatory: {
    OfferToReceiveAudio: true,
    OfferToReceiveVideo: true
    }
    };

    var offerer, answerer;

    window.iceServers = {
    iceServers: [
    {url: 'stun:stun.services.mozilla.com'},
    {url: 'stun:stun.l.google.com:19302'}
    ]
    };



    // And and remove videos as needed
    var remoteVideoViews = [];
    function addStream(stream, me) {
    var videoView = document.createElement('video');
    videoView.autoplay = true;
    videoView.style.width = '300px';
    // videoView.id = me || +new Date();
    videoView.className = me ? 'me' : 'them';
    videoView.src = URL.createObjectURL(stream);
    videoView.load();

    remoteVideoViews.push(videoView);
    document.body.appendChild(videoView);
    return videoView;
    var videoView = document.createElement('video');
    videoView.autoplay = true;
    videoView.setAttribute('muted', true);
    videoView.setAttribute('autoplay', true);
    videoView.setAttribute('controls', true);
    videoView.style.width = '300px';
    // videoView.id = me || +new Date();
    videoView.className = me ? 'me' : 'them';
    videoView.src = URL.createObjectURL(stream);
    videoView.load();

    remoteVideoViews.push(videoView);
    document.body.appendChild(videoView);
    return videoView;
    }

    function removeStream(videoView) {
    document.body.removeChild(videoView);
    remoteVideoViews.splice(videoView, 1);
    document.body.removeChild(videoView);
    remoteVideoViews.splice(videoView, 1);
    }




    var startCalling = function() {

    console.log('startCalling', clients, !!localStream);

    /* offerer */
    function offererPeer(stream) {
    console.log('offererPeer');
    // Wait to recive a client list and access to the local video view
    if(! clients || !localStream) {
    return;
    }

    offerer = new RTCPeerConnection(window.iceServers);
    offerer.addStream(stream);
    // addStream(stream, true);
    signaling.on('messageReceived', function(name, message) {

    offerer.onaddstream = function (event) {
    addStream(event.stream);
    };
    console.log('messageReceived from', name, message);

    offerer.onicecandidate = function (event) {
    if (!event || !event.candidate) return;
    answerer.addIceCandidate(event.candidate);
    };
    if(message.type === 'offer') {
    var peer = peerConnectionTo(name);

    offerer.createOffer(function (offer) {
    offerer.setLocalDescription(offer);
    answererPeer(offer, stream);
    }, onSdpError, mediaConstraints);
    }
    var state = peer.signalingState;

    var offerSDP = new RTCSessionDescription(message.data);
    peer.setRemoteDescription(offerSDP);

    /* answerer */
    function answererPeer(offer, stream) {

    console.log('answererPeer');
    // We have been called so we need to send the SDP and ICE Candidate
    if(state === 'stable') {

    console.log('state', state);

    peer.createAnswer(function (answerSDP) {
    peer.setLocalDescription(answerSDP);

    // use XHR/WebSocket/etc. to exchange answer-sdp with "offerer"
    signaling.emit('sendMessage', name, {
    type: 'offer',
    data: answerSDP
    });

    }, console.log, mediaConstraints);


    peer.onicecandidate = function (event) {
    if (!event || !event.candidate) return;
    // answerer.addIceCandidate(event.candidate);

    signaling.emit('sendMessage', client, {
    type: 'candidate',
    data: event.candidate
    });

    // We only need the first candidate...?
    peer.onicecandidate = null;
    };

    answerer = new RTCPeerConnection(window.iceServers);
    answerer.addStream(stream);
    // addStream(stream);
    }

    answerer.onaddstream = function (event) {
    addStream(event.stream);
    };
    return;
    }

    answerer.onicecandidate = function (event) {
    if (!event || !event.candidate) return;
    offerer.addIceCandidate(event.candidate);
    };
    if(message.type === 'candidate') {
    var peer = peerConnectionTo(name);
    peer.addIceCandidate(new RTCIceCandidate(message.data));
    }
    });

    answerer.setRemoteDescription(offer, onSdpSucces, onSdpError);
    answerer.createAnswer(function (answer) {
    answerer.setLocalDescription(answer);
    offerer.setRemoteDescription(answer, onSdpSucces, onSdpError);
    }, onSdpError, mediaConstraints);
    }

    for (var i = 0; i < clients.length; i++) {
    var client = clients[i];

    // Skip ourselves
    if(client === ourClientId) continue;

    function getUserMedia(callback) {
    navigator.getUserMedia({
    audio: true,
    video: true
    }, callback, onerror);
    if(peers[client]) {
    console.log('already have a peer with', client);
    continue;
    }

    function onerror(e) {
    console.error(e);
    }
    }
    call(client);

    getUserMedia(function (stream) {
    offererPeer(stream);
    });
    };
    }


    function call(name) {

    if(peers[name]) {
    console.log('already have a peer with', name);
    return;
    }

    console.log('call', name);

    var peer = peerConnectionTo(name);

    // let the "negotiationneeded" event trigger offer generation
    peer.onnegotiationneeded = function () {
    console.log('peer.negotiationneeded for ', name);

    peer.createOffer(function (offer) {

    console.log('createOffer', name, offer);
    peer.setLocalDescription(offer);

    signaling.emit('sendMessage', name, {
    type: 'offer',
    data: offer // peer.localDescription
    });

    }, console.log, RTC_Offer_Options);

    };

    peer.onicecandidate = function (event) {

    console.log('onicecandidate for', name, event.candidate);

    if (!event || !event.candidate) return;

    signaling.emit('sendMessage', name, {
    type: 'candidate',
    data: event.candidate
    });

    // We only need the first candidate...?
    // peer.onicecandidate = null;
    };

    }

    function peerConnectionTo(name) {

    if( ! peers[name]) {

    console.log('creating peerConnectionTo', name);

    peers[name] = new RTCPeerConnection(window.iceServers);

    // Add our video stream
    peers[name].addStream(localStream);

    // When we get access to their stream show it
    peers[name].onaddstream = function (event) {
    addStream(event.stream);
    };

    }

    return peers[name];
    }


    function getUserMedia(callback) {

    navigator.getUserMedia({
    audio: true,
    video: true
    }, callback, function(e) {
    console.log(e);
    });
    }

    getUserMedia(function (stream) {
    localStreamView = addStream(stream);
    localStream = stream;
    startCalling();
    });


    function waitUntilRemoteStreamStartsFlowing(remote_video, callback)
    {
    if (!(remote_video.readyState <= HTMLMediaElement.HAVE_CURRENT_DATA
    || remote_video.paused || remote_video.currentTime <= 0))
    {
    // remote stream started flowing!
    callback();
    return;
    }

    setTimeout(function() {
    waitUntilRemoteStreamStartsFlowing(remote_video, callback)
    }, 50);
    }

    function onSdpError(e) {
    console.error('onSdpError', e);
    }

    function onSdpSucces() {
    console.log('onSdpSucces');
    }
    </script>
  4. @xeoncross xeoncross renamed this gist May 26, 2015. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  5. @xeoncross xeoncross created this gist May 26, 2015.
    7,000 changes: 7,000 additions & 0 deletions socket-io.js
    7,000 additions, 0 deletions not shown because the diff is too large. Please use a local Git client to view these changes.
    177 changes: 177 additions & 0 deletions webrtc.html
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,177 @@
    <h1>WebRTC Demo</h1>

    <!--
    http://stackoverflow.com/a/18876640/99923
    https://github.com/shanet/WebRTC-Example/blob/master/client%2Fwebrtc.js
    -->

    <script>
    // This is only needed for browser testing
    window.RTCPeerConnection = window.mozRTCPeerConnection || window.webkitRTCPeerConnection;
    window.RTCSessionDescription = window.mozRTCSessionDescription || window.RTCSessionDescription;
    window.RTCIceCandidate = window.mozRTCIceCandidate || window.RTCIceCandidate;

    navigator.getUserMedia = navigator.mozGetUserMedia || navigator.webkitGetUserMedia;
    // window.URL = window.webkitURL || window.URL;
    </script>





    <!-- Sample signaling server using socket.io -->
    <script src="socket.io.js"></script>
    <script>
    var signaling = io.connect('http://localhost:8000/');

    signaling.on('messageRecived', function(message) {
    console.log(message);
    });

    var clients = [];
    signaling.on('clients', function(clients) {
    clients = clients;
    console.log('clients', clients);
    });

    signaling.emit('messageRecived', function(message) {
    console.log(message);
    });

    signaling.emit('username', 'foo'+ new Date(), function(error) {

    console.log('username', error);

    signaling.emit('join', 'testing', function(error) {
    console.log('join', error);
    });
    });

    function send(name, message) {
    console.log('send', name, message);
    signaling.emit('sendMessage', name, message);
    }
    </script>


    <script>

    var mediaConstraints = {
    optional: [],
    mandatory: {
    OfferToReceiveAudio: true,
    OfferToReceiveVideo: true
    }
    };

    var offerer, answerer;

    window.iceServers = {
    iceServers: [
    {url: 'stun:stun.services.mozilla.com'},
    {url: 'stun:stun.l.google.com:19302'}
    ]
    };



    // And and remove videos as needed
    var remoteVideoViews = [];
    function addStream(stream, me) {
    var videoView = document.createElement('video');
    videoView.autoplay = true;
    videoView.style.width = '300px';
    // videoView.id = me || +new Date();
    videoView.className = me ? 'me' : 'them';
    videoView.src = URL.createObjectURL(stream);
    videoView.load();

    remoteVideoViews.push(videoView);
    document.body.appendChild(videoView);
    return videoView;
    }

    function removeStream(videoView) {
    document.body.removeChild(videoView);
    remoteVideoViews.splice(videoView, 1);
    }






    /* offerer */
    function offererPeer(stream) {
    console.log('offererPeer');

    offerer = new RTCPeerConnection(window.iceServers);
    offerer.addStream(stream);
    // addStream(stream, true);

    offerer.onaddstream = function (event) {
    addStream(event.stream);
    };

    offerer.onicecandidate = function (event) {
    if (!event || !event.candidate) return;
    answerer.addIceCandidate(event.candidate);
    };

    offerer.createOffer(function (offer) {
    offerer.setLocalDescription(offer);
    answererPeer(offer, stream);
    }, onSdpError, mediaConstraints);
    }


    /* answerer */
    function answererPeer(offer, stream) {

    console.log('answererPeer');

    answerer = new RTCPeerConnection(window.iceServers);
    answerer.addStream(stream);
    // addStream(stream);

    answerer.onaddstream = function (event) {
    addStream(event.stream);
    };

    answerer.onicecandidate = function (event) {
    if (!event || !event.candidate) return;
    offerer.addIceCandidate(event.candidate);
    };

    answerer.setRemoteDescription(offer, onSdpSucces, onSdpError);
    answerer.createAnswer(function (answer) {
    answerer.setLocalDescription(answer);
    offerer.setRemoteDescription(answer, onSdpSucces, onSdpError);
    }, onSdpError, mediaConstraints);
    }



    function getUserMedia(callback) {
    navigator.getUserMedia({
    audio: true,
    video: true
    }, callback, onerror);

    function onerror(e) {
    console.error(e);
    }
    }

    getUserMedia(function (stream) {
    offererPeer(stream);
    });

    function onSdpError(e) {
    console.error('onSdpError', e);
    }

    function onSdpSucces() {
    console.log('onSdpSucces');
    }
    </script>