CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/data/webcam/api.js
Views: 1904
// Muaz Khan - https://github.com/muaz-khan1// MIT License - https://www.webrtc-experiment.com/licence/2// Documentation - https://github.com/muaz-khan/WebRTC-Experiment/tree/master/websocket34(function () {56window.PeerConnection = function (socketURL, userid) {7this.userid = userid || getToken();8this.peers = {};910if (!socketURL) throw 'Socket-URL is mandatory.';1112new Signaler(this, socketURL);1314this.addStream = function(stream) {15this.MediaStream = stream;16};17};1819function Signaler(root, socketURL) {20var self = this;2122root.startBroadcasting = function () {23if(!root.MediaStream) throw 'Offerer must have media stream.';2425(function transmit() {26socket.send({27userid: root.userid,28broadcasting: true29});30!self.participantFound &&31!self.stopBroadcasting &&32setTimeout(transmit, 3000);33})();34};3536root.sendParticipationRequest = function (userid) {37socket.send({38participationRequest: true,39userid: root.userid,40to: userid41});42};4344// if someone shared SDP45this.onsdp = function (message) {46var sdp = message.sdp;4748if (sdp.type == 'offer') {49root.peers[message.userid] = Answer.createAnswer(merge(options, {50MediaStream: root.MediaStream,51sdp: sdp52}));53}5455if (sdp.type == 'answer') {56root.peers[message.userid].setRemoteDescription(sdp);57}58};5960root.acceptRequest = function (userid) {61root.peers[userid] = Offer.createOffer(merge(options, {62MediaStream: root.MediaStream63}));64};6566var candidates = [];67// if someone shared ICE68this.onice = function (message) {69var peer = root.peers[message.userid];70if (peer) {71peer.addIceCandidate(message.candidate);72for (var i = 0; i < candidates.length; i++) {73peer.addIceCandidate(candidates[i]);74}75candidates = [];76} else candidates.push(candidates);77};7879// it is passed over Offer/Answer objects for reusability80var options = {81onsdp: function (sdp) {82socket.send({83userid: root.userid,84sdp: sdp,85to: root.participant86});87},88onicecandidate: function (candidate) {89socket.send({90userid: root.userid,91candidate: candidate,92to: root.participant93});94},95onStreamAdded: function (stream) {96console.debug('onStreamAdded', '>>>>>>', stream);9798stream.onended = function () {99if (root.onStreamEnded) root.onStreamEnded(streamObject);100};101102var mediaElement = document.createElement('video');103mediaElement.id = root.participant;104mediaElement[isFirefox ? 'mozSrcObject' : 'src'] = isFirefox ? stream : window.webkitURL.createObjectURL(stream);105mediaElement.autoplay = true;106mediaElement.controls = true;107mediaElement.play();108109var streamObject = {110mediaElement: mediaElement,111stream: stream,112userid: root.participant,113type: 'remote'114};115116function afterRemoteStreamStartedFlowing() {117if (!root.onStreamAdded) return;118root.onStreamAdded(streamObject);119}120121afterRemoteStreamStartedFlowing();122}123};124125function closePeerConnections() {126self.stopBroadcasting = true;127if (root.MediaStream) root.MediaStream.stop();128129for (var userid in root.peers) {130root.peers[userid].peer.close();131}132root.peers = {};133}134135root.close = function () {136socket.send({137userLeft: true,138userid: root.userid,139to: root.participant140});141closePeerConnections();142};143144window.onbeforeunload = function () {145root.close();146};147148window.onkeyup = function (e) {149if (e.keyCode == 116)150root.close();151};152153function onmessage(e) {154var message = JSON.parse(e.data);155156if (message.userid == root.userid) return;157root.participant = message.userid;158159// for pretty logging160console.debug(JSON.stringify(message, function (key, value) {161if (value && value.sdp) {162console.log(value.sdp.type, '---', value.sdp.sdp);163return '';164} else return value;165}, '---'));166167// if someone shared SDP168if (message.sdp && message.to == root.userid) {169self.onsdp(message);170}171172// if someone shared ICE173if (message.candidate && message.to == root.userid) {174self.onice(message);175}176177// if someone sent participation request178if (message.participationRequest && message.to == root.userid) {179self.participantFound = true;180181if (root.onParticipationRequest) {182root.onParticipationRequest(message.userid);183} else root.acceptRequest(message.userid);184}185186// if someone is broadcasting himself!187if (message.broadcasting && root.onUserFound) {188root.onUserFound(message.userid);189}190191if (message.userLeft && message.to == root.userid) {192closePeerConnections();193}194}195196var socket = socketURL;197if(typeof socketURL == 'string') {198socket = new WebSocket(socketURL);199socket.push = socket.send;200socket.send = function (data) {201socket.push(JSON.stringify(data));202};203204socket.onopen = function () {205console.log('websocket connection opened.');206};207}208socket.onmessage = onmessage;209}210211var RTCPeerConnection = window.mozRTCPeerConnection || window.webkitRTCPeerConnection;212var RTCSessionDescription = window.mozRTCSessionDescription || window.RTCSessionDescription;213var RTCIceCandidate = window.mozRTCIceCandidate || window.RTCIceCandidate;214215navigator.getUserMedia = navigator.mozGetUserMedia || navigator.webkitGetUserMedia;216window.URL = window.webkitURL || window.URL;217218var isFirefox = !!navigator.mozGetUserMedia;219var isChrome = !!navigator.webkitGetUserMedia;220221var STUN = {222url: isChrome ? 'stun:stun.l.google.com:19302' : 'stun:23.21.150.121'223};224225var TURN = {226url: 'turn:[email protected]:80',227credential: 'homeo'228};229230var iceServers = {231iceServers: [STUN]232};233234if (isChrome) {235if (parseInt(navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./)[2]) >= 28)236TURN = {237url: 'turn:turn.bistri.com:80',238credential: 'homeo',239username: 'homeo'240};241242iceServers.iceServers = [STUN, TURN];243}244245var optionalArgument = {246optional: [{247DtlsSrtpKeyAgreement: true248}]249};250251var offerAnswerConstraints = {252optional: [],253mandatory: {254OfferToReceiveAudio: true,255OfferToReceiveVideo: true256}257};258259function getToken() {260return Math.round(Math.random() * 9999999999) + 9999999999;261}262263function onSdpError() {}264265// var offer = Offer.createOffer(config);266// offer.setRemoteDescription(sdp);267// offer.addIceCandidate(candidate);268var Offer = {269createOffer: function (config) {270var peer = new RTCPeerConnection(iceServers, optionalArgument);271272if (config.MediaStream) peer.addStream(config.MediaStream);273peer.onaddstream = function (event) {274config.onStreamAdded(event.stream);275};276277peer.onicecandidate = function (event) {278if (event.candidate)279config.onicecandidate(event.candidate);280};281282peer.createOffer(function (sdp) {283peer.setLocalDescription(sdp);284config.onsdp(sdp);285}, onSdpError, offerAnswerConstraints);286287this.peer = peer;288289return this;290},291setRemoteDescription: function (sdp) {292this.peer.setRemoteDescription(new RTCSessionDescription(sdp));293},294addIceCandidate: function (candidate) {295this.peer.addIceCandidate(new RTCIceCandidate({296sdpMLineIndex: candidate.sdpMLineIndex,297candidate: candidate.candidate298}));299}300};301302// var answer = Answer.createAnswer(config);303// answer.setRemoteDescription(sdp);304// answer.addIceCandidate(candidate);305var Answer = {306createAnswer: function (config) {307var peer = new RTCPeerConnection(iceServers, optionalArgument);308309if (config.MediaStream) peer.addStream(config.MediaStream);310peer.onaddstream = function (event) {311config.onStreamAdded(event.stream);312};313314peer.onicecandidate = function (event) {315if (event.candidate)316config.onicecandidate(event.candidate);317};318319peer.setRemoteDescription(new RTCSessionDescription(config.sdp));320peer.createAnswer(function (sdp) {321peer.setLocalDescription(sdp);322config.onsdp(sdp);323}, onSdpError, offerAnswerConstraints);324325this.peer = peer;326327return this;328},329addIceCandidate: function (candidate) {330this.peer.addIceCandidate(new RTCIceCandidate({331sdpMLineIndex: candidate.sdpMLineIndex,332candidate: candidate.candidate333}));334}335};336337function merge(mergein, mergeto) {338for (var t in mergeto) {339mergein[t] = mergeto[t];340}341return mergein;342}343344window.URL = window.webkitURL || window.URL;345navigator.getMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia;346navigator.getUserMedia = function(hints, onsuccess, onfailure) {347if(!hints) hints = {audio:true,video:true};348if(!onsuccess) throw 'Second argument is mandatory. navigator.getUserMedia(hints,onsuccess,onfailure)';349350navigator.getMedia(hints, _onsuccess, _onfailure);351352function _onsuccess(stream) {353onsuccess(stream);354}355356function _onfailure(e) {357if(onfailure) onfailure(e);358else throw Error('getUserMedia failed: ' + JSON.stringify(e, null, '\t'));359}360};361362})();363364