From fdfc52280a49bce9d62c611e71ae94b2b5bdd41d Mon Sep 17 00:00:00 2001 From: Mister Hat Date: Sun, 4 Dec 2016 10:57:37 -0600 Subject: [PATCH 1/5] linux support --- index.js | 17 ++++++++++++++++- package.json | 4 ++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index 5294d2e..945c75a 100644 --- a/index.js +++ b/index.js @@ -34,7 +34,22 @@ class Aperture { recorderOpts.push(`${cropArea.x}:${cropArea.y}:${cropArea.width}:${cropArea.height}`); } - this.recorder = execa(path.join(__dirname, 'swift', 'main'), recorderOpts); + if (process.platform === 'darwin') { + this.recorder = execa(path.join(__dirname, 'swift', 'main'), recorderOpts); + } else if (process.platform === 'linux') { + let args = ['-f', 'x11grab', '-i']; + + if (opts.cropArea) { + args.push(':0+' + opts.cropArea.x + ',' + opts.cropArea.y); + args.push('-video_size', opts.cropArea.width + 'x' + opts.cropArea.height); + } else { + args.push(':0'); + } + + args.push('-framerate', opts.fps, this.tmpPath); + + this.recorder = execa('ffmpeg', args); + } const timeout = setTimeout(() => { const err = new Error('unnable to start the recorder after 5 seconds'); diff --git a/package.json b/package.json index b72c6e9..db15fef 100644 --- a/package.json +++ b/package.json @@ -5,8 +5,8 @@ "main": "index.js", "scripts": { "test": "xo", - "build": "cd swift && xcodebuild && mv build/release/aperture main && rm -r build", - "postinstall": "npm run build" + "build-macos": "cd swift && xcodebuild && mv build/release/aperture main && rm -r build", + "postinstall": "case $OSTYPE in darwin*) npm run build-macos ;; esac" }, "repository": { "type": "git", From d9b84c3723ed5332147ab2e981c3124248f8e861 Mon Sep 17 00:00:00 2001 From: Phred Date: Thu, 16 Mar 2017 08:05:37 -0500 Subject: [PATCH 2/5] cleanup based on code review comments --- index.js | 8 +++++--- package.json | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/index.js b/index.js index 945c75a..78e175d 100644 --- a/index.js +++ b/index.js @@ -37,11 +37,13 @@ class Aperture { if (process.platform === 'darwin') { this.recorder = execa(path.join(__dirname, 'swift', 'main'), recorderOpts); } else if (process.platform === 'linux') { - let args = ['-f', 'x11grab', '-i']; + const args = ['-f', 'x11grab', '-i']; if (opts.cropArea) { - args.push(':0+' + opts.cropArea.x + ',' + opts.cropArea.y); - args.push('-video_size', opts.cropArea.width + 'x' + opts.cropArea.height); + args.push( + `:0+${opts.cropArea.x},${opts.cropArea.y}`, + '-video_size', `${opts.cropArea.width}x${opts.cropArea.height}` + ); } else { args.push(':0'); } diff --git a/package.json b/package.json index db15fef..b266cd4 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "scripts": { "test": "xo", "build-macos": "cd swift && xcodebuild && mv build/release/aperture main && rm -r build", - "postinstall": "case $OSTYPE in darwin*) npm run build-macos ;; esac" + "postinstall": "[ "$(uname) = "Darwin" ] && npm run build-macos" }, "repository": { "type": "git", From d6cace1c87b02992c58c179d7c6d620d4ab55313 Mon Sep 17 00:00:00 2001 From: Phred Date: Thu, 16 Mar 2017 23:22:12 -0500 Subject: [PATCH 3/5] updates to linux functionality --- index.js | 62 ++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 44 insertions(+), 18 deletions(-) diff --git a/index.js b/index.js index 37e71c0..0c05d6e 100644 --- a/index.js +++ b/index.js @@ -9,11 +9,17 @@ const debuglog = util.debuglog('aperture'); class Aperture { constructor() { - macosVersion.assertGreaterThanOrEqualTo('10.10'); + if (process.platform === 'darwin') { + macosVersion.assertGreaterThanOrEqualTo('10.10'); + } } getAudioSources() { - return execa.stdout(path.join(__dirname, 'swift/main'), ['list-audio-devices']).then(JSON.parse); + if (process.platform === 'darwin') { + return execa.stdout(path.join(__dirname, 'swift/main'), ['list-audio-devices']).then(JSON.parse); + } else if (process.platform === 'linux') { + return execa.stdout('sh', ['-c', "arecord -l | awk 'match(\$0, /card ([0-9]): ([^,]+),/, result) { print result[1] \":\" result[2] }'"]); + } } startRecording({ @@ -25,6 +31,8 @@ class Aperture { audioSourceId = 'none' } = {}) { return new Promise((resolve, reject) => { + let cropAreaOpts; + if (this.recorder !== undefined) { reject(new Error('Call `.stopRecording()` first')); return; @@ -45,14 +53,14 @@ class Aperture { return; } - cropArea = `${cropArea.x}:${cropArea.y}:${cropArea.width}:${cropArea.height}`; + cropAreaOpts = `${cropArea.x}:${cropArea.y}:${cropArea.width}:${cropArea.height}`; } if (process.platform === 'darwin') { const recorderOpts = [ this.tmpPath, fps, - cropArea, + cropAreaOpts || cropArea, showCursor, highlightClicks, displayId, @@ -63,16 +71,16 @@ class Aperture { } else if (process.platform === 'linux') { const args = ['-f', 'x11grab', '-i']; - if (opts.cropArea) { + if (typeof cropArea === 'object') { args.push( - `:0+${opts.cropArea.x},${opts.cropArea.y}`, - '-video_size', `${opts.cropArea.width}x${opts.cropArea.height}` + `:0+${cropArea.x},${cropArea.y}`, + '-video_size', `${cropArea.width}x${cropArea.height}` ); } else { args.push(':0'); } - args.push('-framerate', opts.fps, this.tmpPath); + args.push('-framerate', fps, this.tmpPath); this.recorder = execa('ffmpeg', args); } @@ -97,15 +105,28 @@ class Aperture { }); this.recorder.stdout.setEncoding('utf8'); - this.recorder.stdout.on('data', data => { - debuglog(data); - - if (data.trim() === 'R') { - // `R` is printed by Swift when the recording **actually** starts - clearTimeout(timeout); - resolve(this.tmpPath); - } - }); + if (process.platform === 'darwin') { + this.recorder.stdout.on('data', data => { + debuglog(data); + + if (data.trim() === 'R') { + // `R` is printed by Swift when the recording **actually** starts + clearTimeout(timeout); + resolve(this.tmpPath); + } + }); + } else if (process.platform === 'linux') { + this.recorder.stderr.on('data', data => { + debuglog(data); + + if (/^frame=\s*\d+\sfps=\s\d+/.test(data.trim())) { + // fmpeg prints lines like this while it's reocrding + // frame= 203 fps= 30 q=-1.0 Lsize= 54kB time=00:00:06.70 bitrate= 65.8kbits/s dup=21 drop=19 speed=0.996x + clearTimeout(timeout); + resolve(this.tmpPath); + } + }); + } }); } @@ -123,7 +144,12 @@ class Aperture { reject(err.stderr ? new Error(err.stderr) : err); }); - this.recorder.kill(); + if (process.platform === 'darwin') { + this.recorder.kill(); + } else if (process.platform === 'linux') { + this.recorder.stdin.setEncoding('utf8'); + this.recorder.stdin.write('q'); + } }); } } From b2ea6508edeeaf544644b9c7f66c13d83a128d5c Mon Sep 17 00:00:00 2001 From: Phred Date: Fri, 17 Mar 2017 08:45:32 -0500 Subject: [PATCH 4/5] addressed code review comments --- index.js | 37 +++++++++++++++++++++++++------------ package.json | 1 - 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/index.js b/index.js index 0c05d6e..afa21d5 100644 --- a/index.js +++ b/index.js @@ -7,18 +7,31 @@ const macosVersion = require('macos-version'); const debuglog = util.debuglog('aperture'); +const IS_LINUX = process.platform === 'linux'; +const IS_MACOS = process.platform === 'darwin'; + class Aperture { constructor() { - if (process.platform === 'darwin') { + if (IS_MACOS) { macosVersion.assertGreaterThanOrEqualTo('10.10'); } } getAudioSources() { - if (process.platform === 'darwin') { + if (IS_MACOS) { return execa.stdout(path.join(__dirname, 'swift/main'), ['list-audio-devices']).then(JSON.parse); - } else if (process.platform === 'linux') { - return execa.stdout('sh', ['-c', "arecord -l | awk 'match(\$0, /card ([0-9]): ([^,]+),/, result) { print result[1] \":\" result[2] }'"]); + } else if (IS_LINUX) { + return execa.stdout('arecord', ['-l']).then( + stdout => stdout.split('\n').reduce((result, line) => { + const match = line.match(/card (\d+): ([^,]+),/); + + if (match) { + result.push(`${match[1]}:${match[2]}`); + } + + return result; + }, []) + ); } } @@ -56,7 +69,7 @@ class Aperture { cropAreaOpts = `${cropArea.x}:${cropArea.y}:${cropArea.width}:${cropArea.height}`; } - if (process.platform === 'darwin') { + if (IS_MACOS) { const recorderOpts = [ this.tmpPath, fps, @@ -68,7 +81,7 @@ class Aperture { ]; this.recorder = execa(path.join(__dirname, 'swift', 'main'), recorderOpts); - } else if (process.platform === 'linux') { + } else if (IS_LINUX) { const args = ['-f', 'x11grab', '-i']; if (typeof cropArea === 'object') { @@ -80,7 +93,7 @@ class Aperture { args.push(':0'); } - args.push('-framerate', fps, this.tmpPath); + args.push('-framerate', fps, '-draw_mouse', +(showCursor === true), this.tmpPath); this.recorder = execa('ffmpeg', args); } @@ -105,7 +118,7 @@ class Aperture { }); this.recorder.stdout.setEncoding('utf8'); - if (process.platform === 'darwin') { + if (IS_MACOS) { this.recorder.stdout.on('data', data => { debuglog(data); @@ -115,11 +128,11 @@ class Aperture { resolve(this.tmpPath); } }); - } else if (process.platform === 'linux') { + } else if (IS_LINUX) { this.recorder.stderr.on('data', data => { debuglog(data); - if (/^frame=\s*\d+\sfps=\s\d+/.test(data.trim())) { + if (/^frame=\s*\d+\sfps=\s\d+/.test(data.toString('utf8').trim())) { // fmpeg prints lines like this while it's reocrding // frame= 203 fps= 30 q=-1.0 Lsize= 54kB time=00:00:06.70 bitrate= 65.8kbits/s dup=21 drop=19 speed=0.996x clearTimeout(timeout); @@ -144,9 +157,9 @@ class Aperture { reject(err.stderr ? new Error(err.stderr) : err); }); - if (process.platform === 'darwin') { + if (IS_MACOS) { this.recorder.kill(); - } else if (process.platform === 'linux') { + } else if (IS_LINUX) { this.recorder.stdin.setEncoding('utf8'); this.recorder.stdin.write('q'); } diff --git a/package.json b/package.json index efec1e4..ebb0750 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,6 @@ "scripts": { "test": "xo", "build-macos": "cd swift && xcodebuild -derivedDataPath $(mktemp -d) -scheme aperture", - "postinstall": "[ \"$(uname)\" = \"Darwin\" ] && npm run build-macos", "prepublish": "[ \"$(uname)\" = \"Darwin\" ] && npm run build-macos" }, "files": [ From 3e58b2bcb112376918d1c288b35e8170a310d9b2 Mon Sep 17 00:00:00 2001 From: Phred Date: Fri, 17 Mar 2017 09:09:54 -0500 Subject: [PATCH 5/5] video_size needs to come before input device --- index.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/index.js b/index.js index afa21d5..8dfe48f 100644 --- a/index.js +++ b/index.js @@ -82,15 +82,15 @@ class Aperture { this.recorder = execa(path.join(__dirname, 'swift', 'main'), recorderOpts); } else if (IS_LINUX) { - const args = ['-f', 'x11grab', '-i']; + const args = ['-f', 'x11grab']; if (typeof cropArea === 'object') { args.push( - `:0+${cropArea.x},${cropArea.y}`, - '-video_size', `${cropArea.width}x${cropArea.height}` + '-video_size', `${cropArea.width}x${cropArea.height}`, + '-i', `:0+${cropArea.x},${cropArea.y}` ); } else { - args.push(':0'); + args.push('-i', ':0'); } args.push('-framerate', fps, '-draw_mouse', +(showCursor === true), this.tmpPath); @@ -109,7 +109,7 @@ class Aperture { this.recorder.kill(); delete this.recorder; reject(err); - }, 5000); + }, 7500); this.recorder.catch(err => { clearTimeout(timeout);