var util = require('util'); var webrtcSupport = require('webrtcsupport'); var mockconsole = require('mockconsole'); var localMedia = require('localmedia'); var Peer = require('./peer'); function WebRTC(opts) { var self = this; var options = opts || {}; var config = this.config = { debug: false, // makes the entire PC config overridable peerConnectionConfig: { iceServers: [{'urls': 'stun:stun.l.google.com:19302'}] }, peerConnectionConstraints: { optional: [] }, receiveMedia: { offerToReceiveAudio: 1, offerToReceiveVideo: 1 }, enableDataChannels: true }; var item; // We also allow a 'logger' option. It can be any object that implements // log, warn, and error methods. // We log nothing by default, following "the rule of silence": // http://www.linfo.org/rule_of_silence.html this.logger = function () { // we assume that if you're in debug mode and you didn't // pass in a logger, you actually want to log as much as // possible. if (opts.debug) { return opts.logger || console; } else { // or we'll use your logger which should have its own logic // for output. Or we'll return the no-op. return opts.logger || mockconsole; } }(); // set options for (item in options) { if (options.hasOwnProperty(item)) { this.config[item] = options[item]; } } // check for support if (!webrtcSupport.support) { this.logger.error('Your browser doesn\'t seem to support WebRTC'); } // where we'll store our peer connections this.peers = []; // call localMedia constructor localMedia.call(this, this.config); this.on('speaking', function () { if (!self.hardMuted) { // FIXME: should use sendDirectlyToAll, but currently has different semantics wrt payload self.peers.forEach(function (peer) { if (peer.enableDataChannels) { var dc = peer.getDataChannel('hark'); if (dc.readyState != 'open') return; dc.send(JSON.stringify({type: 'speaking'})); } }); } }); this.on('stoppedSpeaking', function () { if (!self.hardMuted) { // FIXME: should use sendDirectlyToAll, but currently has different semantics wrt payload self.peers.forEach(function (peer) { if (peer.enableDataChannels) { var dc = peer.getDataChannel('hark'); if (dc.readyState != 'open') return; dc.send(JSON.stringify({type: 'stoppedSpeaking'})); } }); } }); this.on('volumeChange', function (volume, treshold) { if (!self.hardMuted) { // FIXME: should use sendDirectlyToAll, but currently has different semantics wrt payload self.peers.forEach(function (peer) { if (peer.enableDataChannels) { var dc = peer.getDataChannel('hark'); if (dc.readyState != 'open') return; dc.send(JSON.stringify({type: 'volume', volume: volume })); } }); } }); // log events in debug mode if (this.config.debug) { this.on('*', function (event, val1, val2) { var logger; // if you didn't pass in a logger and you explicitly turning on debug // we're just going to assume you're wanting log output with console if (self.config.logger === mockconsole) { logger = console; } else { logger = self.logger; } logger.log('event:', event, val1, val2); }); } } util.inherits(WebRTC, localMedia); WebRTC.prototype.createPeer = function (opts) { var peer; opts.parent = this; peer = new Peer(opts); this.peers.push(peer); return peer; }; // removes peers WebRTC.prototype.removePeers = function (id, type) { this.getPeers(id, type).forEach(function (peer) { peer.end(); }); }; // fetches all Peer objects by session id and/or type WebRTC.prototype.getPeers = function (sessionId, type) { return this.peers.filter(function (peer) { return (!sessionId || peer.id === sessionId) && (!type || peer.type === type); }); }; // sends message to all WebRTC.prototype.sendToAll = function (message, payload) { this.peers.forEach(function (peer) { peer.send(message, payload); }); }; // sends message to all using a datachannel // only sends to anyone who has an open datachannel WebRTC.prototype.sendDirectlyToAll = function (channel, message, payload) { this.peers.forEach(function (peer) { if (peer.enableDataChannels) { peer.sendDirectly(channel, message, payload); } }); }; module.exports = WebRTC;