From b74acc9b27a22db0f24ab546901ab355e94fd886 Mon Sep 17 00:00:00 2001 From: Contra Date: Wed, 20 Feb 2013 16:39:43 -0700 Subject: [PATCH] fix component dirs --- Makefile | 2 +- dist/Call.js | 185 +++++++++++++++++++++++++++++++++++++++ dist/RTC.js | 222 +++++++++++++++++++++++++++++++++++++++++++++++ dist/holla.js | 233 ++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 641 insertions(+), 1 deletion(-) create mode 100644 dist/Call.js create mode 100644 dist/RTC.js create mode 100644 dist/holla.js diff --git a/Makefile b/Makefile index d3dcdaa..ecb597f 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ build: components lib @coffee -o dist -c lib/holla.coffee lib/Call.coffee lib/RTC.coffee @component build --standalone holla @mv build/build.js holla.js - @rm -rf build dist + @rm -rf build @node_modules/.bin/uglifyjs -nc --unsafe -mt -o holla.min.js holla.js @echo "File size (minified): " && cat holla.min.js | wc -c @echo "File size (gzipped): " && cat holla.min.js | gzip -9f | wc -c diff --git a/dist/Call.js b/dist/Call.js new file mode 100644 index 0000000..1fdcb8b --- /dev/null +++ b/dist/Call.js @@ -0,0 +1,185 @@ +// Generated by CoffeeScript 1.4.0 +(function() { + var Call, EventEmitter, RTC, + __hasProp = {}.hasOwnProperty, + __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; + + RTC = require('./RTC'); + + EventEmitter = require('emitter'); + + Call = (function(_super) { + + __extends(Call, _super); + + function Call(parent, user, isCaller) { + var _this = this; + this.parent = parent; + this.user = user; + this.isCaller = isCaller; + this.startTime = new Date; + this.socket = this.parent.ssocket; + this.pc = this.createConnection(); + if (this.isCaller) { + this.socket.write({ + type: "offer", + to: this.user + }); + } + this.emit("calling"); + this.parent.on("answer." + this.user, function(accepted) { + if (!accepted) { + return _this.emit("rejected"); + } + _this.emit("answered"); + return _this.initSDP(); + }); + this.parent.on("candidate." + this.user, function(candidate) { + return _this.pc.addIceCandidate(new RTC.IceCandidate(candidate)); + }); + this.parent.on("sdp." + this.user, function(desc) { + desc.sdp = RTC.processSDPIn(desc.sdp); + _this.pc.setRemoteDescription(new RTC.SessionDescription(desc)); + return _this.emit("sdp"); + }); + this.parent.on("hangup." + this.user, function() { + return _this.emit("hangup"); + }); + this.parent.on("chat." + this.user, function(msg) { + return _this.emit("chat", msg); + }); + } + + Call.prototype.createConnection = function() { + var pc, + _this = this; + pc = new RTC.PeerConnection(RTC.PeerConnConfig, RTC.constraints); + pc.onconnecting = function() { + _this.emit('connecting'); + }; + pc.onopen = function() { + _this.emit('connected'); + }; + pc.onicecandidate = function(evt) { + if (evt.candidate) { + _this.socket.write({ + type: "candidate", + to: _this.user, + args: { + candidate: evt.candidate + } + }); + } + }; + pc.onaddstream = function(evt) { + _this.remoteStream = evt.stream; + _this._ready = true; + _this.emit("ready", _this.remoteStream); + }; + pc.onremovestream = function(evt) { + console.log("removestream", evt); + }; + return pc; + }; + + Call.prototype.addStream = function(s) { + this.pc.addStream(s); + return this; + }; + + Call.prototype.ready = function(fn) { + if (this._ready) { + fn(this.remoteStream); + } else { + this.once('ready', fn); + } + return this; + }; + + Call.prototype.duration = function() { + var e, s; + if (this.endTime != null) { + s = this.endTime.getTime(); + } + if (s == null) { + s = Date.now(); + } + e = this.startTime.getTime(); + return (s - e) / 1000; + }; + + Call.prototype.chat = function(msg) { + this.parent.chat(this.user, msg); + return this; + }; + + Call.prototype.answer = function() { + this.startTime = new Date; + this.socket.write({ + type: "answer", + to: this.user, + args: { + accepted: true + } + }); + this.initSDP(); + return this; + }; + + Call.prototype.decline = function() { + this.socket.write({ + type: "answer", + to: this.user, + args: { + accepted: false + } + }); + return this; + }; + + Call.prototype.end = function() { + this.endTime = new Date; + try { + this.pc.close(); + } catch (_error) {} + this.socket.write({ + type: "hangup", + to: this.user + }); + this.emit("hangup"); + return this; + }; + + Call.prototype.initSDP = function() { + var done, err, + _this = this; + done = function(desc) { + desc.sdp = RTC.processSDPOut(desc.sdp); + _this.pc.setLocalDescription(desc); + return _this.socket.write({ + type: "sdp", + to: _this.user, + args: desc + }); + }; + err = function(e) { + throw e; + }; + if (this.isCaller) { + return this.pc.createOffer(done, err, RTC.constraints); + } + if (this.pc.remoteDescription) { + return this.pc.createAnswer(done, err, RTC.constraints); + } + return this.once("sdp", function() { + return _this.pc.createAnswer(done, err); + }); + }; + + return Call; + + })(EventEmitter); + + module.exports = Call; + +}).call(this); diff --git a/dist/RTC.js b/dist/RTC.js new file mode 100644 index 0000000..4bb4898 --- /dev/null +++ b/dist/RTC.js @@ -0,0 +1,222 @@ +// Generated by CoffeeScript 1.4.0 +(function() { + var IceCandidate, MediaStream, PeerConnection, SessionDescription, URL, attachStream, browser, extract, getUserMedia, processSDPIn, processSDPOut, removeCN, replaceCodec, shim, supported, useOPUS; + + PeerConnection = window.mozRTCPeerConnection || window.PeerConnection || window.webkitPeerConnection00 || window.webkitRTCPeerConnection; + + IceCandidate = window.mozRTCIceCandidate || window.RTCIceCandidate; + + SessionDescription = window.mozRTCSessionDescription || window.RTCSessionDescription; + + MediaStream = window.MediaStream || window.webkitMediaStream; + + getUserMedia = navigator.mozGetUserMedia || navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.msGetUserMedia; + + URL = window.URL || window.webkitURL || window.msURL || window.oURL; + + getUserMedia = getUserMedia.bind(navigator); + + browser = (navigator.mozGetUserMedia ? 'firefox' : 'chrome'); + + supported = (PeerConnection != null) && (getUserMedia != null); + + extract = function(str, reg) { + var match; + match = str.match(reg); + return (match != null ? match[1] : null); + }; + + replaceCodec = function(line, codec) { + var el, els, idx, out, _i, _len; + els = line.split(' '); + out = []; + for (idx = _i = 0, _len = els.length; _i < _len; idx = ++_i) { + el = els[idx]; + if (idx === 3) { + out[idx++] = codec; + } + if (el !== codec) { + out[idx++] = el; + } + } + return out.join(' '); + }; + + removeCN = function(lines, mLineIdx) { + var cnPos, idx, line, mLineEls, payload, _i, _len; + mLineEls = lines[mLineIdx].split(' '); + for (idx = _i = 0, _len = lines.length; _i < _len; idx = ++_i) { + line = lines[idx]; + if (!(line != null)) { + continue; + } + payload = extract(line, /a=rtpmap:(\d+) CN\/\d+/i); + if (payload != null) { + cnPos = mLineEls.indexOf(payload); + if (cnPos !== -1) { + mLineEls.splice(cnPos, 1); + } + lines.splice(idx, 1); + } + } + lines[mLineIdx] = mLineEls.join(' '); + return lines; + }; + + useOPUS = function(sdp) { + var idx, line, lines, mLineIdx, payload, _i, _len; + lines = sdp.split('\r\n'); + mLineIdx = ((function() { + var _i, _len, _results; + _results = []; + for (idx = _i = 0, _len = lines.length; _i < _len; idx = ++_i) { + line = lines[idx]; + if (line.indexOf('m=audio') !== -1) { + _results.push(idx); + } + } + return _results; + })())[0]; + if (mLineIdx == null) { + return sdp; + } + for (idx = _i = 0, _len = lines.length; _i < _len; idx = ++_i) { + line = lines[idx]; + if (!(line.indexOf('opus/48000') !== -1)) { + continue; + } + payload = extract(line, /:(\d+) opus\/48000/i); + if (payload != null) { + lines[mLineIdx] = replaceCodec(lines[mLineIdx], payload); + } + break; + } + lines = removeCN(lines, mLineIdx); + return lines.join('\r\n'); + }; + + processSDPOut = function(sdp) { + var addCrypto, line, out, _i, _j, _len, _len1, _ref, _ref1; + out = []; + if (browser === 'firefox') { + addCrypto = "a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:BAADBAADBAADBAADBAADBAADBAADBAADBAADBAAD"; + _ref = sdp.split('\r\n'); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + line = _ref[_i]; + out.push(line); + if (line.indexOf('m=') === 0) { + out.push(addCrypto); + } + } + } else { + _ref1 = sdp.split('\r\n'); + for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { + line = _ref1[_j]; + if (line.indexOf("a=ice-options:google-ice") === -1) { + out.push(line); + } + } + } + return useOPUS(out.join('\r\n')); + }; + + processSDPIn = function(sdp) { + return sdp; + }; + + attachStream = function(uri, el) { + var e, _i, _len; + if (typeof el === "string") { + return attachStream(uri, document.getElementById(el)); + } else if (el.jquery) { + el.attr('src', uri); + for (_i = 0, _len = el.length; _i < _len; _i++) { + e = el[_i]; + e.play(); + } + } else { + el.src = uri; + el.play(); + } + return el; + }; + + shim = function() { + var PeerConnConfig, mediaConstraints, out; + if (!supported) { + return; + } + if (browser === 'firefox') { + PeerConnConfig = { + iceServers: [ + { + url: "stun:23.21.150.121" + } + ] + }; + mediaConstraints = { + mandatory: { + OfferToReceiveAudio: true, + OfferToReceiveVideo: true, + MozDontOfferDataChannel: true + } + }; + MediaStream.prototype.getVideoTracks = function() { + return []; + }; + MediaStream.prototype.getAudioTracks = function() { + return []; + }; + } else { + PeerConnConfig = { + iceServers: [ + { + url: "stun:stun.l.google.com:19302" + } + ] + }; + mediaConstraints = { + mandatory: { + OfferToReceiveAudio: true, + OfferToReceiveVideo: true, + DtlsSrtpKeyAgreement: true + } + }; + if (!MediaStream.prototype.getVideoTracks) { + MediaStream.prototype.getVideoTracks = function() { + return this.videoTracks; + }; + MediaStream.prototype.getAudioTracks = function() { + return this.audioTracks; + }; + } + if (!PeerConnection.prototype.getLocalStreams) { + PeerConnection.prototype.getLocalStreams = function() { + return this.localStreams; + }; + PeerConnection.prototype.getRemoteStreams = function() { + return this.remoteStreams; + }; + } + } + out = { + PeerConnection: PeerConnection, + IceCandidate: IceCandidate, + SessionDescription: SessionDescription, + MediaStream: MediaStream, + getUserMedia: getUserMedia, + URL: URL, + attachStream: attachStream, + processSDPIn: processSDPIn, + processSDPOut: processSDPOut, + PeerConnConfig: PeerConnConfig, + browser: browser, + supported: supported, + constraints: mediaConstraints + }; + return out; + }; + + module.exports = shim(); + +}).call(this); diff --git a/dist/holla.js b/dist/holla.js new file mode 100644 index 0000000..8a77f69 --- /dev/null +++ b/dist/holla.js @@ -0,0 +1,233 @@ +// Generated by CoffeeScript 1.4.0 +(function() { + var Call, ProtoSock, RTC, client, holla; + + Call = require('./Call'); + + RTC = require('./RTC'); + + ProtoSock = require('protosock'); + + client = { + options: { + namespace: 'holla', + resource: 'default', + debug: false + }, + register: function(name, cb) { + var _this = this; + this.ssocket.write({ + type: "register", + args: { + name: name + } + }); + return this.once("register", function(worked) { + if (worked) { + _this.user = name; + _this.emit("authorized"); + } + _this.authorized = worked; + return typeof cb === "function" ? cb(worked) : void 0; + }); + }, + call: function(user) { + return new Call(this, user, true); + }, + chat: function(user, msg) { + this.ssocket.write({ + type: "chat", + to: user, + args: { + message: msg + } + }); + return this; + }, + ready: function(fn) { + if (this.authorized) { + fn(); + } else { + this.once('authorized', fn); + } + return this; + }, + validate: function(socket, msg, done) { + if (this.options.debug) { + console.log(msg); + } + if (typeof msg !== 'object') { + return done(false); + } + if (typeof msg.type !== 'string') { + return done(false); + } + if (msg.type === "register") { + if (typeof msg.args !== 'object') { + return done(false); + } + if (typeof msg.args.result !== 'boolean') { + return done(false); + } + } else if (msg.type === "offer") { + if (typeof msg.from !== 'string') { + return done(false); + } + } else if (msg.type === "answer") { + if (typeof msg.args !== 'object') { + return done(false); + } + if (typeof msg.from !== 'string') { + return done(false); + } + if (typeof msg.args.accepted !== 'boolean') { + return done(false); + } + } else if (msg.type === "sdp") { + if (typeof msg.args !== 'object') { + return done(false); + } + if (typeof msg.from !== 'string') { + return done(false); + } + if (!msg.args.sdp) { + return done(false); + } + if (!msg.args.type) { + return done(false); + } + } else if (msg.type === "candidate") { + if (typeof msg.args !== 'object') { + return done(false); + } + if (typeof msg.from !== 'string') { + return done(false); + } + if (typeof msg.args.candidate !== 'object') { + return done(false); + } + } else if (msg.type === "chat") { + if (typeof msg.args !== 'object') { + return done(false); + } + if (typeof msg.from !== 'string') { + return done(false); + } + if (typeof msg.args.message !== 'string') { + return done(false); + } + } else if (msg.type === "hangup") { + if (typeof msg.from !== 'string') { + return done(false); + } + } else if (msg.type === "presence") { + if (typeof msg.args !== 'object') { + return done(false); + } + if (typeof msg.args.name !== 'string') { + return done(false); + } + if (typeof msg.args.online !== 'boolean') { + return done(false); + } + } else { + return done(false); + } + return done(true); + }, + error: function(socket, err) { + return this.emit('error', err, socket); + }, + message: function(socket, msg) { + var c; + switch (msg.type) { + case "register": + return this.emit("register", msg.args.result); + case "offer": + c = new Call(this, msg.from, false); + return this.emit("call", c); + case "presence": + this.emit("presence", msg.args); + return this.emit("presence." + msg.args.name, msg.args.online); + case "chat": + this.emit("chat", { + from: msg.from, + message: msg.args.message + }); + return this.emit("chat." + msg.from, msg.args.message); + case "hangup": + this.emit("hangup", { + from: msg.from + }); + return this.emit("hangup." + msg.from); + case "answer": + this.emit("answer", { + from: msg.from, + accepted: msg.args.accepted + }); + return this.emit("answer." + msg.from, msg.args.accepted); + case "candidate": + this.emit("candidate", { + from: msg.from, + candidate: msg.args.candidate + }); + return this.emit("candidate." + msg.from, msg.args.candidate); + case "sdp": + this.emit("sdp", { + from: msg.from, + sdp: msg.args.sdp, + type: msg.args.type + }); + return this.emit("sdp." + msg.from, msg.args); + } + } + }; + + holla = { + createClient: ProtoSock.createClientWrapper(client), + Call: Call, + supported: RTC.supported, + config: RTC.PeerConnConfig, + streamToBlob: function(s) { + return RTC.URL.createObjectURL(s); + }, + pipe: function(stream, el) { + var uri; + uri = holla.streamToBlob(stream); + return RTC.attachStream(uri, el); + }, + createStream: function(opt, cb) { + var err, succ; + if (RTC.getUserMedia == null) { + return cb("Missing getUserMedia"); + } + err = cb; + succ = function(s) { + return cb(null, s); + }; + RTC.getUserMedia(opt, succ, err); + return holla; + }, + createFullStream: function(cb) { + return holla.createStream({ + video: true, + audio: true + }, cb); + }, + createVideoStream: function(cb) { + return holla.createStream({ + video: true, + audio: false + }, cb); + }, + createAudioStream: function(cb) { + return holla.createStream({ + video: false, + audio: true + }, cb); + } + }; + + module.exports = holla; + +}).call(this);