Repository: vmolsa/webrtc-native Branch: master Commit: acf4abac9c10 Files: 67 Total size: 287.2 KB Directory structure: gitextract_5hvoxsbh/ ├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── binding.gyp ├── examples/ │ ├── node2browser/ │ │ ├── index.html │ │ ├── index.js │ │ └── package.json │ └── node2browser_ws/ │ ├── index.html │ ├── index.js │ └── package.json ├── index.js ├── node_versions.json ├── package.json ├── scripts/ │ ├── build.js │ ├── install.js │ ├── multibuild.cmd │ ├── multibuild.sh │ ├── nvm.sh │ └── upload.js ├── src/ │ ├── ArrayBuffer.h │ ├── BackTrace.cc │ ├── BackTrace.h │ ├── Common.h │ ├── DataChannel.cc │ ├── DataChannel.h │ ├── EventEmitter.cc │ ├── EventEmitter.h │ ├── GetSources.cc │ ├── GetSources.h │ ├── GetUserMedia.cc │ ├── GetUserMedia.h │ ├── Global.cc │ ├── Global.h │ ├── MediaCapturer.cc │ ├── MediaCapturer.h │ ├── MediaConstraints.cc │ ├── MediaConstraints.h │ ├── MediaStream.cc │ ├── MediaStream.h │ ├── MediaStreamTrack.cc │ ├── MediaStreamTrack.h │ ├── Module.cc │ ├── Observers.cc │ ├── Observers.h │ ├── PeerConnection.cc │ ├── PeerConnection.h │ ├── Platform.cc │ ├── Platform.h │ ├── Stats.cc │ ├── Stats.h │ ├── Wrap.h │ ├── addon.gypi │ └── webrtc.gyp └── test/ ├── all.js ├── bwtest.js ├── core.js ├── dataChannel.js ├── getSources.js ├── getUserMedia.js ├── mediaStream.js ├── multiconnect.js ├── p2p-browser.html ├── p2p.js ├── p2p_stream.js ├── test.js └── videoStream.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # Logs logs *.log # Runtime data pids *.pid *.seed # Directory for instrumented libs generated by jscoverage/JSCover lib-cov # Coverage directory used by tools like istanbul coverage # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) .grunt # node-waf configuration .lock-wscript # Compiled binary addons (http://nodejs.org/api/addons.html) build/Release build # Dependency directory # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git node_modules third_party config.gypi nodejs.gypi ================================================ FILE: .npmignore ================================================ # Logs logs *.log # Runtime data pids *.pid *.seed # Directory for instrumented libs generated by jscoverage/JSCover lib-cov # Coverage directory used by tools like istanbul coverage # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) .grunt # node-waf configuration .lock-wscript # Compiled binary addons (http://nodejs.org/api/addons.html) build/Release build # Dependency directory # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git node_modules src third_party config.gypi nodejs.gypi ================================================ FILE: LICENSE ================================================ The MIT License (MIT) Copyright (c) 2015 vmolsa (http://github.com/vmolsa) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ [WebRTC](http://en.wikipedia.org/wiki/WebRTC) for NodeJS ### Chromium webrtc-native is using [WebRTC](http://webrtc.org/) from chromium project. code is compiled with branch [50](https://chromium.googlesource.com/external/webrtc/+/branch-heads/50). ### Usage For installing or building module from source go to page [Getting Started](https://github.com/vmolsa/webrtc-native/wiki/Getting-started) ### API ```` var WebRTC = require('webrtc-native'); ```` #### WebRTC.[RTCPeerConnection](https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection) #### WebRTC.[RTCIceCandidate](https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnectionIceEvent) #### WebRTC.[RTCSessionDescription](https://developer.mozilla.org/en-US/docs/Web/API/RTCSessionDescription) #### WebRTC.[RTCDataChannel](https://developer.mozilla.org/en-US/docs/Web/API/RTCDataChannel) #### WebRTC.[MediaStream](https://developer.mozilla.org/en-US/docs/Web/API/MediaStream) #### WebRTC.[MediaStreamTrack](https://developer.mozilla.org/en-US/docs/Web/API/MediaStreamTrack) #### WebRTC.[getUserMedia](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/getUserMedia) #### WebRTC.[getSources](http://simpl.info/getusermedia/sources/index.html) - Returns array of available device inputs #### WebRTC.RTCGarbageCollect() - Notify V8 Engine to attempt to free memory. #### WebRTC.setDebug(boolean) - Enable / Disable WebRTC log messages ================================================ FILE: binding.gyp ================================================ { 'targets': [ { 'target_name': 'action_before_build', 'dependencies': [], 'hard_dependency': 1, 'type': 'none', 'actions': [ { 'action_name': 'run_build_script', 'inputs': [], 'outputs': [''], 'action': [ 'node', 'scripts/build.js', '-Dtarget-arch=<(target_arch)', '<(node_root_dir)', '<(node_lib_file)', '<(node_gyp_dir)' ], } ] }, ], } ================================================ FILE: examples/node2browser/index.html ================================================ ================================================ FILE: examples/node2browser/index.js ================================================ var express = require('express'); var app = express(); var server = require('http').Server(app); var io = require('socket.io')(server); var WebRTC = require('../../'); app.use(express.static(__dirname)); var io = require('socket.io')(server); var config = { iceServers: [ { url: 'stun:stun.l.google.com:19302', }, ], }; var constraints = { audio: { optional: [ { googEchoCancellation: true, googEchoCancellation2: true, googDAEchoCancellation: true, googAutoGainControl: true, googAutoGainControl2: true, googNoiseSuppression: true, googNoiseSuppression2: true, googHighpassFilter: true, googTypingNoiseDetection: true, googAudioMirroring: true, }, ], }, video: true, /* video: { optional: [ { minAspectRatio: 1.333, maxAspectRatio: 1.778, maxWidth: 1920, minWidth: 320, maxHeight: 1080, minHeight: 180, maxFrameRate: 60, minFrameRate: 30, }, ], }, */ optional: [ { DtlsSrtpKeyAgreement: true, }, ], mandatory: { OfferToReceiveAudio: true, OfferToReceiveVideo: true, }, }; io.on('connection', function(socket) { console.log('Peer Connected'); var peer = new WebRTC.RTCPeerConnection(config, constraints); socket.on('disconnect', function () { console.log('Peer Disconnected'); peer.close(); }); socket.on('icecandidate', function(data) { if (data && data.candidate && data.sdpMid && data.sdpMLineIndex) { peer.addIceCandidate(new WebRTC.RTCIceCandidate(data)); } }); socket.on('offer', function(data) { peer.setRemoteDescription(new WebRTC.RTCSessionDescription(data), function() { peer.createAnswer(function(sdp) { peer.setLocalDescription(sdp, function() { socket.emit('answer', sdp); }); }); }); }); socket.on('answer', function(data) { peer.setRemoteDescription(new WebRTC.RTCSessionDescription(data)); }); peer.onicecandidate = function(event) { var candidate = event.candidate || event; socket.emit('icecandidate', candidate); }; peer.onnegotiationneeded = function() { peer.createOffer(function(sdp) { peer.setLocalDescription(sdp, function() { socket.emit('offer', sdp); }); }); }; peer.onaddstream = function(stream) { console.log('Peer: add mediaStream'); }; peer.onremovestream = function(stream) { console.log('Peer: remove mediaStream'); }; peer.ondatachannel = function(event) { var channel = event ? event.channel || event : null; channel.onopen = function() { console.log('Peer Channel opened!'); }; channel.onclose = function() { console.log('Peer Channel closed!'); }; channel.onmessage = function(event) { var data = event.data; if (data == 'PING') { console.log('Peer: Sending PONG'); channel.send('PONG'); } else { console.log('Peer Message:', data); channel.send(data); } }; }; peer.ondatachannel(peer.createDataChannel('echo')); WebRTC.getUserMedia(constraints, function(stream) { console.log('Sending Stream to Peer'); peer.addStream(stream); }); }); server.listen(8080, function() { console.log('Open in browser: http://localhost:8080/'); }); ================================================ FILE: examples/node2browser/package.json ================================================ { "dependencies": { "express": "^4.13.0", "socket.io": "^1.3.5" } } ================================================ FILE: examples/node2browser_ws/index.html ================================================ ================================================ FILE: examples/node2browser_ws/index.js ================================================ var WebRTC = require('../../'); var WebSocketServer = require('ws').Server; var express = require('express'); var app = express(); var server = require('http').Server(app); WebRTC.setDebug(false); app.use(express.static(__dirname)); var _sources = []; var localstream =undefined; var initialized = false; var config = { iceServers: [ { url: 'stun:stun.l.google.com:19302', }, ], }; var constraints = { audio: true, video: true, }; function sourceSelected(audioSource, videoSource) { _sources.push(videoSource); } WebRTC.getSources(function(sourceInfos) { for (var i = 0; i != sourceInfos.length; ++i) { var sourceInfo = sourceInfos[i]; if (sourceInfo.kind === 'audio') { console.log(sourceInfo.id, sourceInfo.label || 'microphone'); audioSource = sourceInfo.id; } else if (sourceInfo.kind === 'video') { console.log(sourceInfo.id, sourceInfo.label || 'camera'); _sources.push (sourceInfo.id); } else { console.log('Some other kind of source: ', sourceInfo); } } WebRTC.getUserMedia(constraints, getUserMedia_successCallback, getUserMedia_errorCallback); }); function getUserMedia_successCallback(stream) { localstream = stream; var video_list = localstream.getVideoTracks(); video_list.forEach(function (track) { console.log('Video Track %s', JSON.stringify(track)); track.readyState = "alive"; }); console.log('Sending Stream to Peer'); initialized = true; init(); } function getUserMedia_errorCallback(error) { console.log ('error %s',error); } function setLocalSDP(p, callback){ if(p.iceGatheringState != 'complete'){ setTimeout(function(){ setLocalSDP(p, callback); }, 10); }else{ callback && callback(); } } function init() { ws = new WebSocketServer( { host: '127.0.0.1', port: 8181 }); // start websocket server ws.on('connection', onConnect_Handler); function onConnect_Handler(connection) { var peer = new WebRTC.RTCPeerConnection(null, {audio: true, video: true}); peer.addStream(localstream); peer.onicecandidate = function(event) { var candidate = event.candidate || event; connection.send(JSON.stringify({'type':'icecandidate', 'data':candidate})); }; peer.createOffer(function(offer){ peer.setLocalDescription(new WebRTC.RTCSessionDescription(offer), function(){ setLocalSDP(peer, function(){ var offerSDP = JSON.stringify(peer.localDescription); connection.send(offerSDP); }); }, function(e){ console.log (e); }) }); connection.on('message', function onWsMessage(message, flags) { console.log ("INCOMMING %s:", message); var jmsg = undefined; try { jmsg = JSON.parse(message); } catch (e) { return ; } if (jmsg.type=='answer') { console.log ('answer'); var clientRemoteSDP = new WebRTC.RTCSessionDescription(jmsg); peer.setRemoteDescription(clientRemoteSDP, function(){ console.log('setRemoteDescription OK'); }, function(err){ console.log('setRemoteDescription error', err); }) } if (jmsg.hasOwnProperty('candidate')) { console.log ('icecandidate: jmsg: %s',JSON.stringify(jmsg)); peer.addIceCandidate(new WebRTC.RTCIceCandidate(jmsg)); } }); peer.onnegotiationneeded = function() { console.log ('onnegotiationneeded'); }; peer.onaddstream = function(stream) { console.log('Peer: add mediaStream'); }; peer.onremovestream = function(stream) { console.log('Peer: remove mediaStream'); }; peer.ondatachannel = function(event) { var channel = event ? event.channel || event : null; channel.onopen = function() { console.log('Peer Channel opened!'); }; channel.onclose = function() { console.log('Peer Channel closed!'); }; channel.onmessage = function(event) { var data = event.data; if (data == 'PING') { console.log('Peer: Sending PONG'); channel.send('PONG'); } else { console.log('Peer Message:', data); channel.send(data); } }; }; peer.ondatachannel(peer.createDataChannel('echo')); } server.listen(8281, function() { console.log('Open in browser: http://localhost:8281/index.html'); }); } // */ ================================================ FILE: examples/node2browser_ws/package.json ================================================ { "dependencies": { "express": "^4.13.0", "ws": "^1.1.0" } } ================================================ FILE: index.js ================================================ module.exports = require('./build/Release/webrtc.node'); ================================================ FILE: node_versions.json ================================================ [ "iojs-v2.3.2", "iojs-v3.3.1", "v0.10.38", "v0.12.5", "v4.2.1", "v4.3.2", "v4.4.0", "v5.0.0", "v5.1.1", "v5.2.0", "v5.3.0", "v5.4.1", "v5.5.0", "v5.6.0", "v5.7.1", "v5.8.0", "v5.9.1" ] ================================================ FILE: package.json ================================================ { "name": "webrtc-native", "version": "1.4.0", "description": "WebRTC for NodeJS", "homepage": "https://github.com/vmolsa/webrtc-native", "author": "https://github.com/vmolsa", "repository": { "type": "git", "url": "https://github.com/vmolsa/webrtc-native.git" }, "bugs": { "url": "https://github.com/vmolsa/webrtc-native/issues" }, "keywords": [ "webrtc", "p2p", "streaming", "dtls", "srtp", "rtp", "capture", "datachannel" ], "license": "MIT", "main": "index.js", "scripts": { "install": "node scripts/install.js", "test": "node test/all.js" }, "dependencies": { "request": "^2.58.0" }, "devDependencies": { "nan": "^2.1.0", "node-gyp": "^3.0.3", "minimist": "^1.1.1", "simple-peer": "^5.11.5", "tape": "^4.0.0" } } ================================================ FILE: scripts/build.js ================================================ var fs = require('fs'); var os = require('os'); var spawn = require('child_process').spawn; var path = require('path'); var request = require('request'); var ROOT = path.resolve(__dirname, '..'); if (!fs.existsSync(ROOT + path.sep + 'build' + path.sep + 'config.gypi')) { throw new Error('Run node-gyp rebuild instead of node build.js'); } var PACKAGE = require(path.resolve(ROOT, 'package.json')); var CHROMIUM = 'https://chromium.googlesource.com/external/webrtc.git@ca4ec2c3b9331e8f054facf400ebaeb9681831e2'; var USE_OPENSSL = false; var USE_GTK = false; var USE_X11 = false; var PLATFORM = os.platform(); var SYSTEM = os.release(); var ARCH = process.argv[2].substring(14); var NODEJS = path.resolve(process.argv[3]); var NODELIB = process.argv[4].substring(3).split('.')[0]; var NODEGYP = process.argv[5]; var URL = 'http://cide.cc:8080/webrtc/'; var NODEVER = process.version.split('.'); var NODE_ZERO = (NODEVER[0] === 'v0'); var CROSSCOMPILE = (ARCH !== process.arch); NODEVER[2] = 'x'; NODEVER = NODEVER.join('.'); URL += 'webrtc-' + PACKAGE.version + '-' + PLATFORM + '-' + ARCH + '-' + NODEVER + '.node'; if (fs.existsSync(ROOT + path.sep + 'nodejs.gypi')) { fs.unlinkSync(ROOT + path.sep + 'nodejs.gypi'); } var COMMON = path.resolve(NODEJS, 'include', 'node', 'common.gypi'); if (fs.existsSync(COMMON)) { fs.linkSync(COMMON, ROOT + path.sep + 'nodejs.gypi'); } else { fs.linkSync(NODEJS + path.sep + 'common.gypi', ROOT + path.sep + 'nodejs.gypi'); } var CONFIG = 'Release'; if (fs.existsSync(ROOT + path.sep + 'build' + path.sep + 'Debug')) { CONFIG = 'Debug'; } var THIRD_PARTY = path.resolve(ROOT, 'third_party'); var DEPOT_TOOLS_REPO = 'https://chromium.googlesource.com/chromium/tools/depot_tools.git'; var DEPOT_TOOLS = path.resolve(THIRD_PARTY, 'depot_tools'); var WEBRTC = path.resolve(THIRD_PARTY, 'webrtc'); var WEBRTC_SRC = path.resolve(WEBRTC, 'src'); var WEBRTC_OUT = path.resolve(WEBRTC_SRC, 'out', CONFIG); var FETCH = path.resolve(DEPOT_TOOLS, (os.platform() == 'win32') ? 'fetch.bat' : 'fetch'); var GCLIENT = path.resolve(DEPOT_TOOLS, (os.platform() == 'win32') ? 'gclient.bat' : 'gclient'); if (os.platform() == 'win32' && ARCH == 'x64') { WEBRTC_OUT = path.resolve(WEBRTC_SRC, 'out', CONFIG + '_x64'); } function install() { fs.linkSync(WEBRTC_OUT + path.sep + 'webrtc.node', path.resolve(ROOT, 'build', CONFIG, 'webrtc.node')); if (process.env['CIDE_CREDENTIALS']) { console.log('Uploading module.'); var credentials = { 'auth': { 'user': 'cIDE', 'pass': process.env['CIDE_CREDENTIALS'], } }; fs.createReadStream(path.resolve(ROOT, 'build', CONFIG, 'webrtc.node')).pipe(request.put(URL, credentials, function(error, response, body) { if (!error && response.statusCode == 200) { console.log('Done! :)'); } else { console.log('Upload Failed! :('); } })); } else { setTimeout(function() { console.log('Done! :)'); }, 200); } } function compile() { var res = spawn('ninja', [ '-C', WEBRTC_OUT ], { cwd: WEBRTC_SRC, env: process.env, stdio: 'inherit', }); res.on('close', function (code) { if (!code) { return install(); } process.exit(1); }); } function build() { if (fs.existsSync(WEBRTC_SRC)) { var res = spawn('python', [ WEBRTC_SRC + path.sep + 'webrtc' + path.sep + 'build' + path.sep + 'gyp_webrtc', 'src' + path.sep + 'webrtc.gyp' ], { cwd: ROOT, env: process.env, stdio: 'inherit', }); res.on('close', function (code) { if (!code) { return compile(); } process.exit(1); }); } } function sync() { if (!fs.existsSync(THIRD_PARTY + path.sep + 'webrtc_sync')) { var res = spawn(GCLIENT, ['sync', '--with_branch_heads'], { cwd: WEBRTC, env: process.env, stdio: 'inherit', }); res.on('close', function (code) { if (!code) { fs.closeSync(fs.openSync(THIRD_PARTY + path.sep + 'webrtc_sync', 'w')); return build(); } process.exit(1); }); } else { build(); } } function configure() { if (fs.existsSync(WEBRTC_OUT + path.sep + 'webrtc.node')) { fs.unlinkSync(WEBRTC_OUT + path.sep + 'webrtc.node'); } process.env['GYP_DEFINES'] += ' target_arch=' + ARCH; process.env['GYP_DEFINES'] += ' host_arch=' + process.arch; process.env['GYP_DEFINES'] += ' node_root_dir=' + NODEJS.replace(/\\/g, '\\\\'); process.env['GYP_DEFINES'] += ' node_lib_file=' + NODELIB; process.env['GYP_DEFINES'] += ' node_gyp_dir=' + NODEGYP.replace(/\\/g, '\\\\'); process.env['GYP_DEFINES'] += ' build_with_chromium=0'; process.env['GYP_DEFINES'] += ' use_openssl=' + ((USE_OPENSSL) ? '1' : '0'); process.env['GYP_DEFINES'] += ' use_gtk='+ ((USE_GTK) ? '1' : '0'); process.env['GYP_DEFINES'] += ' use_x11=' + ((USE_X11) ? '1' : '0'); process.env['GYP_DEFINES'] += ' ConfigurationName=' + CONFIG; process.env['GYP_DEFINES'] += ' include_tests=0'; switch (os.platform()) { case 'darwin': process.env['GYP_DEFINES'] += ' clang=1'; break; case 'win32': process.env['DEPOT_TOOLS_WIN_TOOLCHAIN'] = 0; process.env['GYP_MSVS_VERSION'] = 2013; break; case 'linux': if (CROSSCOMPILE) { process.env['GYP_CROSSCOMPILE'] = 1; process.env['GYP_DEFINES'] += ' clang=0 use_system_expat=0'; process.env['CXX'] = 'arm-linux-gnueabihf-g++-5'; var CPATH = process.env['CPATH']; process.env['CPATH'] = '/usr/arm-linux-gnueabihf/include/c++/5/'; process.env['CPATH'] += '/usr/arm-linux-gnueabihf/include/c++/5/arm-linux-gnueabihf/'; process.env['CPATH'] += '/usr/arm-linux-gnueabihf/include/c++/5/backward/'; process.env['CPATH'] += CPATH ? ':' + CPATH : ''; } else { if (NODE_ZERO) { process.env['GYP_DEFINES'] += ' clang=0'; process.env['CXX'] = 'g++-4.8'; var CPATH = process.env['CPATH']; process.env['CPATH'] = '/usr/include/c++/4.8/'; process.env['CPATH'] += '/usr/include/x86_64-linux-gnu/c++/4.8/'; process.env['CPATH'] += '/usr/include/c++/4.8/backward/'; process.env['CPATH'] += CPATH ? ':' + CPATH : ''; } else { process.env['GYP_DEFINES'] += ' clang=1'; } if (!process.env['JAVA_HOME']) { if (fs.existsSync('/usr/lib/jvm/java')) { process.env['JAVA_HOME'] = '/usr/lib/jvm/java'; } else { process.env['JAVA_HOME'] = '/usr/lib/jvm/default-java'; } } } break; default: break; } console.log('target_arch =', ARCH); console.log('host_arch =', process.arch); console.log('configuration =', CONFIG); sync(); } function config() { if (!fs.existsSync(WEBRTC) || !fs.existsSync(path.resolve(WEBRTC, '.gclient')) || !fs.existsSync(WEBRTC_SRC)) { if (!fs.existsSync(WEBRTC)) { fs.mkdirSync(WEBRTC); } var res = spawn(GCLIENT, ['config', '--name=src', CHROMIUM], { cwd: WEBRTC, env: process.env, stdio: 'inherit', }); res.on('close', function (code) { if (!code) { return configure(); } process.exit(1); }); } else { configure(); } } process.env['GYP_DEFINES'] = process.env['GYP_DEFINES'] ? process.env['GYP_DEFINES'] : ''; if (!fs.existsSync(THIRD_PARTY)) { fs.mkdirSync(THIRD_PARTY); } process.env['PATH'] = process.env['PATH'] + path.delimiter + DEPOT_TOOLS; if (!fs.existsSync(DEPOT_TOOLS)) { var res = spawn('git', ['clone', DEPOT_TOOLS_REPO], { cwd: THIRD_PARTY, env: process.env, stdio: 'inherit', }); res.on('close', function (code) { if (!code) { return config(); } process.exit(1); }); } else { config(); } ================================================ FILE: scripts/install.js ================================================ var fs = require('fs'); var os = require('os'); var spawn = require('child_process').spawn; var path = require('path'); var request = require('request'); var PLATFORM = os.platform(); var ROOT = path.resolve(__dirname, '..'); var ARCH = os.arch(); var URL = 'http://cide.cc:8080/webrtc/'; var NODEVER = process.version.split('.'); var PACKAGE = require(path.resolve(ROOT, 'package.json')); NODEVER[2] = 'x'; NODEVER = NODEVER.join('.'); URL += 'webrtc-' + PACKAGE.version + '-' + PLATFORM + '-' + ARCH + '-' + NODEVER + '.node'; function build() { console.log('Building module...'); var nodegyp = path.resolve(ROOT, 'node_modules', 'node-gyp', 'bin', 'node-gyp.js'); if (!fs.existsSync(nodegyp)) { nodegyp = path.resolve(ROOT, '..', 'node-gyp', 'bin', 'node-gyp.js'); if (!fs.existsSync(nodegyp)) { throw new Error('Build Failed. Please follow instructions from https://github.com/vmolsa/webrtc-native/wiki/Getting-started#building-from-source'); } } var res = spawn('node', [ nodegyp, 'rebuild' ], { cwd: ROOT, env: process.env, stdio: 'inherit', }); res.on('error', function(error) { var res = spawn('iojs', [ nodegyp, 'rebuild' ], { cwd: ROOT, env: process.env, stdio: 'inherit', }); }); } function test() { console.log('Loading module...'); try { var WebRTC = require(path.resolve(ROOT, 'build', 'Release', 'webrtc.node')); setTimeout(function() { console.log('Done! :)'); }, 200); } catch (ignored) { throw new Error('prebuilt module not working. See the instructions from https://github.com/vmolsa/webrtc-native/wiki/Getting-started#building-from-source for building module from source.'); } } if (process.env['BUILD_WEBRTC'] == 'true') { build(); } else { console.log('Downloading module: webrtc-' + PACKAGE.version + '-' + PLATFORM + '-' + ARCH + '-' + NODEVER + '.node'); if (!fs.existsSync(path.resolve(ROOT, 'build', 'Release'))) { if (!fs.existsSync(path.resolve(ROOT, 'build'))) { fs.mkdirSync(path.resolve(ROOT, 'build')); } fs.mkdirSync(path.resolve(ROOT, 'build', 'Release')); } request.get(URL, function (error, response, body) { if (!error && response.statusCode == 200) { setTimeout(test, 200); } else { throw new Error('prebuilt module not found. See the instructions from https://github.com/vmolsa/webrtc-native/wiki/Getting-started#building-from-source for building module from source.'); } }).pipe(fs.createWriteStream(path.resolve(ROOT, 'build', 'Release', 'webrtc.node'))); } ================================================ FILE: scripts/multibuild.cmd ================================================ nvmw install %1 & nvmw use %1 && npm install ================================================ FILE: scripts/multibuild.sh ================================================ #!/bin/sh . ~/.nvm/nvm.sh nvm install $1 nvm use $1 npm install ================================================ FILE: scripts/nvm.sh ================================================ #!/bin/sh . ~/.nvm/nvm.sh nvm $1 $2 ================================================ FILE: scripts/upload.js ================================================ var os = require('os'); var spawn = require('child_process').spawn; var versions = require('../node_versions.json'); var ROOT = process.cwd(); var MULTIBUILD = (os.platform() == 'win32') ? 'scripts\\multibuild.cmd' : 'scripts/multibuild.sh'; process.env['BUILD_WEBRTC'] = 'true'; function build(version, callback) { var res = spawn(MULTIBUILD, [ version ], { cwd: ROOT, env: process.env, stdio: 'inherit', }); res.on('close', function (code) { callback(code == 0); }); } function buildNext(index) { if (index < versions.length) { build(versions[index], function(result) { if (result) { buildNext(index + 1); } }); } } buildNext(0); ================================================ FILE: src/ArrayBuffer.h ================================================ /* * The MIT License (MIT) * * Copyright (c) 2015 vmolsa (http://github.com/vmolsa) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * */ #ifndef NODE_ARRAYBUFFER_H #define NODE_ARRAYBUFFER_H #ifdef WIN32 #pragma warning( disable : 4267 ) #endif #include #include namespace node { class ArrayBuffer { public: inline static ArrayBuffer* New(const char *str = 0) { #if (NODE_MODULE_VERSION >= NODE_0_12_MODULE_VERSION) return ArrayBuffer::New(v8::Isolate::GetCurrent(), std::string(str)); #else return ArrayBuffer::New(std::string(str)); #endif } inline static ArrayBuffer* New(const std::string &data) { #if (NODE_MODULE_VERSION >= NODE_0_12_MODULE_VERSION) return ArrayBuffer::New(v8::Isolate::GetCurrent(), data.data(), data.size()); #else return ArrayBuffer::New(data.data(), data.size()); #endif } #if (NODE_MODULE_VERSION >= NODE_0_12_MODULE_VERSION) template inline static ArrayBuffer* New(T *str, size_t length) { return ArrayBuffer::New(v8::Isolate::GetCurrent(), reinterpret_cast(str), length); } inline static ArrayBuffer* New(const char *str, size_t length) { return ArrayBuffer::New(v8::Isolate::GetCurrent(), str, length); } inline static ArrayBuffer* New(const v8::Local &arrayBuffer) { return ArrayBuffer::New(v8::Isolate::GetCurrent(), arrayBuffer); } inline static ArrayBuffer* New(const v8::Local &arg) { return ArrayBuffer::New(v8::Isolate::GetCurrent(), arg); } inline static ArrayBuffer* New(v8::Isolate *isolate, const std::string &data) { return ArrayBuffer::New(isolate, data.data(), data.size()); } inline static ArrayBuffer* New(v8::Isolate *isolate, const char *str = 0) { return ArrayBuffer::New(isolate, std::string(str)); } inline static ArrayBuffer* New(v8::Isolate *isolate, const char *str, size_t length) { if (!isolate) { isolate = v8::Isolate::GetCurrent(); } ArrayBuffer *buffer = new ArrayBuffer(); v8::Local arrayBuffer; buffer->_data = 0; buffer->_len = length; if (length) { buffer->_data = new char[length + 1]; buffer->_data[length] = '\0'; for (size_t index = 0; index < length; index++) { buffer->_data[index] = str[index]; } arrayBuffer = v8::ArrayBuffer::New(isolate, buffer->_data, length); } else { arrayBuffer = v8::ArrayBuffer::New(isolate, length); } buffer->_arrayBuffer.Reset(isolate, arrayBuffer); buffer->_arrayBuffer.SetWeak(buffer, ArrayBuffer::onDispose); buffer->_arrayBuffer.MarkIndependent(); arrayBuffer->SetHiddenValue(v8::String::NewFromUtf8(isolate, "node::ArrayBuffer"), v8::External::New(isolate, buffer)); return buffer; } inline static ArrayBuffer* New(v8::Isolate *isolate, const v8::Local &arrayBuffer) { if (!isolate) { isolate = v8::Isolate::GetCurrent(); } if (arrayBuffer.IsEmpty()) { return ArrayBuffer::New(isolate); } if (arrayBuffer->IsExternal()) { v8::Local ptr = arrayBuffer->GetHiddenValue(v8::String::NewFromUtf8(isolate, "node::ArrayBuffer")); if (ptr.IsEmpty()) { v8::Local uintArray = v8::Uint8Array::New(arrayBuffer, 0, arrayBuffer->ByteLength()); return ArrayBuffer::New(isolate, uintArray); } else { v8::Local ext = v8::Local::Cast(ptr); return static_cast(ext->Value()); } } else { ArrayBuffer *buffer = new ArrayBuffer(); v8::ArrayBuffer::Contents content = arrayBuffer->Externalize(); buffer->_data = static_cast(content.Data()); buffer->_len = content.ByteLength(); buffer->_arrayBuffer.Reset(isolate, arrayBuffer); buffer->_arrayBuffer.SetWeak(buffer, ArrayBuffer::onDispose); buffer->_arrayBuffer.MarkIndependent(); arrayBuffer->SetHiddenValue(v8::String::NewFromUtf8(isolate, "node::ArrayBuffer"), v8::External::New(isolate, buffer)); return buffer; } return 0; } inline static ArrayBuffer* New(v8::Isolate *isolate, const v8::Local &arg) { if (!arg.IsEmpty()) { if (arg->IsArrayBuffer() || arg->IsTypedArray()) { v8::Local arrayBuffer; if (arg->IsArrayBuffer()) { arrayBuffer = v8::Local::Cast(arg); } else { v8::Local view = v8::Local::Cast(arg); arrayBuffer = view->Buffer(); } return ArrayBuffer::New(isolate, arrayBuffer); } if (arg->IsString()) { v8::String::Utf8Value str(arg->ToString()); return ArrayBuffer::New(isolate, *str, str.length()); } } return ArrayBuffer::New(isolate); } inline v8::Local ToArrayBuffer(v8::Isolate *isolate = 0) const { if (!isolate) { isolate = v8::Isolate::GetCurrent(); } v8::EscapableHandleScope scope(isolate); return scope.Escape(v8::Local::New(isolate, _arrayBuffer)); } inline v8::Local ToString(v8::Isolate *isolate = 0) const { if (!isolate) { isolate = v8::Isolate::GetCurrent(); } v8::EscapableHandleScope scope(isolate); v8::Local retval = v8::String::NewFromUtf8(isolate, ArrayBuffer::ToUtf8(), v8::String::kNormalString, ArrayBuffer::Length()); return scope.Escape(retval); } #else template inline static ArrayBuffer* New(T *str, size_t length) { const char *ptr = reinterpret_cast(str); return ArrayBuffer::New(ptr, length); } inline static ArrayBuffer* New(const char *str, size_t length) { ArrayBuffer *buffer = new ArrayBuffer(); v8::Local global = v8::Context::GetCurrent()->Global(); v8::Local instance = global->Get(v8::String::New("ArrayBuffer")); v8::Local constructor = v8::Local::Cast(instance); v8::Local arrayBuffer = constructor->NewInstance(); buffer->_data = 0; buffer->_len = length; if (length) { buffer->_data = new char[length + 1]; buffer->_data[length] = '\0'; for (size_t index = 0; index < length; index++) { buffer->_data[index] = str[index]; } arrayBuffer->SetIndexedPropertiesToExternalArrayData(buffer->_data, v8::kExternalByteArray, buffer->_len); } buffer->_arrayBuffer = v8::Persistent::New(arrayBuffer); buffer->_arrayBuffer.MakeWeak(buffer, ArrayBuffer::onDispose); buffer->_arrayBuffer.MarkIndependent(); arrayBuffer->SetHiddenValue(v8::String::New("node::ArrayBuffer"), v8::External::New(buffer)); return buffer; } inline static ArrayBuffer* New(const v8::Local &arrayBuffer) { if (!arrayBuffer.IsEmpty()) { v8::Local ptr = arrayBuffer->GetHiddenValue(v8::String::New("node::ArrayBuffer")); if (!ptr.IsEmpty()) { v8::Local ext = v8::Local::Cast(ptr); return static_cast(ext->Value()); } else { if (arrayBuffer->HasIndexedPropertiesInExternalArrayData()) { int length = arrayBuffer->GetIndexedPropertiesExternalArrayDataLength(); if (length > 0) { char *data = static_cast(arrayBuffer->GetIndexedPropertiesExternalArrayData()); return ArrayBuffer::New(data, static_cast(length)); } } } } return ArrayBuffer::New(); } inline static ArrayBuffer* New(const v8::Local &arg) { if (!arg.IsEmpty()) { if (arg->IsObject()) { v8::Local arrayBuffer = v8::Local::Cast(arg); return ArrayBuffer::New(arrayBuffer); } if (arg->IsString()) { v8::String::Utf8Value str(arg->ToString()); return ArrayBuffer::New(*str, str.length()); } } return ArrayBuffer::New(); } inline v8::Local ToArrayBuffer() const { v8::HandleScope scope; v8::Local arrayBuffer = v8::Local::New(_arrayBuffer); return scope.Close(arrayBuffer); } inline v8::Local ToString() const { v8::HandleScope scope; v8::Local str = v8::String::New(ArrayBuffer::ToUtf8(), ArrayBuffer::Length()); return scope.Close(str); } #endif inline std::string ToCString() const { return std::string(ArrayBuffer::ToUtf8(), ArrayBuffer::Length()); } template inline T* To() { return reinterpret_cast(_data); } template inline const T* To() const { return reinterpret_cast(_data); } inline char *ToUtf8() { return _data; } inline const char *ToUtf8() const { return _data; } inline void *Data() const { return _data; } inline size_t Length() const { return _len; } inline size_t ByteLength() const { return _len; } #if (NODE_MODULE_VERSION >= NODE_0_12_MODULE_VERSION) static inline void onDispose(const v8::WeakCallbackData &info) { v8::Isolate *isolate = info.GetIsolate(); v8::HandleScope scope(isolate); ArrayBuffer *wrap = info.GetParameter(); if (wrap) { v8::Local arrayBuffer = v8::Local::New(isolate, wrap->_arrayBuffer); wrap->_arrayBuffer.Reset(); if (!arrayBuffer.IsEmpty()) { arrayBuffer->DeleteHiddenValue(v8::String::NewFromUtf8(isolate, "node::ArrayBuffer")); } delete wrap; } } #else static inline void onDispose(v8::Persistent value, void *data) { v8::HandleScope scope; ArrayBuffer *wrap = static_cast(data); if (wrap) { v8::Local arrayBuffer = v8::Local::New(wrap->_arrayBuffer); if (!arrayBuffer.IsEmpty()) { arrayBuffer->DeleteHiddenValue(v8::String::New("node::ArrayBuffer")); } delete wrap; } } #endif private: virtual ~ArrayBuffer() { #if (NODE_MODULE_VERSION >= NODE_0_12_MODULE_VERSION) if (_len) { delete [] _data; } #else _arrayBuffer.ClearWeak(); _arrayBuffer.Dispose(); _arrayBuffer.Clear(); #endif } protected: char* _data; size_t _len; #if (NODE_MODULE_VERSION >= NODE_0_12_MODULE_VERSION) v8::Persistent _arrayBuffer; #else v8::Persistent _arrayBuffer; #endif }; }; #endif ================================================ FILE: src/BackTrace.cc ================================================ /* * The MIT License (MIT) * * Copyright (c) 2015 vmolsa (http://github.com/vmolsa) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * */ #ifdef USE_BACKTRACE #include "BackTrace.h" BackTrace BackTrace::landmine; void BackTrace::Dump(const char *event, int skip) { void *stack[50] = {0}; int count = 0, cur = 0; count = backtrace(stack, sizeof(stack)); if (count) { for (int index = (count - 1); index > skip && index >= 1; index--) { char addr[128] = {0}; Dl_info info; snprintf(addr, sizeof(addr), "%p", stack[index]); if (dladdr(stack[index], &info) && info.dli_sname) { char buffer[128] = {0}; size_t len = sizeof(buffer); int status = 0; (void) abi::__cxa_demangle(info.dli_sname, buffer, &len, &status); if (!status) { printf("%d: %s [%s] %s\n", cur, event, addr, buffer); } else { printf("%d: %s [%s] %s\n", cur, event, addr, info.dli_sname); } } else { printf("%d: %s [%s] function()\n", cur, event, addr); } cur++; } } } void BackTrace::Close() { _segv.sa_handler = SIG_DFL; _bus.sa_handler = SIG_DFL; _abrt.sa_handler = SIG_DFL; _ill.sa_handler = SIG_DFL; sigaction(SIGSEGV, &_segv, 0); sigaction(SIGBUS, &_bus, 0); sigaction(SIGABRT, &_abrt, 0); sigaction(SIGILL, &_ill, 0); } void BackTrace::OnSegv(int sig) { BackTrace::Dump("SIGSEGV", 2); landmine.Close(); } void BackTrace::OnBus(int sig) { BackTrace::Dump("SIGBUS", 2); landmine.Close(); } void BackTrace::OnAbort(int sig) { BackTrace::Dump("SIGABRT", 2); landmine.Close(); } void BackTrace::OnIll(int sig) { BackTrace::Dump("SIGILL", 2); landmine.Close(); } BackTrace::BackTrace() { sigemptyset(&_segv.sa_mask); sigemptyset(&_bus.sa_mask); sigemptyset(&_abrt.sa_mask); sigemptyset(&_ill.sa_mask); _segv.sa_flags = 0; _segv.sa_handler = BackTrace::OnSegv; _bus.sa_flags = 0; _bus.sa_handler = BackTrace::OnBus; _abrt.sa_flags = 0; _abrt.sa_handler = BackTrace::OnAbort; _ill.sa_flags = 0; _ill.sa_handler = BackTrace::OnIll; sigaction(SIGSEGV, &_segv, 0); sigaction(SIGBUS, &_bus, 0); sigaction(SIGABRT, &_abrt, 0); sigaction(SIGILL, &_ill, 0); } BackTrace::~BackTrace() { BackTrace::Close(); } #endif ================================================ FILE: src/BackTrace.h ================================================ /* * The MIT License (MIT) * * Copyright (c) 2015 vmolsa (http://github.com/vmolsa) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * */ #ifndef BACKTRACE_H #define BACKTRACE_H #ifdef USE_BACKTRACE #include #include #include #include #include class BackTrace { public: static void Dump(const char *event = "NOEVENT", int skip = 1); private: explicit BackTrace(); virtual ~BackTrace(); void Close(); static void OnSegv(int sig); static void OnBus(int sig); static void OnAbort(int sig); static void OnIll(int sig); protected: struct sigaction _segv; struct sigaction _bus; struct sigaction _abrt; struct sigaction _ill; static BackTrace landmine; }; #else #endif #endif ================================================ FILE: src/Common.h ================================================ /* * The MIT License (MIT) * * Copyright (c) 2015 vmolsa (http://github.com/vmolsa) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * */ #ifndef WEBRTC_COMMON_H #define WEBRTC_COMMON_H #include #include "webrtc/video_frame.h" #include "webrtc/base/atomicops.h" #include "webrtc/base/logging.h" #include "webrtc/base/json.h" #include "webrtc/base/basictypes.h" #include "webrtc/base/common.h" #include "webrtc/base/scoped_ptr.h" #include "webrtc/base/ssladapter.h" #include "webrtc/base/sslstreamadapter.h" #include "webrtc/base/stringutils.h" #include "webrtc/base/thread.h" #include "webrtc/base/buffer.h" #include "webrtc/base/scoped_ref_ptr.h" #include "webrtc/base/refcount.h" #include "webrtc/base/stringencode.h" #include "webrtc/api/jsep.h" #include "webrtc/api/jsepsessiondescription.h" #include "webrtc/api/mediaconstraintsinterface.h" #include "webrtc/api/mediastreaminterface.h" #include "webrtc/api/peerconnectionfactory.h" #include "webrtc/api/peerconnectioninterface.h" #include "webrtc/api/test/fakeconstraints.h" #include "webrtc/api/datachannelinterface.h" #include "webrtc/api/videosourceinterface.h" #include "webrtc/media/base/videosourceinterface.h" #include "webrtc/media/engine/webrtcvideocapturerfactory.h" #include "webrtc/modules/video_capture/video_capture_factory.h" #ifdef WIN32 #ifndef __PRETTY_FUNCTION__ #define __PRETTY_FUNCTION__ __FUNCTION__ #endif #endif #include #include #include #include #endif ================================================ FILE: src/DataChannel.cc ================================================ /* * The MIT License (MIT) * * Copyright (c) 2015 vmolsa (http://github.com/vmolsa) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * */ #include "DataChannel.h" using namespace v8; using namespace WebRTC; Nan::Persistent DataChannel::constructor; void DataChannel::Init() { LOG(LS_INFO) << __PRETTY_FUNCTION__; Nan::HandleScope scope; Local tpl = Nan::New(DataChannel::New); tpl->InstanceTemplate()->SetInternalFieldCount(1); tpl->SetClassName(Nan::New("RTCDataChannel").ToLocalChecked()); Nan::SetPrototypeMethod(tpl, "close", DataChannel::Close); Nan::SetPrototypeMethod(tpl, "send", DataChannel::Send); Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("id").ToLocalChecked(), DataChannel::GetId); Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("label").ToLocalChecked(), DataChannel::GetLabel); Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("ordered").ToLocalChecked(), DataChannel::GetOrdered); Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("protocol").ToLocalChecked(), DataChannel::GetProtocol); Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("readyState").ToLocalChecked(), DataChannel::GetReadyState); Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("bufferedAmount").ToLocalChecked(), DataChannel::GetBufferedAmount); Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("binaryType").ToLocalChecked(), DataChannel::GetBinaryType, DataChannel::SetBinaryType); Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("maxPacketLifeType").ToLocalChecked(), DataChannel::GetMaxPacketLifeType); Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("maxRetransmits").ToLocalChecked(), DataChannel::GetMaxRetransmits); Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("negotiated").ToLocalChecked(), DataChannel::GetNegotiated); Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("reliable").ToLocalChecked(), DataChannel::GetReliable); Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("onopen").ToLocalChecked(), DataChannel::GetOnOpen, DataChannel::SetOnOpen); Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("onmessage").ToLocalChecked(), DataChannel::GetOnMessage, DataChannel::SetOnMessage); Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("onclose").ToLocalChecked(), DataChannel::GetOnClose, DataChannel::SetOnClose); Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("onerror").ToLocalChecked(), DataChannel::GetOnError, DataChannel::SetOnError); constructor.Reset(tpl->GetFunction()); } DataChannel::DataChannel() { LOG(LS_INFO) << __PRETTY_FUNCTION__; _observer = new rtc::RefCountedObject(this); } DataChannel::~DataChannel() { LOG(LS_INFO) << __PRETTY_FUNCTION__; if (_socket.get()) { _socket->UnregisterObserver(); _observer->RemoveListener(this); webrtc::DataChannelInterface::DataState state(_socket->state()); if (state != webrtc::DataChannelInterface::kClosing || state != webrtc::DataChannelInterface::kClosed) { _socket->Close(); } } } void DataChannel::New(const Nan::FunctionCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; if (info.IsConstructCall()) { DataChannel* dataChannel = new DataChannel(); dataChannel->Wrap(info.This(), "DataChannel"); return info.GetReturnValue().Set(info.This()); } Nan::ThrowError("Internal Error"); info.GetReturnValue().SetUndefined(); } Local DataChannel::New(rtc::scoped_refptr dataChannel) { LOG(LS_INFO) << __PRETTY_FUNCTION__; Nan::EscapableHandleScope scope; Local instance = Nan::New(DataChannel::constructor); if (instance.IsEmpty() || !dataChannel.get()) { return scope.Escape(Nan::Null()); } Local ret = instance->NewInstance(); DataChannel *self = RTCWrap::Unwrap(ret, "DataChannel"); self->SetReference(true); self->_socket = dataChannel; self->_socket->RegisterObserver(self->_observer.get()); self->Emit(kDataChannelStateChange); self->_binaryType.Reset(Nan::New("arraybuffer").ToLocalChecked()); return scope.Escape(ret); } webrtc::DataChannelInterface *DataChannel::GetSocket() const { LOG(LS_INFO) << __PRETTY_FUNCTION__; return _socket.get(); } void DataChannel::Close(const Nan::FunctionCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; DataChannel *self = RTCWrap::Unwrap(info.This(), "DataChannel"); webrtc::DataChannelInterface *socket = self->GetSocket(); if (socket) { webrtc::DataChannelInterface::DataState state(socket->state()); if (state != webrtc::DataChannelInterface::kClosing || state != webrtc::DataChannelInterface::kClosed) { socket->Close(); } } info.GetReturnValue().SetUndefined(); } void DataChannel::Send(const Nan::FunctionCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; DataChannel *self = RTCWrap::Unwrap(info.This(), "DataChannel"); webrtc::DataChannelInterface *socket = self->GetSocket(); bool retval = false; if (socket) { if(info[0]->IsString()) { std::string data(*Nan::Utf8String(info[0])); webrtc::DataBuffer buffer(data); retval = socket->Send(buffer); } else { node::ArrayBuffer *container = node::ArrayBuffer::New(info[0]); rtc::Buffer data(reinterpret_cast(container->Data()), container->Length()); //rtc::CopyOnWriteBuffer data(reinterpret_cast(container->Data()), container->Length()); webrtc::DataBuffer buffer(data, true); retval = socket->Send(buffer); } } return info.GetReturnValue().Set(Nan::New(retval)); } void DataChannel::GetId(Local property, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; DataChannel *self = RTCWrap::Unwrap(info.Holder(), "DataChannel"); webrtc::DataChannelInterface *socket = self->GetSocket(); if (socket) { return info.GetReturnValue().Set(Nan::New(socket->id())); } info.GetReturnValue().SetUndefined(); } void DataChannel::GetLabel(Local property, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; DataChannel *self = RTCWrap::Unwrap(info.Holder(), "DataChannel"); webrtc::DataChannelInterface *socket = self->GetSocket(); if (socket) { return info.GetReturnValue().Set(Nan::New(socket->label().c_str()).ToLocalChecked()); } info.GetReturnValue().SetUndefined(); } void DataChannel::GetOrdered(Local property, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; DataChannel *self = RTCWrap::Unwrap(info.Holder(), "DataChannel"); webrtc::DataChannelInterface *socket = self->GetSocket(); if (socket) { return info.GetReturnValue().Set(Nan::New(socket->ordered())); } info.GetReturnValue().SetUndefined(); } void DataChannel::GetProtocol(Local property, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; DataChannel *self = RTCWrap::Unwrap(info.Holder(), "DataChannel"); webrtc::DataChannelInterface *socket = self->GetSocket(); if (socket) { return info.GetReturnValue().Set(Nan::New(socket->protocol().c_str()).ToLocalChecked()); } info.GetReturnValue().SetUndefined(); } void DataChannel::GetReadyState(Local property, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; DataChannel *self = RTCWrap::Unwrap(info.Holder(), "DataChannel"); webrtc::DataChannelInterface *socket = self->GetSocket(); if (socket) { webrtc::DataChannelInterface::DataState state(socket->state()); switch (state) { case webrtc::DataChannelInterface::kConnecting: return info.GetReturnValue().Set(Nan::New("connecting").ToLocalChecked()); break; case webrtc::DataChannelInterface::kOpen: return info.GetReturnValue().Set(Nan::New("open").ToLocalChecked()); break; case webrtc::DataChannelInterface::kClosing: return info.GetReturnValue().Set(Nan::New("closing").ToLocalChecked()); break; case webrtc::DataChannelInterface::kClosed: return info.GetReturnValue().Set(Nan::New("closed").ToLocalChecked()); break; } } info.GetReturnValue().SetUndefined(); } void DataChannel::GetBufferedAmount(Local property, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; DataChannel *self = RTCWrap::Unwrap(info.Holder(), "DataChannel"); webrtc::DataChannelInterface *socket = self->GetSocket(); if (socket) { return info.GetReturnValue().Set(Nan::New(static_cast(socket->buffered_amount()))); } info.GetReturnValue().SetUndefined(); } void DataChannel::GetBinaryType(Local property, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; DataChannel *self = RTCWrap::Unwrap(info.Holder(), "DataChannel"); return info.GetReturnValue().Set(Nan::New(self->_binaryType)); } void DataChannel::GetMaxPacketLifeType(Local property, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; DataChannel *self = RTCWrap::Unwrap(info.Holder(), "DataChannel"); webrtc::DataChannelInterface *socket = self->GetSocket(); if (socket) { return info.GetReturnValue().Set(Nan::New(static_cast(socket->maxRetransmitTime()))); } info.GetReturnValue().SetUndefined(); } void DataChannel::GetMaxRetransmits(Local property, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; DataChannel *self = RTCWrap::Unwrap(info.Holder(), "DataChannel"); webrtc::DataChannelInterface *socket = self->GetSocket(); if (socket) { return info.GetReturnValue().Set(Nan::New(static_cast(socket->maxRetransmits()))); } info.GetReturnValue().SetUndefined(); } void DataChannel::GetNegotiated(Local property, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; DataChannel *self = RTCWrap::Unwrap(info.Holder(), "DataChannel"); webrtc::DataChannelInterface *socket = self->GetSocket(); if (socket) { return info.GetReturnValue().Set(Nan::New(socket->negotiated())); } return info.GetReturnValue().Set(Nan::False()); } void DataChannel::GetReliable(Local property, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; DataChannel *self = RTCWrap::Unwrap(info.Holder(), "DataChannel"); webrtc::DataChannelInterface *socket = self->GetSocket(); if (socket) { return info.GetReturnValue().Set(Nan::New(socket->reliable())); } return info.GetReturnValue().Set(Nan::False()); } void DataChannel::GetOnOpen(Local property, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; DataChannel *self = RTCWrap::Unwrap(info.Holder(), "DataChannel"); return info.GetReturnValue().Set(Nan::New(self->_onopen)); } void DataChannel::GetOnMessage(Local property, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; DataChannel *self = RTCWrap::Unwrap(info.Holder(), "DataChannel"); return info.GetReturnValue().Set(Nan::New(self->_onmessage)); } void DataChannel::GetOnClose(Local property, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; DataChannel *self = RTCWrap::Unwrap(info.Holder(), "DataChannel"); return info.GetReturnValue().Set(Nan::New(self->_onclose)); } void DataChannel::GetOnError(Local property, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; DataChannel *self = RTCWrap::Unwrap(info.Holder(), "DataChannel"); return info.GetReturnValue().Set(Nan::New(self->_onerror)); } void DataChannel::ReadOnly(Local property, Local value, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; } void DataChannel::SetBinaryType(Local property, Local value, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; DataChannel *self = RTCWrap::Unwrap(info.Holder(), "DataChannel"); if (!value.IsEmpty() && value->IsString()) { self->_binaryType.Reset(value->ToString()); } else { self->_binaryType.Reset(Nan::New("arraybuffer").ToLocalChecked()); } } void DataChannel::SetOnOpen(Local property, Local value, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; DataChannel *self = RTCWrap::Unwrap(info.Holder(), "DataChannel"); if (!value.IsEmpty() && value->IsFunction()) { self->_onopen.Reset(Local::Cast(value)); } else { self->_onopen.Reset(); } } void DataChannel::SetOnMessage(Local property, Local value, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; DataChannel *self = RTCWrap::Unwrap(info.Holder(), "DataChannel"); if (!value.IsEmpty() && value->IsFunction()) { self->_onmessage.Reset(Local::Cast(value)); } else { self->_onmessage.Reset(); } } void DataChannel::SetOnClose(Local property, Local value, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; DataChannel *self = RTCWrap::Unwrap(info.Holder(), "DataChannel"); if (!value.IsEmpty() && value->IsFunction()) { self->_onclose.Reset(Local::Cast(value)); } else { self->_onclose.Reset(); } } void DataChannel::SetOnError(Local property, Local value, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; DataChannel *self = RTCWrap::Unwrap(info.Holder(), "DataChannel"); if (!value.IsEmpty() && value->IsFunction()) { self->_onerror.Reset(Local::Cast(value)); } else { self->_onclose.Reset(); } } void DataChannel::On(Event *event) { LOG(LS_INFO) << __PRETTY_FUNCTION__; Nan::HandleScope scope; DataChannelEvent type = event->Type(); node::ArrayBuffer *arrayBuffer = 0; Local callback; Local argv[1]; bool isError = false; int argc = 0; if (type == kDataChannelStateChange) { webrtc::DataChannelInterface *socket = DataChannel::GetSocket(); if (socket) { switch (socket->state()) { case webrtc::DataChannelInterface::kConnecting: break; case webrtc::DataChannelInterface::kOpen: callback = Nan::New(_onopen); break; case webrtc::DataChannelInterface::kClosing: break; case webrtc::DataChannelInterface::kClosed: EventEmitter::SetReference(false); callback = Nan::New(_onclose); _onclose.Reset(); break; } } } else { callback = Nan::New(_onmessage); rtc::Buffer buffer = event->Unwrap(); Local container = Nan::New(); argv[0] = container; argc = 1; if (type == kDataChannelData) { container->Set(Nan::New("data").ToLocalChecked(), Nan::New(reinterpret_cast(buffer.data()), buffer.size()).ToLocalChecked()); } else { arrayBuffer = node::ArrayBuffer::New(buffer.data(), buffer.size()); container->Set(Nan::New("data").ToLocalChecked(), arrayBuffer->ToArrayBuffer()); } } if (!callback.IsEmpty() && callback->IsFunction()) { callback->Call(RTCWrap::This(), argc, argv); } else if (isError) { Nan::ThrowError(argv[0]); } } ================================================ FILE: src/DataChannel.h ================================================ /* * The MIT License (MIT) * * Copyright (c) 2015 vmolsa (http://github.com/vmolsa) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * */ #ifndef WEBRTC_DATACHANNEL_H #define WEBRTC_DATACHANNEL_H #include "Common.h" #include "Observers.h" #include "EventEmitter.h" #include "Wrap.h" #include "ArrayBuffer.h" namespace WebRTC { enum DataChannelEvent { kDataChannelStateChange, kDataChannelBinary, kDataChannelData, }; class DataChannel : public RTCWrap, public EventEmitter { public: static void Init(); static v8::Local New(rtc::scoped_refptr dataChannel); private: DataChannel(); ~DataChannel() final; static void New(const Nan::FunctionCallbackInfo &info); static void Close(const Nan::FunctionCallbackInfo &info); static void Send(const Nan::FunctionCallbackInfo &info); static void GetId(v8::Local property, const Nan::PropertyCallbackInfo &info); static void GetLabel(v8::Local property, const Nan::PropertyCallbackInfo &info); static void GetOrdered(v8::Local property, const Nan::PropertyCallbackInfo &info); static void GetProtocol(v8::Local property, const Nan::PropertyCallbackInfo &info); static void GetReadyState(v8::Local property, const Nan::PropertyCallbackInfo &info); static void GetBufferedAmount(v8::Local property, const Nan::PropertyCallbackInfo &info); static void GetBinaryType(v8::Local property, const Nan::PropertyCallbackInfo &info); static void GetMaxPacketLifeType(v8::Local property, const Nan::PropertyCallbackInfo &info); static void GetMaxRetransmits(v8::Local property, const Nan::PropertyCallbackInfo &info); static void GetNegotiated(v8::Local property, const Nan::PropertyCallbackInfo &info); static void GetReliable(v8::Local property, const Nan::PropertyCallbackInfo &info); static void GetOnOpen(v8::Local property, const Nan::PropertyCallbackInfo &info); static void GetOnMessage(v8::Local property, const Nan::PropertyCallbackInfo &info); static void GetOnClose(v8::Local property, const Nan::PropertyCallbackInfo &info); static void GetOnError(v8::Local property, const Nan::PropertyCallbackInfo &info); static void ReadOnly(v8::Local property, v8::Local value, const Nan::PropertyCallbackInfo &info); static void SetBinaryType(v8::Local property, v8::Local value, const Nan::PropertyCallbackInfo &info); static void SetOnOpen(v8::Local property, v8::Local value, const Nan::PropertyCallbackInfo &info); static void SetOnMessage(v8::Local property, v8::Local value, const Nan::PropertyCallbackInfo &info); static void SetOnClose(v8::Local property, v8::Local value, const Nan::PropertyCallbackInfo &info); static void SetOnError(v8::Local property, v8::Local value, const Nan::PropertyCallbackInfo &info); void On(Event *event) final; webrtc::DataChannelInterface *GetSocket() const; protected: rtc::scoped_refptr _observer; rtc::scoped_refptr _socket; Nan::Persistent _binaryType; Nan::Persistent _onopen; Nan::Persistent _onmessage; Nan::Persistent _onclose; Nan::Persistent _onerror; static Nan::Persistent constructor; }; }; #endif ================================================ FILE: src/EventEmitter.cc ================================================ /* * The MIT License (MIT) * * Copyright (c) 2015 vmolsa (http://github.com/vmolsa) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * */ #include "EventEmitter.h" using namespace WebRTC; EventEmitter::EventEmitter(uv_loop_t *loop, bool notify) : _notify(notify) { LOG(LS_INFO) << __PRETTY_FUNCTION__; uv_mutex_init(&_list); if (!_notify) { uv_mutex_init(&_lock); _async = new uv_async_t(); _async->data = this; if (!loop) { loop = uv_default_loop(); } uv_async_init(loop, _async, reinterpret_cast(EventEmitter::onAsync)); EventEmitter::SetReference(false); } } EventEmitter::~EventEmitter() { LOG(LS_INFO) << __PRETTY_FUNCTION__; EventEmitter::RemoveAllListeners(); EventEmitter::Dispose(); if (!_notify) { _async->data = 0; uv_close(reinterpret_cast(_async), EventEmitter::onEnded); uv_mutex_destroy(&_lock); } uv_mutex_destroy(&_list); } void EventEmitter::AddListener(EventEmitter *listener) { LOG(LS_INFO) << __PRETTY_FUNCTION__; bool found = false; std::vector::iterator index; if (listener && listener != this) { uv_mutex_lock(&_list); for (index = _listeners.begin(); index < _listeners.end(); index++) { if ((*index) == listener) { found = true; break; } } if (!found) { _listeners.push_back(listener); listener->AddParent(this); } uv_mutex_unlock(&_list); } } void EventEmitter::RemoveListener(EventEmitter *listener) { LOG(LS_INFO) << __PRETTY_FUNCTION__; std::vector::iterator index; if (listener && listener != this) { uv_mutex_lock(&_list); for (index = _listeners.begin(); index < _listeners.end(); index++) { if ((*index) == listener) { _listeners.erase(index); listener->RemoveParent(this); break; } } uv_mutex_unlock(&_list); } } void EventEmitter::RemoveAllListeners() { LOG(LS_INFO) << __PRETTY_FUNCTION__; std::vector::iterator index; uv_mutex_lock(&_list); for (index = _listeners.begin(); index < _listeners.end(); index++) { (*index)->RemoveParent(this); _listeners.erase(index); } uv_mutex_unlock(&_list); } void EventEmitter::Dispose() { LOG(LS_INFO) << __PRETTY_FUNCTION__; if (!_notify) { while (!_events.empty()) { rtc::scoped_refptr event = _events.front(); _events.pop(); } } } void EventEmitter::SetReference(bool alive) { LOG(LS_INFO) << __PRETTY_FUNCTION__; if (!_notify) { uv_mutex_lock(&_lock); if (alive) { uv_ref(reinterpret_cast(_async)); } else { uv_unref(reinterpret_cast(_async)); } uv_mutex_unlock(&_lock); } } void EventEmitter::Emit(int event) { LOG(LS_INFO) << __PRETTY_FUNCTION__; EventEmitter::Emit(new rtc::RefCountedObject(event)); } void EventEmitter::Emit(rtc::scoped_refptr event) { LOG(LS_INFO) << __PRETTY_FUNCTION__; if (event.get()) { if (!_notify) { uv_mutex_lock(&_lock); _events.push(event); uv_async_send(_async); uv_mutex_unlock(&_lock); } uv_mutex_lock(&_list); std::vector::iterator index; for (index = _listeners.begin(); index < _listeners.end(); index++) { (*index)->Emit(event); } uv_mutex_unlock(&_list); } } void EventEmitter::AddParent(EventEmitter *listener) { LOG(LS_INFO) << __PRETTY_FUNCTION__; uv_mutex_lock(&_list); _parents.push_back(listener); uv_mutex_unlock(&_list); } void EventEmitter::RemoveParent(EventEmitter *listener) { LOG(LS_INFO) << __PRETTY_FUNCTION__; std::vector::iterator index; uv_mutex_lock(&_list); for (index = _listeners.begin(); index < _listeners.end(); index++) { if ((*index) == listener) { _listeners.erase(index); } } uv_mutex_unlock(&_list); } void EventEmitter::onAsync(uv_async_t *handle, int status) { LOG(LS_INFO) << __PRETTY_FUNCTION__; EventEmitter *self = static_cast(handle->data); if (self) { self->DispatchEvents(); } } void EventEmitter::onEnded(uv_handle_t *handle) { LOG(LS_INFO) << __PRETTY_FUNCTION__; uv_async_t* async = reinterpret_cast(handle); if (async) { delete async; } } void EventEmitter::DispatchEvents() { LOG(LS_INFO) << __PRETTY_FUNCTION__; uv_mutex_lock(&_lock); while (!_events.empty()) { rtc::scoped_refptr event = _events.front(); _events.pop(); uv_mutex_unlock(&_lock); if (event.get()) { On(event); } uv_mutex_lock(&_lock); } uv_mutex_unlock(&_lock); } NotifyEmitter::NotifyEmitter(EventEmitter *listener) : EventEmitter(0, true) { LOG(LS_INFO) << __PRETTY_FUNCTION__; if (listener) { NotifyEmitter::AddListener(listener); } } void NotifyEmitter::On(Event *event) { LOG(LS_INFO) << __PRETTY_FUNCTION__; } ================================================ FILE: src/EventEmitter.h ================================================ /* * The MIT License (MIT) * * Copyright (c) 2015 vmolsa (http://github.com/vmolsa) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * */ #ifndef WEBRTC_EVENTEMITTER_H #define WEBRTC_EVENTEMITTER_H #include "Common.h" namespace WebRTC { template class EventWrapper; class Event : public rtc::RefCountInterface { template friend class EventWrapper; friend class rtc::RefCountedObject; friend class EventEmitter; public: inline bool HasWrap() const { LOG(LS_INFO) << __PRETTY_FUNCTION__; return _wrap; } template inline T Type() const { LOG(LS_INFO) << __PRETTY_FUNCTION__; return static_cast(_event); } template const T &Unwrap() const { LOG(LS_INFO) << __PRETTY_FUNCTION__; static T nowrap; if (_wrap) { const EventWrapper *ptr = static_cast *>(this); return ptr->_content; } nowrap = T(); return nowrap; } private: explicit Event(int event = 0) : _event(event), _wrap(false) { LOG(LS_INFO) << __PRETTY_FUNCTION__; } virtual ~Event() { LOG(LS_INFO) << __PRETTY_FUNCTION__; } protected: int _event; bool _wrap; }; template class EventWrapper : public Event { friend class rtc::RefCountedObject >; friend class Event; friend class EventEmitter; private: explicit EventWrapper(int event, const T &content) : Event(event), _content(content) { _wrap = true; } virtual ~EventWrapper() { } protected: T _content; }; class EventEmitter { friend class NotifyEmitter; public: explicit EventEmitter(uv_loop_t *loop = 0, bool notify = false); virtual ~EventEmitter(); void AddListener(EventEmitter *listener = 0); void RemoveListener(EventEmitter *listener = 0); void RemoveAllListeners(); void SetReference(bool alive = true); void Emit(int event = 0); void Emit(rtc::scoped_refptr event); template inline void Emit(int event, const T &content) { LOG(LS_INFO) << __PRETTY_FUNCTION__; EventEmitter::Emit(new rtc::RefCountedObject >(event, content)); } virtual void On(Event *event) = 0; private: static void onAsync(uv_async_t *handle, int status); static void onEnded(uv_handle_t *handle); void Dispose(); void DispatchEvents(); void AddParent(EventEmitter *listener = 0); void RemoveParent(EventEmitter *listener = 0); protected: bool _notify; uv_mutex_t _lock; uv_mutex_t _list; uv_async_t* _async; std::queue > _events; std::vector _listeners; std::vector _parents; }; class NotifyEmitter : public EventEmitter { public: NotifyEmitter(EventEmitter *listener = 0); virtual void On(Event *event); }; }; #endif ================================================ FILE: src/GetSources.cc ================================================ /* * The MIT License (MIT) * * Copyright (c) 2015 vmolsa (http://github.com/vmolsa) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * */ #include #include "Platform.h" #include "MediaStreamTrack.h" #include "GetSources.h" using namespace v8; using namespace WebRTC; void GetSources::Init(Handle exports) { LOG(LS_INFO) << __PRETTY_FUNCTION__; exports->Set(Nan::New("getSources").ToLocalChecked(), Nan::New(GetSources::GetDevices)->GetFunction()); exports->Set(Nan::New("getVideoSource").ToLocalChecked(), Nan::New(GetSources::GetVideoSource2)->GetFunction()); } rtc::scoped_refptr GetSources::GetAudioSource(const rtc::scoped_refptr &constraints) { LOG(LS_INFO) << __PRETTY_FUNCTION__; rtc::scoped_refptr track; rtc::scoped_refptr factory = webrtc::CreatePeerConnectionFactory(rtc::Thread::Current(), Platform::GetWorker(), 0, 0, 0); if (factory.get()) { track = factory->CreateAudioTrack("audio", factory->CreateAudioSource(constraints->ToConstraints())); } return track; } rtc::scoped_refptr GetSources::GetAudioSource(const std::string id, const rtc::scoped_refptr &constraints) { LOG(LS_INFO) << __PRETTY_FUNCTION__; return GetSources::GetAudioSource(constraints); } rtc::scoped_refptr GetSources::GetVideoSource(const rtc::scoped_refptr &constraints) { LOG(LS_INFO) << __PRETTY_FUNCTION__; cricket::VideoCapturer* capturer = nullptr; rtc::scoped_refptr track; rtc::scoped_refptr factory = webrtc::CreatePeerConnectionFactory(rtc::Thread::Current(), Platform::GetWorker(), 0, 0, 0); std::unique_ptr video_info(webrtc::VideoCaptureFactory::CreateDeviceInfo(0)); cricket::WebRtcVideoDeviceCapturerFactory device_factory; if (factory.get()) { if (video_info) { int num_devices = video_info->NumberOfDevices(); for (int i = 0; i < num_devices; ++i) { const uint32_t kSize = 256; char name[kSize] = {0}; char id[kSize] = {0}; if (video_info->GetDeviceName(i, name, kSize, id, kSize) != -1) { capturer = device_factory.Create(cricket::Device(name, 0)); if (capturer) { for(int i = 0; name[i]; i++){ if (name[i]==' ') name[i] = '_'; name[i] = tolower(name[i]); } track = factory->CreateVideoTrack(name, factory->CreateVideoSource(capturer, constraints->ToConstraints())); return track; } } } } } return track; } rtc::scoped_refptr GetSources::GetVideoSource(const std::string id_name, const rtc::scoped_refptr &constraints) { LOG(LS_INFO) << __PRETTY_FUNCTION__; cricket::VideoCapturer* capturer = nullptr; rtc::scoped_refptr track; rtc::scoped_refptr factory = webrtc::CreatePeerConnectionFactory(rtc::Thread::Current(), Platform::GetWorker(), 0, 0, 0); std::unique_ptr video_info(webrtc::VideoCaptureFactory::CreateDeviceInfo(0)); cricket::WebRtcVideoDeviceCapturerFactory device_factory; if (factory.get()) { if (video_info) { int num_devices = video_info->NumberOfDevices(); for (int i = 0; i < num_devices; ++i) { const uint32_t kSize = 256; char name[kSize] = {0}; char id[kSize] = {0}; if (video_info->GetDeviceName(i, name, kSize, id, kSize) != -1) { if (id_name.empty() || id_name.compare(name) == 0) { capturer = device_factory.Create(cricket::Device(name, 0)); if (capturer) { for(int i = 0; name[i]; i++){ if (name[i]==' ') name[i] = '_'; name[i] = tolower(name[i]); } track = factory->CreateVideoTrack(name, factory->CreateVideoSource(capturer, constraints->ToConstraints())); return track; } } } } } } return track; } Local GetSources::GetDevices() { LOG(LS_INFO) << __PRETTY_FUNCTION__; Nan::EscapableHandleScope scope; Local list = Nan::New(); uint32_t index = 0; std::unique_ptr video_info(webrtc::VideoCaptureFactory::CreateDeviceInfo(0)); if (video_info) { int num_devices = video_info->NumberOfDevices(); for (int i = 0; i < num_devices; ++i) { const uint32_t kSize = 256; char name[kSize] = {0}; char id[kSize] = {0}; if (video_info->GetDeviceName(i, name, kSize, id, kSize) != -1) { Local device = Nan::New(); device->Set(Nan::New("kind").ToLocalChecked(), Nan::New("video").ToLocalChecked()); device->Set(Nan::New("label").ToLocalChecked(), Nan::New(name).ToLocalChecked()); device->Set(Nan::New("id").ToLocalChecked(), Nan::New(id).ToLocalChecked()); list->Set(index, device); index++; } } } return scope.Escape(list); } void GetSources::GetVideoSource2(const Nan::FunctionCallbackInfo &info) { Nan::Utf8String name_id(info[0]->ToString()); rtc::scoped_refptr constraints = MediaConstraints::New(info[1]); rtc::scoped_refptr track = GetSources::GetVideoSource(*name_id,constraints); info.GetReturnValue().Set(MediaStreamTrack::New(track)); } void GetSources::GetDevices(const Nan::FunctionCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; if (info.Length() == 1 && info[0]->IsFunction()) { Local callback = Local::Cast(info[0]); Local argv[1] = { GetSources::GetDevices() }; if (!callback.IsEmpty()) { callback->Call(info.This(), 1, argv); } } info.GetReturnValue().SetUndefined(); } ================================================ FILE: src/GetSources.h ================================================ /* * The MIT License (MIT) * * Copyright (c) 2015 vmolsa (http://github.com/vmolsa) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * */ #ifndef WEBRTC_GETSOURCES_H #define WEBRTC_GETSOURCES_H #include "Common.h" #include "MediaConstraints.h" namespace WebRTC { class GetSources { public: static void Init(v8::Handle exports); static rtc::scoped_refptr GetAudioSource(const rtc::scoped_refptr &constraints); static rtc::scoped_refptr GetAudioSource(const std::string id, const rtc::scoped_refptr &constraints); static rtc::scoped_refptr GetVideoSource(const rtc::scoped_refptr &constraints); static rtc::scoped_refptr GetVideoSource(const std::string id, const rtc::scoped_refptr &constraints); static v8::Local GetDevices(); private: static void GetVideoSource2(const Nan::FunctionCallbackInfo &info); static void GetDevices(const Nan::FunctionCallbackInfo &info); }; }; #endif ================================================ FILE: src/GetUserMedia.cc ================================================ /* * The MIT License (MIT) * * Copyright (c) 2015 vmolsa (http://github.com/vmolsa) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * */ #include "Platform.h" #include "GetUserMedia.h" #include "GetSources.h" #include "MediaStream.h" #include "MediaConstraints.h" using namespace v8; using namespace WebRTC; void GetUserMedia::Init(Handle exports) { LOG(LS_INFO) << __PRETTY_FUNCTION__; exports->Set(Nan::New("getUserMedia").ToLocalChecked(), Nan::New(GetUserMedia::GetMediaStream)->GetFunction()); } void GetUserMedia::GetMediaStream(const Nan::FunctionCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; rtc::scoped_refptr stream; rtc::scoped_refptr constraints = MediaConstraints::New(info[0]); const char *error = 0; bool have_source = false; std::string audioId = constraints->AudioId(); std::string videoId = constraints->VideoId(); if (constraints->UseAudio() || constraints->UseVideo()) { rtc::scoped_refptr factory = webrtc::CreatePeerConnectionFactory(rtc::Thread::Current(), Platform::GetWorker(), 0, 0, 0); if (factory.get()) { stream = factory->CreateLocalMediaStream("stream"); if (stream.get()) { if (constraints->UseAudio()) { rtc::scoped_refptr audio_track; if (audioId.empty()) { audio_track = GetSources::GetAudioSource(constraints); } else { audio_track = GetSources::GetAudioSource(audioId, constraints); } if (audio_track.get()) { if (!stream->AddTrack(audio_track)) { error = "Invalid Audio Input"; } else { have_source = true; } } else { if (!audioId.empty()) { error = "Invalid Audio Input"; } } } if (constraints->UseVideo()) { rtc::scoped_refptr video_track; if (videoId.empty()) { video_track = GetSources::GetVideoSource(constraints); } else { video_track = GetSources::GetVideoSource(videoId, constraints); } if (video_track.get()) { if (!stream->AddTrack(video_track)) { error = "Invalid Video Input"; } else { have_source = true; } } else { if (!videoId.empty()) { error = "Invalid Video Input"; } } } } else { error = "Internal Error"; } } } if (!have_source) { error = "No available inputs"; } Handle argv[1]; if (!error) { if (stream.get()) { argv[0] = MediaStream::New(stream); } else { error = "Invalid MediaStream"; } } if (error) { if (!info[2].IsEmpty() && info[2]->IsFunction()) { Local onerror = Local::Cast(info[2]); argv[0] = Nan::Error(error); onerror->Call(info.This(), 1, argv); } else { Nan::ThrowError(error); } } else { if (!info[1].IsEmpty() && info[1]->IsFunction()) { Local onsuccess = Local::Cast(info[1]); onsuccess->Call(info.This(), 1, argv); } } info.GetReturnValue().SetUndefined(); } ================================================ FILE: src/GetUserMedia.h ================================================ /* * The MIT License (MIT) * * Copyright (c) 2015 vmolsa (http://github.com/vmolsa) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * */ #ifndef WEBRTC_GETUSERMEDIA_H #define WEBRTC_GETUSERMEDIA_H #include "Common.h" namespace WebRTC { class GetUserMedia { public: static void Init(v8::Handle exports); private: static void GetMediaStream(const Nan::FunctionCallbackInfo &info); }; }; #endif ================================================ FILE: src/Global.cc ================================================ /* * The MIT License (MIT) * * Copyright (c) 2015 vmolsa (http://github.com/vmolsa) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * */ #include "Global.h" using namespace v8; using namespace WebRTC; Nan::Persistent _require; #if (NODE_MODULE_VERSION <= NODE_0_10_MODULE_VERSION) Nan::Persistent _parse; #endif void WebRTC::Global::Init(Handle exports) { LOG(LS_INFO) << __PRETTY_FUNCTION__; Nan::HandleScope scope; Local global = Nan::GetCurrentContext()->Global(); _require.Reset(Local::Cast(global->Get(Nan::New("require").ToLocalChecked()))); #if (NODE_MODULE_VERSION <= NODE_0_10_MODULE_VERSION) Local json = Local::Cast(global->Get(Nan::New("JSON").ToLocalChecked())); _parse.Reset(Local::Cast(json->Get(Nan::New("parse").ToLocalChecked()))); #endif } Local WebRTC::Global::Require(Local library) { Nan::EscapableHandleScope scope; Local argv[] = { library }; Local retval = Nan::MakeCallback(Nan::GetCurrentContext()->Global(), Nan::New(_require), 1, argv); return scope.Escape(Local::Cast(retval)); } #if (NODE_MODULE_VERSION <= NODE_0_10_MODULE_VERSION) Local JSON::Parse(Local str) { Nan::EscapableHandleScope scope; Local argv[] = { str }; return scope.Escape(Nan::MakeCallback(Nan::GetCurrentContext()->Global(), Nan::New(_parse), 1, argv)); }; #endif ================================================ FILE: src/Global.h ================================================ /* * The MIT License (MIT) * * Copyright (c) 2015 vmolsa (http://github.com/vmolsa) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * */ #ifndef WEBRTC_GLOBAL_H #define WEBRTC_GLOBAL_H #include "Common.h" namespace WebRTC { class Global { public: static void Init(v8::Handle exports); static v8::Local Require(v8::Local library); }; }; #if (NODE_MODULE_VERSION <= NODE_0_10_MODULE_VERSION) namespace v8 { class JSON { public: static v8::Local Parse(v8::Local str); }; }; #endif #endif ================================================ FILE: src/MediaCapturer.cc ================================================ /* * The MIT License (MIT) * * Copyright (c) 2015 vmolsa (http://github.com/vmolsa) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * */ #include "MediaCapturer.h" using namespace v8; using namespace WebRTC; Nan::Persistent MediaCapturer::constructor; void MediaCapturer::Init() { LOG(LS_INFO) << __PRETTY_FUNCTION__; Nan::HandleScope scope; Local tpl = Nan::New(MediaCapturer::New); tpl->InstanceTemplate()->SetInternalFieldCount(1); tpl->SetClassName(Nan::New("MediaCapturer").ToLocalChecked()); Nan::SetPrototypeMethod(tpl, "stop", MediaCapturer::Stop); Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("data").ToLocalChecked(), MediaStreamTrack::GetOnData, MediaStreamTrack::SetOnData); constructor.Reset(tpl->GetFunction()); } Local MediaCapturer::New(webrtc::AudioSourceInterface *track) { LOG(LS_INFO) << __PRETTY_FUNCTION__; Nan::EscapableHandleScope scope; Local argv[1]; Local instance = Nan::New(MediaCapturer::constructor); if (instance.IsEmpty() || !track) { return scope.Escape(Nan::Null()); } Local ret = instance->NewInstance(0, argv); MediaCapturer *self = RTCWrap::Unwrap(ret, "MediaCapturer"); self->_audio = track; self->_source = track; self->_source->RegisterObserver(self->_observer.get()); self->_audio->AddSink(self); if (self->_audio->state() == webrtc::MediaSourceInterface::kLive) { self->SetReference(true); } return scope.Escape(ret); } Local MediaCapturer::New(webrtc::VideoSourceInterface *track) { LOG(LS_INFO) << __PRETTY_FUNCTION__; Nan::EscapableHandleScope scope; Local argv[1]; Local instance = Nan::New(MediaCapturer::constructor); if (instance.IsEmpty() || !track) { return scope.Escape(Nan::Null()); } Local ret = instance->NewInstance(0, argv); MediaCapturer *self = RTCWrap::Unwrap(ret, "MediaCapturer"); self->_video = track; self->_source = track; self->_source->RegisterObserver(self->_observer.get()); self->_video->AddOrUpdateSink(self, rtc::VideoSinkWants()); if (self->_video->state() == webrtc::MediaSourceInterface::kLive) { self->SetReference(true); } return scope.Escape(ret); } MediaCapturer::MediaCapturer() { LOG(LS_INFO) << __PRETTY_FUNCTION__; _observer = new rtc::RefCountedObject(this); } MediaCapturer::~MediaCapturer() { LOG(LS_INFO) << __PRETTY_FUNCTION__; if (_video.get()) { _video->RemoveSink(this); } if (_audio.get()) { _audio->RemoveSink(this); } if (_source.get()) { _source->UnregisterObserver(_observer.get()); } _observer->RemoveListener(this); } void MediaCapturer::New(const Nan::FunctionCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; Nan::HandleScope scope; if (info.IsConstructCall()) { MediaCapturer* MediaCapturer = new MediaCapturer(); MediaCapturer->Wrap(info.This(), "MediaCapturer"); return info.GetReturnValue().Set(info.This()); } Nan::ThrowError("Internal Error"); info.GetReturnValue().SetUndefined(); } void MediaCapturer::Stop(const Nan::FunctionCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; MediaCapturer *self = RTCWrap::Unwrap(info.This(), "MediaCapturer"); self->SetReference(false); return info.GetReturnValue().SetUndefined(); } void MediaCapturer::GetOnData(Local property, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; MediaCapturer *self = RTCWrap::Unwrap(info.Holder(), "MediaCapturer"); return info.GetReturnValue().Set(Nan::New(self->_ondata)); } void MediaCapturer::SetOnData(Local property, Local value, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; MediaCapturer *self = RTCWrap::Unwrap(info.Holder(), "MediaCapturer"); if (!value.IsEmpty() && value->IsFunction()) { self->_ondata.Reset(Local::Cast(value)); } else { self->_ondata.Reset(); } } void MediaCapturer::OnFrame(const cricket::VideoFrame &frame) { printf("Got Video Frame!\n"); } void MediaCapturer::OnData(const void* audio_data, int bits_per_sample, int sample_rate, size_t number_of_channels, size_t number_of_frames) { printf("Got Audio Frame!\n"); } void MediaCapturer::On(Event *event) { if (_source.get()) { if (_source->state() != webrtc::MediaSourceInterface::kLive) { EventEmitter::SetReference(false); } } } ================================================ FILE: src/MediaCapturer.h ================================================ /* * The MIT License (MIT) * * Copyright (c) 2015 vmolsa (http://github.com/vmolsa) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * */ #ifndef WEBRTC_MEDIACAPTURER_H #define WEBRTC_MEDIACAPTURER_H #include "Common.h" #include "Observers.h" #include "EventEmitter.h" #include "Wrap.h" namespace WebRTC { class MediaCapturer : public RTCWrap, public EventEmitter, public rtc::VideoSourceInterface, public webrtc::AudioTrackSinkInterface { public: static void Init(); static v8::Local New(webrtc::AudioSourceInterface *track = 0); static v8::Local New(webrtc::VideoSourceInterface *track = 0); private: MediaCapturer(); ~MediaCapturer() final; static void New(const Nan::FunctionCallbackInfo &info); static void Stop(const Nan::FunctionCallbackInfo &info); static void GetOnData(v8::Local property, const Nan::PropertyCallbackInfo &info); static void SetOnData(v8::Local property, v8::Local value, const Nan::PropertyCallbackInfo &info); void OnFrame(const cricket::VideoFrame &frame) final; void OnData(const void* audio_data, int bits_per_sample, int sample_rate, size_t number_of_channels, size_t number_of_frames) final; void On(Event *event) final; protected: rtc::scoped_refptr _video; rtc::scoped_refptr _audio; rtc::scoped_refptr _source; rtc::scoped_refptr _observer; Nan::Persistent _ondata; static Nan::Persistent constructor; }; }; #endif ================================================ FILE: src/MediaConstraints.cc ================================================ /* * The MIT License (MIT) * * Copyright (c) 2015 vmolsa (http://github.com/vmolsa) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * */ #include "MediaConstraints.h" using namespace v8; using namespace WebRTC; MediaConstraints::MediaConstraints() : _audio(false), _video(false) { LOG(LS_INFO) << __PRETTY_FUNCTION__; } MediaConstraints::~MediaConstraints() { LOG(LS_INFO) << __PRETTY_FUNCTION__; } rtc::scoped_refptr MediaConstraints::New() { LOG(LS_INFO) << __PRETTY_FUNCTION__; return new rtc::RefCountedObject(); } rtc::scoped_refptr MediaConstraints::New(const Local &constraints) { LOG(LS_INFO) << __PRETTY_FUNCTION__; Nan::HandleScope scope; rtc::scoped_refptr self = MediaConstraints::New(); if (constraints.IsEmpty()) { return self; } Local optional_value = constraints->Get(Nan::New("optional").ToLocalChecked()); if (!optional_value.IsEmpty() && optional_value->IsArray()) { Local options = Local::Cast(optional_value); for (unsigned int index = 0; index < options->Length(); index++) { Local option_value = options->Get(index); if (!option_value.IsEmpty() && option_value->IsObject()) { Local option = Local::Cast(option_value); Local DtlsSrtpKeyAgreement = option->Get(Nan::New("DtlsSrtpKeyAgreement").ToLocalChecked()); Local RtpDataChannels = option->Get(Nan::New("RtpDataChannels").ToLocalChecked()); Local googDscp = option->Get(Nan::New("googDscp").ToLocalChecked()); Local googIPv6 = option->Get(Nan::New("googIPv6").ToLocalChecked()); Local googSuspendBelowMinBitrate = option->Get(Nan::New("googSuspendBelowMinBitrate").ToLocalChecked()); Local googCombinedAudioVideoBwe = option->Get(Nan::New("googCombinedAudioVideoBwe").ToLocalChecked()); Local googScreencastMinBitrate = option->Get(Nan::New("googScreencastMinBitrate").ToLocalChecked()); Local googCpuOveruseDetection = option->Get(Nan::New("googCpuOveruseDetection").ToLocalChecked()); Local googPayloadPadding = option->Get(Nan::New("googPayloadPadding").ToLocalChecked()); self->SetOptional(webrtc::MediaConstraintsInterface::kEnableDtlsSrtp, DtlsSrtpKeyAgreement); self->SetOptional(webrtc::MediaConstraintsInterface::kEnableRtpDataChannels, RtpDataChannels); self->SetOptional(webrtc::MediaConstraintsInterface::kEnableDscp, googDscp); self->SetOptional(webrtc::MediaConstraintsInterface::kEnableIPv6, googIPv6); self->SetOptional(webrtc::MediaConstraintsInterface::kEnableVideoSuspendBelowMinBitrate, googSuspendBelowMinBitrate); self->SetOptional(webrtc::MediaConstraintsInterface::kCombinedAudioVideoBwe, googCombinedAudioVideoBwe); self->SetOptional(webrtc::MediaConstraintsInterface::kScreencastMinBitrate, googScreencastMinBitrate); self->SetOptional(webrtc::MediaConstraintsInterface::kCpuOveruseDetection, googCpuOveruseDetection); self->SetOptional(webrtc::MediaConstraintsInterface::kPayloadPadding, googPayloadPadding); } } } Local mandatory_value = constraints->Get(Nan::New("mandatory").ToLocalChecked()); if (!mandatory_value.IsEmpty() && mandatory_value->IsObject()) { Local mandatory = Local::Cast(mandatory_value); Local OfferToReceiveAudio = mandatory->Get(Nan::New("OfferToReceiveAudio").ToLocalChecked()); Local OfferToReceiveVideo = mandatory->Get(Nan::New("OfferToReceiveVideo").ToLocalChecked()); Local VoiceActivityDetection = mandatory->Get(Nan::New("VoiceActivityDetection").ToLocalChecked()); Local IceRestart = mandatory->Get(Nan::New("IceRestart").ToLocalChecked()); Local googUseRtpMUX = mandatory->Get(Nan::New("googUseRtpMUX").ToLocalChecked()); self->SetMandatory(webrtc::MediaConstraintsInterface::kOfferToReceiveAudio, OfferToReceiveAudio); self->SetMandatory(webrtc::MediaConstraintsInterface::kOfferToReceiveVideo, OfferToReceiveVideo); self->SetMandatory(webrtc::MediaConstraintsInterface::kVoiceActivityDetection, VoiceActivityDetection); self->SetMandatory(webrtc::MediaConstraintsInterface::kIceRestart, IceRestart); self->SetMandatory(webrtc::MediaConstraintsInterface::kUseRtpMux, googUseRtpMUX); } Local audio_value = constraints->Get(Nan::New("audio").ToLocalChecked()); if (!audio_value.IsEmpty()) { if (audio_value->IsTrue() || audio_value->IsFalse()) { self->_audio = true; } else if (audio_value->IsObject()) { Local audio = Local::Cast(audio_value); optional_value = audio->Get(Nan::New("optional").ToLocalChecked()); if (!optional_value.IsEmpty() && optional_value->IsArray()) { Local options = Local::Cast(optional_value); for (unsigned int index = 0; index < options->Length(); index++) { Local option_value = options->Get(index); if (!option_value.IsEmpty() && option_value->IsObject()) { Local option = Local::Cast(option_value); Local EchoCancellation = option->Get(Nan::New("echoCancellation").ToLocalChecked()); Local googEchoCancellation = option->Get(Nan::New("googEchoCancellation").ToLocalChecked()); Local googEchoCancellation2 = option->Get(Nan::New("googEchoCancellation2").ToLocalChecked()); Local googDAEchoCancellation = option->Get(Nan::New("googDAEchoCancellation").ToLocalChecked()); Local googAutoGainControl = option->Get(Nan::New("googAutoGainControl").ToLocalChecked()); Local googAutoGainControl2 = option->Get(Nan::New("googAutoGainControl2").ToLocalChecked()); Local googNoiseSuppression = option->Get(Nan::New("googNoiseSuppression").ToLocalChecked()); Local googNoiseSuppression2 = option->Get(Nan::New("googNoiseSuppression2").ToLocalChecked()); Local googHighpassFilter = option->Get(Nan::New("googHighpassFilter").ToLocalChecked()); Local googTypingNoiseDetection = option->Get(Nan::New("googTypingNoiseDetection").ToLocalChecked()); Local googAudioMirroring = option->Get(Nan::New("googAudioMirroring").ToLocalChecked()); Local noiseReduction = option->Get(Nan::New("googNoiseReduction").ToLocalChecked()); Local sourceId = option->Get(Nan::New("sourceId").ToLocalChecked()); self->SetOptional(webrtc::MediaConstraintsInterface::kEchoCancellation, EchoCancellation); self->SetOptional(webrtc::MediaConstraintsInterface::kGoogEchoCancellation, googEchoCancellation); self->SetOptional(webrtc::MediaConstraintsInterface::kExtendedFilterEchoCancellation, googEchoCancellation2); self->SetOptional(webrtc::MediaConstraintsInterface::kDAEchoCancellation, googDAEchoCancellation); self->SetOptional(webrtc::MediaConstraintsInterface::kAutoGainControl, googAutoGainControl); self->SetOptional(webrtc::MediaConstraintsInterface::kExperimentalAutoGainControl, googAutoGainControl2); self->SetOptional(webrtc::MediaConstraintsInterface::kNoiseSuppression, googNoiseSuppression); self->SetOptional(webrtc::MediaConstraintsInterface::kExperimentalNoiseSuppression, googNoiseSuppression2); self->SetOptional(webrtc::MediaConstraintsInterface::kHighpassFilter, googHighpassFilter); self->SetOptional(webrtc::MediaConstraintsInterface::kTypingNoiseDetection, googTypingNoiseDetection); self->SetOptional(webrtc::MediaConstraintsInterface::kAudioMirroring, googAudioMirroring); self->SetOptional(webrtc::MediaConstraintsInterface::kNoiseReduction, noiseReduction); if (!sourceId.IsEmpty() && sourceId->IsString()) { String::Utf8Value sourceId_str(sourceId->ToString()); self->_audioId = *sourceId_str; } } } } self->_audio = true; } } Local video_value = constraints->Get(Nan::New("video").ToLocalChecked()); if (!video_value.IsEmpty()) { if (video_value->IsTrue() || video_value->IsFalse()) { self->_video = true; } else if (video_value->IsObject()) { Local video = Local::Cast(audio_value); optional_value = video->Get(Nan::New("optional").ToLocalChecked()); if (!optional_value.IsEmpty() && optional_value->IsArray()) { Local options = Local::Cast(optional_value); for (unsigned int index = 0; index < options->Length(); index++) { Local option_value = options->Get(index); if (!option_value.IsEmpty() && option_value->IsObject()) { Local option = Local::Cast(option_value); Local minAspectRatio = option->Get(Nan::New("minAspectRatio").ToLocalChecked()); Local maxAspectRatio = option->Get(Nan::New("maxAspectRatio").ToLocalChecked()); Local maxWidth = option->Get(Nan::New("maxWidth").ToLocalChecked()); Local minWidth = option->Get(Nan::New("minWidth").ToLocalChecked()); Local maxHeight = option->Get(Nan::New("maxHeight").ToLocalChecked()); Local minHeight = option->Get(Nan::New("minHeight").ToLocalChecked()); Local maxFrameRate = option->Get(Nan::New("maxFrameRate").ToLocalChecked()); Local minFrameRate = option->Get(Nan::New("minFrameRate").ToLocalChecked()); Local sourceId = option->Get(Nan::New("sourceId").ToLocalChecked()); self->SetOptional(webrtc::MediaConstraintsInterface::kMinAspectRatio, minAspectRatio); self->SetOptional(webrtc::MediaConstraintsInterface::kMaxAspectRatio, maxAspectRatio); self->SetOptional(webrtc::MediaConstraintsInterface::kMaxWidth, maxWidth); self->SetOptional(webrtc::MediaConstraintsInterface::kMinWidth, minWidth); self->SetOptional(webrtc::MediaConstraintsInterface::kMaxHeight, maxHeight); self->SetOptional(webrtc::MediaConstraintsInterface::kMinHeight, minHeight); self->SetOptional(webrtc::MediaConstraintsInterface::kMaxFrameRate, maxFrameRate); self->SetOptional(webrtc::MediaConstraintsInterface::kMinFrameRate, minFrameRate); if (!sourceId.IsEmpty() && sourceId->IsString()) { String::Utf8Value sourceId_str(sourceId->ToString()); self->_videoId = *sourceId_str; } } } } self->_video = true; } } return self; } rtc::scoped_refptr MediaConstraints::New(const Local &constraints) { LOG(LS_INFO) << __PRETTY_FUNCTION__; Nan::HandleScope scope; if (!constraints.IsEmpty() && constraints->IsObject()) { Local obj = Local::Cast(constraints); return MediaConstraints::New(obj); } return MediaConstraints::New(); } void MediaConstraints::SetOptional(std::string key, Local value) { LOG(LS_INFO) << __PRETTY_FUNCTION__; if (!value.IsEmpty() && !value->IsNull() && !value->IsUndefined()) { if (value->IsTrue() || value->IsFalse()) { MediaConstraints::SetOptional(key, value->IsTrue() ? std::string(webrtc::MediaConstraintsInterface::kValueTrue) : std::string(webrtc::MediaConstraintsInterface::kValueFalse)); } else if (value->IsNumber()) { MediaConstraints::SetOptional(key, value->NumberValue()); } else if (value->IsInt32()) { MediaConstraints::SetOptional(key, value->Int32Value()); } else if (value->IsUint32()) { MediaConstraints::SetOptional(key, value->Uint32Value()); } else { Nan::ThrowError("Unknown MediaConstraints Type"); } } } void MediaConstraints::SetMandatory(std::string key, Local value) { LOG(LS_INFO) << __PRETTY_FUNCTION__; if (!value.IsEmpty() && !value->IsNull() && !value->IsUndefined()) { if (value->IsTrue() || value->IsFalse()) { MediaConstraints::SetMandatory(key, value->IsTrue() ? std::string(webrtc::MediaConstraintsInterface::kValueTrue) : std::string(webrtc::MediaConstraintsInterface::kValueFalse)); } else if (value->IsNumber()) { MediaConstraints::SetMandatory(key, value->NumberValue()); } else if (value->IsInt32()) { MediaConstraints::SetMandatory(key, value->Int32Value()); } else if (value->IsUint32()) { MediaConstraints::SetMandatory(key, value->Uint32Value()); } else { Nan::ThrowError("Unknown MediaConstraints Type"); } } } bool MediaConstraints::IsMandatory(const std::string& key) { LOG(LS_INFO) << __PRETTY_FUNCTION__; std::string value; if (_mandatory.FindFirst(key, &value)) { return true; } return false; } bool MediaConstraints::GetMandatory(const std::string& key) { LOG(LS_INFO) << __PRETTY_FUNCTION__; std::string value; if (_mandatory.FindFirst(key, &value)) { if (!value.compare("true")) { return true; } } return false; } void MediaConstraints::RemoveMandatory(const std::string& key) { LOG(LS_INFO) << __PRETTY_FUNCTION__; std::string value; if (_mandatory.FindFirst(key, &value)) { for (webrtc::MediaConstraintsInterface::Constraints::iterator iter = _mandatory.begin(); iter != _mandatory.end(); ++iter) { if (iter->key == key) { _mandatory.erase(iter); break; } } } } void MediaConstraints::AddMandatory(const std::string &key, const std::string &value) { LOG(LS_INFO) << __PRETTY_FUNCTION__; _mandatory.push_back(webrtc::MediaConstraintsInterface::Constraint(key, value)); } void MediaConstraints::SetMandatory(const std::string &key, const std::string &value) { LOG(LS_INFO) << __PRETTY_FUNCTION__; MediaConstraints::RemoveMandatory(key); MediaConstraints::AddMandatory(key, value); } bool MediaConstraints::IsOptional(const std::string& key) { LOG(LS_INFO) << __PRETTY_FUNCTION__; std::string value; if (_mandatory.FindFirst(key, &value)) { return true; } return false; } bool MediaConstraints::GetOptional(const std::string& key) { LOG(LS_INFO) << __PRETTY_FUNCTION__; std::string value; if (_optional.FindFirst(key, &value)) { if (!value.compare("true")) { return true; } } return false; } void MediaConstraints::RemoveOptional(const std::string& key) { LOG(LS_INFO) << __PRETTY_FUNCTION__; std::string value; if (_optional.FindFirst(key, &value)) { for (webrtc::MediaConstraintsInterface::Constraints::iterator iter = _optional.begin(); iter != _optional.end(); ++iter) { if (iter->key == key) { _optional.erase(iter); break; } } } } void MediaConstraints::AddOptional(const std::string &key, const std::string &value) { LOG(LS_INFO) << __PRETTY_FUNCTION__; _optional.push_back(webrtc::MediaConstraintsInterface::Constraint(key, value)); } void MediaConstraints::SetOptional(const std::string &key, const std::string &value) { LOG(LS_INFO) << __PRETTY_FUNCTION__; MediaConstraints::RemoveOptional(key); MediaConstraints::AddOptional(key, value); } bool MediaConstraints::UseAudio() const { LOG(LS_INFO) << __PRETTY_FUNCTION__; return _audio; } bool MediaConstraints::UseVideo() const { LOG(LS_INFO) << __PRETTY_FUNCTION__; return _video; } std::string MediaConstraints::AudioId() const { LOG(LS_INFO) << __PRETTY_FUNCTION__; return _audioId; } std::string MediaConstraints::VideoId() const { LOG(LS_INFO) << __PRETTY_FUNCTION__; return _videoId; } const webrtc::MediaConstraintsInterface *MediaConstraints::ToConstraints() const { LOG(LS_INFO) << __PRETTY_FUNCTION__; return this; } const webrtc::MediaConstraintsInterface::Constraints &MediaConstraints::GetMandatory() const { LOG(LS_INFO) << __PRETTY_FUNCTION__; return _mandatory; } const webrtc::MediaConstraintsInterface::Constraints &MediaConstraints::GetOptional() const { LOG(LS_INFO) << __PRETTY_FUNCTION__; return _optional; } ================================================ FILE: src/MediaConstraints.h ================================================ /* * The MIT License (MIT) * * Copyright (c) 2015 vmolsa (http://github.com/vmolsa) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * */ #ifndef WEBRTC_MEDIACONSTRAINTS_H #define WEBRTC_MEDIACONSTRAINTS_H #include "Common.h" namespace WebRTC { class MediaConstraints : public webrtc::MediaConstraintsInterface, public rtc::RefCountInterface { friend class rtc::RefCountedObject; public: static rtc::scoped_refptr New(); static rtc::scoped_refptr New(const v8::Local &constraints); static rtc::scoped_refptr New(const v8::Local &constraints); bool UseAudio() const; bool UseVideo() const; std::string AudioId() const; std::string VideoId() const; bool IsMandatory(const std::string& key); bool GetMandatory(const std::string& key); void RemoveMandatory(const std::string& key); void AddMandatory(const std::string &key, const std::string &value); void SetMandatory(const std::string &key, const std::string &value); template void SetMandatory(const std::string& key, const T& value) { SetMandatory(key, rtc::ToString(value)); } bool IsOptional(const std::string& key); bool GetOptional(const std::string& key); void RemoveOptional(const std::string& key); void AddOptional(const std::string &key, const std::string &value); void SetOptional(const std::string &key, const std::string &value); template void SetOptional(const std::string& key, const T& value) { SetOptional(key, rtc::ToString(value)); } const webrtc::MediaConstraintsInterface *ToConstraints() const; const webrtc::MediaConstraintsInterface::Constraints &GetMandatory() const final; const webrtc::MediaConstraintsInterface::Constraints &GetOptional() const final; private: explicit MediaConstraints(); ~MediaConstraints() override; void SetOptional(std::string key, v8::Local value); void SetMandatory(std::string key, v8::Local value); protected: bool _audio; bool _video; std::string _audioId; std::string _videoId; webrtc::MediaConstraintsInterface::Constraints _mandatory; webrtc::MediaConstraintsInterface::Constraints _optional; }; }; #endif ================================================ FILE: src/MediaStream.cc ================================================ /* * The MIT License (MIT) * * Copyright (c) 2015 vmolsa (http://github.com/vmolsa) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * */ #include "MediaStream.h" #include "MediaStreamTrack.h" using namespace v8; using namespace WebRTC; Nan::Persistent MediaStream::constructor; void MediaStream::Init() { LOG(LS_INFO) << __PRETTY_FUNCTION__; Nan::HandleScope scope; Local tpl = Nan::New(MediaStream::New); tpl->InstanceTemplate()->SetInternalFieldCount(1); tpl->SetClassName(Nan::New("MediaStream").ToLocalChecked()); Nan::SetPrototypeMethod(tpl, "addTrack", MediaStream::AddTrack); Nan::SetPrototypeMethod(tpl, "removeTrack", MediaStream::RemoveTrack); Nan::SetPrototypeMethod(tpl, "clone", MediaStream::Clone); Nan::SetPrototypeMethod(tpl, "getAudioTracks", MediaStream::GetAudioTracks); Nan::SetPrototypeMethod(tpl, "getTrackById", MediaStream::GetTrackById); Nan::SetPrototypeMethod(tpl, "getVideoTracks", MediaStream::GetVideoTracks); Nan::SetPrototypeMethod(tpl, "getTracks", MediaStream::GetTracks); Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("active").ToLocalChecked(), MediaStream::GetActive); Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("ended").ToLocalChecked(), MediaStream::GetEnded); Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("id").ToLocalChecked(), MediaStream::GetId); Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("onaddtrack").ToLocalChecked(), MediaStream::GetOnAddTrack, MediaStream::SetOnAddTrack); Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("onremovetrack").ToLocalChecked(), MediaStream::GetOnRemoveTrack, MediaStream::SetOnRemoveTrack); constructor.Reset(tpl->GetFunction()); } Local MediaStream::New(rtc::scoped_refptr mediaStream) { LOG(LS_INFO) << __PRETTY_FUNCTION__; Nan::EscapableHandleScope scope; Local empty; Local instance = Nan::New(MediaStream::constructor); if (instance.IsEmpty() || !mediaStream.get()) { return scope.Escape(Nan::Null()); } Local ret = instance->NewInstance(); MediaStream *self = RTCWrap::Unwrap(ret, "MediaStream"); if (self) { self->_stream = mediaStream; self->_audio_tracks = self->_stream->GetAudioTracks(); self->_video_tracks = self->_stream->GetVideoTracks(); self->_stream->RegisterObserver(self->_observer.get()); self->CheckState(); return scope.Escape(ret); } return scope.Escape(Nan::Null()); } MediaStream::MediaStream() : _active(false), _ended(true) { LOG(LS_INFO) << __PRETTY_FUNCTION__; _observer = new rtc::RefCountedObject(this); } MediaStream::~MediaStream() { LOG(LS_INFO) << __PRETTY_FUNCTION__; if (_stream.get()) { _stream->UnregisterObserver(_observer.get()); } _observer->RemoveListener(this); } void MediaStream::New(const Nan::FunctionCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; Nan::HandleScope scope; if (info.IsConstructCall()) { MediaStream* mediaStream = new MediaStream(); mediaStream->Wrap(info.This(), "MediaStream"); return info.GetReturnValue().Set(info.This()); } Nan::ThrowError("Internal Error"); info.GetReturnValue().SetUndefined(); } rtc::scoped_refptr MediaStream::Unwrap(Local value) { LOG(LS_INFO) << __PRETTY_FUNCTION__; if (!value.IsEmpty()) { MediaStream *self = RTCWrap::Unwrap(value, "MediaStream"); if (self) { return self->_stream; } } return 0; } rtc::scoped_refptr MediaStream::Unwrap(Local value) { LOG(LS_INFO) << __PRETTY_FUNCTION__; if (!value.IsEmpty() && value->IsObject()) { Local stream = Local::Cast(value); return MediaStream::Unwrap(stream); } return 0; } void MediaStream::AddTrack(const Nan::FunctionCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; rtc::scoped_refptr stream = MediaStream::Unwrap(info.This()); bool retval = false; if (stream.get()) { if (info.Length() >= 1 && info[0]->IsObject()) { rtc::scoped_refptr track = MediaStreamTrack::Unwrap(info[0]); if (track.get()) { std::string kind = track->kind(); if (kind.compare("audio") == 0) { rtc::scoped_refptr audio(static_cast(track.get())); retval = stream->AddTrack(audio); } else { rtc::scoped_refptr video(static_cast(track.get())); retval = stream->AddTrack(video); } } } } else { Nan::ThrowError("Internal Error"); } return info.GetReturnValue().Set(Nan::New(retval)); } void MediaStream::RemoveTrack(const Nan::FunctionCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; rtc::scoped_refptr stream = MediaStream::Unwrap(info.This()); bool retval = false; if (stream.get()) { if (info.Length() >= 1 && info[0]->IsObject()) { rtc::scoped_refptr track = MediaStreamTrack::Unwrap(info[0]); if (track.get()) { std::string kind = track->kind(); if (kind.compare("audio") == 0) { rtc::scoped_refptr audio(static_cast(track.get())); retval = stream->RemoveTrack(audio); } else { rtc::scoped_refptr video(static_cast(track.get())); retval = stream->RemoveTrack(video); } } } } else { Nan::ThrowError("Internal Error"); } return info.GetReturnValue().Set(Nan::New(retval)); } void MediaStream::Clone(const Nan::FunctionCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; rtc::scoped_refptr self = MediaStream::Unwrap(info.This()); rtc::scoped_refptr factory = webrtc::CreatePeerConnectionFactory(); rtc::scoped_refptr stream; if (self.get() && factory.get()) { stream = factory->CreateLocalMediaStream("stream"); if (stream.get()) { webrtc::AudioTrackVector audio_list = self->GetAudioTracks(); std::vector >::iterator audio_it; for (audio_it = audio_list.begin(); audio_it != audio_list.end(); audio_it++) { rtc::scoped_refptr track(*audio_it); if (track.get()) { stream->AddTrack(track.get()); } } webrtc::VideoTrackVector video_list = self->GetVideoTracks(); std::vector >::iterator video_it; for (video_it = video_list.begin(); video_it != video_list.end(); video_it++) { rtc::scoped_refptr track(*video_it); if (track.get()) { stream->AddTrack(track.get()); } } return info.GetReturnValue().Set(MediaStream::New(stream)); } } Nan::ThrowError("Internal Error"); info.GetReturnValue().SetUndefined(); } void MediaStream::GetTrackById(const Nan::FunctionCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; rtc::scoped_refptr stream = MediaStream::Unwrap(info.This()); if (stream.get()) { if (info.Length() >= 1 && info[0]->IsString()) { v8::String::Utf8Value idValue(info[0]->ToString()); std::string id(*idValue); rtc::scoped_refptr audio = stream->FindAudioTrack(id); if (audio.get()) { return info.GetReturnValue().Set(MediaStreamTrack::New(audio.get())); } rtc::scoped_refptr video = stream->FindVideoTrack(id); if (video.get()) { return info.GetReturnValue().Set(MediaStreamTrack::New(video.get())); } } return info.GetReturnValue().Set(Nan::Null()); } else { Nan::ThrowError("Internal Error"); } info.GetReturnValue().SetUndefined(); } void MediaStream::GetAudioTracks(const Nan::FunctionCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; rtc::scoped_refptr self = MediaStream::Unwrap(info.This()); if (self.get()) { webrtc::AudioTrackVector audio_list = self->GetAudioTracks(); std::vector >::iterator audio_it; Local list = Nan::New(); uint32_t index = 0; for (audio_it = audio_list.begin(); audio_it != audio_list.end(); audio_it++) { rtc::scoped_refptr track(*audio_it); if (track.get()) { list->Set(index, MediaStreamTrack::New(track.get())); index++; } } return info.GetReturnValue().Set(list); } else { Nan::ThrowError("Internal Error"); } info.GetReturnValue().SetUndefined(); } void MediaStream::GetVideoTracks(const Nan::FunctionCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; rtc::scoped_refptr self = MediaStream::Unwrap(info.This()); if (self.get()) { webrtc::VideoTrackVector video_list = self->GetVideoTracks(); std::vector >::iterator video_it; Local list = Nan::New(); uint32_t index = 0; for (video_it = video_list.begin(); video_it != video_list.end(); video_it++) { rtc::scoped_refptr track(*video_it); if (track.get()) { list->Set(index, MediaStreamTrack::New(track.get())); index++; } } return info.GetReturnValue().Set(list); } else { Nan::ThrowError("Internal Error"); } info.GetReturnValue().SetUndefined(); } void MediaStream::GetTracks(const Nan::FunctionCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; rtc::scoped_refptr self = MediaStream::Unwrap(info.This()); if (self.get()) { webrtc::AudioTrackVector audio_list = self->GetAudioTracks(); webrtc::VideoTrackVector video_list = self->GetVideoTracks(); std::vector >::iterator audio_it; std::vector >::iterator video_it; Local list = Nan::New(); uint32_t index = 0; for (audio_it = audio_list.begin(); audio_it != audio_list.end(); audio_it++) { rtc::scoped_refptr track(*audio_it); if (track.get()) { list->Set(index, MediaStreamTrack::New(track.get())); index++; } } for (video_it = video_list.begin(); video_it != video_list.end(); video_it++) { rtc::scoped_refptr track(*video_it); if (track.get()) { list->Set(index, MediaStreamTrack::New(track.get())); index++; } } return info.GetReturnValue().Set(list); } else { Nan::ThrowError("Internal Error"); } info.GetReturnValue().SetUndefined(); } void MediaStream::GetActive(Local property, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; MediaStream *self = RTCWrap::Unwrap(info.Holder(), "MediaStream"); return info.GetReturnValue().Set(Nan::New(self->_active)); } void MediaStream::GetEnded(Local property, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; MediaStream *self = RTCWrap::Unwrap(info.Holder(), "MediaStream"); return info.GetReturnValue().Set(Nan::New(self->_ended)); } void MediaStream::GetId(Local property, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; rtc::scoped_refptr stream = MediaStream::Unwrap(info.Holder()); if (stream.get()) { return info.GetReturnValue().Set(Nan::New(stream->label().c_str()).ToLocalChecked()); } else { Nan::ThrowError("Internal Error"); } info.GetReturnValue().SetUndefined(); } void MediaStream::GetOnAddTrack(Local property, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; MediaStream *self = RTCWrap::Unwrap(info.Holder(), "MediaStream"); return info.GetReturnValue().Set(Nan::New(self->_onaddtrack)); } void MediaStream::GetOnRemoveTrack(Local property, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; MediaStream *self = RTCWrap::Unwrap(info.Holder(), "MediaStream"); return info.GetReturnValue().Set(Nan::New(self->_onremovetrack)); } void MediaStream::ReadOnly(Local property, Local value, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; } void MediaStream::SetOnAddTrack(Local property, Local value, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; MediaStream *self = RTCWrap::Unwrap(info.Holder(), "MediaStream"); if (!value.IsEmpty() && value->IsFunction()) { self->_onaddtrack.Reset(Local::Cast(value)); } else { self->_onaddtrack.Reset(); } } void MediaStream::SetOnRemoveTrack(Local property, Local value, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; MediaStream *self = RTCWrap::Unwrap(info.Holder(), "MediaStream"); if (!value.IsEmpty() && value->IsFunction()) { self->_onremovetrack.Reset(Local::Cast(value)); } else { self->_onremovetrack.Reset(); } } void MediaStream::CheckState() { _active = false; _ended = true; webrtc::AudioTrackVector new_audio_tracks = _stream->GetAudioTracks(); webrtc::VideoTrackVector new_video_tracks = _stream->GetVideoTracks(); for (const auto& cached_track : _audio_tracks) { auto it = std::find_if( new_audio_tracks.begin(), new_audio_tracks.end(), [cached_track](const webrtc::AudioTrackVector::value_type& new_track) { return new_track->id().compare(cached_track->id()) == 0; }); if (it == new_audio_tracks.end()) { Local callback = Nan::New(_onremovetrack); Local argv[] = { MediaStreamTrack::New(cached_track.get()) }; if (!callback.IsEmpty() && callback->IsFunction()) { callback->Call(RTCWrap::This(), 1, argv); } } } for (const auto& new_track : new_audio_tracks) { if (new_track->state() == webrtc::MediaStreamTrackInterface::kLive) { _active = true; _ended = false; } auto it = std::find_if( _audio_tracks.begin(), _audio_tracks.end(), [new_track](const webrtc::AudioTrackVector::value_type& cached_track) { return new_track->id().compare(cached_track->id()) == 0; }); if (it == _audio_tracks.end()) { Local callback = Nan::New(_onaddtrack); Local argv[] = { MediaStreamTrack::New(new_track.get()) }; if (!callback.IsEmpty() && callback->IsFunction()) { callback->Call(RTCWrap::This(), 1, argv); } } } for (const auto& cached_track : _video_tracks) { auto it = std::find_if( new_video_tracks.begin(), new_video_tracks.end(), [cached_track](const webrtc::VideoTrackVector::value_type& new_track) { return new_track->id().compare(cached_track->id()) == 0; }); if (it == new_video_tracks.end()) { Local callback = Nan::New(_onremovetrack); Local argv[] = { MediaStreamTrack::New(cached_track.get()) }; if (!callback.IsEmpty() && callback->IsFunction()) { callback->Call(RTCWrap::This(), 1, argv); } } } for (const auto& new_track : new_video_tracks) { if (new_track->state() == webrtc::MediaStreamTrackInterface::kLive) { _active = true; _ended = false; } auto it = std::find_if( _video_tracks.begin(), _video_tracks.end(), [new_track](const webrtc::VideoTrackVector::value_type& cached_track) { return new_track->id().compare(cached_track->id()) == 0; }); if (it == _video_tracks.end()) { Local callback = Nan::New(_onaddtrack); Local argv[] = { MediaStreamTrack::New(new_track.get()) }; if (!callback.IsEmpty() && callback->IsFunction()) { callback->Call(RTCWrap::This(), 1, argv); } } } _audio_tracks = new_audio_tracks; _video_tracks = new_video_tracks; } void MediaStream::On(Event *event) { LOG(LS_INFO) << __PRETTY_FUNCTION__; Nan::HandleScope scope; MediaStreamEvent type = event->Type(); if (type != kMediaStreamChanged) { Nan::ThrowError("Internal Error"); return; } MediaStream::CheckState(); } ================================================ FILE: src/MediaStream.h ================================================ /* * The MIT License (MIT) * * Copyright (c) 2015 vmolsa (http://github.com/vmolsa) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * */ #ifndef WEBRTC_MEDIASTREAM_H #define WEBRTC_MEDIASTREAM_H #include "Common.h" #include "Observers.h" #include "EventEmitter.h" #include "Wrap.h" namespace WebRTC { enum MediaStreamEvent { kMediaStreamChanged }; class MediaStream : public RTCWrap, public EventEmitter { public: static void Init(); static v8::Local New(rtc::scoped_refptr mediaStream); static rtc::scoped_refptr Unwrap(v8::Local value); static rtc::scoped_refptr Unwrap(v8::Local value); private: MediaStream(); ~MediaStream() final; static void New(const Nan::FunctionCallbackInfo &info); static void AddTrack(const Nan::FunctionCallbackInfo &info); static void Clone(const Nan::FunctionCallbackInfo &info); static void GetTrackById(const Nan::FunctionCallbackInfo &info); static void GetAudioTracks(const Nan::FunctionCallbackInfo &info); static void GetVideoTracks(const Nan::FunctionCallbackInfo &info); static void GetTracks(const Nan::FunctionCallbackInfo &info); static void RemoveTrack(const Nan::FunctionCallbackInfo &info); static void GetActive(v8::Local property, const Nan::PropertyCallbackInfo &info); static void GetEnded(v8::Local property, const Nan::PropertyCallbackInfo &info); static void GetId(v8::Local property, const Nan::PropertyCallbackInfo &info); static void GetOnAddTrack(v8::Local property, const Nan::PropertyCallbackInfo &info); static void GetOnRemoveTrack(v8::Local property, const Nan::PropertyCallbackInfo &info); static void ReadOnly(v8::Local property, v8::Local value, const Nan::PropertyCallbackInfo &info); static void SetOnAddTrack(v8::Local property, v8::Local value, const Nan::PropertyCallbackInfo &info); static void SetOnRemoveTrack(v8::Local property, v8::Local value, const Nan::PropertyCallbackInfo &info); void CheckState(); void On(Event *event) final; protected: bool _active; bool _ended; Nan::Persistent _onaddtrack; Nan::Persistent _onremovetrack; rtc::scoped_refptr _observer; rtc::scoped_refptr _stream; webrtc::AudioTrackVector _audio_tracks; webrtc::VideoTrackVector _video_tracks; static Nan::Persistent constructor; }; }; #endif ================================================ FILE: src/MediaStreamTrack.cc ================================================ /* * The MIT License (MIT) * * Copyright (c) 2015 vmolsa (http://github.com/vmolsa) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * */ #include "MediaStreamTrack.h" using namespace v8; using namespace WebRTC; Nan::Persistent MediaStreamTrack::constructor; void MediaStreamTrack::Init() { LOG(LS_INFO) << __PRETTY_FUNCTION__; Nan::HandleScope scope; Local tpl = Nan::New(MediaStreamTrack::New); tpl->InstanceTemplate()->SetInternalFieldCount(1); tpl->SetClassName(Nan::New("MediaStreamTrack").ToLocalChecked()); Nan::SetPrototypeMethod(tpl, "getConstraints", MediaStreamTrack::GetConstraints); Nan::SetPrototypeMethod(tpl, "applyConstraints", MediaStreamTrack::ApplyConstraints); Nan::SetPrototypeMethod(tpl, "setSettings", MediaStreamTrack::GetSettings); Nan::SetPrototypeMethod(tpl, "getCapabilities", MediaStreamTrack::GetCapabilities); Nan::SetPrototypeMethod(tpl, "clone", MediaStreamTrack::Clone); Nan::SetPrototypeMethod(tpl, "stop", MediaStreamTrack::Stop); Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("enabled").ToLocalChecked(), MediaStreamTrack::GetEnabled, MediaStreamTrack::SetEnabled); Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("id").ToLocalChecked(), MediaStreamTrack::GetId); Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("kind").ToLocalChecked(), MediaStreamTrack::GetKind); Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("label").ToLocalChecked(), MediaStreamTrack::GetLabel); Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("muted").ToLocalChecked(), MediaStreamTrack::GetMuted); Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("readonly").ToLocalChecked(), MediaStreamTrack::GetReadOnly); Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("readyState").ToLocalChecked(), MediaStreamTrack::GetReadyState); Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("remote").ToLocalChecked(), MediaStreamTrack::GetRemote); Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("onstarted").ToLocalChecked(), MediaStreamTrack::GetOnStarted, MediaStreamTrack::SetOnStarted); Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("onmute").ToLocalChecked(), MediaStreamTrack::GetOnMute, MediaStreamTrack::SetOnMute); Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("onunmute").ToLocalChecked(), MediaStreamTrack::GetOnUnMute, MediaStreamTrack::SetOnUnMute); Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("onoverconstrained").ToLocalChecked(), MediaStreamTrack::GetOnOverConstrained, MediaStreamTrack::SetOnOverConstrained); Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("onended").ToLocalChecked(), MediaStreamTrack::GetOnEnded, MediaStreamTrack::SetOnEnded); constructor.Reset(tpl->GetFunction()); } Local MediaStreamTrack::New(rtc::scoped_refptr audioTrack) { LOG(LS_INFO) << __PRETTY_FUNCTION__; Nan::EscapableHandleScope scope; Local argv[1]; Local instance = Nan::New(MediaStreamTrack::constructor); if (instance.IsEmpty() || !audioTrack.get()) { return scope.Escape(Nan::Null()); } Local ret = instance->NewInstance(0, argv); MediaStreamTrack *self = RTCWrap::Unwrap(ret, "MediaStreamTrack"); self->isAudioTrack = true; self->_track = audioTrack; self->_source = audioTrack->GetSource(); self->_track_state = self->_track->state(); self->_source_state = self->_source->state(); self->_track->RegisterObserver(self->_observer.get()); self->_source->RegisterObserver(self->_observer.get()); self->CheckState(); return scope.Escape(ret); } Local MediaStreamTrack::New(rtc::scoped_refptr videoTrack) { LOG(LS_INFO) << __PRETTY_FUNCTION__; Nan::EscapableHandleScope scope; Local argv[1]; Local instance = Nan::New(MediaStreamTrack::constructor); if (instance.IsEmpty() || !videoTrack.get()) { return scope.Escape(Nan::Null()); } Local ret = instance->NewInstance(0, argv); MediaStreamTrack *self = RTCWrap::Unwrap(ret, "MediaStreamTrack"); self->isVideoTrack = true; self->_track = videoTrack; self->_source = videoTrack->GetSource(); self->_track_state = self->_track->state(); self->_source_state = self->_source->state(); self->_track->RegisterObserver(self->_observer.get()); self->_source->RegisterObserver(self->_observer.get()); self->CheckState(); return scope.Escape(ret); } MediaStreamTrack::MediaStreamTrack() { LOG(LS_INFO) << __PRETTY_FUNCTION__; _observer = new rtc::RefCountedObject(this); } MediaStreamTrack::~MediaStreamTrack() { LOG(LS_INFO) << __PRETTY_FUNCTION__; if (_track.get()) { _track->UnregisterObserver(_observer.get()); } if (_source.get()) { _source->UnregisterObserver(_observer.get()); } _observer->RemoveListener(this); } void MediaStreamTrack::New(const Nan::FunctionCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; Nan::HandleScope scope; if (info.IsConstructCall()) { MediaStreamTrack* mediaStreamTrack = new MediaStreamTrack(); mediaStreamTrack->Wrap(info.This(), "MediaStreamTrack"); return info.GetReturnValue().Set(info.This()); } Nan::ThrowError("Internal Error"); info.GetReturnValue().SetUndefined(); } rtc::scoped_refptr MediaStreamTrack::Unwrap(Local value) { LOG(LS_INFO) << __PRETTY_FUNCTION__; if (!value.IsEmpty()) { MediaStreamTrack *self = RTCWrap::Unwrap(value, "MediaStreamTrack"); if (self) { return self->_track; } } return 0; } rtc::scoped_refptr MediaStreamTrack::Unwrap(Local value) { LOG(LS_INFO) << __PRETTY_FUNCTION__; if (!value.IsEmpty() && value->IsObject()) { Local track = Local::Cast(value); return MediaStreamTrack::Unwrap(track); } return 0; } void MediaStreamTrack::GetConstraints(const Nan::FunctionCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; //MediaStreamTrack *self = RTCWrap::Unwrap(info.This(), "MediaStreamTrack"); info.GetReturnValue().SetUndefined(); } void MediaStreamTrack::ApplyConstraints(const Nan::FunctionCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; //MediaStreamTrack *self = RTCWrap::Unwrap(info.This(), "MediaStreamTrack"); info.GetReturnValue().SetUndefined(); } void MediaStreamTrack::GetSettings(const Nan::FunctionCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; //MediaStreamTrack *self = RTCWrap::Unwrap(info.This(), "MediaStreamTrack"); info.GetReturnValue().SetUndefined(); } void MediaStreamTrack::GetCapabilities(const Nan::FunctionCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; //MediaStreamTrack *self = RTCWrap::Unwrap(info.This(), "MediaStreamTrack"); info.GetReturnValue().SetUndefined(); } void MediaStreamTrack::Clone(const Nan::FunctionCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; //MediaStreamTrack *self = RTCWrap::Unwrap(info.This(), "MediaStreamTrack"); info.GetReturnValue().SetUndefined(); } void MediaStreamTrack::Stop(const Nan::FunctionCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; MediaStreamTrack *self = RTCWrap::Unwrap(info.This(), "MediaStreamTrack"); info.GetReturnValue().Set(Nan::New(self->_track->set_state(webrtc::MediaStreamTrackInterface::kEnded))); } void MediaStreamTrack::GetEnabled(Local property, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; MediaStreamTrack *self = RTCWrap::Unwrap(info.This(), "MediaStreamTrack"); info.GetReturnValue().Set(Nan::New(self->_track->enabled())); } void MediaStreamTrack::GetId(Local property, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; MediaStreamTrack *self = RTCWrap::Unwrap(info.Holder(), "MediaStreamTrack"); info.GetReturnValue().Set(Nan::New(self->_track->id().c_str()).ToLocalChecked()); } void MediaStreamTrack::GetKind(Local property, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; MediaStreamTrack *self = RTCWrap::Unwrap(info.Holder(), "MediaStreamTrack"); info.GetReturnValue().Set(Nan::New(self->_track->kind().c_str()).ToLocalChecked()); } void MediaStreamTrack::GetLabel(Local property, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; info.GetReturnValue().Set(Nan::New("").ToLocalChecked()); } void MediaStreamTrack::GetMuted(Local property, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; MediaStreamTrack *self = RTCWrap::Unwrap(info.Holder(), "MediaStreamTrack"); info.GetReturnValue().Set(Nan::New((self->_source->state() == webrtc::MediaSourceInterface::kMuted) ? true : false)); } void MediaStreamTrack::GetReadOnly(Local property, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; return info.GetReturnValue().Set(Nan::New(true)); } void MediaStreamTrack::GetReadyState(Local property, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; MediaStreamTrack *self = RTCWrap::Unwrap(info.Holder(), "MediaStreamTrack"); if (self->_track->state() == webrtc::MediaStreamTrackInterface::kLive) { return info.GetReturnValue().Set(Nan::New("live").ToLocalChecked()); } info.GetReturnValue().Set(Nan::New("ended").ToLocalChecked()); } void MediaStreamTrack::GetRemote(Local property, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; MediaStreamTrack *self = RTCWrap::Unwrap(info.Holder(), "MediaStreamTrack"); info.GetReturnValue().Set(Nan::New(self->_source->remote())); } void MediaStreamTrack::GetOnStarted(Local property, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; MediaStreamTrack *self = RTCWrap::Unwrap(info.Holder(), "MediaStreamTrack"); return info.GetReturnValue().Set(Nan::New(self->_onstarted)); } void MediaStreamTrack::GetOnMute(Local property, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; MediaStreamTrack *self = RTCWrap::Unwrap(info.Holder(), "MediaStreamTrack"); return info.GetReturnValue().Set(Nan::New(self->_onmute)); } void MediaStreamTrack::GetOnUnMute(Local property, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; MediaStreamTrack *self = RTCWrap::Unwrap(info.Holder(), "MediaStreamTrack"); return info.GetReturnValue().Set(Nan::New(self->_onunmute)); } void MediaStreamTrack::GetOnOverConstrained(Local property, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; MediaStreamTrack *self = RTCWrap::Unwrap(info.Holder(), "MediaStreamTrack"); return info.GetReturnValue().Set(Nan::New(self->_onoverconstrained)); } void MediaStreamTrack::GetOnEnded(Local property, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; MediaStreamTrack *self = RTCWrap::Unwrap(info.Holder(), "MediaStreamTrack"); return info.GetReturnValue().Set(Nan::New(self->_onended)); } void MediaStreamTrack::ReadOnly(Local property, Local value, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; //MediaStreamTrack *self = RTCWrap::Unwrap(info.Holder(), "MediaStreamTrack"); // TODO(): Implement This } void MediaStreamTrack::SetEnabled(Local property, Local value, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; MediaStreamTrack *self = RTCWrap::Unwrap(info.Holder(), "MediaStreamTrack"); if (!value.IsEmpty() && value->IsBoolean()) { self->_track->set_enabled(value->IsTrue() ? true : false); } } void MediaStreamTrack::SetOnStarted(Local property, Local value, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; MediaStreamTrack *self = RTCWrap::Unwrap(info.Holder(), "MediaStreamTrack"); if (!value.IsEmpty() && value->IsFunction()) { self->_onstarted.Reset(Local::Cast(value)); } else { self->_onstarted.Reset(); } } void MediaStreamTrack::SetOnMute(Local property, Local value, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; MediaStreamTrack *self = RTCWrap::Unwrap(info.Holder(), "MediaStreamTrack"); if (!value.IsEmpty() && value->IsFunction()) { self->_onmute.Reset(Local::Cast(value)); } else { self->_onmute.Reset(); } } void MediaStreamTrack::SetOnUnMute(Local property, Local value, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; MediaStreamTrack *self = RTCWrap::Unwrap(info.Holder(), "MediaStreamTrack"); if (!value.IsEmpty() && value->IsFunction()) { self->_onunmute.Reset(Local::Cast(value)); } else { self->_onunmute.Reset(); } } void MediaStreamTrack::SetOnOverConstrained(Local property, Local value, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; MediaStreamTrack *self = RTCWrap::Unwrap(info.Holder(), "MediaStreamTrack"); if (!value.IsEmpty() && value->IsFunction()) { self->_onoverconstrained.Reset(Local::Cast(value)); } else { self->_onoverconstrained.Reset(); } } void MediaStreamTrack::SetOnEnded(Local property, Local value, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; MediaStreamTrack *self = RTCWrap::Unwrap(info.Holder(), "MediaStreamTrack"); if (!value.IsEmpty() && value->IsFunction()) { self->_onended.Reset(Local::Cast(value)); } else { self->_onended.Reset(); } } void MediaStreamTrack::CheckState() { webrtc::MediaStreamTrackInterface::TrackState new_state = _track->state(); webrtc::MediaSourceInterface::SourceState new_source = _source->state(); if (_track_state != new_state) { if (new_state == webrtc::MediaStreamTrackInterface::kEnded || new_state == webrtc::MediaStreamTrackInterface::kFailed) { Local callback = Nan::New(_onended); Local argv[1]; if (!callback.IsEmpty() && callback->IsFunction()) { callback->Call(RTCWrap::This(), 0, argv); } } else if (_track_state == webrtc::MediaStreamTrackInterface::kInitializing && new_state == webrtc::MediaStreamTrackInterface::kLive) { Local callback = Nan::New(_onstarted); Local argv[1]; if (!callback.IsEmpty() && callback->IsFunction()) { callback->Call(RTCWrap::This(), 0, argv); } } } if (_source_state != new_source) { if (new_source == webrtc::MediaSourceInterface::kMuted) { Local callback = Nan::New(_onmute); Local argv[1]; if (!callback.IsEmpty() && callback->IsFunction()) { callback->Call(RTCWrap::This(), 0, argv); } } else { Local callback = Nan::New(_onunmute); Local argv[1]; if (!callback.IsEmpty() && callback->IsFunction()) { callback->Call(RTCWrap::This(), 0, argv); } } } _source_state = new_source; _track_state = new_state; } void MediaStreamTrack::On(Event *event) { LOG(LS_INFO) << __PRETTY_FUNCTION__; Nan::HandleScope scope; MediaStreamTrackEvent type = event->Type(); if (type != kMediaStreamTrackChanged) { return; } MediaStreamTrack::CheckState(); } ================================================ FILE: src/MediaStreamTrack.h ================================================ /* * The MIT License (MIT) * * Copyright (c) 2015 vmolsa (http://github.com/vmolsa) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * */ #ifndef WEBRTC_MEDIASTREAMTRACK_H #define WEBRTC_MEDIASTREAMTRACK_H #include "Common.h" #include "Observers.h" #include "EventEmitter.h" #include "Wrap.h" namespace WebRTC { enum MediaStreamTrackEvent { kMediaStreamTrackChanged }; class MediaStreamTrack : public RTCWrap, public EventEmitter { public: static void Init(); static v8::Local New(rtc::scoped_refptr audioTrack); static v8::Local New(rtc::scoped_refptr videoTrack); static rtc::scoped_refptr Unwrap(v8::Local value); static rtc::scoped_refptr Unwrap(v8::Local value); private: MediaStreamTrack(); ~MediaStreamTrack() final; static void New(const Nan::FunctionCallbackInfo &info); static void GetConstraints(const Nan::FunctionCallbackInfo &info); static void ApplyConstraints(const Nan::FunctionCallbackInfo &info); static void GetSettings(const Nan::FunctionCallbackInfo &info); static void GetCapabilities(const Nan::FunctionCallbackInfo &info); static void Clone(const Nan::FunctionCallbackInfo &info); static void Stop(const Nan::FunctionCallbackInfo &info); static void GetEnabled(v8::Local property, const Nan::PropertyCallbackInfo &info); static void GetId(v8::Local property, const Nan::PropertyCallbackInfo &info); static void GetKind(v8::Local property, const Nan::PropertyCallbackInfo &info); static void GetLabel(v8::Local property, const Nan::PropertyCallbackInfo &info); static void GetMuted(v8::Local property, const Nan::PropertyCallbackInfo &info); static void GetReadOnly(v8::Local property, const Nan::PropertyCallbackInfo &info); static void GetReadyState(v8::Local property, const Nan::PropertyCallbackInfo &info); static void GetRemote(v8::Local property, const Nan::PropertyCallbackInfo &info); static void GetOnStarted(v8::Local property, const Nan::PropertyCallbackInfo &info); static void GetOnMute(v8::Local property, const Nan::PropertyCallbackInfo &info); static void GetOnUnMute(v8::Local property, const Nan::PropertyCallbackInfo &info); static void GetOnOverConstrained(v8::Local property, const Nan::PropertyCallbackInfo &info); static void GetOnEnded(v8::Local property, const Nan::PropertyCallbackInfo &info); static void ReadOnly(v8::Local property, v8::Local value, const Nan::PropertyCallbackInfo &info); static void SetEnabled(v8::Local property, v8::Local value, const Nan::PropertyCallbackInfo &info); static void SetOnStarted(v8::Local property, v8::Local value, const Nan::PropertyCallbackInfo &info); static void SetOnMute(v8::Local property, v8::Local value, const Nan::PropertyCallbackInfo &info); static void SetOnUnMute(v8::Local property, v8::Local value, const Nan::PropertyCallbackInfo &info); static void SetOnOverConstrained(v8::Local property, v8::Local value, const Nan::PropertyCallbackInfo &info); static void SetOnEnded(v8::Local property, v8::Local value, const Nan::PropertyCallbackInfo &info); void CheckState(); void On(Event *event) final; protected: bool isAudioTrack; bool isVideoTrack; rtc::scoped_refptr _track; rtc::scoped_refptr _source; rtc::scoped_refptr _observer; webrtc::MediaStreamTrackInterface::TrackState _track_state; webrtc::MediaSourceInterface::SourceState _source_state; Nan::Persistent _onstarted; Nan::Persistent _onmute; Nan::Persistent _onunmute; Nan::Persistent _onoverconstrained; Nan::Persistent _onended; static Nan::Persistent constructor; }; }; #endif ================================================ FILE: src/Module.cc ================================================ /* * The MIT License (MIT) * * Copyright (c) 2015 vmolsa (http://github.com/vmolsa) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * */ #include "Common.h" #include "Global.h" #include "Platform.h" #include "Stats.h" #include "PeerConnection.h" #include "DataChannel.h" #include "BackTrace.h" #include "GetSources.h" #include "GetUserMedia.h" #include "MediaStream.h" #include "MediaStreamTrack.h" using namespace v8; void SetDebug(const Nan::FunctionCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; if (info.Length() && !info[0].IsEmpty()) { if (info[0]->IsTrue()) { rtc::LogMessage::LogToDebug(rtc::LS_VERBOSE); } else { rtc::LogMessage::LogToDebug(rtc::LS_NONE); } } info.GetReturnValue().SetUndefined(); } void RTCGarbageCollect(const Nan::FunctionCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; Nan::LowMemoryNotification(); info.GetReturnValue().SetUndefined(); } void RTCIceCandidate(const Nan::FunctionCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; if (info.Length() == 1 && info[0]->IsObject() && info.IsConstructCall()) { Local arg = info[0]->ToObject(); Local retval = Nan::New(); retval->Set(Nan::New("candidate").ToLocalChecked(), arg->Get(Nan::New("candidate").ToLocalChecked())); retval->Set(Nan::New("sdpMLineIndex").ToLocalChecked(), arg->Get(Nan::New("sdpMLineIndex").ToLocalChecked())); retval->Set(Nan::New("sdpMid").ToLocalChecked(), arg->Get(Nan::New("sdpMid").ToLocalChecked())); return info.GetReturnValue().Set(retval); } else { return info.GetReturnValue().Set(info[0]); } } void RTCSessionDescription(const Nan::FunctionCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; if (info.Length() == 1 && info[0]->IsObject() && info.IsConstructCall()) { Local arg = info[0]->ToObject(); Local retval = Nan::New(); retval->Set(Nan::New("type").ToLocalChecked(), arg->Get(Nan::New("type").ToLocalChecked())); retval->Set(Nan::New("sdp").ToLocalChecked(), arg->Get(Nan::New("sdp").ToLocalChecked())); return info.GetReturnValue().Set(retval); } else { return info.GetReturnValue().Set(info[0]); } } void WebrtcModuleDispose(void *arg) { LOG(LS_INFO) << __PRETTY_FUNCTION__; WebRTC::Platform::Dispose(); } void WebrtcModuleInit(Handle exports) { LOG(LS_INFO) << __PRETTY_FUNCTION__; Nan::HandleScope scope; WebRTC::Global::Init(exports); WebRTC::Platform::Init(); WebRTC::RTCStatsResponse::Init(); WebRTC::RTCStatsReport::Init(); WebRTC::PeerConnection::Init(exports); WebRTC::DataChannel::Init(); WebRTC::GetSources::Init(exports); WebRTC::GetUserMedia::Init(exports); WebRTC::MediaStream::Init(); WebRTC::MediaStreamTrack::Init(); exports->Set(Nan::New("RTCGarbageCollect").ToLocalChecked(), Nan::New(RTCGarbageCollect)->GetFunction()); exports->Set(Nan::New("RTCIceCandidate").ToLocalChecked(), Nan::New(RTCIceCandidate)->GetFunction()); exports->Set(Nan::New("RTCSessionDescription").ToLocalChecked(), Nan::New(RTCSessionDescription)->GetFunction()); exports->Set(Nan::New("setDebug").ToLocalChecked(), Nan::New(SetDebug)->GetFunction()); node::AtExit(WebrtcModuleDispose); } NODE_MODULE(webrtc, WebrtcModuleInit) ================================================ FILE: src/Observers.cc ================================================ /* * The MIT License (MIT) * * Copyright (c) 2015 vmolsa (http://github.com/vmolsa) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * */ #include "Observers.h" #include "PeerConnection.h" #include "DataChannel.h" #include "MediaStream.h" #include "MediaStreamTrack.h" using namespace WebRTC; OfferObserver::OfferObserver(EventEmitter *listener) : NotifyEmitter(listener) { } void OfferObserver::OnSuccess(webrtc::SessionDescriptionInterface* desc) { LOG(LS_INFO) << __PRETTY_FUNCTION__; Json::StyledWriter writer; Json::Value msg; std::string sdp; if (desc->ToString(&sdp)) { msg["type"] = desc->type(); msg["sdp"] = sdp; Emit(kPeerConnectionCreateOffer, writer.write(msg)); } } void OfferObserver::OnFailure(const std::string &error) { LOG(LS_ERROR) << __PRETTY_FUNCTION__; Emit(kPeerConnectionCreateOfferError, error); } AnswerObserver::AnswerObserver(EventEmitter *listener) : NotifyEmitter(listener) { } void AnswerObserver::OnSuccess(webrtc::SessionDescriptionInterface* desc) { LOG(LS_INFO) << __PRETTY_FUNCTION__; Json::StyledWriter writer; Json::Value msg; std::string sdp; if (desc->ToString(&sdp)) { msg["type"] = desc->type(); msg["sdp"] = sdp; Emit(kPeerConnectionCreateAnswer, writer.write(msg)); } } void AnswerObserver::OnFailure(const std::string &error) { LOG(LS_ERROR) << __PRETTY_FUNCTION__; Emit(kPeerConnectionCreateAnswerError, error); } LocalDescriptionObserver::LocalDescriptionObserver(EventEmitter *listener) : NotifyEmitter(listener) { } void LocalDescriptionObserver::OnSuccess() { LOG(LS_INFO) << __PRETTY_FUNCTION__; Emit(kPeerConnectionSetLocalDescription); } void LocalDescriptionObserver::OnFailure(const std::string &error) { LOG(LS_ERROR) << __PRETTY_FUNCTION__; Emit(kPeerConnectionSetLocalDescriptionError, error); } RemoteDescriptionObserver::RemoteDescriptionObserver(EventEmitter *listener) : NotifyEmitter(listener) { } void RemoteDescriptionObserver::OnSuccess() { LOG(LS_INFO) << __PRETTY_FUNCTION__; Emit(kPeerConnectionSetRemoteDescription); } void RemoteDescriptionObserver::OnFailure(const std::string &error) { LOG(LS_ERROR) << __PRETTY_FUNCTION__; Emit(kPeerConnectionSetRemoteDescriptionError, error); } PeerConnectionObserver::PeerConnectionObserver(EventEmitter *listener) : NotifyEmitter(listener) { } void PeerConnectionObserver::OnSignalingChange(webrtc::PeerConnectionInterface::SignalingState state) { LOG(LS_INFO) << __PRETTY_FUNCTION__; Emit(kPeerConnectionSignalChange); if (state == webrtc::PeerConnectionInterface::kClosed) { Emit(kPeerConnectionCreateClosed); } } void PeerConnectionObserver::OnIceConnectionChange(webrtc::PeerConnectionInterface::IceConnectionState state) { LOG(LS_INFO) << __PRETTY_FUNCTION__; Emit(kPeerConnectionIceChange); } void PeerConnectionObserver::OnIceGatheringChange(webrtc::PeerConnectionInterface::IceGatheringState state) { LOG(LS_INFO) << __PRETTY_FUNCTION__; Emit(kPeerConnectionIceGathering); if (state == webrtc::PeerConnectionInterface::kIceGatheringComplete) { Emit(kPeerConnectionIceCandidate, std::string()); } } void PeerConnectionObserver::OnStateChange(webrtc::PeerConnectionObserver::StateType state) { LOG(LS_INFO) << __PRETTY_FUNCTION__; } void PeerConnectionObserver::OnDataChannel(webrtc::DataChannelInterface *channel) { LOG(LS_INFO) << __PRETTY_FUNCTION__; rtc::scoped_refptr dataChannel = channel; if (dataChannel.get()) { Emit(kPeerConnectionDataChannel, dataChannel); } } void PeerConnectionObserver::OnAddStream(webrtc::MediaStreamInterface *stream) { LOG(LS_INFO) << __PRETTY_FUNCTION__; rtc::scoped_refptr mediaStream = stream; if (mediaStream.get()) { Emit(kPeerConnectionAddStream, mediaStream); } } void PeerConnectionObserver::OnRemoveStream(webrtc::MediaStreamInterface *stream) { LOG(LS_INFO) << __PRETTY_FUNCTION__; rtc::scoped_refptr mediaStream = stream; if (mediaStream.get()) { Emit(kPeerConnectionRemoveStream, mediaStream); } } void PeerConnectionObserver::OnRenegotiationNeeded() { LOG(LS_INFO) << __PRETTY_FUNCTION__; Emit(kPeerConnectionRenegotiation); } void PeerConnectionObserver::OnIceCandidate(const webrtc::IceCandidateInterface* candidate) { LOG(LS_INFO) << __PRETTY_FUNCTION__; Json::StyledWriter writer; Json::Value msg; std::string sdp; if (candidate->ToString(&sdp)) { msg["sdpMid"] = candidate->sdp_mid(); msg["sdpMLineIndex"] = candidate->sdp_mline_index(); msg["candidate"] = sdp; Emit(kPeerConnectionIceCandidate, writer.write(msg)); } } DataChannelObserver::DataChannelObserver(EventEmitter *listener) : NotifyEmitter(listener) { } void DataChannelObserver::OnStateChange() { LOG(LS_INFO) << __PRETTY_FUNCTION__; Emit(kDataChannelStateChange); } void DataChannelObserver::OnMessage(const webrtc::DataBuffer& buffer) { LOG(LS_INFO) << __PRETTY_FUNCTION__; if (buffer.binary) { Emit(kDataChannelBinary, buffer.data); } else { Emit(kDataChannelData, buffer.data); } } MediaStreamObserver::MediaStreamObserver(EventEmitter *listener) : NotifyEmitter(listener) { } void MediaStreamObserver::OnChanged() { LOG(LS_INFO) << __PRETTY_FUNCTION__; Emit(kMediaStreamChanged); } MediaStreamTrackObserver::MediaStreamTrackObserver(EventEmitter *listener) : NotifyEmitter(listener) { } void MediaStreamTrackObserver::OnChanged() { LOG(LS_INFO) << __PRETTY_FUNCTION__; Emit(kMediaStreamTrackChanged); } StatsObserver::StatsObserver(EventEmitter *listener) : NotifyEmitter(listener) { } void StatsObserver::OnComplete(const webrtc::StatsReports &reports) { LOG(LS_INFO) << "StatsObserver::OnComplete()"; Emit(kPeerConnectionStats, reports); } ================================================ FILE: src/Observers.h ================================================ /* * The MIT License (MIT) * * Copyright (c) 2015 vmolsa (http://github.com/vmolsa) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * */ #ifndef WEBRTC_OBSERVERS_H #define WEBRTC_OBSERVERS_H #include "EventEmitter.h" namespace WebRTC { class OfferObserver : public webrtc::CreateSessionDescriptionObserver, public NotifyEmitter { public: OfferObserver(EventEmitter *listener = 0); void OnSuccess(webrtc::SessionDescriptionInterface* sdp) final; void OnFailure(const std::string &error) final; }; class AnswerObserver : public webrtc::CreateSessionDescriptionObserver, public NotifyEmitter { public: AnswerObserver(EventEmitter *listener = 0); void OnSuccess(webrtc::SessionDescriptionInterface* sdp) final; void OnFailure(const std::string &error) final; }; class LocalDescriptionObserver : public webrtc::SetSessionDescriptionObserver, public NotifyEmitter { public: LocalDescriptionObserver(EventEmitter *listener = 0); void OnSuccess() final; void OnFailure(const std::string &error) final; }; class RemoteDescriptionObserver : public webrtc::SetSessionDescriptionObserver, public NotifyEmitter { public: RemoteDescriptionObserver(EventEmitter *listener = 0); void OnSuccess() final; void OnFailure(const std::string &error) final; }; class PeerConnectionObserver : public webrtc::PeerConnectionObserver, public rtc::RefCountInterface, public NotifyEmitter { public: PeerConnectionObserver(EventEmitter *listener = 0); void OnSignalingChange(webrtc::PeerConnectionInterface::SignalingState state) final; void OnIceConnectionChange(webrtc::PeerConnectionInterface::IceConnectionState state) final; void OnIceGatheringChange(webrtc::PeerConnectionInterface::IceGatheringState state) final; void OnStateChange(webrtc::PeerConnectionObserver::StateType state); void OnIceCandidate(const webrtc::IceCandidateInterface* candidate) final; void OnDataChannel(webrtc::DataChannelInterface* channel) final; void OnRenegotiationNeeded() final; void OnAddStream(webrtc::MediaStreamInterface* stream) final; void OnRemoveStream(webrtc::MediaStreamInterface* stream) final; }; class DataChannelObserver : public webrtc::DataChannelObserver, public rtc::RefCountInterface, public NotifyEmitter { public: DataChannelObserver(EventEmitter *listener = 0); void OnStateChange() final; void OnMessage(const webrtc::DataBuffer& buffer) final; }; class MediaStreamObserver : public webrtc::ObserverInterface, public rtc::RefCountInterface, public NotifyEmitter { public: MediaStreamObserver(EventEmitter *listener = 0); void OnChanged() final; }; class MediaStreamTrackObserver : public webrtc::ObserverInterface, public rtc::RefCountInterface, public NotifyEmitter { public: MediaStreamTrackObserver(EventEmitter *listener = 0); void OnChanged() final; }; class StatsObserver : public webrtc::StatsObserver, public NotifyEmitter { public: StatsObserver(EventEmitter *listener = 0); void OnComplete(const webrtc::StatsReports& reports) final; }; }; #endif ================================================ FILE: src/PeerConnection.cc ================================================ /* * The MIT License (MIT) * * Copyright (c) 2015 vmolsa (http://github.com/vmolsa) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * */ #include #include "Global.h" #include "Platform.h" #include "PeerConnection.h" #include "DataChannel.h" #include "MediaStream.h" #include "Stats.h" using namespace v8; using namespace WebRTC; void PeerConnection::Init(Handle exports) { LOG(LS_INFO) << __PRETTY_FUNCTION__; Nan::HandleScope scope; Local tpl = Nan::New(PeerConnection::New); tpl->InstanceTemplate()->SetInternalFieldCount(1); tpl->SetClassName(Nan::New("RTCPeerConnection").ToLocalChecked()); Nan::SetPrototypeMethod(tpl, "createOffer", PeerConnection::CreateOffer); Nan::SetPrototypeMethod(tpl, "createAnswer", PeerConnection::CreateAnswer); Nan::SetPrototypeMethod(tpl, "setLocalDescription", PeerConnection::SetLocalDescription); Nan::SetPrototypeMethod(tpl, "setRemoteDescription", PeerConnection::SetRemoteDescription); Nan::SetPrototypeMethod(tpl, "addIceCandidate", PeerConnection::AddIceCandidate); Nan::SetPrototypeMethod(tpl, "createDataChannel", PeerConnection::CreateDataChannel); Nan::SetPrototypeMethod(tpl, "addStream", PeerConnection::AddStream); Nan::SetPrototypeMethod(tpl, "removeStream", PeerConnection::RemoveStream); Nan::SetPrototypeMethod(tpl, "getLocalStreams", PeerConnection::GetLocalStreams); Nan::SetPrototypeMethod(tpl, "getRemoteStreams", PeerConnection::GetRemoteStreams); Nan::SetPrototypeMethod(tpl, "getStreamById", PeerConnection::GetStreamById); Nan::SetPrototypeMethod(tpl, "getStats", PeerConnection::GetStats); Nan::SetPrototypeMethod(tpl, "close", PeerConnection::Close); Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("signalingState").ToLocalChecked(), PeerConnection::GetSignalingState); Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("iceConnectionState").ToLocalChecked(), PeerConnection::GetIceConnectionState); Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("iceGatheringState").ToLocalChecked(), PeerConnection::GetIceGatheringState); Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("localDescription").ToLocalChecked(), PeerConnection::GetLocalDescription); Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("remoteDescription").ToLocalChecked(), PeerConnection::GetRemoteDescription); Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("onsignalingstatechange").ToLocalChecked(), PeerConnection::GetOnSignalingStateChange, PeerConnection::SetOnSignalingStateChange); Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("oniceconnectionstatechange").ToLocalChecked(), PeerConnection::GetOnIceConnectionStateChange, PeerConnection::SetOnIceConnectionStateChange); Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("onicecandidate").ToLocalChecked(), PeerConnection::GetOnIceCandidate, PeerConnection::SetOnIceCandidate); Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("ondatachannel").ToLocalChecked(), PeerConnection::GetOnDataChannel, PeerConnection::SetOnDataChannel); Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("onnegotiationneeded").ToLocalChecked(), PeerConnection::GetOnNegotiationNeeded, PeerConnection::SetOnNegotiationNeeded); Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("onaddstream").ToLocalChecked(), PeerConnection::GetOnAddStream, PeerConnection::SetOnAddStream); Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("onremovestream").ToLocalChecked(), PeerConnection::GetOnRemoveStream, PeerConnection::SetOnRemoveStream); constructor.Reset(tpl->GetFunction()); exports->Set(Nan::New("RTCPeerConnection").ToLocalChecked(), tpl->GetFunction()); } Nan::Persistent PeerConnection::constructor; PeerConnection::PeerConnection(const Local &configuration, const Local &constraints) { LOG(LS_INFO) << __PRETTY_FUNCTION__; if (!configuration.IsEmpty()) { Local iceservers_value = configuration->Get(Nan::New("iceServers").ToLocalChecked()); if (!iceservers_value.IsEmpty() && iceservers_value->IsArray()) { Local list = Local::Cast(iceservers_value); for (unsigned int index = 0; index < list->Length(); index++) { Local server_value = list->Get(index); if (!server_value.IsEmpty() && server_value->IsObject()) { Local server = Local::Cast(server_value); Local url_value = server->Get(Nan::New("url").ToLocalChecked()); Local username_value = server->Get(Nan::New("username").ToLocalChecked()); Local credential_value = server->Get(Nan::New("credential").ToLocalChecked()); if (!url_value.IsEmpty() && url_value->IsString()) { v8::String::Utf8Value url(url_value->ToString()); webrtc::PeerConnectionInterface::IceServer entry; entry.uri = *url; if (!username_value.IsEmpty() && username_value->IsString()) { String::Utf8Value username(username_value->ToString()); entry.username = *username; } if (!credential_value.IsEmpty() && credential_value->IsString()) { String::Utf8Value credential(credential_value->ToString()); entry.password = *credential; } _config.servers.push_back(entry); } } } } } _constraints = MediaConstraints::New(constraints); if (!_constraints->GetOptional("RtpDataChannels")) { if (!_constraints->IsOptional("DtlsSrtpKeyAgreement")) { _constraints->SetOptional("DtlsSrtpKeyAgreement", "true"); } } _stats = new rtc::RefCountedObject(this); _offer = new rtc::RefCountedObject(this); _answer = new rtc::RefCountedObject(this); _local = new rtc::RefCountedObject(this); _remote = new rtc::RefCountedObject(this); _peer = new rtc::RefCountedObject(this); _factory = webrtc::CreatePeerConnectionFactory(rtc::Thread::Current(), Platform::GetWorker(), 0, 0, 0); } PeerConnection::~PeerConnection() { LOG(LS_INFO) << __PRETTY_FUNCTION__; if (_socket.get()) { webrtc::PeerConnectionInterface::SignalingState state(_socket->signaling_state()); if (state != webrtc::PeerConnectionInterface::kClosed) { _socket->Close(); } } _stats->RemoveListener(this); _offer->RemoveListener(this); _answer->RemoveListener(this); _local->RemoveListener(this); _remote->RemoveListener(this); _peer->RemoveListener(this); } webrtc::PeerConnectionInterface *PeerConnection::GetSocket() { LOG(LS_INFO) << __PRETTY_FUNCTION__; if (!_socket.get()) { if (_factory.get()) { EventEmitter::SetReference(true); _socket = _factory->CreatePeerConnection(_config, _constraints->ToConstraints(), NULL, NULL, _peer.get()); if (!_socket.get()) { Nan::ThrowError("Internal Socket Error"); } } else { Nan::ThrowError("Internal Factory Error"); } } return _socket.get(); } void PeerConnection::New(const Nan::FunctionCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; Local configuration; Local constraints; if (info.Length() >= 1 && info[0]->IsObject()) { configuration = Local::Cast(info[0]); if (info.Length() >= 2 && info[1]->IsObject()) { constraints = Local::Cast(info[1]); } } if (info.IsConstructCall()) { PeerConnection* peer = new PeerConnection(configuration, constraints); peer->Wrap(info.This(), "PeerConnection"); return info.GetReturnValue().Set(info.This()); } else { const int argc = 2; Local argv[argc] = { configuration, constraints }; Local instance = Nan::New(PeerConnection::constructor); return info.GetReturnValue().Set(instance->NewInstance(argc, argv)); } } void PeerConnection::CreateOffer(const Nan::FunctionCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; PeerConnection *self = RTCWrap::Unwrap(info.This(), "PeerConnection"); webrtc::PeerConnectionInterface *socket = self->GetSocket(); if (!info[0].IsEmpty() && info[0]->IsFunction()) { self->_offerCallback.Reset(Local::Cast(info[0])); } else { self->_offerCallback.Reset(); } if (!info[1].IsEmpty() && info[1]->IsFunction()) { self->_offerErrorCallback.Reset(Local::Cast(info[1])); } else { self->_offerErrorCallback.Reset(); } if (socket) { socket->CreateOffer(self->_offer.get(), self->_constraints->ToConstraints()); } else { Nan::ThrowError("Internal Error"); } info.GetReturnValue().SetUndefined(); } void PeerConnection::CreateAnswer(const Nan::FunctionCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; PeerConnection *self = RTCWrap::Unwrap(info.This(), "PeerConnection"); webrtc::PeerConnectionInterface *socket = self->GetSocket(); if (!info[0].IsEmpty() && info[0]->IsFunction()) { self->_answerCallback.Reset(Local::Cast(info[0])); } else { self->_answerCallback.Reset(); } if (!info[1].IsEmpty() && info[1]->IsFunction()) { self->_answerErrorCallback.Reset(Local::Cast(info[1])); } else { self->_answerErrorCallback.Reset(); } if (socket) { socket->CreateAnswer(self->_answer.get(), self->_constraints->ToConstraints()); } else { Nan::ThrowError("Internal Error"); } info.GetReturnValue().SetUndefined(); } void PeerConnection::SetLocalDescription(const Nan::FunctionCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; PeerConnection *self = RTCWrap::Unwrap(info.This(), "PeerConnection"); webrtc::PeerConnectionInterface *socket = self->GetSocket(); const char *error = "Invalid SessionDescription"; if (!info[0].IsEmpty() && info[0]->IsObject()) { Local desc_obj = Local::Cast(info[0]); Local type_value = desc_obj->Get(Nan::New("type").ToLocalChecked()); Local sdp_value = desc_obj->Get(Nan::New("sdp").ToLocalChecked()); if (!type_value.IsEmpty() && type_value->IsString()) { if (!sdp_value.IsEmpty() && sdp_value->IsString()) { if (!info[1].IsEmpty() && info[1]->IsFunction()) { self->_localCallback.Reset(Local::Cast(info[1])); } else { self->_localCallback.Reset(); } if (!info[2].IsEmpty() && info[2]->IsFunction()) { self->_localErrorCallback.Reset(Local::Cast(info[2])); } else { self->_localErrorCallback.Reset(); } String::Utf8Value type(type_value->ToString()); String::Utf8Value sdp(sdp_value->ToString()); webrtc::SessionDescriptionInterface *desc(webrtc::CreateSessionDescription(*type, *sdp, 0)); if (desc) { if (socket) { self->_localsdp.Reset(desc_obj); socket->SetLocalDescription(self->_local.get(), desc); error = 0; } else { error = "Internal Error"; } } } } } if (error) { if (!info[2].IsEmpty() && info[2]->IsFunction()) { Local argv[1] = { Nan::Error(error) }; Local onerror = Local::Cast(info[2]); onerror->Call(info.This(), 1, argv); } else { Nan::ThrowError(error); } } info.GetReturnValue().SetUndefined(); } void PeerConnection::SetRemoteDescription(const Nan::FunctionCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; PeerConnection *self = RTCWrap::Unwrap(info.This(), "PeerConnection"); webrtc::PeerConnectionInterface *socket = self->GetSocket(); const char *error = "Invalid SessionDescription"; if (!info[0].IsEmpty() && info[0]->IsObject()) { Local desc_obj = Local::Cast(info[0]); Local type_value = desc_obj->Get(Nan::New("type").ToLocalChecked()); Local sdp_value = desc_obj->Get(Nan::New("sdp").ToLocalChecked()); if (!type_value.IsEmpty() && type_value->IsString()) { if (!sdp_value.IsEmpty() && sdp_value->IsString()) { if (!info[1].IsEmpty() && info[1]->IsFunction()) { self->_remoteCallback.Reset(Local::Cast(info[1])); } else { self->_remoteCallback.Reset(); } if (!info[2].IsEmpty() && info[2]->IsFunction()) { self->_remoteErrorCallback.Reset(Local::Cast(info[2])); } else { self->_remoteErrorCallback.Reset(); } String::Utf8Value type(type_value->ToString()); String::Utf8Value sdp(sdp_value->ToString()); webrtc::SessionDescriptionInterface *desc(webrtc::CreateSessionDescription(*type, *sdp, 0)); if (desc) { if (socket) { self->_remotesdp.Reset(desc_obj); socket->SetRemoteDescription(self->_remote.get(), desc); error = 0; } else { error = "Internal Error"; } } } } } if (error) { if (!info[2].IsEmpty() && info[2]->IsFunction()) { Local argv[1] = { Nan::Error(error) }; Local onerror = Local::Cast(info[2]); onerror->Call(info.This(), 1, argv); } else { Nan::ThrowError(error); } } info.GetReturnValue().SetUndefined(); } void PeerConnection::AddIceCandidate(const Nan::FunctionCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; PeerConnection *self = RTCWrap::Unwrap(info.This(), "PeerConnection"); webrtc::PeerConnectionInterface *socket = self->GetSocket(); const char *error = 0; Local argv[1]; if (!info[0].IsEmpty() && info[0]->IsObject()) { Local desc = Local::Cast(info[0]); Local sdpMid_value = desc->Get(Nan::New("sdpMid").ToLocalChecked()); Local sdpMLineIndex_value = desc->Get(Nan::New("sdpMLineIndex").ToLocalChecked()); Local sdp_value = desc->Get(Nan::New("candidate").ToLocalChecked()); if (!sdpMid_value.IsEmpty() && sdpMid_value->IsString()) { if (!sdpMLineIndex_value.IsEmpty() && sdpMLineIndex_value->IsInt32()) { if (!sdp_value.IsEmpty() && sdp_value->IsString()) { Local sdpMLineIndex(sdpMLineIndex_value->ToInt32()); String::Utf8Value sdpMid(sdpMid_value->ToString()); String::Utf8Value sdp(sdp_value->ToString()); rtc::scoped_ptr candidate(webrtc::CreateIceCandidate(*sdpMid, sdpMLineIndex->Value(), *sdp, 0)); if (candidate.get()) { if (socket) { if (socket->AddIceCandidate(candidate.get())) { if (!info[1].IsEmpty() && info[1]->IsFunction()) { Local success = Local::Cast(info[1]); success->Call(info.This(), 0, argv); } } else { error = "Failed to add ICECandidate"; } } else { error = "Internal Error"; } } else { error = "Invalid ICECandidate"; } } else { error = "Invalid candidate"; } } else { error = "Invalid sdpMLineIndex"; } } else { error = "Invalid sdpMid"; } } if (error) { if (!info[2].IsEmpty() && info[2]->IsFunction()) { argv[0] = Nan::Error(error); Local onerror = Local::Cast(info[2]); onerror->Call(info.This(), 1, argv); } else { Nan::ThrowError(error); } } info.GetReturnValue().SetUndefined(); } void PeerConnection::CreateDataChannel(const Nan::FunctionCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; PeerConnection *self = RTCWrap::Unwrap(info.This(), "PeerConnection"); webrtc::PeerConnectionInterface *socket = self->GetSocket(); std::string label; webrtc::DataChannelInit config; if (!info[0].IsEmpty() && info[0]->IsString()) { String::Utf8Value label_utf8(info[0]->ToString()); label = *label_utf8; } if (!info[1].IsEmpty() && info[1]->IsObject()) { Local config_obj = Local::Cast(info[0]); Local reliable_value = config_obj->Get(Nan::New("reliable").ToLocalChecked()); Local ordered_value = config_obj->Get(Nan::New("ordered").ToLocalChecked()); Local maxRetransmitTime_value = config_obj->Get(Nan::New("maxRetransmitTime").ToLocalChecked()); Local maxRetransmits_value = config_obj->Get(Nan::New("maxRetransmits").ToLocalChecked()); Local protocol_value = config_obj->Get(Nan::New("protocol").ToLocalChecked()); Local id_value = config_obj->Get(Nan::New("id").ToLocalChecked()); if (!reliable_value.IsEmpty()) { if (reliable_value->IsTrue()) { config.reliable = true; } else { config.reliable = false; } } if (!ordered_value.IsEmpty()) { if (ordered_value->IsTrue()) { config.ordered = true; } else { config.ordered = false; } } if (!maxRetransmitTime_value.IsEmpty() && maxRetransmitTime_value->IsInt32()) { Local maxRetransmitTime(maxRetransmitTime_value->ToInt32()); config.maxRetransmitTime = maxRetransmitTime->Value(); } if (!maxRetransmits_value.IsEmpty() && maxRetransmits_value->IsInt32()) { Local maxRetransmits(maxRetransmits_value->ToInt32()); config.maxRetransmits = maxRetransmits->Value(); } if (!protocol_value.IsEmpty() && protocol_value->IsString()) { String::Utf8Value protocol(protocol_value->ToString()); config.protocol = *protocol; } if (!id_value.IsEmpty() && id_value->IsInt32()) { Local id(id_value->ToInt32()); config.id = id->Value(); } } if (socket) { rtc::scoped_refptr dataChannel = socket->CreateDataChannel(label, &config); if (dataChannel.get()) { return info.GetReturnValue().Set(DataChannel::New(dataChannel)); } } Nan::ThrowError("Internal Error"); info.GetReturnValue().SetUndefined(); } void PeerConnection::AddStream(const Nan::FunctionCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; PeerConnection *self = RTCWrap::Unwrap(info.This(), "PeerConnection"); rtc::scoped_refptr mediaStream = MediaStream::Unwrap(info[0]); if (mediaStream.get()) { webrtc::PeerConnectionInterface *socket = self->GetSocket(); if (socket) { if (!socket->AddStream(mediaStream)) { Nan::ThrowError("AddStream Failed"); } } else { Nan::ThrowError("Internal Error"); } } else { Nan::ThrowError("Invalid MediaStream Object"); } info.GetReturnValue().SetUndefined(); } void PeerConnection::RemoveStream(const Nan::FunctionCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; PeerConnection *self = RTCWrap::Unwrap(info.This(), "PeerConnection"); rtc::scoped_refptr mediaStream = MediaStream::Unwrap(info[0]); if (mediaStream.get()) { webrtc::PeerConnectionInterface *socket = self->GetSocket(); if (socket) { socket->RemoveStream(mediaStream); } else { Nan::ThrowError("Internal Error"); } } else { Nan::ThrowError("Invalid MediaStream Object"); } info.GetReturnValue().SetUndefined(); } void PeerConnection::GetLocalStreams(const Nan::FunctionCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; PeerConnection *self = RTCWrap::Unwrap(info.This(), "PeerConnection"); webrtc::PeerConnectionInterface *socket = self->GetSocket(); if (socket) { rtc::scoped_refptr local = socket->local_streams(); if (local.get()) { Local list = Nan::New(); uint32_t index = 0; size_t count; for (count = 0; count < local->count(); count++) { list->Set(index, MediaStream::New(local->at(count))); } return info.GetReturnValue().Set(list); } } Nan::ThrowError("Internal Error"); info.GetReturnValue().SetUndefined(); } void PeerConnection::GetRemoteStreams(const Nan::FunctionCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; PeerConnection *self = RTCWrap::Unwrap(info.This(), "PeerConnection"); webrtc::PeerConnectionInterface *socket = self->GetSocket(); if (socket) { rtc::scoped_refptr remote = socket->remote_streams(); if (remote.get()) { Local list = Nan::New(); uint32_t index = 0; size_t count; for (count = 0; count < remote->count(); count++) { list->Set(index, MediaStream::New(remote->at(count))); } return info.GetReturnValue().Set(list); } } Nan::ThrowError("Internal Error"); info.GetReturnValue().SetUndefined(); } void PeerConnection::GetStreamById(const Nan::FunctionCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; PeerConnection *self = RTCWrap::Unwrap(info.This(), "PeerConnection"); webrtc::PeerConnectionInterface *socket = self->GetSocket(); if (socket) { if (info.Length() >= 1 && info[0]->IsString()) { v8::String::Utf8Value idValue(info[0]->ToString()); std::string id(*idValue); rtc::scoped_refptr local = socket->local_streams(); rtc::scoped_refptr remote = socket->remote_streams(); rtc::scoped_refptr stream; if (local.get()) { stream = local->find(id); } if (remote.get() && !stream.get()) { stream = remote->find(id); } if (stream.get()) { return info.GetReturnValue().Set(MediaStream::New(stream)); } else { return info.GetReturnValue().Set(Nan::Null()); } } else { Nan::ThrowError("Invalid Argument"); } } Nan::ThrowError("Internal Error"); info.GetReturnValue().SetUndefined(); } void PeerConnection::GetStats(const Nan::FunctionCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; PeerConnection *self = RTCWrap::Unwrap(info.This(), "PeerConnection"); webrtc::PeerConnectionInterface *socket = self->GetSocket(); if (!info[0].IsEmpty() && info[0]->IsFunction()) { self->_onstats.Reset(Local::Cast(info[0])); if (socket) { if (!socket->GetStats(self->_stats.get(), 0, webrtc::PeerConnectionInterface::kStatsOutputLevelStandard)) { Local callback = Nan::New(self->_onstats); Local argv[1] = { Nan::Null() }; callback->Call(info.This(), 1, argv); self->_onstats.Reset(); } } else { Nan::ThrowError("Internal Error"); } } else { Nan::ThrowError("Missing Callback"); } info.GetReturnValue().SetUndefined(); } void PeerConnection::Close(const Nan::FunctionCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; PeerConnection *self = RTCWrap::Unwrap(info.This(), "PeerConnection"); webrtc::PeerConnectionInterface *socket = self->GetSocket(); if (socket) { socket->Close(); } else { Nan::ThrowError("Internal Error"); } info.GetReturnValue().SetUndefined(); } void PeerConnection::GetSignalingState(Local property, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; PeerConnection *self = RTCWrap::Unwrap(info.Holder(), "PeerConnection"); webrtc::PeerConnectionInterface *socket = self->GetSocket(); if (socket) { webrtc::PeerConnectionInterface::SignalingState state(socket->signaling_state()); switch (state) { case webrtc::PeerConnectionInterface::kStable: return info.GetReturnValue().Set(Nan::New("stable").ToLocalChecked()); break; case webrtc::PeerConnectionInterface::kHaveLocalOffer: return info.GetReturnValue().Set(Nan::New("have-local-offer").ToLocalChecked()); break; case webrtc::PeerConnectionInterface::kHaveLocalPrAnswer: return info.GetReturnValue().Set(Nan::New("have-local-pranswer").ToLocalChecked()); break; case webrtc::PeerConnectionInterface::kHaveRemoteOffer: return info.GetReturnValue().Set(Nan::New("have-remote-offer").ToLocalChecked()); break; case webrtc::PeerConnectionInterface::kHaveRemotePrAnswer: return info.GetReturnValue().Set(Nan::New("have-remote-pranswer").ToLocalChecked()); break; default: return info.GetReturnValue().Set(Nan::New("closed").ToLocalChecked()); break; } } else { Nan::ThrowError("Internal Error"); } info.GetReturnValue().SetUndefined(); } void PeerConnection::GetIceConnectionState(Local property, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; PeerConnection *self = RTCWrap::Unwrap(info.Holder(), "PeerConnection"); webrtc::PeerConnectionInterface *socket = self->GetSocket(); if (socket) { webrtc::PeerConnectionInterface::IceConnectionState state(socket->ice_connection_state()); switch (state) { case webrtc::PeerConnectionInterface::kIceConnectionNew: return info.GetReturnValue().Set(Nan::New("new").ToLocalChecked()); break; case webrtc::PeerConnectionInterface::kIceConnectionChecking: return info.GetReturnValue().Set(Nan::New("checking").ToLocalChecked()); break; case webrtc::PeerConnectionInterface::kIceConnectionConnected: return info.GetReturnValue().Set(Nan::New("connected").ToLocalChecked()); break; case webrtc::PeerConnectionInterface::kIceConnectionCompleted: return info.GetReturnValue().Set(Nan::New("completed").ToLocalChecked()); break; case webrtc::PeerConnectionInterface::kIceConnectionFailed: return info.GetReturnValue().Set(Nan::New("failed").ToLocalChecked()); break; case webrtc::PeerConnectionInterface::kIceConnectionDisconnected: return info.GetReturnValue().Set(Nan::New("disconnected").ToLocalChecked()); break; default: return info.GetReturnValue().Set(Nan::New("closed").ToLocalChecked()); break; } } else { Nan::ThrowError("Internal Error"); } info.GetReturnValue().SetUndefined(); } void PeerConnection::GetIceGatheringState(Local property, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; PeerConnection *self = RTCWrap::Unwrap(info.Holder(), "PeerConnection"); webrtc::PeerConnectionInterface *socket = self->GetSocket(); if (socket) { webrtc::PeerConnectionInterface::IceGatheringState state(socket->ice_gathering_state()); switch (state) { case webrtc::PeerConnectionInterface::kIceGatheringNew: return info.GetReturnValue().Set(Nan::New("new").ToLocalChecked()); break; case webrtc::PeerConnectionInterface::kIceGatheringGathering: return info.GetReturnValue().Set(Nan::New("gathering").ToLocalChecked()); break; default: return info.GetReturnValue().Set(Nan::New("complete").ToLocalChecked()); break; } } else { Nan::ThrowError("Internal Error"); } info.GetReturnValue().SetUndefined(); } void PeerConnection::GetOnSignalingStateChange(Local property, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; PeerConnection *self = RTCWrap::Unwrap(info.Holder(), "PeerConnection"); return info.GetReturnValue().Set(Nan::New(self->_onsignalingstatechange)); } void PeerConnection::GetOnIceConnectionStateChange(Local property, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; PeerConnection *self = RTCWrap::Unwrap(info.Holder(), "PeerConnection"); return info.GetReturnValue().Set(Nan::New(self->_oniceconnectionstatechange)); } void PeerConnection::GetOnIceCandidate(Local property, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; PeerConnection *self = RTCWrap::Unwrap(info.Holder(), "PeerConnection"); return info.GetReturnValue().Set(Nan::New(self->_onicecandidate)); } void PeerConnection::GetLocalDescription(Local property, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; PeerConnection *self = RTCWrap::Unwrap(info.Holder(), "PeerConnection"); return info.GetReturnValue().Set(Nan::New(self->_localsdp)); } void PeerConnection::GetRemoteDescription(Local property, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; PeerConnection *self = RTCWrap::Unwrap(info.Holder(), "PeerConnection"); return info.GetReturnValue().Set(Nan::New(self->_remotesdp)); } void PeerConnection::GetOnDataChannel(Local property, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; PeerConnection *self = RTCWrap::Unwrap(info.Holder(), "PeerConnection"); return info.GetReturnValue().Set(Nan::New(self->_ondatachannel)); } void PeerConnection::GetOnNegotiationNeeded(Local property, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; PeerConnection *self = RTCWrap::Unwrap(info.Holder(), "PeerConnection"); return info.GetReturnValue().Set(Nan::New(self->_onnegotiationneeded)); } void PeerConnection::GetOnAddStream(Local property, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; PeerConnection *self = RTCWrap::Unwrap(info.Holder(), "PeerConnection"); return info.GetReturnValue().Set(Nan::New(self->_onaddstream)); } void PeerConnection::GetOnRemoveStream(Local property, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; PeerConnection *self = RTCWrap::Unwrap(info.Holder(), "PeerConnection"); return info.GetReturnValue().Set(Nan::New(self->_onremovestream)); } void PeerConnection::ReadOnly(Local property, Local value, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; } void PeerConnection::SetOnSignalingStateChange(Local property, Local value, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; PeerConnection *self = RTCWrap::Unwrap(info.Holder(), "PeerConnection"); if (!value.IsEmpty() && value->IsFunction()) { self->_onsignalingstatechange.Reset(Local::Cast(value)); } else { self->_onsignalingstatechange.Reset(); } } void PeerConnection::SetOnIceConnectionStateChange(Local property, Local value, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; PeerConnection *self = RTCWrap::Unwrap(info.Holder(), "PeerConnection"); if (!value.IsEmpty() && value->IsFunction()) { self->_oniceconnectionstatechange.Reset(Local::Cast(value)); } else { self->_oniceconnectionstatechange.Reset(); } } void PeerConnection::SetOnIceCandidate(Local property, Local value, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; PeerConnection *self = RTCWrap::Unwrap(info.Holder(), "PeerConnection"); if (!value.IsEmpty() && value->IsFunction()) { self->_onicecandidate.Reset(Local::Cast(value)); } else { self->_onicecandidate.Reset(); } } void PeerConnection::SetOnDataChannel(Local property, Local value, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; PeerConnection *self = RTCWrap::Unwrap(info.Holder(), "PeerConnection"); if (!value.IsEmpty() && value->IsFunction()) { self->_ondatachannel.Reset(Local::Cast(value)); } else { self->_ondatachannel.Reset(); } } void PeerConnection::SetOnNegotiationNeeded(Local property, Local value, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; PeerConnection *self = RTCWrap::Unwrap(info.Holder(), "PeerConnection"); if (!value.IsEmpty() && value->IsFunction()) { self->_onnegotiationneeded.Reset(Local::Cast(value)); } else { self->_onnegotiationneeded.Reset(); } } void PeerConnection::SetOnAddStream(Local property, Local value, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; PeerConnection *self = RTCWrap::Unwrap(info.Holder(), "PeerConnection"); if (!value.IsEmpty() && value->IsFunction()) { self->_onaddstream.Reset(Local::Cast(value)); } else { self->_onaddstream.Reset(); } } void PeerConnection::SetOnRemoveStream(Local property, Local value, const Nan::PropertyCallbackInfo &info) { LOG(LS_INFO) << __PRETTY_FUNCTION__; PeerConnection *self = RTCWrap::Unwrap(info.Holder(), "PeerConnection"); if (!value.IsEmpty() && value->IsFunction()) { self->_onremovestream.Reset(Local::Cast(value)); } else { self->_onremovestream.Reset(); } } void PeerConnection::On(Event *event) { LOG(LS_INFO) << __PRETTY_FUNCTION__; Nan::HandleScope scope; PeerConnectionEvent type = event->Type(); Local callback; Local container; Local argv[1]; bool isError = false; std::string data; int argc = 0; switch (type) { case kPeerConnectionCreateClosed: EventEmitter::SetReference(false); break; case kPeerConnectionCreateOffer: callback = Nan::New(_offerCallback); _offerCallback.Reset(); _offerErrorCallback.Reset(); data = event->Unwrap(); argv[0] = JSON::Parse(Nan::New(data.c_str()).ToLocalChecked()); argc = 1; break; case kPeerConnectionCreateOfferError: callback = Nan::New(_offerErrorCallback); _offerCallback.Reset(); _offerErrorCallback.Reset(); isError = true; data = event->Unwrap(); argv[0] = Nan::Error(data.c_str()); argc = 1; break; case kPeerConnectionCreateAnswer: callback = Nan::New(_answerCallback); _answerCallback.Reset(); _answerErrorCallback.Reset(); data = event->Unwrap(); argv[0] = JSON::Parse(Nan::New(data.c_str()).ToLocalChecked()); argc = 1; break; case kPeerConnectionCreateAnswerError: callback = Nan::New(_answerErrorCallback); _answerCallback.Reset(); _answerErrorCallback.Reset(); isError = true; data = event->Unwrap(); argv[0] = Nan::Error(data.c_str()); argc = 1; break; case kPeerConnectionSetLocalDescription: callback = Nan::New(_localCallback); _localCallback.Reset(); _localErrorCallback.Reset(); break; case kPeerConnectionSetLocalDescriptionError: callback = Nan::New(_localErrorCallback); _localCallback.Reset(); _localErrorCallback.Reset(); _localsdp.Reset(); isError = true; data = event->Unwrap(); argv[0] = Nan::Error(data.c_str()); argc = 1; break; case kPeerConnectionSetRemoteDescription: callback = Nan::New(_remoteCallback); _remoteCallback.Reset(); _remoteErrorCallback.Reset(); break; case kPeerConnectionSetRemoteDescriptionError: callback = Nan::New(_remoteErrorCallback); _remoteCallback.Reset(); _remoteErrorCallback.Reset(); _remotesdp.Reset(); isError = true; data = event->Unwrap(); argv[0] = Nan::Error(data.c_str()); argc = 1; break; case kPeerConnectionIceCandidate: callback = Nan::New(_onicecandidate); container = Nan::New(); data = event->Unwrap(); if (data.empty()) { container->Set(Nan::New("candidate").ToLocalChecked(), Nan::Null()); } else { container->Set(Nan::New("candidate").ToLocalChecked(), JSON::Parse(Nan::New(data.c_str()).ToLocalChecked())); } argv[0] = container; argc = 1; break; case kPeerConnectionSignalChange: callback = Nan::New(_onsignalingstatechange); break; case kPeerConnectionIceChange: callback = Nan::New(_oniceconnectionstatechange); break; case kPeerConnectionIceGathering: break; case kPeerConnectionDataChannel: callback = Nan::New(_ondatachannel); container = Nan::New(); container->Set(Nan::New("channel").ToLocalChecked(), DataChannel::New(event->Unwrap >())); argv[0] = container; argc = 1; break; case kPeerConnectionAddStream: callback = Nan::New(_onaddstream); container = Nan::New(); container->Set(Nan::New("stream").ToLocalChecked(), MediaStream::New(event->Unwrap >())); argv[0] = container; argc = 1; break; case kPeerConnectionRemoveStream: callback = Nan::New(_onremovestream); container = Nan::New(); container->Set(Nan::New("stream").ToLocalChecked(), MediaStream::New(event->Unwrap >())); argv[0] = container; argc = 1; break; case kPeerConnectionRenegotiation: callback = Nan::New(_onnegotiationneeded); break; case kPeerConnectionStats: callback = Nan::New(_onstats); argv[0] = RTCStatsResponse::New(event->Unwrap()); argc = 1; break; } if (!callback.IsEmpty() && callback->IsFunction()) { callback->Call(RTCWrap::This(), argc, argv); } else if (isError) { Nan::ThrowError(argv[0]); } } bool PeerConnection::IsStable() { webrtc::PeerConnectionInterface *socket = PeerConnection::GetSocket(); if (socket) { webrtc::PeerConnectionInterface::SignalingState state(socket->signaling_state()); if (state == webrtc::PeerConnectionInterface::kStable) { return true; } } return false; } ================================================ FILE: src/PeerConnection.h ================================================ /* * The MIT License (MIT) * * Copyright (c) 2015 vmolsa (http://github.com/vmolsa) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * */ #ifndef WEBRTC_PEERCONNECTION_H #define WEBRTC_PEERCONNECTION_H #include "Common.h" #include "Observers.h" #include "EventEmitter.h" #include "MediaConstraints.h" #include "Wrap.h" namespace WebRTC { enum PeerConnectionEvent { kPeerConnectionCreateClosed = 1, kPeerConnectionCreateOffer, kPeerConnectionCreateOfferError, kPeerConnectionCreateAnswer, kPeerConnectionCreateAnswerError, kPeerConnectionSetLocalDescription, kPeerConnectionSetLocalDescriptionError, kPeerConnectionSetRemoteDescription, kPeerConnectionSetRemoteDescriptionError, kPeerConnectionIceCandidate, kPeerConnectionSignalChange, kPeerConnectionIceChange, kPeerConnectionIceGathering, kPeerConnectionDataChannel, kPeerConnectionAddStream, kPeerConnectionRemoveStream, kPeerConnectionRenegotiation, kPeerConnectionStats }; class PeerConnection : public RTCWrap, public EventEmitter { public: static void Init(v8::Handle exports); private: PeerConnection(const v8::Local &configuration, const v8::Local &constraints); ~PeerConnection() final; static void New(const Nan::FunctionCallbackInfo &info); static void CreateOffer(const Nan::FunctionCallbackInfo &info); static void CreateAnswer(const Nan::FunctionCallbackInfo &info); static void SetLocalDescription(const Nan::FunctionCallbackInfo &info); static void SetRemoteDescription(const Nan::FunctionCallbackInfo &info); static void AddIceCandidate(const Nan::FunctionCallbackInfo &info); static void CreateDataChannel(const Nan::FunctionCallbackInfo &info); static void AddStream(const Nan::FunctionCallbackInfo &info); static void RemoveStream(const Nan::FunctionCallbackInfo &info); static void GetLocalStreams(const Nan::FunctionCallbackInfo &info); static void GetRemoteStreams(const Nan::FunctionCallbackInfo &info); static void GetStreamById(const Nan::FunctionCallbackInfo &info); static void GetStats(const Nan::FunctionCallbackInfo &info); static void Close(const Nan::FunctionCallbackInfo &info); static void GetSignalingState(v8::Local property, const Nan::PropertyCallbackInfo &info); static void GetIceConnectionState(v8::Local property, const Nan::PropertyCallbackInfo &info); static void GetIceGatheringState(v8::Local property, const Nan::PropertyCallbackInfo &info); static void GetOnSignalingStateChange(v8::Local property, const Nan::PropertyCallbackInfo &info); static void GetOnIceConnectionStateChange(v8::Local property, const Nan::PropertyCallbackInfo &info); static void GetOnIceCandidate(v8::Local property, const Nan::PropertyCallbackInfo &info); static void GetOnDataChannel(v8::Local property, const Nan::PropertyCallbackInfo &info); static void GetOnNegotiationNeeded(v8::Local property, const Nan::PropertyCallbackInfo &info); static void GetOnAddStream(v8::Local property, const Nan::PropertyCallbackInfo &info); static void GetOnRemoveStream(v8::Local property, const Nan::PropertyCallbackInfo &info); static void GetLocalDescription(v8::Local property, const Nan::PropertyCallbackInfo &info); static void GetRemoteDescription(v8::Local property, const Nan::PropertyCallbackInfo &info); static void ReadOnly(v8::Local property, v8::Local value, const Nan::PropertyCallbackInfo &info); static void SetOnSignalingStateChange(v8::Local property, v8::Local value, const Nan::PropertyCallbackInfo &info); static void SetOnIceConnectionStateChange(v8::Local property, v8::Local value, const Nan::PropertyCallbackInfo &info); static void SetOnIceCandidate(v8::Local property, v8::Local value, const Nan::PropertyCallbackInfo &info); static void SetOnDataChannel(v8::Local property, v8::Local value, const Nan::PropertyCallbackInfo &info); static void SetOnNegotiationNeeded(v8::Local property, v8::Local value, const Nan::PropertyCallbackInfo &info); static void SetOnAddStream(v8::Local property, v8::Local value, const Nan::PropertyCallbackInfo &info); static void SetOnRemoveStream(v8::Local property, v8::Local value, const Nan::PropertyCallbackInfo &info); void On(Event *event) final; bool IsStable(); webrtc::PeerConnectionInterface *GetSocket(); protected: Nan::Persistent _onsignalingstatechange; Nan::Persistent _oniceconnectionstatechange; Nan::Persistent _onicecandidate; Nan::Persistent _ondatachannel; Nan::Persistent _onnegotiationneeded; Nan::Persistent _onaddstream; Nan::Persistent _onremovestream; Nan::Persistent _offerCallback; Nan::Persistent _offerErrorCallback; Nan::Persistent _answerCallback; Nan::Persistent _answerErrorCallback; Nan::Persistent _localCallback; Nan::Persistent _localErrorCallback; Nan::Persistent _remoteCallback; Nan::Persistent _remoteErrorCallback; Nan::Persistent _onstats; Nan::Persistent _localsdp; Nan::Persistent _remotesdp; static Nan::Persistent constructor; rtc::scoped_refptr _stats; rtc::scoped_refptr _offer; rtc::scoped_refptr _answer; rtc::scoped_refptr _local; rtc::scoped_refptr _remote; rtc::scoped_refptr _peer; rtc::scoped_refptr _socket; rtc::scoped_refptr _factory; rtc::scoped_refptr _constraints; webrtc::PeerConnectionInterface::RTCConfiguration _config; }; }; #endif ================================================ FILE: src/Platform.cc ================================================ /* * The MIT License (MIT) * * Copyright (c) 2015 vmolsa (http://github.com/vmolsa) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * */ #include "Platform.h" #if defined(WEBRTC_WIN) #include #include #endif using namespace WebRTC; #ifndef WEBRTC_THREAD_COUNT #define WEBRTC_THREAD_COUNT 4 #endif rtc::Thread signal_thread; rtc::Thread worker_thread[WEBRTC_THREAD_COUNT]; uint32_t counter = 0; void Platform::Init() { LOG(LS_INFO) << __PRETTY_FUNCTION__; #if defined(WEBRTC_WIN) rtc::EnsureWinsockInit(); #endif rtc::InitializeSSL(); signal_thread.Start(); rtc::ThreadManager::Instance()->SetCurrentThread(&signal_thread); if (rtc::ThreadManager::Instance()->CurrentThread() != &signal_thread) { Nan::ThrowError("Internal Thread Error!"); } for (int index = 0; index < WEBRTC_THREAD_COUNT; index++) { worker_thread[index].Start(); } } void Platform::Dispose() { LOG(LS_INFO) << __PRETTY_FUNCTION__; signal_thread.SetAllowBlockingCalls(true); signal_thread.Stop(); for (int index = 0; index < WEBRTC_THREAD_COUNT; index++) { worker_thread[index].SetAllowBlockingCalls(true); worker_thread[index].Stop(); } if (rtc::ThreadManager::Instance()->CurrentThread() == &signal_thread) { rtc::ThreadManager::Instance()->SetCurrentThread(NULL); } rtc::CleanupSSL(); } rtc::Thread *Platform::GetWorker() { return &worker_thread[(counter++) % WEBRTC_THREAD_COUNT]; } ================================================ FILE: src/Platform.h ================================================ /* * The MIT License (MIT) * * Copyright (c) 2015 vmolsa (http://github.com/vmolsa) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * */ #ifndef WEBRTC_PLATFORM_H #define WEBRTC_PLATFORM_H #include "Common.h" namespace WebRTC { class Platform { public: static void Init(); static void Dispose(); static rtc::Thread *GetWorker(); }; }; #endif ================================================ FILE: src/Stats.cc ================================================ /* * The MIT License (MIT) * * Copyright (c) 2015 vmolsa (http://github.com/vmolsa) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * */ #include "Stats.h" using namespace v8; using namespace WebRTC; Nan::Persistent RTCStatsReport::constructor; RTCStatsReport::~RTCStatsReport() { } void RTCStatsReport::Init() { Nan::HandleScope scope; Local tpl = Nan::New(RTCStatsReport::New); tpl->InstanceTemplate()->SetInternalFieldCount(1); tpl->SetClassName(Nan::New("RTCStatsReport").ToLocalChecked()); tpl->PrototypeTemplate()->Set(Nan::New("names").ToLocalChecked(), Nan::New(RTCStatsReport::Names)->GetFunction()); tpl->PrototypeTemplate()->Set(Nan::New("stat").ToLocalChecked(), Nan::New(RTCStatsReport::Stat)->GetFunction()); Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("id").ToLocalChecked(), RTCStatsReport::Id); Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("type").ToLocalChecked(), RTCStatsReport::Type); Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("timestamp").ToLocalChecked(), RTCStatsReport::Timestamp); constructor.Reset(tpl->GetFunction()); }; Local RTCStatsReport::New(webrtc::StatsReport *report) { Nan::EscapableHandleScope scope; Local instance = Nan::New(RTCStatsReport::constructor); if (instance.IsEmpty()) { return scope.Escape(Nan::Null()); } Local ret = instance->NewInstance(); RTCStatsReport *stats = RTCWrap::Unwrap(ret, "RTCStatsReport"); if (stats) { stats->_report = report; } return scope.Escape(ret); } void RTCStatsReport::New(const Nan::FunctionCallbackInfo &info) { if (info.IsConstructCall()) { RTCStatsReport* report = new RTCStatsReport(); report->Wrap(info.This(), "RTCStatsReport"); return info.GetReturnValue().Set(info.This()); } Nan::ThrowError("Internal Error"); info.GetReturnValue().SetUndefined(); } void RTCStatsReport::Names(const Nan::FunctionCallbackInfo &info) { RTCStatsReport *stats = RTCWrap::Unwrap(info.This(), "RTCStatsReport"); webrtc::StatsReport::Values values = stats->_report->values(); Local list = Nan::New(); unsigned int index = 0; for (webrtc::StatsReport::Values::iterator it = values.begin(); it != values.end(); it++) { webrtc::StatsReport::ValuePtr value = values[it->first]; list->Set(index, Nan::New(value->display_name()).ToLocalChecked()); index++; } return info.GetReturnValue().Set(list); } void RTCStatsReport::Stat(const Nan::FunctionCallbackInfo &info) { RTCStatsReport *stats = RTCWrap::Unwrap(info.This(), "RTCStatsReport"); webrtc::StatsReport::Values values = stats->_report->values(); if (info.Length() >= 1 && info[0]->IsString()) { String::Utf8Value entry_value(info[0]->ToString()); std::string entry(*entry_value); for (webrtc::StatsReport::Values::iterator it = values.begin(); it != values.end(); it++) { webrtc::StatsReport::ValuePtr value = values[it->first]; if (!entry.compare(value->display_name())) { switch (value->type()) { case webrtc::StatsReport::Value::kInt: return info.GetReturnValue().Set(Nan::New(value->int_val())); break; case webrtc::StatsReport::Value::kInt64: return info.GetReturnValue().Set(Nan::New(static_cast(value->int64_val()))); break; case webrtc::StatsReport::Value::kFloat: return info.GetReturnValue().Set(Nan::New(value->float_val())); break; case webrtc::StatsReport::Value::kString: return info.GetReturnValue().Set(Nan::New(value->string_val().c_str()).ToLocalChecked()); break; case webrtc::StatsReport::Value::kStaticString: return info.GetReturnValue().Set(Nan::New(value->static_string_val()).ToLocalChecked()); break; case webrtc::StatsReport::Value::kBool: return info.GetReturnValue().Set(Nan::New(value->bool_val())); break; case webrtc::StatsReport::Value::kId: return info.GetReturnValue().Set(Nan::New(value->ToString().c_str()).ToLocalChecked()); break; } } } } info.GetReturnValue().SetUndefined(); } void RTCStatsReport::Id(Local property, const Nan::PropertyCallbackInfo &info) { RTCStatsReport *stats = RTCWrap::Unwrap(info.This(), "RTCStatsReport"); std::string id(stats->_report->id()->ToString()); return info.GetReturnValue().Set(Nan::New(id.c_str()).ToLocalChecked()); } void RTCStatsReport::Type(Local property, const Nan::PropertyCallbackInfo &info) { RTCStatsReport *stats = RTCWrap::Unwrap(info.This(), "RTCStatsReport"); return info.GetReturnValue().Set(Nan::New(stats->_report->TypeToString()).ToLocalChecked()); } void RTCStatsReport::Timestamp(Local property, const Nan::PropertyCallbackInfo &info) { RTCStatsReport *stats = RTCWrap::Unwrap(info.This(), "RTCStatsReport"); return info.GetReturnValue().Set(Nan::New(stats->_report->timestamp())); } Nan::Persistent RTCStatsResponse::constructor; RTCStatsResponse::~RTCStatsResponse() { } void RTCStatsResponse::Init() { Nan::HandleScope scope; Local tpl = Nan::New(RTCStatsResponse::New); tpl->InstanceTemplate()->SetInternalFieldCount(1); tpl->SetClassName(Nan::New("RTCStatsResponse").ToLocalChecked()); tpl->PrototypeTemplate()->Set(Nan::New("result").ToLocalChecked(), Nan::New(RTCStatsResponse::Result)->GetFunction()); constructor.Reset(tpl->GetFunction()); } void RTCStatsResponse::New(const Nan::FunctionCallbackInfo &info) { if (info.IsConstructCall()) { RTCStatsResponse *response = new RTCStatsResponse(); response->Wrap(info.This(), "RTCStatsResponse"); return info.GetReturnValue().Set(info.This()); } Nan::ThrowError("Internal Error"); info.GetReturnValue().SetUndefined(); } Local RTCStatsResponse::New(const webrtc::StatsReports &reports) { Nan::EscapableHandleScope scope; Local instance = Nan::New(RTCStatsResponse::constructor); if (instance.IsEmpty()) { return scope.Escape(Nan::Null()); } Local ret = instance->NewInstance(); RTCStatsResponse *response = RTCWrap::Unwrap(ret, "RTCStatsResponse"); response->_reports = reports; return scope.Escape(ret); } void RTCStatsResponse::Result(const Nan::FunctionCallbackInfo &info) { RTCStatsResponse *response = RTCWrap::Unwrap(info.This(), "RTCStatsResponse"); Local list = Nan::New(response->_reports.size()); for(unsigned int index = 0; index < response->_reports.size(); index++) { list->Set(index, RTCStatsReport::New(const_cast(response->_reports.at(index)))); } return info.GetReturnValue().Set(list); } ================================================ FILE: src/Stats.h ================================================ /* * The MIT License (MIT) * * Copyright (c) 2015 vmolsa (http://github.com/vmolsa) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * */ #ifndef WEBRTC_STATS_H #define WEBRTC_STATS_H #include "Common.h" #include "Observers.h" #include "EventEmitter.h" #include "Wrap.h" namespace WebRTC { class RTCStatsReport : public RTCWrap { public: static void Init(); static v8::Local New(webrtc::StatsReport *report); private: ~RTCStatsReport() final; static void New(const Nan::FunctionCallbackInfo &info); static void Names(const Nan::FunctionCallbackInfo &info); static void Stat(const Nan::FunctionCallbackInfo &info); static void Id(v8::Local property, const Nan::PropertyCallbackInfo &info); static void Type(v8::Local property, const Nan::PropertyCallbackInfo &info); static void Timestamp(v8::Local property, const Nan::PropertyCallbackInfo &info); protected: static Nan::Persistent constructor; webrtc::StatsReport* _report; }; class RTCStatsResponse : public RTCWrap { public: static void Init(); static v8::Local New(const webrtc::StatsReports &reports); private: ~RTCStatsResponse() final; static void New(const Nan::FunctionCallbackInfo &info); static void Result(const Nan::FunctionCallbackInfo &info); protected: static Nan::Persistent constructor; webrtc::StatsReports _reports; }; }; #endif ================================================ FILE: src/Wrap.h ================================================ /* * The MIT License (MIT) * * Copyright (c) 2015 vmolsa (http://github.com/vmolsa) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * */ #ifndef WEBRTC_WRAP_H #define WEBRTC_WRAP_H #include "Common.h" namespace WebRTC { class RTCWrap : public node::ObjectWrap { public: inline void Wrap(v8::Local obj, const char *className = "RTCWrap") { LOG(LS_INFO) << __PRETTY_FUNCTION__; _className = className; node::ObjectWrap::Wrap(obj); } inline v8::Local This() { LOG(LS_INFO) << __PRETTY_FUNCTION__; #if (NODE_MODULE_VERSION < NODE_0_12_MODULE_VERSION) Nan::EscapableHandleScope scope; return scope.Escape(Nan::New(node::ObjectWrap::handle_)); #else return node::ObjectWrap::handle(); #endif } template inline T* Unwrap() { LOG(LS_INFO) << __PRETTY_FUNCTION__; return static_cast(this); } template inline static T* Unwrap(v8::Local obj, const char *className = "RTCWrap") { LOG(LS_INFO) << __PRETTY_FUNCTION__; RTCWrap *wrap = node::ObjectWrap::Unwrap(obj); if (wrap) { if (!wrap->_className.compare(className)) { return wrap->Unwrap(); } } return 0; } protected: std::string _className; }; }; #endif ================================================ FILE: src/addon.gypi ================================================ { 'target_defaults': { 'type': 'loadable_module', 'product_prefix': '', 'product_extension': 'node', 'include_dirs': [ '<(node_root_dir)/include/node', '<(node_root_dir)/src', '<(node_root_dir)/deps/uv/include', '<(node_root_dir)/deps/v8/include' ], 'defines': [ 'BUILDING_NODE_EXTENSION', 'NODE_GYP_MODULE_NAME=>(_target_name)' ], 'conditions': [ [ 'OS=="mac"', { 'defines': [ '_DARWIN_USE_64_BIT_INODE=1' ], 'libraries': [ '-undefined dynamic_lookup' ], 'xcode_settings': { 'DYLIB_INSTALL_NAME_BASE': '@rpath' }, }], [ 'OS=="win"', { 'libraries': [ '-lkernel32.lib', '-luser32.lib', '-lgdi32.lib', '-lwinspool.lib', '-lcomdlg32.lib', '-ladvapi32.lib', '-lshell32.lib', '-lole32.lib', '-loleaut32.lib', '-luuid.lib', '-lodbc32.lib', '-lDelayImp.lib', '-l"<(node_root_dir)\\<(ConfigurationName)\\<(node_lib_file)"' ], 'msvs_disabled_warnings': [ 4251 ], }, { 'defines': [ '_LARGEFILE_SOURCE', '_FILE_OFFSET_BITS=64', ], }], [ 'OS=="freebsd" or OS=="openbsd" or OS=="solaris" or (OS=="linux" and target_arch!="ia32")', { 'cflags': [ '-fPIC' ], }] ] } } ================================================ FILE: src/webrtc.gyp ================================================ { 'includes': [ '../third_party/webrtc/src/talk/build/common.gypi', '../third_party/webrtc/src/webrtc/build/common.gypi', '../build/config.gypi', '../nodejs.gypi', 'addon.gypi', ], 'targets': [ { 'target_name': 'webrtc', 'sources': [ 'Platform.cc', 'Global.cc', 'BackTrace.cc', 'EventEmitter.cc', 'Observers.cc', 'Module.cc', 'PeerConnection.cc', 'DataChannel.cc', 'GetSources.cc', 'GetUserMedia.cc', 'MediaStream.cc', 'MediaStreamTrack.cc', 'MediaConstraints.cc', 'Stats.cc', ], 'dependencies': [ '<(webrtc_root)/webrtc.gyp:webrtc_all', ], 'include_dirs': [ '<(DEPTH)/third_party/jsoncpp/source/include', '<(DEPTH)/third_party/libsrtp/srtp', '<(DEPTH)/third_party/libyuv/include', "= options.packetCount) { console.log('SEND DONE!', info()); return; } if (n % 100 === 0) { console.log('SENDING:', info()); } if (congestion()) { setTimeout(send, options.bufferedDelayMs); return; } // TODO allocating new buffer per send as workaround to repeated Externalize() // after fixing the issues around that we can move back to higher scope. var buffer = new ArrayBuffer(options.packetSize); peer1.send(buffer); n += 1; if (global.setImmediate) { global.setImmediate(send); } else { setTimeout(send, 0); } } /** * callback for the receiver to update stats and finish the test */ function receive(data) { stats.count += 1; stats.bytes += data.length; if (stats.count >= options.packetCount) { console.log('RECEIVE DONE!', info()); // closing the channels so the process can exit peer1.destroy(); peer2.destroy(); callback(); } } /** * failure handler */ function failure(err) { // make sure to call the callback with error, even if anything here will throw setTimeout(callback.bind(null, err), 0); console.error('ERROR!', info(), err.stack || err); // closing the channels so the process can exit peer1.destroy(); peer2.destroy(); } /** * handle channel congestion using bufferedAmount */ function congestion() { var bufferedAmount = peer1._channel && peer1._channel.bufferedAmount || 0; if ((bufferedAmount > options.congestHighThreshold) || (congested && bufferedAmount > options.congestLowThreshold)) { if (!congested) { congested = Date.now(); } stats.congestCount += 1; } else { if (congested) { stats.congestTime += Date.now() - congested; } congested = 0; } return congested; } /** * return information string on the test progress */ function info() { var now = Date.now(); if (congested) { stats.congestTime += Date.now() - congested; congested = now; } var took = (now - stats.startTime) / 1000; var bufferedAmount = peer1._channel && peer1._channel.bufferedAmount; return 'sent ' + n + ' received ' + stats.count + '. ' + 'congestion #' + stats.congestCount + ' ' + (stats.congestTime / 1024).toFixed(3) + ' seconds. ' + (bufferedAmount ? 'bufferedAmount ' + bufferedAmount + '. ' : '') + 'took ' + took.toFixed(3) + ' seconds. ' + 'bandwidth ' + (stats.bytes / took / 1024).toFixed(0) + ' KB/s. '; } /** * default ice config is just here for documenting the options - see inside. */ function defaultIceConfig() { return { /* * data channels are ordered by default - using unordered channel improves * performance but will likely require ordering in another app layer */ // ordered: false, /* * data channels are reliable by default - using either maxRetransmits * or maxPacketLifeTime will change to unreliable mode */ // maxRetransmits: 5, // maxPacketLifeTime: 3000, /* * iceServers with stun urls. not needed for this in-process test. */ // iceServers: [{url: 'stun:23.21.150.121'}], }; } } ================================================ FILE: test/core.js ================================================ var WebRTC = require('../'); console.log('WebRTC Module Loaded!'); //WebRTC.setDebug(true); ================================================ FILE: test/dataChannel.js ================================================ var WebRTC = require('../'); //WebRTC.setDebug(true); function P2P(alice, bob) { alice.onicecandidate = function(event) { var candidate = event.candidate; if (candidate) { bob.addIceCandidate(candidate); } }; bob.onicecandidate = function(event) { var candidate = event.candidate; if (candidate) { alice.addIceCandidate(candidate); } }; alice.onnegotiationneeded = function() { alice.createOffer(function(sdp) { alice.setLocalDescription(sdp, function() { bob.setRemoteDescription(sdp, function() { bob.createAnswer(function(sdp) { bob.setLocalDescription(sdp, function() { alice.setRemoteDescription(sdp, function() { console.log("Alice -> Bob: Connected!"); }); }); }); }); }); }); }; bob.onnegotiationneeded = function() { bob.createOffer(function(sdp) { bob.setLocalDescription(sdp, function() { alice.setRemoteDescription(sdp, function() { alice.createAnswer(function(sdp) { alice.setLocalDescription(sdp, function() { bob.setRemoteDescription(sdp, function() { console.log("Bob -> Alice: Connected!"); }); }); }); }); }); }); }; alice.onaddstream = function(stream) { if (stream) { console.log('Alice got mediaStream'); } }; bob.onaddstream = function(stream) { if (stream) { console.log('Bob got mediaStream'); } }; alice.ondatachannel = function(event, callback) { var channel = event ? event.channel || event : null; if (!channel) { return false; } console.log('Alice: Got DataChannel!'); channel.onopen = function() { console.log('Alice: DataChannel Open!'); if (callback) { callback(channel); } }; channel.onmessage = function(event) { var data = event.data; console.log('Alice:', data); }; channel.onclose = function() { console.log('Alice: DataChannel Closed!'); }; }; bob.ondatachannel = function(event, callback) { var channel = event ? event.channel || event : null; if (!channel) { return false; } console.log('Bob: Got DataChannel!'); channel.onopen = function() { console.log('Bob: DataChannel Open!'); if (callback) { callback(channel); } }; channel.onmessage = function(event) { var data = event.data; console.log('Bob:', data); channel.send('Hello Alice!'); }; channel.onclose = function() { console.log('Bob: DataChannel Closed!'); }; }; } var config = { iceServers: [ { url: 'stun:stun.l.google.com:19302', }, ], }; function sctpTest() { console.log('Running SCTP DataChannel Test'); var sctpDataChannelConfig = { reliable: true, ordered: true, }; var sctpDataChannelConstraints = { audio: false, video: false, optional: [ { RtpDataChannels: false, DtlsSrtpKeyAgreement: true, }, ], mandatory: { OfferToReceiveAudio: false, OfferToReceiveVideo: false, }, }; var alice = new WebRTC.RTCPeerConnection(config, sctpDataChannelConstraints); var bob = new WebRTC.RTCPeerConnection(config, sctpDataChannelConstraints); P2P(alice, bob); alice.ondatachannel(alice.createDataChannel('TestChannel', sctpDataChannelConfig), function(channel) { channel.send('Hello Bob!'); setTimeout(function () { channel.close(); }, 1000); setTimeout(function() { alice.close(); bob.close(); }, 5000); }); } function rtpTest() { console.log('Running RTP DataChannel Test'); var rtpDataChannelConfig = { reliable: false, ordered: false, }; var rtpDataChannelConstraints = { audio: false, video: false, optional: [ { RtpDataChannels: true, DtlsSrtpKeyAgreement: false, }, ], mandatory: { OfferToReceiveAudio: false, OfferToReceiveVideo: false, }, }; var alice = new WebRTC.RTCPeerConnection(config, rtpDataChannelConstraints); var bob = new WebRTC.RTCPeerConnection(config, rtpDataChannelConstraints); P2P(alice, bob); alice.ondatachannel(alice.createDataChannel('TestChannel', rtpDataChannelConfig), function(channel) { channel.send('Hello Bob!'); setTimeout(function () { channel.close(); }, 1000); setTimeout(function() { alice.close(); bob.close(); setTimeout(function() { sctpTest(); }, 1000); }, 5000); }); } rtpTest(); ================================================ FILE: test/getSources.js ================================================ var WebRTC = require('../'); WebRTC.getSources(function(sources) { console.log(sources); }); ================================================ FILE: test/getUserMedia.js ================================================ var WebRTC = require('../'); //WebRTC.setDebug(true); function dumpTrack(track) { console.log('MediaStreamTrack Enabled:', track.enabled); console.log('MediaStreamTrack Id:', track.id); console.log('MediaStreamTrack Kind:', track.kind); console.log('MediaStreamTrack Label:', track.label); console.log('MediaStreamTrack Muted:', track.muted); console.log('MediaStreamTrack ReadyState:', track.readyState); console.log('MediaStreamTrack Remote:', track.remote); } function onSuccess(stream) { if (stream && stream.active) { stream.onaddtrack = function(track) { console.log('Track Added!'); }; stream.onremovetrack = function (track) { console.log('Track Removed!'); }; console.log('MediaStream Active:', stream.active); console.log('MediaStream Ended:', stream.ended); console.log('MediaStream ID:', stream.id); var audio_list = stream.getAudioTracks(); audio_list.forEach(function (track) { console.log('Audio Track'); dumpTrack(track); }); var video_list = stream.getVideoTracks(); video_list.forEach(function (track) { console.log('Video Track'); dumpTrack(track); }); setTimeout(function() { console.log('Closing...'); }, 5000); } } function onError(error) { throw error; } WebRTC.getUserMedia({ audio: true, video: true, }, onSuccess, onError); ================================================ FILE: test/mediaStream.js ================================================ var WEBRTC = require('../'); var config = { iceServers: [ { url: 'stun:stun.l.google.com:19302', }, ], }; var constraints = { audio: { optional: [ { googEchoCancellation: true, googEchoCancellation2: true, googDAEchoCancellation: true, googAutoGainControl: true, googAutoGainControl2: true, googNoiseSuppression: true, googNoiseSuppression2: true, googHighpassFilter: true, googTypingNoiseDetection: true, googAudioMirroring: true, }, ], }, video: { optional: [ { minAspectRatio: 1.333, maxAspectRatio: 1.778, maxWidth: 1920, minWidth: 320, maxHeight: 1080, minHeight: 180, maxFrameRate: 60, minFrameRate: 30, }, ], }, optional: [ { DtlsSrtpKeyAgreement: true, }, ], mandatory: { OfferToReceiveAudio: true, OfferToReceiveVideo: true, }, }; function P2P(alice, bob) { function showState() { console.log('Alice State:', alice.signalingState); console.log('Bob State:', bob.signalingState); } alice.onicecandidate = function(event) { var candidate = event.candidate || event; bob.addIceCandidate(candidate); }; bob.onicecandidate = function(event) { var candidate = event.candidate || event; alice.addIceCandidate(candidate); }; alice.onnegotiationneeded = function() { showState(); alice.createOffer(function(sdp) { showState(); alice.setLocalDescription(sdp, function() { showState(); bob.setRemoteDescription(sdp, function() { showState(); bob.createAnswer(function(sdp) { showState(); bob.setLocalDescription(sdp, function() { showState(); alice.setRemoteDescription(sdp, function() { showState(); }); }); }); }); }); }); }; bob.onnegotiationneeded = function() { showState(); bob.createOffer(function(sdp) { showState(); bob.setLocalDescription(sdp, function() { showState(); alice.setRemoteDescription(sdp, function() { showState(); alice.createAnswer(function(sdp) { showState(); alice.setLocalDescription(sdp, function() { showState(); bob.setRemoteDescription(sdp, function() { showState(); }); }); }); }); }); }); }; alice.onaddstream = function(stream) { if (stream) { console.log('Alice got mediaStream'); } }; bob.onaddstream = function(stream) { if (stream) { console.log('Bob got mediaStream'); } }; } WEBRTC.setDebug(true); var alice = new WEBRTC.RTCPeerConnection(config, constraints); var bob = new WEBRTC.RTCPeerConnection(config, constraints); P2P(alice, bob); function onSuccess(stream) { if (stream) { console.log('Alice: new mediaStream'); alice.addStream(stream); setTimeout(function () { alice.close(); bob.close(); }, 10000); } } function onError(error) { throw error; } WEBRTC.getUserMedia(constraints, onSuccess, onError); ================================================ FILE: test/multiconnect.js ================================================ 'use strict'; var tape = require('tape'); var SimplePeer = require('simple-peer'); var wrtc = require('..'); //wrtc.setDebug(true); tape('connect once', function(t) { t.plan(1); console.log('###########################\n'); connect(function(err) { t.error(err, 'connect callback'); }); }); tape('connect loop', function(t) { t.plan(1); console.log('###########################\n'); connectLoop(10, function(err) { t.error(err, 'connect callback'); }); }); tape('connect concurrent', function(t) { var n = 10; t.plan(n); console.log('###########################\n'); for (var i = 0; i < n; i += 1) { connect(callback); } function callback(err) { t.error(err, 'connect callback'); } }); tape('connect loop concurrent', function(t) { var n = 10; t.plan(n); console.log('###########################\n'); for (var i = 0; i < n; i += 1) { connectLoop(10, callback); } function callback(err) { t.error(err, 'connect callback'); } }); var connIdGen = 1; function connect(callback) { var connId = connIdGen; var connName = 'CONNECTION-' + connId; connIdGen += 1; console.log(connName, 'starting'); // setup two peers with simple-peer var peer1 = new SimplePeer({ wrtc: wrtc }); var peer2 = new SimplePeer({ wrtc: wrtc, initiator: true }); var timeout = setTimeout(function () { peer1.destroy(); peer2.destroy(); callback(new Error("Timeout")); }, 10000); // when peer1 has signaling data, give it to peer2, and vice versa peer1.on('signal', function(data) { //console.log(connName, 'signal peer1 -> peer2:'); //console.log(' ', data); peer2.signal(data); }); peer2.on('signal', function(data) { //console.log(connName, 'signal peer2 -> peer1:'); //console.log(' ', data); peer1.signal(data); }); peer1.on('error', function(err) { console.log(connName, 'peer1 error', err); callback(err); }); peer2.on('error', function(err) { console.log(connName, 'peer2 error', err); callback(err); }); // wait for 'connect' event peer1.on('connect', function() { //console.log(connName, 'sending message'); peer1.send('peers are for kids'); }); peer2.on('data', function() { //console.log(connName, 'completed'); clearTimeout(timeout); peer1.destroy(); peer2.destroy(); callback(); }); } function connectLoop(count, callback) { if (count <= 0) { console.log('connect loop completed'); return callback(); } else { console.log('connect loop remain', count); connect(function(err) { if (err) { callback(err); } else { connectLoop(count - 1, callback); } }); } } ================================================ FILE: test/p2p-browser.html ================================================ ================================================ FILE: test/p2p.js ================================================ var WEBRTC = require('../'); function P2P(alice, bob) { alice.onicecandidate = function(event) { var candidate = event.candidate || event; bob.addIceCandidate(candidate); }; bob.onicecandidate = function(event) { var candidate = event.candidate || event; alice.addIceCandidate(candidate); }; alice.onnegotiationneeded = function(callback) { alice.createOffer(function(sdp) { alice.setLocalDescription(sdp, function() { bob.setRemoteDescription(sdp, function() { bob.createAnswer(function(sdp) { bob.setLocalDescription(sdp, function() { alice.setRemoteDescription(sdp, function() { console.log("Alice -> Bob: Connected!"); if (callback) { callback(); } }); }); }); }); }); }); }; bob.onnegotiationneeded = function(callback) { bob.createOffer(function(sdp) { bob.setLocalDescription(sdp, function() { alice.setRemoteDescription(sdp, function() { alice.createAnswer(function(sdp) { alice.setLocalDescription(sdp, function() { bob.setRemoteDescription(sdp, function() { console.log("Bob -> Alice: Connected!"); if (callback) { callback(); } }); }); }); }); }); }); }; } var config = { iceServers: [ { url: 'stun:stun.l.google.com:19302', }, ], }; var constraints = { audio: { optional: [ { /* googEchoCancellation: true, googEchoCancellation2: true, googDAEchoCancellation: true, googAutoGainControl: true, googAutoGainControl2: true, googNoiseSuppression: true, googNoiseSuppression2: true, googHighpassFilter: true, googTypingNoiseDetection: true, googAudioMirroring: true, */ }, ], }, video: { optional: [ { minAspectRatio: 1.333, maxAspectRatio: 1.778, maxWidth: 1920, minWidth: 320, maxHeight: 1080, minHeight: 180, maxFrameRate: 60, minFrameRate: 30, }, ], }, optional: [ { DtlsSrtpKeyAgreement: true, }, ], mandatory: { OfferToReceiveAudio: true, OfferToReceiveVideo: true, }, }; var alice = new WEBRTC.RTCPeerConnection(config, constraints); var bob = new WEBRTC.RTCPeerConnection(config, constraints); P2P(alice, bob); alice.onnegotiationneeded(function() { console.log('Done! :)'); setTimeout(function() { console.log('Closing...'); alice.close(); bob.close(); }, 5000); }); ================================================ FILE: test/p2p_stream.js ================================================ var WEBRTC = require('../'); WEBRTC.setDebug(true); var config = { iceServers: [ { url: 'stun:stun.l.google.com:19302', }, ], }; var constraints = { audio: { optional: [ { googEchoCancellation: true, googEchoCancellation2: true, googDAEchoCancellation: true, googAutoGainControl: true, googAutoGainControl2: true, googNoiseSuppression: true, googNoiseSuppression2: true, googHighpassFilter: true, googTypingNoiseDetection: true, googAudioMirroring: true, }, ], }, video: { optional: [ { minAspectRatio: 1.333, maxAspectRatio: 1.778, maxWidth: 1920, minWidth: 320, maxHeight: 1080, minHeight: 180, maxFrameRate: 60, minFrameRate: 30, }, ], }, optional: [ { DtlsSrtpKeyAgreement: true, }, ], mandatory: { OfferToReceiveAudio: true, OfferToReceiveVideo: true, }, }; var alice = new WEBRTC.RTCPeerConnection(config, constraints); var bob = new WEBRTC.RTCPeerConnection(config, constraints); alice.onicecandidate = function (event) { var candidate = event.candidate || event; bob.addIceCandidate(candidate); }; bob.onicecandidate = function (event) { var candidate = event.candidate || event; alice.addIceCandidate(candidate); }; alice.onnegotiationneeded = function () { alice.createOffer(function (sdp) { alice.setLocalDescription(sdp, function () { bob.setRemoteDescription(sdp, function () { bob.createAnswer(function (sdp) { bob.setLocalDescription(sdp, function () { alice.setRemoteDescription(sdp, function () { console.log('Alice -> Bob Connected!'); }); }); }); }); }); }); }; bob.onnegotiationneeded = function () { bob.createOffer(function (sdp) { bob.setLocalDescription(sdp, function () { alice.setRemoteDescription(sdp, function () { alice.createAnswer(function (sdp) { alice.setLocalDescription(sdp, function () { bob.setRemoteDescription(sdp, function () { console.log('Bob -> Alice Connected!'); }); }); }); }); }); }); }; alice.onaddstream = function(stream) { console.log('Alice: mediaStream added!'); }; bob.onaddstream = function(stream) { console.log('Bob: mediaStream added!'); }; alice.onremovestream = function(stream) { console.log('Alice: mediaStream removed!'); }; bob.onremovestream = function(stream) { console.log('Bob: mediaStream removed!'); }; WEBRTC.getSources(function (inputs) { console.log(inputs); WEBRTC.getUserMedia(constraints, function(stream) { if (stream) { alice.addStream(stream); setTimeout(function () { alice.close(); bob.close(); }, 5000); } }); }); ================================================ FILE: test/test.js ================================================ var WebRTC = require('../'); //WebRTC.setDebug(true); var renderer = new WebRTC.MediaSource('window'); var capturer = new WebRTC.MediaSource('webcam'); capturer.connect(renderer); setTimeout(function() { console.log('Closing...'); capturer.end(); }, 10000); ================================================ FILE: test/videoStream.js ================================================ var WebRTC = require('../'); WebRTC.setDebug(true); function onSuccess(stream) { var video_list = stream.getVideoTracks(); video_list.forEach(function (track) { console.log('Video Track'); }); } var constraints = { audio: false, video: true, /* audio: { optional: [ { googEchoCancellation: true, googEchoCancellation2: true, googDAEchoCancellation: true, googAutoGainControl: true, googAutoGainControl2: true, googNoiseSuppression: true, googNoiseSuppression2: true, googHighpassFilter: true, googTypingNoiseDetection: true, googAudioMirroring: true, }, ], }, video: { optional: [ { minAspectRatio: 1.333, maxAspectRatio: 1.778, maxWidth: 1920, minWidth: 320, maxHeight: 1080, minHeight: 180, maxFrameRate: 60, minFrameRate: 30, }, ], }, optional: [ { DtlsSrtpKeyAgreement: true, }, ], mandatory: { OfferToReceiveAudio: true, OfferToReceiveVideo: true, }, */ }; WebRTC.getUserMedia(constraints, onSuccess);